agentwheel 0.14.6 → 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/README.md +3 -2
- package/dist/index.js +443 -276
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,9 +10,9 @@ import {
|
|
|
10
10
|
|
|
11
11
|
// src/cli/index.ts
|
|
12
12
|
import { existsSync } from "fs";
|
|
13
|
-
import { mkdir as
|
|
13
|
+
import { mkdir as mkdir21, rm as rm11, writeFile as writeFile20 } from "fs/promises";
|
|
14
14
|
import { homedir as homedir9 } from "os";
|
|
15
|
-
import { dirname as
|
|
15
|
+
import { dirname as dirname30, join as join41, resolve as resolve20 } from "path";
|
|
16
16
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
17
17
|
import { Command } from "commander";
|
|
18
18
|
|
|
@@ -111,6 +111,7 @@ var targetMappingSchema = z2.object({
|
|
|
111
111
|
"codex-plugin",
|
|
112
112
|
"hermes-plugin",
|
|
113
113
|
"copilot-plugin",
|
|
114
|
+
"openclaw-subagent",
|
|
114
115
|
"codex-subagent",
|
|
115
116
|
"copilot-instruction",
|
|
116
117
|
"copilot-prompt",
|
|
@@ -376,6 +377,9 @@ var openClawAdapter = {
|
|
|
376
377
|
local: { enabled: true, dest: "skills" },
|
|
377
378
|
user: { enabled: true, root: "home", dest: ".openclaw/skills" }
|
|
378
379
|
},
|
|
380
|
+
subagents: {
|
|
381
|
+
user: { enabled: true, root: "home", dest: ".openclaw/workspace-subagents", semantic: "openclaw-subagent" }
|
|
382
|
+
},
|
|
379
383
|
mcp: {
|
|
380
384
|
user: { enabled: true, root: "home", dest: ".openclaw/openclaw.json", merge: "openclaw-json-deep" }
|
|
381
385
|
},
|
|
@@ -469,7 +473,7 @@ async function resolveAdapter(options) {
|
|
|
469
473
|
import { execFile as execFile3 } from "child_process";
|
|
470
474
|
import { mkdtemp as mkdtemp2, rm as rm4, writeFile as writeFile9 } from "fs/promises";
|
|
471
475
|
import { tmpdir as tmpdir3 } from "os";
|
|
472
|
-
import { basename as basename3, join as join5 } from "path";
|
|
476
|
+
import { basename as basename3, dirname as dirname10, join as join5 } from "path";
|
|
473
477
|
import { promisify as promisify3 } from "util";
|
|
474
478
|
|
|
475
479
|
// src/model/graph-lock.ts
|
|
@@ -892,11 +896,33 @@ async function mergeOpenClawJsonFile(sourcePath, destPath) {
|
|
|
892
896
|
sourcePath
|
|
893
897
|
);
|
|
894
898
|
const current = await pathExists(destPath) ? JSON.parse(await readFile6(destPath, "utf8")) : {};
|
|
895
|
-
const merged =
|
|
899
|
+
const merged = mergeOpenClawJson(current, source);
|
|
896
900
|
await mkdir4(dirname5(destPath), { recursive: true });
|
|
897
901
|
await writeFile5(destPath, `${JSON.stringify(merged, null, 2)}
|
|
898
902
|
`, "utf8");
|
|
899
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
|
+
}
|
|
900
926
|
function expandEnvPlaceholders(value, sourcePath) {
|
|
901
927
|
if (typeof value === "string") {
|
|
902
928
|
return value.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g, (_match, name) => {
|
|
@@ -934,6 +960,24 @@ function normalizeOpenClawMcpServer(server) {
|
|
|
934
960
|
delete out.type;
|
|
935
961
|
return out;
|
|
936
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
|
+
}
|
|
937
981
|
|
|
938
982
|
// src/install/manifest.ts
|
|
939
983
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -1908,7 +1952,7 @@ async function applyOperation(operation, context) {
|
|
|
1908
1952
|
if (operation.mergeStrategy === "json-deep") {
|
|
1909
1953
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeJsonFile);
|
|
1910
1954
|
} else if (operation.mergeStrategy === "openclaw-json-deep") {
|
|
1911
|
-
await
|
|
1955
|
+
await mergeOpenClawJsonWithTransport(operation.sourcePath, operation.destPath, transport);
|
|
1912
1956
|
} else if (operation.mergeStrategy === "yaml-deep") {
|
|
1913
1957
|
await mergeWithTransport(operation.sourcePath, operation.destPath, transport, mergeYamlFile);
|
|
1914
1958
|
} else if (operation.mergeStrategy === "codex-toml-mcp") {
|
|
@@ -2258,13 +2302,58 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
2258
2302
|
await rm4(tempRoot, { recursive: true, force: true });
|
|
2259
2303
|
}
|
|
2260
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
|
+
}
|
|
2261
2350
|
|
|
2262
2351
|
// src/install/plan.ts
|
|
2263
|
-
import { basename as
|
|
2352
|
+
import { basename as basename8, join as join16, relative as relative3 } from "path";
|
|
2264
2353
|
|
|
2265
2354
|
// src/staging/codex-subagents.ts
|
|
2266
2355
|
import { mkdir as mkdir8, readFile as readFile10, writeFile as writeFile10 } from "fs/promises";
|
|
2267
|
-
import { basename as basename4, dirname as
|
|
2356
|
+
import { basename as basename4, dirname as dirname11, join as join6 } from "path";
|
|
2268
2357
|
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
2269
2358
|
async function renderCodexSubagents(artifacts, stageRoot, adapter) {
|
|
2270
2359
|
if (adapter?.name !== "codex") return artifacts;
|
|
@@ -2309,7 +2398,7 @@ async function renderCodexSubagent(artifact, stageRoot) {
|
|
|
2309
2398
|
}
|
|
2310
2399
|
const markdown = await readFile10(markdownPath, "utf8");
|
|
2311
2400
|
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
2312
|
-
await mkdir8(
|
|
2401
|
+
await mkdir8(dirname11(renderedPath), { recursive: true });
|
|
2313
2402
|
await writeFile10(renderedPath, toml, "utf8");
|
|
2314
2403
|
return {
|
|
2315
2404
|
...artifact,
|
|
@@ -2379,7 +2468,7 @@ function escapeRegExp(value) {
|
|
|
2379
2468
|
|
|
2380
2469
|
// src/staging/copilot-artifacts.ts
|
|
2381
2470
|
import { mkdir as mkdir9, readFile as readFile11, writeFile as writeFile11 } from "fs/promises";
|
|
2382
|
-
import { basename as basename5, dirname as
|
|
2471
|
+
import { basename as basename5, dirname as dirname12, join as join7 } from "path";
|
|
2383
2472
|
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
2384
2473
|
if (adapter?.name !== "copilot") return artifacts;
|
|
2385
2474
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -2410,7 +2499,7 @@ async function renderCopilotSubagent(artifact, stageRoot) {
|
|
|
2410
2499
|
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
2411
2500
|
}
|
|
2412
2501
|
const markdown = await readFile11(markdownPath, "utf8");
|
|
2413
|
-
await mkdir9(
|
|
2502
|
+
await mkdir9(dirname12(renderedPath), { recursive: true });
|
|
2414
2503
|
await writeFile11(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
2415
2504
|
return {
|
|
2416
2505
|
...artifact,
|
|
@@ -2463,15 +2552,84 @@ function yamlString(value) {
|
|
|
2463
2552
|
return JSON.stringify(value);
|
|
2464
2553
|
}
|
|
2465
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
|
+
|
|
2466
2624
|
// src/targets/plugins/claude.ts
|
|
2467
|
-
import { join as
|
|
2625
|
+
import { join as join10 } from "path";
|
|
2468
2626
|
|
|
2469
2627
|
// src/targets/plugins/common.ts
|
|
2470
|
-
import { readFile as
|
|
2471
|
-
import { join as
|
|
2628
|
+
import { readFile as readFile13 } from "fs/promises";
|
|
2629
|
+
import { join as join9 } from "path";
|
|
2472
2630
|
import { parseDocument } from "yaml";
|
|
2473
2631
|
function pluginStateRoot(request) {
|
|
2474
|
-
return
|
|
2632
|
+
return join9(
|
|
2475
2633
|
request.targetRoot,
|
|
2476
2634
|
".agentwheel",
|
|
2477
2635
|
"plugins",
|
|
@@ -2489,16 +2647,16 @@ function safeNameSegment(value) {
|
|
|
2489
2647
|
return normalized.length > 0 ? normalized : "unnamed";
|
|
2490
2648
|
}
|
|
2491
2649
|
async function jsonPluginName(root, relativeManifestPath, fallback) {
|
|
2492
|
-
const manifestPath =
|
|
2650
|
+
const manifestPath = join9(root, relativeManifestPath);
|
|
2493
2651
|
if (!await pathExists(manifestPath)) return fallback;
|
|
2494
|
-
const parsed = JSON.parse(await
|
|
2652
|
+
const parsed = JSON.parse(await readFile13(manifestPath, "utf8"));
|
|
2495
2653
|
return typeof parsed.name === "string" && parsed.name.trim().length > 0 ? parsed.name.trim() : fallback;
|
|
2496
2654
|
}
|
|
2497
2655
|
async function yamlPluginName(root, relativeManifestPaths, fallback) {
|
|
2498
2656
|
for (const relativeManifestPath of relativeManifestPaths) {
|
|
2499
|
-
const manifestPath =
|
|
2657
|
+
const manifestPath = join9(root, relativeManifestPath);
|
|
2500
2658
|
if (!await pathExists(manifestPath)) continue;
|
|
2501
|
-
const document = parseDocument(await
|
|
2659
|
+
const document = parseDocument(await readFile13(manifestPath, "utf8"));
|
|
2502
2660
|
const parsed = document.toJSON();
|
|
2503
2661
|
if (!isRecord4(parsed)) continue;
|
|
2504
2662
|
for (const key of ["name", "module", "package"]) {
|
|
@@ -2527,7 +2685,7 @@ async function claudePluginSpec(request) {
|
|
|
2527
2685
|
packageName: request.artifact.packageName,
|
|
2528
2686
|
installName: request.installName
|
|
2529
2687
|
});
|
|
2530
|
-
const marketplaceRoot =
|
|
2688
|
+
const marketplaceRoot = join10(stateRoot, "marketplace");
|
|
2531
2689
|
const scope = claudeScope(request.installationType);
|
|
2532
2690
|
const selector = `${pluginName}@${marketplaceName}`;
|
|
2533
2691
|
return {
|
|
@@ -2552,7 +2710,7 @@ function claudeScope(installationType) {
|
|
|
2552
2710
|
}
|
|
2553
2711
|
|
|
2554
2712
|
// src/targets/plugins/codex.ts
|
|
2555
|
-
import { join as
|
|
2713
|
+
import { join as join11 } from "path";
|
|
2556
2714
|
async function codexPluginSpec(request) {
|
|
2557
2715
|
const pluginName = await jsonPluginName(request.sourcePath, ".codex-plugin/plugin.json", request.installName);
|
|
2558
2716
|
const marketplaceName = agentwheelMarketplaceName(request.artifact.packageName, pluginName);
|
|
@@ -2563,7 +2721,7 @@ async function codexPluginSpec(request) {
|
|
|
2563
2721
|
packageName: request.artifact.packageName,
|
|
2564
2722
|
installName: request.installName
|
|
2565
2723
|
});
|
|
2566
|
-
const marketplaceRoot =
|
|
2724
|
+
const marketplaceRoot = join11(stateRoot, "marketplace");
|
|
2567
2725
|
const selector = `${pluginName}@${marketplaceName}`;
|
|
2568
2726
|
return {
|
|
2569
2727
|
runtime: "codex",
|
|
@@ -2582,7 +2740,7 @@ async function codexPluginSpec(request) {
|
|
|
2582
2740
|
}
|
|
2583
2741
|
|
|
2584
2742
|
// src/targets/plugins/copilot.ts
|
|
2585
|
-
import { join as
|
|
2743
|
+
import { join as join12 } from "path";
|
|
2586
2744
|
async function copilotPluginSpec(request) {
|
|
2587
2745
|
if (request.installationType !== "user") {
|
|
2588
2746
|
throw new Error("Copilot plugins are persistent user-level installs only; pass --installation-type user.");
|
|
@@ -2595,7 +2753,7 @@ async function copilotPluginSpec(request) {
|
|
|
2595
2753
|
packageName: request.artifact.packageName,
|
|
2596
2754
|
installName: request.installName
|
|
2597
2755
|
});
|
|
2598
|
-
const pluginRoot =
|
|
2756
|
+
const pluginRoot = join12(stateRoot, "plugin");
|
|
2599
2757
|
return {
|
|
2600
2758
|
runtime: "copilot",
|
|
2601
2759
|
pluginName,
|
|
@@ -2606,7 +2764,7 @@ async function copilotPluginSpec(request) {
|
|
|
2606
2764
|
}
|
|
2607
2765
|
|
|
2608
2766
|
// src/targets/plugins/hermes.ts
|
|
2609
|
-
import { join as
|
|
2767
|
+
import { join as join13 } from "path";
|
|
2610
2768
|
async function hermesPluginSpec(request) {
|
|
2611
2769
|
if (request.installationType !== "user") {
|
|
2612
2770
|
throw new Error("Hermes plugins are user-level installs only; pass --installation-type user.");
|
|
@@ -2619,7 +2777,7 @@ async function hermesPluginSpec(request) {
|
|
|
2619
2777
|
packageName: request.artifact.packageName,
|
|
2620
2778
|
installName: request.installName
|
|
2621
2779
|
});
|
|
2622
|
-
const repoRoot =
|
|
2780
|
+
const repoRoot = join13(stateRoot, "repo");
|
|
2623
2781
|
return {
|
|
2624
2782
|
runtime: "hermes",
|
|
2625
2783
|
pluginName,
|
|
@@ -2630,8 +2788,8 @@ async function hermesPluginSpec(request) {
|
|
|
2630
2788
|
}
|
|
2631
2789
|
|
|
2632
2790
|
// src/targets/plugins/openclaw.ts
|
|
2633
|
-
import { readFile as
|
|
2634
|
-
import { join as
|
|
2791
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
2792
|
+
import { join as join14 } from "path";
|
|
2635
2793
|
function openClawPluginInstallCommand(request) {
|
|
2636
2794
|
return ["openclaw", "plugins", "install", "--force", request.path];
|
|
2637
2795
|
}
|
|
@@ -2658,19 +2816,19 @@ async function openClawPluginSpec(request) {
|
|
|
2658
2816
|
}
|
|
2659
2817
|
async function openClawPluginName(root, fallback) {
|
|
2660
2818
|
for (const manifestName of ["plugin.json", "openclaw.plugin.json"]) {
|
|
2661
|
-
const manifestPath =
|
|
2819
|
+
const manifestPath = join14(root, manifestName);
|
|
2662
2820
|
if (!await pathExists(manifestPath)) continue;
|
|
2663
|
-
const parsed = JSON.parse(await
|
|
2821
|
+
const parsed = JSON.parse(await readFile14(manifestPath, "utf8"));
|
|
2664
2822
|
if (typeof parsed.name === "string" && parsed.name.trim().length > 0) return parsed.name.trim();
|
|
2665
2823
|
}
|
|
2666
2824
|
return fallback;
|
|
2667
2825
|
}
|
|
2668
2826
|
async function openClawClawHubPluginMetadata(root, fallback) {
|
|
2669
|
-
const metadataPath =
|
|
2827
|
+
const metadataPath = join14(root, "clawhub.json");
|
|
2670
2828
|
if (!await pathExists(metadataPath)) {
|
|
2671
2829
|
throw new Error("OpenClaw ClawHub plugins must contain clawhub.json");
|
|
2672
2830
|
}
|
|
2673
|
-
const parsed = JSON.parse(await
|
|
2831
|
+
const parsed = JSON.parse(await readFile14(metadataPath, "utf8"));
|
|
2674
2832
|
const installSpec = stringField(parsed.installSpec);
|
|
2675
2833
|
if (!installSpec?.startsWith("clawhub:")) {
|
|
2676
2834
|
throw new Error("OpenClaw ClawHub plugin metadata must declare installSpec starting with clawhub:");
|
|
@@ -2710,8 +2868,8 @@ async function semanticPluginSpecForArtifact(request) {
|
|
|
2710
2868
|
}
|
|
2711
2869
|
|
|
2712
2870
|
// src/validation/artifacts.ts
|
|
2713
|
-
import { readFile as
|
|
2714
|
-
import { basename as
|
|
2871
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
2872
|
+
import { basename as basename7, join as join15 } from "path";
|
|
2715
2873
|
import { parseDocument as parseDocument2 } from "yaml";
|
|
2716
2874
|
|
|
2717
2875
|
// src/model/selection.ts
|
|
@@ -2875,7 +3033,7 @@ async function inferArtifactFormat(artifact, target) {
|
|
|
2875
3033
|
return void 0;
|
|
2876
3034
|
}
|
|
2877
3035
|
if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
|
|
2878
|
-
if (artifact.kind === "dir" && await pathExists(
|
|
3036
|
+
if (artifact.kind === "dir" && await pathExists(join15(artifactPath(artifact), "clawhub.json"))) return "openclaw-clawhub-plugin";
|
|
2879
3037
|
if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
|
|
2880
3038
|
}
|
|
2881
3039
|
return void 0;
|
|
@@ -2927,7 +3085,7 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2927
3085
|
const issues = [];
|
|
2928
3086
|
if (artifact.type === "skills") {
|
|
2929
3087
|
if (artifact.kind === "dir") {
|
|
2930
|
-
const skillMd =
|
|
3088
|
+
const skillMd = join15(artifactPath(artifact), "SKILL.md");
|
|
2931
3089
|
if (!await pathExists(skillMd)) {
|
|
2932
3090
|
issues.push({ artifact, message: "skill directory must contain SKILL.md" });
|
|
2933
3091
|
} else {
|
|
@@ -2939,10 +3097,17 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2939
3097
|
issues.push(...await validateSkillFrontmatter(artifact, artifactPath(artifact)));
|
|
2940
3098
|
}
|
|
2941
3099
|
}
|
|
2942
|
-
if (target.merge === "json-deep") {
|
|
3100
|
+
if (target.merge === "json-deep" || target.merge === "openclaw-json-deep") {
|
|
2943
3101
|
const parsed = await parseJsonObjectArtifact(artifact);
|
|
2944
3102
|
if (!parsed.ok) issues.push({ artifact, message: parsed.message });
|
|
2945
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
|
+
}
|
|
2946
3111
|
if (target.merge === "yaml-deep") {
|
|
2947
3112
|
const parsed = await parseYamlObjectArtifact(artifact);
|
|
2948
3113
|
if (!parsed.ok) issues.push({ artifact, message: parsed.message });
|
|
@@ -2960,7 +3125,7 @@ async function validateGenericStructure(artifact, target) {
|
|
|
2960
3125
|
async function validateSkillFrontmatter(artifact, skillMdPath) {
|
|
2961
3126
|
let content;
|
|
2962
3127
|
try {
|
|
2963
|
-
content = await
|
|
3128
|
+
content = await readFile15(skillMdPath, "utf8");
|
|
2964
3129
|
} catch (error) {
|
|
2965
3130
|
return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
|
|
2966
3131
|
}
|
|
@@ -3013,7 +3178,7 @@ function validatePluginArtifact(artifact, format) {
|
|
|
3013
3178
|
async function validateJsonPluginDescriptor(artifact, relativeManifestPath, label) {
|
|
3014
3179
|
const generic = validatePluginArtifact(artifact, `${label.toLowerCase()}-plugin`);
|
|
3015
3180
|
if (generic.length > 0) return generic;
|
|
3016
|
-
const manifestPath =
|
|
3181
|
+
const manifestPath = join15(artifactPath(artifact), relativeManifestPath);
|
|
3017
3182
|
if (!await pathExists(manifestPath)) {
|
|
3018
3183
|
return [{ artifact, message: `${label} plugins must contain ${relativeManifestPath}` }];
|
|
3019
3184
|
}
|
|
@@ -3024,26 +3189,26 @@ async function validateHermesPlugin(artifact) {
|
|
|
3024
3189
|
const generic = validatePluginArtifact(artifact, "hermes-plugin");
|
|
3025
3190
|
if (generic.length > 0) return generic;
|
|
3026
3191
|
const root = artifactPath(artifact);
|
|
3027
|
-
const manifestPaths = [
|
|
3192
|
+
const manifestPaths = [join15(root, "plugin.yaml"), join15(root, "plugin.yml")];
|
|
3028
3193
|
const manifestPath = await firstExistingPath(manifestPaths);
|
|
3029
3194
|
if (!manifestPath) {
|
|
3030
3195
|
return [{ artifact, message: "Hermes plugins must contain plugin.yaml or plugin.yml" }];
|
|
3031
3196
|
}
|
|
3032
3197
|
try {
|
|
3033
|
-
const document = parseDocument2(await
|
|
3198
|
+
const document = parseDocument2(await readFile15(manifestPath, "utf8"));
|
|
3034
3199
|
if (document.errors.length > 0) {
|
|
3035
|
-
return [{ artifact, message: `Hermes ${
|
|
3200
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` }];
|
|
3036
3201
|
}
|
|
3037
3202
|
const parsed = document.toJSON();
|
|
3038
3203
|
if (!isUnknownRecord(parsed)) {
|
|
3039
|
-
return [{ artifact, message: `Hermes ${
|
|
3204
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must contain a YAML object` }];
|
|
3040
3205
|
}
|
|
3041
3206
|
if (!hasStringField(parsed, "name") && !hasStringField(parsed, "module") && !hasStringField(parsed, "package") && !hasNestedPackageName(parsed)) {
|
|
3042
|
-
return [{ artifact, message: `Hermes ${
|
|
3207
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must declare a non-empty name, module, or package name` }];
|
|
3043
3208
|
}
|
|
3044
3209
|
return [];
|
|
3045
3210
|
} catch (error) {
|
|
3046
|
-
return [{ artifact, message: `Hermes ${
|
|
3211
|
+
return [{ artifact, message: `Hermes ${basename7(manifestPath)} must be valid YAML: ${errorMessage(error)}` }];
|
|
3047
3212
|
}
|
|
3048
3213
|
}
|
|
3049
3214
|
async function firstExistingPath(paths) {
|
|
@@ -3054,16 +3219,16 @@ async function firstExistingPath(paths) {
|
|
|
3054
3219
|
}
|
|
3055
3220
|
async function parseJsonPluginManifest(manifestPath, label) {
|
|
3056
3221
|
try {
|
|
3057
|
-
const parsed = JSON.parse(await
|
|
3222
|
+
const parsed = JSON.parse(await readFile15(manifestPath, "utf8"));
|
|
3058
3223
|
if (!isRecord5(parsed)) {
|
|
3059
|
-
return { ok: false, message: `${label} ${
|
|
3224
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must be a JSON object` };
|
|
3060
3225
|
}
|
|
3061
3226
|
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
3062
|
-
return { ok: false, message: `${label} ${
|
|
3227
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must declare a non-empty name` };
|
|
3063
3228
|
}
|
|
3064
3229
|
return { ok: true, name: parsed.name.trim() };
|
|
3065
3230
|
} catch (error) {
|
|
3066
|
-
return { ok: false, message: `${label} ${
|
|
3231
|
+
return { ok: false, message: `${label} ${basename7(manifestPath)} must be valid JSON: ${errorMessage(error)}` };
|
|
3067
3232
|
}
|
|
3068
3233
|
}
|
|
3069
3234
|
async function validateOpenClawPlugin(artifact) {
|
|
@@ -3096,12 +3261,12 @@ async function validateOpenClawClawHubPlugin(artifact) {
|
|
|
3096
3261
|
if (artifact.kind !== "dir") {
|
|
3097
3262
|
return [{ artifact, message: "OpenClaw ClawHub plugins must be directory artifacts" }];
|
|
3098
3263
|
}
|
|
3099
|
-
const metadataPath =
|
|
3264
|
+
const metadataPath = join15(artifactPath(artifact), "clawhub.json");
|
|
3100
3265
|
if (!await pathExists(metadataPath)) {
|
|
3101
3266
|
return [{ artifact, message: "OpenClaw ClawHub plugins must contain clawhub.json" }];
|
|
3102
3267
|
}
|
|
3103
3268
|
try {
|
|
3104
|
-
const parsed = JSON.parse(await
|
|
3269
|
+
const parsed = JSON.parse(await readFile15(metadataPath, "utf8"));
|
|
3105
3270
|
if (!isRecord5(parsed)) {
|
|
3106
3271
|
return [{ artifact, message: "OpenClaw ClawHub clawhub.json must be a JSON object" }];
|
|
3107
3272
|
}
|
|
@@ -3118,14 +3283,14 @@ async function validateOpenClawClawHubPlugin(artifact) {
|
|
|
3118
3283
|
}
|
|
3119
3284
|
async function openClawPluginManifestPaths(artifact) {
|
|
3120
3285
|
const root = artifactPath(artifact);
|
|
3121
|
-
const candidates = [
|
|
3286
|
+
const candidates = [join15(root, "plugin.json"), join15(root, "openclaw.plugin.json")];
|
|
3122
3287
|
const existing = await Promise.all(candidates.map(async (candidate) => await pathExists(candidate) ? candidate : void 0));
|
|
3123
3288
|
return existing.filter((candidate) => candidate !== void 0);
|
|
3124
3289
|
}
|
|
3125
3290
|
async function parseOpenClawPluginManifest(manifestPath) {
|
|
3126
|
-
const manifestName =
|
|
3291
|
+
const manifestName = basename7(manifestPath);
|
|
3127
3292
|
try {
|
|
3128
|
-
const parsed = JSON.parse(await
|
|
3293
|
+
const parsed = JSON.parse(await readFile15(manifestPath, "utf8"));
|
|
3129
3294
|
if (!isRecord5(parsed)) {
|
|
3130
3295
|
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
|
|
3131
3296
|
}
|
|
@@ -3142,7 +3307,7 @@ async function parseJsonObjectArtifact(artifact) {
|
|
|
3142
3307
|
return { ok: false, message: "merge artifacts must be JSON files" };
|
|
3143
3308
|
}
|
|
3144
3309
|
try {
|
|
3145
|
-
const parsed = JSON.parse(await
|
|
3310
|
+
const parsed = JSON.parse(await readFile15(artifactPath(artifact), "utf8"));
|
|
3146
3311
|
if (!isRecord5(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
|
|
3147
3312
|
return { ok: true, value: parsed };
|
|
3148
3313
|
} catch (error) {
|
|
@@ -3154,7 +3319,7 @@ async function parseYamlObjectArtifact(artifact) {
|
|
|
3154
3319
|
return { ok: false, message: "merge artifacts must be YAML files" };
|
|
3155
3320
|
}
|
|
3156
3321
|
try {
|
|
3157
|
-
const document = parseDocument2(await
|
|
3322
|
+
const document = parseDocument2(await readFile15(artifactPath(artifact), "utf8"));
|
|
3158
3323
|
if (document.errors.length > 0) {
|
|
3159
3324
|
return { ok: false, message: `merge artifact must be valid YAML: ${document.errors[0]?.message ?? "parse error"}` };
|
|
3160
3325
|
}
|
|
@@ -3181,7 +3346,7 @@ function artifactLabel(artifact) {
|
|
|
3181
3346
|
return `${owner}${artifact.type}/${artifact.name}`;
|
|
3182
3347
|
}
|
|
3183
3348
|
function hasExtension(artifact, extension) {
|
|
3184
|
-
return
|
|
3349
|
+
return basename7(artifact.name).toLowerCase().endsWith(extension) || basename7(artifactPath(artifact)).toLowerCase().endsWith(extension);
|
|
3185
3350
|
}
|
|
3186
3351
|
function isPluginFormat(value) {
|
|
3187
3352
|
return value !== void 0 && pluginFormats.includes(value);
|
|
@@ -3359,7 +3524,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
3359
3524
|
for (const entry of effectiveEntries) {
|
|
3360
3525
|
if (desired.has(entry.path)) continue;
|
|
3361
3526
|
const semanticPlugin = entry.semanticPlugin;
|
|
3362
|
-
const destPath = semanticPlugin ? targetRoot :
|
|
3527
|
+
const destPath = semanticPlugin ? targetRoot : join16(targetRoot, entry.path);
|
|
3363
3528
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3364
3529
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash(entry, destPath, transport);
|
|
3365
3530
|
if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
|
|
@@ -3508,15 +3673,15 @@ async function prepareManagedBlockOperations(desiredOps, adapter, targetRoot, tr
|
|
|
3508
3673
|
return prepared;
|
|
3509
3674
|
}
|
|
3510
3675
|
async function shouldSkipClaudeBridge(adapter, op, targetRoot, transport) {
|
|
3511
|
-
if (adapter.name !== "claude" || op.artifactType !== "instructions" ||
|
|
3676
|
+
if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename8(op.destPath).toLowerCase() !== "claude.md") {
|
|
3512
3677
|
return false;
|
|
3513
3678
|
}
|
|
3514
|
-
const agentsPath =
|
|
3679
|
+
const agentsPath = join16(targetRoot, "AGENTS.md");
|
|
3515
3680
|
return claudeInstructionBridgesAgents(op.destPath, agentsPath, transport);
|
|
3516
3681
|
}
|
|
3517
3682
|
async function warnOnCopilotDoubleRead(adapter, op, targetRoot, transport, options) {
|
|
3518
|
-
if (adapter.name !== "claude" || op.artifactType !== "instructions" ||
|
|
3519
|
-
const agentsPath =
|
|
3683
|
+
if (adapter.name !== "claude" || op.artifactType !== "instructions" || basename8(op.destPath).toLowerCase() !== "claude.md") return;
|
|
3684
|
+
const agentsPath = join16(targetRoot, "AGENTS.md");
|
|
3520
3685
|
if (!await transport.pathExists(agentsPath)) return;
|
|
3521
3686
|
if (await claudeInstructionBridgesAgents(op.destPath, agentsPath, transport)) return;
|
|
3522
3687
|
options.warn?.("CLAUDE.md and AGENTS.md are separate instruction files; if Copilot is active it may read the managed instructions twice.");
|
|
@@ -3600,7 +3765,7 @@ async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
|
|
|
3600
3765
|
if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
|
|
3601
3766
|
if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
|
|
3602
3767
|
if (!packageIdentityMatches(entry, op)) return false;
|
|
3603
|
-
const destPath =
|
|
3768
|
+
const destPath = join16(targetRoot, entry.path);
|
|
3604
3769
|
if (!await transport.pathExists(destPath)) return false;
|
|
3605
3770
|
return await transport.hashPath(destPath) === entry.hash;
|
|
3606
3771
|
}
|
|
@@ -3731,7 +3896,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
|
|
|
3731
3896
|
artifactType: entry.artifactType,
|
|
3732
3897
|
artifactName: entry.artifactName,
|
|
3733
3898
|
kind: entry.kind,
|
|
3734
|
-
destPath: operation?.destPath ??
|
|
3899
|
+
destPath: operation?.destPath ?? join16(targetRoot, entry.path),
|
|
3735
3900
|
relativeDestPath: entry.path,
|
|
3736
3901
|
desiredHash: entry.sourceHash,
|
|
3737
3902
|
currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
|
|
@@ -3799,7 +3964,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
3799
3964
|
}
|
|
3800
3965
|
}
|
|
3801
3966
|
if (artifact.type === "subagents" && target.semantic === "codex-subagent") {
|
|
3802
|
-
const destPath2 =
|
|
3967
|
+
const destPath2 = join16(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
|
|
3803
3968
|
return {
|
|
3804
3969
|
action: "create",
|
|
3805
3970
|
artifactType: artifact.type,
|
|
@@ -3817,7 +3982,7 @@ async function operationForArtifact(artifact, adapter, targetRoot, installationT
|
|
|
3817
3982
|
installName: installName.replace(/\.toml$/i, "")
|
|
3818
3983
|
};
|
|
3819
3984
|
}
|
|
3820
|
-
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);
|
|
3821
3986
|
return {
|
|
3822
3987
|
action: "create",
|
|
3823
3988
|
artifactType: artifact.type,
|
|
@@ -3896,12 +4061,12 @@ function isPendingInstallOperation(operation) {
|
|
|
3896
4061
|
}
|
|
3897
4062
|
|
|
3898
4063
|
// src/install/uninstall.ts
|
|
3899
|
-
import { join as
|
|
4064
|
+
import { join as join17 } from "path";
|
|
3900
4065
|
async function createUninstallPlan(manifest, transport = localTransport) {
|
|
3901
4066
|
const operations = [];
|
|
3902
4067
|
for (const entry of manifest.entries) {
|
|
3903
4068
|
const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
|
|
3904
|
-
const destPath = semanticPlugin ? manifest.targetRoot :
|
|
4069
|
+
const destPath = semanticPlugin ? manifest.targetRoot : join17(manifest.targetRoot, entry.path);
|
|
3905
4070
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3906
4071
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
|
|
3907
4072
|
if (currentHash !== entry.hash) {
|
|
@@ -3972,7 +4137,7 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
|
|
|
3972
4137
|
const operations = [];
|
|
3973
4138
|
for (const entry of manifest.entries) {
|
|
3974
4139
|
const semanticPlugin = "semanticPlugin" in entry ? entry.semanticPlugin : void 0;
|
|
3975
|
-
const destPath = semanticPlugin ? manifest.targetRoot :
|
|
4140
|
+
const destPath = semanticPlugin ? manifest.targetRoot : join17(manifest.targetRoot, entry.path);
|
|
3976
4141
|
if (!semanticPlugin && !await transport.pathExists(destPath)) continue;
|
|
3977
4142
|
const currentHash = semanticPlugin ? entry.hash : await currentEntryHash2(entry, destPath, transport);
|
|
3978
4143
|
const remainingOwners = ownersByPath.get(entry.path) ?? [];
|
|
@@ -4291,17 +4456,17 @@ function ownerChains(lock, nodeId) {
|
|
|
4291
4456
|
}
|
|
4292
4457
|
|
|
4293
4458
|
// src/source/clawhub.ts
|
|
4294
|
-
import { mkdir as
|
|
4295
|
-
import { basename as
|
|
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";
|
|
4296
4461
|
|
|
4297
4462
|
// src/source/local.ts
|
|
4298
4463
|
import { createHash as createHash4 } from "crypto";
|
|
4299
4464
|
import { readdir, stat as stat3 } from "fs/promises";
|
|
4300
|
-
import { basename as
|
|
4465
|
+
import { basename as basename9, join as join19, relative as relative4, resolve as resolve5 } from "path";
|
|
4301
4466
|
|
|
4302
4467
|
// src/model/package.ts
|
|
4303
|
-
import { readFile as
|
|
4304
|
-
import { join as
|
|
4468
|
+
import { readFile as readFile16 } from "fs/promises";
|
|
4469
|
+
import { join as join18 } from "path";
|
|
4305
4470
|
import { parse as parse3, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
|
|
4306
4471
|
import { z as z5 } from "zod";
|
|
4307
4472
|
var legacyArtifactTypeSchema = z5.enum([
|
|
@@ -4384,7 +4549,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
|
|
|
4384
4549
|
var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
|
|
4385
4550
|
async function findPackageManifestPath(root, options = {}) {
|
|
4386
4551
|
for (const name of packageManifestNames) {
|
|
4387
|
-
const candidate =
|
|
4552
|
+
const candidate = join18(root, name);
|
|
4388
4553
|
if (!await pathExists(candidate)) continue;
|
|
4389
4554
|
if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
|
|
4390
4555
|
warnedLegacyManifestPaths.add(candidate);
|
|
@@ -4397,7 +4562,7 @@ async function findPackageManifestPath(root, options = {}) {
|
|
|
4397
4562
|
async function readPackageManifest(root) {
|
|
4398
4563
|
const path = await findPackageManifestPath(root);
|
|
4399
4564
|
if (!path) return void 0;
|
|
4400
|
-
const content = await
|
|
4565
|
+
const content = await readFile16(path, "utf8");
|
|
4401
4566
|
const errors = [];
|
|
4402
4567
|
const parsed = parse3(content, errors, { allowTrailingComma: true, disallowComments: false });
|
|
4403
4568
|
if (errors.length > 0) {
|
|
@@ -4480,29 +4645,29 @@ var LocalSourceDriver = class {
|
|
|
4480
4645
|
}
|
|
4481
4646
|
const artifacts = [];
|
|
4482
4647
|
const root = resolved.resolvedPath;
|
|
4483
|
-
const instructions = await firstExisting([
|
|
4648
|
+
const instructions = await firstExisting([join19(root, "instructions.md"), join19(root, "AGENTS.md")]);
|
|
4484
4649
|
if (instructions) {
|
|
4485
4650
|
artifacts.push({
|
|
4486
4651
|
type: "instructions",
|
|
4487
|
-
name:
|
|
4652
|
+
name: basename9(instructions),
|
|
4488
4653
|
sourcePath: instructions,
|
|
4489
|
-
relativePath:
|
|
4654
|
+
relativePath: basename9(instructions),
|
|
4490
4655
|
kind: "file",
|
|
4491
4656
|
hash: await hashPath(instructions),
|
|
4492
4657
|
packageName: resolved.packageName,
|
|
4493
4658
|
channel: "managed"
|
|
4494
4659
|
});
|
|
4495
4660
|
}
|
|
4496
|
-
const rulesDir =
|
|
4661
|
+
const rulesDir = join19(root, "rules");
|
|
4497
4662
|
if (await pathExists(rulesDir)) {
|
|
4498
4663
|
for (const entry of await sortedDirEntries(rulesDir)) {
|
|
4499
|
-
const full =
|
|
4664
|
+
const full = join19(rulesDir, entry.name);
|
|
4500
4665
|
if (entry.isFile()) {
|
|
4501
4666
|
artifacts.push({
|
|
4502
4667
|
type: "rules",
|
|
4503
4668
|
name: entry.name,
|
|
4504
4669
|
sourcePath: full,
|
|
4505
|
-
relativePath:
|
|
4670
|
+
relativePath: join19("rules", entry.name),
|
|
4506
4671
|
kind: "file",
|
|
4507
4672
|
hash: await hashPath(full),
|
|
4508
4673
|
packageName: resolved.packageName,
|
|
@@ -4511,16 +4676,16 @@ var LocalSourceDriver = class {
|
|
|
4511
4676
|
}
|
|
4512
4677
|
}
|
|
4513
4678
|
}
|
|
4514
|
-
const fragmentsDir =
|
|
4679
|
+
const fragmentsDir = join19(root, "fragments");
|
|
4515
4680
|
if (await pathExists(fragmentsDir)) {
|
|
4516
4681
|
for (const entry of await sortedDirEntries(fragmentsDir)) {
|
|
4517
|
-
const full =
|
|
4682
|
+
const full = join19(fragmentsDir, entry.name);
|
|
4518
4683
|
if (entry.isFile()) {
|
|
4519
4684
|
artifacts.push({
|
|
4520
4685
|
type: "fragments",
|
|
4521
4686
|
name: entry.name,
|
|
4522
4687
|
sourcePath: full,
|
|
4523
|
-
relativePath:
|
|
4688
|
+
relativePath: join19("fragments", entry.name),
|
|
4524
4689
|
kind: "file",
|
|
4525
4690
|
hash: await hashPath(full),
|
|
4526
4691
|
packageName: resolved.packageName,
|
|
@@ -4529,16 +4694,16 @@ var LocalSourceDriver = class {
|
|
|
4529
4694
|
}
|
|
4530
4695
|
}
|
|
4531
4696
|
}
|
|
4532
|
-
const skillsDir =
|
|
4697
|
+
const skillsDir = join19(root, "skills");
|
|
4533
4698
|
if (await pathExists(skillsDir)) {
|
|
4534
4699
|
for (const entry of await sortedDirEntries(skillsDir)) {
|
|
4535
|
-
const full =
|
|
4700
|
+
const full = join19(skillsDir, entry.name);
|
|
4536
4701
|
if (entry.isDirectory()) {
|
|
4537
4702
|
artifacts.push({
|
|
4538
4703
|
type: "skills",
|
|
4539
4704
|
name: entry.name,
|
|
4540
4705
|
sourcePath: full,
|
|
4541
|
-
relativePath:
|
|
4706
|
+
relativePath: join19("skills", entry.name),
|
|
4542
4707
|
kind: "dir",
|
|
4543
4708
|
hash: await hashPath(full),
|
|
4544
4709
|
packageName: resolved.packageName,
|
|
@@ -4549,7 +4714,7 @@ var LocalSourceDriver = class {
|
|
|
4549
4714
|
type: "skills",
|
|
4550
4715
|
name: entry.name.replace(/\.md$/, ""),
|
|
4551
4716
|
sourcePath: full,
|
|
4552
|
-
relativePath:
|
|
4717
|
+
relativePath: join19("skills", entry.name),
|
|
4553
4718
|
kind: "file",
|
|
4554
4719
|
hash: await hashPath(full),
|
|
4555
4720
|
packageName: resolved.packageName,
|
|
@@ -4559,7 +4724,7 @@ var LocalSourceDriver = class {
|
|
|
4559
4724
|
}
|
|
4560
4725
|
}
|
|
4561
4726
|
for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
|
|
4562
|
-
const dir =
|
|
4727
|
+
const dir = join19(root, type);
|
|
4563
4728
|
if (!await pathExists(dir)) continue;
|
|
4564
4729
|
artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
|
|
4565
4730
|
}
|
|
@@ -4575,7 +4740,7 @@ var LocalSourceDriver = class {
|
|
|
4575
4740
|
findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
|
|
4576
4741
|
}
|
|
4577
4742
|
for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
|
|
4578
|
-
if (!await pathExists(
|
|
4743
|
+
if (!await pathExists(join19(artifact.sourcePath, "SKILL.md"))) {
|
|
4579
4744
|
findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
|
|
4580
4745
|
}
|
|
4581
4746
|
}
|
|
@@ -4599,7 +4764,7 @@ async function hashLocalSource(root, manifest) {
|
|
|
4599
4764
|
}
|
|
4600
4765
|
const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
|
|
4601
4766
|
for (const provide of provides) {
|
|
4602
|
-
const full =
|
|
4767
|
+
const full = join19(root, provide.path);
|
|
4603
4768
|
if (!await pathExists(full)) continue;
|
|
4604
4769
|
hash.update("provide\0");
|
|
4605
4770
|
hash.update(provide.type).update("\0");
|
|
@@ -4622,27 +4787,27 @@ async function listFromManifest(root, packageName) {
|
|
|
4622
4787
|
if (!manifest) return [];
|
|
4623
4788
|
const artifacts = [];
|
|
4624
4789
|
for (const provide of manifest.provides) {
|
|
4625
|
-
const full =
|
|
4790
|
+
const full = join19(root, provide.path);
|
|
4626
4791
|
if (!await pathExists(full)) continue;
|
|
4627
4792
|
const stats = await stat3(full);
|
|
4628
4793
|
if (provide.type === "instructions") {
|
|
4629
4794
|
if (stats.isFile()) {
|
|
4630
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
4795
|
+
artifacts.push(await artifactForFile(provide.type, basename9(full), full, provide.path, packageName, provide, manifest, basename9(full)));
|
|
4631
4796
|
}
|
|
4632
4797
|
continue;
|
|
4633
4798
|
}
|
|
4634
4799
|
if (stats.isDirectory()) {
|
|
4635
4800
|
for (const entry of await sortedDirEntries(full)) {
|
|
4636
|
-
const child =
|
|
4801
|
+
const child = join19(full, entry.name);
|
|
4637
4802
|
if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
|
|
4638
|
-
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));
|
|
4639
4804
|
} else if (entry.isFile()) {
|
|
4640
4805
|
const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
|
|
4641
|
-
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));
|
|
4642
4807
|
}
|
|
4643
4808
|
}
|
|
4644
4809
|
} else if (stats.isFile()) {
|
|
4645
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
4810
|
+
artifacts.push(await artifactForFile(provide.type, basename9(full), full, provide.path, packageName, provide, manifest, basename9(full)));
|
|
4646
4811
|
}
|
|
4647
4812
|
}
|
|
4648
4813
|
return artifacts;
|
|
@@ -4650,11 +4815,11 @@ async function listFromManifest(root, packageName) {
|
|
|
4650
4815
|
async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
|
|
4651
4816
|
const artifacts = [];
|
|
4652
4817
|
for (const entry of await sortedDirEntries(dir)) {
|
|
4653
|
-
const full =
|
|
4818
|
+
const full = join19(dir, entry.name);
|
|
4654
4819
|
if (entry.isDirectory()) {
|
|
4655
|
-
artifacts.push(await artifactForDir(type, entry.name, full,
|
|
4820
|
+
artifacts.push(await artifactForDir(type, entry.name, full, join19(relativeRoot, entry.name), packageName));
|
|
4656
4821
|
} else if (entry.isFile()) {
|
|
4657
|
-
artifacts.push(await artifactForFile(type, entry.name, full,
|
|
4822
|
+
artifacts.push(await artifactForFile(type, entry.name, full, join19(relativeRoot, entry.name), packageName));
|
|
4658
4823
|
}
|
|
4659
4824
|
}
|
|
4660
4825
|
return artifacts;
|
|
@@ -4771,7 +4936,7 @@ var ClawHubSourceDriver = class {
|
|
|
4771
4936
|
return this.local.list({ ...resolved, driver: "local" });
|
|
4772
4937
|
}
|
|
4773
4938
|
async scan(resolved) {
|
|
4774
|
-
if (!await pathExists(
|
|
4939
|
+
if (!await pathExists(join20(resolved.resolvedPath, "plugins"))) {
|
|
4775
4940
|
return { ok: false, findings: [{ level: "error", message: "ClawHub source has no generated plugin artifact" }] };
|
|
4776
4941
|
}
|
|
4777
4942
|
return { ok: true, findings: [] };
|
|
@@ -4823,10 +4988,10 @@ async function writeGeneratedPackage(root, packageInfo) {
|
|
|
4823
4988
|
artifact: packageInfo.artifact,
|
|
4824
4989
|
verification: packageInfo.verification
|
|
4825
4990
|
};
|
|
4826
|
-
const pluginPath =
|
|
4991
|
+
const pluginPath = join20(root, "plugins", pluginId, "clawhub.json");
|
|
4827
4992
|
await rm5(root, { recursive: true, force: true });
|
|
4828
|
-
await
|
|
4829
|
-
await
|
|
4993
|
+
await mkdir11(dirname14(pluginPath), { recursive: true });
|
|
4994
|
+
await writeFile13(join20(root, "openpack.json"), `${JSON.stringify({
|
|
4830
4995
|
schemaVersion: 2,
|
|
4831
4996
|
name: `clawhub/${name}`,
|
|
4832
4997
|
version: packageInfo.latestVersion ?? "latest",
|
|
@@ -4842,23 +5007,23 @@ async function writeGeneratedPackage(root, packageInfo) {
|
|
|
4842
5007
|
]
|
|
4843
5008
|
}, null, 2)}
|
|
4844
5009
|
`, "utf8");
|
|
4845
|
-
await
|
|
5010
|
+
await writeFile13(pluginPath, `${JSON.stringify(plugin, null, 2)}
|
|
4846
5011
|
`, "utf8");
|
|
4847
5012
|
}
|
|
4848
5013
|
function installNameFor2(value) {
|
|
4849
|
-
return
|
|
5014
|
+
return basename10(value).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "clawhub-plugin";
|
|
4850
5015
|
}
|
|
4851
5016
|
function cachePathFor(packageName, cacheRoot) {
|
|
4852
|
-
const root = cacheRoot ? resolve6(cacheRoot) :
|
|
5017
|
+
const root = cacheRoot ? resolve6(cacheRoot) : join20(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
4853
5018
|
const slug2 = `clawhub-${packageName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
4854
|
-
return
|
|
5019
|
+
return join20(root, slug2 || "clawhub-package");
|
|
4855
5020
|
}
|
|
4856
5021
|
|
|
4857
5022
|
// src/source/git.ts
|
|
4858
5023
|
import { execFile as execFile4 } from "child_process";
|
|
4859
|
-
import { cp as cp2, mkdir as
|
|
5024
|
+
import { cp as cp2, mkdir as mkdir12, rename as rename3, rm as rm6, writeFile as writeFile14 } from "fs/promises";
|
|
4860
5025
|
import { homedir as homedir2 } from "os";
|
|
4861
|
-
import { basename as
|
|
5026
|
+
import { basename as basename11, dirname as dirname15, join as join21, resolve as resolve7 } from "path";
|
|
4862
5027
|
import { promisify as promisify4 } from "util";
|
|
4863
5028
|
var execFileAsync4 = promisify4(execFile4);
|
|
4864
5029
|
var GitSourceDriver = class {
|
|
@@ -4881,8 +5046,8 @@ var GitSourceDriver = class {
|
|
|
4881
5046
|
async fetch(resolved) {
|
|
4882
5047
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
4883
5048
|
const parsed = parseGitSource(resolved.source);
|
|
4884
|
-
await
|
|
4885
|
-
if (!await pathExists(
|
|
5049
|
+
await mkdir12(resolve7(resolved.resolvedPath, ".."), { recursive: true });
|
|
5050
|
+
if (!await pathExists(join21(resolved.resolvedPath, ".git"))) {
|
|
4886
5051
|
if (resolved.frozenLock) {
|
|
4887
5052
|
throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
|
|
4888
5053
|
}
|
|
@@ -4949,20 +5114,20 @@ function parseGitSource(source) {
|
|
|
4949
5114
|
throw new Error(`Invalid git source: ${source}`);
|
|
4950
5115
|
}
|
|
4951
5116
|
function cachePathFor2(url, cacheRoot) {
|
|
4952
|
-
const root = cacheRoot ? resolve7(cacheRoot) :
|
|
5117
|
+
const root = cacheRoot ? resolve7(cacheRoot) : join21(homedir2(), ".agentwheel", "cache");
|
|
4953
5118
|
const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
4954
|
-
return
|
|
5119
|
+
return join21(root, slug2 || basename11(url));
|
|
4955
5120
|
}
|
|
4956
5121
|
async function git(args) {
|
|
4957
5122
|
return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
|
|
4958
5123
|
}
|
|
4959
5124
|
async function snapshotCheckout(checkoutPath, commit) {
|
|
4960
|
-
const snapshotPath =
|
|
5125
|
+
const snapshotPath = join21(dirname15(checkoutPath), `${basename11(checkoutPath)}-${commit.slice(0, 12)}`);
|
|
4961
5126
|
if (await pathExists(snapshotPath)) return snapshotPath;
|
|
4962
|
-
const tempPath =
|
|
5127
|
+
const tempPath = join21(dirname15(checkoutPath), `${basename11(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
|
|
4963
5128
|
await rm6(tempPath, { recursive: true, force: true });
|
|
4964
5129
|
await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
|
|
4965
|
-
await rm6(
|
|
5130
|
+
await rm6(join21(tempPath, ".git"), { recursive: true, force: true });
|
|
4966
5131
|
try {
|
|
4967
5132
|
await rename3(tempPath, snapshotPath);
|
|
4968
5133
|
} catch (error) {
|
|
@@ -4973,12 +5138,12 @@ async function snapshotCheckout(checkoutPath, commit) {
|
|
|
4973
5138
|
return snapshotPath;
|
|
4974
5139
|
}
|
|
4975
5140
|
async function withFilesystemLock(lockPath, timeoutMs, fn) {
|
|
4976
|
-
await
|
|
5141
|
+
await mkdir12(dirname15(lockPath), { recursive: true });
|
|
4977
5142
|
const started = Date.now();
|
|
4978
5143
|
while (true) {
|
|
4979
5144
|
try {
|
|
4980
|
-
await
|
|
4981
|
-
await
|
|
5145
|
+
await mkdir12(lockPath);
|
|
5146
|
+
await writeFile14(join21(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
4982
5147
|
break;
|
|
4983
5148
|
} catch (error) {
|
|
4984
5149
|
if (!isAlreadyExists2(error)) throw error;
|
|
@@ -4999,8 +5164,8 @@ function isAlreadyExists2(error) {
|
|
|
4999
5164
|
}
|
|
5000
5165
|
|
|
5001
5166
|
// src/source/mcp-registry.ts
|
|
5002
|
-
import { mkdir as
|
|
5003
|
-
import { basename as
|
|
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";
|
|
5004
5169
|
var registryBaseUrl = "https://registry.modelcontextprotocol.io/v0.1";
|
|
5005
5170
|
var sourcePrefix2 = "mcp-registry:";
|
|
5006
5171
|
var McpRegistrySourceDriver = class {
|
|
@@ -5066,7 +5231,7 @@ var McpRegistrySourceDriver = class {
|
|
|
5066
5231
|
return this.local.list({ ...resolved, driver: "local" });
|
|
5067
5232
|
}
|
|
5068
5233
|
async scan(resolved) {
|
|
5069
|
-
if (!await pathExists(
|
|
5234
|
+
if (!await pathExists(join22(resolved.resolvedPath, "mcp"))) {
|
|
5070
5235
|
return { ok: false, findings: [{ level: "error", message: "MCP registry source has no generated mcp artifact" }] };
|
|
5071
5236
|
}
|
|
5072
5237
|
return { ok: true, findings: [] };
|
|
@@ -5106,16 +5271,16 @@ function isSafeHttpUrl(value) {
|
|
|
5106
5271
|
}
|
|
5107
5272
|
async function writeGeneratedPackage2(root, server) {
|
|
5108
5273
|
const serverId = installNameFor3(server.serverName);
|
|
5109
|
-
const mcpPath =
|
|
5110
|
-
await
|
|
5111
|
-
await
|
|
5274
|
+
const mcpPath = join22(root, "mcp", `${serverId}.json`);
|
|
5275
|
+
await mkdir13(dirname16(mcpPath), { recursive: true });
|
|
5276
|
+
await writeFile15(join22(root, "openpack.json"), `${JSON.stringify({
|
|
5112
5277
|
schemaVersion: 2,
|
|
5113
5278
|
name: `mcp-registry/${server.serverName}`,
|
|
5114
5279
|
version: server.version ?? "latest",
|
|
5115
5280
|
provides: [{ type: "mcp", path: "mcp" }]
|
|
5116
5281
|
}, null, 2)}
|
|
5117
5282
|
`, "utf8");
|
|
5118
|
-
await
|
|
5283
|
+
await writeFile15(mcpPath, `${JSON.stringify({
|
|
5119
5284
|
mcpServers: {
|
|
5120
5285
|
[serverId]: {
|
|
5121
5286
|
type: "streamable-http",
|
|
@@ -5126,23 +5291,23 @@ async function writeGeneratedPackage2(root, server) {
|
|
|
5126
5291
|
`, "utf8");
|
|
5127
5292
|
}
|
|
5128
5293
|
function installNameFor3(serverName) {
|
|
5129
|
-
return
|
|
5294
|
+
return basename12(serverName).replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "mcp-server";
|
|
5130
5295
|
}
|
|
5131
5296
|
function cachePathFor3(serverName, cacheRoot) {
|
|
5132
|
-
const root = cacheRoot ? resolve8(cacheRoot) :
|
|
5297
|
+
const root = cacheRoot ? resolve8(cacheRoot) : join22(process.env.HOME ?? ".", ".agentwheel", "cache");
|
|
5133
5298
|
const slug2 = `mcp-registry-${serverName}`.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
5134
|
-
return
|
|
5299
|
+
return join22(root, slug2 || "mcp-registry-server");
|
|
5135
5300
|
}
|
|
5136
5301
|
|
|
5137
5302
|
// src/source/skillkit.ts
|
|
5138
|
-
import { cp as cp3, mkdir as
|
|
5303
|
+
import { cp as cp3, mkdir as mkdir14, readFile as readFile17, rm as rm7 } from "fs/promises";
|
|
5139
5304
|
import { homedir as homedir3 } from "os";
|
|
5140
|
-
import { basename as
|
|
5305
|
+
import { basename as basename14, dirname as dirname18, join as join24, resolve as resolve9 } from "path";
|
|
5141
5306
|
import * as defaultSkillKit from "@skillkit/core";
|
|
5142
5307
|
|
|
5143
5308
|
// src/source/skill-artifacts.ts
|
|
5144
5309
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
5145
|
-
import { basename as
|
|
5310
|
+
import { basename as basename13, dirname as dirname17, extname as extname2, join as join23 } from "path";
|
|
5146
5311
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
5147
5312
|
const artifacts = [];
|
|
5148
5313
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5164,28 +5329,28 @@ async function discoverSkillPaths(root) {
|
|
|
5164
5329
|
async function artifactFromSkillPath(item, packageName) {
|
|
5165
5330
|
const stats = await stat4(item.path);
|
|
5166
5331
|
if (stats.isDirectory()) {
|
|
5167
|
-
const skillMd =
|
|
5332
|
+
const skillMd = join23(item.path, "SKILL.md");
|
|
5168
5333
|
if (!await pathExists(skillMd)) return void 0;
|
|
5169
|
-
const name = sanitizeSkillName(item.name ??
|
|
5334
|
+
const name = sanitizeSkillName(item.name ?? basename13(item.path));
|
|
5170
5335
|
return {
|
|
5171
5336
|
type: "skills",
|
|
5172
5337
|
name,
|
|
5173
5338
|
sourcePath: item.path,
|
|
5174
|
-
relativePath:
|
|
5339
|
+
relativePath: join23("skills", name),
|
|
5175
5340
|
kind: "dir",
|
|
5176
5341
|
hash: await hashPath(item.path),
|
|
5177
5342
|
packageName,
|
|
5178
5343
|
channel: "managed"
|
|
5179
5344
|
};
|
|
5180
5345
|
}
|
|
5181
|
-
if (stats.isFile() &&
|
|
5182
|
-
const dir =
|
|
5183
|
-
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));
|
|
5184
5349
|
return {
|
|
5185
5350
|
type: "skills",
|
|
5186
5351
|
name,
|
|
5187
5352
|
sourcePath: dir,
|
|
5188
|
-
relativePath:
|
|
5353
|
+
relativePath: join23("skills", name),
|
|
5189
5354
|
kind: "dir",
|
|
5190
5355
|
hash: await hashPath(dir),
|
|
5191
5356
|
packageName,
|
|
@@ -5193,12 +5358,12 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
5193
5358
|
};
|
|
5194
5359
|
}
|
|
5195
5360
|
if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
|
|
5196
|
-
const name = sanitizeSkillName(item.name ??
|
|
5361
|
+
const name = sanitizeSkillName(item.name ?? basename13(item.path, ".md"));
|
|
5197
5362
|
return {
|
|
5198
5363
|
type: "skills",
|
|
5199
5364
|
name,
|
|
5200
5365
|
sourcePath: item.path,
|
|
5201
|
-
relativePath:
|
|
5366
|
+
relativePath: join23("skills", `${name}.md`),
|
|
5202
5367
|
kind: "file",
|
|
5203
5368
|
hash: await hashPath(item.path),
|
|
5204
5369
|
packageName,
|
|
@@ -5216,7 +5381,7 @@ async function walk(dir, paths) {
|
|
|
5216
5381
|
}
|
|
5217
5382
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5218
5383
|
if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
|
|
5219
|
-
await walk(
|
|
5384
|
+
await walk(join23(dir, entry.name), paths);
|
|
5220
5385
|
}
|
|
5221
5386
|
}
|
|
5222
5387
|
function sanitizeSkillName(name) {
|
|
@@ -5238,7 +5403,7 @@ var SkillKitSourceDriver = class {
|
|
|
5238
5403
|
driver: this.name,
|
|
5239
5404
|
source,
|
|
5240
5405
|
resolvedPath,
|
|
5241
|
-
packageName: `skillkit/${
|
|
5406
|
+
packageName: `skillkit/${basename14(resolvedPath)}`,
|
|
5242
5407
|
mode: options.mode ?? "pinned",
|
|
5243
5408
|
sourceHash: await hashPath(resolvedPath)
|
|
5244
5409
|
};
|
|
@@ -5272,7 +5437,7 @@ var SkillKitSourceDriver = class {
|
|
|
5272
5437
|
if (!provider?.clone) {
|
|
5273
5438
|
throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
|
|
5274
5439
|
}
|
|
5275
|
-
await
|
|
5440
|
+
await mkdir14(dirname18(resolved.resolvedPath), { recursive: true });
|
|
5276
5441
|
const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
|
|
5277
5442
|
if (!result.success || !result.path) {
|
|
5278
5443
|
throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
|
|
@@ -5316,9 +5481,9 @@ var SkillKitSourceDriver = class {
|
|
|
5316
5481
|
throw new Error("SkillKit translateSkill API unavailable");
|
|
5317
5482
|
}
|
|
5318
5483
|
for (const skill of this.discover(resolved.resolvedPath)) {
|
|
5319
|
-
const skillMd =
|
|
5484
|
+
const skillMd = join24(skill.path, "SKILL.md");
|
|
5320
5485
|
if (await pathExists(skillMd)) {
|
|
5321
|
-
this.core.translateSkill(await
|
|
5486
|
+
this.core.translateSkill(await readFile17(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
|
|
5322
5487
|
}
|
|
5323
5488
|
}
|
|
5324
5489
|
return resolved;
|
|
@@ -5347,8 +5512,8 @@ function normalizeProviderSource(spec) {
|
|
|
5347
5512
|
return spec;
|
|
5348
5513
|
}
|
|
5349
5514
|
function cachePathFor4(spec, cacheRoot) {
|
|
5350
|
-
const root = cacheRoot ? resolve9(cacheRoot) :
|
|
5351
|
-
return
|
|
5515
|
+
const root = cacheRoot ? resolve9(cacheRoot) : join24(homedir3(), ".agentwheel", "cache");
|
|
5516
|
+
return join24(root, "skillkit", packageSlug(spec));
|
|
5352
5517
|
}
|
|
5353
5518
|
function packageSlug(spec) {
|
|
5354
5519
|
return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
|
|
@@ -5361,7 +5526,7 @@ function mapSeverity(severity) {
|
|
|
5361
5526
|
|
|
5362
5527
|
// src/source/vercel-skills.ts
|
|
5363
5528
|
import { stat as stat5 } from "fs/promises";
|
|
5364
|
-
import { basename as
|
|
5529
|
+
import { basename as basename15, join as join25, relative as relative5, resolve as resolve10 } from "path";
|
|
5365
5530
|
var VercelSkillsSourceDriver = class {
|
|
5366
5531
|
name = "vercel-skills";
|
|
5367
5532
|
git = new GitSourceDriver();
|
|
@@ -5376,7 +5541,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
5376
5541
|
driver: this.name,
|
|
5377
5542
|
source,
|
|
5378
5543
|
resolvedPath,
|
|
5379
|
-
packageName: `vercel/${
|
|
5544
|
+
packageName: `vercel/${basename15(resolvedPath)}`,
|
|
5380
5545
|
mode: options.mode ?? "pinned",
|
|
5381
5546
|
sourceHash: await hashPath(resolvedPath)
|
|
5382
5547
|
};
|
|
@@ -5424,7 +5589,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
5424
5589
|
};
|
|
5425
5590
|
async function resolveVercelSkillSubpath(root, subpath) {
|
|
5426
5591
|
if (!subpath) return root;
|
|
5427
|
-
const candidates = [
|
|
5592
|
+
const candidates = [join25(root, subpath), join25(root, "skills", subpath)];
|
|
5428
5593
|
for (const candidate of candidates) {
|
|
5429
5594
|
if (await pathExists(candidate)) return candidate;
|
|
5430
5595
|
}
|
|
@@ -5494,14 +5659,14 @@ function getSourceDriver(name = "local") {
|
|
|
5494
5659
|
}
|
|
5495
5660
|
|
|
5496
5661
|
// src/staging/staging.ts
|
|
5497
|
-
import { chmod, cp as cp5, mkdir as
|
|
5498
|
-
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";
|
|
5499
5664
|
import { tmpdir as tmpdir4 } from "os";
|
|
5500
5665
|
|
|
5501
5666
|
// src/compose/markdown.ts
|
|
5502
5667
|
import { createHash as createHash5 } from "crypto";
|
|
5503
|
-
import { readdir as readdir3, readFile as
|
|
5504
|
-
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";
|
|
5505
5670
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5506
5671
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
5507
5672
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -5516,7 +5681,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5516
5681
|
const composedFrom = [];
|
|
5517
5682
|
for (const file of files) {
|
|
5518
5683
|
const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
|
|
5519
|
-
if (result.changed) await
|
|
5684
|
+
if (result.changed) await writeFile16(file, result.content, "utf8");
|
|
5520
5685
|
composedFrom.push(...result.composedFrom);
|
|
5521
5686
|
}
|
|
5522
5687
|
const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
|
|
@@ -5538,7 +5703,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
5538
5703
|
}
|
|
5539
5704
|
}
|
|
5540
5705
|
async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
|
|
5541
|
-
const raw = await
|
|
5706
|
+
const raw = await readFile18(file, "utf8");
|
|
5542
5707
|
const owner = ownerSelector(packageRoot, file, options.nodeId);
|
|
5543
5708
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
5544
5709
|
let content = expanded.content;
|
|
@@ -5631,7 +5796,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
5631
5796
|
if (!stats.isFile()) {
|
|
5632
5797
|
throw new Error(`OpenPack include is not a file: ${displaySelector}`);
|
|
5633
5798
|
}
|
|
5634
|
-
const raw = sourceContent ?? await
|
|
5799
|
+
const raw = sourceContent ?? await readFile18(sourcePath, "utf8");
|
|
5635
5800
|
const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
|
|
5636
5801
|
const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
|
|
5637
5802
|
...childOptions,
|
|
@@ -5707,7 +5872,7 @@ async function listMarkdownFiles(root) {
|
|
|
5707
5872
|
const out = [];
|
|
5708
5873
|
async function walk2(dir) {
|
|
5709
5874
|
for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5710
|
-
const full =
|
|
5875
|
+
const full = join26(dir, entry.name);
|
|
5711
5876
|
if (entry.isDirectory()) {
|
|
5712
5877
|
await walk2(full);
|
|
5713
5878
|
} else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
|
|
@@ -5721,7 +5886,7 @@ async function listMarkdownFiles(root) {
|
|
|
5721
5886
|
function composeEntriesForFile(artifact, file) {
|
|
5722
5887
|
if (!artifact.compose?.length) return [];
|
|
5723
5888
|
if (artifact.kind === "file") return [resolve11(artifact.stagedPath ?? artifact.sourcePath), resolve11(file)].every(Boolean) && resolve11(artifact.stagedPath ?? artifact.sourcePath) === resolve11(file) ? artifact.compose : [];
|
|
5724
|
-
return
|
|
5889
|
+
return basename16(file) === "SKILL.md" && dirname19(file) === resolve11(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
|
|
5725
5890
|
}
|
|
5726
5891
|
function orderedForExpansion(artifacts) {
|
|
5727
5892
|
return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
|
|
@@ -5767,8 +5932,8 @@ function artifactPathMap(artifacts) {
|
|
|
5767
5932
|
}
|
|
5768
5933
|
|
|
5769
5934
|
// src/staging/customize.ts
|
|
5770
|
-
import { cp as cp4, mkdir as
|
|
5771
|
-
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";
|
|
5772
5937
|
async function applyCustomizations(artifacts, options) {
|
|
5773
5938
|
let next = [...artifacts];
|
|
5774
5939
|
next = await applyReplacements2(next, options, "override", installableArtifactTypes());
|
|
@@ -5784,16 +5949,16 @@ async function applyFragmentCustomizations(artifacts, options) {
|
|
|
5784
5949
|
return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
|
|
5785
5950
|
}
|
|
5786
5951
|
async function applyInstructionOverlay(artifacts, options) {
|
|
5787
|
-
const overlayPath =
|
|
5952
|
+
const overlayPath = join27(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
|
|
5788
5953
|
if (!await pathExists(overlayPath)) return artifacts;
|
|
5789
5954
|
const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
|
|
5790
5955
|
if (index < 0) return artifacts;
|
|
5791
5956
|
const artifact = artifacts[index];
|
|
5792
|
-
const managed = await
|
|
5793
|
-
const local = await
|
|
5794
|
-
const composedPath =
|
|
5795
|
-
await
|
|
5796
|
-
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(
|
|
5797
5962
|
composedPath,
|
|
5798
5963
|
[
|
|
5799
5964
|
"<!-- BEGIN agentwheel managed: upstream -->",
|
|
@@ -5819,19 +5984,19 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
5819
5984
|
return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
|
|
5820
5985
|
}
|
|
5821
5986
|
async function applyAdditions(artifacts, options) {
|
|
5822
|
-
const additionsRoot =
|
|
5823
|
-
const rulesRoot =
|
|
5987
|
+
const additionsRoot = join27(options.workspaceRoot, ".agentwheel", "additions");
|
|
5988
|
+
const rulesRoot = join27(additionsRoot, "rules");
|
|
5824
5989
|
if (!await pathExists(rulesRoot)) return artifacts;
|
|
5825
5990
|
const additions = [];
|
|
5826
5991
|
for (const entry of await sortedDirEntries2(rulesRoot)) {
|
|
5827
|
-
const full =
|
|
5992
|
+
const full = join27(rulesRoot, entry.name);
|
|
5828
5993
|
if (!entry.isFile()) continue;
|
|
5829
5994
|
additions.push({
|
|
5830
5995
|
type: "rules",
|
|
5831
5996
|
name: entry.name,
|
|
5832
5997
|
sourcePath: full,
|
|
5833
5998
|
stagedPath: full,
|
|
5834
|
-
relativePath:
|
|
5999
|
+
relativePath: join27("additions", "rules", entry.name),
|
|
5835
6000
|
kind: "file",
|
|
5836
6001
|
hash: await hashPath(full),
|
|
5837
6002
|
packageName: options.packageName,
|
|
@@ -5855,17 +6020,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5855
6020
|
);
|
|
5856
6021
|
}
|
|
5857
6022
|
for (const type of artifactTypes) {
|
|
5858
|
-
const typeRoot =
|
|
6023
|
+
const typeRoot = join27(root, type);
|
|
5859
6024
|
if (!await pathExists(typeRoot)) continue;
|
|
5860
6025
|
for (const entry of await sortedDirEntries2(typeRoot)) {
|
|
5861
6026
|
const artifactMapKey = `${type}:${entry.name}`;
|
|
5862
6027
|
if (seen.has(artifactMapKey)) continue;
|
|
5863
6028
|
seen.add(artifactMapKey);
|
|
5864
|
-
const full =
|
|
6029
|
+
const full = join27(typeRoot, entry.name);
|
|
5865
6030
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
5866
6031
|
const existing = byKey.get(artifactMapKey);
|
|
5867
|
-
const stagedPath =
|
|
5868
|
-
await
|
|
6032
|
+
const stagedPath = join27(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
6033
|
+
await mkdir15(dirname20(stagedPath), { recursive: true });
|
|
5869
6034
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
5870
6035
|
byKey.set(artifactMapKey, {
|
|
5871
6036
|
...existing,
|
|
@@ -5873,7 +6038,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
5873
6038
|
name: entry.name,
|
|
5874
6039
|
sourcePath: full,
|
|
5875
6040
|
stagedPath,
|
|
5876
|
-
relativePath: existing?.relativePath ??
|
|
6041
|
+
relativePath: existing?.relativePath ?? join27(type, entry.name),
|
|
5877
6042
|
kind: artifactKind,
|
|
5878
6043
|
hash: await hashPath(stagedPath),
|
|
5879
6044
|
packageName,
|
|
@@ -5888,13 +6053,13 @@ function replacementRoots(options, channel) {
|
|
|
5888
6053
|
const stateDir = channel === "override" ? "overrides" : "ejected";
|
|
5889
6054
|
const roots = [];
|
|
5890
6055
|
if (options.graphNodeId) {
|
|
5891
|
-
roots.push({ root:
|
|
6056
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
|
|
5892
6057
|
}
|
|
5893
6058
|
if (options.packageName && options.packageVersion) {
|
|
5894
|
-
roots.push({ root:
|
|
6059
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
|
|
5895
6060
|
}
|
|
5896
6061
|
if (options.packageName) {
|
|
5897
|
-
roots.push({ root:
|
|
6062
|
+
roots.push({ root: join27(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
|
|
5898
6063
|
}
|
|
5899
6064
|
return roots;
|
|
5900
6065
|
}
|
|
@@ -5921,15 +6086,15 @@ async function stageResolvedSourceRaw(driver, resolved) {
|
|
|
5921
6086
|
return stageResolvedArtifactsRaw(resolved, artifacts);
|
|
5922
6087
|
}
|
|
5923
6088
|
async function stageResolvedArtifactsRaw(resolved, artifacts) {
|
|
5924
|
-
const root = await mkdtemp3(
|
|
6089
|
+
const root = await mkdtemp3(join28(tmpdir4(), "agentwheel-stage-"));
|
|
5925
6090
|
const stagedArtifacts = [];
|
|
5926
6091
|
for (const artifact of artifacts) {
|
|
5927
|
-
const stagedPath =
|
|
5928
|
-
await
|
|
6092
|
+
const stagedPath = join28(root, artifact.relativePath);
|
|
6093
|
+
await mkdir16(dirname21(stagedPath), { recursive: true });
|
|
5929
6094
|
await cp5(artifact.sourcePath, stagedPath, {
|
|
5930
6095
|
recursive: artifact.kind === "dir",
|
|
5931
6096
|
dereference: true,
|
|
5932
|
-
filter: (path) => !isIgnoredGeneratedEntry(
|
|
6097
|
+
filter: (path) => !isIgnoredGeneratedEntry(basename18(path))
|
|
5933
6098
|
});
|
|
5934
6099
|
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
5935
6100
|
stagedArtifacts.push({
|
|
@@ -5958,7 +6123,8 @@ async function renderStagedBundle(bundle, options = {}) {
|
|
|
5958
6123
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(options.select, options.skills) ?? []);
|
|
5959
6124
|
const runtimeArtifacts = options.adapter ? filterArtifactsByRuntime(selectedArtifacts, options.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
5960
6125
|
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, root, options.adapter);
|
|
5961
|
-
const
|
|
6126
|
+
const openClawRenderedArtifacts = await renderOpenClawSubagents(codexRenderedArtifacts, root, options.adapter);
|
|
6127
|
+
const renderedArtifacts = await renderCopilotArtifacts(openClawRenderedArtifacts, root, options.adapter);
|
|
5962
6128
|
const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(renderedArtifacts, {
|
|
5963
6129
|
workspaceRoot: options.workspaceRoot,
|
|
5964
6130
|
adapter: options.adapter,
|
|
@@ -6010,16 +6176,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
|
|
|
6010
6176
|
}
|
|
6011
6177
|
for (const asset of artifact.assets) {
|
|
6012
6178
|
const source = resolvePackagePath(packageRoot, asset.from);
|
|
6013
|
-
const dest =
|
|
6179
|
+
const dest = join28(stagedPath, asset.into);
|
|
6014
6180
|
await copyAsset(asset, source, dest);
|
|
6015
6181
|
}
|
|
6016
6182
|
}
|
|
6017
6183
|
async function copyAsset(asset, source, dest) {
|
|
6018
6184
|
const sourceStats = await stat8(source);
|
|
6019
6185
|
if (sourceStats.isFile()) {
|
|
6020
|
-
if (matchesAny(
|
|
6021
|
-
await
|
|
6022
|
-
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);
|
|
6023
6189
|
}
|
|
6024
6190
|
return;
|
|
6025
6191
|
}
|
|
@@ -6027,19 +6193,19 @@ async function copyAsset(asset, source, dest) {
|
|
|
6027
6193
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
6028
6194
|
}
|
|
6029
6195
|
if (!asset.include?.length) {
|
|
6030
|
-
await
|
|
6196
|
+
await mkdir16(dirname21(dest), { recursive: true });
|
|
6031
6197
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
6032
6198
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
6033
6199
|
return;
|
|
6034
6200
|
}
|
|
6035
6201
|
for (const file of await listFiles(source)) {
|
|
6036
6202
|
const rel = relative7(source, file).replaceAll("\\", "/");
|
|
6037
|
-
if (!matchesAny(rel, asset.include) && !matchesAny(
|
|
6038
|
-
await copyAssetFile(file,
|
|
6203
|
+
if (!matchesAny(rel, asset.include) && !matchesAny(basename18(file), asset.include)) continue;
|
|
6204
|
+
await copyAssetFile(file, join28(dest, rel), asset);
|
|
6039
6205
|
}
|
|
6040
6206
|
}
|
|
6041
6207
|
async function copyAssetFile(source, dest, asset) {
|
|
6042
|
-
await
|
|
6208
|
+
await mkdir16(dirname21(dest), { recursive: true });
|
|
6043
6209
|
await cp5(source, dest, { dereference: true });
|
|
6044
6210
|
if (asset.mode === "copy") await chmod(dest, 420);
|
|
6045
6211
|
}
|
|
@@ -6055,7 +6221,7 @@ async function listFiles(root) {
|
|
|
6055
6221
|
const out = [];
|
|
6056
6222
|
async function walk2(dir) {
|
|
6057
6223
|
for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6058
|
-
const full =
|
|
6224
|
+
const full = join28(dir, entry.name);
|
|
6059
6225
|
if (entry.isDirectory()) {
|
|
6060
6226
|
await walk2(full);
|
|
6061
6227
|
} else if (entry.isFile()) {
|
|
@@ -6074,7 +6240,7 @@ async function normalizeCopiedModes(path) {
|
|
|
6074
6240
|
}
|
|
6075
6241
|
if (!stats.isDirectory()) return;
|
|
6076
6242
|
for (const entry of await readdir5(path, { withFileTypes: true })) {
|
|
6077
|
-
await normalizeCopiedModes(
|
|
6243
|
+
await normalizeCopiedModes(join28(path, entry.name));
|
|
6078
6244
|
}
|
|
6079
6245
|
}
|
|
6080
6246
|
function matchesAny(path, patterns) {
|
|
@@ -6087,9 +6253,9 @@ function matchesGlob(path, pattern) {
|
|
|
6087
6253
|
}
|
|
6088
6254
|
|
|
6089
6255
|
// src/model/workspace.ts
|
|
6090
|
-
import { readFile as
|
|
6256
|
+
import { readFile as readFile20 } from "fs/promises";
|
|
6091
6257
|
import { homedir as homedir4 } from "os";
|
|
6092
|
-
import { dirname as
|
|
6258
|
+
import { dirname as dirname22, join as join29, resolve as resolve13 } from "path";
|
|
6093
6259
|
import { z as z6 } from "zod";
|
|
6094
6260
|
var workspacePackageSchema = z6.object({
|
|
6095
6261
|
name: z6.string().min(1),
|
|
@@ -6160,12 +6326,12 @@ var workspaceConfigSchema = z6.object({
|
|
|
6160
6326
|
agents: z6.record(z6.string(), workspaceAgentSchema).default({})
|
|
6161
6327
|
});
|
|
6162
6328
|
function workspaceConfigPath(workspaceRoot) {
|
|
6163
|
-
return
|
|
6329
|
+
return join29(workspaceRoot, ".agentwheel", "config.json");
|
|
6164
6330
|
}
|
|
6165
6331
|
async function readWorkspaceConfig(workspaceRoot) {
|
|
6166
6332
|
const path = workspaceConfigPath(workspaceRoot);
|
|
6167
6333
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
6168
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6334
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile20(path, "utf8")));
|
|
6169
6335
|
}
|
|
6170
6336
|
async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
6171
6337
|
await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
|
|
@@ -6178,13 +6344,13 @@ function upsertPackage(config, entry) {
|
|
|
6178
6344
|
return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
|
|
6179
6345
|
}
|
|
6180
6346
|
function globalWorkspaceConfigPath(globalRoot = homedir4()) {
|
|
6181
|
-
return
|
|
6347
|
+
return join29(globalRoot, ".agentwheel", "config.json");
|
|
6182
6348
|
}
|
|
6183
6349
|
async function findWorkspaceRoot(start = process.cwd()) {
|
|
6184
6350
|
let current = resolve13(start);
|
|
6185
6351
|
while (true) {
|
|
6186
6352
|
if (await pathExists(workspaceConfigPath(current))) return current;
|
|
6187
|
-
const parent =
|
|
6353
|
+
const parent = dirname22(current);
|
|
6188
6354
|
if (parent === current) return resolve13(start);
|
|
6189
6355
|
current = parent;
|
|
6190
6356
|
}
|
|
@@ -6220,7 +6386,7 @@ function emptyWorkspaceConfig() {
|
|
|
6220
6386
|
}
|
|
6221
6387
|
async function readConfigPath(path) {
|
|
6222
6388
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
6223
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
6389
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile20(path, "utf8")));
|
|
6224
6390
|
}
|
|
6225
6391
|
function mergeWorkspaceTrust(global, project) {
|
|
6226
6392
|
return {
|
|
@@ -6235,23 +6401,23 @@ function sortedUnique2(values) {
|
|
|
6235
6401
|
}
|
|
6236
6402
|
|
|
6237
6403
|
// src/lifecycle/customization.ts
|
|
6238
|
-
import { appendFile, cp as cp6, mkdir as
|
|
6239
|
-
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";
|
|
6240
6406
|
|
|
6241
6407
|
// src/resolve/graph.ts
|
|
6242
6408
|
import { createHash as createHash6 } from "crypto";
|
|
6243
|
-
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";
|
|
6244
6410
|
import { tmpdir as tmpdir5 } from "os";
|
|
6245
|
-
import { basename as
|
|
6411
|
+
import { basename as basename19, extname as extname4, join as join31 } from "path";
|
|
6246
6412
|
|
|
6247
6413
|
// src/resolve/identity.ts
|
|
6248
6414
|
import { homedir as homedir6 } from "os";
|
|
6249
6415
|
import { resolve as resolve15 } from "path";
|
|
6250
6416
|
|
|
6251
6417
|
// src/registry/client.ts
|
|
6252
|
-
import { readFile as
|
|
6418
|
+
import { readFile as readFile21, rm as rm8, stat as stat9 } from "fs/promises";
|
|
6253
6419
|
import { homedir as homedir5 } from "os";
|
|
6254
|
-
import { dirname as
|
|
6420
|
+
import { dirname as dirname23, join as join30, resolve as resolve14 } from "path";
|
|
6255
6421
|
import { fileURLToPath } from "url";
|
|
6256
6422
|
|
|
6257
6423
|
// src/model/registry.ts
|
|
@@ -6355,7 +6521,7 @@ var RegistryClient = class {
|
|
|
6355
6521
|
}
|
|
6356
6522
|
async readCache() {
|
|
6357
6523
|
if (!await pathExists(this.cachePath)) return void 0;
|
|
6358
|
-
return registryCacheSchema.parse(JSON.parse(await
|
|
6524
|
+
return registryCacheSchema.parse(JSON.parse(await readFile21(this.cachePath, "utf8")));
|
|
6359
6525
|
}
|
|
6360
6526
|
isExpired(cache, ttlMs) {
|
|
6361
6527
|
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
@@ -6374,10 +6540,10 @@ var RegistryClient = class {
|
|
|
6374
6540
|
if (await pathExists(filePath)) {
|
|
6375
6541
|
const fullPath = resolve14(filePath);
|
|
6376
6542
|
const stats = await stat9(fullPath);
|
|
6377
|
-
return
|
|
6543
|
+
return readFile21(stats.isDirectory() ? join30(fullPath, "index.json") : fullPath, "utf8");
|
|
6378
6544
|
}
|
|
6379
|
-
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot:
|
|
6380
|
-
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");
|
|
6381
6547
|
}
|
|
6382
6548
|
warnCompatibility(entries) {
|
|
6383
6549
|
for (const entry of entries) {
|
|
@@ -6415,7 +6581,7 @@ function mergeIndexes(indexes) {
|
|
|
6415
6581
|
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
6416
6582
|
}
|
|
6417
6583
|
function defaultRegistryCachePath() {
|
|
6418
|
-
return
|
|
6584
|
+
return join30(homedir5(), ".agentwheel", "registry-cache.json");
|
|
6419
6585
|
}
|
|
6420
6586
|
function sameSources(a, b) {
|
|
6421
6587
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
@@ -6665,7 +6831,7 @@ function compareSemver(a, b) {
|
|
|
6665
6831
|
var cacheLocks = /* @__PURE__ */ new Map();
|
|
6666
6832
|
async function resolveDependencyGraph(roots, options) {
|
|
6667
6833
|
if (roots.length === 0) throw new Error("At least one graph root is required.");
|
|
6668
|
-
const graphRoot = await mkdtemp4(
|
|
6834
|
+
const graphRoot = await mkdtemp4(join31(tmpdir5(), "agentwheel-graph-"));
|
|
6669
6835
|
const fetchCache = /* @__PURE__ */ new Map();
|
|
6670
6836
|
const nodesByKey = /* @__PURE__ */ new Map();
|
|
6671
6837
|
const rootResults = [];
|
|
@@ -7128,7 +7294,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
|
|
|
7128
7294
|
const file = stack.shift();
|
|
7129
7295
|
if (scanned.has(file)) continue;
|
|
7130
7296
|
scanned.add(file);
|
|
7131
|
-
const content = await
|
|
7297
|
+
const content = await readFile22(file, "utf8");
|
|
7132
7298
|
for (const include of extractOpenPackIncludeSelectors(content)) {
|
|
7133
7299
|
await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
|
|
7134
7300
|
}
|
|
@@ -7171,7 +7337,7 @@ async function listMarkdownFiles2(root) {
|
|
|
7171
7337
|
const out = [];
|
|
7172
7338
|
async function walk2(dir) {
|
|
7173
7339
|
for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
7174
|
-
const full =
|
|
7340
|
+
const full = join31(dir, entry.name);
|
|
7175
7341
|
if (entry.isDirectory()) {
|
|
7176
7342
|
await walk2(full);
|
|
7177
7343
|
} else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
|
|
@@ -7206,7 +7372,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
7206
7372
|
const promise = (async () => {
|
|
7207
7373
|
const driver = getSourceDriver(normalized.driver);
|
|
7208
7374
|
const resolved = await driver.resolve(normalized.source, {
|
|
7209
|
-
cacheRoot: options.cacheRoot ??
|
|
7375
|
+
cacheRoot: options.cacheRoot ?? join31(options.workspaceRoot, ".agentwheel", "cache"),
|
|
7210
7376
|
mode,
|
|
7211
7377
|
ref: refOverride ?? normalized.requestedRef,
|
|
7212
7378
|
frozenLock: hardLockedCheckout
|
|
@@ -7216,7 +7382,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
7216
7382
|
const exported = await driver.export(translated);
|
|
7217
7383
|
const manifest = await readPackageManifest(exported.resolvedPath);
|
|
7218
7384
|
const artifacts = await driver.list(exported);
|
|
7219
|
-
const name = manifest?.name ?? exported.packageName ??
|
|
7385
|
+
const name = manifest?.name ?? exported.packageName ?? basename19(exported.resolvedPath);
|
|
7220
7386
|
const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
|
|
7221
7387
|
const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
|
|
7222
7388
|
return {
|
|
@@ -7413,8 +7579,8 @@ async function mapLimit(items, limit, fn) {
|
|
|
7413
7579
|
|
|
7414
7580
|
// src/lifecycle/customization.ts
|
|
7415
7581
|
async function remember(workspaceRoot, runtime, text) {
|
|
7416
|
-
const overlayPath =
|
|
7417
|
-
await
|
|
7582
|
+
const overlayPath = join32(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
7583
|
+
await mkdir17(dirname24(overlayPath), { recursive: true });
|
|
7418
7584
|
await appendFile(overlayPath, `${text.trim()}
|
|
7419
7585
|
`, "utf8");
|
|
7420
7586
|
return { overlayPath };
|
|
@@ -7437,8 +7603,8 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
7437
7603
|
throw new Error(`Artifact not found: ${item}`);
|
|
7438
7604
|
}
|
|
7439
7605
|
const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
|
|
7440
|
-
const ejectedPath =
|
|
7441
|
-
await
|
|
7606
|
+
const ejectedPath = join32(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
|
|
7607
|
+
await mkdir17(dirname24(ejectedPath), { recursive: true });
|
|
7442
7608
|
await rm9(ejectedPath, { recursive: true, force: true });
|
|
7443
7609
|
await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
7444
7610
|
return {
|
|
@@ -7480,7 +7646,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
|
|
|
7480
7646
|
const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
|
|
7481
7647
|
const bundle = await stageSource(driver, normalized.source, {
|
|
7482
7648
|
adapter,
|
|
7483
|
-
cacheRoot:
|
|
7649
|
+
cacheRoot: join32(workspaceRoot, ".agentwheel", "cache"),
|
|
7484
7650
|
mode: pkg.mode,
|
|
7485
7651
|
ref: normalized.requestedRef ?? pkg.requestedRef
|
|
7486
7652
|
});
|
|
@@ -7531,8 +7697,8 @@ import { rm as rm10 } from "fs/promises";
|
|
|
7531
7697
|
|
|
7532
7698
|
// src/lifecycle/source-plan.ts
|
|
7533
7699
|
import { createHash as createHash8 } from "crypto";
|
|
7534
|
-
import { mkdir as
|
|
7535
|
-
import { dirname as
|
|
7700
|
+
import { mkdir as mkdir19 } from "fs/promises";
|
|
7701
|
+
import { dirname as dirname26, join as join35, resolve as resolve16 } from "path";
|
|
7536
7702
|
|
|
7537
7703
|
// src/resolve/graph-diff.ts
|
|
7538
7704
|
function diffGraphLocks(previous, next) {
|
|
@@ -7654,11 +7820,11 @@ function short(hash) {
|
|
|
7654
7820
|
|
|
7655
7821
|
// src/resolve/render.ts
|
|
7656
7822
|
import { createHash as createHash7 } from "crypto";
|
|
7657
|
-
import { readFile as
|
|
7823
|
+
import { readFile as readFile23, mkdtemp as mkdtemp5 } from "fs/promises";
|
|
7658
7824
|
import { tmpdir as tmpdir6 } from "os";
|
|
7659
|
-
import { join as
|
|
7825
|
+
import { join as join33 } from "path";
|
|
7660
7826
|
async function renderGraphForTarget(graph, targetContext = {}) {
|
|
7661
|
-
const root = await mkdtemp5(
|
|
7827
|
+
const root = await mkdtemp5(join33(tmpdir6(), "agentwheel-render-"));
|
|
7662
7828
|
const artifacts = [];
|
|
7663
7829
|
const stagedNodes = /* @__PURE__ */ new Map();
|
|
7664
7830
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
@@ -7731,7 +7897,8 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
7731
7897
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(rawNode.node.selected) ?? []);
|
|
7732
7898
|
const runtimeArtifacts = targetContext.adapter ? filterArtifactsByRuntime2(selectedArtifacts, targetContext.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
7733
7899
|
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, staged.root, targetContext.adapter);
|
|
7734
|
-
const
|
|
7900
|
+
const openClawRenderedArtifacts = await renderOpenClawSubagents(codexRenderedArtifacts, staged.root, targetContext.adapter);
|
|
7901
|
+
const runtimeRenderedArtifacts = await renderCopilotArtifacts(openClawRenderedArtifacts, staged.root, targetContext.adapter);
|
|
7735
7902
|
const renderedArtifacts = targetContext.workspaceRoot && targetContext.adapter ? await applyCustomizations(runtimeRenderedArtifacts, {
|
|
7736
7903
|
workspaceRoot: targetContext.workspaceRoot,
|
|
7737
7904
|
adapter: targetContext.adapter,
|
|
@@ -7778,7 +7945,7 @@ async function artifactContentMap(artifacts) {
|
|
|
7778
7945
|
const out = /* @__PURE__ */ new Map();
|
|
7779
7946
|
for (const artifact of artifacts) {
|
|
7780
7947
|
if (artifact.kind !== "file") continue;
|
|
7781
|
-
out.set(artifact.relativePath.replaceAll("\\", "/"), await
|
|
7948
|
+
out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile23(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
|
|
7782
7949
|
}
|
|
7783
7950
|
return out;
|
|
7784
7951
|
}
|
|
@@ -8041,9 +8208,9 @@ function lockArtifactFor(artifact) {
|
|
|
8041
8208
|
}
|
|
8042
8209
|
|
|
8043
8210
|
// src/lifecycle/trust.ts
|
|
8044
|
-
import { mkdir as
|
|
8211
|
+
import { mkdir as mkdir18, readFile as readFile24 } from "fs/promises";
|
|
8045
8212
|
import { homedir as homedir7 } from "os";
|
|
8046
|
-
import { dirname as
|
|
8213
|
+
import { dirname as dirname25, join as join34 } from "path";
|
|
8047
8214
|
import { z as z8 } from "zod";
|
|
8048
8215
|
var trustStoreSchema = z8.object({
|
|
8049
8216
|
version: z8.literal(1),
|
|
@@ -8117,14 +8284,14 @@ function sortedUnique4(values) {
|
|
|
8117
8284
|
}
|
|
8118
8285
|
async function readTrustStore(path) {
|
|
8119
8286
|
if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
|
|
8120
|
-
return trustStoreSchema.parse(JSON.parse(await
|
|
8287
|
+
return trustStoreSchema.parse(JSON.parse(await readFile24(path, "utf8")));
|
|
8121
8288
|
}
|
|
8122
8289
|
async function writeTrustStore(path, store) {
|
|
8123
|
-
await
|
|
8290
|
+
await mkdir18(dirname25(path), { recursive: true });
|
|
8124
8291
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
8125
8292
|
}
|
|
8126
8293
|
function defaultTrustStorePath() {
|
|
8127
|
-
return process.env.AGENTWHEEL_TRUST_STORE ??
|
|
8294
|
+
return process.env.AGENTWHEEL_TRUST_STORE ?? join34(homedir7(), ".agentwheel", "trust.json");
|
|
8128
8295
|
}
|
|
8129
8296
|
|
|
8130
8297
|
// src/lifecycle/source-plan.ts
|
|
@@ -8162,7 +8329,7 @@ async function createGraphSourcePlan(options) {
|
|
|
8162
8329
|
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
8163
8330
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
8164
8331
|
workspaceRoot,
|
|
8165
|
-
cacheRoot:
|
|
8332
|
+
cacheRoot: join35(workspaceRoot, ".agentwheel", "cache"),
|
|
8166
8333
|
registryClient,
|
|
8167
8334
|
noDeps: options.noDeps,
|
|
8168
8335
|
includeSuggestions: options.includeSuggestions,
|
|
@@ -8262,7 +8429,7 @@ async function readExistingGraphLock(path) {
|
|
|
8262
8429
|
return readGraphLock(path);
|
|
8263
8430
|
}
|
|
8264
8431
|
function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
|
|
8265
|
-
return
|
|
8432
|
+
return join35(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
|
|
8266
8433
|
}
|
|
8267
8434
|
function sanitizePathSegment(value) {
|
|
8268
8435
|
return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
|
|
@@ -8326,7 +8493,7 @@ ${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
|
8326
8493
|
}
|
|
8327
8494
|
|
|
8328
8495
|
// src/runtime/target.ts
|
|
8329
|
-
import { basename as
|
|
8496
|
+
import { basename as basename20, dirname as dirname27, join as join36, resolve as resolve17 } from "path";
|
|
8330
8497
|
var runtimeMarkers = [
|
|
8331
8498
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
8332
8499
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -8437,9 +8604,9 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
|
|
|
8437
8604
|
for (const marker of runtimeMarkers) {
|
|
8438
8605
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
8439
8606
|
for (const dir of marker.dirs) {
|
|
8440
|
-
if (
|
|
8441
|
-
matches.push({ adapter: marker.adapter, targetRoot:
|
|
8442
|
-
} 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))) {
|
|
8443
8610
|
matches.push({ adapter: marker.adapter, targetRoot: root });
|
|
8444
8611
|
}
|
|
8445
8612
|
}
|
|
@@ -8478,7 +8645,7 @@ function dedupeTargets(matches) {
|
|
|
8478
8645
|
function runtimeScanRoot(request) {
|
|
8479
8646
|
const root = resolve17(request.targetRoot ?? request.cwd ?? process.cwd());
|
|
8480
8647
|
if (request.targetRoot) return root;
|
|
8481
|
-
return runtimeMarkers.some((marker) => marker.dirs.includes(
|
|
8648
|
+
return runtimeMarkers.some((marker) => marker.dirs.includes(basename20(root))) ? dirname27(root) : root;
|
|
8482
8649
|
}
|
|
8483
8650
|
|
|
8484
8651
|
// src/lifecycle/profile.ts
|
|
@@ -8762,9 +8929,9 @@ function shellQuoteArg(value) {
|
|
|
8762
8929
|
}
|
|
8763
8930
|
|
|
8764
8931
|
// src/cli/update-check.ts
|
|
8765
|
-
import { mkdir as
|
|
8932
|
+
import { mkdir as mkdir20, readFile as readFile25, writeFile as writeFile18 } from "fs/promises";
|
|
8766
8933
|
import { homedir as homedir8 } from "os";
|
|
8767
|
-
import { dirname as
|
|
8934
|
+
import { dirname as dirname28, join as join37 } from "path";
|
|
8768
8935
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
8769
8936
|
var DEFAULT_TIMEOUT_MS = 300;
|
|
8770
8937
|
var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
|
|
@@ -8772,7 +8939,7 @@ async function maybeCheckForUpdate(options) {
|
|
|
8772
8939
|
if (isDisabled(options)) return;
|
|
8773
8940
|
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
8774
8941
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
8775
|
-
const cachePath = options.cachePath ??
|
|
8942
|
+
const cachePath = options.cachePath ?? join37(homedir8(), ".agentwheel", "update-check.json");
|
|
8776
8943
|
try {
|
|
8777
8944
|
const cached = await readCache(cachePath);
|
|
8778
8945
|
if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
|
|
@@ -8809,7 +8976,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
|
|
|
8809
8976
|
}
|
|
8810
8977
|
async function readCache(path) {
|
|
8811
8978
|
try {
|
|
8812
|
-
const parsed = JSON.parse(await
|
|
8979
|
+
const parsed = JSON.parse(await readFile25(path, "utf8"));
|
|
8813
8980
|
if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
|
|
8814
8981
|
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
8815
8982
|
} catch {
|
|
@@ -8817,8 +8984,8 @@ async function readCache(path) {
|
|
|
8817
8984
|
}
|
|
8818
8985
|
}
|
|
8819
8986
|
async function writeCache(path, cache) {
|
|
8820
|
-
await
|
|
8821
|
-
await
|
|
8987
|
+
await mkdir20(dirname28(path), { recursive: true });
|
|
8988
|
+
await writeFile18(path, `${JSON.stringify(cache, null, 2)}
|
|
8822
8989
|
`, "utf8");
|
|
8823
8990
|
}
|
|
8824
8991
|
function warnIfNewer(latest, current, stderr = process.stderr) {
|
|
@@ -8981,13 +9148,13 @@ function isCrossPackageSelector(value) {
|
|
|
8981
9148
|
}
|
|
8982
9149
|
|
|
8983
9150
|
// src/model/package-migrate.ts
|
|
8984
|
-
import { readFile as
|
|
8985
|
-
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";
|
|
8986
9153
|
import { applyEdits, modify, parse as parse4 } from "jsonc-parser";
|
|
8987
9154
|
async function migratePackageManifest(root) {
|
|
8988
9155
|
const packageRoot = resolve19(root);
|
|
8989
9156
|
for (const name of openPackManifestNames) {
|
|
8990
|
-
const path =
|
|
9157
|
+
const path = join39(packageRoot, name);
|
|
8991
9158
|
if (await pathExists(path)) {
|
|
8992
9159
|
return { changed: false, to: path, message: `Package already uses ${name}.` };
|
|
8993
9160
|
}
|
|
@@ -8996,18 +9163,18 @@ async function migratePackageManifest(root) {
|
|
|
8996
9163
|
if (!legacyName) {
|
|
8997
9164
|
throw new Error(`No legacy package manifest found at ${packageRoot}`);
|
|
8998
9165
|
}
|
|
8999
|
-
const from =
|
|
9166
|
+
const from = join39(packageRoot, legacyName);
|
|
9000
9167
|
const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
|
|
9001
|
-
const to =
|
|
9002
|
-
const content = await
|
|
9168
|
+
const to = join39(packageRoot, toName);
|
|
9169
|
+
const content = await readFile26(from, "utf8");
|
|
9003
9170
|
const updated = updateSchemaVersion(content);
|
|
9004
9171
|
await rename4(from, to);
|
|
9005
|
-
await
|
|
9172
|
+
await writeFile19(to, updated, "utf8");
|
|
9006
9173
|
return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
|
|
9007
9174
|
}
|
|
9008
9175
|
async function firstExistingLegacyManifest(root) {
|
|
9009
9176
|
for (const name of legacyPackageManifestNames) {
|
|
9010
|
-
if (await pathExists(
|
|
9177
|
+
if (await pathExists(join39(root, name))) return name;
|
|
9011
9178
|
}
|
|
9012
9179
|
return void 0;
|
|
9013
9180
|
}
|
|
@@ -9025,20 +9192,20 @@ function updateSchemaVersion(content) {
|
|
|
9025
9192
|
|
|
9026
9193
|
// src/cli/version.ts
|
|
9027
9194
|
import { readFileSync } from "fs";
|
|
9028
|
-
import { dirname as
|
|
9195
|
+
import { dirname as dirname29, join as join40 } from "path";
|
|
9029
9196
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
9030
9197
|
var FALLBACK_VERSION = "0.0.0";
|
|
9031
9198
|
function resolveCliVersion() {
|
|
9032
|
-
let dir =
|
|
9199
|
+
let dir = dirname29(fileURLToPath2(import.meta.url));
|
|
9033
9200
|
while (true) {
|
|
9034
9201
|
try {
|
|
9035
|
-
const pkg = JSON.parse(readFileSync(
|
|
9202
|
+
const pkg = JSON.parse(readFileSync(join40(dir, "package.json"), "utf8"));
|
|
9036
9203
|
if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
|
|
9037
9204
|
return pkg.version;
|
|
9038
9205
|
}
|
|
9039
9206
|
} catch {
|
|
9040
9207
|
}
|
|
9041
|
-
const parent =
|
|
9208
|
+
const parent = dirname29(dir);
|
|
9042
9209
|
if (parent === dir) return FALLBACK_VERSION;
|
|
9043
9210
|
dir = parent;
|
|
9044
9211
|
}
|
|
@@ -9086,7 +9253,7 @@ program.command("list").description("list artifacts exposed by a package source"
|
|
|
9086
9253
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
9087
9254
|
const selectedArtifacts = selectedArtifactsFromOptionsOrRegistry(options, resolvedInput.registryEntry);
|
|
9088
9255
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
9089
|
-
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") }))));
|
|
9090
9257
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
9091
9258
|
for (const artifact of artifacts) {
|
|
9092
9259
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
@@ -9096,7 +9263,7 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
9096
9263
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
9097
9264
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
9098
9265
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
9099
|
-
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") }))));
|
|
9100
9267
|
const result = await driver.scan(resolved);
|
|
9101
9268
|
if (result.findings.length === 0) {
|
|
9102
9269
|
console.log("Scan ok: no findings");
|
|
@@ -9393,7 +9560,7 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
9393
9560
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
9394
9561
|
workspaceRoot: targetRoot,
|
|
9395
9562
|
adapter,
|
|
9396
|
-
cacheRoot:
|
|
9563
|
+
cacheRoot: join41(targetRoot, ".agentwheel", "cache"),
|
|
9397
9564
|
mode: options.mode,
|
|
9398
9565
|
frozenLock: lockMode,
|
|
9399
9566
|
select: selectedArtifacts
|
|
@@ -9751,7 +9918,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
|
|
|
9751
9918
|
artifactType: entry.artifactType,
|
|
9752
9919
|
artifactName: entry.artifactName,
|
|
9753
9920
|
kind: entry.kind,
|
|
9754
|
-
destPath: operation?.destPath ??
|
|
9921
|
+
destPath: operation?.destPath ?? join41(targetRoot, entry.path),
|
|
9755
9922
|
relativeDestPath: entry.path,
|
|
9756
9923
|
desiredHash: entry.sourceHash,
|
|
9757
9924
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -10011,12 +10178,12 @@ async function printDoctor(target, options) {
|
|
|
10011
10178
|
const requestedSkills = doctorSkillRequests(target, options);
|
|
10012
10179
|
const skills = [];
|
|
10013
10180
|
for (const request of requestedSkills) {
|
|
10014
|
-
const skillPath =
|
|
10181
|
+
const skillPath = join41(state.installRoot, targetMapping.dest, request.name);
|
|
10015
10182
|
const exists = await pathExists(skillPath);
|
|
10016
10183
|
const manifestEntry = manifest?.entries.find((entry) => {
|
|
10017
10184
|
if (entry.artifactType !== "skills") return false;
|
|
10018
10185
|
const legacyInstallName = "installName" in entry && typeof entry.installName === "string" ? entry.installName : void 0;
|
|
10019
|
-
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path ===
|
|
10186
|
+
return entry.artifactName === request.name || legacyInstallName === request.name || entry.path === join41(targetMapping.dest, request.name);
|
|
10020
10187
|
});
|
|
10021
10188
|
const status = manifestEntry ? "managed" : exists ? "present-unmanaged" : "missing";
|
|
10022
10189
|
skills.push({
|
|
@@ -10096,7 +10263,7 @@ function doctorSkillLabel(name) {
|
|
|
10096
10263
|
return `${name} skill`;
|
|
10097
10264
|
}
|
|
10098
10265
|
function isSyncwheelWorkspace(targetRoot) {
|
|
10099
|
-
return existsSync(
|
|
10266
|
+
return existsSync(join41(targetRoot, ".syncwheel", "manifest.json"));
|
|
10100
10267
|
}
|
|
10101
10268
|
function skillInstallCommand(adapter, installationType, options, skill, behavior = {}) {
|
|
10102
10269
|
const args = [
|
|
@@ -10238,10 +10405,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
10238
10405
|
};
|
|
10239
10406
|
}
|
|
10240
10407
|
async function initPackage(root) {
|
|
10241
|
-
await
|
|
10242
|
-
await
|
|
10243
|
-
await
|
|
10244
|
-
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");
|
|
10245
10412
|
const manifest = {
|
|
10246
10413
|
schemaVersion: 2,
|
|
10247
10414
|
name: "example/agentwheel-package",
|
|
@@ -10252,12 +10419,12 @@ async function initPackage(root) {
|
|
|
10252
10419
|
{ type: "skills", path: "skills" }
|
|
10253
10420
|
]
|
|
10254
10421
|
};
|
|
10255
|
-
await
|
|
10422
|
+
await writeFile20(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
10256
10423
|
`, "utf8");
|
|
10257
|
-
await
|
|
10424
|
+
await writeFile20(join41(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
10258
10425
|
}
|
|
10259
10426
|
async function defaultBootstrapPackage(_root) {
|
|
10260
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
10427
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname30(fileURLToPath3(import.meta.url)));
|
|
10261
10428
|
if (!packageRoot) return void 0;
|
|
10262
10429
|
return {
|
|
10263
10430
|
name: "agentwheel",
|
|
@@ -10304,7 +10471,7 @@ async function findAgentwheelPackageRoot(start) {
|
|
|
10304
10471
|
let current = resolve20(start);
|
|
10305
10472
|
while (true) {
|
|
10306
10473
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
10307
|
-
const parent =
|
|
10474
|
+
const parent = dirname30(current);
|
|
10308
10475
|
if (parent === current) return void 0;
|
|
10309
10476
|
current = parent;
|
|
10310
10477
|
}
|