agentwheel 0.14.7 → 0.14.9
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/dist/index.js +169 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -143,6 +143,20 @@ function supportedInstallationTypes(adapter, artifactType) {
|
|
|
143
143
|
}
|
|
144
144
|
return [...types].sort((a, b) => a.localeCompare(b));
|
|
145
145
|
}
|
|
146
|
+
function adapterTargetSupport(adapter, artifactType, installationType) {
|
|
147
|
+
if (artifactType === "fragments") return { ok: true };
|
|
148
|
+
const registry = adapter.targets[artifactType];
|
|
149
|
+
const supported = supportedInstallationTypes(adapter, artifactType);
|
|
150
|
+
if (!registry) {
|
|
151
|
+
return { ok: false, reason: "adapter-target-unsupported", supportedInstallationTypes: supported };
|
|
152
|
+
}
|
|
153
|
+
const target = registry[installationType];
|
|
154
|
+
if (target?.enabled) return { ok: true };
|
|
155
|
+
if (target && !target.enabled) {
|
|
156
|
+
return { ok: false, reason: "adapter-target-disabled", supportedInstallationTypes: supported };
|
|
157
|
+
}
|
|
158
|
+
return { ok: false, reason: "adapter-target-unsupported", supportedInstallationTypes: supported };
|
|
159
|
+
}
|
|
146
160
|
function resolveInstallationTypeForArtifacts(adapter, artifactTypes, requested) {
|
|
147
161
|
const installableTypes = [...new Set(artifactTypes.filter((type) => type !== "fragments"))];
|
|
148
162
|
if (installableTypes.length === 0) {
|
|
@@ -1256,6 +1270,23 @@ async function removeApplyJournal(targetRoot, adapter, transport = localTranspor
|
|
|
1256
1270
|
await transport.rm(applyJournalPath(targetRoot, adapter, scope));
|
|
1257
1271
|
await transport.rm(applyBackupDir(targetRoot, adapter, scope));
|
|
1258
1272
|
}
|
|
1273
|
+
async function abortApplyJournal(targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
1274
|
+
const lock = await acquireApplyLock(targetRoot, adapter, transport, {}, scope);
|
|
1275
|
+
try {
|
|
1276
|
+
const journalPath = applyJournalPath(targetRoot, adapter, scope);
|
|
1277
|
+
if (!await transport.pathExists(journalPath)) return void 0;
|
|
1278
|
+
const stateKey = stateKeyFor(adapter, scope);
|
|
1279
|
+
const archivePath = join3(metadataDir(targetRoot), "archive", `${stateKey}.apply-journal.failed-${journalTimestamp(/* @__PURE__ */ new Date())}.json`);
|
|
1280
|
+
const content = await transport.readFile(journalPath);
|
|
1281
|
+
await transport.writeFileAtomic(archivePath, content.endsWith("\n") ? content : `${content}
|
|
1282
|
+
`);
|
|
1283
|
+
await transport.rm(journalPath);
|
|
1284
|
+
await transport.rm(applyBackupDir(targetRoot, adapter, scope));
|
|
1285
|
+
return { journalPath, archivePath };
|
|
1286
|
+
} finally {
|
|
1287
|
+
await lock.release();
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1259
1290
|
async function recordBackup(operation, index, targetRoot, adapter, transport = localTransport, scope = {}) {
|
|
1260
1291
|
const hadExisting = await transport.pathExists(operation.destPath);
|
|
1261
1292
|
if (!hadExisting || transport.kind !== "local" || operation.action !== "update" && operation.action !== "remove" && operation.action !== "create") {
|
|
@@ -1312,6 +1343,9 @@ async function readLockOwner(ownerPath, transport) {
|
|
|
1312
1343
|
function isAlreadyExists(error) {
|
|
1313
1344
|
return typeof error === "object" && error !== null && "code" in error && error.code === "EEXIST";
|
|
1314
1345
|
}
|
|
1346
|
+
function journalTimestamp(date) {
|
|
1347
|
+
return date.toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z");
|
|
1348
|
+
}
|
|
1315
1349
|
async function localPathExists(path) {
|
|
1316
1350
|
try {
|
|
1317
1351
|
await stat2(path);
|
|
@@ -2024,7 +2058,12 @@ async function executePluginInstall(operation, transport) {
|
|
|
2024
2058
|
async function executePluginUninstall(operation, transport) {
|
|
2025
2059
|
const commands = operation.semanticPlugin?.uninstallCommands ?? [];
|
|
2026
2060
|
if (commands.length === 0) throw new Error(`Invalid semantic plugin operation missing uninstall command: ${operation.relativeDestPath}`);
|
|
2027
|
-
|
|
2061
|
+
try {
|
|
2062
|
+
await executeSemanticCommands(operation, commands, transport);
|
|
2063
|
+
} catch (error) {
|
|
2064
|
+
if (!isPluginAlreadyAbsentError(operation, error)) throw error;
|
|
2065
|
+
console.warn(`WARNING plugin-already-absent ${operation.relativeDestPath}: ${firstErrorLine(error)}`);
|
|
2066
|
+
}
|
|
2028
2067
|
if (operation.semanticPlugin?.stateRoot) {
|
|
2029
2068
|
await transport.rm(operation.semanticPlugin.stateRoot);
|
|
2030
2069
|
}
|
|
@@ -2171,6 +2210,35 @@ async function executeSemanticCommands(operation, commands, transport, options =
|
|
|
2171
2210
|
await transport.rm(stagingRoot);
|
|
2172
2211
|
}
|
|
2173
2212
|
}
|
|
2213
|
+
function isPluginAlreadyAbsentError(operation, error) {
|
|
2214
|
+
const output = commandErrorOutput(error).toLowerCase();
|
|
2215
|
+
if (!output || output.includes("command not found") || output.includes("module not found")) return false;
|
|
2216
|
+
const pluginName = (operation.semanticPlugin?.pluginName ?? operation.artifactName).toLowerCase();
|
|
2217
|
+
const escapedName = escapeRegExp(pluginName);
|
|
2218
|
+
const namedAbsent = [
|
|
2219
|
+
new RegExp(`${escapedName}.{0,120}\\b(not installed|not found|does not exist|absent)\\b`, "s"),
|
|
2220
|
+
new RegExp(`\\b(not installed|not found|does not exist|absent)\\b.{0,120}${escapedName}`, "s"),
|
|
2221
|
+
new RegExp(`\\bunknown plugin\\b.{0,120}${escapedName}`, "s"),
|
|
2222
|
+
new RegExp(`${escapedName}.{0,120}\\bunknown plugin\\b`, "s")
|
|
2223
|
+
].some((pattern) => pattern.test(output));
|
|
2224
|
+
const genericAbsent = /\b(no such|unknown)\s+plugins?\b/.test(output) || /\bplugins?\b.{0,80}\bnot installed\b/s.test(output);
|
|
2225
|
+
return namedAbsent || genericAbsent;
|
|
2226
|
+
}
|
|
2227
|
+
function commandErrorOutput(error) {
|
|
2228
|
+
if (typeof error === "object" && error !== null) {
|
|
2229
|
+
const stderr = "stderr" in error ? String(error.stderr ?? "") : "";
|
|
2230
|
+
if (stderr.trim()) return stderr;
|
|
2231
|
+
const stdout = "stdout" in error ? String(error.stdout ?? "") : "";
|
|
2232
|
+
if (stdout.trim()) return stdout;
|
|
2233
|
+
}
|
|
2234
|
+
return error instanceof Error ? error.message : String(error);
|
|
2235
|
+
}
|
|
2236
|
+
function firstErrorLine(error) {
|
|
2237
|
+
return commandErrorOutput(error).split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "plugin is already absent";
|
|
2238
|
+
}
|
|
2239
|
+
function escapeRegExp(value) {
|
|
2240
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2241
|
+
}
|
|
2174
2242
|
function semanticInstallCommands(operation) {
|
|
2175
2243
|
if (operation.semanticPlugin) return operation.semanticPlugin.installCommands;
|
|
2176
2244
|
return operation.semanticCommand ? [operation.semanticCommand] : [];
|
|
@@ -2416,7 +2484,7 @@ function codexAgentName(artifact) {
|
|
|
2416
2484
|
}
|
|
2417
2485
|
function validateCodexAgentToml(content, path) {
|
|
2418
2486
|
for (const field of requiredCodexAgentFields) {
|
|
2419
|
-
const pattern = new RegExp(`(^|\\n)\\s*${
|
|
2487
|
+
const pattern = new RegExp(`(^|\\n)\\s*${escapeRegExp2(field)}\\s*=`, "m");
|
|
2420
2488
|
if (!pattern.test(content)) {
|
|
2421
2489
|
throw new Error(`Codex custom agent TOML ${path} is missing required field '${field}'.`);
|
|
2422
2490
|
}
|
|
@@ -2462,7 +2530,7 @@ function tomlMultilineString(value) {
|
|
|
2462
2530
|
${escaped.endsWith("\n") ? escaped : `${escaped}
|
|
2463
2531
|
`}"""`;
|
|
2464
2532
|
}
|
|
2465
|
-
function
|
|
2533
|
+
function escapeRegExp2(value) {
|
|
2466
2534
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2467
2535
|
}
|
|
2468
2536
|
|
|
@@ -2867,11 +2935,6 @@ async function semanticPluginSpecForArtifact(request) {
|
|
|
2867
2935
|
return void 0;
|
|
2868
2936
|
}
|
|
2869
2937
|
|
|
2870
|
-
// src/validation/artifacts.ts
|
|
2871
|
-
import { readFile as readFile15 } from "fs/promises";
|
|
2872
|
-
import { basename as basename7, join as join15 } from "path";
|
|
2873
|
-
import { parseDocument as parseDocument2 } from "yaml";
|
|
2874
|
-
|
|
2875
2938
|
// src/model/selection.ts
|
|
2876
2939
|
function artifactSelectorKey(artifact) {
|
|
2877
2940
|
return `${artifact.type}/${artifact.name}`;
|
|
@@ -2922,7 +2985,51 @@ function subagentBaseName(name) {
|
|
|
2922
2985
|
return name.replace(/\.agent\.md$/i, "").replace(/\.toml$/i, "").replace(/\.md$/i, "");
|
|
2923
2986
|
}
|
|
2924
2987
|
|
|
2988
|
+
// src/validation/adapter-targets.ts
|
|
2989
|
+
function filterArtifactsByAdapterTargets(artifacts, adapter, installationType, options = {}) {
|
|
2990
|
+
const skipped = [];
|
|
2991
|
+
const installable = artifacts.filter((artifact) => {
|
|
2992
|
+
if (artifact.type === "fragments") return true;
|
|
2993
|
+
const support = adapterTargetSupport(adapter, artifact.type, installationType);
|
|
2994
|
+
if (support.ok) return true;
|
|
2995
|
+
const selector = artifactSelectorKey(artifact);
|
|
2996
|
+
skipped.push({ selector, artifactType: artifact.type, support });
|
|
2997
|
+
options.warn?.(skipWarning(selector, adapter, installationType, artifact.type, support));
|
|
2998
|
+
return false;
|
|
2999
|
+
});
|
|
3000
|
+
const before = artifacts.filter((artifact) => artifact.type !== "fragments");
|
|
3001
|
+
const after = installable.filter((artifact) => artifact.type !== "fragments");
|
|
3002
|
+
if (before.length > 0 && after.length === 0) {
|
|
3003
|
+
throw new Error(
|
|
3004
|
+
`${unsupportedSummary(adapter, installationType, skipped)} No installable artifacts remain for adapter ${adapter.name}/${installationType} after skipping unsupported targets: ${skipped.map((item) => item.selector).join(", ")}`
|
|
3005
|
+
);
|
|
3006
|
+
}
|
|
3007
|
+
return installable;
|
|
3008
|
+
}
|
|
3009
|
+
function unsupportedSummary(adapter, installationType, skipped) {
|
|
3010
|
+
const types = [...new Set(skipped.map((item) => item.artifactType))].sort((a, b) => a.localeCompare(b));
|
|
3011
|
+
if (types.length !== 1) {
|
|
3012
|
+
return `Adapter ${adapter.name} does not support selected artifact targets for installation type '${installationType}'.`;
|
|
3013
|
+
}
|
|
3014
|
+
const type = types[0];
|
|
3015
|
+
const supported = [...new Set(skipped.flatMap((item) => item.support.supportedInstallationTypes))].sort((a, b) => a.localeCompare(b));
|
|
3016
|
+
if (supported.length > 0) {
|
|
3017
|
+
return `Adapter ${adapter.name} does not support ${type} artifacts for installation type '${installationType}'. Supported: ${supported.join(", ")}.`;
|
|
3018
|
+
}
|
|
3019
|
+
return `Adapter ${adapter.name} does not support ${type} artifacts for any installation type.`;
|
|
3020
|
+
}
|
|
3021
|
+
function skipWarning(selector, adapter, installationType, artifactType, support) {
|
|
3022
|
+
if (support.reason === "adapter-target-disabled") {
|
|
3023
|
+
return `skip ${selector} (selected but adapter-target-disabled: ${adapter.name}/${installationType} disables ${artifactType})`;
|
|
3024
|
+
}
|
|
3025
|
+
const suffix = support.supportedInstallationTypes.length > 0 ? `; supported installation types: ${support.supportedInstallationTypes.join(", ")}` : "";
|
|
3026
|
+
return `skip ${selector} (selected but adapter-target-unsupported: ${adapter.name}/${installationType} has no enabled target for ${artifactType}${suffix})`;
|
|
3027
|
+
}
|
|
3028
|
+
|
|
2925
3029
|
// src/validation/artifacts.ts
|
|
3030
|
+
import { readFile as readFile15 } from "fs/promises";
|
|
3031
|
+
import { basename as basename7, join as join15 } from "path";
|
|
3032
|
+
import { parseDocument as parseDocument2 } from "yaml";
|
|
2926
3033
|
var behavioralRuleFormats = ["markdown-rule", "claude-markdown-rule", "copilot-instruction-rule"];
|
|
2927
3034
|
var pluginFormats = ["claude-plugin", "codex-plugin", "hermes-plugin", "copilot-plugin", "openclaw-plugin", "openclaw-clawhub-plugin"];
|
|
2928
3035
|
async function filterArtifactsByInstallFormat(artifacts, adapter, installationType, options = {}) {
|
|
@@ -3380,7 +3487,10 @@ function errorMessage(error) {
|
|
|
3380
3487
|
// src/install/plan.ts
|
|
3381
3488
|
async function createCombinedInstallPlan(desiredArtifacts, adapter, targetRoot, manifest, transport = localTransport, options = {}) {
|
|
3382
3489
|
const requestedInstallationType = options.installationType ?? defaultInstallationType;
|
|
3383
|
-
const
|
|
3490
|
+
const formatCompatibleArtifacts = await filterArtifactsByInstallFormat(desiredArtifacts, adapter, requestedInstallationType, { warn: options.warn });
|
|
3491
|
+
const installableArtifacts = filterArtifactsByAdapterTargets(formatCompatibleArtifacts, adapter, requestedInstallationType, {
|
|
3492
|
+
warn: options.suppressAdapterTargetWarnings ? void 0 : options.warn
|
|
3493
|
+
});
|
|
3384
3494
|
const installationType = resolveInstallationTypeForArtifacts(adapter, installableArtifacts.map((artifact) => artifact.type), requestedInstallationType);
|
|
3385
3495
|
const installRoot = installRootForArtifacts(adapter, targetRoot, installationType, installableArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
3386
3496
|
await validateArtifactsForInstall(installableArtifacts, adapter, installationType);
|
|
@@ -6299,6 +6409,8 @@ var workspaceTrustSchema = z6.object({
|
|
|
6299
6409
|
}).default({});
|
|
6300
6410
|
var workspaceAgentSchema = z6.object({
|
|
6301
6411
|
adapter: z6.string().min(1),
|
|
6412
|
+
adapterConfig: z6.string().min(1).optional(),
|
|
6413
|
+
adapterModule: z6.string().min(1).optional(),
|
|
6302
6414
|
root: z6.string().min(1),
|
|
6303
6415
|
installationType: installationTypeSchema.optional(),
|
|
6304
6416
|
transport: z6.enum(["local", "ssh"]).default("local"),
|
|
@@ -8354,7 +8466,12 @@ async function createGraphSourcePlan(options) {
|
|
|
8354
8466
|
targetFingerprint,
|
|
8355
8467
|
warn
|
|
8356
8468
|
});
|
|
8357
|
-
const desiredArtifacts =
|
|
8469
|
+
const desiredArtifacts = filterArtifactsByAdapterTargets(
|
|
8470
|
+
desiredArtifactsFromGraphBundle(bundle),
|
|
8471
|
+
options.adapter,
|
|
8472
|
+
installationType,
|
|
8473
|
+
{ warn }
|
|
8474
|
+
);
|
|
8358
8475
|
const resolvedInstallationType = resolveInstallationTypeForArtifacts(options.adapter, desiredArtifacts.map((artifact) => artifact.type), installationType);
|
|
8359
8476
|
const resolvedInstallRoot = installRootForArtifacts(options.adapter, options.targetRoot, resolvedInstallationType, desiredArtifacts.map((artifact) => artifact.type), transport.kind === "ssh");
|
|
8360
8477
|
const graphLockDigest = digestGraphLock(bundle.graphLock);
|
|
@@ -8622,6 +8739,8 @@ function targetFromAgent(name, config, workspaceRoot, installationType) {
|
|
|
8622
8739
|
agentName: name,
|
|
8623
8740
|
targetKey: name,
|
|
8624
8741
|
adapter: agent.adapter,
|
|
8742
|
+
adapterConfig: agent.adapterConfig,
|
|
8743
|
+
adapterModule: agent.adapterModule,
|
|
8625
8744
|
installationType: installationType ?? agent.installationType,
|
|
8626
8745
|
targetRoot: agent.transport === "ssh" ? agent.root : resolveConfigPath(agent.root, workspaceRoot),
|
|
8627
8746
|
workspaceRoot,
|
|
@@ -9449,6 +9568,38 @@ program.command("status").description("show configured packages and runtime inst
|
|
|
9449
9568
|
await printStatus(target, normalizedOptions);
|
|
9450
9569
|
}
|
|
9451
9570
|
});
|
|
9571
|
+
var journalCommand = program.command("journal").description("inspect or abort pending apply journals");
|
|
9572
|
+
journalCommand.command("list").description("show pending apply journals for resolved runtime targets").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) => {
|
|
9573
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
9574
|
+
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
9575
|
+
let pending = 0;
|
|
9576
|
+
for (const target of targets) {
|
|
9577
|
+
const state = await journalStateForTarget(target, normalizedOptions);
|
|
9578
|
+
const journal = await readApplyJournal(state.installRoot, state.adapter.name, state.transport, state.state);
|
|
9579
|
+
if (!journal) continue;
|
|
9580
|
+
pending += 1;
|
|
9581
|
+
console.log(`PENDING ${state.adapter.name}/${state.installationType} at ${state.installRoot}`);
|
|
9582
|
+
console.log(` journal: ${join41(state.installRoot, ".agentwheel", `${state.state.stateKey}.apply-journal.json`)}`);
|
|
9583
|
+
console.log(` stateKey: ${state.state.stateKey}`);
|
|
9584
|
+
console.log(` createdAt: ${journal.createdAt}`);
|
|
9585
|
+
console.log(` updatedAt: ${journal.updatedAt}`);
|
|
9586
|
+
console.log(` operations: ${journal.operations.length}, completed: ${journal.completed.length}`);
|
|
9587
|
+
}
|
|
9588
|
+
if (pending === 0) console.log("No pending apply journals.");
|
|
9589
|
+
});
|
|
9590
|
+
journalCommand.command("abort").description("archive pending apply journals without touching runtime files").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) => {
|
|
9591
|
+
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
9592
|
+
const targets = await resolveCliTargets(normalizedOptions, { preferAllProfile: true });
|
|
9593
|
+
let aborted = 0;
|
|
9594
|
+
for (const target of targets) {
|
|
9595
|
+
const state = await journalStateForTarget(target, normalizedOptions);
|
|
9596
|
+
const result = await abortApplyJournal(state.installRoot, state.adapter.name, state.transport, state.state);
|
|
9597
|
+
if (!result) continue;
|
|
9598
|
+
aborted += 1;
|
|
9599
|
+
console.log(`Archived ${state.adapter.name}/${state.installationType} pending journal: ${result.archivePath}`);
|
|
9600
|
+
}
|
|
9601
|
+
if (aborted === 0) console.log("No pending apply journals.");
|
|
9602
|
+
});
|
|
9452
9603
|
program.command("doctor").description("check agentwheel runtime setup and companion skill guidance").option("--adapter <adapter>", "built-in adapter").option("-i, --installation-type <type>", "installation type (for example local or user)").option("--user", "shortcut for --installation-type user and home-scoped state", false).option("--local", "shortcut for --installation-type local", false).option("-t, --target-root <path>", "runtime/project root").option("--agent <name>", "named agent from merged config").option("--adapter-config <path>", "adapter JSON/JSONC file").option("--adapter-module <path>", "local programmatic adapter module").option("--allow-adapter-code", "allow loading local adapter code", false).option("--skill <name>", "check a specific skill by name (repeatable or comma-separated)", collectSkillOption, []).option("--source <source>", "source to use in suggested install commands").option("--json", "print machine-readable doctor report", false).action(async (options) => {
|
|
9453
9604
|
const normalizedOptions = normalizeRuntimeScopeOptions(options);
|
|
9454
9605
|
const target = await resolveRuntimeTarget({
|
|
@@ -10141,6 +10292,14 @@ async function printStatus(target, options) {
|
|
|
10141
10292
|
}
|
|
10142
10293
|
await printPendingInstallWork(target, options);
|
|
10143
10294
|
}
|
|
10295
|
+
async function journalStateForTarget(target, options) {
|
|
10296
|
+
const adapterOptions = adapterOptionsForTarget(target, options);
|
|
10297
|
+
const adapter = await resolveAdapterForTarget(target, adapterOptions);
|
|
10298
|
+
const transport = transportForTarget(target);
|
|
10299
|
+
const installationType = options.installationType ?? target.installationType ?? resolveInstallationTypeForAdapter(adapter);
|
|
10300
|
+
const state = installStateForTarget(target, adapter, adapterOptions, installationType);
|
|
10301
|
+
return { adapter, transport, installationType, installRoot: state.installRoot, state };
|
|
10302
|
+
}
|
|
10144
10303
|
async function printPendingInstallWork(target, options) {
|
|
10145
10304
|
let results = [];
|
|
10146
10305
|
try {
|