agentwheel 0.11.0 → 0.14.3
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 +66 -33
- package/dist/{chunk-N2LZY7LO.js → chunk-B3FMBTWC.js} +8 -2
- package/dist/{identify-7SEBWCNQ.js → identify-TXIDGMNL.js} +1 -1
- package/dist/index.js +1664 -605
- package/openpack.json +2 -2
- package/package.json +2 -1
- package/skills/agentwheel/SKILL.md +39 -16
- package/skills/agentwheel-smoke/SKILL.md +63 -0
- package/skills/agentwheel-smoke/scripts/smoke.mjs +397 -0
package/dist/index.js
CHANGED
|
@@ -3,13 +3,15 @@ import {
|
|
|
3
3
|
atomicCopy,
|
|
4
4
|
hashPath,
|
|
5
5
|
inferSourceDriverName,
|
|
6
|
+
isIgnoredGeneratedEntry,
|
|
6
7
|
pathExists,
|
|
7
8
|
writeJsonAtomic
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-B3FMBTWC.js";
|
|
9
10
|
|
|
10
11
|
// src/cli/index.ts
|
|
11
|
-
import { mkdir as
|
|
12
|
-
import {
|
|
12
|
+
import { mkdir as mkdir16, rm as rm9, writeFile as writeFile14 } from "fs/promises";
|
|
13
|
+
import { homedir as homedir9 } from "os";
|
|
14
|
+
import { dirname as dirname23, join as join31, resolve as resolve18 } from "path";
|
|
13
15
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
14
16
|
import { Command } from "commander";
|
|
15
17
|
|
|
@@ -18,6 +20,7 @@ import { resolve as resolve2 } from "path";
|
|
|
18
20
|
|
|
19
21
|
// src/model/adapter.ts
|
|
20
22
|
import { readFile } from "fs/promises";
|
|
23
|
+
import { homedir } from "os";
|
|
21
24
|
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
22
25
|
import { z as z2 } from "zod";
|
|
23
26
|
|
|
@@ -36,6 +39,7 @@ var artifactTypeSchema = z.enum([
|
|
|
36
39
|
"fragments"
|
|
37
40
|
]);
|
|
38
41
|
var fileKindSchema = z.enum(["file", "dir"]);
|
|
42
|
+
var artifactFormatSchema = z.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "artifact format must be a stable identifier");
|
|
39
43
|
var packageAssetSchema = z.object({
|
|
40
44
|
from: z.string().min(1),
|
|
41
45
|
into: z.string().min(1),
|
|
@@ -68,6 +72,7 @@ var artifactSchema = z.object({
|
|
|
68
72
|
relativePath: z.string().min(1),
|
|
69
73
|
kind: fileKindSchema,
|
|
70
74
|
hash: z.string().min(16),
|
|
75
|
+
format: artifactFormatSchema.optional(),
|
|
71
76
|
packageName: z.string().min(1).optional(),
|
|
72
77
|
channel: z.enum(["managed", "overlay", "addition", "override", "ejected"]).default("managed"),
|
|
73
78
|
assets: z.array(packageAssetSchema).optional(),
|
|
@@ -79,20 +84,105 @@ var artifactSchema = z.object({
|
|
|
79
84
|
});
|
|
80
85
|
|
|
81
86
|
// src/model/adapter.ts
|
|
87
|
+
var defaultInstallationType = "local";
|
|
88
|
+
var installationTypeSchema = z2.string().min(1).regex(/^[a-z0-9][a-z0-9._-]*$/i, "installation type must be a stable path-safe identifier");
|
|
82
89
|
var targetMappingSchema = z2.object({
|
|
83
90
|
dest: z2.string().min(1),
|
|
84
91
|
enabled: z2.boolean().default(true),
|
|
85
|
-
|
|
92
|
+
root: z2.enum(["target", "home"]).optional(),
|
|
93
|
+
formats: z2.array(z2.string().min(1)).optional(),
|
|
94
|
+
semantic: z2.enum(["openclaw-plugin", "codex-subagent", "copilot-instruction", "copilot-prompt", "copilot-agent"]).optional(),
|
|
86
95
|
merge: z2.enum(["json-deep", "codex-toml-mcp"]).optional()
|
|
87
96
|
});
|
|
97
|
+
var targetRegistrySchema = z2.record(installationTypeSchema, targetMappingSchema);
|
|
98
|
+
var targetRegistryInputSchema = z2.union([
|
|
99
|
+
targetMappingSchema.transform((mapping) => ({ [defaultInstallationType]: mapping })),
|
|
100
|
+
targetRegistrySchema
|
|
101
|
+
]);
|
|
88
102
|
var adapterSchema = z2.object({
|
|
89
103
|
name: z2.string().min(1),
|
|
90
104
|
displayName: z2.string().min(1).optional(),
|
|
91
105
|
targets: z2.partialRecord(
|
|
92
106
|
artifactTypeSchema,
|
|
93
|
-
|
|
107
|
+
targetRegistryInputSchema
|
|
94
108
|
).default({})
|
|
95
109
|
});
|
|
110
|
+
function supportedInstallationTypes(adapter, artifactType) {
|
|
111
|
+
const registries = artifactType ? [adapter.targets[artifactType]] : Object.entries(adapter.targets).filter(([type]) => type !== "fragments").map(([, registry]) => registry);
|
|
112
|
+
const types = /* @__PURE__ */ new Set();
|
|
113
|
+
for (const registry of registries) {
|
|
114
|
+
for (const [installationType, target] of Object.entries(registry ?? {})) {
|
|
115
|
+
if (target.enabled) types.add(installationType);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return [...types].sort((a, b) => a.localeCompare(b));
|
|
119
|
+
}
|
|
120
|
+
function resolveInstallationTypeForArtifacts(adapter, artifactTypes, requested) {
|
|
121
|
+
const installableTypes = [...new Set(artifactTypes.filter((type) => type !== "fragments"))];
|
|
122
|
+
if (installableTypes.length === 0) {
|
|
123
|
+
return requested ?? resolveInstallationTypeForAdapter(adapter, requested);
|
|
124
|
+
}
|
|
125
|
+
for (const type of installableTypes) {
|
|
126
|
+
const supported = supportedInstallationTypes(adapter, type);
|
|
127
|
+
if (supported.length === 0) {
|
|
128
|
+
throw new Error(`Adapter ${adapter.name} does not support ${type} artifacts for any installation type.`);
|
|
129
|
+
}
|
|
130
|
+
if (requested && !supported.includes(requested)) {
|
|
131
|
+
throw new Error(`Adapter ${adapter.name} does not support ${type} artifacts for installation type '${requested}'. Supported: ${supported.join(", ")}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (requested) return requested;
|
|
135
|
+
const [firstType, ...restTypes] = installableTypes;
|
|
136
|
+
let candidates = new Set(supportedInstallationTypes(adapter, firstType));
|
|
137
|
+
for (const type of restTypes) {
|
|
138
|
+
const supported = new Set(supportedInstallationTypes(adapter, type));
|
|
139
|
+
candidates = new Set([...candidates].filter((candidate) => supported.has(candidate)));
|
|
140
|
+
}
|
|
141
|
+
const available = [...candidates].sort((a, b) => a.localeCompare(b));
|
|
142
|
+
if (available.length === 1) return available[0];
|
|
143
|
+
if (available.length === 0) {
|
|
144
|
+
throw new Error(`Adapter ${adapter.name} has no common installation type for: ${installableTypes.join(", ")}`);
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`Installation type required for ${adapter.name}; supported for selected artifacts: ${available.join(", ")}. Pass --installation-type <type>.`);
|
|
147
|
+
}
|
|
148
|
+
function resolveInstallationTypeForAdapter(adapter, requested) {
|
|
149
|
+
const supported = supportedInstallationTypes(adapter);
|
|
150
|
+
if (requested) {
|
|
151
|
+
if (!supported.includes(requested)) {
|
|
152
|
+
throw new Error(`Adapter ${adapter.name} does not support installation type '${requested}'. Supported: ${supported.join(", ") || "<none>"}`);
|
|
153
|
+
}
|
|
154
|
+
return requested;
|
|
155
|
+
}
|
|
156
|
+
if (supported.length === 1) return supported[0];
|
|
157
|
+
if (supported.length === 0) return defaultInstallationType;
|
|
158
|
+
throw new Error(`Installation type required for ${adapter.name}; supported: ${supported.join(", ")}. Pass --installation-type <type>.`);
|
|
159
|
+
}
|
|
160
|
+
function targetMappingForArtifact(adapter, artifactType, installationType) {
|
|
161
|
+
return adapter.targets[artifactType]?.[installationType];
|
|
162
|
+
}
|
|
163
|
+
function installRootForArtifacts(adapter, targetRoot, installationType, artifactTypes, isSsh = false) {
|
|
164
|
+
const roots = new Set(
|
|
165
|
+
[...new Set(artifactTypes.filter((type) => type !== "fragments"))].map((type) => targetMappingForArtifact(adapter, type, installationType)?.root ?? "target")
|
|
166
|
+
);
|
|
167
|
+
if (roots.has("home") && roots.has("target")) {
|
|
168
|
+
throw new Error(`Installation type '${installationType}' for ${adapter.name} mixes home-rooted and target-rooted artifacts.`);
|
|
169
|
+
}
|
|
170
|
+
return roots.has("home") && !isSsh ? userHomeRoot() : targetRoot;
|
|
171
|
+
}
|
|
172
|
+
function installRootForAdapterInstallationType(adapter, targetRoot, installationType, isSsh = false) {
|
|
173
|
+
const roots = /* @__PURE__ */ new Set();
|
|
174
|
+
for (const registry of Object.values(adapter.targets)) {
|
|
175
|
+
const target = registry?.[installationType];
|
|
176
|
+
if (target?.enabled) roots.add(target.root ?? "target");
|
|
177
|
+
}
|
|
178
|
+
if (roots.has("home") && roots.has("target")) {
|
|
179
|
+
throw new Error(`Installation type '${installationType}' for ${adapter.name} mixes home-rooted and target-rooted artifacts.`);
|
|
180
|
+
}
|
|
181
|
+
return roots.has("home") && !isSsh ? userHomeRoot() : targetRoot;
|
|
182
|
+
}
|
|
183
|
+
function userHomeRoot() {
|
|
184
|
+
return process.env.AGENTWHEEL_TEST_HOME || process.env.HOME || homedir();
|
|
185
|
+
}
|
|
96
186
|
async function loadAdapterConfig(path) {
|
|
97
187
|
const content = await readFile(path, "utf8");
|
|
98
188
|
const errors = [];
|
|
@@ -112,28 +202,72 @@ var claudeAdapter = {
|
|
|
112
202
|
name: "claude",
|
|
113
203
|
displayName: "Claude Code",
|
|
114
204
|
targets: {
|
|
115
|
-
instructions: {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
205
|
+
instructions: {
|
|
206
|
+
local: { enabled: true, dest: "CLAUDE.md" },
|
|
207
|
+
user: { enabled: true, root: "home", dest: ".claude/CLAUDE.md" }
|
|
208
|
+
},
|
|
209
|
+
rules: {
|
|
210
|
+
local: { enabled: true, dest: ".claude/rules", formats: ["markdown-rule", "claude-markdown-rule"] },
|
|
211
|
+
user: { enabled: true, root: "home", dest: ".claude/rules", formats: ["markdown-rule", "claude-markdown-rule"] }
|
|
212
|
+
},
|
|
213
|
+
skills: {
|
|
214
|
+
local: { enabled: true, dest: ".claude/skills" },
|
|
215
|
+
user: { enabled: true, root: "home", dest: ".claude/skills" }
|
|
216
|
+
},
|
|
217
|
+
commands: {
|
|
218
|
+
local: { enabled: true, dest: ".claude/commands" },
|
|
219
|
+
user: { enabled: true, root: "home", dest: ".claude/commands" }
|
|
220
|
+
},
|
|
221
|
+
subagents: {
|
|
222
|
+
local: { enabled: true, dest: ".claude/agents" },
|
|
223
|
+
user: { enabled: true, root: "home", dest: ".claude/agents" }
|
|
224
|
+
},
|
|
225
|
+
mcp: {
|
|
226
|
+
local: { enabled: true, dest: ".mcp.json", merge: "json-deep" }
|
|
227
|
+
},
|
|
228
|
+
hooks: {
|
|
229
|
+
local: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
|
|
230
|
+
user: { enabled: true, root: "home", dest: ".claude/settings.json", merge: "json-deep" }
|
|
231
|
+
},
|
|
232
|
+
settings: {
|
|
233
|
+
local: { enabled: true, dest: ".claude/settings.json", merge: "json-deep" },
|
|
234
|
+
user: { enabled: true, root: "home", dest: ".claude/settings.json", merge: "json-deep" }
|
|
235
|
+
}
|
|
123
236
|
}
|
|
124
237
|
};
|
|
125
238
|
|
|
126
239
|
// src/adapters/copilot.ts
|
|
127
240
|
var copilotAdapter = {
|
|
128
241
|
name: "copilot",
|
|
129
|
-
displayName: "GitHub Copilot",
|
|
242
|
+
displayName: "GitHub Copilot CLI",
|
|
130
243
|
targets: {
|
|
131
|
-
instructions: {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
244
|
+
instructions: {
|
|
245
|
+
local: { enabled: true, dest: ".github/copilot-instructions.md" },
|
|
246
|
+
user: { enabled: true, root: "home", dest: ".copilot/copilot-instructions.md" }
|
|
247
|
+
},
|
|
248
|
+
rules: {
|
|
249
|
+
local: { enabled: true, dest: ".github/instructions", formats: ["markdown-rule", "copilot-instruction-rule"], semantic: "copilot-instruction" },
|
|
250
|
+
user: { enabled: true, root: "home", dest: ".copilot/instructions", formats: ["markdown-rule", "copilot-instruction-rule"], semantic: "copilot-instruction" }
|
|
251
|
+
},
|
|
252
|
+
commands: {
|
|
253
|
+
local: { enabled: true, dest: ".github/prompts", semantic: "copilot-prompt" }
|
|
254
|
+
},
|
|
255
|
+
skills: {
|
|
256
|
+
local: { enabled: true, dest: ".github/skills" },
|
|
257
|
+
user: { enabled: true, root: "home", dest: ".copilot/skills" }
|
|
258
|
+
},
|
|
259
|
+
subagents: {
|
|
260
|
+
local: { enabled: true, dest: ".github/agents", semantic: "copilot-agent" },
|
|
261
|
+
user: { enabled: true, root: "home", dest: ".copilot/agents", semantic: "copilot-agent" }
|
|
262
|
+
},
|
|
263
|
+
mcp: {
|
|
264
|
+
local: { enabled: true, dest: ".github/mcp.json", merge: "json-deep" },
|
|
265
|
+
user: { enabled: true, root: "home", dest: ".copilot/mcp-config.json", merge: "json-deep" }
|
|
266
|
+
},
|
|
267
|
+
hooks: {
|
|
268
|
+
local: { enabled: true, dest: ".github/hooks" },
|
|
269
|
+
user: { enabled: true, root: "home", dest: ".copilot/hooks" }
|
|
270
|
+
}
|
|
137
271
|
}
|
|
138
272
|
};
|
|
139
273
|
|
|
@@ -142,14 +276,30 @@ var codexAdapter = {
|
|
|
142
276
|
name: "codex",
|
|
143
277
|
displayName: "Codex CLI",
|
|
144
278
|
targets: {
|
|
145
|
-
instructions: {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
279
|
+
instructions: {
|
|
280
|
+
local: { enabled: true, dest: "AGENTS.md" },
|
|
281
|
+
user: { enabled: true, root: "home", dest: ".codex/AGENTS.md" }
|
|
282
|
+
},
|
|
283
|
+
rules: {
|
|
284
|
+
local: { enabled: true, dest: ".codex/rules", formats: ["codex-command-policy"] },
|
|
285
|
+
user: { enabled: true, root: "home", dest: ".codex/rules", formats: ["codex-command-policy"] }
|
|
286
|
+
},
|
|
287
|
+
skills: {
|
|
288
|
+
local: { enabled: true, dest: ".agents/skills" },
|
|
289
|
+
user: { enabled: true, root: "home", dest: ".agents/skills" }
|
|
290
|
+
},
|
|
291
|
+
subagents: {
|
|
292
|
+
local: { enabled: true, dest: ".codex/agents", semantic: "codex-subagent" },
|
|
293
|
+
user: { enabled: true, root: "home", dest: ".codex/agents", semantic: "codex-subagent" }
|
|
294
|
+
},
|
|
295
|
+
mcp: {
|
|
296
|
+
local: { enabled: true, dest: ".codex/config.toml", merge: "codex-toml-mcp" },
|
|
297
|
+
user: { enabled: true, root: "home", dest: ".codex/config.toml", merge: "codex-toml-mcp" }
|
|
298
|
+
},
|
|
299
|
+
hooks: {
|
|
300
|
+
local: { enabled: true, dest: ".codex/hooks.json", merge: "json-deep" },
|
|
301
|
+
user: { enabled: true, root: "home", dest: ".codex/hooks.json", merge: "json-deep" }
|
|
302
|
+
}
|
|
153
303
|
}
|
|
154
304
|
};
|
|
155
305
|
|
|
@@ -158,14 +308,12 @@ var hermesAdapter = {
|
|
|
158
308
|
name: "hermes",
|
|
159
309
|
displayName: "Hermes",
|
|
160
310
|
targets: {
|
|
161
|
-
instructions: {
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
hooks: { enabled: true, dest: ".hermes/hooks", merge: "json-deep" },
|
|
168
|
-
settings: { enabled: true, dest: ".hermes/settings.json", merge: "json-deep" }
|
|
311
|
+
instructions: {
|
|
312
|
+
local: { enabled: true, dest: "AGENTS.md" }
|
|
313
|
+
},
|
|
314
|
+
skills: {
|
|
315
|
+
user: { enabled: true, root: "home", dest: ".hermes/skills" }
|
|
316
|
+
}
|
|
169
317
|
}
|
|
170
318
|
};
|
|
171
319
|
|
|
@@ -174,15 +322,13 @@ var openClawAdapter = {
|
|
|
174
322
|
name: "openclaw",
|
|
175
323
|
displayName: "OpenClaw",
|
|
176
324
|
targets: {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
settings: { enabled: true, dest: ".openclaw/settings.json", merge: "json-deep" },
|
|
185
|
-
plugins: { enabled: true, dest: ".openclaw/plugins", semantic: "openclaw-plugin" }
|
|
325
|
+
skills: {
|
|
326
|
+
local: { enabled: true, dest: "skills" },
|
|
327
|
+
user: { enabled: true, root: "home", dest: ".openclaw/skills" }
|
|
328
|
+
},
|
|
329
|
+
plugins: {
|
|
330
|
+
local: { enabled: true, dest: ".openclaw/plugins", formats: ["openclaw-plugin"], semantic: "openclaw-plugin" }
|
|
331
|
+
}
|
|
186
332
|
}
|
|
187
333
|
};
|
|
188
334
|
|
|
@@ -264,11 +410,11 @@ async function resolveAdapter(options) {
|
|
|
264
410
|
}
|
|
265
411
|
|
|
266
412
|
// src/install/apply.ts
|
|
267
|
-
import { execFile as
|
|
413
|
+
import { execFile as execFile3 } from "child_process";
|
|
268
414
|
import { mkdtemp, rm as rm3, writeFile as writeFile6 } from "fs/promises";
|
|
269
415
|
import { tmpdir as tmpdir2 } from "os";
|
|
270
416
|
import { basename as basename2, join as join4 } from "path";
|
|
271
|
-
import { promisify as
|
|
417
|
+
import { promisify as promisify3 } from "util";
|
|
272
418
|
|
|
273
419
|
// src/model/graph-lock.ts
|
|
274
420
|
import { createHash } from "crypto";
|
|
@@ -444,8 +590,11 @@ function stableValue(value) {
|
|
|
444
590
|
}
|
|
445
591
|
|
|
446
592
|
// src/transport/local.ts
|
|
593
|
+
import { execFile } from "child_process";
|
|
447
594
|
import { mkdir as mkdir2, readFile as readFile4, rename as rename2, rm, writeFile as writeFile3 } from "fs/promises";
|
|
448
595
|
import { dirname as dirname2 } from "path";
|
|
596
|
+
import { promisify } from "util";
|
|
597
|
+
var execFileAsync = promisify(execFile);
|
|
449
598
|
var localTransport = {
|
|
450
599
|
kind: "local",
|
|
451
600
|
description: "local filesystem",
|
|
@@ -464,20 +613,23 @@ var localTransport = {
|
|
|
464
613
|
},
|
|
465
614
|
writeJsonAtomic,
|
|
466
615
|
atomicCopy,
|
|
467
|
-
rm: (path) => rm(path, { recursive: true, force: true })
|
|
616
|
+
rm: (path) => rm(path, { recursive: true, force: true }),
|
|
617
|
+
async execFile(command, args, options = {}) {
|
|
618
|
+
await execFileAsync(command, args, { cwd: options.cwd });
|
|
619
|
+
}
|
|
468
620
|
};
|
|
469
621
|
|
|
470
622
|
// src/transport/ssh.ts
|
|
471
|
-
import { execFile, spawn } from "child_process";
|
|
623
|
+
import { execFile as execFile2, spawn } from "child_process";
|
|
472
624
|
import { basename, dirname as dirname3 } from "path";
|
|
473
625
|
import { dirname as posixDirname } from "path/posix";
|
|
474
|
-
import { promisify } from "util";
|
|
475
|
-
var
|
|
626
|
+
import { promisify as promisify2 } from "util";
|
|
627
|
+
var execFileAsync2 = promisify2(execFile2);
|
|
476
628
|
function createSshTransport(config) {
|
|
477
629
|
const endpoint = config.user ? `${config.user}@${config.host}` : config.host;
|
|
478
630
|
const args = baseSshArgs(config, endpoint);
|
|
479
631
|
async function run(command) {
|
|
480
|
-
const { stdout } = await
|
|
632
|
+
const { stdout } = await execFileAsync2("ssh", [...args, command], { maxBuffer: 20 * 1024 * 1024 });
|
|
481
633
|
return stdout;
|
|
482
634
|
}
|
|
483
635
|
async function runWithInput(command, input) {
|
|
@@ -523,6 +675,11 @@ function createSshTransport(config) {
|
|
|
523
675
|
},
|
|
524
676
|
rm(path) {
|
|
525
677
|
return run(`rm -rf -- ${quoteSh(path)}`).then(() => void 0);
|
|
678
|
+
},
|
|
679
|
+
execFile(command, commandArgs, options = {}) {
|
|
680
|
+
const quoted = [command, ...commandArgs].map(quoteSh).join(" ");
|
|
681
|
+
const remoteCommand = options.cwd ? `cd ${quoteSh(options.cwd)} && ${quoted}` : quoted;
|
|
682
|
+
return run(remoteCommand).then(() => void 0);
|
|
526
683
|
}
|
|
527
684
|
};
|
|
528
685
|
}
|
|
@@ -589,6 +746,11 @@ const { createHash } = require("node:crypto");
|
|
|
589
746
|
const { readdirSync, readFileSync, statSync } = require("node:fs");
|
|
590
747
|
const { join, relative } = require("node:path");
|
|
591
748
|
const target = process.argv[1];
|
|
749
|
+
const ignoredNames = new Set([".git", "node_modules", "__pycache__", ".DS_Store"]);
|
|
750
|
+
const ignoredSuffixes = [".pyc", ".pyo"];
|
|
751
|
+
function isIgnoredGeneratedEntry(name) {
|
|
752
|
+
return ignoredNames.has(name) || ignoredSuffixes.some((suffix) => name.endsWith(suffix));
|
|
753
|
+
}
|
|
592
754
|
function hashPath(path) {
|
|
593
755
|
const stats = statSync(path);
|
|
594
756
|
if (stats.isFile()) {
|
|
@@ -606,7 +768,7 @@ function listFiles(root) {
|
|
|
606
768
|
const out = [];
|
|
607
769
|
function walk(dir) {
|
|
608
770
|
for (const entry of readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
609
|
-
if (entry.name
|
|
771
|
+
if (isIgnoredGeneratedEntry(entry.name)) continue;
|
|
610
772
|
const full = join(dir, entry.name);
|
|
611
773
|
if (entry.isDirectory()) walk(full);
|
|
612
774
|
else if (entry.isFile()) out.push(full);
|
|
@@ -722,6 +884,8 @@ var installManifestV1Schema = z4.object({
|
|
|
722
884
|
var installManifestV2Schema = z4.object({
|
|
723
885
|
version: z4.literal(2),
|
|
724
886
|
adapter: z4.string().min(1),
|
|
887
|
+
installationType: installationTypeSchema.default(defaultInstallationType),
|
|
888
|
+
stateKey: z4.string().min(1).optional(),
|
|
725
889
|
targetRoot: z4.string().min(1),
|
|
726
890
|
generatedAt: z4.string().datetime(),
|
|
727
891
|
revision: z4.string().min(16),
|
|
@@ -754,6 +918,7 @@ var sourceLockSchema = z4.object({
|
|
|
754
918
|
relativePath: z4.string().min(1),
|
|
755
919
|
kind: fileKindSchema,
|
|
756
920
|
hash: z4.string().min(16),
|
|
921
|
+
format: artifactFormatSchema.optional(),
|
|
757
922
|
composedFrom: z4.array(composedFromEntrySchema).optional()
|
|
758
923
|
})
|
|
759
924
|
)
|
|
@@ -764,16 +929,25 @@ import { join as join2 } from "path";
|
|
|
764
929
|
function metadataDir(targetRoot) {
|
|
765
930
|
return join2(targetRoot, ".agentwheel");
|
|
766
931
|
}
|
|
767
|
-
function
|
|
768
|
-
|
|
932
|
+
function stateKeyFor(adapter, scope = {}) {
|
|
933
|
+
if (scope.stateKey) return sanitizeStateKey(scope.stateKey);
|
|
934
|
+
const installationType = scope.installationType ?? defaultInstallationType;
|
|
935
|
+
const fingerprint = scope.targetFingerprint ? `.${scope.targetFingerprint}` : "";
|
|
936
|
+
return sanitizeStateKey(`${adapter}.${installationType}${fingerprint}`);
|
|
769
937
|
}
|
|
770
|
-
function
|
|
771
|
-
return join2(metadataDir(targetRoot), `${adapter}.
|
|
938
|
+
function installManifestPath(targetRoot, adapter, scope = {}) {
|
|
939
|
+
return join2(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.install-manifest.json`);
|
|
940
|
+
}
|
|
941
|
+
function sourceLockPath(targetRoot, adapter, scope = {}) {
|
|
942
|
+
return join2(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.source-lock.json`);
|
|
943
|
+
}
|
|
944
|
+
function sanitizeStateKey(value) {
|
|
945
|
+
return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
|
|
772
946
|
}
|
|
773
947
|
|
|
774
948
|
// src/install/manifest.ts
|
|
775
|
-
async function readInstallManifest(targetRoot, adapter, transport = localTransport) {
|
|
776
|
-
const path = installManifestPath(targetRoot, adapter);
|
|
949
|
+
async function readInstallManifest(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
950
|
+
const path = installManifestPath(targetRoot, adapter, scope);
|
|
777
951
|
if (!await transport.pathExists(path)) return void 0;
|
|
778
952
|
const raw = JSON.parse(await transport.readFile(path));
|
|
779
953
|
const parsed = installManifestSchema.parse(raw);
|
|
@@ -784,14 +958,17 @@ async function readInstallManifest(targetRoot, adapter, transport = localTranspo
|
|
|
784
958
|
}
|
|
785
959
|
async function writeInstallManifest(manifest, transport = localTransport) {
|
|
786
960
|
const next = withManifestRevision(manifest);
|
|
787
|
-
await transport.writeJsonAtomic(installManifestPath(next.targetRoot, next.adapter
|
|
961
|
+
await transport.writeJsonAtomic(installManifestPath(next.targetRoot, next.adapter, {
|
|
962
|
+
installationType: next.installationType,
|
|
963
|
+
stateKey: next.stateKey
|
|
964
|
+
}), stripReadOnlyManifestFields(next));
|
|
788
965
|
}
|
|
789
|
-
async function writeSourceLock(targetRoot, adapter, lock, transport = localTransport) {
|
|
790
|
-
await transport.writeJsonAtomic(sourceLockPath(targetRoot, adapter), lock);
|
|
966
|
+
async function writeSourceLock(targetRoot, adapter, lock, transport = localTransport, scope = {}) {
|
|
967
|
+
await transport.writeJsonAtomic(sourceLockPath(targetRoot, adapter, scope), lock);
|
|
791
968
|
}
|
|
792
|
-
async function removeStateFiles(targetRoot, adapter, transport = localTransport) {
|
|
793
|
-
await transport.rm(installManifestPath(targetRoot, adapter));
|
|
794
|
-
await transport.rm(sourceLockPath(targetRoot, adapter));
|
|
969
|
+
async function removeStateFiles(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
970
|
+
await transport.rm(installManifestPath(targetRoot, adapter, scope));
|
|
971
|
+
await transport.rm(sourceLockPath(targetRoot, adapter, scope));
|
|
795
972
|
}
|
|
796
973
|
function normalizeTargetRoot(path) {
|
|
797
974
|
return resolve3(path);
|
|
@@ -800,7 +977,11 @@ function withManifestRevision(manifest) {
|
|
|
800
977
|
if (manifest.version !== 2) {
|
|
801
978
|
throw new Error("Install manifest writes must use version 2");
|
|
802
979
|
}
|
|
803
|
-
const
|
|
980
|
+
const raw = stripReadOnlyManifestFields(manifest);
|
|
981
|
+
const normalized = installManifestV2Schema.parse({
|
|
982
|
+
installationType: defaultInstallationType,
|
|
983
|
+
...raw
|
|
984
|
+
});
|
|
804
985
|
const withoutRevision = stripRuntimeManifestFields(normalized);
|
|
805
986
|
return {
|
|
806
987
|
...normalized,
|
|
@@ -859,21 +1040,23 @@ function assertOperationContained(operation, targetRoot) {
|
|
|
859
1040
|
// src/install/transaction.ts
|
|
860
1041
|
import { cp, mkdir as mkdir4, rm as rm2, stat as stat2 } from "fs/promises";
|
|
861
1042
|
import { dirname as dirname5, join as join3 } from "path";
|
|
862
|
-
function applyLockPath(targetRoot, adapter) {
|
|
863
|
-
return join3(metadataDir(targetRoot), `${adapter}.apply-lock`);
|
|
1043
|
+
function applyLockPath(targetRoot, adapter, scope = {}) {
|
|
1044
|
+
return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-lock`);
|
|
864
1045
|
}
|
|
865
|
-
function applyJournalPath(targetRoot, adapter) {
|
|
866
|
-
return join3(metadataDir(targetRoot), `${adapter}.apply-journal.json`);
|
|
1046
|
+
function applyJournalPath(targetRoot, adapter, scope = {}) {
|
|
1047
|
+
return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-journal.json`);
|
|
867
1048
|
}
|
|
868
|
-
function applyBackupDir(targetRoot, adapter) {
|
|
869
|
-
return join3(metadataDir(targetRoot), `${adapter}.apply-backups`);
|
|
1049
|
+
function applyBackupDir(targetRoot, adapter, scope = {}) {
|
|
1050
|
+
return join3(metadataDir(targetRoot), `${stateKeyFor(adapter, scope)}.apply-backups`);
|
|
870
1051
|
}
|
|
871
|
-
async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}) {
|
|
872
|
-
const lockPath = applyLockPath(targetRoot, adapter);
|
|
1052
|
+
async function acquireApplyLock(targetRoot, adapter, transport = localTransport, options = {}, scope = {}) {
|
|
1053
|
+
const lockPath = applyLockPath(targetRoot, adapter, scope);
|
|
873
1054
|
const ownerPath = join3(lockPath, "owner.json");
|
|
874
1055
|
const metadata = {
|
|
875
1056
|
pid: process.pid,
|
|
876
1057
|
adapter,
|
|
1058
|
+
installationType: scope.installationType,
|
|
1059
|
+
stateKey: scope.stateKey,
|
|
877
1060
|
targetRoot,
|
|
878
1061
|
transport: transport.description,
|
|
879
1062
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -892,21 +1075,24 @@ async function acquireApplyLock(targetRoot, adapter, transport = localTransport,
|
|
|
892
1075
|
};
|
|
893
1076
|
}
|
|
894
1077
|
async function writeApplyJournal(journal, transport = localTransport) {
|
|
895
|
-
await transport.writeJsonAtomic(applyJournalPath(journal.targetRoot, journal.adapter
|
|
1078
|
+
await transport.writeJsonAtomic(applyJournalPath(journal.targetRoot, journal.adapter, {
|
|
1079
|
+
installationType: journal.installationType,
|
|
1080
|
+
stateKey: journal.stateKey
|
|
1081
|
+
}), {
|
|
896
1082
|
...journal,
|
|
897
1083
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
898
1084
|
});
|
|
899
1085
|
}
|
|
900
|
-
async function readApplyJournal(targetRoot, adapter, transport = localTransport) {
|
|
901
|
-
const path = applyJournalPath(targetRoot, adapter);
|
|
1086
|
+
async function readApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
1087
|
+
const path = applyJournalPath(targetRoot, adapter, scope);
|
|
902
1088
|
if (!await transport.pathExists(path)) return void 0;
|
|
903
1089
|
return JSON.parse(await transport.readFile(path));
|
|
904
1090
|
}
|
|
905
|
-
async function removeApplyJournal(targetRoot, adapter, transport = localTransport) {
|
|
906
|
-
await transport.rm(applyJournalPath(targetRoot, adapter));
|
|
907
|
-
await transport.rm(applyBackupDir(targetRoot, adapter));
|
|
1091
|
+
async function removeApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
1092
|
+
await transport.rm(applyJournalPath(targetRoot, adapter, scope));
|
|
1093
|
+
await transport.rm(applyBackupDir(targetRoot, adapter, scope));
|
|
908
1094
|
}
|
|
909
|
-
async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport) {
|
|
1095
|
+
async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
910
1096
|
const hadExisting = await transport.pathExists(operation.destPath);
|
|
911
1097
|
if (!hadExisting || transport.kind !== "local" || operation.action !== "update" && operation.action !== "remove" && operation.action !== "create") {
|
|
912
1098
|
return {
|
|
@@ -916,7 +1102,7 @@ async function recordBackup(operation, index, targetRoot, adapter, transport = l
|
|
|
916
1102
|
hadExisting
|
|
917
1103
|
};
|
|
918
1104
|
}
|
|
919
|
-
const backupPath = join3(applyBackupDir(targetRoot, adapter), String(index));
|
|
1105
|
+
const backupPath = join3(applyBackupDir(targetRoot, adapter, scope), String(index));
|
|
920
1106
|
await rm2(backupPath, { recursive: true, force: true });
|
|
921
1107
|
await mkdir4(dirname5(backupPath), { recursive: true });
|
|
922
1108
|
await cp(operation.destPath, backupPath, { recursive: operation.kind === "dir", dereference: true });
|
|
@@ -1069,14 +1255,14 @@ function normalizeOwners(owners) {
|
|
|
1069
1255
|
}
|
|
1070
1256
|
|
|
1071
1257
|
// src/install/apply.ts
|
|
1072
|
-
var
|
|
1258
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
1073
1259
|
async function applyCombinedInstallPlan(plan, options = {}) {
|
|
1074
1260
|
return applyPlanTransactionally(plan, options);
|
|
1075
1261
|
}
|
|
1076
|
-
async function recoverPendingApply(targetRoot, adapter, transport = localTransport) {
|
|
1077
|
-
const lock = await acquireApplyLock(targetRoot, adapter, transport);
|
|
1262
|
+
async function recoverPendingApply(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
1263
|
+
const lock = await acquireApplyLock(targetRoot, adapter, transport, {}, scope);
|
|
1078
1264
|
try {
|
|
1079
|
-
const journal = await readApplyJournal(targetRoot, adapter, transport);
|
|
1265
|
+
const journal = await readApplyJournal(targetRoot, adapter, transport, scope);
|
|
1080
1266
|
if (!journal) return void 0;
|
|
1081
1267
|
if (journal.operations.some((operation) => operation.action === "plugin" || operation.action === "program")) {
|
|
1082
1268
|
throw new Error("Cannot automatically recover a journal containing semantic plugin or programmatic operations");
|
|
@@ -1096,10 +1282,10 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
|
|
|
1096
1282
|
}
|
|
1097
1283
|
if (operationNeedsSource(operation) && operation.sourcePath && !await localPathExists(operation.sourcePath)) {
|
|
1098
1284
|
await rollbackStartedOperations(journal, transport);
|
|
1099
|
-
await removeApplyJournal(targetRoot, adapter, transport);
|
|
1285
|
+
await removeApplyJournal(targetRoot, adapter, transport, scope);
|
|
1100
1286
|
return void 0;
|
|
1101
1287
|
}
|
|
1102
|
-
const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport);
|
|
1288
|
+
const backup = started ?? await recordBackup(operation, index, targetRoot, adapter, transport, scope);
|
|
1103
1289
|
if (!started && isJournaledMutation(operation)) {
|
|
1104
1290
|
journal.completed.push(backup);
|
|
1105
1291
|
startedByIndex.set(index, backup);
|
|
@@ -1119,11 +1305,12 @@ async function recoverPendingApply(targetRoot, adapter, transport = localTranspo
|
|
|
1119
1305
|
}
|
|
1120
1306
|
async function applyPlanTransactionally(plan, options = {}) {
|
|
1121
1307
|
const transport = options.transport ?? localTransport;
|
|
1308
|
+
const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
|
|
1122
1309
|
if (plan.hasBlockingChanges) {
|
|
1123
1310
|
const blockers = plan.operations.filter((operation) => operation.action === "drift" || operation.action === "conflict");
|
|
1124
1311
|
throw new Error(`Refusing to apply with blocking changes: ${blockers.map((item) => item.relativeDestPath).join(", ")}`);
|
|
1125
1312
|
}
|
|
1126
|
-
const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock);
|
|
1313
|
+
const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, options.lock, scope);
|
|
1127
1314
|
try {
|
|
1128
1315
|
await assertBaseRevision(plan, transport);
|
|
1129
1316
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1131,6 +1318,8 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1131
1318
|
const journal = {
|
|
1132
1319
|
version: 1,
|
|
1133
1320
|
adapter: plan.adapter,
|
|
1321
|
+
installationType: plan.installationType,
|
|
1322
|
+
stateKey: plan.stateKey,
|
|
1134
1323
|
targetRoot: plan.targetRoot,
|
|
1135
1324
|
baseRevision: plan.baseRevision,
|
|
1136
1325
|
graphLockDigest,
|
|
@@ -1141,6 +1330,8 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1141
1330
|
manifest: {
|
|
1142
1331
|
version: 2,
|
|
1143
1332
|
adapter: plan.adapter,
|
|
1333
|
+
installationType: plan.installationType,
|
|
1334
|
+
stateKey: plan.stateKey,
|
|
1144
1335
|
targetRoot: plan.targetRoot,
|
|
1145
1336
|
generatedAt: now,
|
|
1146
1337
|
revision: "pending-apply-0000",
|
|
@@ -1156,7 +1347,7 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1156
1347
|
const entries = [];
|
|
1157
1348
|
for (const [index, operation] of plan.operations.entries()) {
|
|
1158
1349
|
assertOperationContained(operation, plan.targetRoot);
|
|
1159
|
-
const backup = isJournaledMutation(operation) ? await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport) : void 0;
|
|
1350
|
+
const backup = isJournaledMutation(operation) ? await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport, scope) : void 0;
|
|
1160
1351
|
if (backup) {
|
|
1161
1352
|
journal.completed.push(backup);
|
|
1162
1353
|
await writeApplyJournal(journal, transport);
|
|
@@ -1182,6 +1373,7 @@ async function applyPlanTransactionally(plan, options = {}) {
|
|
|
1182
1373
|
async function uninstall(plan, options = {}) {
|
|
1183
1374
|
const resolvedOptions = typeof options === "boolean" ? { dryRun: options } : options;
|
|
1184
1375
|
const transport = resolvedOptions.transport ?? localTransport;
|
|
1376
|
+
const scope = { installationType: plan.installationType, stateKey: plan.stateKey };
|
|
1185
1377
|
if (resolvedOptions.keepFiles && resolvedOptions.force) {
|
|
1186
1378
|
throw new Error("--keep-files cannot be combined with --force.");
|
|
1187
1379
|
}
|
|
@@ -1201,6 +1393,8 @@ async function uninstall(plan, options = {}) {
|
|
|
1201
1393
|
const finalManifest = withManifestRevision({
|
|
1202
1394
|
version: 2,
|
|
1203
1395
|
adapter: plan.adapter,
|
|
1396
|
+
installationType: plan.installationType,
|
|
1397
|
+
stateKey: plan.stateKey,
|
|
1204
1398
|
targetRoot: plan.targetRoot,
|
|
1205
1399
|
generatedAt: now,
|
|
1206
1400
|
revision: "pending-uninstall-0",
|
|
@@ -1218,13 +1412,15 @@ async function uninstall(plan, options = {}) {
|
|
|
1218
1412
|
});
|
|
1219
1413
|
}).sort((a, b) => a.path.localeCompare(b.path))
|
|
1220
1414
|
});
|
|
1221
|
-
const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock);
|
|
1415
|
+
const lock = await acquireApplyLock(plan.targetRoot, plan.adapter, transport, resolvedOptions.lock, scope);
|
|
1222
1416
|
try {
|
|
1223
1417
|
await assertBaseRevision(plan, transport);
|
|
1224
1418
|
const journal = {
|
|
1225
1419
|
version: 1,
|
|
1226
1420
|
mode: "uninstall",
|
|
1227
1421
|
adapter: plan.adapter,
|
|
1422
|
+
installationType: plan.installationType,
|
|
1423
|
+
stateKey: plan.stateKey,
|
|
1228
1424
|
targetRoot: plan.targetRoot,
|
|
1229
1425
|
baseRevision: plan.baseRevision,
|
|
1230
1426
|
graphLockDigest: plan.graphLockDigest,
|
|
@@ -1241,7 +1437,7 @@ async function uninstall(plan, options = {}) {
|
|
|
1241
1437
|
};
|
|
1242
1438
|
await writeApplyJournal(journal, transport);
|
|
1243
1439
|
for (const [index, operation] of (resolvedOptions.keepFiles ? [] : removable).entries()) {
|
|
1244
|
-
const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport);
|
|
1440
|
+
const backup = await recordBackup(operation, index, plan.targetRoot, plan.adapter, transport, scope);
|
|
1245
1441
|
journal.completed.push(backup);
|
|
1246
1442
|
await writeApplyJournal(journal, transport);
|
|
1247
1443
|
await applyOperation(operation, { transport, now, graphLockDigest: plan.graphLockDigest });
|
|
@@ -1267,15 +1463,24 @@ async function commitJournalState(journal, transport, entries, now) {
|
|
|
1267
1463
|
entries: entries ? entries.sort((a, b) => a.path.localeCompare(b.path)) : journal.manifest.entries
|
|
1268
1464
|
});
|
|
1269
1465
|
if (journal.mode === "uninstall" && manifest.entries.length === 0) {
|
|
1270
|
-
await removeStateFiles(journal.targetRoot, journal.adapter, transport
|
|
1466
|
+
await removeStateFiles(journal.targetRoot, journal.adapter, transport, {
|
|
1467
|
+
installationType: journal.installationType,
|
|
1468
|
+
stateKey: journal.stateKey
|
|
1469
|
+
});
|
|
1271
1470
|
} else {
|
|
1272
|
-
if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport
|
|
1471
|
+
if (journal.sourceLock) await writeSourceLock(journal.targetRoot, journal.adapter, journal.sourceLock, transport, {
|
|
1472
|
+
installationType: journal.installationType,
|
|
1473
|
+
stateKey: journal.stateKey
|
|
1474
|
+
});
|
|
1273
1475
|
await writeInstallManifest(manifest, transport);
|
|
1274
1476
|
}
|
|
1275
1477
|
if (journal.graphLockPath && journal.graphLock) await writeGraphLock(journal.graphLockPath, journal.graphLock);
|
|
1276
1478
|
if (journal.graphLockRemovePath) await rm3(journal.graphLockRemovePath, { force: true });
|
|
1277
1479
|
if (journal.workspaceConfigPath && journal.workspaceConfig) await writeJsonAtomic(journal.workspaceConfigPath, journal.workspaceConfig);
|
|
1278
|
-
await removeApplyJournal(journal.targetRoot, journal.adapter, transport
|
|
1480
|
+
await removeApplyJournal(journal.targetRoot, journal.adapter, transport, {
|
|
1481
|
+
installationType: journal.installationType,
|
|
1482
|
+
stateKey: journal.stateKey
|
|
1483
|
+
});
|
|
1279
1484
|
return manifest;
|
|
1280
1485
|
}
|
|
1281
1486
|
async function applyOperation(operation, context) {
|
|
@@ -1285,18 +1490,10 @@ async function applyOperation(operation, context) {
|
|
|
1285
1490
|
throw new Error(`Invalid plugin operation missing hash: ${operation.relativeDestPath}`);
|
|
1286
1491
|
}
|
|
1287
1492
|
if (context.executePlugins) {
|
|
1288
|
-
if (transport.kind !== "local") {
|
|
1289
|
-
throw new Error(`Cannot execute semantic plugin install over ${transport.description}. Run plugin installation on the remote host.`);
|
|
1290
|
-
}
|
|
1291
1493
|
if (!operation.semanticCommand || operation.semanticCommand.length === 0) {
|
|
1292
1494
|
throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
|
|
1293
1495
|
}
|
|
1294
|
-
|
|
1295
|
-
const args = operation.semanticCommand.slice(1);
|
|
1296
|
-
if (!command) {
|
|
1297
|
-
throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
|
|
1298
|
-
}
|
|
1299
|
-
await execFileAsync2(command, args);
|
|
1496
|
+
await executePluginInstall(operation, transport);
|
|
1300
1497
|
}
|
|
1301
1498
|
return manifestEntryForOperation(operation, {
|
|
1302
1499
|
now,
|
|
@@ -1376,6 +1573,32 @@ async function applyOperation(operation, context) {
|
|
|
1376
1573
|
}
|
|
1377
1574
|
return void 0;
|
|
1378
1575
|
}
|
|
1576
|
+
async function executePluginInstall(operation, transport) {
|
|
1577
|
+
const command = operation.semanticCommand?.[0];
|
|
1578
|
+
const args = operation.semanticCommand?.slice(1) ?? [];
|
|
1579
|
+
if (!command) {
|
|
1580
|
+
throw new Error(`Invalid plugin operation missing command: ${operation.relativeDestPath}`);
|
|
1581
|
+
}
|
|
1582
|
+
if (!operation.sourcePath) {
|
|
1583
|
+
throw new Error(`Invalid plugin operation missing source path: ${operation.relativeDestPath}`);
|
|
1584
|
+
}
|
|
1585
|
+
if (transport.kind === "local") {
|
|
1586
|
+
await execFileAsync3(command, args);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
if (!transport.execFile) {
|
|
1590
|
+
throw new Error(`Cannot execute semantic plugin install over ${transport.description}: transport does not support remote commands.`);
|
|
1591
|
+
}
|
|
1592
|
+
const stagingRoot = join4(operation.destPath, ".agentwheel", "plugin-staging", `${process.pid}-${Date.now()}`);
|
|
1593
|
+
const remoteSourcePath = join4(stagingRoot, basename2(operation.sourcePath));
|
|
1594
|
+
try {
|
|
1595
|
+
await transport.atomicCopy(operation.sourcePath, remoteSourcePath, operation.kind);
|
|
1596
|
+
const remoteArgs = args.map((arg) => arg === operation.sourcePath ? remoteSourcePath : arg);
|
|
1597
|
+
await transport.execFile(command, remoteArgs, { cwd: operation.destPath });
|
|
1598
|
+
} finally {
|
|
1599
|
+
await transport.rm(stagingRoot);
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1379
1602
|
async function entryForCompletedOperation(operation, transport, now, graphLockDigest) {
|
|
1380
1603
|
if (operation.action === "remove") return void 0;
|
|
1381
1604
|
if (operation.action === "create" || operation.action === "update") {
|
|
@@ -1423,7 +1646,10 @@ function manifestEntryForOperation(operation, values) {
|
|
|
1423
1646
|
};
|
|
1424
1647
|
}
|
|
1425
1648
|
async function assertBaseRevision(plan, transport) {
|
|
1426
|
-
const current = await readInstallManifest(plan.targetRoot, plan.adapter, transport
|
|
1649
|
+
const current = await readInstallManifest(plan.targetRoot, plan.adapter, transport, {
|
|
1650
|
+
installationType: plan.installationType,
|
|
1651
|
+
stateKey: plan.stateKey
|
|
1652
|
+
});
|
|
1427
1653
|
const currentRevision = current?.revision ?? null;
|
|
1428
1654
|
if (currentRevision !== plan.baseRevision) {
|
|
1429
1655
|
throw new Error(`Install manifest changed since planning for ${plan.adapter}; replan needed`);
|
|
@@ -1472,15 +1698,476 @@ async function mergeWithTransport(sourcePath, destPath, transport, merge) {
|
|
|
1472
1698
|
}
|
|
1473
1699
|
|
|
1474
1700
|
// src/install/plan.ts
|
|
1475
|
-
import { join as
|
|
1701
|
+
import { join as join8, relative as relative2 } from "path";
|
|
1702
|
+
|
|
1703
|
+
// src/staging/codex-subagents.ts
|
|
1704
|
+
import { mkdir as mkdir6, readFile as readFile7, writeFile as writeFile7 } from "fs/promises";
|
|
1705
|
+
import { basename as basename3, dirname as dirname7, join as join5 } from "path";
|
|
1706
|
+
var requiredCodexAgentFields = ["name", "description", "developer_instructions"];
|
|
1707
|
+
async function renderCodexSubagents(artifacts, stageRoot, adapter) {
|
|
1708
|
+
if (adapter?.name !== "codex") return artifacts;
|
|
1709
|
+
const names = /* @__PURE__ */ new Set();
|
|
1710
|
+
const rendered = [];
|
|
1711
|
+
for (const artifact of artifacts) {
|
|
1712
|
+
if (artifact.type !== "subagents") {
|
|
1713
|
+
rendered.push(artifact);
|
|
1714
|
+
continue;
|
|
1715
|
+
}
|
|
1716
|
+
const next = await renderCodexSubagent(artifact, stageRoot);
|
|
1717
|
+
if (names.has(next.name)) {
|
|
1718
|
+
throw new Error(`Codex subagents produce duplicate custom agent name '${next.name}'.`);
|
|
1719
|
+
}
|
|
1720
|
+
names.add(next.name);
|
|
1721
|
+
rendered.push(next);
|
|
1722
|
+
}
|
|
1723
|
+
return rendered;
|
|
1724
|
+
}
|
|
1725
|
+
async function renderCodexSubagent(artifact, stageRoot) {
|
|
1726
|
+
const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
|
|
1727
|
+
const agentName = codexAgentName(artifact);
|
|
1728
|
+
const renderedPath = join5(stageRoot, ".agentwheel-rendered", "codex-subagents", `${agentName}.toml`);
|
|
1729
|
+
const lowerSourcePath = sourcePath.toLowerCase();
|
|
1730
|
+
if (artifact.kind === "file" && (artifact.name.toLowerCase().endsWith(".toml") || lowerSourcePath.endsWith(".toml"))) {
|
|
1731
|
+
const content = await readFile7(sourcePath, "utf8");
|
|
1732
|
+
validateCodexAgentToml(content, sourcePath);
|
|
1733
|
+
return {
|
|
1734
|
+
...artifact,
|
|
1735
|
+
name: agentName,
|
|
1736
|
+
relativePath: join5("subagents", `${agentName}.toml`),
|
|
1737
|
+
kind: "file",
|
|
1738
|
+
hash: await hashPath(sourcePath)
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
const markdownPath = artifact.kind === "dir" ? join5(sourcePath, "AGENTS.md") : sourcePath;
|
|
1742
|
+
if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
|
|
1743
|
+
throw new Error(`Codex subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
|
|
1744
|
+
}
|
|
1745
|
+
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md") && !lowerSourcePath.endsWith(".md")) {
|
|
1746
|
+
throw new Error(`Codex subagent ${artifact.relativePath} must be a .toml file, .md file, or directory containing AGENTS.md.`);
|
|
1747
|
+
}
|
|
1748
|
+
const markdown = await readFile7(markdownPath, "utf8");
|
|
1749
|
+
const toml = markdownToCodexAgentToml(agentName, markdown);
|
|
1750
|
+
await mkdir6(dirname7(renderedPath), { recursive: true });
|
|
1751
|
+
await writeFile7(renderedPath, toml, "utf8");
|
|
1752
|
+
return {
|
|
1753
|
+
...artifact,
|
|
1754
|
+
name: agentName,
|
|
1755
|
+
sourcePath: renderedPath,
|
|
1756
|
+
stagedPath: renderedPath,
|
|
1757
|
+
relativePath: join5("subagents", `${agentName}.toml`),
|
|
1758
|
+
kind: "file",
|
|
1759
|
+
hash: await hashPath(renderedPath)
|
|
1760
|
+
};
|
|
1761
|
+
}
|
|
1762
|
+
function codexAgentName(artifact) {
|
|
1763
|
+
const raw = artifact.kind === "dir" ? artifact.name : basename3(artifact.name);
|
|
1764
|
+
return raw.replace(/\.toml$/i, "").replace(/\.md$/i, "");
|
|
1765
|
+
}
|
|
1766
|
+
function validateCodexAgentToml(content, path) {
|
|
1767
|
+
for (const field of requiredCodexAgentFields) {
|
|
1768
|
+
const pattern = new RegExp(`(^|\\n)\\s*${escapeRegExp(field)}\\s*=`, "m");
|
|
1769
|
+
if (!pattern.test(content)) {
|
|
1770
|
+
throw new Error(`Codex custom agent TOML ${path} is missing required field '${field}'.`);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
function markdownToCodexAgentToml(agentName, markdown) {
|
|
1775
|
+
const parsed = splitFrontmatter(markdown);
|
|
1776
|
+
const description = parsed.description ?? firstMeaningfulMarkdownLine(parsed.body) ?? `Custom Codex subagent ${agentName}.`;
|
|
1777
|
+
const developerInstructions = parsed.body.trim().length > 0 ? parsed.body.trimEnd() : description;
|
|
1778
|
+
return [
|
|
1779
|
+
`name = ${tomlString(agentName)}`,
|
|
1780
|
+
`description = ${tomlString(description)}`,
|
|
1781
|
+
`developer_instructions = ${tomlMultilineString(developerInstructions)}`,
|
|
1782
|
+
""
|
|
1783
|
+
].join("\n");
|
|
1784
|
+
}
|
|
1785
|
+
function splitFrontmatter(markdown) {
|
|
1786
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
|
|
1787
|
+
if (!match) return { body: markdown };
|
|
1788
|
+
const frontmatter = match[1] ?? "";
|
|
1789
|
+
const body = markdown.slice(match[0].length);
|
|
1790
|
+
const description = frontmatter.split(/\r?\n/).map((line) => /^description:\s*(?:"([^"]*)"|'([^']*)'|(.+))\s*$/.exec(line.trim())).find((item) => item !== null);
|
|
1791
|
+
return {
|
|
1792
|
+
body,
|
|
1793
|
+
description: description ? (description[1] ?? description[2] ?? description[3] ?? "").trim() : void 0
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
function firstMeaningfulMarkdownLine(markdown) {
|
|
1797
|
+
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
1798
|
+
const line = rawLine.trim();
|
|
1799
|
+
if (!line) continue;
|
|
1800
|
+
const heading = /^#{1,6}\s+(.+)$/.exec(line);
|
|
1801
|
+
return (heading?.[1] ?? line).trim();
|
|
1802
|
+
}
|
|
1803
|
+
return void 0;
|
|
1804
|
+
}
|
|
1805
|
+
function tomlString(value) {
|
|
1806
|
+
return JSON.stringify(value);
|
|
1807
|
+
}
|
|
1808
|
+
function tomlMultilineString(value) {
|
|
1809
|
+
const escaped = value.replaceAll("\\", "\\\\").replaceAll('"""', '\\"\\"\\"').replace(/\r\n?/g, "\n");
|
|
1810
|
+
return `"""
|
|
1811
|
+
${escaped.endsWith("\n") ? escaped : `${escaped}
|
|
1812
|
+
`}"""`;
|
|
1813
|
+
}
|
|
1814
|
+
function escapeRegExp(value) {
|
|
1815
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
// src/staging/copilot-artifacts.ts
|
|
1819
|
+
import { mkdir as mkdir7, readFile as readFile8, writeFile as writeFile8 } from "fs/promises";
|
|
1820
|
+
import { basename as basename4, dirname as dirname8, join as join6 } from "path";
|
|
1821
|
+
async function renderCopilotArtifacts(artifacts, stageRoot, adapter) {
|
|
1822
|
+
if (adapter?.name !== "copilot") return artifacts;
|
|
1823
|
+
const names = /* @__PURE__ */ new Set();
|
|
1824
|
+
const rendered = [];
|
|
1825
|
+
for (const artifact of artifacts) {
|
|
1826
|
+
if (artifact.type !== "subagents") {
|
|
1827
|
+
rendered.push(artifact);
|
|
1828
|
+
continue;
|
|
1829
|
+
}
|
|
1830
|
+
const next = await renderCopilotSubagent(artifact, stageRoot);
|
|
1831
|
+
if (names.has(next.name)) {
|
|
1832
|
+
throw new Error(`Copilot subagents produce duplicate custom agent name '${next.name}'.`);
|
|
1833
|
+
}
|
|
1834
|
+
names.add(next.name);
|
|
1835
|
+
rendered.push(next);
|
|
1836
|
+
}
|
|
1837
|
+
return rendered;
|
|
1838
|
+
}
|
|
1839
|
+
async function renderCopilotSubagent(artifact, stageRoot) {
|
|
1840
|
+
const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
|
|
1841
|
+
const agentName = copilotAgentName(artifact);
|
|
1842
|
+
const renderedPath = join6(stageRoot, ".agentwheel-rendered", "copilot-subagents", `${agentName}.agent.md`);
|
|
1843
|
+
const markdownPath = artifact.kind === "dir" ? join6(sourcePath, "AGENTS.md") : sourcePath;
|
|
1844
|
+
if (artifact.kind === "dir" && !await pathExists(markdownPath)) {
|
|
1845
|
+
throw new Error(`Copilot subagent directory ${artifact.relativePath} must contain AGENTS.md.`);
|
|
1846
|
+
}
|
|
1847
|
+
if (artifact.kind === "file" && !artifact.name.toLowerCase().endsWith(".md")) {
|
|
1848
|
+
throw new Error(`Copilot subagent ${artifact.relativePath} must be a .md file or directory containing AGENTS.md.`);
|
|
1849
|
+
}
|
|
1850
|
+
const markdown = await readFile8(markdownPath, "utf8");
|
|
1851
|
+
await mkdir7(dirname8(renderedPath), { recursive: true });
|
|
1852
|
+
await writeFile8(renderedPath, ensureCopilotAgentDescription(agentName, markdown), "utf8");
|
|
1853
|
+
return {
|
|
1854
|
+
...artifact,
|
|
1855
|
+
name: `${agentName}.agent.md`,
|
|
1856
|
+
sourcePath: renderedPath,
|
|
1857
|
+
stagedPath: renderedPath,
|
|
1858
|
+
relativePath: join6("subagents", `${agentName}.agent.md`),
|
|
1859
|
+
kind: "file",
|
|
1860
|
+
hash: await hashPath(renderedPath)
|
|
1861
|
+
};
|
|
1862
|
+
}
|
|
1863
|
+
function copilotAgentName(artifact) {
|
|
1864
|
+
const raw = artifact.kind === "dir" ? artifact.name : basename4(artifact.name);
|
|
1865
|
+
return raw.replace(/\.agent\.md$/i, "").replace(/\.md$/i, "");
|
|
1866
|
+
}
|
|
1867
|
+
function ensureCopilotAgentDescription(agentName, markdown) {
|
|
1868
|
+
const parsed = splitFrontmatter2(markdown);
|
|
1869
|
+
if (parsed.frontmatter !== void 0 && /^description\s*:/im.test(parsed.frontmatter)) {
|
|
1870
|
+
return markdown;
|
|
1871
|
+
}
|
|
1872
|
+
const description = firstMeaningfulMarkdownLine2(parsed.body) ?? `Custom Copilot agent ${agentName}.`;
|
|
1873
|
+
const frontmatter = parsed.frontmatter === void 0 ? `description: ${yamlString(description)}` : `${parsed.frontmatter.trimEnd()}
|
|
1874
|
+
description: ${yamlString(description)}`;
|
|
1875
|
+
return [
|
|
1876
|
+
"---",
|
|
1877
|
+
frontmatter,
|
|
1878
|
+
"---",
|
|
1879
|
+
"",
|
|
1880
|
+
parsed.body.trimStart()
|
|
1881
|
+
].join("\n");
|
|
1882
|
+
}
|
|
1883
|
+
function splitFrontmatter2(markdown) {
|
|
1884
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(markdown);
|
|
1885
|
+
if (!match) return { body: markdown };
|
|
1886
|
+
return {
|
|
1887
|
+
body: markdown.slice(match[0].length),
|
|
1888
|
+
frontmatter: match[1] ?? ""
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
function firstMeaningfulMarkdownLine2(markdown) {
|
|
1892
|
+
for (const rawLine of markdown.split(/\r?\n/)) {
|
|
1893
|
+
const line = rawLine.trim();
|
|
1894
|
+
if (!line) continue;
|
|
1895
|
+
const heading = /^#{1,6}\s+(.+)$/.exec(line);
|
|
1896
|
+
return (heading?.[1] ?? line).trim();
|
|
1897
|
+
}
|
|
1898
|
+
return void 0;
|
|
1899
|
+
}
|
|
1900
|
+
function yamlString(value) {
|
|
1901
|
+
return JSON.stringify(value);
|
|
1902
|
+
}
|
|
1476
1903
|
|
|
1477
1904
|
// src/targets/plugins/openclaw.ts
|
|
1478
1905
|
function openClawPluginInstallCommand(request) {
|
|
1479
|
-
return ["openclaw", "plugins", "install", "--
|
|
1906
|
+
return ["openclaw", "plugins", "install", "--force", request.path];
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
// src/validation/artifacts.ts
|
|
1910
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
1911
|
+
import { basename as basename5, join as join7 } from "path";
|
|
1912
|
+
async function validateArtifactsForInstall(artifacts, adapter, installationType) {
|
|
1913
|
+
const issues = [];
|
|
1914
|
+
for (const artifact of artifacts) {
|
|
1915
|
+
if (artifact.type === "fragments") continue;
|
|
1916
|
+
const target = targetMappingForArtifact(adapter, artifact.type, installationType);
|
|
1917
|
+
if (!target?.enabled) {
|
|
1918
|
+
issues.push({ artifact, message: `adapter ${adapter.name} does not support this artifact for installation type '${installationType}'` });
|
|
1919
|
+
continue;
|
|
1920
|
+
}
|
|
1921
|
+
issues.push(...await validateArtifact(artifact, target));
|
|
1922
|
+
}
|
|
1923
|
+
if (issues.length === 0) return;
|
|
1924
|
+
throw new Error([
|
|
1925
|
+
`Package artifacts are not installable for ${adapter.name}/${installationType}:`,
|
|
1926
|
+
...issues.map((issue) => `- ${artifactLabel(issue.artifact)}: ${issue.message}`)
|
|
1927
|
+
].join("\n"));
|
|
1928
|
+
}
|
|
1929
|
+
async function validateArtifact(artifact, target) {
|
|
1930
|
+
const issues = [];
|
|
1931
|
+
const format = artifact.format ?? await inferArtifactFormat(artifact, target) ?? semanticDefaultFormat(target);
|
|
1932
|
+
if (target.formats?.length) {
|
|
1933
|
+
if (!format) {
|
|
1934
|
+
issues.push({
|
|
1935
|
+
artifact,
|
|
1936
|
+
message: `format is unknown; expected one of: ${target.formats.join(", ")}`
|
|
1937
|
+
});
|
|
1938
|
+
} else if (!target.formats.includes(format)) {
|
|
1939
|
+
issues.push({
|
|
1940
|
+
artifact,
|
|
1941
|
+
message: `format '${format}' is not compatible; expected one of: ${target.formats.join(", ")}`
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
issues.push(...await validateKnownFormat(artifact, format, target));
|
|
1946
|
+
issues.push(...await validateGenericStructure(artifact, target));
|
|
1947
|
+
return issues;
|
|
1948
|
+
}
|
|
1949
|
+
async function inferArtifactFormat(artifact, target) {
|
|
1950
|
+
if (artifact.type === "rules") {
|
|
1951
|
+
if (hasExtension(artifact, ".rules")) return "codex-command-policy";
|
|
1952
|
+
if (hasExtension(artifact, ".md") || hasExtension(artifact, ".markdown")) return "markdown-rule";
|
|
1953
|
+
return void 0;
|
|
1954
|
+
}
|
|
1955
|
+
if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
|
|
1956
|
+
if (artifact.kind === "dir" && (await openClawPluginManifestPaths(artifact)).length > 0) return "openclaw-plugin";
|
|
1957
|
+
}
|
|
1958
|
+
return void 0;
|
|
1959
|
+
}
|
|
1960
|
+
function semanticDefaultFormat(target) {
|
|
1961
|
+
if (target.semantic === "openclaw-plugin") return "openclaw-plugin";
|
|
1962
|
+
return void 0;
|
|
1963
|
+
}
|
|
1964
|
+
async function validateKnownFormat(artifact, format, target) {
|
|
1965
|
+
if (!format) return [];
|
|
1966
|
+
if (format === "codex-command-policy") return validateCodexCommandPolicyRule(artifact);
|
|
1967
|
+
if (format === "markdown-rule" || format === "claude-markdown-rule" || format === "copilot-instruction-rule") {
|
|
1968
|
+
return validateMarkdownRule(artifact, format);
|
|
1969
|
+
}
|
|
1970
|
+
if (format === "openclaw-plugin" || target.semantic === "openclaw-plugin") {
|
|
1971
|
+
return validateOpenClawPlugin(artifact);
|
|
1972
|
+
}
|
|
1973
|
+
return [];
|
|
1974
|
+
}
|
|
1975
|
+
async function validateGenericStructure(artifact, target) {
|
|
1976
|
+
const issues = [];
|
|
1977
|
+
if (artifact.type === "skills") {
|
|
1978
|
+
if (artifact.kind === "dir") {
|
|
1979
|
+
const skillMd = join7(artifactPath(artifact), "SKILL.md");
|
|
1980
|
+
if (!await pathExists(skillMd)) {
|
|
1981
|
+
issues.push({ artifact, message: "skill directory must contain SKILL.md" });
|
|
1982
|
+
} else {
|
|
1983
|
+
issues.push(...await validateSkillFrontmatter(artifact, skillMd));
|
|
1984
|
+
}
|
|
1985
|
+
} else if (!hasExtension(artifact, ".md")) {
|
|
1986
|
+
issues.push({ artifact, message: "file skill artifacts must be Markdown files" });
|
|
1987
|
+
} else {
|
|
1988
|
+
issues.push(...await validateSkillFrontmatter(artifact, artifactPath(artifact)));
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (target.merge === "json-deep") {
|
|
1992
|
+
const parsed = await parseJsonObjectArtifact(artifact);
|
|
1993
|
+
if (!parsed.ok) issues.push({ artifact, message: parsed.message });
|
|
1994
|
+
}
|
|
1995
|
+
if (target.merge === "codex-toml-mcp") {
|
|
1996
|
+
const parsed = await parseJsonObjectArtifact(artifact);
|
|
1997
|
+
if (!parsed.ok) {
|
|
1998
|
+
issues.push({ artifact, message: parsed.message });
|
|
1999
|
+
} else if (Object.keys(extractMcpServers2(parsed.value)).length === 0) {
|
|
2000
|
+
issues.push({ artifact, message: "Codex MCP artifact must contain at least one server object, either under mcpServers or as top-level server entries" });
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
return issues;
|
|
2004
|
+
}
|
|
2005
|
+
async function validateSkillFrontmatter(artifact, skillMdPath) {
|
|
2006
|
+
let content;
|
|
2007
|
+
try {
|
|
2008
|
+
content = await readFile9(skillMdPath, "utf8");
|
|
2009
|
+
} catch (error) {
|
|
2010
|
+
return [{ artifact, message: `could not read SKILL.md: ${errorMessage(error)}` }];
|
|
2011
|
+
}
|
|
2012
|
+
if (content.charCodeAt(0) === 65279) {
|
|
2013
|
+
return [{ artifact, message: "SKILL.md must not start with a UTF-8 BOM; YAML frontmatter must be the first bytes" }];
|
|
2014
|
+
}
|
|
2015
|
+
const isDelimiter = (line) => line.trimEnd() === "---";
|
|
2016
|
+
const lines = content.split(/\r?\n/);
|
|
2017
|
+
if (!isDelimiter(lines[0] ?? "")) {
|
|
2018
|
+
return [{ artifact, message: "SKILL.md must begin with YAML frontmatter delimited by '---' on the first line (no content before it)" }];
|
|
2019
|
+
}
|
|
2020
|
+
const closing = lines.findIndex((line, index) => index > 0 && isDelimiter(line));
|
|
2021
|
+
if (closing === -1) {
|
|
2022
|
+
return [{ artifact, message: "SKILL.md frontmatter is not closed with a '---' delimiter" }];
|
|
2023
|
+
}
|
|
2024
|
+
const issues = [];
|
|
2025
|
+
const frontmatter = lines.slice(1, closing);
|
|
2026
|
+
if (!frontmatter.some((line) => /^name\s*:/.test(line))) {
|
|
2027
|
+
issues.push({ artifact, message: "SKILL.md frontmatter must define 'name'" });
|
|
2028
|
+
}
|
|
2029
|
+
if (!frontmatter.some((line) => /^description\s*:/.test(line))) {
|
|
2030
|
+
issues.push({ artifact, message: "SKILL.md frontmatter must define 'description'" });
|
|
2031
|
+
}
|
|
2032
|
+
return issues;
|
|
2033
|
+
}
|
|
2034
|
+
async function validateCodexCommandPolicyRule(artifact) {
|
|
2035
|
+
const issues = [];
|
|
2036
|
+
if (artifact.type !== "rules") {
|
|
2037
|
+
return [{ artifact, message: "codex-command-policy format is only valid for rules artifacts" }];
|
|
2038
|
+
}
|
|
2039
|
+
if (artifact.kind !== "file") {
|
|
2040
|
+
issues.push({ artifact, message: "Codex command-policy rules must be files" });
|
|
2041
|
+
}
|
|
2042
|
+
if (!hasExtension(artifact, ".rules")) {
|
|
2043
|
+
issues.push({ artifact, message: "Codex command-policy rules must use the .rules extension" });
|
|
2044
|
+
}
|
|
2045
|
+
const content = await readUtf8Artifact(artifact, issues);
|
|
2046
|
+
if (content === void 0) return issues;
|
|
2047
|
+
if (!/\bprefix_rule\s*\(/.test(content)) {
|
|
2048
|
+
issues.push({ artifact, message: "Codex command-policy rules must contain at least one prefix_rule(...)" });
|
|
2049
|
+
}
|
|
2050
|
+
if (/\bprefix_rule\s*\(/.test(content) && !/\bpattern\s*=/.test(content)) {
|
|
2051
|
+
issues.push({ artifact, message: "Codex command-policy rules must define a pattern field" });
|
|
2052
|
+
}
|
|
2053
|
+
for (const match of content.matchAll(/\bdecision\s*=\s*["']([^"']+)["']/g)) {
|
|
2054
|
+
const decision = match[1];
|
|
2055
|
+
if (decision !== "allow" && decision !== "prompt" && decision !== "forbidden") {
|
|
2056
|
+
issues.push({ artifact, message: `Codex command-policy decision '${decision}' must be allow, prompt, or forbidden` });
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
return issues;
|
|
2060
|
+
}
|
|
2061
|
+
function validateMarkdownRule(artifact, format) {
|
|
2062
|
+
const label = format === "copilot-instruction-rule" ? "Copilot instruction rules" : format === "claude-markdown-rule" ? "Claude markdown rules" : "Markdown rules";
|
|
2063
|
+
const issues = [];
|
|
2064
|
+
if (artifact.type !== "rules") {
|
|
2065
|
+
issues.push({ artifact, message: `${format} format is only valid for rules artifacts` });
|
|
2066
|
+
}
|
|
2067
|
+
if (artifact.kind !== "file") {
|
|
2068
|
+
issues.push({ artifact, message: `${label} must be files` });
|
|
2069
|
+
}
|
|
2070
|
+
if (!hasExtension(artifact, ".md") && !hasExtension(artifact, ".markdown")) {
|
|
2071
|
+
issues.push({ artifact, message: `${label} must use a Markdown extension` });
|
|
2072
|
+
}
|
|
2073
|
+
return issues;
|
|
2074
|
+
}
|
|
2075
|
+
async function validateOpenClawPlugin(artifact) {
|
|
2076
|
+
const issues = [];
|
|
2077
|
+
if (artifact.type !== "plugins") {
|
|
2078
|
+
return [{ artifact, message: "openclaw-plugin format is only valid for plugins artifacts" }];
|
|
2079
|
+
}
|
|
2080
|
+
if (artifact.kind !== "dir") {
|
|
2081
|
+
return [{ artifact, message: "OpenClaw plugins must be directory artifacts" }];
|
|
2082
|
+
}
|
|
2083
|
+
const manifestPaths = await openClawPluginManifestPaths(artifact);
|
|
2084
|
+
if (manifestPaths.length === 0) {
|
|
2085
|
+
return [{ artifact, message: "OpenClaw plugins must contain plugin.json or openclaw.plugin.json" }];
|
|
2086
|
+
}
|
|
2087
|
+
const parsed = await Promise.all(manifestPaths.map(async (manifestPath) => {
|
|
2088
|
+
const result = await parseOpenClawPluginManifest(manifestPath);
|
|
2089
|
+
if (!result.ok) issues.push({ artifact, message: result.message });
|
|
2090
|
+
return result;
|
|
2091
|
+
}));
|
|
2092
|
+
const names = new Set(parsed.filter((result) => result.ok).map((result) => result.name));
|
|
2093
|
+
if (names.size > 1) {
|
|
2094
|
+
issues.push({ artifact, message: "OpenClaw plugin descriptors must declare the same name" });
|
|
2095
|
+
}
|
|
2096
|
+
return issues;
|
|
2097
|
+
}
|
|
2098
|
+
async function openClawPluginManifestPaths(artifact) {
|
|
2099
|
+
const root = artifactPath(artifact);
|
|
2100
|
+
const candidates = [join7(root, "plugin.json"), join7(root, "openclaw.plugin.json")];
|
|
2101
|
+
const existing = await Promise.all(candidates.map(async (candidate) => await pathExists(candidate) ? candidate : void 0));
|
|
2102
|
+
return existing.filter((candidate) => candidate !== void 0);
|
|
2103
|
+
}
|
|
2104
|
+
async function parseOpenClawPluginManifest(manifestPath) {
|
|
2105
|
+
const manifestName = basename5(manifestPath);
|
|
2106
|
+
try {
|
|
2107
|
+
const parsed = JSON.parse(await readFile9(manifestPath, "utf8"));
|
|
2108
|
+
if (!isRecord3(parsed)) {
|
|
2109
|
+
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be a JSON object` };
|
|
2110
|
+
}
|
|
2111
|
+
if (typeof parsed.name !== "string" || parsed.name.trim().length === 0) {
|
|
2112
|
+
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must declare a non-empty name` };
|
|
2113
|
+
}
|
|
2114
|
+
return { ok: true, path: manifestPath, name: parsed.name.trim() };
|
|
2115
|
+
} catch (error) {
|
|
2116
|
+
return { ok: false, path: manifestPath, message: `OpenClaw ${manifestName} must be valid JSON: ${errorMessage(error)}` };
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
async function readUtf8Artifact(artifact, issues) {
|
|
2120
|
+
if (artifact.kind !== "file") return void 0;
|
|
2121
|
+
try {
|
|
2122
|
+
return await readFile9(artifactPath(artifact), "utf8");
|
|
2123
|
+
} catch (error) {
|
|
2124
|
+
issues.push({ artifact, message: `could not read artifact: ${errorMessage(error)}` });
|
|
2125
|
+
return void 0;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
async function parseJsonObjectArtifact(artifact) {
|
|
2129
|
+
if (artifact.kind !== "file") {
|
|
2130
|
+
return { ok: false, message: "merge artifacts must be JSON files" };
|
|
2131
|
+
}
|
|
2132
|
+
try {
|
|
2133
|
+
const parsed = JSON.parse(await readFile9(artifactPath(artifact), "utf8"));
|
|
2134
|
+
if (!isRecord3(parsed)) return { ok: false, message: "merge artifacts must contain a JSON object" };
|
|
2135
|
+
return { ok: true, value: parsed };
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
return { ok: false, message: `merge artifact must be valid JSON: ${errorMessage(error)}` };
|
|
2138
|
+
}
|
|
2139
|
+
}
|
|
2140
|
+
function extractMcpServers2(source) {
|
|
2141
|
+
const raw = isRecord3(source.mcpServers) ? source.mcpServers : source;
|
|
2142
|
+
const servers = {};
|
|
2143
|
+
for (const [name, value] of Object.entries(raw)) {
|
|
2144
|
+
if (isRecord3(value)) servers[name] = value;
|
|
2145
|
+
}
|
|
2146
|
+
return servers;
|
|
2147
|
+
}
|
|
2148
|
+
function artifactPath(artifact) {
|
|
2149
|
+
return artifact.stagedPath ?? artifact.sourcePath;
|
|
2150
|
+
}
|
|
2151
|
+
function artifactLabel(artifact) {
|
|
2152
|
+
const owner = artifact.packageName ? `${artifact.packageName}:` : "";
|
|
2153
|
+
return `${owner}${artifact.type}/${artifact.name}`;
|
|
2154
|
+
}
|
|
2155
|
+
function hasExtension(artifact, extension) {
|
|
2156
|
+
return basename5(artifact.name).toLowerCase().endsWith(extension) || basename5(artifactPath(artifact)).toLowerCase().endsWith(extension);
|
|
2157
|
+
}
|
|
2158
|
+
function isRecord3(value) {
|
|
2159
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2160
|
+
}
|
|
2161
|
+
function errorMessage(error) {
|
|
2162
|
+
return error instanceof Error ? error.message : String(error);
|
|
1480
2163
|
}
|
|
1481
2164
|
|
|
1482
2165
|
// src/install/plan.ts
|
|
1483
2166
|
async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
|
|
2167
|
+
const requestedInstallationType = options.installationType ?? defaultInstallationType;
|
|
2168
|
+
const installationType = resolveInstallationTypeForArtifacts(adapter, desiredArtifacts.map((artifact) => artifact.type), requestedInstallationType);
|
|
2169
|
+
const installRoot = installRootForArtifacts(adapter, targetRoot, installationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
2170
|
+
await validateArtifactsForInstall(desiredArtifacts, adapter, installationType);
|
|
1484
2171
|
for (const artifact of desiredArtifacts) {
|
|
1485
2172
|
if (artifact.meta.dependencyRole !== "root" && isGuardedMergeTarget(artifact.type)) {
|
|
1486
2173
|
throw new Error(`Dependency-provided ${artifact.type} artifacts cannot be installed until per-subentry ownership exists: ${artifact.type}/${artifact.name}`);
|
|
@@ -1488,13 +2175,13 @@ async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot,
|
|
|
1488
2175
|
}
|
|
1489
2176
|
const desired = [];
|
|
1490
2177
|
for (const artifact of desiredArtifacts) {
|
|
1491
|
-
const op = operationForArtifact(artifact, adapter,
|
|
2178
|
+
const op = operationForArtifact(artifact, adapter, installRoot, installationType, artifact.meta);
|
|
1492
2179
|
if (op) {
|
|
1493
2180
|
desired.push(op);
|
|
1494
2181
|
}
|
|
1495
2182
|
}
|
|
1496
|
-
await addProgrammaticOperations(desired, adapter,
|
|
1497
|
-
return createPlanFromOperations(desired, adapter,
|
|
2183
|
+
await addProgrammaticOperations(desired, adapter, installRoot);
|
|
2184
|
+
return createPlanFromOperations(desired, adapter, installRoot, manifest, transport, { ...options, installationType });
|
|
1498
2185
|
}
|
|
1499
2186
|
async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifest, transport, options) {
|
|
1500
2187
|
const workspaceOwner = options.workspaceOwner;
|
|
@@ -1556,11 +2243,38 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1556
2243
|
}
|
|
1557
2244
|
const currentHash = await transport.hashPath(op.destPath);
|
|
1558
2245
|
if (!existing) {
|
|
1559
|
-
|
|
2246
|
+
if (currentHash === op.desiredHash && options.forceConflict) {
|
|
2247
|
+
operations.push({
|
|
2248
|
+
...op,
|
|
2249
|
+
action: "skip",
|
|
2250
|
+
currentHash,
|
|
2251
|
+
reason: "force adopting unmanaged destination with matching hash"
|
|
2252
|
+
});
|
|
2253
|
+
} else if (options.replaceConflict) {
|
|
2254
|
+
operations.push({
|
|
2255
|
+
...op,
|
|
2256
|
+
action: "update",
|
|
2257
|
+
currentHash,
|
|
2258
|
+
reason: "force replacing unmanaged destination"
|
|
2259
|
+
});
|
|
2260
|
+
} else {
|
|
2261
|
+
operations.push({ ...op, action: "conflict", currentHash, reason: "destination exists but is not managed" });
|
|
2262
|
+
}
|
|
1560
2263
|
continue;
|
|
1561
2264
|
}
|
|
1562
2265
|
if (currentHash !== existing.hash) {
|
|
1563
2266
|
const composedFromDiff = changedComposedSelectors(op.composedFrom, existing.composedFrom);
|
|
2267
|
+
if (options.forceDrift) {
|
|
2268
|
+
operations.push({
|
|
2269
|
+
...op,
|
|
2270
|
+
action: "update",
|
|
2271
|
+
currentHash,
|
|
2272
|
+
manifestHash: existing.hash,
|
|
2273
|
+
reason: reasonWithComposedDiff("force replacing drifted managed destination", op.composedFrom, existing.composedFrom),
|
|
2274
|
+
composedFromDiff
|
|
2275
|
+
});
|
|
2276
|
+
continue;
|
|
2277
|
+
}
|
|
1564
2278
|
operations.push({
|
|
1565
2279
|
...op,
|
|
1566
2280
|
action: "drift",
|
|
@@ -1587,7 +2301,7 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1587
2301
|
}
|
|
1588
2302
|
for (const entry of effectiveEntries) {
|
|
1589
2303
|
if (desired.has(entry.path)) continue;
|
|
1590
|
-
const destPath =
|
|
2304
|
+
const destPath = join8(targetRoot, entry.path);
|
|
1591
2305
|
if (!await transport.pathExists(destPath)) continue;
|
|
1592
2306
|
const currentHash = await transport.hashPath(destPath);
|
|
1593
2307
|
if (workspaceOwner && !entryOwnedByWorkspace(entry, workspaceOwner)) {
|
|
@@ -1608,7 +2322,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1608
2322
|
channel: entry.channel,
|
|
1609
2323
|
packageName: entry.packageName,
|
|
1610
2324
|
composedFrom: entry.composedFrom,
|
|
1611
|
-
...operationMetadataFromEntry(entry)
|
|
2325
|
+
...operationMetadataFromEntry(entry),
|
|
2326
|
+
...options.forceDrift ? { action: "remove", reason: "force removing drifted stale managed destination" } : {}
|
|
1612
2327
|
});
|
|
1613
2328
|
} else {
|
|
1614
2329
|
operations.push({
|
|
@@ -1632,6 +2347,8 @@ async function createPlanFromOperations(desiredOps, adapter, targetRoot, manifes
|
|
|
1632
2347
|
operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
|
|
1633
2348
|
return {
|
|
1634
2349
|
adapter: adapter.name,
|
|
2350
|
+
installationType: options.installationType ?? defaultInstallationType,
|
|
2351
|
+
stateKey: options.stateKey,
|
|
1635
2352
|
targetRoot,
|
|
1636
2353
|
operations,
|
|
1637
2354
|
hasBlockingChanges: operations.some((op) => op.action === "drift" || op.action === "conflict"),
|
|
@@ -1694,7 +2411,7 @@ async function canStrictlyAdoptLegacyEntry(entry, op, targetRoot, transport) {
|
|
|
1694
2411
|
if (entry.artifactType !== op.artifactType || entry.artifactName !== op.artifactName) return false;
|
|
1695
2412
|
if (!op.desiredHash || entry.sourceHash !== op.desiredHash) return false;
|
|
1696
2413
|
if (!packageIdentityMatches(entry, op)) return false;
|
|
1697
|
-
const destPath =
|
|
2414
|
+
const destPath = join8(targetRoot, entry.path);
|
|
1698
2415
|
if (!await transport.pathExists(destPath)) return false;
|
|
1699
2416
|
return await transport.hashPath(destPath) === entry.hash;
|
|
1700
2417
|
}
|
|
@@ -1824,7 +2541,7 @@ function keepForeignManifestEntryOperation(entry, targetRoot, workspaceOwner, op
|
|
|
1824
2541
|
artifactType: entry.artifactType,
|
|
1825
2542
|
artifactName: entry.artifactName,
|
|
1826
2543
|
kind: entry.kind,
|
|
1827
|
-
destPath: operation?.destPath ??
|
|
2544
|
+
destPath: operation?.destPath ?? join8(targetRoot, entry.path),
|
|
1828
2545
|
relativeDestPath: entry.path,
|
|
1829
2546
|
desiredHash: entry.sourceHash,
|
|
1830
2547
|
currentHash: currentHash ?? operation?.currentHash ?? entry.hash,
|
|
@@ -1846,11 +2563,18 @@ function normalizeOperationOwners(op) {
|
|
|
1846
2563
|
function isGuardedMergeTarget(type) {
|
|
1847
2564
|
return type === "mcp" || type === "hooks" || type === "settings" || type === "plugins";
|
|
1848
2565
|
}
|
|
1849
|
-
function operationForArtifact(artifact, adapter, targetRoot, meta) {
|
|
1850
|
-
|
|
1851
|
-
|
|
2566
|
+
function operationForArtifact(artifact, adapter, targetRoot, installationType, meta) {
|
|
2567
|
+
if (artifact.type === "fragments") return void 0;
|
|
2568
|
+
const target = targetMappingForArtifact(adapter, artifact.type, installationType);
|
|
2569
|
+
if (!target?.enabled) {
|
|
2570
|
+
const supported = Object.keys(adapter.targets[artifact.type] ?? {});
|
|
2571
|
+
const suffix = supported.length > 0 ? ` Supported installation types: ${supported.join(", ")}` : "";
|
|
2572
|
+
throw new Error(`Adapter ${adapter.name} does not support ${artifact.type}/${artifact.name} for installation type '${installationType}'.${suffix}`);
|
|
2573
|
+
}
|
|
1852
2574
|
const metadata = operationMetadataFromDesired(artifact, meta);
|
|
1853
|
-
const
|
|
2575
|
+
const rawInstallName = metadata.installName ?? artifact.name;
|
|
2576
|
+
assertSafeInstallName(rawInstallName, `${artifact.type}/${artifact.name}`);
|
|
2577
|
+
const installName = semanticInstallName(artifact, target.semantic, rawInstallName);
|
|
1854
2578
|
assertSafeInstallName(installName, `${artifact.type}/${artifact.name}`);
|
|
1855
2579
|
if (artifact.type === "plugins" && target.semantic === "openclaw-plugin") {
|
|
1856
2580
|
const sourcePath = artifact.stagedPath ?? artifact.sourcePath;
|
|
@@ -1871,7 +2595,26 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
|
|
|
1871
2595
|
...metadata
|
|
1872
2596
|
};
|
|
1873
2597
|
}
|
|
1874
|
-
|
|
2598
|
+
if (artifact.type === "subagents" && target.semantic === "codex-subagent") {
|
|
2599
|
+
const destPath2 = join8(targetRoot, target.dest, `${installName.replace(/\.toml$/i, "")}.toml`);
|
|
2600
|
+
return {
|
|
2601
|
+
action: "create",
|
|
2602
|
+
artifactType: artifact.type,
|
|
2603
|
+
artifactName: artifact.name,
|
|
2604
|
+
kind: "file",
|
|
2605
|
+
sourcePath: artifact.stagedPath ?? artifact.sourcePath,
|
|
2606
|
+
destPath: destPath2,
|
|
2607
|
+
relativeDestPath: relative2(targetRoot, destPath2).replaceAll("\\", "/"),
|
|
2608
|
+
desiredHash: artifact.hash,
|
|
2609
|
+
reason: "destination missing",
|
|
2610
|
+
channel: artifact.channel ?? "managed",
|
|
2611
|
+
packageName: artifact.packageName,
|
|
2612
|
+
composedFrom: metadata.composedFrom,
|
|
2613
|
+
...metadata,
|
|
2614
|
+
installName: installName.replace(/\.toml$/i, "")
|
|
2615
|
+
};
|
|
2616
|
+
}
|
|
2617
|
+
const destPath = artifact.type === "instructions" || artifact.type === "settings" || isFileTarget(target.dest) ? join8(targetRoot, target.dest) : join8(targetRoot, target.dest, installName);
|
|
1875
2618
|
return {
|
|
1876
2619
|
action: "create",
|
|
1877
2620
|
artifactType: artifact.type,
|
|
@@ -1886,9 +2629,27 @@ function operationForArtifact(artifact, adapter, targetRoot, meta) {
|
|
|
1886
2629
|
packageName: artifact.packageName,
|
|
1887
2630
|
mergeStrategy: target.merge,
|
|
1888
2631
|
composedFrom: metadata.composedFrom,
|
|
1889
|
-
...metadata
|
|
2632
|
+
...metadata,
|
|
2633
|
+
installName
|
|
1890
2634
|
};
|
|
1891
2635
|
}
|
|
2636
|
+
function semanticInstallName(artifact, semantic, installName) {
|
|
2637
|
+
if (artifact.type === "rules" && semantic === "copilot-instruction") {
|
|
2638
|
+
return withExtension(installName, ".instructions.md", [".instructions.md", ".md"]);
|
|
2639
|
+
}
|
|
2640
|
+
if (artifact.type === "commands" && semantic === "copilot-prompt") {
|
|
2641
|
+
return withExtension(installName, ".prompt.md", [".prompt.md", ".md"]);
|
|
2642
|
+
}
|
|
2643
|
+
if (artifact.type === "subagents" && semantic === "copilot-agent") {
|
|
2644
|
+
return withExtension(installName, ".agent.md", [".agent.md", ".md"]);
|
|
2645
|
+
}
|
|
2646
|
+
return installName;
|
|
2647
|
+
}
|
|
2648
|
+
function withExtension(name, targetExtension, knownExtensions) {
|
|
2649
|
+
const match = knownExtensions.find((extension) => name.toLowerCase().endsWith(extension.toLowerCase()));
|
|
2650
|
+
const base = match ? name.slice(0, -match.length) : name;
|
|
2651
|
+
return `${base}${targetExtension}`;
|
|
2652
|
+
}
|
|
1892
2653
|
function isFileTarget(dest) {
|
|
1893
2654
|
return /\.(json|jsonc|toml|md)$/i.test(dest);
|
|
1894
2655
|
}
|
|
@@ -1923,13 +2684,19 @@ function summarizePlan(plan) {
|
|
|
1923
2684
|
}
|
|
1924
2685
|
return summary;
|
|
1925
2686
|
}
|
|
2687
|
+
function isPendingInstallAction(action) {
|
|
2688
|
+
return action !== "skip" && action !== "keep";
|
|
2689
|
+
}
|
|
2690
|
+
function isPendingInstallOperation(operation) {
|
|
2691
|
+
return isPendingInstallAction(operation.action);
|
|
2692
|
+
}
|
|
1926
2693
|
|
|
1927
2694
|
// src/install/uninstall.ts
|
|
1928
|
-
import { join as
|
|
2695
|
+
import { join as join9 } from "path";
|
|
1929
2696
|
async function createUninstallPlan(manifest, transport = localTransport) {
|
|
1930
2697
|
const operations = [];
|
|
1931
2698
|
for (const entry of manifest.entries) {
|
|
1932
|
-
const destPath =
|
|
2699
|
+
const destPath = join9(manifest.targetRoot, entry.path);
|
|
1933
2700
|
if (!await transport.pathExists(destPath)) continue;
|
|
1934
2701
|
const currentHash = await transport.hashPath(destPath);
|
|
1935
2702
|
if (currentHash !== entry.hash) {
|
|
@@ -1975,6 +2742,8 @@ async function createUninstallPlan(manifest, transport = localTransport) {
|
|
|
1975
2742
|
operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
|
|
1976
2743
|
return {
|
|
1977
2744
|
adapter: manifest.adapter,
|
|
2745
|
+
installationType: "installationType" in manifest ? manifest.installationType : defaultInstallationType,
|
|
2746
|
+
stateKey: "stateKey" in manifest ? manifest.stateKey : void 0,
|
|
1978
2747
|
targetRoot: manifest.targetRoot,
|
|
1979
2748
|
operations,
|
|
1980
2749
|
hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
|
|
@@ -1983,13 +2752,15 @@ async function createUninstallPlan(manifest, transport = localTransport) {
|
|
|
1983
2752
|
};
|
|
1984
2753
|
}
|
|
1985
2754
|
async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter, transport = localTransport, options = {}) {
|
|
1986
|
-
const
|
|
2755
|
+
const installationType = "installationType" in manifest ? manifest.installationType : defaultInstallationType;
|
|
2756
|
+
const stateKey = "stateKey" in manifest ? manifest.stateKey : void 0;
|
|
2757
|
+
const desiredPlan = await createCombinedInstallPlan(remainingDesired, adapter, manifest.targetRoot, void 0, transport, { installationType, stateKey });
|
|
1987
2758
|
const ownersByPath = new Map(
|
|
1988
2759
|
desiredPlan.operations.filter((operation) => operation.owners?.length).map((operation) => [operation.relativeDestPath, operation.owners ?? []])
|
|
1989
2760
|
);
|
|
1990
2761
|
const operations = [];
|
|
1991
2762
|
for (const entry of manifest.entries) {
|
|
1992
|
-
const destPath =
|
|
2763
|
+
const destPath = join9(manifest.targetRoot, entry.path);
|
|
1993
2764
|
if (!await transport.pathExists(destPath)) continue;
|
|
1994
2765
|
const currentHash = await transport.hashPath(destPath);
|
|
1995
2766
|
const remainingOwners = ownersByPath.get(entry.path) ?? [];
|
|
@@ -2060,6 +2831,8 @@ async function createOwnershipUninstallPlan(manifest, remainingDesired, adapter,
|
|
|
2060
2831
|
operations.sort((a, b) => a.relativeDestPath.localeCompare(b.relativeDestPath));
|
|
2061
2832
|
return {
|
|
2062
2833
|
adapter: manifest.adapter,
|
|
2834
|
+
installationType,
|
|
2835
|
+
stateKey,
|
|
2063
2836
|
targetRoot: manifest.targetRoot,
|
|
2064
2837
|
operations,
|
|
2065
2838
|
hasBlockingChanges: operations.some((operation) => operation.action === "conflict"),
|
|
@@ -2107,7 +2880,7 @@ var channelLabels = {
|
|
|
2107
2880
|
ejected: "EJECTED"
|
|
2108
2881
|
};
|
|
2109
2882
|
function formatPlan(plan) {
|
|
2110
|
-
const lines = [`Plan for ${plan.adapter} at ${plan.targetRoot}`];
|
|
2883
|
+
const lines = [`Plan for ${plan.adapter}/${plan.installationType} at ${plan.targetRoot}`];
|
|
2111
2884
|
if (plan.migrationReport) {
|
|
2112
2885
|
const dropped = plan.migrationReport.dropped.length > 0 ? `; dropped unmanaged ${plan.migrationReport.dropped.join(", ")}` : "";
|
|
2113
2886
|
lines.push(`MIGRATE adopted ${plan.migrationReport.adopted} legacy entries${dropped}`);
|
|
@@ -2279,15 +3052,15 @@ function ownerChains(lock, nodeId) {
|
|
|
2279
3052
|
}
|
|
2280
3053
|
|
|
2281
3054
|
// src/source/git.ts
|
|
2282
|
-
import { execFile as
|
|
2283
|
-
import { cp as cp2, mkdir as
|
|
2284
|
-
import { homedir } from "os";
|
|
2285
|
-
import { basename as
|
|
2286
|
-
import { promisify as
|
|
3055
|
+
import { execFile as execFile4 } from "child_process";
|
|
3056
|
+
import { cp as cp2, mkdir as mkdir8, rename as rename3, rm as rm4, writeFile as writeFile9 } from "fs/promises";
|
|
3057
|
+
import { homedir as homedir2 } from "os";
|
|
3058
|
+
import { basename as basename7, dirname as dirname9, join as join12, resolve as resolve6 } from "path";
|
|
3059
|
+
import { promisify as promisify4 } from "util";
|
|
2287
3060
|
|
|
2288
3061
|
// src/model/package.ts
|
|
2289
|
-
import { readFile as
|
|
2290
|
-
import { join as
|
|
3062
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
3063
|
+
import { join as join10 } from "path";
|
|
2291
3064
|
import { parse as parse2, printParseErrorCode as printParseErrorCode2 } from "jsonc-parser";
|
|
2292
3065
|
import { z as z5 } from "zod";
|
|
2293
3066
|
var legacyArtifactTypeSchema = z5.enum([
|
|
@@ -2304,10 +3077,12 @@ var legacyArtifactTypeSchema = z5.enum([
|
|
|
2304
3077
|
var runtimeListSchema = z5.array(z5.string().min(1));
|
|
2305
3078
|
var packageProvideBaseSchema = z5.object({
|
|
2306
3079
|
path: z5.string().min(1),
|
|
3080
|
+
format: artifactFormatSchema.optional(),
|
|
2307
3081
|
assets: z5.array(packageAssetSchema).optional(),
|
|
2308
3082
|
required: z5.boolean().optional()
|
|
2309
3083
|
});
|
|
2310
3084
|
var packageItemSchema = z5.object({
|
|
3085
|
+
format: artifactFormatSchema.optional(),
|
|
2311
3086
|
requires: z5.array(packageItemRequireSchema).optional(),
|
|
2312
3087
|
compose: z5.array(packageComposeEntrySchema).optional(),
|
|
2313
3088
|
runtimes: runtimeListSchema.optional()
|
|
@@ -2362,7 +3137,7 @@ var packageManifestNames = [...openPackManifestNames, ...legacyPackageManifestNa
|
|
|
2362
3137
|
var warnedLegacyManifestPaths = /* @__PURE__ */ new Set();
|
|
2363
3138
|
async function findPackageManifestPath(root, options = {}) {
|
|
2364
3139
|
for (const name of packageManifestNames) {
|
|
2365
|
-
const candidate =
|
|
3140
|
+
const candidate = join10(root, name);
|
|
2366
3141
|
if (!await pathExists(candidate)) continue;
|
|
2367
3142
|
if (isLegacyPackageManifestName(name) && options.warnLegacy !== false && !warnedLegacyManifestPaths.has(candidate)) {
|
|
2368
3143
|
warnedLegacyManifestPaths.add(candidate);
|
|
@@ -2375,7 +3150,7 @@ async function findPackageManifestPath(root, options = {}) {
|
|
|
2375
3150
|
async function readPackageManifest(root) {
|
|
2376
3151
|
const path = await findPackageManifestPath(root);
|
|
2377
3152
|
if (!path) return void 0;
|
|
2378
|
-
const content = await
|
|
3153
|
+
const content = await readFile10(path, "utf8");
|
|
2379
3154
|
const errors = [];
|
|
2380
3155
|
const parsed = parse2(content, errors, { allowTrailingComma: true, disallowComments: false });
|
|
2381
3156
|
if (errors.length > 0) {
|
|
@@ -2385,7 +3160,7 @@ async function readPackageManifest(root) {
|
|
|
2385
3160
|
return parsePackageManifest(parsed, path);
|
|
2386
3161
|
}
|
|
2387
3162
|
function parsePackageManifest(parsed, path = "package manifest") {
|
|
2388
|
-
if (!
|
|
3163
|
+
if (!isRecord4(parsed)) {
|
|
2389
3164
|
throw new Error(`Invalid package manifest ${path}: expected an object`);
|
|
2390
3165
|
}
|
|
2391
3166
|
if (parsed.schemaVersion === 1) {
|
|
@@ -2413,9 +3188,9 @@ function v1OpenPackViolations(manifest) {
|
|
|
2413
3188
|
}
|
|
2414
3189
|
const provides = Array.isArray(manifest.provides) ? manifest.provides : [];
|
|
2415
3190
|
for (const [index, provide] of provides.entries()) {
|
|
2416
|
-
if (!
|
|
3191
|
+
if (!isRecord4(provide)) continue;
|
|
2417
3192
|
if (provide.type === "fragments") violations.push(`provides[${index}].type=fragments`);
|
|
2418
|
-
for (const key of ["items", "compose", "runtimes"]) {
|
|
3193
|
+
for (const key of ["format", "items", "compose", "runtimes"]) {
|
|
2419
3194
|
if (Object.prototype.hasOwnProperty.call(provide, key)) violations.push(`provides[${index}].${key}`);
|
|
2420
3195
|
}
|
|
2421
3196
|
}
|
|
@@ -2424,13 +3199,14 @@ function v1OpenPackViolations(manifest) {
|
|
|
2424
3199
|
function isLegacyPackageManifestName(name) {
|
|
2425
3200
|
return legacyPackageManifestNames.includes(name);
|
|
2426
3201
|
}
|
|
2427
|
-
function
|
|
3202
|
+
function isRecord4(value) {
|
|
2428
3203
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2429
3204
|
}
|
|
2430
3205
|
|
|
2431
3206
|
// src/source/local.ts
|
|
3207
|
+
import { createHash as createHash3 } from "crypto";
|
|
2432
3208
|
import { readdir, stat as stat3 } from "fs/promises";
|
|
2433
|
-
import { basename as
|
|
3209
|
+
import { basename as basename6, join as join11, relative as relative3, resolve as resolve5 } from "path";
|
|
2434
3210
|
var LocalSourceDriver = class {
|
|
2435
3211
|
name = "local";
|
|
2436
3212
|
async resolve(source) {
|
|
@@ -2450,7 +3226,7 @@ var LocalSourceDriver = class {
|
|
|
2450
3226
|
packageName: manifest?.name,
|
|
2451
3227
|
packageVersion: manifest?.version,
|
|
2452
3228
|
mode: "pinned",
|
|
2453
|
-
sourceHash: await
|
|
3229
|
+
sourceHash: await hashLocalSource(resolvedPath, manifest)
|
|
2454
3230
|
};
|
|
2455
3231
|
}
|
|
2456
3232
|
async list(resolved) {
|
|
@@ -2460,29 +3236,29 @@ var LocalSourceDriver = class {
|
|
|
2460
3236
|
}
|
|
2461
3237
|
const artifacts = [];
|
|
2462
3238
|
const root = resolved.resolvedPath;
|
|
2463
|
-
const instructions = await firstExisting([
|
|
3239
|
+
const instructions = await firstExisting([join11(root, "instructions.md"), join11(root, "AGENTS.md")]);
|
|
2464
3240
|
if (instructions) {
|
|
2465
3241
|
artifacts.push({
|
|
2466
3242
|
type: "instructions",
|
|
2467
|
-
name:
|
|
3243
|
+
name: basename6(instructions),
|
|
2468
3244
|
sourcePath: instructions,
|
|
2469
|
-
relativePath:
|
|
3245
|
+
relativePath: basename6(instructions),
|
|
2470
3246
|
kind: "file",
|
|
2471
3247
|
hash: await hashPath(instructions),
|
|
2472
3248
|
packageName: resolved.packageName,
|
|
2473
3249
|
channel: "managed"
|
|
2474
3250
|
});
|
|
2475
3251
|
}
|
|
2476
|
-
const rulesDir =
|
|
3252
|
+
const rulesDir = join11(root, "rules");
|
|
2477
3253
|
if (await pathExists(rulesDir)) {
|
|
2478
3254
|
for (const entry of await sortedDirEntries(rulesDir)) {
|
|
2479
|
-
const full =
|
|
3255
|
+
const full = join11(rulesDir, entry.name);
|
|
2480
3256
|
if (entry.isFile()) {
|
|
2481
3257
|
artifacts.push({
|
|
2482
3258
|
type: "rules",
|
|
2483
3259
|
name: entry.name,
|
|
2484
3260
|
sourcePath: full,
|
|
2485
|
-
relativePath:
|
|
3261
|
+
relativePath: join11("rules", entry.name),
|
|
2486
3262
|
kind: "file",
|
|
2487
3263
|
hash: await hashPath(full),
|
|
2488
3264
|
packageName: resolved.packageName,
|
|
@@ -2491,16 +3267,16 @@ var LocalSourceDriver = class {
|
|
|
2491
3267
|
}
|
|
2492
3268
|
}
|
|
2493
3269
|
}
|
|
2494
|
-
const fragmentsDir =
|
|
3270
|
+
const fragmentsDir = join11(root, "fragments");
|
|
2495
3271
|
if (await pathExists(fragmentsDir)) {
|
|
2496
3272
|
for (const entry of await sortedDirEntries(fragmentsDir)) {
|
|
2497
|
-
const full =
|
|
3273
|
+
const full = join11(fragmentsDir, entry.name);
|
|
2498
3274
|
if (entry.isFile()) {
|
|
2499
3275
|
artifacts.push({
|
|
2500
3276
|
type: "fragments",
|
|
2501
3277
|
name: entry.name,
|
|
2502
3278
|
sourcePath: full,
|
|
2503
|
-
relativePath:
|
|
3279
|
+
relativePath: join11("fragments", entry.name),
|
|
2504
3280
|
kind: "file",
|
|
2505
3281
|
hash: await hashPath(full),
|
|
2506
3282
|
packageName: resolved.packageName,
|
|
@@ -2509,16 +3285,16 @@ var LocalSourceDriver = class {
|
|
|
2509
3285
|
}
|
|
2510
3286
|
}
|
|
2511
3287
|
}
|
|
2512
|
-
const skillsDir =
|
|
3288
|
+
const skillsDir = join11(root, "skills");
|
|
2513
3289
|
if (await pathExists(skillsDir)) {
|
|
2514
3290
|
for (const entry of await sortedDirEntries(skillsDir)) {
|
|
2515
|
-
const full =
|
|
3291
|
+
const full = join11(skillsDir, entry.name);
|
|
2516
3292
|
if (entry.isDirectory()) {
|
|
2517
3293
|
artifacts.push({
|
|
2518
3294
|
type: "skills",
|
|
2519
3295
|
name: entry.name,
|
|
2520
3296
|
sourcePath: full,
|
|
2521
|
-
relativePath:
|
|
3297
|
+
relativePath: join11("skills", entry.name),
|
|
2522
3298
|
kind: "dir",
|
|
2523
3299
|
hash: await hashPath(full),
|
|
2524
3300
|
packageName: resolved.packageName,
|
|
@@ -2529,7 +3305,7 @@ var LocalSourceDriver = class {
|
|
|
2529
3305
|
type: "skills",
|
|
2530
3306
|
name: entry.name.replace(/\.md$/, ""),
|
|
2531
3307
|
sourcePath: full,
|
|
2532
|
-
relativePath:
|
|
3308
|
+
relativePath: join11("skills", entry.name),
|
|
2533
3309
|
kind: "file",
|
|
2534
3310
|
hash: await hashPath(full),
|
|
2535
3311
|
packageName: resolved.packageName,
|
|
@@ -2539,7 +3315,7 @@ var LocalSourceDriver = class {
|
|
|
2539
3315
|
}
|
|
2540
3316
|
}
|
|
2541
3317
|
for (const type of ["commands", "subagents", "mcp", "hooks", "settings", "plugins"]) {
|
|
2542
|
-
const dir =
|
|
3318
|
+
const dir = join11(root, type);
|
|
2543
3319
|
if (!await pathExists(dir)) continue;
|
|
2544
3320
|
artifacts.push(...await listGenericArtifacts(type, dir, type, resolved.packageName));
|
|
2545
3321
|
}
|
|
@@ -2555,7 +3331,7 @@ var LocalSourceDriver = class {
|
|
|
2555
3331
|
findings.push({ level: "warning", message: "No instructions.md or AGENTS.md found", path: resolved.resolvedPath });
|
|
2556
3332
|
}
|
|
2557
3333
|
for (const artifact of artifacts.filter((item) => item.type === "skills" && item.kind === "dir")) {
|
|
2558
|
-
if (!await pathExists(
|
|
3334
|
+
if (!await pathExists(join11(artifact.sourcePath, "SKILL.md"))) {
|
|
2559
3335
|
findings.push({ level: "warning", message: `Skill directory has no SKILL.md: ${artifact.name}`, path: artifact.sourcePath });
|
|
2560
3336
|
}
|
|
2561
3337
|
}
|
|
@@ -2568,6 +3344,26 @@ var LocalSourceDriver = class {
|
|
|
2568
3344
|
return resolved;
|
|
2569
3345
|
}
|
|
2570
3346
|
};
|
|
3347
|
+
async function hashLocalSource(root, manifest) {
|
|
3348
|
+
if (!manifest) return hashPath(root);
|
|
3349
|
+
const hash = createHash3("sha256").update("local-openpack\0");
|
|
3350
|
+
const manifestPath = await findPackageManifestPath(root, { warnLegacy: false });
|
|
3351
|
+
if (manifestPath) {
|
|
3352
|
+
hash.update("manifest\0");
|
|
3353
|
+
hash.update(relative3(root, manifestPath).replaceAll("\\", "/")).update("\0");
|
|
3354
|
+
hash.update(await hashPath(manifestPath)).update("\0");
|
|
3355
|
+
}
|
|
3356
|
+
const provides = [...manifest.provides].sort((a, b) => `${a.type}\0${a.path}`.localeCompare(`${b.type}\0${b.path}`));
|
|
3357
|
+
for (const provide of provides) {
|
|
3358
|
+
const full = join11(root, provide.path);
|
|
3359
|
+
if (!await pathExists(full)) continue;
|
|
3360
|
+
hash.update("provide\0");
|
|
3361
|
+
hash.update(provide.type).update("\0");
|
|
3362
|
+
hash.update(provide.path.replaceAll("\\", "/")).update("\0");
|
|
3363
|
+
hash.update(await hashPath(full)).update("\0");
|
|
3364
|
+
}
|
|
3365
|
+
return hash.digest("hex");
|
|
3366
|
+
}
|
|
2571
3367
|
async function firstExisting(paths) {
|
|
2572
3368
|
for (const path of paths) {
|
|
2573
3369
|
if (await pathExists(path)) return path;
|
|
@@ -2582,27 +3378,27 @@ async function listFromManifest(root, packageName) {
|
|
|
2582
3378
|
if (!manifest) return [];
|
|
2583
3379
|
const artifacts = [];
|
|
2584
3380
|
for (const provide of manifest.provides) {
|
|
2585
|
-
const full =
|
|
3381
|
+
const full = join11(root, provide.path);
|
|
2586
3382
|
if (!await pathExists(full)) continue;
|
|
2587
3383
|
const stats = await stat3(full);
|
|
2588
3384
|
if (provide.type === "instructions") {
|
|
2589
3385
|
if (stats.isFile()) {
|
|
2590
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
3386
|
+
artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
|
|
2591
3387
|
}
|
|
2592
3388
|
continue;
|
|
2593
3389
|
}
|
|
2594
3390
|
if (stats.isDirectory()) {
|
|
2595
3391
|
for (const entry of await sortedDirEntries(full)) {
|
|
2596
|
-
const child =
|
|
3392
|
+
const child = join11(full, entry.name);
|
|
2597
3393
|
if ((provide.type === "skills" || provide.type === "plugins" || provide.type === "subagents") && entry.isDirectory()) {
|
|
2598
|
-
artifacts.push(await artifactForDir(provide.type, entry.name, child,
|
|
3394
|
+
artifacts.push(await artifactForDir(provide.type, entry.name, child, join11(provide.path, entry.name), packageName, provide, manifest, entry.name));
|
|
2599
3395
|
} else if (entry.isFile()) {
|
|
2600
3396
|
const name = provide.type === "rules" && entry.name.endsWith(".md") ? entry.name : entry.name;
|
|
2601
|
-
artifacts.push(await artifactForFile(provide.type, name, child,
|
|
3397
|
+
artifacts.push(await artifactForFile(provide.type, name, child, join11(provide.path, entry.name), packageName, provide, manifest, name));
|
|
2602
3398
|
}
|
|
2603
3399
|
}
|
|
2604
3400
|
} else if (stats.isFile()) {
|
|
2605
|
-
artifacts.push(await artifactForFile(provide.type,
|
|
3401
|
+
artifacts.push(await artifactForFile(provide.type, basename6(full), full, provide.path, packageName, provide, manifest, basename6(full)));
|
|
2606
3402
|
}
|
|
2607
3403
|
}
|
|
2608
3404
|
return artifacts;
|
|
@@ -2610,11 +3406,11 @@ async function listFromManifest(root, packageName) {
|
|
|
2610
3406
|
async function listGenericArtifacts(type, dir, relativeRoot, packageName) {
|
|
2611
3407
|
const artifacts = [];
|
|
2612
3408
|
for (const entry of await sortedDirEntries(dir)) {
|
|
2613
|
-
const full =
|
|
3409
|
+
const full = join11(dir, entry.name);
|
|
2614
3410
|
if (entry.isDirectory()) {
|
|
2615
|
-
artifacts.push(await artifactForDir(type, entry.name, full,
|
|
3411
|
+
artifacts.push(await artifactForDir(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
|
|
2616
3412
|
} else if (entry.isFile()) {
|
|
2617
|
-
artifacts.push(await artifactForFile(type, entry.name, full,
|
|
3413
|
+
artifacts.push(await artifactForFile(type, entry.name, full, join11(relativeRoot, entry.name), packageName));
|
|
2618
3414
|
}
|
|
2619
3415
|
}
|
|
2620
3416
|
return artifacts;
|
|
@@ -2628,6 +3424,7 @@ async function artifactForFile(type, name, sourcePath, relativePath, packageName
|
|
|
2628
3424
|
relativePath,
|
|
2629
3425
|
kind: "file",
|
|
2630
3426
|
hash: await hashPath(sourcePath),
|
|
3427
|
+
format: item.format ?? provide?.format,
|
|
2631
3428
|
packageName,
|
|
2632
3429
|
channel: "managed",
|
|
2633
3430
|
assets: provide?.assets,
|
|
@@ -2646,6 +3443,7 @@ async function artifactForDir(type, name, sourcePath, relativePath, packageName,
|
|
|
2646
3443
|
relativePath,
|
|
2647
3444
|
kind: "dir",
|
|
2648
3445
|
hash: await hashPath(sourcePath),
|
|
3446
|
+
format: item.format ?? provide?.format,
|
|
2649
3447
|
packageName,
|
|
2650
3448
|
channel: "managed",
|
|
2651
3449
|
assets: provide?.assets,
|
|
@@ -2659,7 +3457,7 @@ function itemMetadata(provide, itemName) {
|
|
|
2659
3457
|
if (!provide || !("items" in provide) || !provide.items || !itemName) return {};
|
|
2660
3458
|
const item = provide.items[itemName];
|
|
2661
3459
|
if (!item) return {};
|
|
2662
|
-
return { requires: item.requires, compose: item.compose, runtimes: item.runtimes };
|
|
3460
|
+
return { format: item.format, requires: item.requires, compose: item.compose, runtimes: item.runtimes };
|
|
2663
3461
|
}
|
|
2664
3462
|
function provideRuntimes(provide) {
|
|
2665
3463
|
return provide && "runtimes" in provide ? provide.runtimes : void 0;
|
|
@@ -2669,7 +3467,7 @@ function manifestRuntimes(manifest) {
|
|
|
2669
3467
|
}
|
|
2670
3468
|
|
|
2671
3469
|
// src/source/git.ts
|
|
2672
|
-
var
|
|
3470
|
+
var execFileAsync4 = promisify4(execFile4);
|
|
2673
3471
|
var GitSourceDriver = class {
|
|
2674
3472
|
name = "git";
|
|
2675
3473
|
local = new LocalSourceDriver();
|
|
@@ -2690,8 +3488,8 @@ var GitSourceDriver = class {
|
|
|
2690
3488
|
async fetch(resolved) {
|
|
2691
3489
|
return withFilesystemLock(`${resolved.resolvedPath}.lock`, resolved.cacheLockTimeoutMs ?? 3e4, async () => {
|
|
2692
3490
|
const parsed = parseGitSource(resolved.source);
|
|
2693
|
-
await
|
|
2694
|
-
if (!await pathExists(
|
|
3491
|
+
await mkdir8(resolve6(resolved.resolvedPath, ".."), { recursive: true });
|
|
3492
|
+
if (!await pathExists(join12(resolved.resolvedPath, ".git"))) {
|
|
2695
3493
|
if (resolved.frozenLock) {
|
|
2696
3494
|
throw new Error(`Frozen lock requires cached git checkout at ${resolved.resolvedPath}`);
|
|
2697
3495
|
}
|
|
@@ -2758,20 +3556,20 @@ function parseGitSource(source) {
|
|
|
2758
3556
|
throw new Error(`Invalid git source: ${source}`);
|
|
2759
3557
|
}
|
|
2760
3558
|
function cachePathFor(url, cacheRoot) {
|
|
2761
|
-
const root = cacheRoot ? resolve6(cacheRoot) :
|
|
3559
|
+
const root = cacheRoot ? resolve6(cacheRoot) : join12(homedir2(), ".agentwheel", "cache");
|
|
2762
3560
|
const slug2 = url.replace(/^[a-z]+:\/\//i, "").replace(/\.git$/i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "");
|
|
2763
|
-
return
|
|
3561
|
+
return join12(root, slug2 || basename7(url));
|
|
2764
3562
|
}
|
|
2765
3563
|
async function git(args) {
|
|
2766
|
-
return
|
|
3564
|
+
return execFileAsync4("git", args, { maxBuffer: 1024 * 1024 * 10 });
|
|
2767
3565
|
}
|
|
2768
3566
|
async function snapshotCheckout(checkoutPath, commit) {
|
|
2769
|
-
const snapshotPath =
|
|
3567
|
+
const snapshotPath = join12(dirname9(checkoutPath), `${basename7(checkoutPath)}-${commit.slice(0, 12)}`);
|
|
2770
3568
|
if (await pathExists(snapshotPath)) return snapshotPath;
|
|
2771
|
-
const tempPath =
|
|
3569
|
+
const tempPath = join12(dirname9(checkoutPath), `${basename7(snapshotPath)}.tmp-${process.pid}-${Date.now()}`);
|
|
2772
3570
|
await rm4(tempPath, { recursive: true, force: true });
|
|
2773
3571
|
await cp2(checkoutPath, tempPath, { recursive: true, dereference: true });
|
|
2774
|
-
await rm4(
|
|
3572
|
+
await rm4(join12(tempPath, ".git"), { recursive: true, force: true });
|
|
2775
3573
|
try {
|
|
2776
3574
|
await rename3(tempPath, snapshotPath);
|
|
2777
3575
|
} catch (error) {
|
|
@@ -2782,12 +3580,12 @@ async function snapshotCheckout(checkoutPath, commit) {
|
|
|
2782
3580
|
return snapshotPath;
|
|
2783
3581
|
}
|
|
2784
3582
|
async function withFilesystemLock(lockPath, timeoutMs, fn) {
|
|
2785
|
-
await
|
|
3583
|
+
await mkdir8(dirname9(lockPath), { recursive: true });
|
|
2786
3584
|
const started = Date.now();
|
|
2787
3585
|
while (true) {
|
|
2788
3586
|
try {
|
|
2789
|
-
await
|
|
2790
|
-
await
|
|
3587
|
+
await mkdir8(lockPath);
|
|
3588
|
+
await writeFile9(join12(lockPath, "owner.json"), JSON.stringify({ pid: process.pid, createdAt: (/* @__PURE__ */ new Date()).toISOString() }), "utf8");
|
|
2791
3589
|
break;
|
|
2792
3590
|
} catch (error) {
|
|
2793
3591
|
if (!isAlreadyExists2(error)) throw error;
|
|
@@ -2808,14 +3606,14 @@ function isAlreadyExists2(error) {
|
|
|
2808
3606
|
}
|
|
2809
3607
|
|
|
2810
3608
|
// src/source/skillkit.ts
|
|
2811
|
-
import { cp as cp3, mkdir as
|
|
2812
|
-
import { homedir as
|
|
2813
|
-
import { basename as
|
|
3609
|
+
import { cp as cp3, mkdir as mkdir9, readFile as readFile11, rm as rm5 } from "fs/promises";
|
|
3610
|
+
import { homedir as homedir3 } from "os";
|
|
3611
|
+
import { basename as basename9, dirname as dirname11, join as join14, resolve as resolve7 } from "path";
|
|
2814
3612
|
import * as defaultSkillKit from "@skillkit/core";
|
|
2815
3613
|
|
|
2816
3614
|
// src/source/skill-artifacts.ts
|
|
2817
3615
|
import { readdir as readdir2, stat as stat4 } from "fs/promises";
|
|
2818
|
-
import { basename as
|
|
3616
|
+
import { basename as basename8, dirname as dirname10, extname as extname2, join as join13 } from "path";
|
|
2819
3617
|
async function artifactsFromSkillPaths(paths, packageName) {
|
|
2820
3618
|
const artifacts = [];
|
|
2821
3619
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2837,28 +3635,28 @@ async function discoverSkillPaths(root) {
|
|
|
2837
3635
|
async function artifactFromSkillPath(item, packageName) {
|
|
2838
3636
|
const stats = await stat4(item.path);
|
|
2839
3637
|
if (stats.isDirectory()) {
|
|
2840
|
-
const skillMd =
|
|
3638
|
+
const skillMd = join13(item.path, "SKILL.md");
|
|
2841
3639
|
if (!await pathExists(skillMd)) return void 0;
|
|
2842
|
-
const name = sanitizeSkillName(item.name ??
|
|
3640
|
+
const name = sanitizeSkillName(item.name ?? basename8(item.path));
|
|
2843
3641
|
return {
|
|
2844
3642
|
type: "skills",
|
|
2845
3643
|
name,
|
|
2846
3644
|
sourcePath: item.path,
|
|
2847
|
-
relativePath:
|
|
3645
|
+
relativePath: join13("skills", name),
|
|
2848
3646
|
kind: "dir",
|
|
2849
3647
|
hash: await hashPath(item.path),
|
|
2850
3648
|
packageName,
|
|
2851
3649
|
channel: "managed"
|
|
2852
3650
|
};
|
|
2853
3651
|
}
|
|
2854
|
-
if (stats.isFile() &&
|
|
2855
|
-
const dir =
|
|
2856
|
-
const name = sanitizeSkillName(item.name ??
|
|
3652
|
+
if (stats.isFile() && basename8(item.path).toLowerCase() === "skill.md") {
|
|
3653
|
+
const dir = dirname10(item.path);
|
|
3654
|
+
const name = sanitizeSkillName(item.name ?? basename8(dir));
|
|
2857
3655
|
return {
|
|
2858
3656
|
type: "skills",
|
|
2859
3657
|
name,
|
|
2860
3658
|
sourcePath: dir,
|
|
2861
|
-
relativePath:
|
|
3659
|
+
relativePath: join13("skills", name),
|
|
2862
3660
|
kind: "dir",
|
|
2863
3661
|
hash: await hashPath(dir),
|
|
2864
3662
|
packageName,
|
|
@@ -2866,12 +3664,12 @@ async function artifactFromSkillPath(item, packageName) {
|
|
|
2866
3664
|
};
|
|
2867
3665
|
}
|
|
2868
3666
|
if (stats.isFile() && extname2(item.path).toLowerCase() === ".md") {
|
|
2869
|
-
const name = sanitizeSkillName(item.name ??
|
|
3667
|
+
const name = sanitizeSkillName(item.name ?? basename8(item.path, ".md"));
|
|
2870
3668
|
return {
|
|
2871
3669
|
type: "skills",
|
|
2872
3670
|
name,
|
|
2873
3671
|
sourcePath: item.path,
|
|
2874
|
-
relativePath:
|
|
3672
|
+
relativePath: join13("skills", `${name}.md`),
|
|
2875
3673
|
kind: "file",
|
|
2876
3674
|
hash: await hashPath(item.path),
|
|
2877
3675
|
packageName,
|
|
@@ -2889,7 +3687,7 @@ async function walk(dir, paths) {
|
|
|
2889
3687
|
}
|
|
2890
3688
|
for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
2891
3689
|
if (!entry.isDirectory() || entry.name === ".git" || entry.name === "node_modules") continue;
|
|
2892
|
-
await walk(
|
|
3690
|
+
await walk(join13(dir, entry.name), paths);
|
|
2893
3691
|
}
|
|
2894
3692
|
}
|
|
2895
3693
|
function sanitizeSkillName(name) {
|
|
@@ -2911,7 +3709,7 @@ var SkillKitSourceDriver = class {
|
|
|
2911
3709
|
driver: this.name,
|
|
2912
3710
|
source,
|
|
2913
3711
|
resolvedPath,
|
|
2914
|
-
packageName: `skillkit/${
|
|
3712
|
+
packageName: `skillkit/${basename9(resolvedPath)}`,
|
|
2915
3713
|
mode: options.mode ?? "pinned",
|
|
2916
3714
|
sourceHash: await hashPath(resolvedPath)
|
|
2917
3715
|
};
|
|
@@ -2945,7 +3743,7 @@ var SkillKitSourceDriver = class {
|
|
|
2945
3743
|
if (!provider?.clone) {
|
|
2946
3744
|
throw new Error("SkillKit provider API unavailable or cannot resolve source. Expected @skillkit/core detectProvider().clone().");
|
|
2947
3745
|
}
|
|
2948
|
-
await
|
|
3746
|
+
await mkdir9(dirname11(resolved.resolvedPath), { recursive: true });
|
|
2949
3747
|
const result = await provider.clone(providerSpec, resolved.resolvedPath, {});
|
|
2950
3748
|
if (!result.success || !result.path) {
|
|
2951
3749
|
throw new Error(`SkillKit provider failed to fetch ${spec}: ${result.error ?? "unknown error"}`);
|
|
@@ -2989,9 +3787,9 @@ var SkillKitSourceDriver = class {
|
|
|
2989
3787
|
throw new Error("SkillKit translateSkill API unavailable");
|
|
2990
3788
|
}
|
|
2991
3789
|
for (const skill of this.discover(resolved.resolvedPath)) {
|
|
2992
|
-
const skillMd =
|
|
3790
|
+
const skillMd = join14(skill.path, "SKILL.md");
|
|
2993
3791
|
if (await pathExists(skillMd)) {
|
|
2994
|
-
this.core.translateSkill(await
|
|
3792
|
+
this.core.translateSkill(await readFile11(skillMd, "utf8"), "openclaw", { sourceFilename: "SKILL.md" });
|
|
2995
3793
|
}
|
|
2996
3794
|
}
|
|
2997
3795
|
return resolved;
|
|
@@ -3020,8 +3818,8 @@ function normalizeProviderSource(spec) {
|
|
|
3020
3818
|
return spec;
|
|
3021
3819
|
}
|
|
3022
3820
|
function cachePathFor2(spec, cacheRoot) {
|
|
3023
|
-
const root = cacheRoot ? resolve7(cacheRoot) :
|
|
3024
|
-
return
|
|
3821
|
+
const root = cacheRoot ? resolve7(cacheRoot) : join14(homedir3(), ".agentwheel", "cache");
|
|
3822
|
+
return join14(root, "skillkit", packageSlug(spec));
|
|
3025
3823
|
}
|
|
3026
3824
|
function packageSlug(spec) {
|
|
3027
3825
|
return spec.replace(/^[a-z]+:\/\//i, "").replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "source";
|
|
@@ -3034,7 +3832,7 @@ function mapSeverity(severity) {
|
|
|
3034
3832
|
|
|
3035
3833
|
// src/source/vercel-skills.ts
|
|
3036
3834
|
import { stat as stat5 } from "fs/promises";
|
|
3037
|
-
import { basename as
|
|
3835
|
+
import { basename as basename10, join as join15, resolve as resolve8 } from "path";
|
|
3038
3836
|
var VercelSkillsSourceDriver = class {
|
|
3039
3837
|
name = "vercel-skills";
|
|
3040
3838
|
git = new GitSourceDriver();
|
|
@@ -3049,7 +3847,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
3049
3847
|
driver: this.name,
|
|
3050
3848
|
source,
|
|
3051
3849
|
resolvedPath,
|
|
3052
|
-
packageName: `vercel/${
|
|
3850
|
+
packageName: `vercel/${basename10(resolvedPath)}`,
|
|
3053
3851
|
mode: options.mode ?? "pinned",
|
|
3054
3852
|
sourceHash: await hashPath(resolvedPath)
|
|
3055
3853
|
};
|
|
@@ -3070,7 +3868,7 @@ var VercelSkillsSourceDriver = class {
|
|
|
3070
3868
|
driver: "git",
|
|
3071
3869
|
source: parsed.gitSource
|
|
3072
3870
|
});
|
|
3073
|
-
const resolvedPath = parsed.subpath ?
|
|
3871
|
+
const resolvedPath = parsed.subpath ? join15(fetched.resolvedPath, parsed.subpath) : fetched.resolvedPath;
|
|
3074
3872
|
if (!await pathExists(resolvedPath)) {
|
|
3075
3873
|
throw new Error(`Vercel skills subpath not found: ${parsed.subpath}`);
|
|
3076
3874
|
}
|
|
@@ -3159,14 +3957,14 @@ function getSourceDriver(name = "local") {
|
|
|
3159
3957
|
}
|
|
3160
3958
|
|
|
3161
3959
|
// src/staging/staging.ts
|
|
3162
|
-
import { chmod, cp as cp5, mkdir as
|
|
3163
|
-
import { basename as
|
|
3960
|
+
import { chmod, cp as cp5, mkdir as mkdir11, mkdtemp as mkdtemp2, readdir as readdir5, stat as stat8 } from "fs/promises";
|
|
3961
|
+
import { basename as basename13, dirname as dirname14, join as join18, relative as relative5, resolve as resolve10, sep as sep2 } from "path";
|
|
3164
3962
|
import { tmpdir as tmpdir3 } from "os";
|
|
3165
3963
|
|
|
3166
3964
|
// src/compose/markdown.ts
|
|
3167
|
-
import { createHash as
|
|
3168
|
-
import { readdir as readdir3, readFile as
|
|
3169
|
-
import { basename as
|
|
3965
|
+
import { createHash as createHash4 } from "crypto";
|
|
3966
|
+
import { readdir as readdir3, readFile as readFile12, stat as stat6, writeFile as writeFile10 } from "fs/promises";
|
|
3967
|
+
import { basename as basename11, dirname as dirname12, extname as extname3, join as join16, relative as relative4, resolve as resolve9, sep } from "path";
|
|
3170
3968
|
var includePattern = /<!--\s*openpack:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
3171
3969
|
var escapedIncludePattern = /<!--\s*openpack\\:include(\?)?\s+([^>]+?)\s*-->/g;
|
|
3172
3970
|
var generatedPattern = /<!--\s*(?:BEGIN|END)\s+openpack:include\b/;
|
|
@@ -3181,7 +3979,7 @@ async function expandMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
3181
3979
|
const composedFrom = [];
|
|
3182
3980
|
for (const file of files) {
|
|
3183
3981
|
const result = await expandFile(file, packageRoot, composeEntriesForFile(artifact, file), artifactPaths, options);
|
|
3184
|
-
if (result.changed) await
|
|
3982
|
+
if (result.changed) await writeFile10(file, result.content, "utf8");
|
|
3185
3983
|
composedFrom.push(...result.composedFrom);
|
|
3186
3984
|
}
|
|
3187
3985
|
const stagedPath = artifact.stagedPath ?? artifact.sourcePath;
|
|
@@ -3203,7 +4001,7 @@ async function validateMarkdownIncludes(artifacts, packageRoot, options = {}) {
|
|
|
3203
4001
|
}
|
|
3204
4002
|
}
|
|
3205
4003
|
async function expandFile(file, packageRoot, appendEntries, artifactPaths, options) {
|
|
3206
|
-
const raw = await
|
|
4004
|
+
const raw = await readFile12(file, "utf8");
|
|
3207
4005
|
const owner = ownerSelector(packageRoot, file, options.nodeId);
|
|
3208
4006
|
const expanded = await expandContent(raw, packageRoot, [owner], artifactPaths, options);
|
|
3209
4007
|
let content = expanded.content;
|
|
@@ -3296,7 +4094,7 @@ async function expandInclude(selector, packageRoot, artifactPaths, options) {
|
|
|
3296
4094
|
if (!stats.isFile()) {
|
|
3297
4095
|
throw new Error(`OpenPack include is not a file: ${displaySelector}`);
|
|
3298
4096
|
}
|
|
3299
|
-
const raw = sourceContent ?? await
|
|
4097
|
+
const raw = sourceContent ?? await readFile12(sourcePath, "utf8");
|
|
3300
4098
|
const { optional: _optional, markers: _markers, chain: _chain, ...childOptions } = options;
|
|
3301
4099
|
const expanded = await expandContent(raw, includePackageRoot, [...options.chain, displaySelector], includeArtifactPaths, {
|
|
3302
4100
|
...childOptions,
|
|
@@ -3372,7 +4170,7 @@ async function listMarkdownFiles(root) {
|
|
|
3372
4170
|
const out = [];
|
|
3373
4171
|
async function walk2(dir) {
|
|
3374
4172
|
for (const entry of (await readdir3(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3375
|
-
const full =
|
|
4173
|
+
const full = join16(dir, entry.name);
|
|
3376
4174
|
if (entry.isDirectory()) {
|
|
3377
4175
|
await walk2(full);
|
|
3378
4176
|
} else if (entry.isFile() && extname3(entry.name).toLowerCase() === ".md") {
|
|
@@ -3386,7 +4184,7 @@ async function listMarkdownFiles(root) {
|
|
|
3386
4184
|
function composeEntriesForFile(artifact, file) {
|
|
3387
4185
|
if (!artifact.compose?.length) return [];
|
|
3388
4186
|
if (artifact.kind === "file") return [resolve9(artifact.stagedPath ?? artifact.sourcePath), resolve9(file)].every(Boolean) && resolve9(artifact.stagedPath ?? artifact.sourcePath) === resolve9(file) ? artifact.compose : [];
|
|
3389
|
-
return
|
|
4187
|
+
return basename11(file) === "SKILL.md" && dirname12(file) === resolve9(artifact.stagedPath ?? artifact.sourcePath) ? artifact.compose : [];
|
|
3390
4188
|
}
|
|
3391
4189
|
function orderedForExpansion(artifacts) {
|
|
3392
4190
|
return [...artifacts].sort((a, b) => Number(a.type === "fragments") - Number(b.type === "fragments"));
|
|
@@ -3403,7 +4201,7 @@ function applyReplacements(content, replacements) {
|
|
|
3403
4201
|
return out + content.slice(cursor);
|
|
3404
4202
|
}
|
|
3405
4203
|
function relativeSelector(root, file) {
|
|
3406
|
-
return
|
|
4204
|
+
return relative4(root, file).replaceAll("\\", "/");
|
|
3407
4205
|
}
|
|
3408
4206
|
function ownerSelector(root, file, nodeId) {
|
|
3409
4207
|
const selector = relativeSelector(root, file);
|
|
@@ -3417,7 +4215,7 @@ function cleanSelector(value) {
|
|
|
3417
4215
|
return value.trim().replace(/\s+/g, " ");
|
|
3418
4216
|
}
|
|
3419
4217
|
function sha256(content) {
|
|
3420
|
-
return
|
|
4218
|
+
return createHash4("sha256").update(content).digest("hex");
|
|
3421
4219
|
}
|
|
3422
4220
|
function uniqueComposedFrom(entries) {
|
|
3423
4221
|
if (entries.length === 0) return [];
|
|
@@ -3432,8 +4230,8 @@ function artifactPathMap(artifacts) {
|
|
|
3432
4230
|
}
|
|
3433
4231
|
|
|
3434
4232
|
// src/staging/customize.ts
|
|
3435
|
-
import { cp as cp4, mkdir as
|
|
3436
|
-
import { dirname as
|
|
4233
|
+
import { cp as cp4, mkdir as mkdir10, readdir as readdir4, readFile as readFile13, writeFile as writeFile11 } from "fs/promises";
|
|
4234
|
+
import { dirname as dirname13, join as join17 } from "path";
|
|
3437
4235
|
async function applyCustomizations(artifacts, options) {
|
|
3438
4236
|
let next = [...artifacts];
|
|
3439
4237
|
next = await applyReplacements2(next, options, "override", installableArtifactTypes());
|
|
@@ -3449,16 +4247,16 @@ async function applyFragmentCustomizations(artifacts, options) {
|
|
|
3449
4247
|
return next.sort((a, b) => `${a.type}:${a.name}:${a.channel}`.localeCompare(`${b.type}:${b.name}:${b.channel}`));
|
|
3450
4248
|
}
|
|
3451
4249
|
async function applyInstructionOverlay(artifacts, options) {
|
|
3452
|
-
const overlayPath =
|
|
4250
|
+
const overlayPath = join17(options.workspaceRoot, ".agentwheel", "overlays", options.adapter.name, "instructions.local.md");
|
|
3453
4251
|
if (!await pathExists(overlayPath)) return artifacts;
|
|
3454
4252
|
const index = artifacts.findIndex((artifact2) => artifact2.type === "instructions");
|
|
3455
4253
|
if (index < 0) return artifacts;
|
|
3456
4254
|
const artifact = artifacts[index];
|
|
3457
|
-
const managed = await
|
|
3458
|
-
const local = await
|
|
3459
|
-
const composedPath =
|
|
3460
|
-
await
|
|
3461
|
-
await
|
|
4255
|
+
const managed = await readFile13(artifact.stagedPath ?? artifact.sourcePath, "utf8");
|
|
4256
|
+
const local = await readFile13(overlayPath, "utf8");
|
|
4257
|
+
const composedPath = join17(options.stageRoot, ".agentwheel-composed", "instructions", "AGENTS.md");
|
|
4258
|
+
await mkdir10(dirname13(composedPath), { recursive: true });
|
|
4259
|
+
await writeFile11(
|
|
3462
4260
|
composedPath,
|
|
3463
4261
|
[
|
|
3464
4262
|
"<!-- BEGIN agentwheel managed: upstream -->",
|
|
@@ -3484,19 +4282,19 @@ async function applyInstructionOverlay(artifacts, options) {
|
|
|
3484
4282
|
return [...artifacts.slice(0, index), updated, ...artifacts.slice(index + 1)];
|
|
3485
4283
|
}
|
|
3486
4284
|
async function applyAdditions(artifacts, options) {
|
|
3487
|
-
const additionsRoot =
|
|
3488
|
-
const rulesRoot =
|
|
4285
|
+
const additionsRoot = join17(options.workspaceRoot, ".agentwheel", "additions");
|
|
4286
|
+
const rulesRoot = join17(additionsRoot, "rules");
|
|
3489
4287
|
if (!await pathExists(rulesRoot)) return artifacts;
|
|
3490
4288
|
const additions = [];
|
|
3491
4289
|
for (const entry of await sortedDirEntries2(rulesRoot)) {
|
|
3492
|
-
const full =
|
|
4290
|
+
const full = join17(rulesRoot, entry.name);
|
|
3493
4291
|
if (!entry.isFile()) continue;
|
|
3494
4292
|
additions.push({
|
|
3495
4293
|
type: "rules",
|
|
3496
4294
|
name: entry.name,
|
|
3497
4295
|
sourcePath: full,
|
|
3498
4296
|
stagedPath: full,
|
|
3499
|
-
relativePath:
|
|
4297
|
+
relativePath: join17("additions", "rules", entry.name),
|
|
3500
4298
|
kind: "file",
|
|
3501
4299
|
hash: await hashPath(full),
|
|
3502
4300
|
packageName: options.packageName,
|
|
@@ -3520,17 +4318,17 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
3520
4318
|
);
|
|
3521
4319
|
}
|
|
3522
4320
|
for (const type of artifactTypes) {
|
|
3523
|
-
const typeRoot =
|
|
4321
|
+
const typeRoot = join17(root, type);
|
|
3524
4322
|
if (!await pathExists(typeRoot)) continue;
|
|
3525
4323
|
for (const entry of await sortedDirEntries2(typeRoot)) {
|
|
3526
4324
|
const artifactMapKey = `${type}:${entry.name}`;
|
|
3527
4325
|
if (seen.has(artifactMapKey)) continue;
|
|
3528
4326
|
seen.add(artifactMapKey);
|
|
3529
|
-
const full =
|
|
4327
|
+
const full = join17(typeRoot, entry.name);
|
|
3530
4328
|
const artifactKind = entry.isDirectory() ? "dir" : "file";
|
|
3531
4329
|
const existing = byKey.get(artifactMapKey);
|
|
3532
|
-
const stagedPath =
|
|
3533
|
-
await
|
|
4330
|
+
const stagedPath = join17(options.stageRoot, ".agentwheel-composed", channel, type, entry.name);
|
|
4331
|
+
await mkdir10(dirname13(stagedPath), { recursive: true });
|
|
3534
4332
|
await cp4(full, stagedPath, { recursive: artifactKind === "dir", dereference: true });
|
|
3535
4333
|
byKey.set(artifactMapKey, {
|
|
3536
4334
|
...existing,
|
|
@@ -3538,7 +4336,7 @@ async function applyReplacements2(artifacts, options, channel, artifactTypes) {
|
|
|
3538
4336
|
name: entry.name,
|
|
3539
4337
|
sourcePath: full,
|
|
3540
4338
|
stagedPath,
|
|
3541
|
-
relativePath: existing?.relativePath ??
|
|
4339
|
+
relativePath: existing?.relativePath ?? join17(type, entry.name),
|
|
3542
4340
|
kind: artifactKind,
|
|
3543
4341
|
hash: await hashPath(stagedPath),
|
|
3544
4342
|
packageName,
|
|
@@ -3553,13 +4351,13 @@ function replacementRoots(options, channel) {
|
|
|
3553
4351
|
const stateDir = channel === "override" ? "overrides" : "ejected";
|
|
3554
4352
|
const roots = [];
|
|
3555
4353
|
if (options.graphNodeId) {
|
|
3556
|
-
roots.push({ root:
|
|
4354
|
+
roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.graphNodeId.split("/")), kind: "node" });
|
|
3557
4355
|
}
|
|
3558
4356
|
if (options.packageName && options.packageVersion) {
|
|
3559
|
-
roots.push({ root:
|
|
4357
|
+
roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...`${options.packageName}@${options.packageVersion}`.split("/")), kind: "version" });
|
|
3560
4358
|
}
|
|
3561
4359
|
if (options.packageName) {
|
|
3562
|
-
roots.push({ root:
|
|
4360
|
+
roots.push({ root: join17(options.workspaceRoot, ".agentwheel", stateDir, ...options.packageName.split("/")), kind: "package" });
|
|
3563
4361
|
}
|
|
3564
4362
|
return roots;
|
|
3565
4363
|
}
|
|
@@ -3577,6 +4375,13 @@ async function sortedDirEntries2(path) {
|
|
|
3577
4375
|
function artifactSelectorKey(artifact) {
|
|
3578
4376
|
return `${artifact.type}/${artifact.name}`;
|
|
3579
4377
|
}
|
|
4378
|
+
function artifactSelectorAliases(artifact) {
|
|
4379
|
+
const primary = artifactSelectorKey(artifact);
|
|
4380
|
+
if (artifact.type !== "subagents") return [primary];
|
|
4381
|
+
const baseName = subagentBaseName(artifact.name);
|
|
4382
|
+
if (baseName === artifact.name) return [primary];
|
|
4383
|
+
return [primary, `subagents/${baseName}`];
|
|
4384
|
+
}
|
|
3580
4385
|
function normalizeArtifactSelectors(select, legacySkills) {
|
|
3581
4386
|
const selected = [
|
|
3582
4387
|
...select ?? [],
|
|
@@ -3589,12 +4394,12 @@ function filterArtifactsBySelection(artifacts, selectors, legacySkills) {
|
|
|
3589
4394
|
const selected = normalizeArtifactSelectors(selectors, legacySkills);
|
|
3590
4395
|
if (!selected?.length) return artifacts;
|
|
3591
4396
|
const selectedSet = new Set(selected);
|
|
3592
|
-
const available = new Set(artifacts.
|
|
4397
|
+
const available = new Set(artifacts.flatMap(artifactSelectorAliases));
|
|
3593
4398
|
const missing = selected.filter((selector) => !available.has(selector));
|
|
3594
4399
|
if (missing.length > 0) {
|
|
3595
4400
|
throw new Error(`Selected artifact not found in package: ${missing.join(", ")}`);
|
|
3596
4401
|
}
|
|
3597
|
-
return artifacts.filter((artifact) => artifact.required || selectedSet.has(
|
|
4402
|
+
return artifacts.filter((artifact) => artifact.required || artifactSelectorAliases(artifact).some((selector) => selectedSet.has(selector)));
|
|
3598
4403
|
}
|
|
3599
4404
|
function splitSelectorList(value) {
|
|
3600
4405
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
@@ -3612,6 +4417,9 @@ function parseArtifactSelector(value) {
|
|
|
3612
4417
|
}
|
|
3613
4418
|
return `${parsedType.data}/${name}`;
|
|
3614
4419
|
}
|
|
4420
|
+
function subagentBaseName(name) {
|
|
4421
|
+
return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
|
|
4422
|
+
}
|
|
3615
4423
|
|
|
3616
4424
|
// src/staging/staging.ts
|
|
3617
4425
|
async function stageSource(driver, source, options = {}) {
|
|
@@ -3626,12 +4434,16 @@ async function stageResolvedSourceRaw(driver, resolved) {
|
|
|
3626
4434
|
return stageResolvedArtifactsRaw(resolved, artifacts);
|
|
3627
4435
|
}
|
|
3628
4436
|
async function stageResolvedArtifactsRaw(resolved, artifacts) {
|
|
3629
|
-
const root = await mkdtemp2(
|
|
4437
|
+
const root = await mkdtemp2(join18(tmpdir3(), "agentwheel-stage-"));
|
|
3630
4438
|
const stagedArtifacts = [];
|
|
3631
4439
|
for (const artifact of artifacts) {
|
|
3632
|
-
const stagedPath =
|
|
3633
|
-
await
|
|
3634
|
-
await cp5(artifact.sourcePath, stagedPath, {
|
|
4440
|
+
const stagedPath = join18(root, artifact.relativePath);
|
|
4441
|
+
await mkdir11(dirname14(stagedPath), { recursive: true });
|
|
4442
|
+
await cp5(artifact.sourcePath, stagedPath, {
|
|
4443
|
+
recursive: artifact.kind === "dir",
|
|
4444
|
+
dereference: true,
|
|
4445
|
+
filter: (path) => !isIgnoredGeneratedEntry(basename13(path))
|
|
4446
|
+
});
|
|
3635
4447
|
await composeAssets(artifact, resolved.resolvedPath, stagedPath);
|
|
3636
4448
|
stagedArtifacts.push({
|
|
3637
4449
|
...artifact,
|
|
@@ -3658,12 +4470,14 @@ async function renderStagedBundle(bundle, options = {}) {
|
|
|
3658
4470
|
const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, options.select, options.skills);
|
|
3659
4471
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(options.select, options.skills) ?? []);
|
|
3660
4472
|
const runtimeArtifacts = options.adapter ? filterArtifactsByRuntime(selectedArtifacts, options.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
3661
|
-
const
|
|
4473
|
+
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, root, options.adapter);
|
|
4474
|
+
const renderedArtifacts = await renderCopilotArtifacts(codexRenderedArtifacts, root, options.adapter);
|
|
4475
|
+
const finalArtifacts = options.workspaceRoot && options.adapter ? await applyCustomizations(renderedArtifacts, {
|
|
3662
4476
|
workspaceRoot: options.workspaceRoot,
|
|
3663
4477
|
adapter: options.adapter,
|
|
3664
4478
|
stageRoot: root,
|
|
3665
4479
|
packageName: resolved.packageName
|
|
3666
|
-
}) :
|
|
4480
|
+
}) : renderedArtifacts;
|
|
3667
4481
|
const generatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3668
4482
|
return {
|
|
3669
4483
|
root,
|
|
@@ -3687,6 +4501,7 @@ async function renderStagedBundle(bundle, options = {}) {
|
|
|
3687
4501
|
relativePath: artifact.relativePath,
|
|
3688
4502
|
kind: artifact.kind,
|
|
3689
4503
|
hash: artifact.hash,
|
|
4504
|
+
format: artifact.format,
|
|
3690
4505
|
composedFrom: artifact.composedFrom
|
|
3691
4506
|
}))
|
|
3692
4507
|
}
|
|
@@ -3708,16 +4523,16 @@ async function composeAssets(artifact, packageRoot, stagedPath) {
|
|
|
3708
4523
|
}
|
|
3709
4524
|
for (const asset of artifact.assets) {
|
|
3710
4525
|
const source = resolvePackagePath(packageRoot, asset.from);
|
|
3711
|
-
const dest =
|
|
4526
|
+
const dest = join18(stagedPath, asset.into);
|
|
3712
4527
|
await copyAsset(asset, source, dest);
|
|
3713
4528
|
}
|
|
3714
4529
|
}
|
|
3715
4530
|
async function copyAsset(asset, source, dest) {
|
|
3716
4531
|
const sourceStats = await stat8(source);
|
|
3717
4532
|
if (sourceStats.isFile()) {
|
|
3718
|
-
if (matchesAny(
|
|
3719
|
-
await
|
|
3720
|
-
await copyAssetFile(source,
|
|
4533
|
+
if (matchesAny(basename13(source), asset.include)) {
|
|
4534
|
+
await mkdir11(dest, { recursive: true });
|
|
4535
|
+
await copyAssetFile(source, join18(dest, basename13(source)), asset);
|
|
3721
4536
|
}
|
|
3722
4537
|
return;
|
|
3723
4538
|
}
|
|
@@ -3725,19 +4540,19 @@ async function copyAsset(asset, source, dest) {
|
|
|
3725
4540
|
throw new Error(`Asset include source is not a file or directory: ${source}`);
|
|
3726
4541
|
}
|
|
3727
4542
|
if (!asset.include?.length) {
|
|
3728
|
-
await
|
|
4543
|
+
await mkdir11(dirname14(dest), { recursive: true });
|
|
3729
4544
|
await cp5(source, dest, { recursive: true, dereference: true });
|
|
3730
4545
|
if (asset.mode === "copy") await normalizeCopiedModes(dest);
|
|
3731
4546
|
return;
|
|
3732
4547
|
}
|
|
3733
4548
|
for (const file of await listFiles(source)) {
|
|
3734
|
-
const rel =
|
|
3735
|
-
if (!matchesAny(rel, asset.include) && !matchesAny(
|
|
3736
|
-
await copyAssetFile(file,
|
|
4549
|
+
const rel = relative5(source, file).replaceAll("\\", "/");
|
|
4550
|
+
if (!matchesAny(rel, asset.include) && !matchesAny(basename13(file), asset.include)) continue;
|
|
4551
|
+
await copyAssetFile(file, join18(dest, rel), asset);
|
|
3737
4552
|
}
|
|
3738
4553
|
}
|
|
3739
4554
|
async function copyAssetFile(source, dest, asset) {
|
|
3740
|
-
await
|
|
4555
|
+
await mkdir11(dirname14(dest), { recursive: true });
|
|
3741
4556
|
await cp5(source, dest, { dereference: true });
|
|
3742
4557
|
if (asset.mode === "copy") await chmod(dest, 420);
|
|
3743
4558
|
}
|
|
@@ -3753,7 +4568,7 @@ async function listFiles(root) {
|
|
|
3753
4568
|
const out = [];
|
|
3754
4569
|
async function walk2(dir) {
|
|
3755
4570
|
for (const entry of (await readdir5(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
3756
|
-
const full =
|
|
4571
|
+
const full = join18(dir, entry.name);
|
|
3757
4572
|
if (entry.isDirectory()) {
|
|
3758
4573
|
await walk2(full);
|
|
3759
4574
|
} else if (entry.isFile()) {
|
|
@@ -3772,7 +4587,7 @@ async function normalizeCopiedModes(path) {
|
|
|
3772
4587
|
}
|
|
3773
4588
|
if (!stats.isDirectory()) return;
|
|
3774
4589
|
for (const entry of await readdir5(path, { withFileTypes: true })) {
|
|
3775
|
-
await normalizeCopiedModes(
|
|
4590
|
+
await normalizeCopiedModes(join18(path, entry.name));
|
|
3776
4591
|
}
|
|
3777
4592
|
}
|
|
3778
4593
|
function matchesAny(path, patterns) {
|
|
@@ -3785,9 +4600,9 @@ function matchesGlob(path, pattern) {
|
|
|
3785
4600
|
}
|
|
3786
4601
|
|
|
3787
4602
|
// src/model/workspace.ts
|
|
3788
|
-
import { readFile as
|
|
3789
|
-
import { homedir as
|
|
3790
|
-
import { dirname as
|
|
4603
|
+
import { readFile as readFile14 } from "fs/promises";
|
|
4604
|
+
import { homedir as homedir4 } from "os";
|
|
4605
|
+
import { dirname as dirname15, join as join19, resolve as resolve11 } from "path";
|
|
3791
4606
|
import { z as z6 } from "zod";
|
|
3792
4607
|
var workspacePackageSchema = z6.object({
|
|
3793
4608
|
name: z6.string().min(1),
|
|
@@ -3797,6 +4612,7 @@ var workspacePackageSchema = z6.object({
|
|
|
3797
4612
|
adapterConfig: z6.string().min(1).optional(),
|
|
3798
4613
|
adapterModule: z6.string().min(1).optional(),
|
|
3799
4614
|
adapterCodeHash: z6.string().min(16).optional(),
|
|
4615
|
+
installationType: installationTypeSchema.optional(),
|
|
3800
4616
|
mode: z6.enum(["pinned", "tracking"]).default("pinned"),
|
|
3801
4617
|
requestedRef: z6.string().min(1).optional(),
|
|
3802
4618
|
select: z6.array(z6.string().min(1)).optional(),
|
|
@@ -3809,6 +4625,7 @@ var workspaceProfileRuntimeSchema = z6.object({
|
|
|
3809
4625
|
adapter: z6.string().min(1).default("openclaw"),
|
|
3810
4626
|
adapterConfig: z6.string().min(1).optional(),
|
|
3811
4627
|
adapterModule: z6.string().min(1).optional(),
|
|
4628
|
+
installationType: installationTypeSchema.optional(),
|
|
3812
4629
|
targetRoot: z6.string().min(1).optional(),
|
|
3813
4630
|
executePlugins: z6.boolean().optional()
|
|
3814
4631
|
});
|
|
@@ -3828,6 +4645,7 @@ var workspaceTrustSchema = z6.object({
|
|
|
3828
4645
|
var workspaceAgentSchema = z6.object({
|
|
3829
4646
|
adapter: z6.string().min(1),
|
|
3830
4647
|
root: z6.string().min(1),
|
|
4648
|
+
installationType: installationTypeSchema.optional(),
|
|
3831
4649
|
transport: z6.enum(["local", "ssh"]).default("local"),
|
|
3832
4650
|
host: z6.string().min(1).optional(),
|
|
3833
4651
|
user: z6.string().min(1).optional(),
|
|
@@ -3853,12 +4671,12 @@ var workspaceConfigSchema = z6.object({
|
|
|
3853
4671
|
agents: z6.record(z6.string(), workspaceAgentSchema).default({})
|
|
3854
4672
|
});
|
|
3855
4673
|
function workspaceConfigPath(workspaceRoot) {
|
|
3856
|
-
return
|
|
4674
|
+
return join19(workspaceRoot, ".agentwheel", "config.json");
|
|
3857
4675
|
}
|
|
3858
4676
|
async function readWorkspaceConfig(workspaceRoot) {
|
|
3859
4677
|
const path = workspaceConfigPath(workspaceRoot);
|
|
3860
4678
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
3861
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
4679
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
|
|
3862
4680
|
}
|
|
3863
4681
|
async function writeWorkspaceConfig(workspaceRoot, config) {
|
|
3864
4682
|
await writeJsonAtomic(workspaceConfigPath(workspaceRoot), workspaceConfigSchema.parse(config));
|
|
@@ -3870,14 +4688,14 @@ function upsertPackage(config, entry) {
|
|
|
3870
4688
|
packages.sort((a, b) => a.name.localeCompare(b.name));
|
|
3871
4689
|
return { schemaVersion: 1, packages, bootstrapSkills: parsed.bootstrapSkills, registry: parsed.registry ?? {}, trust: parsed.trust ?? {}, profiles: parsed.profiles ?? {}, agents: parsed.agents ?? {} };
|
|
3872
4690
|
}
|
|
3873
|
-
function globalWorkspaceConfigPath(globalRoot =
|
|
3874
|
-
return
|
|
4691
|
+
function globalWorkspaceConfigPath(globalRoot = homedir4()) {
|
|
4692
|
+
return join19(globalRoot, ".agentwheel", "config.json");
|
|
3875
4693
|
}
|
|
3876
4694
|
async function findWorkspaceRoot(start = process.cwd()) {
|
|
3877
4695
|
let current = resolve11(start);
|
|
3878
4696
|
while (true) {
|
|
3879
4697
|
if (await pathExists(workspaceConfigPath(current))) return current;
|
|
3880
|
-
const parent =
|
|
4698
|
+
const parent = dirname15(current);
|
|
3881
4699
|
if (parent === current) return resolve11(start);
|
|
3882
4700
|
current = parent;
|
|
3883
4701
|
}
|
|
@@ -3904,8 +4722,8 @@ function mergeWorkspaceConfig(global, project) {
|
|
|
3904
4722
|
});
|
|
3905
4723
|
}
|
|
3906
4724
|
function resolveConfigPath(path, baseRoot) {
|
|
3907
|
-
if (path.startsWith("~/")) return resolve11(
|
|
3908
|
-
if (path === "~") return
|
|
4725
|
+
if (path.startsWith("~/")) return resolve11(homedir4(), path.slice(2));
|
|
4726
|
+
if (path === "~") return homedir4();
|
|
3909
4727
|
return path.startsWith("/") ? resolve11(path) : resolve11(baseRoot, path);
|
|
3910
4728
|
}
|
|
3911
4729
|
function emptyWorkspaceConfig() {
|
|
@@ -3913,7 +4731,7 @@ function emptyWorkspaceConfig() {
|
|
|
3913
4731
|
}
|
|
3914
4732
|
async function readConfigPath(path) {
|
|
3915
4733
|
if (!await pathExists(path)) return emptyWorkspaceConfig();
|
|
3916
|
-
return workspaceConfigSchema.parse(JSON.parse(await
|
|
4734
|
+
return workspaceConfigSchema.parse(JSON.parse(await readFile14(path, "utf8")));
|
|
3917
4735
|
}
|
|
3918
4736
|
function mergeWorkspaceTrust(global, project) {
|
|
3919
4737
|
return {
|
|
@@ -3928,23 +4746,23 @@ function sortedUnique2(values) {
|
|
|
3928
4746
|
}
|
|
3929
4747
|
|
|
3930
4748
|
// src/lifecycle/customization.ts
|
|
3931
|
-
import { appendFile, cp as cp6, mkdir as
|
|
3932
|
-
import { dirname as
|
|
4749
|
+
import { appendFile, cp as cp6, mkdir as mkdir12, rm as rm7 } from "fs/promises";
|
|
4750
|
+
import { dirname as dirname17, join as join22 } from "path";
|
|
3933
4751
|
|
|
3934
4752
|
// src/resolve/graph.ts
|
|
3935
|
-
import { createHash as
|
|
3936
|
-
import { mkdtemp as mkdtemp3, readdir as readdir6, readFile as
|
|
4753
|
+
import { createHash as createHash5 } from "crypto";
|
|
4754
|
+
import { mkdtemp as mkdtemp3, readdir as readdir6, readFile as readFile16, stat as stat10 } from "fs/promises";
|
|
3937
4755
|
import { tmpdir as tmpdir4 } from "os";
|
|
3938
|
-
import { basename as
|
|
4756
|
+
import { basename as basename14, extname as extname4, join as join21 } from "path";
|
|
3939
4757
|
|
|
3940
4758
|
// src/resolve/identity.ts
|
|
3941
|
-
import { homedir as
|
|
4759
|
+
import { homedir as homedir6 } from "os";
|
|
3942
4760
|
import { resolve as resolve13 } from "path";
|
|
3943
4761
|
|
|
3944
4762
|
// src/registry/client.ts
|
|
3945
|
-
import { readFile as
|
|
3946
|
-
import { homedir as
|
|
3947
|
-
import { dirname as
|
|
4763
|
+
import { readFile as readFile15, rm as rm6, stat as stat9 } from "fs/promises";
|
|
4764
|
+
import { homedir as homedir5 } from "os";
|
|
4765
|
+
import { dirname as dirname16, join as join20, resolve as resolve12 } from "path";
|
|
3948
4766
|
import { fileURLToPath } from "url";
|
|
3949
4767
|
|
|
3950
4768
|
// src/model/registry.ts
|
|
@@ -4042,7 +4860,7 @@ var RegistryClient = class {
|
|
|
4042
4860
|
}
|
|
4043
4861
|
async readCache() {
|
|
4044
4862
|
if (!await pathExists(this.cachePath)) return void 0;
|
|
4045
|
-
return registryCacheSchema.parse(JSON.parse(await
|
|
4863
|
+
return registryCacheSchema.parse(JSON.parse(await readFile15(this.cachePath, "utf8")));
|
|
4046
4864
|
}
|
|
4047
4865
|
isExpired(cache, ttlMs) {
|
|
4048
4866
|
return this.now().getTime() - new Date(cache.fetchedAt).getTime() > ttlMs;
|
|
@@ -4061,10 +4879,10 @@ var RegistryClient = class {
|
|
|
4061
4879
|
if (await pathExists(filePath)) {
|
|
4062
4880
|
const fullPath = resolve12(filePath);
|
|
4063
4881
|
const stats = await stat9(fullPath);
|
|
4064
|
-
return
|
|
4882
|
+
return readFile15(stats.isDirectory() ? join20(fullPath, "index.json") : fullPath, "utf8");
|
|
4065
4883
|
}
|
|
4066
|
-
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot:
|
|
4067
|
-
return
|
|
4884
|
+
const resolved = await this.git.fetch(await this.git.resolve(source, { cacheRoot: join20(dirname16(this.cachePath), "registry-repos") }));
|
|
4885
|
+
return readFile15(join20(resolved.resolvedPath, "index.json"), "utf8");
|
|
4068
4886
|
}
|
|
4069
4887
|
warnCompatibility(entries) {
|
|
4070
4888
|
for (const entry of entries) {
|
|
@@ -4078,10 +4896,13 @@ var RegistryClient = class {
|
|
|
4078
4896
|
}
|
|
4079
4897
|
};
|
|
4080
4898
|
async function resolvePackageSource(source, workspaceRoot, options = {}) {
|
|
4081
|
-
const { isExplicitSource } = await import("./identify-
|
|
4899
|
+
const { isExplicitSource } = await import("./identify-TXIDGMNL.js");
|
|
4082
4900
|
if (await isExplicitSource(source)) return { source };
|
|
4083
4901
|
const entry = await new RegistryClient({ workspaceRoot, offline: options.offline, warn: options.warn }).resolve(source);
|
|
4084
4902
|
if (!entry) {
|
|
4903
|
+
if (options.offline) {
|
|
4904
|
+
throw new Error(`Offline cannot refresh registry indexes (entry not found in cache: ${source}). Run without --offline first.`);
|
|
4905
|
+
}
|
|
4085
4906
|
throw new Error(`Registry entry not found: ${source}. Use an explicit path/git/skillkit/vercel source to bypass the registry.`);
|
|
4086
4907
|
}
|
|
4087
4908
|
return { source: entry.source, registryEntry: entry };
|
|
@@ -4096,7 +4917,7 @@ function mergeIndexes(indexes) {
|
|
|
4096
4917
|
return [...merged.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
4097
4918
|
}
|
|
4098
4919
|
function defaultRegistryCachePath() {
|
|
4099
|
-
return
|
|
4920
|
+
return join20(homedir5(), ".agentwheel", "registry-cache.json");
|
|
4100
4921
|
}
|
|
4101
4922
|
function sameSources(a, b) {
|
|
4102
4923
|
return a.length === b.length && a.every((source, index) => source === b[index]);
|
|
@@ -4165,8 +4986,8 @@ function localSourcePath(source) {
|
|
|
4165
4986
|
return source.startsWith("local:") ? source.slice("local:".length) : source;
|
|
4166
4987
|
}
|
|
4167
4988
|
function resolveLocalPath(path, declaringPackageRoot) {
|
|
4168
|
-
if (path === "~") return
|
|
4169
|
-
if (path.startsWith("~/")) return resolve13(
|
|
4989
|
+
if (path === "~") return homedir6();
|
|
4990
|
+
if (path.startsWith("~/")) return resolve13(homedir6(), path.slice(2));
|
|
4170
4991
|
if (path.startsWith("/")) return resolve13(path);
|
|
4171
4992
|
return resolve13(declaringPackageRoot, path);
|
|
4172
4993
|
}
|
|
@@ -4325,7 +5146,7 @@ function compareSemver(a, b) {
|
|
|
4325
5146
|
var cacheLocks = /* @__PURE__ */ new Map();
|
|
4326
5147
|
async function resolveDependencyGraph(roots, options) {
|
|
4327
5148
|
if (roots.length === 0) throw new Error("At least one graph root is required.");
|
|
4328
|
-
const graphRoot = await mkdtemp3(
|
|
5149
|
+
const graphRoot = await mkdtemp3(join21(tmpdir4(), "agentwheel-graph-"));
|
|
4329
5150
|
const fetchCache = /* @__PURE__ */ new Map();
|
|
4330
5151
|
const nodesByKey = /* @__PURE__ */ new Map();
|
|
4331
5152
|
const rootResults = [];
|
|
@@ -4687,7 +5508,7 @@ async function collectIncludeNeeds(artifact, artifactsByRelativePath) {
|
|
|
4687
5508
|
const file = stack.shift();
|
|
4688
5509
|
if (scanned.has(file)) continue;
|
|
4689
5510
|
scanned.add(file);
|
|
4690
|
-
const content = await
|
|
5511
|
+
const content = await readFile16(file, "utf8");
|
|
4691
5512
|
for (const include of extractOpenPackIncludeSelectors(content)) {
|
|
4692
5513
|
await collectIncludeSelector(include.raw, include.optional, artifactsByRelativePath, scanned, stack, needs);
|
|
4693
5514
|
}
|
|
@@ -4730,7 +5551,7 @@ async function listMarkdownFiles2(root) {
|
|
|
4730
5551
|
const out = [];
|
|
4731
5552
|
async function walk2(dir) {
|
|
4732
5553
|
for (const entry of (await readdir6(dir, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4733
|
-
const full =
|
|
5554
|
+
const full = join21(dir, entry.name);
|
|
4734
5555
|
if (entry.isDirectory()) {
|
|
4735
5556
|
await walk2(full);
|
|
4736
5557
|
} else if (entry.isFile() && extname4(entry.name).toLowerCase() === ".md") {
|
|
@@ -4765,7 +5586,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4765
5586
|
const promise = (async () => {
|
|
4766
5587
|
const driver = getSourceDriver(normalized.driver);
|
|
4767
5588
|
const resolved = await driver.resolve(normalized.source, {
|
|
4768
|
-
cacheRoot: options.cacheRoot ??
|
|
5589
|
+
cacheRoot: options.cacheRoot ?? join21(options.workspaceRoot, ".agentwheel", "cache"),
|
|
4769
5590
|
mode,
|
|
4770
5591
|
ref: refOverride ?? normalized.requestedRef,
|
|
4771
5592
|
frozenLock: hardLockedCheckout
|
|
@@ -4775,7 +5596,7 @@ async function fetchPackage(normalized, mode, options, fetchCache, refOverride)
|
|
|
4775
5596
|
const exported = await driver.export(translated);
|
|
4776
5597
|
const manifest = await readPackageManifest(exported.resolvedPath);
|
|
4777
5598
|
const artifacts = await driver.list(exported);
|
|
4778
|
-
const name = manifest?.name ?? exported.packageName ??
|
|
5599
|
+
const name = manifest?.name ?? exported.packageName ?? basename14(exported.resolvedPath);
|
|
4779
5600
|
const version = manifest?.version ?? exported.packageVersion ?? "0.0.0";
|
|
4780
5601
|
const sourceHash = exported.sourceHash ?? await hashPath(exported.resolvedPath);
|
|
4781
5602
|
return {
|
|
@@ -4951,7 +5772,7 @@ function detectDirectCollisions(nodes) {
|
|
|
4951
5772
|
}
|
|
4952
5773
|
}
|
|
4953
5774
|
function graphNodeId(name, version, normalizedSource, resolvedCommit, sourceHash) {
|
|
4954
|
-
const digest =
|
|
5775
|
+
const digest = createHash5("sha256").update(normalizedSource).update("\0").update(resolvedCommit ?? sourceHash).digest("hex").slice(0, 12);
|
|
4955
5776
|
return `${name}@${version}+${digest}`;
|
|
4956
5777
|
}
|
|
4957
5778
|
function sortedUnique3(values) {
|
|
@@ -4972,8 +5793,8 @@ async function mapLimit(items, limit, fn) {
|
|
|
4972
5793
|
|
|
4973
5794
|
// src/lifecycle/customization.ts
|
|
4974
5795
|
async function remember(workspaceRoot, runtime, text) {
|
|
4975
|
-
const overlayPath =
|
|
4976
|
-
await
|
|
5796
|
+
const overlayPath = join22(workspaceRoot, ".agentwheel", "overlays", runtime, "instructions.local.md");
|
|
5797
|
+
await mkdir12(dirname17(overlayPath), { recursive: true });
|
|
4977
5798
|
await appendFile(overlayPath, `${text.trim()}
|
|
4978
5799
|
`, "utf8");
|
|
4979
5800
|
return { overlayPath };
|
|
@@ -4996,8 +5817,8 @@ async function ejectArtifact(workspaceRoot, item) {
|
|
|
4996
5817
|
throw new Error(`Artifact not found: ${item}`);
|
|
4997
5818
|
}
|
|
4998
5819
|
const ejectedIdentity = parsed.packageIdentity === parsed.packageName ? parsed.packageIdentity : candidate.nodeId === parsed.packageIdentity ? candidate.nodeId : `${candidate.packageName}@${candidate.packageVersion}`;
|
|
4999
|
-
const ejectedPath =
|
|
5000
|
-
await
|
|
5820
|
+
const ejectedPath = join22(workspaceRoot, ".agentwheel", "ejected", ...ejectedIdentity.split("/"), parsed.type, parsed.name);
|
|
5821
|
+
await mkdir12(dirname17(ejectedPath), { recursive: true });
|
|
5001
5822
|
await rm7(ejectedPath, { recursive: true, force: true });
|
|
5002
5823
|
await cp6(artifact.stagedPath ?? artifact.sourcePath, ejectedPath, { recursive: artifact.kind === "dir", dereference: true });
|
|
5003
5824
|
return {
|
|
@@ -5039,7 +5860,7 @@ async function stageEjectCandidate(workspaceRoot, pkg) {
|
|
|
5039
5860
|
const adapter = pkg.adapterConfig ? await loadAdapterConfig(pkg.adapterConfig) : getAdapter(pkg.adapter);
|
|
5040
5861
|
const bundle = await stageSource(driver, normalized.source, {
|
|
5041
5862
|
adapter,
|
|
5042
|
-
cacheRoot:
|
|
5863
|
+
cacheRoot: join22(workspaceRoot, ".agentwheel", "cache"),
|
|
5043
5864
|
mode: pkg.mode,
|
|
5044
5865
|
ref: normalized.requestedRef ?? pkg.requestedRef
|
|
5045
5866
|
});
|
|
@@ -5089,9 +5910,9 @@ function ejectCommands(candidates, parsed) {
|
|
|
5089
5910
|
import { rm as rm8 } from "fs/promises";
|
|
5090
5911
|
|
|
5091
5912
|
// src/lifecycle/source-plan.ts
|
|
5092
|
-
import { createHash as
|
|
5093
|
-
import { mkdir as
|
|
5094
|
-
import { dirname as
|
|
5913
|
+
import { createHash as createHash7 } from "crypto";
|
|
5914
|
+
import { mkdir as mkdir14 } from "fs/promises";
|
|
5915
|
+
import { dirname as dirname19, join as join25, resolve as resolve14 } from "path";
|
|
5095
5916
|
|
|
5096
5917
|
// src/resolve/graph-diff.ts
|
|
5097
5918
|
function diffGraphLocks(previous, next) {
|
|
@@ -5212,12 +6033,12 @@ function short(hash) {
|
|
|
5212
6033
|
}
|
|
5213
6034
|
|
|
5214
6035
|
// src/resolve/render.ts
|
|
5215
|
-
import { createHash as
|
|
5216
|
-
import { readFile as
|
|
6036
|
+
import { createHash as createHash6 } from "crypto";
|
|
6037
|
+
import { readFile as readFile17, mkdtemp as mkdtemp4 } from "fs/promises";
|
|
5217
6038
|
import { tmpdir as tmpdir5 } from "os";
|
|
5218
|
-
import { join as
|
|
6039
|
+
import { join as join23 } from "path";
|
|
5219
6040
|
async function renderGraphForTarget(graph, targetContext = {}) {
|
|
5220
|
-
const root = await mkdtemp4(
|
|
6041
|
+
const root = await mkdtemp4(join23(tmpdir5(), "agentwheel-render-"));
|
|
5221
6042
|
const artifacts = [];
|
|
5222
6043
|
const stagedNodes = /* @__PURE__ */ new Map();
|
|
5223
6044
|
const includeEdges = /* @__PURE__ */ new Map();
|
|
@@ -5289,7 +6110,9 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
5289
6110
|
const selectedArtifacts = filterArtifactsBySelection(expandedArtifacts, rawNode.node.selected);
|
|
5290
6111
|
const runtimeSelectedSet = new Set(normalizeArtifactSelectors(rawNode.node.selected) ?? []);
|
|
5291
6112
|
const runtimeArtifacts = targetContext.adapter ? filterArtifactsByRuntime2(selectedArtifacts, targetContext.adapter.name, runtimeSelectedSet) : selectedArtifacts;
|
|
5292
|
-
const
|
|
6113
|
+
const codexRenderedArtifacts = await renderCodexSubagents(runtimeArtifacts, staged.root, targetContext.adapter);
|
|
6114
|
+
const runtimeRenderedArtifacts = await renderCopilotArtifacts(codexRenderedArtifacts, staged.root, targetContext.adapter);
|
|
6115
|
+
const renderedArtifacts = targetContext.workspaceRoot && targetContext.adapter ? await applyCustomizations(runtimeRenderedArtifacts, {
|
|
5293
6116
|
workspaceRoot: targetContext.workspaceRoot,
|
|
5294
6117
|
adapter: targetContext.adapter,
|
|
5295
6118
|
stageRoot: staged.root,
|
|
@@ -5297,7 +6120,7 @@ async function renderGraphForTarget(graph, targetContext = {}) {
|
|
|
5297
6120
|
packageVersion: rawNode.resolved.packageVersion,
|
|
5298
6121
|
graphNodeId: rawNode.node.id,
|
|
5299
6122
|
packageNameAmbiguous: ambiguousPackageNames.has(rawNode.node.name)
|
|
5300
|
-
}) :
|
|
6123
|
+
}) : runtimeRenderedArtifacts;
|
|
5301
6124
|
const installableArtifacts = renderedArtifacts.filter((artifact) => rawNode.depth === 0 || artifact.type !== "fragments");
|
|
5302
6125
|
artifacts.push(...installableArtifacts.map((artifact) => ({
|
|
5303
6126
|
...artifact,
|
|
@@ -5331,7 +6154,7 @@ async function artifactContentMap(artifacts) {
|
|
|
5331
6154
|
const out = /* @__PURE__ */ new Map();
|
|
5332
6155
|
for (const artifact of artifacts) {
|
|
5333
6156
|
if (artifact.kind !== "file") continue;
|
|
5334
|
-
out.set(artifact.relativePath.replaceAll("\\", "/"), await
|
|
6157
|
+
out.set(artifact.relativePath.replaceAll("\\", "/"), await readFile17(artifact.stagedPath ?? artifact.sourcePath, "utf8"));
|
|
5335
6158
|
}
|
|
5336
6159
|
return out;
|
|
5337
6160
|
}
|
|
@@ -5345,7 +6168,7 @@ function filterArtifactsByRuntime2(artifacts, adapterName, selectedSet) {
|
|
|
5345
6168
|
});
|
|
5346
6169
|
}
|
|
5347
6170
|
function sha2562(content) {
|
|
5348
|
-
return
|
|
6171
|
+
return createHash6("sha256").update(content).digest("hex");
|
|
5349
6172
|
}
|
|
5350
6173
|
function assignInstallNames(graph, artifacts) {
|
|
5351
6174
|
const aliases = workspaceAliases(graph);
|
|
@@ -5594,9 +6417,9 @@ function lockArtifactFor(artifact) {
|
|
|
5594
6417
|
}
|
|
5595
6418
|
|
|
5596
6419
|
// src/lifecycle/trust.ts
|
|
5597
|
-
import { mkdir as
|
|
5598
|
-
import { homedir as
|
|
5599
|
-
import { dirname as
|
|
6420
|
+
import { mkdir as mkdir13, readFile as readFile18 } from "fs/promises";
|
|
6421
|
+
import { homedir as homedir7 } from "os";
|
|
6422
|
+
import { dirname as dirname18, join as join24 } from "path";
|
|
5600
6423
|
import { z as z8 } from "zod";
|
|
5601
6424
|
var trustStoreSchema = z8.object({
|
|
5602
6425
|
version: z8.literal(1),
|
|
@@ -5670,14 +6493,14 @@ function sortedUnique4(values) {
|
|
|
5670
6493
|
}
|
|
5671
6494
|
async function readTrustStore(path) {
|
|
5672
6495
|
if (!await pathExists(path)) return { version: 1, acceptedSources: [] };
|
|
5673
|
-
return trustStoreSchema.parse(JSON.parse(await
|
|
6496
|
+
return trustStoreSchema.parse(JSON.parse(await readFile18(path, "utf8")));
|
|
5674
6497
|
}
|
|
5675
6498
|
async function writeTrustStore(path, store) {
|
|
5676
|
-
await
|
|
6499
|
+
await mkdir13(dirname18(path), { recursive: true });
|
|
5677
6500
|
await writeJsonAtomic(path, trustStoreSchema.parse(store));
|
|
5678
6501
|
}
|
|
5679
6502
|
function defaultTrustStorePath() {
|
|
5680
|
-
return process.env.AGENTWHEEL_TRUST_STORE ??
|
|
6503
|
+
return process.env.AGENTWHEEL_TRUST_STORE ?? join24(homedir7(), ".agentwheel", "trust.json");
|
|
5681
6504
|
}
|
|
5682
6505
|
|
|
5683
6506
|
// src/lifecycle/source-plan.ts
|
|
@@ -5692,7 +6515,17 @@ async function createGraphSourcePlan(options) {
|
|
|
5692
6515
|
warnings.push(message);
|
|
5693
6516
|
options.warn?.(message);
|
|
5694
6517
|
};
|
|
5695
|
-
const
|
|
6518
|
+
const installationType = options.installationType ?? resolveInstallationTypeForAdapterTarget(options.adapter);
|
|
6519
|
+
const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
|
|
6520
|
+
adapter: options.adapter.name,
|
|
6521
|
+
installationType,
|
|
6522
|
+
targetRoot: options.targetRoot,
|
|
6523
|
+
transport: transport.kind,
|
|
6524
|
+
transportDescription: transport.description
|
|
6525
|
+
});
|
|
6526
|
+
const stateKey = options.stateKey ?? stateKeyFor(options.adapter.name, { installationType, targetFingerprint });
|
|
6527
|
+
const installRoot = installRootForAdapterInstallationType(options.adapter, options.targetRoot, installationType, transport.kind === "ssh");
|
|
6528
|
+
const recoveredPendingApply = options.readOnly === true ? false : await recoverPendingApplyIfSafe(installRoot, options.adapter.name, transport, { installationType, stateKey });
|
|
5696
6529
|
const workspaceConfig = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: options.globalRoot });
|
|
5697
6530
|
const trustPolicy = {
|
|
5698
6531
|
...normalizeTrustPolicy(workspaceConfig.trust),
|
|
@@ -5700,18 +6533,12 @@ async function createGraphSourcePlan(options) {
|
|
|
5700
6533
|
};
|
|
5701
6534
|
const lockMode = options.frozenLock === true || options.offline === true;
|
|
5702
6535
|
const lockLabel = options.offline === true ? "Offline" : options.frozenLock === true ? "Frozen lock" : options.lockedResolution === true ? "Locked install" : "Fresh resolve";
|
|
5703
|
-
const targetFingerprint = computeTargetFingerprint(options.targetFingerprintParts ?? {
|
|
5704
|
-
adapter: options.adapter.name,
|
|
5705
|
-
targetRoot: options.targetRoot,
|
|
5706
|
-
transport: transport.kind,
|
|
5707
|
-
transportDescription: transport.description
|
|
5708
|
-
});
|
|
5709
6536
|
const graphLockPath = pathForGraphLock(workspaceRoot, options.targetKey ?? "default", options.adapter.name, targetFingerprint);
|
|
5710
6537
|
const previousLock = await readExistingGraphLock(graphLockPath);
|
|
5711
6538
|
const registryClient = new RegistryClient({ workspaceRoot, offline: lockMode, offlineLabel: lockLabel, warn });
|
|
5712
6539
|
const graph = await resolveDependencyGraph(options.roots, {
|
|
5713
6540
|
workspaceRoot,
|
|
5714
|
-
cacheRoot:
|
|
6541
|
+
cacheRoot: join25(workspaceRoot, ".agentwheel", "cache"),
|
|
5715
6542
|
registryClient,
|
|
5716
6543
|
noDeps: options.noDeps,
|
|
5717
6544
|
lockedResolution: options.lockedResolution,
|
|
@@ -5733,13 +6560,20 @@ async function createGraphSourcePlan(options) {
|
|
|
5733
6560
|
targetFingerprint
|
|
5734
6561
|
});
|
|
5735
6562
|
const desiredArtifacts = desiredArtifactsFromGraphBundle(bundle);
|
|
6563
|
+
const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
|
|
6564
|
+
const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
5736
6565
|
const graphLockDigest = digestGraphLock(bundle.graphLock);
|
|
5737
6566
|
const graphDiff = diffGraphLocks(previousLock, bundle.graphLock);
|
|
5738
|
-
const manifest = await readInstallManifest(
|
|
6567
|
+
const manifest = await readInstallManifest(resolvedInstallRoot, options.adapter.name, transport, { installationType: resolvedInstallationType, stateKey });
|
|
5739
6568
|
const plan = await createCombinedInstallPlan(desiredArtifacts, options.adapter, options.targetRoot, manifest, transport, {
|
|
5740
6569
|
baseRevision: manifest?.revision ?? null,
|
|
5741
6570
|
graphLockDigest,
|
|
5742
|
-
workspaceOwner: workspaceOwnerId(workspaceRoot)
|
|
6571
|
+
workspaceOwner: workspaceOwnerId(workspaceRoot),
|
|
6572
|
+
installationType: resolvedInstallationType,
|
|
6573
|
+
stateKey,
|
|
6574
|
+
forceDrift: options.forceDrift,
|
|
6575
|
+
forceConflict: options.forceConflict,
|
|
6576
|
+
replaceConflict: options.replaceConflict
|
|
5743
6577
|
});
|
|
5744
6578
|
return {
|
|
5745
6579
|
plan,
|
|
@@ -5774,28 +6608,38 @@ function desiredArtifactFromResolved(artifact) {
|
|
|
5774
6608
|
}
|
|
5775
6609
|
};
|
|
5776
6610
|
}
|
|
5777
|
-
async function recoverPendingApplyIfSafe(targetRoot, adapter, transport) {
|
|
5778
|
-
if (!await readApplyJournal(targetRoot, adapter, transport)) return false;
|
|
6611
|
+
async function recoverPendingApplyIfSafe(targetRoot, adapter, transport, scope) {
|
|
6612
|
+
if (!await readApplyJournal(targetRoot, adapter, transport, scope)) return false;
|
|
5779
6613
|
try {
|
|
5780
|
-
await recoverPendingApply(targetRoot, adapter, transport);
|
|
6614
|
+
await recoverPendingApply(targetRoot, adapter, transport, scope);
|
|
5781
6615
|
return true;
|
|
5782
6616
|
} catch (error) {
|
|
5783
6617
|
const message = error instanceof Error ? error.message : String(error);
|
|
5784
6618
|
throw new Error(`Pending apply journal for ${adapter} at ${targetRoot} could not be recovered automatically: ${message}`);
|
|
5785
6619
|
}
|
|
5786
6620
|
}
|
|
6621
|
+
function resolveInstallationTypeForAdapterTarget(adapter) {
|
|
6622
|
+
const supported = /* @__PURE__ */ new Set();
|
|
6623
|
+
for (const registry of Object.values(adapter.targets)) {
|
|
6624
|
+
for (const [installationType, target] of Object.entries(registry ?? {})) {
|
|
6625
|
+
if (target.enabled) supported.add(installationType);
|
|
6626
|
+
}
|
|
6627
|
+
}
|
|
6628
|
+
if (supported.size === 1) return [...supported][0];
|
|
6629
|
+
return "local";
|
|
6630
|
+
}
|
|
5787
6631
|
async function readExistingGraphLock(path) {
|
|
5788
6632
|
if (!await pathExists(path)) return void 0;
|
|
5789
6633
|
return readGraphLock(path);
|
|
5790
6634
|
}
|
|
5791
6635
|
function pathForGraphLock(workspaceRoot, targetKey, adapter, targetFingerprint) {
|
|
5792
|
-
return
|
|
6636
|
+
return join25(workspaceRoot, ".agentwheel", "locks", sanitizePathSegment(targetKey), sanitizePathSegment(adapter), `${targetFingerprint}.graph-lock.json`);
|
|
5793
6637
|
}
|
|
5794
6638
|
function sanitizePathSegment(value) {
|
|
5795
6639
|
return value.replace(/[^a-z0-9._-]+/gi, "-").replace(/^-+|-+$/g, "") || "default";
|
|
5796
6640
|
}
|
|
5797
6641
|
function digestGraphLock(lock) {
|
|
5798
|
-
return
|
|
6642
|
+
return createHash7("sha256").update(canonicalGraphLockJson(lock)).digest("hex");
|
|
5799
6643
|
}
|
|
5800
6644
|
function workspaceOwnerId(workspaceRoot) {
|
|
5801
6645
|
return `workspace-root:${resolve14(workspaceRoot)}`;
|
|
@@ -5852,129 +6696,8 @@ Type yes to continue: `);
|
|
|
5852
6696
|
${sources.map((source) => `- ${source}`).join("\n")}`);
|
|
5853
6697
|
}
|
|
5854
6698
|
|
|
5855
|
-
// src/lifecycle/profile.ts
|
|
5856
|
-
async function syncProfile(options) {
|
|
5857
|
-
const config = await readMergedWorkspaceConfig(options.workspaceRoot);
|
|
5858
|
-
const profile = config.profiles[options.profile];
|
|
5859
|
-
if (!profile) {
|
|
5860
|
-
throw new Error(`Unknown profile: ${options.profile}`);
|
|
5861
|
-
}
|
|
5862
|
-
const packages = options.source ? [await packageFromSource(options.source, options)] : config.packages;
|
|
5863
|
-
if (packages.length === 0) {
|
|
5864
|
-
throw new Error("Profile sync needs a source argument or configured packages.");
|
|
5865
|
-
}
|
|
5866
|
-
const results = [];
|
|
5867
|
-
for (const runtime of profile.runtimes) {
|
|
5868
|
-
const target = resolveProfileRuntime(runtime, config, options.workspaceRoot);
|
|
5869
|
-
const adapter = await resolveAdapter({
|
|
5870
|
-
adapter: target.adapter,
|
|
5871
|
-
adapterConfig: runtime.adapterConfig,
|
|
5872
|
-
adapterModule: runtime.adapterModule,
|
|
5873
|
-
allowAdapterCode: options.allowAdapterCode,
|
|
5874
|
-
baseDir: options.workspaceRoot,
|
|
5875
|
-
warn: options.warn
|
|
5876
|
-
});
|
|
5877
|
-
const selected = normalizeArtifactSelectors(options.select, options.skills);
|
|
5878
|
-
const graphPlan = await createGraphSourcePlan({
|
|
5879
|
-
roots: packages.map((pkg) => ({
|
|
5880
|
-
rootId: pkg.name,
|
|
5881
|
-
source: pkg.source,
|
|
5882
|
-
mode: options.mode ?? pkg.mode,
|
|
5883
|
-
ref: pkg.requestedRef,
|
|
5884
|
-
select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
5885
|
-
aliases: pkg.aliases,
|
|
5886
|
-
overrides: pkg.overrides
|
|
5887
|
-
})),
|
|
5888
|
-
targetRoot: target.targetRoot,
|
|
5889
|
-
workspaceRoot: options.workspaceRoot,
|
|
5890
|
-
adapter,
|
|
5891
|
-
transport: target.transport,
|
|
5892
|
-
targetKey: runtime.agent ?? adapter.name,
|
|
5893
|
-
targetFingerprintParts: {
|
|
5894
|
-
adapter: adapter.name,
|
|
5895
|
-
adapterConfig: runtime.adapterConfig,
|
|
5896
|
-
adapterModule: runtime.adapterModule,
|
|
5897
|
-
adapterCodeHash: adapter.programmatic?.hash,
|
|
5898
|
-
targetRoot: target.targetRoot,
|
|
5899
|
-
transport: target.transport.kind
|
|
5900
|
-
},
|
|
5901
|
-
noDeps: options.noDeps,
|
|
5902
|
-
lockedResolution: options.lockedResolution,
|
|
5903
|
-
frozenLock: options.frozenLock,
|
|
5904
|
-
offline: options.offline,
|
|
5905
|
-
yes: options.yes,
|
|
5906
|
-
trustPatterns: options.trustPatterns ?? [],
|
|
5907
|
-
readOnly: options.readOnly,
|
|
5908
|
-
isTTY: options.isTTY,
|
|
5909
|
-
warn: options.warn
|
|
5910
|
-
});
|
|
5911
|
-
try {
|
|
5912
|
-
results.push({
|
|
5913
|
-
runtime: adapter.name,
|
|
5914
|
-
targetRoot: target.targetRoot,
|
|
5915
|
-
transport: target.transport.kind,
|
|
5916
|
-
packageName: packages.map((pkg) => pkg.name).join(","),
|
|
5917
|
-
plan: graphPlan.plan
|
|
5918
|
-
});
|
|
5919
|
-
if (!options.dryRun) {
|
|
5920
|
-
await applyCombinedInstallPlan(graphPlan.plan, {
|
|
5921
|
-
executePlugins: runtime.executePlugins ?? options.executePlugins,
|
|
5922
|
-
transport: target.transport,
|
|
5923
|
-
graphLockDigest: graphPlan.graphLockDigest,
|
|
5924
|
-
graphLock: { path: graphPlan.graphLockPath, lock: graphPlan.bundle.graphLock }
|
|
5925
|
-
});
|
|
5926
|
-
}
|
|
5927
|
-
} finally {
|
|
5928
|
-
await rm8(graphPlan.bundle.root, { recursive: true, force: true });
|
|
5929
|
-
}
|
|
5930
|
-
}
|
|
5931
|
-
return results;
|
|
5932
|
-
}
|
|
5933
|
-
function resolveProfileRuntime(runtime, config, workspaceRoot) {
|
|
5934
|
-
if (runtime.agent) {
|
|
5935
|
-
const agent = config.agents[runtime.agent];
|
|
5936
|
-
if (!agent) throw new Error(`Unknown agent in profile: ${runtime.agent}`);
|
|
5937
|
-
const target = {
|
|
5938
|
-
agentName: runtime.agent,
|
|
5939
|
-
adapter: agent.adapter,
|
|
5940
|
-
targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
|
|
5941
|
-
workspaceRoot,
|
|
5942
|
-
transport: agent.transport,
|
|
5943
|
-
ssh: agent.transport === "ssh" ? {
|
|
5944
|
-
host: agent.host ?? "",
|
|
5945
|
-
user: agent.user,
|
|
5946
|
-
port: agent.port,
|
|
5947
|
-
identityFile: agent.identityFile ? resolveConfigPath(agent.identityFile, workspaceRoot) : void 0
|
|
5948
|
-
} : void 0,
|
|
5949
|
-
source: "agent"
|
|
5950
|
-
};
|
|
5951
|
-
return { adapter: target.adapter, targetRoot: target.targetRoot, transport: transportForTarget(target) };
|
|
5952
|
-
}
|
|
5953
|
-
return {
|
|
5954
|
-
adapter: runtime.adapter,
|
|
5955
|
-
targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
|
|
5956
|
-
transport: localTransport
|
|
5957
|
-
};
|
|
5958
|
-
}
|
|
5959
|
-
async function packageFromSource(source, options) {
|
|
5960
|
-
const resolved = await resolvePackageSource(source, options.workspaceRoot, {
|
|
5961
|
-
offline: options.frozenLock === true || options.offline === true,
|
|
5962
|
-
warn: options.warn
|
|
5963
|
-
});
|
|
5964
|
-
const driver = options.driver ?? inferSourceDriverName(resolved.source);
|
|
5965
|
-
return {
|
|
5966
|
-
name: resolved.registryEntry?.name ?? source,
|
|
5967
|
-
source: resolved.source,
|
|
5968
|
-
driver,
|
|
5969
|
-
adapter: "openclaw",
|
|
5970
|
-
mode: options.mode ?? "pinned",
|
|
5971
|
-
select: options.select,
|
|
5972
|
-
skills: options.skills
|
|
5973
|
-
};
|
|
5974
|
-
}
|
|
5975
|
-
|
|
5976
6699
|
// src/runtime/target.ts
|
|
5977
|
-
import { basename as
|
|
6700
|
+
import { basename as basename15, dirname as dirname20, join as join26, resolve as resolve15 } from "path";
|
|
5978
6701
|
var runtimeMarkers = [
|
|
5979
6702
|
{ adapter: "openclaw", dirs: [".openclaw", ".clawdbot", ".moltbot"] },
|
|
5980
6703
|
{ adapter: "claude", dirs: [".claude"] },
|
|
@@ -5988,6 +6711,7 @@ async function resolveRuntimeTarget(request = {}) {
|
|
|
5988
6711
|
const targetRoot = resolve15(request.targetRoot);
|
|
5989
6712
|
return {
|
|
5990
6713
|
adapter: request.adapter ?? "openclaw",
|
|
6714
|
+
installationType: request.installationType,
|
|
5991
6715
|
targetRoot,
|
|
5992
6716
|
workspaceRoot: targetRoot,
|
|
5993
6717
|
transport: "local",
|
|
@@ -5997,14 +6721,15 @@ async function resolveRuntimeTarget(request = {}) {
|
|
|
5997
6721
|
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
5998
6722
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
5999
6723
|
if (request.agent) {
|
|
6000
|
-
return targetFromAgent(request.agent, config, workspaceRoot);
|
|
6724
|
+
return targetFromAgent(request.agent, config, workspaceRoot, request.installationType);
|
|
6001
6725
|
}
|
|
6002
6726
|
const detected = await detectRuntimeTarget(cwd, request.adapter);
|
|
6003
6727
|
if (detected) {
|
|
6004
|
-
return { ...detected, workspaceRoot: await findWorkspaceRoot(detected.targetRoot), transport: "local", source: "auto-detect" };
|
|
6728
|
+
return { ...detected, installationType: request.installationType, workspaceRoot: await findWorkspaceRoot(detected.targetRoot), transport: "local", source: "auto-detect" };
|
|
6005
6729
|
}
|
|
6006
6730
|
return {
|
|
6007
6731
|
adapter: request.adapter ?? "openclaw",
|
|
6732
|
+
installationType: request.installationType,
|
|
6008
6733
|
targetRoot: cwd,
|
|
6009
6734
|
workspaceRoot,
|
|
6010
6735
|
transport: "local",
|
|
@@ -6017,12 +6742,44 @@ async function resolveAllRuntimeTargets(request = {}) {
|
|
|
6017
6742
|
const cwd = resolve15(request.cwd ?? process.cwd());
|
|
6018
6743
|
const workspaceRoot = await findWorkspaceRoot(cwd);
|
|
6019
6744
|
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
6020
|
-
const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot));
|
|
6745
|
+
const targets = Object.entries(config.agents).map(([name]) => targetFromAgent(name, config, workspaceRoot, request.installationType));
|
|
6021
6746
|
if (targets.length === 0) {
|
|
6022
6747
|
throw new Error("No agents configured. Add agents to .agentwheel/config.json or pass --target-root.");
|
|
6023
6748
|
}
|
|
6024
6749
|
return targets;
|
|
6025
6750
|
}
|
|
6751
|
+
async function resolveProfileRuntimeTargets(request) {
|
|
6752
|
+
const cwd = resolve15(request.cwd ?? process.cwd());
|
|
6753
|
+
const workspaceRoot = request.targetRoot ? resolve15(request.targetRoot) : await findWorkspaceRoot(cwd);
|
|
6754
|
+
const config = await readMergedWorkspaceConfig(workspaceRoot, { globalRoot: request.globalRoot });
|
|
6755
|
+
const profile = config.profiles[request.profile];
|
|
6756
|
+
if (!profile) {
|
|
6757
|
+
throw new Error(`Unknown profile: ${request.profile}`);
|
|
6758
|
+
}
|
|
6759
|
+
return profile.runtimes.map((runtime) => resolveProfileRuntimeTarget(runtime, config, workspaceRoot, request.installationType));
|
|
6760
|
+
}
|
|
6761
|
+
function resolveProfileRuntimeTarget(runtime, config, workspaceRoot, installationType) {
|
|
6762
|
+
if (runtime.agent) {
|
|
6763
|
+
const target = targetFromAgent(runtime.agent, config, workspaceRoot, installationType ?? runtime.installationType);
|
|
6764
|
+
return {
|
|
6765
|
+
...target,
|
|
6766
|
+
adapterConfig: runtime.adapterConfig,
|
|
6767
|
+
adapterModule: runtime.adapterModule,
|
|
6768
|
+
targetKey: runtime.agent,
|
|
6769
|
+
source: "profile"
|
|
6770
|
+
};
|
|
6771
|
+
}
|
|
6772
|
+
return {
|
|
6773
|
+
adapter: runtime.adapter,
|
|
6774
|
+
adapterConfig: runtime.adapterConfig,
|
|
6775
|
+
adapterModule: runtime.adapterModule,
|
|
6776
|
+
installationType: installationType ?? runtime.installationType,
|
|
6777
|
+
targetRoot: runtime.targetRoot ? resolveConfigPath(runtime.targetRoot, workspaceRoot) : workspaceRoot,
|
|
6778
|
+
workspaceRoot,
|
|
6779
|
+
transport: "local",
|
|
6780
|
+
source: "profile"
|
|
6781
|
+
};
|
|
6782
|
+
}
|
|
6026
6783
|
async function resolveAllDetectedRuntimeTargets(request = {}) {
|
|
6027
6784
|
if (request.agent) return [await resolveRuntimeTarget(request)];
|
|
6028
6785
|
const scanRoot = runtimeScanRoot(request);
|
|
@@ -6032,6 +6789,7 @@ async function resolveAllDetectedRuntimeTargets(request = {}) {
|
|
|
6032
6789
|
}
|
|
6033
6790
|
return Promise.all(matches.map(async (match) => ({
|
|
6034
6791
|
...match,
|
|
6792
|
+
installationType: request.installationType,
|
|
6035
6793
|
workspaceRoot: await findWorkspaceRoot(match.targetRoot),
|
|
6036
6794
|
transport: "local",
|
|
6037
6795
|
source: "auto-detect"
|
|
@@ -6050,23 +6808,25 @@ async function detectRuntimeTargets(cwd = process.cwd(), adapterFilter) {
|
|
|
6050
6808
|
for (const marker of runtimeMarkers) {
|
|
6051
6809
|
if (adapterFilter && marker.adapter !== adapterFilter) continue;
|
|
6052
6810
|
for (const dir of marker.dirs) {
|
|
6053
|
-
if (
|
|
6054
|
-
matches.push({ adapter: marker.adapter, targetRoot:
|
|
6055
|
-
} else if (await pathExists(
|
|
6811
|
+
if (basename15(root) === dir) {
|
|
6812
|
+
matches.push({ adapter: marker.adapter, targetRoot: dirname20(root) });
|
|
6813
|
+
} else if (await pathExists(join26(root, dir))) {
|
|
6056
6814
|
matches.push({ adapter: marker.adapter, targetRoot: root });
|
|
6057
6815
|
}
|
|
6058
6816
|
}
|
|
6059
6817
|
}
|
|
6060
6818
|
return dedupeTargets(matches);
|
|
6061
6819
|
}
|
|
6062
|
-
function targetFromAgent(name, config, workspaceRoot) {
|
|
6820
|
+
function targetFromAgent(name, config, workspaceRoot, installationType) {
|
|
6063
6821
|
const agent = config.agents[name];
|
|
6064
6822
|
if (!agent) {
|
|
6065
6823
|
throw new Error(`Unknown agent: ${name}`);
|
|
6066
6824
|
}
|
|
6067
6825
|
return {
|
|
6068
6826
|
agentName: name,
|
|
6827
|
+
targetKey: name,
|
|
6069
6828
|
adapter: agent.adapter,
|
|
6829
|
+
installationType: installationType ?? agent.installationType,
|
|
6070
6830
|
targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
|
|
6071
6831
|
workspaceRoot,
|
|
6072
6832
|
transport: agent.transport,
|
|
@@ -6089,13 +6849,119 @@ function dedupeTargets(matches) {
|
|
|
6089
6849
|
function runtimeScanRoot(request) {
|
|
6090
6850
|
const root = resolve15(request.targetRoot ?? request.cwd ?? process.cwd());
|
|
6091
6851
|
if (request.targetRoot) return root;
|
|
6092
|
-
return runtimeMarkers.some((marker) => marker.dirs.includes(
|
|
6852
|
+
return runtimeMarkers.some((marker) => marker.dirs.includes(basename15(root))) ? dirname20(root) : root;
|
|
6853
|
+
}
|
|
6854
|
+
|
|
6855
|
+
// src/lifecycle/profile.ts
|
|
6856
|
+
async function syncProfile(options) {
|
|
6857
|
+
const config = await readMergedWorkspaceConfig(options.workspaceRoot);
|
|
6858
|
+
const profile = config.profiles[options.profile];
|
|
6859
|
+
if (!profile) {
|
|
6860
|
+
throw new Error(`Unknown profile: ${options.profile}`);
|
|
6861
|
+
}
|
|
6862
|
+
const packages = options.source ? [await packageFromSource(options.source, options)] : config.packages;
|
|
6863
|
+
if (packages.length === 0) {
|
|
6864
|
+
throw new Error("Profile sync needs a source argument or configured packages.");
|
|
6865
|
+
}
|
|
6866
|
+
const results = [];
|
|
6867
|
+
for (const runtime of profile.runtimes) {
|
|
6868
|
+
const target = resolveProfileRuntimeTarget(runtime, config, options.workspaceRoot, options.installationType);
|
|
6869
|
+
const transport = transportForTarget(target);
|
|
6870
|
+
const adapter = await resolveAdapter({
|
|
6871
|
+
adapter: target.adapter,
|
|
6872
|
+
adapterConfig: target.adapterConfig,
|
|
6873
|
+
adapterModule: target.adapterModule,
|
|
6874
|
+
allowAdapterCode: options.allowAdapterCode,
|
|
6875
|
+
baseDir: options.workspaceRoot,
|
|
6876
|
+
warn: options.warn
|
|
6877
|
+
});
|
|
6878
|
+
const installationType = target.installationType ?? defaultInstallationType;
|
|
6879
|
+
resolveInstallationTypeForAdapter(adapter, installationType);
|
|
6880
|
+
const selected = normalizeArtifactSelectors(options.select, options.skills);
|
|
6881
|
+
const graphPlan = await createGraphSourcePlan({
|
|
6882
|
+
roots: packages.map((pkg) => ({
|
|
6883
|
+
rootId: pkg.name,
|
|
6884
|
+
source: pkg.source,
|
|
6885
|
+
mode: options.mode ?? pkg.mode,
|
|
6886
|
+
ref: pkg.requestedRef,
|
|
6887
|
+
select: selected ?? normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
6888
|
+
aliases: pkg.aliases,
|
|
6889
|
+
overrides: pkg.overrides
|
|
6890
|
+
})),
|
|
6891
|
+
targetRoot: target.targetRoot,
|
|
6892
|
+
workspaceRoot: options.workspaceRoot,
|
|
6893
|
+
adapter,
|
|
6894
|
+
transport,
|
|
6895
|
+
targetKey: target.targetKey ?? target.agentName ?? adapter.name,
|
|
6896
|
+
targetFingerprintParts: {
|
|
6897
|
+
adapter: adapter.name,
|
|
6898
|
+
installationType,
|
|
6899
|
+
adapterConfig: target.adapterConfig,
|
|
6900
|
+
adapterModule: target.adapterModule,
|
|
6901
|
+
adapterCodeHash: adapter.programmatic?.hash,
|
|
6902
|
+
agentName: target.agentName,
|
|
6903
|
+
targetRoot: target.targetRoot,
|
|
6904
|
+
transport: transport.kind,
|
|
6905
|
+
ssh: target.ssh
|
|
6906
|
+
},
|
|
6907
|
+
installationType,
|
|
6908
|
+
noDeps: options.noDeps,
|
|
6909
|
+
lockedResolution: options.lockedResolution,
|
|
6910
|
+
frozenLock: options.frozenLock,
|
|
6911
|
+
offline: options.offline,
|
|
6912
|
+
yes: options.yes,
|
|
6913
|
+
trustPatterns: options.trustPatterns ?? [],
|
|
6914
|
+
readOnly: options.readOnly,
|
|
6915
|
+
isTTY: options.isTTY,
|
|
6916
|
+
warn: options.warn,
|
|
6917
|
+
forceDrift: options.forceDrift,
|
|
6918
|
+
forceConflict: options.forceConflict,
|
|
6919
|
+
replaceConflict: options.replaceConflict
|
|
6920
|
+
});
|
|
6921
|
+
try {
|
|
6922
|
+
results.push({
|
|
6923
|
+
runtime: adapter.name,
|
|
6924
|
+
targetRoot: installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, transport.kind === "ssh"),
|
|
6925
|
+
transport: transport.kind,
|
|
6926
|
+
packageName: packages.map((pkg) => pkg.name).join(","),
|
|
6927
|
+
plan: graphPlan.plan
|
|
6928
|
+
});
|
|
6929
|
+
if (!options.dryRun) {
|
|
6930
|
+
await applyCombinedInstallPlan(graphPlan.plan, {
|
|
6931
|
+
executePlugins: runtime.executePlugins ?? options.executePlugins,
|
|
6932
|
+
transport,
|
|
6933
|
+
graphLockDigest: graphPlan.graphLockDigest,
|
|
6934
|
+
graphLock: { path: graphPlan.graphLockPath, lock: graphPlan.bundle.graphLock }
|
|
6935
|
+
});
|
|
6936
|
+
}
|
|
6937
|
+
} finally {
|
|
6938
|
+
await rm8(graphPlan.bundle.root, { recursive: true, force: true });
|
|
6939
|
+
}
|
|
6940
|
+
}
|
|
6941
|
+
return results;
|
|
6942
|
+
}
|
|
6943
|
+
async function packageFromSource(source, options) {
|
|
6944
|
+
const resolved = await resolvePackageSource(source, options.workspaceRoot, {
|
|
6945
|
+
offline: options.frozenLock === true || options.offline === true,
|
|
6946
|
+
warn: options.warn
|
|
6947
|
+
});
|
|
6948
|
+
const driver = options.driver ?? inferSourceDriverName(resolved.source);
|
|
6949
|
+
return {
|
|
6950
|
+
name: resolved.registryEntry?.name ?? source,
|
|
6951
|
+
source: resolved.source,
|
|
6952
|
+
driver,
|
|
6953
|
+
adapter: "openclaw",
|
|
6954
|
+
installationType: options.installationType,
|
|
6955
|
+
mode: options.mode ?? "pinned",
|
|
6956
|
+
select: options.select,
|
|
6957
|
+
skills: options.skills
|
|
6958
|
+
};
|
|
6093
6959
|
}
|
|
6094
6960
|
|
|
6095
6961
|
// src/cli/update-check.ts
|
|
6096
|
-
import { mkdir as
|
|
6097
|
-
import { homedir as
|
|
6098
|
-
import { dirname as
|
|
6962
|
+
import { mkdir as mkdir15, readFile as readFile19, writeFile as writeFile12 } from "fs/promises";
|
|
6963
|
+
import { homedir as homedir8 } from "os";
|
|
6964
|
+
import { dirname as dirname21, join as join27 } from "path";
|
|
6099
6965
|
var DEFAULT_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
6100
6966
|
var DEFAULT_TIMEOUT_MS = 300;
|
|
6101
6967
|
var REGISTRY_URL = "https://registry.npmjs.org/agentwheel";
|
|
@@ -6103,7 +6969,7 @@ async function maybeCheckForUpdate(options) {
|
|
|
6103
6969
|
if (isDisabled(options)) return;
|
|
6104
6970
|
const now = options.now?.() ?? /* @__PURE__ */ new Date();
|
|
6105
6971
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
6106
|
-
const cachePath = options.cachePath ??
|
|
6972
|
+
const cachePath = options.cachePath ?? join27(homedir8(), ".agentwheel", "update-check.json");
|
|
6107
6973
|
try {
|
|
6108
6974
|
const cached = await readCache(cachePath);
|
|
6109
6975
|
if (cached && now.getTime() - Date.parse(cached.checkedAt) < ttlMs) {
|
|
@@ -6140,7 +7006,7 @@ async function fetchLatestVersion(fetchImpl, timeoutMs) {
|
|
|
6140
7006
|
}
|
|
6141
7007
|
async function readCache(path) {
|
|
6142
7008
|
try {
|
|
6143
|
-
const parsed = JSON.parse(await
|
|
7009
|
+
const parsed = JSON.parse(await readFile19(path, "utf8"));
|
|
6144
7010
|
if (typeof parsed.checkedAt !== "string" || typeof parsed.latest !== "string") return void 0;
|
|
6145
7011
|
return { checkedAt: parsed.checkedAt, latest: parsed.latest };
|
|
6146
7012
|
} catch {
|
|
@@ -6148,8 +7014,8 @@ async function readCache(path) {
|
|
|
6148
7014
|
}
|
|
6149
7015
|
}
|
|
6150
7016
|
async function writeCache(path, cache) {
|
|
6151
|
-
await
|
|
6152
|
-
await
|
|
7017
|
+
await mkdir15(dirname21(path), { recursive: true });
|
|
7018
|
+
await writeFile12(path, `${JSON.stringify(cache, null, 2)}
|
|
6153
7019
|
`, "utf8");
|
|
6154
7020
|
}
|
|
6155
7021
|
function warnIfNewer(latest, current, stderr = process.stderr) {
|
|
@@ -6295,13 +7161,13 @@ function isCrossPackageSelector(value) {
|
|
|
6295
7161
|
}
|
|
6296
7162
|
|
|
6297
7163
|
// src/model/package-migrate.ts
|
|
6298
|
-
import { readFile as
|
|
6299
|
-
import { join as
|
|
7164
|
+
import { readFile as readFile20, rename as rename4, writeFile as writeFile13 } from "fs/promises";
|
|
7165
|
+
import { join as join29, resolve as resolve17 } from "path";
|
|
6300
7166
|
import { applyEdits, modify, parse as parse3 } from "jsonc-parser";
|
|
6301
7167
|
async function migratePackageManifest(root) {
|
|
6302
7168
|
const packageRoot = resolve17(root);
|
|
6303
7169
|
for (const name of openPackManifestNames) {
|
|
6304
|
-
const path =
|
|
7170
|
+
const path = join29(packageRoot, name);
|
|
6305
7171
|
if (await pathExists(path)) {
|
|
6306
7172
|
return { changed: false, to: path, message: `Package already uses ${name}.` };
|
|
6307
7173
|
}
|
|
@@ -6310,18 +7176,18 @@ async function migratePackageManifest(root) {
|
|
|
6310
7176
|
if (!legacyName) {
|
|
6311
7177
|
throw new Error(`No legacy package manifest found at ${packageRoot}`);
|
|
6312
7178
|
}
|
|
6313
|
-
const from =
|
|
7179
|
+
const from = join29(packageRoot, legacyName);
|
|
6314
7180
|
const toName = legacyName.endsWith(".jsonc") ? "openpack.jsonc" : "openpack.json";
|
|
6315
|
-
const to =
|
|
6316
|
-
const content = await
|
|
7181
|
+
const to = join29(packageRoot, toName);
|
|
7182
|
+
const content = await readFile20(from, "utf8");
|
|
6317
7183
|
const updated = updateSchemaVersion(content);
|
|
6318
7184
|
await rename4(from, to);
|
|
6319
|
-
await
|
|
7185
|
+
await writeFile13(to, updated, "utf8");
|
|
6320
7186
|
return { changed: true, from, to, message: `Migrated ${legacyName} to ${toName}.` };
|
|
6321
7187
|
}
|
|
6322
7188
|
async function firstExistingLegacyManifest(root) {
|
|
6323
7189
|
for (const name of legacyPackageManifestNames) {
|
|
6324
|
-
if (await pathExists(
|
|
7190
|
+
if (await pathExists(join29(root, name))) return name;
|
|
6325
7191
|
}
|
|
6326
7192
|
return void 0;
|
|
6327
7193
|
}
|
|
@@ -6339,20 +7205,20 @@ function updateSchemaVersion(content) {
|
|
|
6339
7205
|
|
|
6340
7206
|
// src/cli/version.ts
|
|
6341
7207
|
import { readFileSync } from "fs";
|
|
6342
|
-
import { dirname as
|
|
7208
|
+
import { dirname as dirname22, join as join30 } from "path";
|
|
6343
7209
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6344
7210
|
var FALLBACK_VERSION = "0.0.0";
|
|
6345
7211
|
function resolveCliVersion() {
|
|
6346
|
-
let dir =
|
|
7212
|
+
let dir = dirname22(fileURLToPath2(import.meta.url));
|
|
6347
7213
|
while (true) {
|
|
6348
7214
|
try {
|
|
6349
|
-
const pkg = JSON.parse(readFileSync(
|
|
7215
|
+
const pkg = JSON.parse(readFileSync(join30(dir, "package.json"), "utf8"));
|
|
6350
7216
|
if (pkg.name === "agentwheel" && typeof pkg.version === "string") {
|
|
6351
7217
|
return pkg.version;
|
|
6352
7218
|
}
|
|
6353
7219
|
} catch {
|
|
6354
7220
|
}
|
|
6355
|
-
const parent =
|
|
7221
|
+
const parent = dirname22(dir);
|
|
6356
7222
|
if (parent === dir) return FALLBACK_VERSION;
|
|
6357
7223
|
dir = parent;
|
|
6358
7224
|
}
|
|
@@ -6368,7 +7234,7 @@ Core flow:
|
|
|
6368
7234
|
$ agentwheel plan
|
|
6369
7235
|
$ agentwheel install
|
|
6370
7236
|
`);
|
|
6371
|
-
program.command("init").description("initialize an agentwheel workspace or package").argument("[kind]", "workspace or package", "workspace").option("--target-root <path>", "workspace root", process.cwd()).option("--fleet-example", "scaffold example agents and profiles in workspace config", false).action(async (kind, options) => {
|
|
7237
|
+
program.command("init").description("initialize an agentwheel workspace or package").argument("[kind]", "workspace or package", "workspace").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--fleet-example", "scaffold example agents and profiles in workspace config", false).action(async (kind, options) => {
|
|
6372
7238
|
const root = normalizeTargetRoot(options.targetRoot);
|
|
6373
7239
|
if (kind === "package") {
|
|
6374
7240
|
await initPackage(root);
|
|
@@ -6386,28 +7252,29 @@ program.command("init").description("initialize an agentwheel workspace or packa
|
|
|
6386
7252
|
if (bootstrapPackage) console.log("Auto-added the agentwheel bootstrap skill for openclaw.");
|
|
6387
7253
|
console.log(nextInstallNudge());
|
|
6388
7254
|
});
|
|
6389
|
-
program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "workspace root"
|
|
6390
|
-
const
|
|
6391
|
-
const
|
|
7255
|
+
program.command("add").description("add a package to .agentwheel/config.json without touching runtimes").argument("<source>", "package source").option("--driver <driver>", "source driver (local, git, skillkit, or vercel-skills)").option("--adapter <adapter>", "built-in adapter", "openclaw").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "workspace root").option("--mode <mode>", "pinned or tracking", "pinned").option("--name <name>", "package alias").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "allow this package to replace a colliding artifact (repeatable)", collectOverrideOption, []).action(async (source, options) => {
|
|
7256
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7257
|
+
const targetRoot = normalizeTargetRoot(normalizedOptions.targetRoot ?? process.cwd());
|
|
7258
|
+
const entry = await packageEntryFromSource(source, targetRoot, normalizedOptions);
|
|
6392
7259
|
await writeWorkspaceConfig(targetRoot, upsertPackage(await readWorkspaceConfig(targetRoot), entry));
|
|
6393
7260
|
console.log(`Added ${entry.name}. Preview: agentwheel plan - Apply: agentwheel install`);
|
|
6394
7261
|
});
|
|
6395
|
-
program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
|
|
7262
|
+
program.command("list").description("list artifacts exposed by a package source").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).action(async (source, options) => {
|
|
6396
7263
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6397
7264
|
const selectedArtifacts = selectedArtifactsFromOptions(options);
|
|
6398
7265
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6399
7266
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
6400
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
7267
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
|
|
6401
7268
|
const artifacts = filterArtifactsBySelection(await driver.list(resolved), selectedArtifacts);
|
|
6402
7269
|
for (const artifact of artifacts) {
|
|
6403
7270
|
console.log(`${artifact.type} ${artifact.name} ${artifact.relativePath}`);
|
|
6404
7271
|
}
|
|
6405
7272
|
});
|
|
6406
|
-
program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("--target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
7273
|
+
program.command("scan").description("scan a package source for validation findings").argument("<source>", "package source").option("--driver <driver>", "source driver").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (source, options) => {
|
|
6407
7274
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6408
7275
|
const resolvedInput = await resolvePackageSource(source, targetRoot);
|
|
6409
7276
|
const driver = getSourceDriver(options.driver ?? inferSourceDriverName(resolvedInput.source));
|
|
6410
|
-
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot:
|
|
7277
|
+
const resolved = await driver.export(await driver.translate(await driver.fetch(await driver.resolve(resolvedInput.source, { cacheRoot: join31(targetRoot, ".agentwheel", "cache") }))));
|
|
6411
7278
|
const result = await driver.scan(resolved);
|
|
6412
7279
|
if (result.findings.length === 0) {
|
|
6413
7280
|
console.log("Scan ok: no findings");
|
|
@@ -6418,28 +7285,30 @@ program.command("scan").description("scan a package source for validation findin
|
|
|
6418
7285
|
}
|
|
6419
7286
|
if (!result.ok) process.exitCode = 1;
|
|
6420
7287
|
});
|
|
6421
|
-
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
7288
|
+
program.command("plan").description("preview what install would reconcile without writing").argument("[name-or-source]", "configured package name/source or package source to preview").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "for source previews, allow the source to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--dry-run", "accepted for symmetry; plan never writes", false).option("--force-drift", "replace drifted managed artifacts during install planning", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6422
7289
|
await runInstallCommand(source, { ...options, dryRun: true }, { apply: false });
|
|
6423
7290
|
});
|
|
6424
|
-
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
|
|
7291
|
+
program.command("install").description("install configured packages into runtime targets").argument("[name-or-source]", "configured package name/source or package source to add and install").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).addHelpText("after", "\nScoped install never removes files owned only by other configured packages; run a full install to reconcile those removals.\n").action(async (source, options) => {
|
|
6425
7292
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6426
7293
|
});
|
|
6427
|
-
program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
7294
|
+
program.command("sync", { hidden: true }).argument("[name-or-source]", "configured package name/source or package source").option("--driver <driver>", "source driver").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--all-detected", "run for every runtime directory detected in the target root", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--override <source-or-package::type/name>", "when adding a source, allow it to replace a colliding artifact (repeatable)", collectOverrideOption, []).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plan without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--only-source", "with a source argument, exclude configured workspace packages", false).option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6428
7295
|
console.error("warning: 'agentwheel sync' is deprecated and will be removed in 0.10. Use 'agentwheel install'.");
|
|
6429
7296
|
await runInstallCommand(source, options, { apply: !options.dryRun });
|
|
6430
7297
|
});
|
|
6431
|
-
program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter").option("--target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show plans without writing", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
|
|
6432
|
-
const
|
|
7298
|
+
program.command("update").description("re-resolve tracking packages, then apply the result").argument("[name]", "configured package name or source to update").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "workspace root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").option("--dry-run", "show plans without writing", false).option("--force-drift", "replace drifted managed artifacts", false).option("--force-conflict", "adopt unmanaged destinations when their content already matches the desired artifact", false).option("--replace-conflict", "replace unmanaged destinations even when their content differs", false).option("--execute-plugins", "execute semantic plugin installs", false).option("--allow-adapter-code", "allow loading local adapter code from configured packages", false).option("--select <type/name>", "temporarily select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "temporarily select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (name, options) => {
|
|
7299
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7300
|
+
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
6433
7301
|
for (const target of targets) {
|
|
6434
|
-
await runConfiguredGraphPackages(target, { ...
|
|
7302
|
+
await runConfiguredGraphPackages(target, { ...normalizedOptions, scope: name }, { mode: "update" });
|
|
6435
7303
|
}
|
|
6436
7304
|
});
|
|
6437
7305
|
program.command("deps").description("inspect the OpenPack dependency graph").addCommand(
|
|
6438
|
-
new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
6439
|
-
const
|
|
7306
|
+
new Command("tree").description("print the OpenPack dependency graph").argument("[source]", "optional package source to resolve").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--mode <mode>", "pinned or tracking").option("--select <type/name>", "select an artifact by type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "select a skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--no-deps", "resolve only root sources and ignore requires with a warning").option("--frozen-lock", "resolve strictly from the existing graph lock and cached sources", false).option("--offline", "resolve strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (source, options) => {
|
|
7307
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(source, options) });
|
|
7308
|
+
const targets = await resolveCliTargets(normalizedOptions);
|
|
6440
7309
|
for (const target of targets) {
|
|
6441
7310
|
if (source) {
|
|
6442
|
-
for (const result of await buildGraphPlansForTarget(target, source,
|
|
7311
|
+
for (const result of await buildGraphPlansForTarget(target, source, normalizedOptions, { mode: "install" })) {
|
|
6443
7312
|
console.log(formatDependencyTree(result.graph).join("\n"));
|
|
6444
7313
|
for (const decision of result.bundle.graphLock.canonical.namespacing) {
|
|
6445
7314
|
console.log(`NAMESPACE ${decision.graphNodeId}:${decision.type}/${decision.name} -> ${decision.type}/${decision.installName} (${decision.reason})`);
|
|
@@ -6451,39 +7320,42 @@ program.command("deps").description("inspect the OpenPack dependency graph").add
|
|
|
6451
7320
|
}
|
|
6452
7321
|
continue;
|
|
6453
7322
|
}
|
|
6454
|
-
const { lock } = await readTargetGraphLock(target,
|
|
7323
|
+
const { lock } = await readTargetGraphLock(target, normalizedOptions);
|
|
6455
7324
|
console.log(formatLockDependencyTree(lock));
|
|
6456
7325
|
}
|
|
6457
7326
|
})
|
|
6458
7327
|
).addCommand(
|
|
6459
|
-
new Command("why").description("explain why an artifact is installed").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (selector, options) => {
|
|
6460
|
-
const
|
|
7328
|
+
new Command("why").description("explain why an artifact is installed").argument("<selector>", "installed path, type/installName, or graphNodeId:type/name").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (selector, options) => {
|
|
7329
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7330
|
+
const targets = await resolveCliTargets(normalizedOptions);
|
|
6461
7331
|
for (const target of targets) {
|
|
6462
|
-
const { lock, adapter } = await readTargetGraphLock(target,
|
|
6463
|
-
const
|
|
7332
|
+
const { lock, adapter } = await readTargetGraphLock(target, normalizedOptions);
|
|
7333
|
+
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
7334
|
+
const state = installStateForTarget(target, adapter, normalizedOptions, installationType);
|
|
7335
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transportForTarget(target), state);
|
|
6464
7336
|
console.log(formatDepsWhy(lock, manifest, selector));
|
|
6465
7337
|
}
|
|
6466
7338
|
})
|
|
6467
7339
|
);
|
|
6468
7340
|
program.command("registry").description("manage optional registry indexes").addCommand(
|
|
6469
|
-
new Command("update").description("refresh the local registry cache").option("--target-root <path>", "workspace root", process.cwd()).action(async (options) => {
|
|
7341
|
+
new Command("update").description("refresh the local registry cache").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (options) => {
|
|
6470
7342
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
6471
7343
|
const index = await client.getIndex({ refresh: true });
|
|
6472
7344
|
console.log(`Registry refreshed: ${index.entries.length} entries from ${index.sources.join(", ")}`);
|
|
6473
7345
|
})
|
|
6474
7346
|
).addCommand(
|
|
6475
|
-
new Command("list").description("list available registry entries").option("--target-root <path>", "workspace root", process.cwd()).action(async (options) => {
|
|
7347
|
+
new Command("list").description("list available registry entries").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (options) => {
|
|
6476
7348
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
6477
7349
|
printRegistryEntries((await client.getIndex()).entries);
|
|
6478
7350
|
})
|
|
6479
7351
|
).addCommand(
|
|
6480
|
-
new Command("search").description("search registry entries").argument("<query>", "search query").option("--target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
|
|
7352
|
+
new Command("search").description("search registry entries").argument("<query>", "search query").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (query, options) => {
|
|
6481
7353
|
const client = new RegistryClient({ workspaceRoot: normalizeTargetRoot(options.targetRoot), warn: (message) => console.warn(message) });
|
|
6482
7354
|
printRegistryEntries(await client.search(query));
|
|
6483
7355
|
})
|
|
6484
7356
|
);
|
|
6485
7357
|
program.command("trust").description("manage persisted source trust decisions").addCommand(
|
|
6486
|
-
new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("--target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
7358
|
+
new Command("forget").description("forget a persisted trusted source pattern").argument("<pattern>", "trusted source glob to revoke").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (pattern, options) => {
|
|
6487
7359
|
const removed = await forgetTrustedSources(normalizeTargetRoot(options.targetRoot), pattern);
|
|
6488
7360
|
if (removed.length === 0) {
|
|
6489
7361
|
console.log(`No persisted trust matched ${pattern}.`);
|
|
@@ -6510,114 +7382,128 @@ program.command("package").description("validate and migrate OpenPack packages")
|
|
|
6510
7382
|
console.log(result.message);
|
|
6511
7383
|
})
|
|
6512
7384
|
);
|
|
6513
|
-
program.command("remember").description("append text to the local instructions overlay").requiredOption("--runtime <runtime>", "runtime/adapter name").option("--target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
|
|
7385
|
+
program.command("remember").description("append text to the local instructions overlay").requiredOption("--runtime <runtime>", "runtime/adapter name").option("-t, --target-root <path>", "workspace root", process.cwd()).argument("<text>", "text to append to the local instructions overlay").action(async (text, options) => {
|
|
6514
7386
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6515
7387
|
const result = await remember(targetRoot, options.runtime, text);
|
|
6516
7388
|
console.log(`Remembered in ${result.overlayPath}.`);
|
|
6517
7389
|
console.log(nextInstallNudge());
|
|
6518
7390
|
});
|
|
6519
|
-
program.command("eject").description("copy a managed artifact into local ownership").argument("<item>", "package/type/name").option("--target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
|
|
7391
|
+
program.command("eject").description("copy a managed artifact into local ownership").argument("<item>", "package/type/name").option("-t, --target-root <path>", "workspace root", process.cwd()).action(async (item, options) => {
|
|
6520
7392
|
const targetRoot = normalizeTargetRoot(options.targetRoot);
|
|
6521
7393
|
const result = await ejectArtifact(targetRoot, item);
|
|
6522
7394
|
console.log(`Ejected ${item} to ${result.ejectedPath}.`);
|
|
6523
7395
|
console.log(nextInstallNudge());
|
|
6524
7396
|
});
|
|
6525
|
-
program.command("uninstall").description("remove configured packages or managed runtime files").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "adapter").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--keep-files", "remove from config and manifest but leave runtime files unmanaged", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
|
|
7397
|
+
program.command("uninstall").description("remove configured packages or managed runtime files").argument("[package]", "configured package name or source to remove from the ownership graph").option("--adapter <adapter>", "adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--dry-run", "show removals without writing", false).option("--force", "remove drifted managed files too", false).option("--keep-files", "remove from config and manifest but leave runtime files unmanaged", false).option("--select <type/name>", "uninstall only selected artifact type/name (repeatable or comma-separated)", collectSelectOption, []).option("--skill <name>", "uninstall only selected skill name (repeatable or comma-separated)", collectSkillOption, []).option("--frozen-lock", "resolve remaining packages strictly from the existing graph lock and cached sources", false).option("--offline", "resolve remaining packages strictly from graph locks and local caches", false).option("--yes", "trust all new transitive sources while resolving remaining packages", false).option("--trust <pattern>", "pre-approve a transitive source glob (repeatable)", collectTrustOption, []).action(async (packageName, options) => {
|
|
6526
7398
|
if (options.keepFiles && options.force) {
|
|
6527
7399
|
throw new Error("--keep-files cannot be combined with --force.");
|
|
6528
7400
|
}
|
|
6529
|
-
const
|
|
7401
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7402
|
+
const targets = await resolveCliTargets(normalizedOptions);
|
|
6530
7403
|
for (const target of targets) {
|
|
6531
7404
|
if (packageName) {
|
|
6532
|
-
await uninstallConfiguredPackage(target, packageName,
|
|
7405
|
+
await uninstallConfiguredPackage(target, packageName, normalizedOptions);
|
|
6533
7406
|
continue;
|
|
6534
7407
|
}
|
|
6535
|
-
if (
|
|
7408
|
+
if (normalizedOptions.keepFiles) {
|
|
6536
7409
|
throw new Error("--keep-files requires a configured package name or source.");
|
|
6537
7410
|
}
|
|
6538
|
-
const adapter = await resolveAdapterForTarget(target,
|
|
7411
|
+
const adapter = await resolveAdapterForTarget(target, normalizedOptions);
|
|
6539
7412
|
const transport = transportForTarget(target);
|
|
6540
|
-
const
|
|
7413
|
+
const installationType = normalizedOptions.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
7414
|
+
const state = installStateForTarget(target, adapter, normalizedOptions, installationType);
|
|
7415
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
|
|
6541
7416
|
if (!manifest) {
|
|
6542
|
-
console.log(`No install manifest for ${adapter.name} at ${
|
|
7417
|
+
console.log(`No install manifest for ${adapter.name}/${installationType} at ${state.installRoot}`);
|
|
6543
7418
|
continue;
|
|
6544
7419
|
}
|
|
6545
7420
|
const plan = filterUninstallPlanBySelection(await createUninstallPlan(manifest), selectedArtifactsFromOptions(options));
|
|
6546
7421
|
console.log(formatPlan(plan));
|
|
6547
|
-
const result = await uninstall(plan, { dryRun:
|
|
6548
|
-
if (!
|
|
7422
|
+
const result = await uninstall(plan, { dryRun: normalizedOptions.dryRun, force: normalizedOptions.force, transport });
|
|
7423
|
+
if (!normalizedOptions.dryRun) {
|
|
6549
7424
|
if (transport.kind !== "local" && adapter.programmatic?.uninstall) {
|
|
6550
7425
|
throw new Error(`Cannot execute programmatic adapter uninstall over ${transport.description}.`);
|
|
6551
7426
|
}
|
|
6552
|
-
await adapter.programmatic?.uninstall?.({ targetRoot:
|
|
7427
|
+
await adapter.programmatic?.uninstall?.({ targetRoot: state.installRoot, adapterName: adapter.name });
|
|
6553
7428
|
console.log(formatUninstallResult(result));
|
|
6554
7429
|
}
|
|
6555
7430
|
if (plan.hasBlockingChanges) process.exitCode = 1;
|
|
6556
7431
|
}
|
|
6557
7432
|
});
|
|
6558
|
-
program.command("status").description("show configured packages and runtime install state").option("--adapter <adapter>", "built-in adapter").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).action(async (options) => {
|
|
6559
|
-
const
|
|
7433
|
+
program.command("status").description("show configured packages and runtime install state").option("--adapter <adapter>", "built-in adapter or comma-separated adapters").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--all", "run for every configured agent", false).option("--profile <name>", "workspace runtime profile").action(async (options) => {
|
|
7434
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7435
|
+
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
6560
7436
|
for (const target of targets) {
|
|
6561
|
-
await printStatus(target,
|
|
7437
|
+
await printStatus(target, normalizedOptions);
|
|
6562
7438
|
}
|
|
6563
7439
|
});
|
|
6564
7440
|
async function runInstallCommand(nameOrSource, options, behavior) {
|
|
7441
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options, { defaultUser: shouldDefaultUserInstall(nameOrSource, options) });
|
|
6565
7442
|
if (options.profile) {
|
|
6566
|
-
const target = await resolveRuntimeTarget({
|
|
7443
|
+
const target = await resolveRuntimeTarget({
|
|
7444
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7445
|
+
adapter: normalizedOptions.adapter,
|
|
7446
|
+
installationType: normalizedOptions.installationType,
|
|
7447
|
+
agent: normalizedOptions.agent
|
|
7448
|
+
});
|
|
6567
7449
|
const results = await syncProfile({
|
|
6568
7450
|
workspaceRoot: target.workspaceRoot,
|
|
6569
7451
|
profile: options.profile,
|
|
6570
7452
|
source: nameOrSource,
|
|
6571
|
-
driver:
|
|
6572
|
-
mode:
|
|
6573
|
-
select: selectedArtifactsFromOptions(
|
|
7453
|
+
driver: normalizedOptions.driver,
|
|
7454
|
+
mode: normalizedOptions.mode,
|
|
7455
|
+
select: selectedArtifactsFromOptions(normalizedOptions),
|
|
7456
|
+
installationType: normalizedOptions.installationType,
|
|
6574
7457
|
dryRun: !behavior.apply,
|
|
6575
|
-
executePlugins:
|
|
6576
|
-
allowAdapterCode:
|
|
6577
|
-
|
|
7458
|
+
executePlugins: normalizedOptions.executePlugins,
|
|
7459
|
+
allowAdapterCode: normalizedOptions.allowAdapterCode,
|
|
7460
|
+
forceDrift: normalizedOptions.forceDrift,
|
|
7461
|
+
forceConflict: normalizedOptions.forceConflict,
|
|
7462
|
+
replaceConflict: normalizedOptions.replaceConflict,
|
|
7463
|
+
noDeps: noDepsFromOptions(normalizedOptions),
|
|
6578
7464
|
lockedResolution: true,
|
|
6579
|
-
frozenLock:
|
|
6580
|
-
offline:
|
|
6581
|
-
yes:
|
|
6582
|
-
trustPatterns:
|
|
7465
|
+
frozenLock: normalizedOptions.frozenLock,
|
|
7466
|
+
offline: normalizedOptions.offline,
|
|
7467
|
+
yes: normalizedOptions.yes,
|
|
7468
|
+
trustPatterns: normalizedOptions.trust ?? [],
|
|
6583
7469
|
readOnly: !behavior.apply,
|
|
6584
7470
|
isTTY: process.stdin.isTTY === true,
|
|
6585
7471
|
warn: (message) => console.warn(message)
|
|
6586
7472
|
});
|
|
6587
7473
|
for (const result of results) {
|
|
6588
|
-
console.log(`Profile ${
|
|
7474
|
+
console.log(`Profile ${normalizedOptions.profile} / ${result.runtime} / ${result.packageName} at ${result.targetRoot} (${result.transport}):`);
|
|
6589
7475
|
console.log(formatPlan(result.plan));
|
|
6590
7476
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6591
7477
|
}
|
|
6592
7478
|
if (behavior.apply) console.log("Applied.");
|
|
6593
7479
|
return;
|
|
6594
7480
|
}
|
|
6595
|
-
const targets = await resolveCliTargets(
|
|
7481
|
+
const targets = await resolveCliTargets(normalizedOptions);
|
|
6596
7482
|
for (const target of targets) {
|
|
7483
|
+
const targetOptions = optionsForResolvedTarget(normalizedOptions, target);
|
|
6597
7484
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6598
|
-
const configured = nameOrSource ?
|
|
7485
|
+
const configured = nameOrSource ? findConfiguredPackageForTarget(config.packages, nameOrSource, targetOptions, target) : void 0;
|
|
6599
7486
|
let source;
|
|
6600
7487
|
let scope = configured?.name;
|
|
6601
7488
|
let extraPackage;
|
|
6602
7489
|
if (nameOrSource && !configured) {
|
|
6603
7490
|
try {
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), entry));
|
|
6608
|
-
} else {
|
|
6609
|
-
source = nameOrSource;
|
|
6610
|
-
extraPackage = entry;
|
|
7491
|
+
let entry = await packageEntryFromSource(nameOrSource, target.workspaceRoot, { ...targetOptions, adapter: targetOptions.adapter ?? target.adapter });
|
|
7492
|
+
if (targetOptions.multiAdapterSource) {
|
|
7493
|
+
entry = packageEntryWithAdapterSuffix(entry);
|
|
6611
7494
|
}
|
|
7495
|
+
scope = entry.name;
|
|
7496
|
+
source = nameOrSource;
|
|
7497
|
+
extraPackage = entry;
|
|
6612
7498
|
} catch (error) {
|
|
6613
7499
|
throw teachingInstallError(nameOrSource, error);
|
|
6614
7500
|
}
|
|
6615
7501
|
}
|
|
6616
|
-
for (const result of await buildGraphPlansForTarget(target, source, { ...
|
|
7502
|
+
for (const result of await buildGraphPlansForTarget(target, source, { ...targetOptions, scope, extraPackage }, { mode: "install" })) {
|
|
6617
7503
|
console.log(formatGraphPlan(result));
|
|
6618
7504
|
if (behavior.apply) {
|
|
6619
7505
|
await applyCombinedInstallPlan(result.plan, {
|
|
6620
|
-
executePlugins:
|
|
7506
|
+
executePlugins: targetOptions.executePlugins,
|
|
6621
7507
|
transport: transportForTarget(target),
|
|
6622
7508
|
graphLockDigest: result.graphLockDigest,
|
|
6623
7509
|
graphLock: { path: result.graphLockPath, lock: result.bundle.graphLock }
|
|
@@ -6627,6 +7513,9 @@ async function runInstallCommand(nameOrSource, options, behavior) {
|
|
|
6627
7513
|
await rm9(result.bundle.root, { recursive: true, force: true });
|
|
6628
7514
|
if (result.plan.hasBlockingChanges) process.exitCode = 1;
|
|
6629
7515
|
}
|
|
7516
|
+
if (behavior.apply && extraPackage && !targetOptions.onlySource) {
|
|
7517
|
+
await writeWorkspaceConfig(target.workspaceRoot, upsertPackage(await readWorkspaceConfig(target.workspaceRoot), extraPackage));
|
|
7518
|
+
}
|
|
6630
7519
|
}
|
|
6631
7520
|
}
|
|
6632
7521
|
async function packageEntryFromSource(source, targetRoot, options) {
|
|
@@ -6647,17 +7536,19 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
6647
7536
|
const bundle = await stageSource(driver, resolvedSource, {
|
|
6648
7537
|
workspaceRoot: targetRoot,
|
|
6649
7538
|
adapter,
|
|
6650
|
-
cacheRoot:
|
|
7539
|
+
cacheRoot: join31(targetRoot, ".agentwheel", "cache"),
|
|
6651
7540
|
mode: options.mode,
|
|
6652
7541
|
frozenLock: lockMode,
|
|
6653
7542
|
select: selectedArtifacts
|
|
6654
7543
|
});
|
|
7544
|
+
const installationType = resolveInstallationTypeForArtifacts(adapter, bundle.artifacts.map((artifact) => artifact.type), options.installationType);
|
|
6655
7545
|
try {
|
|
6656
7546
|
return {
|
|
6657
7547
|
name: options.name ?? resolvedInput.registryEntry?.name ?? bundle.source.packageName ?? source,
|
|
6658
7548
|
source: resolvedSource,
|
|
6659
7549
|
driver: driverName,
|
|
6660
7550
|
adapter: adapter.name,
|
|
7551
|
+
installationType,
|
|
6661
7552
|
adapterConfig: options.adapterConfig,
|
|
6662
7553
|
adapterModule: options.adapterModule,
|
|
6663
7554
|
adapterCodeHash: adapter.programmatic?.hash,
|
|
@@ -6673,9 +7564,17 @@ async function packageEntryFromSource(source, targetRoot, options) {
|
|
|
6673
7564
|
function findConfiguredPackage(packages, value) {
|
|
6674
7565
|
return packages.find((pkg) => pkg.name === value || pkg.source === value);
|
|
6675
7566
|
}
|
|
7567
|
+
function findConfiguredPackageForTarget(packages, value, options, target) {
|
|
7568
|
+
const matches = packages.filter((pkg) => pkg.name === value || pkg.source === value);
|
|
7569
|
+
return options.multiAdapterSource ? matches.find((pkg) => pkg.adapter === target.adapter) : matches[0];
|
|
7570
|
+
}
|
|
6676
7571
|
function noDepsFromOptions(options) {
|
|
6677
7572
|
return options.noDeps === true || options.deps === false;
|
|
6678
7573
|
}
|
|
7574
|
+
function packageEntryWithAdapterSuffix(entry) {
|
|
7575
|
+
const suffix = `-${entry.adapter}`;
|
|
7576
|
+
return entry.name.endsWith(suffix) ? entry : { ...entry, name: `${entry.name}${suffix}` };
|
|
7577
|
+
}
|
|
6679
7578
|
function teachingInstallError(input, cause) {
|
|
6680
7579
|
const message = cause instanceof Error ? cause.message : String(cause);
|
|
6681
7580
|
return new Error(
|
|
@@ -6689,28 +7588,103 @@ Resolver error: ${message}`
|
|
|
6689
7588
|
function nextInstallNudge() {
|
|
6690
7589
|
return "Preview: agentwheel plan - Apply: agentwheel install";
|
|
6691
7590
|
}
|
|
6692
|
-
async function resolveCliTargets(options) {
|
|
6693
|
-
|
|
7591
|
+
async function resolveCliTargets(options, behavior = {}) {
|
|
7592
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
7593
|
+
if (normalizedOptions.all && normalizedOptions.allDetected) {
|
|
6694
7594
|
throw new Error("Choose either --all for configured agents or --all-detected for detected runtime directories.");
|
|
6695
7595
|
}
|
|
6696
|
-
if (
|
|
6697
|
-
|
|
7596
|
+
if (normalizedOptions.profile && (normalizedOptions.all || normalizedOptions.allDetected || normalizedOptions.agent)) {
|
|
7597
|
+
throw new Error("--profile cannot be combined with --all, --all-detected, or --agent.");
|
|
7598
|
+
}
|
|
7599
|
+
const adapters2 = adapterListFromOption(normalizedOptions.adapter);
|
|
7600
|
+
if (adapters2.length > 1) {
|
|
7601
|
+
if (normalizedOptions.all || normalizedOptions.allDetected || normalizedOptions.agent || normalizedOptions.profile) {
|
|
7602
|
+
throw new Error("--adapter <a,b> cannot be combined with --all, --all-detected, --agent, or --profile. Use a profile for mixed configured targets.");
|
|
7603
|
+
}
|
|
7604
|
+
const targets = [];
|
|
7605
|
+
for (const adapter of adapters2) {
|
|
7606
|
+
targets.push(await resolveRuntimeTarget({
|
|
7607
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7608
|
+
adapter,
|
|
7609
|
+
installationType: normalizedOptions.installationType
|
|
7610
|
+
}));
|
|
7611
|
+
}
|
|
7612
|
+
return targets;
|
|
7613
|
+
}
|
|
7614
|
+
if (normalizedOptions.profile) {
|
|
7615
|
+
return resolveProfileRuntimeTargets({
|
|
7616
|
+
cwd: process.cwd(),
|
|
7617
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7618
|
+
installationType: normalizedOptions.installationType,
|
|
7619
|
+
profile: normalizedOptions.profile
|
|
7620
|
+
});
|
|
6698
7621
|
}
|
|
6699
|
-
if (
|
|
6700
|
-
|
|
7622
|
+
if (normalizedOptions.all) {
|
|
7623
|
+
if (behavior.preferAllProfile && !normalizedOptions.agent) {
|
|
7624
|
+
const profileTargets = await tryResolveProfileAllRuntimeTargets(normalizedOptions);
|
|
7625
|
+
if (profileTargets) return profileTargets;
|
|
7626
|
+
}
|
|
7627
|
+
return resolveAllRuntimeTargets({
|
|
7628
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7629
|
+
adapter: normalizedOptions.adapter,
|
|
7630
|
+
installationType: normalizedOptions.installationType,
|
|
7631
|
+
agent: normalizedOptions.agent,
|
|
7632
|
+
all: normalizedOptions.all
|
|
7633
|
+
});
|
|
7634
|
+
}
|
|
7635
|
+
if (normalizedOptions.allDetected) {
|
|
7636
|
+
return resolveAllDetectedRuntimeTargets({
|
|
7637
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7638
|
+
adapter: normalizedOptions.adapter,
|
|
7639
|
+
installationType: normalizedOptions.installationType,
|
|
7640
|
+
agent: normalizedOptions.agent,
|
|
7641
|
+
allDetected: normalizedOptions.allDetected
|
|
7642
|
+
});
|
|
6701
7643
|
}
|
|
6702
|
-
return [await resolveRuntimeTarget({
|
|
7644
|
+
return [await resolveRuntimeTarget({
|
|
7645
|
+
targetRoot: normalizedOptions.targetRoot,
|
|
7646
|
+
adapter: normalizedOptions.adapter,
|
|
7647
|
+
installationType: normalizedOptions.installationType,
|
|
7648
|
+
agent: normalizedOptions.agent
|
|
7649
|
+
})];
|
|
7650
|
+
}
|
|
7651
|
+
async function tryResolveProfileAllRuntimeTargets(options) {
|
|
7652
|
+
try {
|
|
7653
|
+
return await resolveProfileRuntimeTargets({
|
|
7654
|
+
cwd: process.cwd(),
|
|
7655
|
+
targetRoot: options.targetRoot,
|
|
7656
|
+
installationType: options.installationType,
|
|
7657
|
+
profile: "all"
|
|
7658
|
+
});
|
|
7659
|
+
} catch (error) {
|
|
7660
|
+
if (error instanceof Error && error.message === "Unknown profile: all") return void 0;
|
|
7661
|
+
throw error;
|
|
7662
|
+
}
|
|
7663
|
+
}
|
|
7664
|
+
function optionsForResolvedTarget(options, target) {
|
|
7665
|
+
return adapterListFromOption(options.adapter).length > 1 ? { ...options, adapter: target.adapter, multiAdapterSource: true } : options;
|
|
6703
7666
|
}
|
|
6704
7667
|
async function resolveAdapterForTarget(target, options) {
|
|
7668
|
+
const adapterOptions = adapterOptionsForTarget(target, options);
|
|
6705
7669
|
return resolveAdapter({
|
|
6706
7670
|
adapter: target.adapter,
|
|
6707
|
-
adapterConfig:
|
|
6708
|
-
adapterModule:
|
|
6709
|
-
allowAdapterCode:
|
|
7671
|
+
adapterConfig: adapterOptions.adapterConfig,
|
|
7672
|
+
adapterModule: adapterOptions.adapterModule,
|
|
7673
|
+
allowAdapterCode: adapterOptions.allowAdapterCode,
|
|
6710
7674
|
baseDir: target.workspaceRoot,
|
|
6711
7675
|
warn: (message) => console.warn(message)
|
|
6712
7676
|
});
|
|
6713
7677
|
}
|
|
7678
|
+
function adapterOptionsForTarget(target, options) {
|
|
7679
|
+
return {
|
|
7680
|
+
adapterConfig: options.adapterConfig ?? target.adapterConfig,
|
|
7681
|
+
adapterModule: options.adapterModule ?? target.adapterModule,
|
|
7682
|
+
allowAdapterCode: options.allowAdapterCode
|
|
7683
|
+
};
|
|
7684
|
+
}
|
|
7685
|
+
function targetKeyForTarget(target, adapterName) {
|
|
7686
|
+
return target.targetKey ?? target.agentName ?? adapterName ?? target.source;
|
|
7687
|
+
}
|
|
6714
7688
|
async function runConfiguredGraphPackages(target, options, behavior) {
|
|
6715
7689
|
const results = await buildGraphPlansForTarget(target, void 0, options, behavior);
|
|
6716
7690
|
for (const result of results) {
|
|
@@ -6730,37 +7704,39 @@ async function runConfiguredGraphPackages(target, options, behavior) {
|
|
|
6730
7704
|
}
|
|
6731
7705
|
}
|
|
6732
7706
|
async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
7707
|
+
const targetOptions = optionsForResolvedTarget(options, target);
|
|
6733
7708
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
6734
7709
|
const groups = /* @__PURE__ */ new Map();
|
|
6735
|
-
const selectedArtifacts = selectedArtifactsFromOptions(
|
|
6736
|
-
const scopedPackage =
|
|
6737
|
-
const scopedRootId = scopedPackage?.name ?? (source ?
|
|
6738
|
-
if (
|
|
6739
|
-
if (!source || !
|
|
7710
|
+
const selectedArtifacts = selectedArtifactsFromOptions(targetOptions);
|
|
7711
|
+
const scopedPackage = targetOptions.scope ? findConfiguredPackage(config.packages, targetOptions.scope) : void 0;
|
|
7712
|
+
const scopedRootId = scopedPackage?.name ?? (source ? targetOptions.scope : void 0);
|
|
7713
|
+
if (targetOptions.scope && !scopedPackage && !source) throw new Error(`Configured package not found: ${targetOptions.scope}`);
|
|
7714
|
+
if (!source || !targetOptions.onlySource) {
|
|
6740
7715
|
for (const pkg of config.packages) {
|
|
6741
|
-
const group = graphGroupForPackage(groups, target, pkg,
|
|
7716
|
+
const group = graphGroupForPackage(groups, target, pkg, targetOptions);
|
|
6742
7717
|
group.packages.push(pkg);
|
|
6743
7718
|
}
|
|
6744
7719
|
}
|
|
6745
7720
|
if (source) {
|
|
7721
|
+
let entry = targetOptions.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, targetOptions);
|
|
7722
|
+
if (targetOptions.multiAdapterSource) {
|
|
7723
|
+
entry = packageEntryWithAdapterSuffix(entry);
|
|
7724
|
+
}
|
|
6746
7725
|
const sourceTarget = target;
|
|
7726
|
+
const sourceInstallationType = targetOptions.installationType ?? entry.installationType ?? sourceTarget.installationType ?? "local";
|
|
7727
|
+
const sourceAdapterOptions = adapterOptionsForTarget(sourceTarget, targetOptions);
|
|
6747
7728
|
const key = graphGroupKey(sourceTarget, {
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
allowAdapterCode: options.allowAdapterCode
|
|
7729
|
+
installationType: sourceInstallationType,
|
|
7730
|
+
...sourceAdapterOptions
|
|
6751
7731
|
});
|
|
6752
7732
|
const group = groups.get(key) ?? {
|
|
6753
7733
|
target: sourceTarget,
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
adapterModule: options.adapterModule,
|
|
6757
|
-
allowAdapterCode: options.allowAdapterCode
|
|
6758
|
-
},
|
|
7734
|
+
installationType: sourceInstallationType,
|
|
7735
|
+
adapterOptions: sourceAdapterOptions,
|
|
6759
7736
|
packages: [],
|
|
6760
7737
|
extraRoots: [],
|
|
6761
7738
|
extraPackages: []
|
|
6762
7739
|
};
|
|
6763
|
-
const entry = options.extraPackage ?? await packageEntryFromSource(source, target.workspaceRoot, options);
|
|
6764
7740
|
group.extraPackages.push(entry);
|
|
6765
7741
|
groups.set(key, group);
|
|
6766
7742
|
}
|
|
@@ -6773,18 +7749,19 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
6773
7749
|
const adapter = await resolveAdapterForTarget(group.target, group.adapterOptions);
|
|
6774
7750
|
const transport = transportForTarget(group.target);
|
|
6775
7751
|
const allPackages = [...group.packages, ...group.extraPackages];
|
|
6776
|
-
const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source ===
|
|
7752
|
+
const groupHasScope = !scopedRootId || allPackages.some((pkg) => pkg.name === scopedRootId || pkg.source === targetOptions.scope);
|
|
6777
7753
|
if (behavior.mode === "install" && scopedRootId && !groupHasScope) continue;
|
|
6778
7754
|
const updateScope = behavior.mode === "update" ? scopedPackage ? /* @__PURE__ */ new Set([scopedPackage.name]) : void 0 : void 0;
|
|
6779
7755
|
const roots = [
|
|
6780
7756
|
...allPackages.map((pkg) => {
|
|
6781
7757
|
const updateThisPackage = behavior.mode === "update" && pkg.mode === "tracking" && (!updateScope || updateScope.has(pkg.name) || updateScope.has(pkg.source));
|
|
7758
|
+
const packageIsScoped = scopedRootId ? pkg.name === scopedRootId || pkg.source === targetOptions.scope : true;
|
|
6782
7759
|
return {
|
|
6783
7760
|
rootId: pkg.name,
|
|
6784
7761
|
source: pkg.source,
|
|
6785
7762
|
mode: pkg.mode,
|
|
6786
7763
|
ref: pkg.requestedRef,
|
|
6787
|
-
select: selectedArtifacts
|
|
7764
|
+
select: selectedArtifacts && packageIsScoped ? selectedArtifacts : normalizeArtifactSelectors(pkg.select, pkg.skills),
|
|
6788
7765
|
aliases: pkg.aliases,
|
|
6789
7766
|
overrides: pkg.overrides,
|
|
6790
7767
|
useLock: behavior.mode === "install" ? true : !updateThisPackage
|
|
@@ -6795,7 +7772,7 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
6795
7772
|
if (behavior.mode === "update") {
|
|
6796
7773
|
const changed = roots.filter((root) => root.useLock === false);
|
|
6797
7774
|
if (changed.length === 0) {
|
|
6798
|
-
const label =
|
|
7775
|
+
const label = targetOptions.scope ? ` ${targetOptions.scope}` : "";
|
|
6799
7776
|
console.log(`No tracking packages to update${label}.`);
|
|
6800
7777
|
continue;
|
|
6801
7778
|
}
|
|
@@ -6807,19 +7784,24 @@ async function buildGraphPlansForTarget(target, source, options, behavior) {
|
|
|
6807
7784
|
workspaceRoot: group.target.workspaceRoot,
|
|
6808
7785
|
adapter,
|
|
6809
7786
|
transport,
|
|
6810
|
-
targetKey: group.target
|
|
6811
|
-
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions),
|
|
6812
|
-
|
|
7787
|
+
targetKey: targetKeyForTarget(group.target, adapter.name),
|
|
7788
|
+
targetFingerprintParts: targetFingerprintParts(group.target, adapter, group.adapterOptions, group.installationType),
|
|
7789
|
+
installationType: group.installationType,
|
|
7790
|
+
noDeps: noDepsFromOptions(targetOptions),
|
|
6813
7791
|
lockedResolution: behavior.mode === "install",
|
|
6814
|
-
frozenLock:
|
|
6815
|
-
offline:
|
|
6816
|
-
yes:
|
|
6817
|
-
trustPatterns:
|
|
6818
|
-
readOnly:
|
|
6819
|
-
isTTY: process.stdin.isTTY === true
|
|
7792
|
+
frozenLock: targetOptions.frozenLock,
|
|
7793
|
+
offline: targetOptions.offline,
|
|
7794
|
+
yes: targetOptions.yes,
|
|
7795
|
+
trustPatterns: targetOptions.trust ?? [],
|
|
7796
|
+
readOnly: targetOptions.dryRun === true,
|
|
7797
|
+
isTTY: process.stdin.isTTY === true,
|
|
7798
|
+
forceDrift: targetOptions.forceDrift,
|
|
7799
|
+
forceConflict: targetOptions.forceConflict,
|
|
7800
|
+
replaceConflict: targetOptions.replaceConflict
|
|
6820
7801
|
});
|
|
6821
7802
|
if (behavior.mode === "install" && scopedRootId) {
|
|
6822
|
-
const
|
|
7803
|
+
const state = installStateForTarget(group.target, adapter, group.adapterOptions, group.installationType);
|
|
7804
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
|
|
6823
7805
|
results.push(scopeInstallPlanToRoot(result, scopedRootId, manifest));
|
|
6824
7806
|
} else {
|
|
6825
7807
|
results.push(result);
|
|
@@ -6906,7 +7888,7 @@ function keepManifestEntryOperation(entry, targetRoot, rootId, operation, option
|
|
|
6906
7888
|
artifactType: entry.artifactType,
|
|
6907
7889
|
artifactName: entry.artifactName,
|
|
6908
7890
|
kind: entry.kind,
|
|
6909
|
-
destPath: operation?.destPath ??
|
|
7891
|
+
destPath: operation?.destPath ?? join31(targetRoot, entry.path),
|
|
6910
7892
|
relativeDestPath: entry.path,
|
|
6911
7893
|
desiredHash: entry.sourceHash,
|
|
6912
7894
|
currentHash: operation?.currentHash ?? entry.hash,
|
|
@@ -6942,13 +7924,15 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6942
7924
|
for (const pkg of removed) {
|
|
6943
7925
|
const removedTarget = targetForPackage(target, pkg, options);
|
|
6944
7926
|
const removedAdapterOptions = {
|
|
6945
|
-
adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
|
|
6946
|
-
adapterModule: options.adapterModule ?? pkg.adapterModule,
|
|
7927
|
+
adapterConfig: options.adapterConfig ?? removedTarget.adapterConfig ?? pkg.adapterConfig,
|
|
7928
|
+
adapterModule: options.adapterModule ?? removedTarget.adapterModule ?? pkg.adapterModule,
|
|
6947
7929
|
allowAdapterCode: options.allowAdapterCode
|
|
6948
7930
|
};
|
|
7931
|
+
const removedInstallationType = options.installationType ?? pkg.installationType ?? removedTarget.installationType ?? "local";
|
|
6949
7932
|
const adapter = await resolveAdapterForTarget(removedTarget, removedAdapterOptions);
|
|
6950
7933
|
const transport = transportForTarget(removedTarget);
|
|
6951
|
-
const
|
|
7934
|
+
const removedState = installStateForTarget(removedTarget, adapter, removedAdapterOptions, removedInstallationType);
|
|
7935
|
+
const manifest = await readInstallManifest(removedState.installRoot, adapter.name, transport, removedState);
|
|
6952
7936
|
if (!manifest) {
|
|
6953
7937
|
console.log(`No install manifest for ${adapter.name} at ${removedTarget.targetRoot}`);
|
|
6954
7938
|
continue;
|
|
@@ -6974,8 +7958,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6974
7958
|
workspaceRoot: remainingGroup.target.workspaceRoot,
|
|
6975
7959
|
adapter: remainingAdapter,
|
|
6976
7960
|
transport,
|
|
6977
|
-
targetKey: remainingGroup.target
|
|
6978
|
-
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions),
|
|
7961
|
+
targetKey: targetKeyForTarget(remainingGroup.target, remainingAdapter.name),
|
|
7962
|
+
targetFingerprintParts: targetFingerprintParts(remainingGroup.target, remainingAdapter, remainingGroup.adapterOptions, remainingGroup.installationType),
|
|
7963
|
+
installationType: remainingGroup.installationType,
|
|
6979
7964
|
lockedResolution: true,
|
|
6980
7965
|
frozenLock: options.frozenLock,
|
|
6981
7966
|
offline: options.offline,
|
|
@@ -6994,9 +7979,9 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
6994
7979
|
const graphLockFinalState = remainingGraphPlan ? { graphLock: { path: remainingGraphPlan.graphLockPath, lock: remainingGraphPlan.bundle.graphLock } } : {
|
|
6995
7980
|
removeGraphLockPath: graphLockPathForTarget(
|
|
6996
7981
|
removedTarget.workspaceRoot,
|
|
6997
|
-
removedTarget
|
|
7982
|
+
targetKeyForTarget(removedTarget, adapter.name),
|
|
6998
7983
|
adapter.name,
|
|
6999
|
-
targetFingerprintParts(removedTarget, adapter, removedAdapterOptions)
|
|
7984
|
+
targetFingerprintParts(removedTarget, adapter, removedAdapterOptions, removedInstallationType)
|
|
7000
7985
|
)
|
|
7001
7986
|
};
|
|
7002
7987
|
const result = await uninstall(plan, {
|
|
@@ -7019,16 +8004,18 @@ async function uninstallConfiguredPackage(target, packageName, options) {
|
|
|
7019
8004
|
}
|
|
7020
8005
|
function graphGroupForPackage(groups, target, pkg, options) {
|
|
7021
8006
|
const packageTarget = targetForPackage(target, pkg, options);
|
|
8007
|
+
const installationType = options.installationType ?? pkg.installationType ?? packageTarget.installationType ?? "local";
|
|
7022
8008
|
const adapterOptions = {
|
|
7023
|
-
adapterConfig: options.adapterConfig ?? pkg.adapterConfig,
|
|
7024
|
-
adapterModule: options.adapterModule ?? pkg.adapterModule,
|
|
8009
|
+
adapterConfig: options.adapterConfig ?? packageTarget.adapterConfig ?? pkg.adapterConfig,
|
|
8010
|
+
adapterModule: options.adapterModule ?? packageTarget.adapterModule ?? pkg.adapterModule,
|
|
7025
8011
|
allowAdapterCode: options.allowAdapterCode
|
|
7026
8012
|
};
|
|
7027
|
-
const key = graphGroupKey(packageTarget, adapterOptions);
|
|
8013
|
+
const key = graphGroupKey(packageTarget, { ...adapterOptions, installationType });
|
|
7028
8014
|
const existing = groups.get(key);
|
|
7029
8015
|
if (existing) return existing;
|
|
7030
8016
|
const created = {
|
|
7031
8017
|
target: packageTarget,
|
|
8018
|
+
installationType,
|
|
7032
8019
|
adapterOptions,
|
|
7033
8020
|
packages: [],
|
|
7034
8021
|
extraRoots: [],
|
|
@@ -7038,11 +8025,13 @@ function graphGroupForPackage(groups, target, pkg, options) {
|
|
|
7038
8025
|
return created;
|
|
7039
8026
|
}
|
|
7040
8027
|
function targetForPackage(target, pkg, options) {
|
|
7041
|
-
|
|
8028
|
+
const installationType = options.installationType ?? pkg.installationType ?? target.installationType;
|
|
8029
|
+
return options.adapter || target.source !== "cwd" ? { ...target, installationType } : { ...target, adapter: pkg.adapter, installationType };
|
|
7042
8030
|
}
|
|
7043
8031
|
function graphGroupKey(target, options) {
|
|
7044
8032
|
return JSON.stringify({
|
|
7045
8033
|
adapter: target.adapter,
|
|
8034
|
+
installationType: options.installationType ?? target.installationType ?? "local",
|
|
7046
8035
|
targetRoot: target.targetRoot,
|
|
7047
8036
|
transport: target.transport,
|
|
7048
8037
|
agentName: target.agentName,
|
|
@@ -7050,9 +8039,10 @@ function graphGroupKey(target, options) {
|
|
|
7050
8039
|
adapterModule: options.adapterModule
|
|
7051
8040
|
});
|
|
7052
8041
|
}
|
|
7053
|
-
function targetFingerprintParts(target, adapter, options) {
|
|
8042
|
+
function targetFingerprintParts(target, adapter, options, installationType) {
|
|
7054
8043
|
return {
|
|
7055
8044
|
adapter: adapter.name,
|
|
8045
|
+
installationType: installationType ?? target.installationType ?? "local",
|
|
7056
8046
|
adapterConfig: options.adapterConfig,
|
|
7057
8047
|
adapterModule: options.adapterModule,
|
|
7058
8048
|
adapterCodeHash: adapter.programmatic?.hash,
|
|
@@ -7062,13 +8052,27 @@ function targetFingerprintParts(target, adapter, options) {
|
|
|
7062
8052
|
ssh: target.ssh
|
|
7063
8053
|
};
|
|
7064
8054
|
}
|
|
8055
|
+
function installStateForTarget(target, adapter, options, installationType) {
|
|
8056
|
+
const targetFingerprint = targetFingerprintDigest(target, adapter, options, installationType);
|
|
8057
|
+
return {
|
|
8058
|
+
installationType,
|
|
8059
|
+
stateKey: stateKeyFor(adapter.name, { installationType, targetFingerprint }),
|
|
8060
|
+
installRoot: installRootForAdapterInstallationType(adapter, target.targetRoot, installationType, target.transport === "ssh")
|
|
8061
|
+
};
|
|
8062
|
+
}
|
|
8063
|
+
function targetFingerprintDigest(target, adapter, options, installationType) {
|
|
8064
|
+
const fingerprintInput = targetFingerprintParts(target, adapter, options, installationType);
|
|
8065
|
+
return computeTargetFingerprint(fingerprintInput);
|
|
8066
|
+
}
|
|
7065
8067
|
async function readTargetGraphLock(target, options) {
|
|
7066
|
-
const
|
|
8068
|
+
const adapterOptions = adapterOptionsForTarget(target, options);
|
|
8069
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
8070
|
+
const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter, void 0);
|
|
7067
8071
|
const path = graphLockPathForTarget(
|
|
7068
8072
|
target.workspaceRoot,
|
|
7069
|
-
target
|
|
8073
|
+
targetKeyForTarget(target, adapter.name),
|
|
7070
8074
|
adapter.name,
|
|
7071
|
-
targetFingerprintParts(target, adapter,
|
|
8075
|
+
targetFingerprintParts(target, adapter, adapterOptions, installationType)
|
|
7072
8076
|
);
|
|
7073
8077
|
if (!await pathExists(path)) {
|
|
7074
8078
|
throw new Error(`No graph lock for ${adapter.name} at ${target.targetRoot}: ${path}`);
|
|
@@ -7077,9 +8081,12 @@ async function readTargetGraphLock(target, options) {
|
|
|
7077
8081
|
}
|
|
7078
8082
|
async function printStatus(target, options) {
|
|
7079
8083
|
const config = await readMergedWorkspaceConfig(target.workspaceRoot);
|
|
7080
|
-
const
|
|
8084
|
+
const adapterOptions = adapterOptionsForTarget(target, options);
|
|
8085
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
7081
8086
|
const transport = transportForTarget(target);
|
|
7082
|
-
|
|
8087
|
+
const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
8088
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
8089
|
+
console.log(`Status for ${adapter.name}/${installationType} at ${state.installRoot}`);
|
|
7083
8090
|
if (config.packages.length === 0) {
|
|
7084
8091
|
console.log(`Configured packages: none at ${target.workspaceRoot}`);
|
|
7085
8092
|
return;
|
|
@@ -7088,10 +8095,10 @@ async function printStatus(target, options) {
|
|
|
7088
8095
|
for (const pkg of config.packages) {
|
|
7089
8096
|
console.log(`- ${pkg.name} (${pkg.mode}) ${pkg.source}`);
|
|
7090
8097
|
}
|
|
7091
|
-
const manifest = await readInstallManifest(
|
|
8098
|
+
const manifest = await readInstallManifest(state.installRoot, adapter.name, transport, state);
|
|
7092
8099
|
console.log(manifest ? `Install manifest: ${manifest.entries.length} entries, revision ${manifest.revision}` : "Install manifest: missing");
|
|
7093
8100
|
try {
|
|
7094
|
-
const { path, lock } = await readTargetGraphLock(target,
|
|
8101
|
+
const { path, lock } = await readTargetGraphLock(target, adapterOptions);
|
|
7095
8102
|
console.log(`Graph lock: ${path}`);
|
|
7096
8103
|
console.log(`Locked graph: ${lock.canonical.roots.length} roots, ${lock.canonical.nodes.length} nodes, ${lock.canonical.artifacts.length} artifacts`);
|
|
7097
8104
|
} catch {
|
|
@@ -7104,7 +8111,7 @@ async function printPendingInstallWork(target, options) {
|
|
|
7104
8111
|
try {
|
|
7105
8112
|
results = await buildGraphPlansForTarget(target, void 0, { ...options, dryRun: true }, { mode: "install" });
|
|
7106
8113
|
const operations = results.flatMap((result) => result.plan.operations);
|
|
7107
|
-
const pending = operations.filter(
|
|
8114
|
+
const pending = operations.filter(isPendingInstallOperation);
|
|
7108
8115
|
if (pending.length === 0) {
|
|
7109
8116
|
console.log("Pending install work: none");
|
|
7110
8117
|
return;
|
|
@@ -7134,6 +8141,57 @@ function collectTrustOption(value, previous) {
|
|
|
7134
8141
|
function collectOverrideOption(value, previous) {
|
|
7135
8142
|
return [...previous, ...splitSelectorList(value)];
|
|
7136
8143
|
}
|
|
8144
|
+
function normalizeRuntimeScopeOptions(options, behavior = {}) {
|
|
8145
|
+
if (options.user && options.local) {
|
|
8146
|
+
throw new Error("Choose either --user or --local.");
|
|
8147
|
+
}
|
|
8148
|
+
const shortcutType = options.user ? "user" : options.local ? "local" : void 0;
|
|
8149
|
+
if (shortcutType && options.installationType && options.installationType !== shortcutType) {
|
|
8150
|
+
throw new Error(`--${shortcutType} conflicts with --installation-type ${options.installationType}.`);
|
|
8151
|
+
}
|
|
8152
|
+
let targetRoot = options.targetRoot ? normalizeCliPath(options.targetRoot) : void 0;
|
|
8153
|
+
let installationType = options.installationType ?? shortcutType;
|
|
8154
|
+
if (!installationType && targetRoot) {
|
|
8155
|
+
installationType = isHomePath(targetRoot) ? "user" : "local";
|
|
8156
|
+
}
|
|
8157
|
+
const canDefaultTargetRoot = !options.agent && !options.all && !options.allDetected && !options.profile;
|
|
8158
|
+
if (!targetRoot && canDefaultTargetRoot && (options.user || installationType === "user" || behavior.defaultUser)) {
|
|
8159
|
+
targetRoot = homedir9();
|
|
8160
|
+
}
|
|
8161
|
+
if (!installationType && behavior.defaultUser) {
|
|
8162
|
+
installationType = "user";
|
|
8163
|
+
}
|
|
8164
|
+
return {
|
|
8165
|
+
...options,
|
|
8166
|
+
installationType,
|
|
8167
|
+
targetRoot
|
|
8168
|
+
};
|
|
8169
|
+
}
|
|
8170
|
+
function shouldDefaultUserInstall(nameOrSource, options) {
|
|
8171
|
+
return Boolean(
|
|
8172
|
+
nameOrSource && options.adapter && !options.installationType && !options.user && !options.local && !options.targetRoot && !options.agent && !options.all && !options.allDetected && !options.profile && looksLikeSourceSpecifier(nameOrSource)
|
|
8173
|
+
);
|
|
8174
|
+
}
|
|
8175
|
+
function looksLikeSourceSpecifier(value) {
|
|
8176
|
+
return value.includes(":") || value.startsWith("/") || value.startsWith("./") || value.startsWith("../") || value === "~" || value.startsWith("~/");
|
|
8177
|
+
}
|
|
8178
|
+
function normalizeCliPath(value) {
|
|
8179
|
+
if (value === "~") return homedir9();
|
|
8180
|
+
if (value.startsWith("~/")) return resolve18(homedir9(), value.slice(2));
|
|
8181
|
+
return resolve18(value);
|
|
8182
|
+
}
|
|
8183
|
+
function isHomePath(path) {
|
|
8184
|
+
return resolve18(path) === resolve18(homedir9());
|
|
8185
|
+
}
|
|
8186
|
+
function adapterListFromOption(adapter) {
|
|
8187
|
+
if (!adapter) return [];
|
|
8188
|
+
const adapters2 = splitSelectorList(adapter);
|
|
8189
|
+
const unique = [...new Set(adapters2)];
|
|
8190
|
+
if (unique.length !== adapters2.length) {
|
|
8191
|
+
throw new Error(`Duplicate adapter in --adapter: ${adapter}`);
|
|
8192
|
+
}
|
|
8193
|
+
return unique;
|
|
8194
|
+
}
|
|
7137
8195
|
function selectedArtifactsFromOptions(options) {
|
|
7138
8196
|
return normalizeArtifactSelectors(options.select, options.skills ?? options.skill);
|
|
7139
8197
|
}
|
|
@@ -7166,10 +8224,10 @@ function filterUninstallPlanBySelection(plan, selected) {
|
|
|
7166
8224
|
};
|
|
7167
8225
|
}
|
|
7168
8226
|
async function initPackage(root) {
|
|
7169
|
-
await
|
|
7170
|
-
await
|
|
7171
|
-
await
|
|
7172
|
-
const manifestPath =
|
|
8227
|
+
await mkdir16(join31(root, "instructions"), { recursive: true });
|
|
8228
|
+
await mkdir16(join31(root, "rules"), { recursive: true });
|
|
8229
|
+
await mkdir16(join31(root, "skills"), { recursive: true });
|
|
8230
|
+
const manifestPath = join31(root, "openpack.json");
|
|
7173
8231
|
const manifest = {
|
|
7174
8232
|
schemaVersion: 2,
|
|
7175
8233
|
name: "example/agentwheel-package",
|
|
@@ -7180,18 +8238,19 @@ async function initPackage(root) {
|
|
|
7180
8238
|
{ type: "skills", path: "skills" }
|
|
7181
8239
|
]
|
|
7182
8240
|
};
|
|
7183
|
-
await
|
|
8241
|
+
await writeFile14(manifestPath, `${JSON.stringify(manifest, null, 2)}
|
|
7184
8242
|
`, "utf8");
|
|
7185
|
-
await
|
|
8243
|
+
await writeFile14(join31(root, "instructions", "AGENTS.md"), "# Agent Instructions\n", "utf8");
|
|
7186
8244
|
}
|
|
7187
8245
|
async function defaultBootstrapPackage(_root) {
|
|
7188
|
-
const packageRoot = await findAgentwheelPackageRoot(
|
|
8246
|
+
const packageRoot = await findAgentwheelPackageRoot(dirname23(fileURLToPath3(import.meta.url)));
|
|
7189
8247
|
if (!packageRoot) return void 0;
|
|
7190
8248
|
return {
|
|
7191
8249
|
name: "agentwheel",
|
|
7192
8250
|
source: packageRoot,
|
|
7193
8251
|
driver: "local",
|
|
7194
8252
|
adapter: "openclaw",
|
|
8253
|
+
installationType: "local",
|
|
7195
8254
|
mode: "tracking",
|
|
7196
8255
|
select: ["skills/agentwheel"]
|
|
7197
8256
|
};
|
|
@@ -7208,7 +8267,7 @@ function withFleetExample(config) {
|
|
|
7208
8267
|
},
|
|
7209
8268
|
"remote-codex": config.agents["remote-codex"] ?? {
|
|
7210
8269
|
adapter: "codex",
|
|
7211
|
-
root: "/
|
|
8270
|
+
root: "/workspace/agent-runtime",
|
|
7212
8271
|
transport: "ssh",
|
|
7213
8272
|
host: "remote-host.example",
|
|
7214
8273
|
user: "administrator",
|
|
@@ -7231,7 +8290,7 @@ async function findAgentwheelPackageRoot(start) {
|
|
|
7231
8290
|
let current = resolve18(start);
|
|
7232
8291
|
while (true) {
|
|
7233
8292
|
if (await findPackageManifestPath(current, { warnLegacy: false })) return current;
|
|
7234
|
-
const parent =
|
|
8293
|
+
const parent = dirname23(current);
|
|
7235
8294
|
if (parent === current) return void 0;
|
|
7236
8295
|
current = parent;
|
|
7237
8296
|
}
|