apcore-cli 0.9.0 → 0.10.0
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/CHANGELOG.md +42 -0
- package/dist/bin/apcore-cli.js +1644 -1579
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/index.d.ts +44 -10
- package/dist/index.js +2281 -2226
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
package/dist/index.js
CHANGED
|
@@ -115,6 +115,7 @@ var init_errors = __esm({
|
|
|
115
115
|
}
|
|
116
116
|
};
|
|
117
117
|
SchemaValidationError = class extends Error {
|
|
118
|
+
code = "SCHEMA_VALIDATION_ERROR";
|
|
118
119
|
constructor(message = "Schema validation failed") {
|
|
119
120
|
super(message);
|
|
120
121
|
this.name = "SchemaValidationError";
|
|
@@ -651,7 +652,7 @@ var init_sandbox = __esm({
|
|
|
651
652
|
*/
|
|
652
653
|
async execute(moduleId, inputData, executor) {
|
|
653
654
|
if (!this.enabled) {
|
|
654
|
-
return executor.
|
|
655
|
+
return executor.call(moduleId, inputData);
|
|
655
656
|
}
|
|
656
657
|
return this._sandboxedExecute(moduleId, inputData);
|
|
657
658
|
}
|
|
@@ -780,7 +781,7 @@ init_errors();
|
|
|
780
781
|
import { readFileSync as readFileSync3 } from "fs";
|
|
781
782
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
782
783
|
import * as path5 from "path";
|
|
783
|
-
import { Command as
|
|
784
|
+
import { Command as Command6, CommanderError, Option as Option4 } from "commander";
|
|
784
785
|
|
|
785
786
|
// src/ref-resolver.ts
|
|
786
787
|
init_esm_shims();
|
|
@@ -953,9 +954,17 @@ function mapType(propName, propSchema) {
|
|
|
953
954
|
array: "string"
|
|
954
955
|
};
|
|
955
956
|
if (!schemaType) {
|
|
957
|
+
warn(`No type specified for property '${propName}', defaulting to string.`);
|
|
956
958
|
return "string";
|
|
957
959
|
}
|
|
958
|
-
|
|
960
|
+
const mapped = typeMap[schemaType];
|
|
961
|
+
if (mapped === void 0) {
|
|
962
|
+
warn(
|
|
963
|
+
`Unknown schema type '${schemaType}' for property '${propName}', defaulting to string.`
|
|
964
|
+
);
|
|
965
|
+
return "string";
|
|
966
|
+
}
|
|
967
|
+
return mapped;
|
|
959
968
|
}
|
|
960
969
|
function extractHelp(propSchema, maxLength = 1e3) {
|
|
961
970
|
let text = propSchema["x-llm-description"];
|
|
@@ -1154,7 +1163,7 @@ var CliApprovalHandler = class {
|
|
|
1154
1163
|
const message = extra.approval_message ?? `Module '${moduleId}' requires approval to execute.`;
|
|
1155
1164
|
process.stderr.write(message + "\n");
|
|
1156
1165
|
try {
|
|
1157
|
-
await promptWithTimeout({
|
|
1166
|
+
await promptWithTimeout({ moduleId }, this.timeout);
|
|
1158
1167
|
return { status: "approved", approved_by: "tty_user" };
|
|
1159
1168
|
} catch {
|
|
1160
1169
|
return { status: "rejected", reason: "User rejected or timed out" };
|
|
@@ -1177,7 +1186,7 @@ async function checkApproval(moduleDef, autoApprove, timeout) {
|
|
|
1177
1186
|
if (!requiresApproval) {
|
|
1178
1187
|
return;
|
|
1179
1188
|
}
|
|
1180
|
-
const moduleId = moduleDef.
|
|
1189
|
+
const moduleId = moduleDef.moduleId;
|
|
1181
1190
|
if (autoApprove) {
|
|
1182
1191
|
return;
|
|
1183
1192
|
}
|
|
@@ -1201,7 +1210,7 @@ async function checkApproval(moduleDef, autoApprove, timeout) {
|
|
|
1201
1210
|
}
|
|
1202
1211
|
async function promptWithTimeout(moduleDef, timeout) {
|
|
1203
1212
|
timeout = Math.max(1, Math.min(timeout, 3600));
|
|
1204
|
-
const moduleId = moduleDef.
|
|
1213
|
+
const moduleId = moduleDef.moduleId;
|
|
1205
1214
|
const annotations = moduleDef.annotations;
|
|
1206
1215
|
const message = (annotations ? getAnnotation(annotations, "approval_message") : void 0) ?? `Module '${moduleId}' requires approval to execute.`;
|
|
1207
1216
|
process.stderr.write(message + "\n");
|
|
@@ -1247,7 +1256,7 @@ function descriptorToScanned(m) {
|
|
|
1247
1256
|
const metadata = m.metadata ?? {};
|
|
1248
1257
|
const display = metadata["display"] ?? null;
|
|
1249
1258
|
return {
|
|
1250
|
-
moduleId: m.
|
|
1259
|
+
moduleId: m.moduleId,
|
|
1251
1260
|
description: m.description ?? "",
|
|
1252
1261
|
inputSchema: m.inputSchema ?? {},
|
|
1253
1262
|
outputSchema: m.outputSchema ?? {},
|
|
@@ -1303,13 +1312,13 @@ async function formatModuleList(modules, format, filterTags, showDeps = false, e
|
|
|
1303
1312
|
if (showDeps) headers.push("Deps");
|
|
1304
1313
|
if (exposureFilter) headers.push("Exposure");
|
|
1305
1314
|
const rows = modules.map((m) => {
|
|
1306
|
-
const base = [m.
|
|
1315
|
+
const base = [m.moduleId, truncate(m.description, 80), (m.tags ?? []).join(", ")];
|
|
1307
1316
|
if (showDeps) {
|
|
1308
1317
|
const deps = m.dependencies;
|
|
1309
1318
|
base.push(String(Array.isArray(deps) ? deps.length : 0));
|
|
1310
1319
|
}
|
|
1311
1320
|
if (exposureFilter) {
|
|
1312
|
-
base.push(exposureFilter.isExposed(m.
|
|
1321
|
+
base.push(exposureFilter.isExposed(m.moduleId ?? "") ? "\u2713" : "\u2014");
|
|
1313
1322
|
}
|
|
1314
1323
|
return base;
|
|
1315
1324
|
});
|
|
@@ -1317,7 +1326,7 @@ async function formatModuleList(modules, format, filterTags, showDeps = false, e
|
|
|
1317
1326
|
} else if (format === "json") {
|
|
1318
1327
|
const result = modules.map((m) => {
|
|
1319
1328
|
const entry = {
|
|
1320
|
-
id: m.
|
|
1329
|
+
id: m.moduleId,
|
|
1321
1330
|
description: m.description,
|
|
1322
1331
|
tags: m.tags ?? []
|
|
1323
1332
|
};
|
|
@@ -1326,7 +1335,7 @@ async function formatModuleList(modules, format, filterTags, showDeps = false, e
|
|
|
1326
1335
|
entry.dependency_count = Array.isArray(deps) ? deps.length : 0;
|
|
1327
1336
|
}
|
|
1328
1337
|
if (exposureFilter) {
|
|
1329
|
-
entry.exposed = exposureFilter.isExposed(m.
|
|
1338
|
+
entry.exposed = exposureFilter.isExposed(m.moduleId ?? "");
|
|
1330
1339
|
}
|
|
1331
1340
|
return entry;
|
|
1332
1341
|
});
|
|
@@ -1358,7 +1367,7 @@ function annotationsToDict(annotations) {
|
|
|
1358
1367
|
async function formatModuleDetail(moduleDef, format) {
|
|
1359
1368
|
if (format === "table") {
|
|
1360
1369
|
process.stdout.write(`
|
|
1361
|
-
Module: ${moduleDef.
|
|
1370
|
+
Module: ${moduleDef.moduleId}
|
|
1362
1371
|
`);
|
|
1363
1372
|
process.stdout.write(`
|
|
1364
1373
|
Description:
|
|
@@ -1406,7 +1415,7 @@ Tags: ${tags.join(", ")}
|
|
|
1406
1415
|
}
|
|
1407
1416
|
} else if (format === "json") {
|
|
1408
1417
|
const result = {
|
|
1409
|
-
id: moduleDef.
|
|
1418
|
+
id: moduleDef.moduleId,
|
|
1410
1419
|
description: moduleDef.description
|
|
1411
1420
|
};
|
|
1412
1421
|
if (moduleDef.inputSchema) result.input_schema = moduleDef.inputSchema;
|
|
@@ -1755,7 +1764,7 @@ function getDisplay(descriptor) {
|
|
|
1755
1764
|
if (display && typeof display === "object" && !Array.isArray(display)) {
|
|
1756
1765
|
return display;
|
|
1757
1766
|
}
|
|
1758
|
-
const overlay = lookupBindingDisplay(descriptor.
|
|
1767
|
+
const overlay = lookupBindingDisplay(descriptor.moduleId);
|
|
1759
1768
|
return overlay ?? {};
|
|
1760
1769
|
}
|
|
1761
1770
|
|
|
@@ -1810,9 +1819,9 @@ var LEGACY_TO_NAMESPACE = Object.fromEntries(
|
|
|
1810
1819
|
function registerConfigNamespace() {
|
|
1811
1820
|
try {
|
|
1812
1821
|
const nodeRequire = createRequire(import.meta.url);
|
|
1813
|
-
const { Config } = nodeRequire("apcore-js");
|
|
1814
|
-
if (typeof
|
|
1815
|
-
|
|
1822
|
+
const { Config: Config2 } = nodeRequire("apcore-js");
|
|
1823
|
+
if (typeof Config2?.registerNamespace === "function") {
|
|
1824
|
+
Config2.registerNamespace({
|
|
1816
1825
|
name: "apcore-cli",
|
|
1817
1826
|
envPrefix: "APCORE_CLI",
|
|
1818
1827
|
defaults: NAMESPACE_DEFAULTS
|
|
@@ -2273,2445 +2282,2491 @@ function registerCompletionCommand(host) {
|
|
|
2273
2282
|
|
|
2274
2283
|
// src/discovery.ts
|
|
2275
2284
|
init_esm_shims();
|
|
2276
|
-
import { Command as
|
|
2277
|
-
init_errors();
|
|
2278
|
-
init_audit();
|
|
2285
|
+
import { Command as Command3, Option as Option2 } from "commander";
|
|
2279
2286
|
|
|
2280
|
-
// src/
|
|
2287
|
+
// src/cli.ts
|
|
2281
2288
|
init_esm_shims();
|
|
2282
|
-
import
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
"
|
|
2289
|
-
"30d": 30 * 24 * 60 * 60 * 1e3
|
|
2290
|
-
};
|
|
2291
|
-
var DEFAULT_AUDIT_PATH = path4.join(
|
|
2292
|
-
os2.homedir(),
|
|
2293
|
-
".apcore-cli",
|
|
2294
|
-
"audit.jsonl"
|
|
2295
|
-
);
|
|
2296
|
-
function computeSummary(options = {}) {
|
|
2297
|
-
const auditPath = options.auditPath ?? DEFAULT_AUDIT_PATH;
|
|
2298
|
-
const period = options.period ?? "24h";
|
|
2299
|
-
const cutoff = (options.now ?? /* @__PURE__ */ new Date()).getTime() - PERIOD_TO_MS[period];
|
|
2300
|
-
if (!fs4.existsSync(auditPath)) {
|
|
2301
|
-
return /* @__PURE__ */ new Map();
|
|
2302
|
-
}
|
|
2303
|
-
let raw;
|
|
2304
|
-
try {
|
|
2305
|
-
raw = fs4.readFileSync(auditPath, "utf-8");
|
|
2306
|
-
} catch {
|
|
2307
|
-
return /* @__PURE__ */ new Map();
|
|
2308
|
-
}
|
|
2309
|
-
const counts = /* @__PURE__ */ new Map();
|
|
2310
|
-
const errors = /* @__PURE__ */ new Map();
|
|
2311
|
-
const latencySum = /* @__PURE__ */ new Map();
|
|
2312
|
-
for (const line of raw.split("\n")) {
|
|
2313
|
-
const trimmed = line.trim();
|
|
2314
|
-
if (!trimmed) continue;
|
|
2315
|
-
let entry;
|
|
2316
|
-
try {
|
|
2317
|
-
entry = JSON.parse(trimmed);
|
|
2318
|
-
} catch {
|
|
2319
|
-
continue;
|
|
2320
|
-
}
|
|
2321
|
-
const ts = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : NaN;
|
|
2322
|
-
if (Number.isNaN(ts) || ts < cutoff) continue;
|
|
2323
|
-
const moduleId = typeof entry.module_id === "string" ? entry.module_id : null;
|
|
2324
|
-
if (!moduleId) continue;
|
|
2325
|
-
counts.set(moduleId, (counts.get(moduleId) ?? 0) + 1);
|
|
2326
|
-
if (entry.status === "error") {
|
|
2327
|
-
errors.set(moduleId, (errors.get(moduleId) ?? 0) + 1);
|
|
2328
|
-
}
|
|
2329
|
-
const duration = entry.duration_ms;
|
|
2330
|
-
if (typeof duration === "number") {
|
|
2331
|
-
latencySum.set(moduleId, (latencySum.get(moduleId) ?? 0) + duration);
|
|
2332
|
-
}
|
|
2333
|
-
}
|
|
2334
|
-
const out = /* @__PURE__ */ new Map();
|
|
2335
|
-
for (const [id, calls] of counts) {
|
|
2336
|
-
out.set(id, {
|
|
2337
|
-
module_id: id,
|
|
2338
|
-
calls,
|
|
2339
|
-
errors: errors.get(id) ?? 0,
|
|
2340
|
-
latency_ms: calls > 0 ? (latencySum.get(id) ?? 0) / calls : 0
|
|
2341
|
-
});
|
|
2342
|
-
}
|
|
2343
|
-
return out;
|
|
2289
|
+
import { Command as Command2 } from "commander";
|
|
2290
|
+
|
|
2291
|
+
// src/exposure.ts
|
|
2292
|
+
init_esm_shims();
|
|
2293
|
+
init_logger();
|
|
2294
|
+
function escapeRegex(str) {
|
|
2295
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2344
2296
|
}
|
|
2345
|
-
function
|
|
2346
|
-
const
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
modules.sort((a, b) => (a.id ?? a.module_id ?? "").localeCompare(b.id ?? b.module_id ?? ""));
|
|
2353
|
-
if (reverse) modules.reverse();
|
|
2354
|
-
return { used: false };
|
|
2355
|
-
}
|
|
2356
|
-
const key = (m) => {
|
|
2357
|
-
const id = m.id ?? m.module_id ?? "";
|
|
2358
|
-
const s = summary.get(id);
|
|
2359
|
-
if (!s) return 0;
|
|
2360
|
-
return field === "latency" ? s.latency_ms : field === "calls" ? s.calls : s.errors;
|
|
2361
|
-
};
|
|
2362
|
-
modules.sort((a, b) => {
|
|
2363
|
-
const diff = key(a) - key(b);
|
|
2364
|
-
if (diff !== 0) return reverse ? -diff : diff;
|
|
2365
|
-
return (a.id ?? a.module_id ?? "").localeCompare(b.id ?? b.module_id ?? "");
|
|
2297
|
+
function compilePattern(pattern) {
|
|
2298
|
+
const sentinel = "\0GLOB\0";
|
|
2299
|
+
const escaped = pattern.replaceAll("**", sentinel);
|
|
2300
|
+
const parts = escaped.split("*");
|
|
2301
|
+
const regexParts = parts.map((p) => {
|
|
2302
|
+
const restored = p.replaceAll(sentinel, "**");
|
|
2303
|
+
return escapeRegex(restored);
|
|
2366
2304
|
});
|
|
2367
|
-
|
|
2305
|
+
let regex = regexParts.join("[^.]*");
|
|
2306
|
+
regex = regex.replaceAll("\\*\\*", ".+");
|
|
2307
|
+
return new RegExp(`^${regex}$`);
|
|
2368
2308
|
}
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2309
|
+
var ExposureFilter = class _ExposureFilter {
|
|
2310
|
+
static VALID_MODES = ["all", "include", "exclude", "none"];
|
|
2311
|
+
_mode;
|
|
2312
|
+
_compiledInclude;
|
|
2313
|
+
_compiledExclude;
|
|
2314
|
+
constructor(mode = "all", include, exclude) {
|
|
2315
|
+
if (!_ExposureFilter.VALID_MODES.includes(mode)) {
|
|
2316
|
+
process.stderr.write(
|
|
2317
|
+
`Warning: Unknown ExposureFilter mode '${mode}' \u2014 defaulting to 'none'. Valid modes: ${_ExposureFilter.VALID_MODES.join(", ")}.
|
|
2376
2318
|
`
|
|
2377
|
-
|
|
2378
|
-
|
|
2319
|
+
);
|
|
2320
|
+
mode = "none";
|
|
2321
|
+
}
|
|
2322
|
+
this._mode = mode;
|
|
2323
|
+
const dedup = (arr) => [...new Set(arr)];
|
|
2324
|
+
this._compiledInclude = dedup(include ?? []).map(compilePattern);
|
|
2325
|
+
this._compiledExclude = dedup(exclude ?? []).map(compilePattern);
|
|
2379
2326
|
}
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
return previous.concat([value]);
|
|
2386
|
-
}
|
|
2387
|
-
function getAnnotationFlag(moduleDef, flag) {
|
|
2388
|
-
const annotations = moduleDef.annotations;
|
|
2389
|
-
if (!annotations || typeof annotations !== "object") return false;
|
|
2390
|
-
const ann = annotations;
|
|
2391
|
-
const map = {
|
|
2392
|
-
"destructive": "destructive",
|
|
2393
|
-
"requires-approval": "requires_approval",
|
|
2394
|
-
"readonly": "readonly",
|
|
2395
|
-
"streaming": "streaming",
|
|
2396
|
-
"cacheable": "cacheable",
|
|
2397
|
-
"idempotent": "idempotent",
|
|
2398
|
-
"paginated": "paginated"
|
|
2399
|
-
};
|
|
2400
|
-
const attr = map[flag] ?? flag;
|
|
2401
|
-
return ann[attr] === true;
|
|
2402
|
-
}
|
|
2403
|
-
function registerListCommand(apcliGroup, registry, exposureFilter) {
|
|
2404
|
-
const listCmd = new Command2("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--flat", "Show flat list (no grouping).", false).addOption(
|
|
2405
|
-
new Option2("--format <format>", "Output format.").choices(["table", "json", "csv", "yaml", "jsonl", "markdown", "skill"])
|
|
2406
|
-
).option("-s, --search <query>", "Filter by substring match on ID and description.").addOption(
|
|
2407
|
-
new Option2("--status <status>", "Filter by module status.").choices(["enabled", "disabled", "all"]).default("enabled")
|
|
2408
|
-
).option("-a, --annotation <flag>", "Filter by annotation flag (AND logic). Repeatable.", collectAnnotation, []).addOption(
|
|
2409
|
-
new Option2("--sort <field>", "Sort order.").choices(["id", "calls", "errors", "latency"]).default("id")
|
|
2410
|
-
).option("--reverse", "Reverse sort order.", false).option("--deprecated", "Include deprecated modules.", false).option("--deps", "Show dependency count column.", false).addOption(
|
|
2411
|
-
new Option2("--exposure <mode>", "Filter by exposure status.").choices(["exposed", "hidden", "all"]).default("exposed")
|
|
2412
|
-
).action((opts) => {
|
|
2413
|
-
for (const t of opts.tag) {
|
|
2414
|
-
validateTag(t);
|
|
2327
|
+
/** Return true if the module should be exposed as a CLI command. */
|
|
2328
|
+
isExposed(moduleId) {
|
|
2329
|
+
if (this._mode === "all") return true;
|
|
2330
|
+
if (this._mode === "include") {
|
|
2331
|
+
return this._compiledInclude.some((rx) => rx.test(moduleId));
|
|
2415
2332
|
}
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
modules.push(m);
|
|
2333
|
+
if (this._mode === "exclude") {
|
|
2334
|
+
return !this._compiledExclude.some((rx) => rx.test(moduleId));
|
|
2419
2335
|
}
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2336
|
+
return false;
|
|
2337
|
+
}
|
|
2338
|
+
/** Partition moduleIds into [exposed, hidden] lists. */
|
|
2339
|
+
filterModules(moduleIds) {
|
|
2340
|
+
const exposed = [];
|
|
2341
|
+
const hidden = [];
|
|
2342
|
+
for (const mid of moduleIds) {
|
|
2343
|
+
(this.isExposed(mid) ? exposed : hidden).push(mid);
|
|
2426
2344
|
}
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
|
|
2431
|
-
|
|
2345
|
+
return [exposed, hidden];
|
|
2346
|
+
}
|
|
2347
|
+
/**
|
|
2348
|
+
* Create an ExposureFilter from a parsed config dict.
|
|
2349
|
+
*
|
|
2350
|
+
* Expected: `{ expose: { mode: "include", include: ["admin.*"] } }`
|
|
2351
|
+
*/
|
|
2352
|
+
static fromConfig(config) {
|
|
2353
|
+
const expose = config.expose ?? {};
|
|
2354
|
+
if (typeof expose !== "object" || expose === null || Array.isArray(expose)) {
|
|
2355
|
+
warn("Invalid 'expose' config (expected dict), using mode: all.");
|
|
2356
|
+
return new _ExposureFilter();
|
|
2432
2357
|
}
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
modules = modules.filter((m) => {
|
|
2440
|
-
const enabled = m.enabled;
|
|
2441
|
-
return enabled === false;
|
|
2442
|
-
});
|
|
2358
|
+
const exposeObj = expose;
|
|
2359
|
+
const mode = exposeObj.mode ?? "all";
|
|
2360
|
+
if (!["all", "include", "exclude"].includes(mode)) {
|
|
2361
|
+
throw new Error(
|
|
2362
|
+
`Invalid expose mode: '${mode}'. Must be one of: all, include, exclude.`
|
|
2363
|
+
);
|
|
2443
2364
|
}
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
});
|
|
2365
|
+
let include = exposeObj.include ?? [];
|
|
2366
|
+
if (!Array.isArray(include)) {
|
|
2367
|
+
warn("Invalid 'expose.include' (expected list), ignoring.");
|
|
2368
|
+
include = [];
|
|
2449
2369
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2370
|
+
let exclude = exposeObj.exclude ?? [];
|
|
2371
|
+
if (!Array.isArray(exclude)) {
|
|
2372
|
+
warn("Invalid 'expose.exclude' (expected list), ignoring.");
|
|
2373
|
+
exclude = [];
|
|
2454
2374
|
}
|
|
2455
|
-
|
|
2456
|
-
const
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
`
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
} else {
|
|
2464
|
-
modules.sort((a, b) => (a.id ?? "").localeCompare(b.id ?? ""));
|
|
2465
|
-
if (opts.reverse) {
|
|
2466
|
-
modules.reverse();
|
|
2375
|
+
const filterList = (arr, label) => {
|
|
2376
|
+
const result = [];
|
|
2377
|
+
for (const p of arr) {
|
|
2378
|
+
if (!p) {
|
|
2379
|
+
warn(`Empty pattern in expose.${label}, skipping.`);
|
|
2380
|
+
} else {
|
|
2381
|
+
result.push(String(p));
|
|
2382
|
+
}
|
|
2467
2383
|
}
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
if (opts.exposure === "all" && exposureFilter) {
|
|
2478
|
-
showExposureCol = true;
|
|
2479
|
-
}
|
|
2480
|
-
const fmt = resolveFormat(opts.format);
|
|
2481
|
-
const filterTagsArg = opts.tag.length > 0 ? opts.tag : void 0;
|
|
2482
|
-
void formatModuleList(modules, fmt, filterTagsArg, opts.deps, showExposureCol ? exposureFilter : void 0);
|
|
2483
|
-
});
|
|
2484
|
-
apcliGroup.addCommand(listCmd);
|
|
2485
|
-
}
|
|
2486
|
-
function registerDescribeCommand(apcliGroup, registry) {
|
|
2487
|
-
const describeCmd = new Command2("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").addOption(
|
|
2488
|
-
new Option2("--format <format>", "Output format.").choices(["table", "json", "csv", "yaml", "jsonl", "markdown", "skill"])
|
|
2489
|
-
).action((moduleId, opts) => {
|
|
2490
|
-
validateModuleId(moduleId);
|
|
2491
|
-
const moduleDef = registry.getModule(moduleId);
|
|
2492
|
-
if (!moduleDef) {
|
|
2493
|
-
process.stderr.write(
|
|
2494
|
-
`Error: Module '${moduleId}' not found.
|
|
2495
|
-
`
|
|
2496
|
-
);
|
|
2497
|
-
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
2498
|
-
}
|
|
2499
|
-
const fmt = resolveFormat(opts.format);
|
|
2500
|
-
void formatModuleDetail(moduleDef, fmt);
|
|
2501
|
-
});
|
|
2502
|
-
apcliGroup.addCommand(describeCmd);
|
|
2503
|
-
}
|
|
2504
|
-
function registerExecCommand(apcliGroup, registry, executor) {
|
|
2505
|
-
const execCmd = new Command2("exec").description("Execute a module by ID with JSON input.").argument("<module-id>", "Module ID to execute").option("--format <format>", "Output format (json, table, csv, yaml, jsonl).").option("--fields <fields>", "Comma-separated dot-paths to select from the result.").option(
|
|
2506
|
-
"--input <json>",
|
|
2507
|
-
"JSON object passed as input to the module. Use '-' to read JSON from stdin."
|
|
2508
|
-
).option("-y, --yes", "Auto-approve if the module declares requires_approval.", false).option(
|
|
2509
|
-
"--approval-timeout <seconds>",
|
|
2510
|
-
"Seconds to wait for interactive approval.",
|
|
2511
|
-
parseInt
|
|
2512
|
-
).option("--sandbox", "Run module in an isolated subprocess with restricted env.", false).option("--strategy <name>", "Execution strategy (standard, parallel, sequential, etc.).").option("--trace", "Enable pipeline trace output.", false).option("--dry-run", "Validate inputs without executing the module.", false).option("--stream", "Stream output as JSONL instead of buffering.", false).action(async (moduleId, opts) => {
|
|
2513
|
-
validateModuleId(moduleId);
|
|
2514
|
-
const moduleDef = registry.getModule(moduleId);
|
|
2515
|
-
if (!moduleDef) {
|
|
2516
|
-
process.stderr.write(`Error: Module '${moduleId}' not found.
|
|
2517
|
-
`);
|
|
2518
|
-
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
2519
|
-
}
|
|
2520
|
-
let merged = {};
|
|
2521
|
-
if (opts.input === "-") {
|
|
2522
|
-
merged = await collectInput("-", {}, false);
|
|
2523
|
-
} else if (opts.input !== void 0) {
|
|
2524
|
-
try {
|
|
2525
|
-
const parsed = JSON.parse(opts.input);
|
|
2526
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
2527
|
-
process.stderr.write("Error: --input JSON must be an object.\n");
|
|
2528
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2529
|
-
}
|
|
2530
|
-
merged = parsed;
|
|
2531
|
-
} catch (err) {
|
|
2532
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2533
|
-
process.stderr.write(`Error: --input is not valid JSON: ${msg}
|
|
2534
|
-
`);
|
|
2535
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
const startTime = performance.now();
|
|
2539
|
-
try {
|
|
2540
|
-
await checkApproval(moduleDef, opts.yes, opts.approvalTimeout);
|
|
2541
|
-
if (opts.dryRun) {
|
|
2542
|
-
if (executor.validate) {
|
|
2543
|
-
const preflight = await executor.validate(moduleId, merged);
|
|
2544
|
-
formatPreflightResult(preflight, opts.format);
|
|
2545
|
-
} else {
|
|
2546
|
-
process.stdout.write(JSON.stringify({ valid: true }) + "\n");
|
|
2547
|
-
}
|
|
2548
|
-
return;
|
|
2549
|
-
}
|
|
2550
|
-
let result;
|
|
2551
|
-
if ((opts.trace || opts.strategy) && executor.callWithTrace) {
|
|
2552
|
-
const [res] = await executor.callWithTrace(moduleId, merged, { strategy: opts.strategy });
|
|
2553
|
-
result = res;
|
|
2554
|
-
} else {
|
|
2555
|
-
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
2556
|
-
const sandbox = new Sandbox2(opts.sandbox);
|
|
2557
|
-
result = await sandbox.execute(moduleId, merged, executor);
|
|
2558
|
-
}
|
|
2559
|
-
const durationMs = Math.round(performance.now() - startTime);
|
|
2560
|
-
const fmt = resolveFormat(opts.format);
|
|
2561
|
-
formatExecResult(result, fmt, opts.fields);
|
|
2562
|
-
const auditLogger = getAuditLogger();
|
|
2563
|
-
if (auditLogger) {
|
|
2564
|
-
auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
|
|
2565
|
-
}
|
|
2566
|
-
} catch (err) {
|
|
2567
|
-
const exitCode = exitCodeForError(err);
|
|
2568
|
-
const durationMs = Math.round(performance.now() - startTime);
|
|
2569
|
-
try {
|
|
2570
|
-
const auditLogger = getAuditLogger();
|
|
2571
|
-
if (auditLogger) {
|
|
2572
|
-
auditLogger.logExecution(moduleId, merged, "error", exitCode, durationMs);
|
|
2573
|
-
}
|
|
2574
|
-
} catch {
|
|
2575
|
-
}
|
|
2576
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
|
|
2577
|
-
`);
|
|
2578
|
-
process.exit(exitCode);
|
|
2579
|
-
}
|
|
2580
|
-
});
|
|
2581
|
-
apcliGroup.addCommand(execCmd);
|
|
2582
|
-
}
|
|
2583
|
-
function registerValidateCommand(cli, registry, executor) {
|
|
2584
|
-
const validateCmd = new Command2("validate").description("Run preflight checks without executing a module.").argument("<module-id>", "Module ID to validate").option("--input <source>", "JSON input file or '-' for stdin.").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
2585
|
-
validateModuleId(moduleId);
|
|
2586
|
-
const moduleDef = registry.getModule(moduleId);
|
|
2587
|
-
if (!moduleDef) {
|
|
2588
|
-
process.stderr.write(`Error: Module '${moduleId}' not found.
|
|
2589
|
-
`);
|
|
2590
|
-
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
2591
|
-
}
|
|
2592
|
-
const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
|
|
2593
|
-
if (!executor.validate) {
|
|
2594
|
-
process.stderr.write("Error: Executor does not support validate.\n");
|
|
2595
|
-
process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
|
|
2596
|
-
}
|
|
2597
|
-
try {
|
|
2598
|
-
const preflight = await executor.validate(moduleId, merged);
|
|
2599
|
-
formatPreflightResult(preflight, opts.format);
|
|
2600
|
-
process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
|
|
2601
|
-
} catch (err) {
|
|
2602
|
-
const exitCode = exitCodeForError(err);
|
|
2603
|
-
try {
|
|
2604
|
-
const auditLogger = getAuditLogger();
|
|
2605
|
-
if (auditLogger) {
|
|
2606
|
-
auditLogger.logExecution(moduleId, merged, "error", exitCode, 0);
|
|
2607
|
-
}
|
|
2608
|
-
} catch {
|
|
2609
|
-
}
|
|
2610
|
-
process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
|
|
2611
|
-
`);
|
|
2612
|
-
process.exit(exitCode);
|
|
2613
|
-
}
|
|
2614
|
-
});
|
|
2615
|
-
cli.addCommand(validateCmd);
|
|
2616
|
-
}
|
|
2384
|
+
return result;
|
|
2385
|
+
};
|
|
2386
|
+
return new _ExposureFilter(
|
|
2387
|
+
mode,
|
|
2388
|
+
filterList(include, "include"),
|
|
2389
|
+
filterList(exclude, "exclude")
|
|
2390
|
+
);
|
|
2391
|
+
}
|
|
2392
|
+
};
|
|
2617
2393
|
|
|
2618
|
-
// src/
|
|
2394
|
+
// src/cli.ts
|
|
2395
|
+
init_logger();
|
|
2396
|
+
|
|
2397
|
+
// src/builtin-group.ts
|
|
2619
2398
|
init_esm_shims();
|
|
2620
|
-
import { Command as Command3 } from "commander";
|
|
2621
2399
|
init_errors();
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
}
|
|
2628
|
-
function emitResult(jsonPayload, fmt, ttyRender) {
|
|
2629
|
-
if (fmt === "json" || !process.stdout.isTTY) {
|
|
2630
|
-
process.stdout.write(JSON.stringify(jsonPayload, null, 2) + "\n");
|
|
2631
|
-
} else {
|
|
2632
|
-
ttyRender();
|
|
2400
|
+
init_logger();
|
|
2401
|
+
var ApcliGroupError = class extends Error {
|
|
2402
|
+
constructor(message) {
|
|
2403
|
+
super(message);
|
|
2404
|
+
this.name = "ApcliGroupError";
|
|
2633
2405
|
}
|
|
2406
|
+
};
|
|
2407
|
+
var DEFAULT_BUILTIN_GROUP_NAME = "apcli";
|
|
2408
|
+
var RESERVED_GROUP_NAMES = /* @__PURE__ */ new Set([DEFAULT_BUILTIN_GROUP_NAME]);
|
|
2409
|
+
var _effectiveReservedNames = RESERVED_GROUP_NAMES;
|
|
2410
|
+
function getReservedGroupNames() {
|
|
2411
|
+
return _effectiveReservedNames;
|
|
2634
2412
|
}
|
|
2635
|
-
function
|
|
2636
|
-
|
|
2637
|
-
`);
|
|
2638
|
-
process.exit(exitCodeForError(e));
|
|
2639
|
-
}
|
|
2640
|
-
async function requireApprovalForSystemCommand(moduleId, autoApprove) {
|
|
2641
|
-
const syntheticModuleDef = {
|
|
2642
|
-
id: moduleId,
|
|
2643
|
-
name: moduleId,
|
|
2644
|
-
description: `system command: ${moduleId}`,
|
|
2645
|
-
annotations: { requires_approval: true }
|
|
2646
|
-
};
|
|
2647
|
-
await checkApproval(syntheticModuleDef, autoApprove, void 0);
|
|
2413
|
+
function setReservedGroupNames(names) {
|
|
2414
|
+
_effectiveReservedNames = names;
|
|
2648
2415
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
return;
|
|
2655
|
-
}
|
|
2656
|
-
const total = summary.total_modules ?? modules.length;
|
|
2657
|
-
process.stdout.write(`Health Overview (${total} modules)
|
|
2658
|
-
|
|
2659
|
-
`);
|
|
2660
|
-
process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
|
|
2661
|
-
`);
|
|
2662
|
-
process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
|
|
2663
|
-
`);
|
|
2664
|
-
for (const m of modules) {
|
|
2665
|
-
const top = m.top_error;
|
|
2666
|
-
const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
|
|
2667
|
-
const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
|
|
2668
|
-
process.stdout.write(
|
|
2669
|
-
` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
|
|
2670
|
-
`
|
|
2416
|
+
var _NAME_REGEX = /^[a-z][a-z0-9_-]*$/;
|
|
2417
|
+
function _validateBuiltinGroupName(name) {
|
|
2418
|
+
if (!name || !_NAME_REGEX.test(name)) {
|
|
2419
|
+
throw new ApcliGroupError(
|
|
2420
|
+
`builtinGroupName ${JSON.stringify(name)} must match /^[a-z][a-z0-9_-]*$/ (non-empty, lowercase, alphanumeric + '_' / '-', leading letter).`
|
|
2671
2421
|
);
|
|
2672
2422
|
}
|
|
2673
|
-
const parts = [];
|
|
2674
|
-
for (const key of ["healthy", "degraded", "error"]) {
|
|
2675
|
-
const count = summary[key];
|
|
2676
|
-
if (count) parts.push(`${count} ${key}`);
|
|
2677
|
-
}
|
|
2678
|
-
process.stdout.write(`
|
|
2679
|
-
Summary: ${parts.join(", ") || "no data"}
|
|
2680
|
-
`);
|
|
2681
|
-
}
|
|
2682
|
-
function formatHealthModuleTty(result) {
|
|
2683
|
-
process.stdout.write(`Module: ${result.module_id ?? "?"}
|
|
2684
|
-
`);
|
|
2685
|
-
process.stdout.write(`Status: ${result.status ?? "unknown"}
|
|
2686
|
-
`);
|
|
2687
|
-
const total = result.total_calls ?? 0;
|
|
2688
|
-
const errors = result.error_count ?? 0;
|
|
2689
|
-
const rate = result.error_rate ?? 0;
|
|
2690
|
-
const avg = result.avg_latency_ms ?? 0;
|
|
2691
|
-
const p99 = result.p99_latency_ms ?? 0;
|
|
2692
|
-
process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
|
|
2693
|
-
`);
|
|
2694
|
-
process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
|
|
2695
|
-
`);
|
|
2696
|
-
const recent = result.recent_errors ?? [];
|
|
2697
|
-
if (recent.length > 0) {
|
|
2698
|
-
process.stdout.write(`
|
|
2699
|
-
Recent Errors (top ${recent.length}):
|
|
2700
|
-
`);
|
|
2701
|
-
for (const e of recent) {
|
|
2702
|
-
const count = e.count ?? "?";
|
|
2703
|
-
const last = e.last_occurred ?? "?";
|
|
2704
|
-
process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
|
|
2705
|
-
`);
|
|
2706
|
-
}
|
|
2707
|
-
}
|
|
2708
2423
|
}
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2424
|
+
var VALID_USER_MODES = /* @__PURE__ */ new Set([
|
|
2425
|
+
"all",
|
|
2426
|
+
"none",
|
|
2427
|
+
"include",
|
|
2428
|
+
"exclude"
|
|
2429
|
+
]);
|
|
2430
|
+
var APCLI_SUBCOMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
2431
|
+
"list",
|
|
2432
|
+
"describe",
|
|
2433
|
+
"exec",
|
|
2434
|
+
"validate",
|
|
2435
|
+
"init",
|
|
2436
|
+
"health",
|
|
2437
|
+
"usage",
|
|
2438
|
+
"enable",
|
|
2439
|
+
"disable",
|
|
2440
|
+
"reload",
|
|
2441
|
+
"config",
|
|
2442
|
+
"completion",
|
|
2443
|
+
"describe-pipeline"
|
|
2444
|
+
]);
|
|
2445
|
+
var ApcliGroup = class _ApcliGroup {
|
|
2446
|
+
_mode;
|
|
2447
|
+
_include;
|
|
2448
|
+
_exclude;
|
|
2449
|
+
_disableEnv;
|
|
2450
|
+
_registryInjected;
|
|
2451
|
+
_fromCliConfig;
|
|
2452
|
+
_name;
|
|
2453
|
+
constructor(init) {
|
|
2454
|
+
this._mode = init.mode;
|
|
2455
|
+
this._include = init.include;
|
|
2456
|
+
this._exclude = init.exclude;
|
|
2457
|
+
this._disableEnv = init.disableEnv;
|
|
2458
|
+
this._registryInjected = init.registryInjected;
|
|
2459
|
+
this._fromCliConfig = init.fromCliConfig;
|
|
2460
|
+
this._name = init.name;
|
|
2716
2461
|
}
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
`
|
|
2720
|
-
|
|
2721
|
-
`)
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2462
|
+
/**
|
|
2463
|
+
* Resolved name for the built-in command group (default `"apcli"`).
|
|
2464
|
+
* Overridable via createCli's `builtinGroupName` option for downstream
|
|
2465
|
+
* branded CLIs that want a custom namespace. Cross-SDK parity with
|
|
2466
|
+
* Python `ApcliGroup.name` (2026-05-08).
|
|
2467
|
+
*/
|
|
2468
|
+
get name() {
|
|
2469
|
+
return this._name;
|
|
2470
|
+
}
|
|
2471
|
+
/**
|
|
2472
|
+
* Tier 1 constructor — config came from `createCli({ apcli })`.
|
|
2473
|
+
*
|
|
2474
|
+
* A non-auto mode from this tier wins over env var and yaml.
|
|
2475
|
+
*/
|
|
2476
|
+
static fromCliConfig(config, opts) {
|
|
2477
|
+
return _ApcliGroup._build(
|
|
2478
|
+
config,
|
|
2479
|
+
opts,
|
|
2480
|
+
/*fromCliConfig*/
|
|
2481
|
+
true
|
|
2729
2482
|
);
|
|
2730
2483
|
}
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
const result = await callSystemModule(executor, "system.health.summary", {
|
|
2749
|
-
error_rate_threshold: opts.threshold,
|
|
2750
|
-
include_healthy: opts.all
|
|
2751
|
-
});
|
|
2752
|
-
emitResult(result, fmt, () => formatHealthSummaryTty(result));
|
|
2753
|
-
}
|
|
2754
|
-
} catch (e) {
|
|
2755
|
-
emitErrorAndExit(e);
|
|
2484
|
+
/**
|
|
2485
|
+
* Tier 3 constructor — config came from `apcore.yaml`.
|
|
2486
|
+
*
|
|
2487
|
+
* Env var (Tier 2) may override the yaml-supplied mode.
|
|
2488
|
+
*/
|
|
2489
|
+
static fromYaml(config, opts) {
|
|
2490
|
+
if (config !== null && config !== void 0 && typeof config !== "boolean" && (typeof config !== "object" || Array.isArray(config))) {
|
|
2491
|
+
const got = Array.isArray(config) ? "array" : typeof config;
|
|
2492
|
+
warn(
|
|
2493
|
+
`apcore.yaml apcli has unexpected type ${got}; using auto-detect.`
|
|
2494
|
+
);
|
|
2495
|
+
return _ApcliGroup._build(
|
|
2496
|
+
void 0,
|
|
2497
|
+
opts,
|
|
2498
|
+
/*fromCliConfig*/
|
|
2499
|
+
false
|
|
2500
|
+
);
|
|
2756
2501
|
}
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2502
|
+
return _ApcliGroup._build(
|
|
2503
|
+
config,
|
|
2504
|
+
opts,
|
|
2505
|
+
/*fromCliConfig*/
|
|
2506
|
+
false
|
|
2507
|
+
);
|
|
2508
|
+
}
|
|
2509
|
+
/**
|
|
2510
|
+
* Non-panicking Tier 3 factory (A-001 parity with Rust's `try_from_yaml`).
|
|
2511
|
+
* Returns `[instance, null]` on success or `[null, errorMessage]` on invalid input.
|
|
2512
|
+
* Use this in programmatic contexts where throwing/exiting is unwanted.
|
|
2513
|
+
*/
|
|
2514
|
+
static tryFromYaml(config, opts) {
|
|
2515
|
+
if (config !== null && config !== void 0 && typeof config !== "boolean" && (typeof config !== "object" || Array.isArray(config))) {
|
|
2516
|
+
const got = Array.isArray(config) ? "array" : typeof config;
|
|
2517
|
+
return [
|
|
2518
|
+
null,
|
|
2519
|
+
`apcore.yaml 'apcli:' must be a bool, object, or null; got ${got}`
|
|
2520
|
+
];
|
|
2521
|
+
}
|
|
2522
|
+
if (config !== null && config !== void 0 && typeof config === "object" && !Array.isArray(config)) {
|
|
2523
|
+
const mode = config["mode"];
|
|
2524
|
+
if (mode !== void 0 && mode !== null) {
|
|
2525
|
+
const validModes = ["all", "none", "include", "exclude"];
|
|
2526
|
+
if (typeof mode !== "string" || !validModes.includes(mode)) {
|
|
2527
|
+
return [null, `Invalid apcli mode: '${mode}'. Must be one of: all, none, include, exclude.`];
|
|
2780
2528
|
}
|
|
2781
|
-
}
|
|
2782
|
-
} catch (e) {
|
|
2783
|
-
emitErrorAndExit(e);
|
|
2529
|
+
}
|
|
2784
2530
|
}
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2531
|
+
return [_ApcliGroup.fromYaml(config, opts), null];
|
|
2532
|
+
}
|
|
2533
|
+
// -------------------------------------------------------------------------
|
|
2534
|
+
// Internal builder — shared by both factories
|
|
2535
|
+
// -------------------------------------------------------------------------
|
|
2536
|
+
static _build(config, opts, fromCliConfig) {
|
|
2537
|
+
const name = opts.name ?? DEFAULT_BUILTIN_GROUP_NAME;
|
|
2538
|
+
_validateBuiltinGroupName(name);
|
|
2539
|
+
if (config === true) {
|
|
2540
|
+
return new _ApcliGroup({
|
|
2541
|
+
mode: "all",
|
|
2542
|
+
include: [],
|
|
2543
|
+
exclude: [],
|
|
2544
|
+
disableEnv: false,
|
|
2545
|
+
registryInjected: opts.registryInjected,
|
|
2546
|
+
fromCliConfig,
|
|
2547
|
+
name
|
|
2802
2548
|
});
|
|
2803
|
-
} catch (e) {
|
|
2804
|
-
emitErrorAndExit(e);
|
|
2805
2549
|
}
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
module_id: moduleId,
|
|
2816
|
-
enabled: false,
|
|
2817
|
-
reason: opts.reason
|
|
2818
|
-
});
|
|
2819
|
-
emitResult(result, fmt, () => {
|
|
2820
|
-
process.stdout.write(`Module '${moduleId}' disabled.
|
|
2821
|
-
Reason: ${opts.reason}
|
|
2822
|
-
`);
|
|
2550
|
+
if (config === false) {
|
|
2551
|
+
return new _ApcliGroup({
|
|
2552
|
+
mode: "none",
|
|
2553
|
+
include: [],
|
|
2554
|
+
exclude: [],
|
|
2555
|
+
disableEnv: false,
|
|
2556
|
+
registryInjected: opts.registryInjected,
|
|
2557
|
+
fromCliConfig,
|
|
2558
|
+
name
|
|
2823
2559
|
});
|
|
2824
|
-
} catch (e) {
|
|
2825
|
-
emitErrorAndExit(e);
|
|
2826
2560
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
module_id: moduleId,
|
|
2837
|
-
reason: opts.reason
|
|
2838
|
-
});
|
|
2839
|
-
emitResult(result, fmt, () => {
|
|
2840
|
-
const prev = result.previous_version ?? "?";
|
|
2841
|
-
const newVer = result.new_version ?? "?";
|
|
2842
|
-
const dur = result.reload_duration_ms ?? "?";
|
|
2843
|
-
process.stdout.write(`Module '${moduleId}' reloaded.
|
|
2844
|
-
`);
|
|
2845
|
-
process.stdout.write(` Version: ${prev} -> ${newVer}
|
|
2846
|
-
`);
|
|
2847
|
-
process.stdout.write(` Duration: ${dur}ms
|
|
2848
|
-
`);
|
|
2561
|
+
if (config === void 0 || config === null) {
|
|
2562
|
+
return new _ApcliGroup({
|
|
2563
|
+
mode: "auto",
|
|
2564
|
+
include: [],
|
|
2565
|
+
exclude: [],
|
|
2566
|
+
disableEnv: false,
|
|
2567
|
+
registryInjected: opts.registryInjected,
|
|
2568
|
+
fromCliConfig,
|
|
2569
|
+
name
|
|
2849
2570
|
});
|
|
2850
|
-
} catch (e) {
|
|
2851
|
-
emitErrorAndExit(e);
|
|
2852
2571
|
}
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
}
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
const fmt = resolveFormat(opts.format);
|
|
2860
|
-
try {
|
|
2861
|
-
const result = await callSystemModule(executor, "system.config.get", { key });
|
|
2862
|
-
const value = result?.value ?? result;
|
|
2863
|
-
emitResult({ key, value }, fmt, () => {
|
|
2864
|
-
process.stdout.write(`${key} = ${JSON.stringify(value)}
|
|
2865
|
-
`);
|
|
2866
|
-
});
|
|
2867
|
-
} catch (e) {
|
|
2868
|
-
emitErrorAndExit(e);
|
|
2869
|
-
}
|
|
2870
|
-
});
|
|
2871
|
-
configGroup.addCommand(configGetCmd);
|
|
2872
|
-
const configSetCmd = new Command3("set").description("Update a runtime configuration value (audit-logged; client-side approval gate applies).").argument("<key>", "Configuration key (dot-path)").argument("<value>", "New value").requiredOption("--reason <reason>", "Reason for config change (required for audit).").option("-y, --yes", "Skip approval prompt (audit D11-B-001 cross-SDK parity).", false).option("--format <format>", "Output format.").action(async (key, value, opts) => {
|
|
2873
|
-
const fmt = resolveFormat(opts.format);
|
|
2874
|
-
let parsedValue;
|
|
2875
|
-
try {
|
|
2876
|
-
parsedValue = JSON.parse(value);
|
|
2877
|
-
} catch {
|
|
2878
|
-
parsedValue = value;
|
|
2572
|
+
if (typeof config !== "object" || Array.isArray(config)) {
|
|
2573
|
+
process.stderr.write(
|
|
2574
|
+
`Error: apcli config must be a boolean or object; got ${Array.isArray(config) ? "array" : typeof config}.
|
|
2575
|
+
`
|
|
2576
|
+
);
|
|
2577
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2879
2578
|
}
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
`
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
emitErrorAndExit(e);
|
|
2579
|
+
const cfg = config;
|
|
2580
|
+
let mode;
|
|
2581
|
+
if (cfg.mode === void 0 || cfg.mode === null) {
|
|
2582
|
+
mode = "auto";
|
|
2583
|
+
} else if (typeof cfg.mode !== "string") {
|
|
2584
|
+
process.stderr.write(
|
|
2585
|
+
`Error: apcli.mode must be a string; got ${typeof cfg.mode}. Expected one of all|none|include|exclude.
|
|
2586
|
+
`
|
|
2587
|
+
);
|
|
2588
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2589
|
+
} else if (!VALID_USER_MODES.has(cfg.mode)) {
|
|
2590
|
+
process.stderr.write(
|
|
2591
|
+
`Error: apcli.mode '${cfg.mode}' is invalid. Expected one of all|none|include|exclude.
|
|
2592
|
+
`
|
|
2593
|
+
);
|
|
2594
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2595
|
+
} else {
|
|
2596
|
+
mode = cfg.mode;
|
|
2899
2597
|
}
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
2911
|
-
const current = executor.describePipeline();
|
|
2912
|
-
if (current && current.name === strategyName) {
|
|
2913
|
-
return { info: current, isCurrent: true };
|
|
2598
|
+
const include = _ApcliGroup._normalizeList(cfg.include, "include");
|
|
2599
|
+
const exclude = _ApcliGroup._normalizeList(cfg.exclude, "exclude");
|
|
2600
|
+
const rawDisableEnv = cfg.disableEnv !== void 0 ? cfg.disableEnv : cfg["disable_env"];
|
|
2601
|
+
let disableEnv = false;
|
|
2602
|
+
if (rawDisableEnv !== void 0) {
|
|
2603
|
+
if (typeof rawDisableEnv === "boolean") {
|
|
2604
|
+
disableEnv = rawDisableEnv;
|
|
2605
|
+
} else {
|
|
2606
|
+
warn(
|
|
2607
|
+
`apcli.disable_env must be boolean; got ${typeof rawDisableEnv}. Treating as false.`
|
|
2608
|
+
);
|
|
2914
2609
|
}
|
|
2915
|
-
} catch {
|
|
2916
2610
|
}
|
|
2611
|
+
return new _ApcliGroup({
|
|
2612
|
+
mode,
|
|
2613
|
+
include,
|
|
2614
|
+
exclude,
|
|
2615
|
+
disableEnv,
|
|
2616
|
+
registryInjected: opts.registryInjected,
|
|
2617
|
+
fromCliConfig,
|
|
2618
|
+
name
|
|
2619
|
+
});
|
|
2917
2620
|
}
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2621
|
+
/**
|
|
2622
|
+
* Normalize an include/exclude list. Non-array → warn and return [].
|
|
2623
|
+
*
|
|
2624
|
+
* Unknown but well-formed entries emit a WARNING (spec §7 error table,
|
|
2625
|
+
* T-APCLI-25) but are retained in the returned list for forward-compat —
|
|
2626
|
+
* if apcore-cli later adds a subcommand named `foo`, existing configs
|
|
2627
|
+
* continue to work without a config change. At runtime, unknown names
|
|
2628
|
+
* simply never match any registered subcommand.
|
|
2629
|
+
*/
|
|
2630
|
+
static _normalizeList(raw, label) {
|
|
2631
|
+
if (raw === void 0 || raw === null) return [];
|
|
2632
|
+
if (!Array.isArray(raw)) {
|
|
2633
|
+
warn(`apcli.${label} must be a list; got ${typeof raw}. Ignoring.`);
|
|
2634
|
+
return [];
|
|
2926
2635
|
}
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
"module_lookup",
|
|
2935
|
-
"acl_check",
|
|
2936
|
-
"approval_gate",
|
|
2937
|
-
"middleware_before",
|
|
2938
|
-
"input_validation",
|
|
2939
|
-
"execute",
|
|
2940
|
-
"output_validation",
|
|
2941
|
-
"middleware_after",
|
|
2942
|
-
"return_result"
|
|
2943
|
-
],
|
|
2944
|
-
internal: [
|
|
2945
|
-
"context_creation",
|
|
2946
|
-
"call_chain_guard",
|
|
2947
|
-
"module_lookup",
|
|
2948
|
-
"middleware_before",
|
|
2949
|
-
"input_validation",
|
|
2950
|
-
"execute",
|
|
2951
|
-
"output_validation",
|
|
2952
|
-
"middleware_after",
|
|
2953
|
-
"return_result"
|
|
2954
|
-
],
|
|
2955
|
-
testing: [
|
|
2956
|
-
"context_creation",
|
|
2957
|
-
"module_lookup",
|
|
2958
|
-
"middleware_before",
|
|
2959
|
-
"input_validation",
|
|
2960
|
-
"execute",
|
|
2961
|
-
"output_validation",
|
|
2962
|
-
"middleware_after",
|
|
2963
|
-
"return_result"
|
|
2964
|
-
],
|
|
2965
|
-
performance: [
|
|
2966
|
-
"context_creation",
|
|
2967
|
-
"call_chain_guard",
|
|
2968
|
-
"module_lookup",
|
|
2969
|
-
"acl_check",
|
|
2970
|
-
"approval_gate",
|
|
2971
|
-
"input_validation",
|
|
2972
|
-
"execute",
|
|
2973
|
-
"output_validation",
|
|
2974
|
-
"return_result"
|
|
2975
|
-
],
|
|
2976
|
-
minimal: [
|
|
2977
|
-
"context_creation",
|
|
2978
|
-
"module_lookup",
|
|
2979
|
-
"execute",
|
|
2980
|
-
"return_result"
|
|
2981
|
-
]
|
|
2982
|
-
};
|
|
2983
|
-
function registerPipelineCommand(cli, executor) {
|
|
2984
|
-
const pipelineCmd = new Command4("describe-pipeline").description("Show the execution pipeline steps for a strategy.").addOption(
|
|
2985
|
-
new Option3("--strategy <name>", "Strategy to describe (default: standard).").choices(["standard", "internal", "testing", "performance", "minimal"]).default("standard")
|
|
2986
|
-
).option("--format <format>", "Output format.").action((opts) => {
|
|
2987
|
-
const fmt = resolveFormat(opts.format);
|
|
2988
|
-
const { info, isCurrent } = lookupStrategyInfo(executor, opts.strategy);
|
|
2989
|
-
if (info) {
|
|
2990
|
-
const strategySteps = isCurrent ? executor.currentStrategy?.steps ?? [] : [];
|
|
2991
|
-
const header = `Pipeline: ${info.name} (${info.stepCount} steps)`;
|
|
2992
|
-
if (fmt === "json" || !process.stdout.isTTY) {
|
|
2993
|
-
const payload = {
|
|
2994
|
-
strategy: info.name,
|
|
2995
|
-
step_count: info.stepCount,
|
|
2996
|
-
description: info.description,
|
|
2997
|
-
steps: info.stepNames.map((name, i) => {
|
|
2998
|
-
const stepMeta = strategySteps[i];
|
|
2999
|
-
return {
|
|
3000
|
-
index: i + 1,
|
|
3001
|
-
name,
|
|
3002
|
-
pure: stepMeta?.pure ?? false,
|
|
3003
|
-
removable: stepMeta?.removable ?? true,
|
|
3004
|
-
timeout_ms: stepMeta?.timeoutMs ?? null
|
|
3005
|
-
};
|
|
3006
|
-
})
|
|
3007
|
-
};
|
|
3008
|
-
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
3009
|
-
} else {
|
|
3010
|
-
process.stdout.write(`${header}
|
|
3011
|
-
|
|
3012
|
-
`);
|
|
3013
|
-
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
3014
|
-
`);
|
|
3015
|
-
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
3016
|
-
`);
|
|
3017
|
-
for (let i = 0; i < info.stepNames.length; i++) {
|
|
3018
|
-
const stepMeta = strategySteps[i];
|
|
3019
|
-
const pure = stepMeta?.pure ? "yes" : "no";
|
|
3020
|
-
const removable = stepMeta?.removable !== false ? "yes" : "no";
|
|
3021
|
-
const timeout = stepMeta?.timeoutMs ? `${stepMeta.timeoutMs}ms` : "\u2014";
|
|
3022
|
-
process.stdout.write(` ${String(i + 1).padEnd(4)} ${info.stepNames[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
|
|
3023
|
-
`);
|
|
2636
|
+
const out = [];
|
|
2637
|
+
for (const entry of raw) {
|
|
2638
|
+
if (typeof entry === "string" && entry.length > 0) {
|
|
2639
|
+
if (!APCLI_SUBCOMMAND_NAMES.has(entry)) {
|
|
2640
|
+
warn(
|
|
2641
|
+
`Unknown apcli subcommand '${entry}' in ${label} list \u2014 ignoring.`
|
|
2642
|
+
);
|
|
3024
2643
|
}
|
|
2644
|
+
out.push(entry);
|
|
2645
|
+
} else {
|
|
2646
|
+
warn(`apcli.${label} contains non-string entry; skipping.`);
|
|
3025
2647
|
}
|
|
3026
|
-
return;
|
|
3027
2648
|
}
|
|
3028
|
-
|
|
3029
|
-
const pureSteps = /* @__PURE__ */ new Set([
|
|
3030
|
-
"context_creation",
|
|
3031
|
-
"call_chain_guard",
|
|
3032
|
-
"module_lookup",
|
|
3033
|
-
"acl_check",
|
|
3034
|
-
"input_validation"
|
|
3035
|
-
]);
|
|
3036
|
-
const nonRemovable = /* @__PURE__ */ new Set([
|
|
3037
|
-
"context_creation",
|
|
3038
|
-
"module_lookup",
|
|
3039
|
-
"execute",
|
|
3040
|
-
"return_result"
|
|
3041
|
-
]);
|
|
3042
|
-
if (fmt === "json" || !process.stdout.isTTY) {
|
|
3043
|
-
const payload = {
|
|
3044
|
-
strategy: opts.strategy,
|
|
3045
|
-
step_count: steps.length,
|
|
3046
|
-
steps: steps.map((s, i) => ({
|
|
3047
|
-
index: i + 1,
|
|
3048
|
-
name: s,
|
|
3049
|
-
pure: pureSteps.has(s),
|
|
3050
|
-
removable: !nonRemovable.has(s)
|
|
3051
|
-
}))
|
|
3052
|
-
};
|
|
3053
|
-
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
3054
|
-
} else {
|
|
3055
|
-
process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
|
|
3056
|
-
|
|
3057
|
-
`);
|
|
3058
|
-
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
3059
|
-
`);
|
|
3060
|
-
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
3061
|
-
`);
|
|
3062
|
-
for (let i = 0; i < steps.length; i++) {
|
|
3063
|
-
const pure = pureSteps.has(steps[i]) ? "yes" : "no";
|
|
3064
|
-
const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
|
|
3065
|
-
process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
|
|
3066
|
-
`);
|
|
3067
|
-
}
|
|
3068
|
-
}
|
|
3069
|
-
});
|
|
3070
|
-
cli.addCommand(pipelineCmd);
|
|
3071
|
-
}
|
|
3072
|
-
|
|
3073
|
-
// src/builtin-group.ts
|
|
3074
|
-
init_esm_shims();
|
|
3075
|
-
init_errors();
|
|
3076
|
-
init_logger();
|
|
3077
|
-
var ApcliGroupError = class extends Error {
|
|
3078
|
-
constructor(message) {
|
|
3079
|
-
super(message);
|
|
3080
|
-
this.name = "ApcliGroupError";
|
|
3081
|
-
}
|
|
3082
|
-
};
|
|
3083
|
-
var DEFAULT_BUILTIN_GROUP_NAME = "apcli";
|
|
3084
|
-
var RESERVED_GROUP_NAMES = /* @__PURE__ */ new Set([DEFAULT_BUILTIN_GROUP_NAME]);
|
|
3085
|
-
var _effectiveReservedNames = RESERVED_GROUP_NAMES;
|
|
3086
|
-
function getReservedGroupNames() {
|
|
3087
|
-
return _effectiveReservedNames;
|
|
3088
|
-
}
|
|
3089
|
-
function setReservedGroupNames(names) {
|
|
3090
|
-
_effectiveReservedNames = names;
|
|
3091
|
-
}
|
|
3092
|
-
var _NAME_REGEX = /^[a-z][a-z0-9_-]*$/;
|
|
3093
|
-
function _validateBuiltinGroupName(name) {
|
|
3094
|
-
if (!name || !_NAME_REGEX.test(name)) {
|
|
3095
|
-
throw new ApcliGroupError(
|
|
3096
|
-
`builtinGroupName ${JSON.stringify(name)} must match /^[a-z][a-z0-9_-]*$/ (non-empty, lowercase, alphanumeric + '_' / '-', leading letter).`
|
|
3097
|
-
);
|
|
3098
|
-
}
|
|
3099
|
-
}
|
|
3100
|
-
var VALID_USER_MODES = /* @__PURE__ */ new Set([
|
|
3101
|
-
"all",
|
|
3102
|
-
"none",
|
|
3103
|
-
"include",
|
|
3104
|
-
"exclude"
|
|
3105
|
-
]);
|
|
3106
|
-
var APCLI_SUBCOMMAND_NAMES = /* @__PURE__ */ new Set([
|
|
3107
|
-
"list",
|
|
3108
|
-
"describe",
|
|
3109
|
-
"exec",
|
|
3110
|
-
"validate",
|
|
3111
|
-
"init",
|
|
3112
|
-
"health",
|
|
3113
|
-
"usage",
|
|
3114
|
-
"enable",
|
|
3115
|
-
"disable",
|
|
3116
|
-
"reload",
|
|
3117
|
-
"config",
|
|
3118
|
-
"completion",
|
|
3119
|
-
"describe-pipeline"
|
|
3120
|
-
]);
|
|
3121
|
-
var ApcliGroup = class _ApcliGroup {
|
|
3122
|
-
_mode;
|
|
3123
|
-
_include;
|
|
3124
|
-
_exclude;
|
|
3125
|
-
_disableEnv;
|
|
3126
|
-
_registryInjected;
|
|
3127
|
-
_fromCliConfig;
|
|
3128
|
-
_name;
|
|
3129
|
-
constructor(init) {
|
|
3130
|
-
this._mode = init.mode;
|
|
3131
|
-
this._include = init.include;
|
|
3132
|
-
this._exclude = init.exclude;
|
|
3133
|
-
this._disableEnv = init.disableEnv;
|
|
3134
|
-
this._registryInjected = init.registryInjected;
|
|
3135
|
-
this._fromCliConfig = init.fromCliConfig;
|
|
3136
|
-
this._name = init.name;
|
|
2649
|
+
return out;
|
|
3137
2650
|
}
|
|
2651
|
+
// -------------------------------------------------------------------------
|
|
2652
|
+
// Public API
|
|
2653
|
+
// -------------------------------------------------------------------------
|
|
3138
2654
|
/**
|
|
3139
|
-
*
|
|
3140
|
-
*
|
|
3141
|
-
*
|
|
3142
|
-
*
|
|
2655
|
+
* Resolve effective visibility mode after applying tier precedence.
|
|
2656
|
+
*
|
|
2657
|
+
* Returns one of `"all" | "none" | "include" | "exclude"` — never `"auto"`.
|
|
2658
|
+
*
|
|
2659
|
+
* Tier order (spec §4.4):
|
|
2660
|
+
* 1. CliConfig non-auto wins outright.
|
|
2661
|
+
* 2. `APCORE_CLI_APCLI` env var (unless sealed by disableEnv).
|
|
2662
|
+
* 3. yaml non-auto.
|
|
2663
|
+
* 4. Auto-detect from registryInjected.
|
|
3143
2664
|
*/
|
|
3144
|
-
|
|
3145
|
-
|
|
2665
|
+
resolveVisibility() {
|
|
2666
|
+
if (this._fromCliConfig && this._mode !== "auto") {
|
|
2667
|
+
return this._mode;
|
|
2668
|
+
}
|
|
2669
|
+
if (!this._disableEnv) {
|
|
2670
|
+
const envMode = this._parseEnv(process.env.APCORE_CLI_APCLI);
|
|
2671
|
+
if (envMode !== null) {
|
|
2672
|
+
return envMode;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
if (this._mode !== "auto") {
|
|
2676
|
+
return this._mode;
|
|
2677
|
+
}
|
|
2678
|
+
return this._registryInjected ? "none" : "all";
|
|
3146
2679
|
}
|
|
3147
2680
|
/**
|
|
3148
|
-
*
|
|
2681
|
+
* True iff `subcommand` passes the include/exclude filter.
|
|
3149
2682
|
*
|
|
3150
|
-
*
|
|
2683
|
+
* Callers MUST first check {@link resolveVisibility} — this method throws
|
|
2684
|
+
* under modes `"all"` or `"none"` (caller bug per spec §4.6).
|
|
3151
2685
|
*/
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
|
|
2686
|
+
isSubcommandIncluded(subcommand) {
|
|
2687
|
+
const mode = this.resolveVisibility();
|
|
2688
|
+
if (mode === "include") return this._include.includes(subcommand);
|
|
2689
|
+
if (mode === "exclude") return !this._exclude.includes(subcommand);
|
|
2690
|
+
throw new Error(
|
|
2691
|
+
`isSubcommandIncluded called under mode '${mode}'; caller should bypass.`
|
|
3158
2692
|
);
|
|
3159
2693
|
}
|
|
2694
|
+
/** True iff the `apcli` group itself should appear in root `--help`. */
|
|
2695
|
+
isGroupVisible() {
|
|
2696
|
+
return this.resolveVisibility() !== "none";
|
|
2697
|
+
}
|
|
2698
|
+
// -------------------------------------------------------------------------
|
|
2699
|
+
// Env parser (Tier 2) — co-located per spec §4.4
|
|
2700
|
+
// -------------------------------------------------------------------------
|
|
3160
2701
|
/**
|
|
3161
|
-
*
|
|
2702
|
+
* Parse APCORE_CLI_APCLI. Case-insensitive.
|
|
3162
2703
|
*
|
|
3163
|
-
*
|
|
2704
|
+
* - `show` / `1` / `true` → `"all"`
|
|
2705
|
+
* - `hide` / `0` / `false` → `"none"`
|
|
2706
|
+
* - Empty / unset → `null`
|
|
2707
|
+
* - Anything else → warn and return `null`
|
|
3164
2708
|
*/
|
|
3165
|
-
|
|
3166
|
-
if (
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
return _ApcliGroup._build(
|
|
3172
|
-
void 0,
|
|
3173
|
-
opts,
|
|
3174
|
-
/*fromCliConfig*/
|
|
3175
|
-
false
|
|
3176
|
-
);
|
|
2709
|
+
_parseEnv(raw) {
|
|
2710
|
+
if (raw === void 0 || raw === "") return null;
|
|
2711
|
+
const normalized = raw.trim().toLowerCase();
|
|
2712
|
+
if (normalized === "") return null;
|
|
2713
|
+
if (normalized === "show" || normalized === "1" || normalized === "true") {
|
|
2714
|
+
return "all";
|
|
3177
2715
|
}
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
false
|
|
2716
|
+
if (normalized === "hide" || normalized === "0" || normalized === "false") {
|
|
2717
|
+
return "none";
|
|
2718
|
+
}
|
|
2719
|
+
warn(
|
|
2720
|
+
`Unknown APCORE_CLI_APCLI value '${raw}', ignoring. Expected: show, hide, 1, 0, true, false.`
|
|
3183
2721
|
);
|
|
2722
|
+
return null;
|
|
2723
|
+
}
|
|
2724
|
+
};
|
|
2725
|
+
|
|
2726
|
+
// src/cli.ts
|
|
2727
|
+
init_errors();
|
|
2728
|
+
function listAllDefinitions(registry) {
|
|
2729
|
+
const defs = [];
|
|
2730
|
+
for (const id of registry.list()) {
|
|
2731
|
+
const def = registry.getDefinition(id);
|
|
2732
|
+
if (def) defs.push(def);
|
|
2733
|
+
}
|
|
2734
|
+
return defs;
|
|
2735
|
+
}
|
|
2736
|
+
function assertNotReserved(kind, name, moduleId) {
|
|
2737
|
+
if (!getReservedGroupNames().has(name)) return;
|
|
2738
|
+
let msg;
|
|
2739
|
+
if (kind === "group") {
|
|
2740
|
+
msg = `Error: Module '${moduleId}': display.cli.group '${name}' is reserved. Use a different CLI alias or set display.cli.group to another value.
|
|
2741
|
+
`;
|
|
2742
|
+
} else if (kind === "auto-group") {
|
|
2743
|
+
msg = `Error: Module '${moduleId}': auto-group '${name}' is reserved. Rename the module id or set display.cli.group to another value.
|
|
2744
|
+
`;
|
|
2745
|
+
} else {
|
|
2746
|
+
msg = `Error: Module '${moduleId}': top-level CLI name '${name}' is reserved. Use a different CLI alias.
|
|
2747
|
+
`;
|
|
2748
|
+
}
|
|
2749
|
+
process.stderr.write(msg);
|
|
2750
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2751
|
+
}
|
|
2752
|
+
var LazyModuleGroup = class {
|
|
2753
|
+
registry;
|
|
2754
|
+
executor;
|
|
2755
|
+
helpTextMaxLength;
|
|
2756
|
+
commandCache = /* @__PURE__ */ new Map();
|
|
2757
|
+
/** alias -> canonical module_id (populated lazily) */
|
|
2758
|
+
aliasMap = /* @__PURE__ */ new Map();
|
|
2759
|
+
/** module_id -> descriptor cache (populated during alias map build) */
|
|
2760
|
+
descriptorCache = /* @__PURE__ */ new Map();
|
|
2761
|
+
aliasMapBuilt = false;
|
|
2762
|
+
constructor(registry, executor, helpTextMaxLength = 1e3) {
|
|
2763
|
+
this.registry = registry;
|
|
2764
|
+
this.executor = executor;
|
|
2765
|
+
this.helpTextMaxLength = helpTextMaxLength;
|
|
3184
2766
|
}
|
|
3185
2767
|
/**
|
|
3186
|
-
*
|
|
3187
|
-
* Returns `[instance, null]` on success or `[null, errorMessage]` on invalid input.
|
|
3188
|
-
* Use this in programmatic contexts where throwing/exiting is unwanted.
|
|
2768
|
+
* Build alias->module_id map from display overlay metadata.
|
|
3189
2769
|
*/
|
|
3190
|
-
|
|
3191
|
-
if (
|
|
3192
|
-
|
|
3193
|
-
return [
|
|
3194
|
-
null,
|
|
3195
|
-
`apcore.yaml 'apcli:' must be a bool, object, or null; got ${got}`
|
|
3196
|
-
];
|
|
2770
|
+
buildAliasMap() {
|
|
2771
|
+
if (this.aliasMapBuilt) {
|
|
2772
|
+
return;
|
|
3197
2773
|
}
|
|
3198
|
-
|
|
3199
|
-
const
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
3203
|
-
|
|
2774
|
+
try {
|
|
2775
|
+
for (const descriptor of listAllDefinitions(this.registry)) {
|
|
2776
|
+
const moduleId = descriptor.moduleId;
|
|
2777
|
+
this.descriptorCache.set(moduleId, descriptor);
|
|
2778
|
+
const display = getDisplay(descriptor);
|
|
2779
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
2780
|
+
const cliAlias = cliDisplay.alias;
|
|
2781
|
+
if (cliAlias && cliAlias !== moduleId) {
|
|
2782
|
+
this.aliasMap.set(cliAlias, moduleId);
|
|
3204
2783
|
}
|
|
3205
2784
|
}
|
|
2785
|
+
this.aliasMapBuilt = true;
|
|
2786
|
+
} catch {
|
|
2787
|
+
warn("Failed to build alias map from registry");
|
|
3206
2788
|
}
|
|
3207
|
-
return [_ApcliGroup.fromYaml(config, opts), null];
|
|
3208
2789
|
}
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
mode: "all",
|
|
3218
|
-
include: [],
|
|
3219
|
-
exclude: [],
|
|
3220
|
-
disableEnv: false,
|
|
3221
|
-
registryInjected: opts.registryInjected,
|
|
3222
|
-
fromCliConfig,
|
|
3223
|
-
name
|
|
3224
|
-
});
|
|
2790
|
+
/**
|
|
2791
|
+
* List all available command names from the Registry.
|
|
2792
|
+
*/
|
|
2793
|
+
listCommands() {
|
|
2794
|
+
this.buildAliasMap();
|
|
2795
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
2796
|
+
for (const [alias, moduleId] of this.aliasMap) {
|
|
2797
|
+
reverse.set(moduleId, alias);
|
|
3225
2798
|
}
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
2799
|
+
const moduleIds = listAllDefinitions(this.registry).map((m) => m.moduleId);
|
|
2800
|
+
const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
|
|
2801
|
+
return [...new Set(names)].sort();
|
|
2802
|
+
}
|
|
2803
|
+
/**
|
|
2804
|
+
* Get or lazily build a Commander Command for the given module.
|
|
2805
|
+
*/
|
|
2806
|
+
getCommand(cmdName) {
|
|
2807
|
+
if (this.commandCache.has(cmdName)) {
|
|
2808
|
+
return this.commandCache.get(cmdName);
|
|
3236
2809
|
}
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
disableEnv: false,
|
|
3243
|
-
registryInjected: opts.registryInjected,
|
|
3244
|
-
fromCliConfig,
|
|
3245
|
-
name
|
|
3246
|
-
});
|
|
2810
|
+
this.buildAliasMap();
|
|
2811
|
+
const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
|
|
2812
|
+
let moduleDef = this.descriptorCache.get(moduleId);
|
|
2813
|
+
if (!moduleDef) {
|
|
2814
|
+
moduleDef = this.registry.getDefinition(moduleId) ?? void 0;
|
|
3247
2815
|
}
|
|
3248
|
-
if (
|
|
3249
|
-
|
|
3250
|
-
`Error: apcli config must be a boolean or object; got ${Array.isArray(config) ? "array" : typeof config}.
|
|
3251
|
-
`
|
|
3252
|
-
);
|
|
3253
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2816
|
+
if (!moduleDef) {
|
|
2817
|
+
return null;
|
|
3254
2818
|
}
|
|
3255
|
-
const
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
2819
|
+
const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
|
|
2820
|
+
this.commandCache.set(cmdName, cmd);
|
|
2821
|
+
return cmd;
|
|
2822
|
+
}
|
|
2823
|
+
};
|
|
2824
|
+
var LazyGroup = class {
|
|
2825
|
+
members;
|
|
2826
|
+
_executor;
|
|
2827
|
+
_helpTextMaxLength;
|
|
2828
|
+
_cmdCache = /* @__PURE__ */ new Map();
|
|
2829
|
+
command;
|
|
2830
|
+
constructor(members, executor, name, helpTextMaxLength = 1e3) {
|
|
2831
|
+
this.members = members;
|
|
2832
|
+
this._executor = executor;
|
|
2833
|
+
this._helpTextMaxLength = helpTextMaxLength;
|
|
2834
|
+
this.command = new Command2(name).description(`${name} commands`);
|
|
2835
|
+
for (const [cmdName, [, descriptor]] of this.members) {
|
|
2836
|
+
const cmd = buildModuleCommand(
|
|
2837
|
+
descriptor,
|
|
2838
|
+
this._executor,
|
|
2839
|
+
this._helpTextMaxLength,
|
|
2840
|
+
cmdName
|
|
3269
2841
|
);
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
mode = cfg.mode;
|
|
2842
|
+
this._cmdCache.set(cmdName, cmd);
|
|
2843
|
+
this.command.addCommand(cmd);
|
|
3273
2844
|
}
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
} else {
|
|
3282
|
-
warn(
|
|
3283
|
-
`apcli.disable_env must be boolean; got ${typeof rawDisableEnv}. Treating as false.`
|
|
3284
|
-
);
|
|
3285
|
-
}
|
|
2845
|
+
}
|
|
2846
|
+
listCommands() {
|
|
2847
|
+
return [...this.members.keys()].sort();
|
|
2848
|
+
}
|
|
2849
|
+
getCommand(cmdName) {
|
|
2850
|
+
if (this._cmdCache.has(cmdName)) {
|
|
2851
|
+
return this._cmdCache.get(cmdName);
|
|
3286
2852
|
}
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
2853
|
+
const entry = this.members.get(cmdName);
|
|
2854
|
+
if (!entry) {
|
|
2855
|
+
return null;
|
|
2856
|
+
}
|
|
2857
|
+
const [, descriptor] = entry;
|
|
2858
|
+
const cmd = buildModuleCommand(
|
|
2859
|
+
descriptor,
|
|
2860
|
+
this._executor,
|
|
2861
|
+
this._helpTextMaxLength,
|
|
2862
|
+
cmdName
|
|
2863
|
+
);
|
|
2864
|
+
this._cmdCache.set(cmdName, cmd);
|
|
2865
|
+
return cmd;
|
|
2866
|
+
}
|
|
2867
|
+
};
|
|
2868
|
+
var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
2869
|
+
/** groupName -> { cmdName -> [moduleId, descriptor] } */
|
|
2870
|
+
groupMap = /* @__PURE__ */ new Map();
|
|
2871
|
+
/** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
|
|
2872
|
+
topLevelModules = /* @__PURE__ */ new Map();
|
|
2873
|
+
/** Cached LazyGroup instances */
|
|
2874
|
+
groupCache = /* @__PURE__ */ new Map();
|
|
2875
|
+
groupMapBuilt = false;
|
|
2876
|
+
/** Exposure filter (FE-12) — controls which modules appear as CLI commands */
|
|
2877
|
+
exposureFilter;
|
|
2878
|
+
/** Effective group depth (CLAUDE.md v0.6.0): constructor arg > APCORE_CLI_GROUP_DEPTH env > 1. */
|
|
2879
|
+
groupDepth;
|
|
2880
|
+
constructor(registry, executor, helpTextMaxLength = 1e3, exposureFilter, groupDepth) {
|
|
2881
|
+
super(registry, executor, helpTextMaxLength);
|
|
2882
|
+
this.exposureFilter = exposureFilter ?? new ExposureFilter();
|
|
2883
|
+
this.groupDepth = _GroupedModuleGroup.resolveGroupDepth(groupDepth);
|
|
3296
2884
|
}
|
|
3297
2885
|
/**
|
|
3298
|
-
*
|
|
3299
|
-
*
|
|
3300
|
-
* Unknown but well-formed entries emit a WARNING (spec §7 error table,
|
|
3301
|
-
* T-APCLI-25) but are retained in the returned list for forward-compat —
|
|
3302
|
-
* if apcore-cli later adds a subcommand named `foo`, existing configs
|
|
3303
|
-
* continue to work without a config change. At runtime, unknown names
|
|
3304
|
-
* simply never match any registered subcommand.
|
|
2886
|
+
* Resolve group depth from constructor arg > APCORE_CLI_GROUP_DEPTH env > default 1.
|
|
2887
|
+
* Invalid env values (non-integer, non-positive) fall through to the default.
|
|
3305
2888
|
*/
|
|
3306
|
-
static
|
|
3307
|
-
if (
|
|
3308
|
-
|
|
3309
|
-
warn(`apcli.${label} must be a list; got ${typeof raw}. Ignoring.`);
|
|
3310
|
-
return [];
|
|
2889
|
+
static resolveGroupDepth(explicit) {
|
|
2890
|
+
if (explicit !== void 0 && Number.isFinite(explicit) && explicit > 0) {
|
|
2891
|
+
return Math.floor(explicit);
|
|
3311
2892
|
}
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
`Unknown apcli subcommand '${entry}' in ${label} list \u2014 ignoring.`
|
|
3318
|
-
);
|
|
3319
|
-
}
|
|
3320
|
-
out.push(entry);
|
|
3321
|
-
} else {
|
|
3322
|
-
warn(`apcli.${label} contains non-string entry; skipping.`);
|
|
2893
|
+
const raw = process.env.APCORE_CLI_GROUP_DEPTH;
|
|
2894
|
+
if (raw !== void 0 && raw !== "") {
|
|
2895
|
+
const parsed = parseInt(raw, 10);
|
|
2896
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
2897
|
+
return parsed;
|
|
3323
2898
|
}
|
|
3324
2899
|
}
|
|
3325
|
-
return
|
|
2900
|
+
return 1;
|
|
3326
2901
|
}
|
|
3327
|
-
// -------------------------------------------------------------------------
|
|
3328
|
-
// Public API
|
|
3329
|
-
// -------------------------------------------------------------------------
|
|
3330
2902
|
/**
|
|
3331
|
-
*
|
|
3332
|
-
*
|
|
3333
|
-
* Returns one of `"all" | "none" | "include" | "exclude"` — never `"auto"`.
|
|
2903
|
+
* Determine (groupName | null, commandName) for a module from its display overlay.
|
|
3334
2904
|
*
|
|
3335
|
-
*
|
|
3336
|
-
*
|
|
3337
|
-
*
|
|
3338
|
-
*
|
|
3339
|
-
* 4. Auto-detect from registryInjected.
|
|
2905
|
+
* @param groupDepth Number of dotted segments to consume as the group prefix.
|
|
2906
|
+
* Defaults to 1 (e.g., "math.add" → group="math", cmd="add").
|
|
2907
|
+
* Set to 2 for multi-level grouping (e.g., "math.trig.sin" →
|
|
2908
|
+
* group="math.trig", cmd="sin").
|
|
3340
2909
|
*/
|
|
3341
|
-
|
|
3342
|
-
if (
|
|
3343
|
-
|
|
2910
|
+
static resolveGroup(moduleId, descriptor, groupDepth = 1) {
|
|
2911
|
+
if (!moduleId) {
|
|
2912
|
+
warn("Empty module_id encountered in resolveGroup");
|
|
2913
|
+
return [null, ""];
|
|
3344
2914
|
}
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
2915
|
+
const display = getDisplay(descriptor);
|
|
2916
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
2917
|
+
const explicitGroup = cliDisplay.group;
|
|
2918
|
+
if (typeof explicitGroup === "string" && explicitGroup !== "") {
|
|
2919
|
+
return [explicitGroup, cliDisplay.alias ?? moduleId];
|
|
3350
2920
|
}
|
|
3351
|
-
if (
|
|
3352
|
-
return
|
|
2921
|
+
if (explicitGroup === "") {
|
|
2922
|
+
return [null, cliDisplay.alias ?? moduleId];
|
|
3353
2923
|
}
|
|
3354
|
-
|
|
2924
|
+
const cliName = cliDisplay.alias ?? moduleId;
|
|
2925
|
+
if (cliName.includes(".")) {
|
|
2926
|
+
const parts = cliName.split(".");
|
|
2927
|
+
const depth = Math.max(1, Math.min(groupDepth, parts.length - 1));
|
|
2928
|
+
const group = parts.slice(0, depth).join(".");
|
|
2929
|
+
const cmd = parts.slice(depth).join(".");
|
|
2930
|
+
return [group, cmd];
|
|
2931
|
+
}
|
|
2932
|
+
return [null, cliName];
|
|
3355
2933
|
}
|
|
3356
2934
|
/**
|
|
3357
|
-
*
|
|
2935
|
+
* Build the group map from registry modules.
|
|
3358
2936
|
*
|
|
3359
|
-
*
|
|
3360
|
-
*
|
|
2937
|
+
* FE-13: hard-fails with exit 2 when a module resolves to the reserved
|
|
2938
|
+
* `apcli` namespace in any of three ways — explicit `display.cli.group`,
|
|
2939
|
+
* auto-grouped dotted prefix, or top-level alias/id. See spec §4.10.
|
|
3361
2940
|
*/
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
if (mode === "exclude") return !this._exclude.includes(subcommand);
|
|
3366
|
-
throw new Error(
|
|
3367
|
-
`isSubcommandIncluded called under mode '${mode}'; caller should bypass.`
|
|
3368
|
-
);
|
|
3369
|
-
}
|
|
3370
|
-
/** True iff the `apcli` group itself should appear in root `--help`. */
|
|
3371
|
-
isGroupVisible() {
|
|
3372
|
-
return this.resolveVisibility() !== "none";
|
|
3373
|
-
}
|
|
3374
|
-
// -------------------------------------------------------------------------
|
|
3375
|
-
// Env parser (Tier 2) — co-located per spec §4.4
|
|
3376
|
-
// -------------------------------------------------------------------------
|
|
3377
|
-
/**
|
|
3378
|
-
* Parse APCORE_CLI_APCLI. Case-insensitive.
|
|
3379
|
-
*
|
|
3380
|
-
* - `show` / `1` / `true` → `"all"`
|
|
3381
|
-
* - `hide` / `0` / `false` → `"none"`
|
|
3382
|
-
* - Empty / unset → `null`
|
|
3383
|
-
* - Anything else → warn and return `null`
|
|
3384
|
-
*/
|
|
3385
|
-
_parseEnv(raw) {
|
|
3386
|
-
if (raw === void 0 || raw === "") return null;
|
|
3387
|
-
const normalized = raw.trim().toLowerCase();
|
|
3388
|
-
if (normalized === "") return null;
|
|
3389
|
-
if (normalized === "show" || normalized === "1" || normalized === "true") {
|
|
3390
|
-
return "all";
|
|
3391
|
-
}
|
|
3392
|
-
if (normalized === "hide" || normalized === "0" || normalized === "false") {
|
|
3393
|
-
return "none";
|
|
3394
|
-
}
|
|
3395
|
-
warn(
|
|
3396
|
-
`Unknown APCORE_CLI_APCLI value '${raw}', ignoring. Expected: show, hide, 1, 0, true, false.`
|
|
3397
|
-
);
|
|
3398
|
-
return null;
|
|
3399
|
-
}
|
|
3400
|
-
};
|
|
3401
|
-
|
|
3402
|
-
// src/exposure.ts
|
|
3403
|
-
init_esm_shims();
|
|
3404
|
-
init_logger();
|
|
3405
|
-
function escapeRegex(str) {
|
|
3406
|
-
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3407
|
-
}
|
|
3408
|
-
function compilePattern(pattern) {
|
|
3409
|
-
const sentinel = "\0GLOB\0";
|
|
3410
|
-
const escaped = pattern.replaceAll("**", sentinel);
|
|
3411
|
-
const parts = escaped.split("*");
|
|
3412
|
-
const regexParts = parts.map((p) => {
|
|
3413
|
-
const restored = p.replaceAll(sentinel, "**");
|
|
3414
|
-
return escapeRegex(restored);
|
|
3415
|
-
});
|
|
3416
|
-
let regex = regexParts.join("[^.]*");
|
|
3417
|
-
regex = regex.replaceAll("\\*\\*", ".+");
|
|
3418
|
-
return new RegExp(`^${regex}$`);
|
|
3419
|
-
}
|
|
3420
|
-
var ExposureFilter = class _ExposureFilter {
|
|
3421
|
-
static VALID_MODES = ["all", "include", "exclude", "none"];
|
|
3422
|
-
_mode;
|
|
3423
|
-
_compiledInclude;
|
|
3424
|
-
_compiledExclude;
|
|
3425
|
-
constructor(mode = "all", include, exclude) {
|
|
3426
|
-
if (!_ExposureFilter.VALID_MODES.includes(mode)) {
|
|
3427
|
-
process.stderr.write(
|
|
3428
|
-
`Warning: Unknown ExposureFilter mode '${mode}' \u2014 defaulting to 'none'. Valid modes: ${_ExposureFilter.VALID_MODES.join(", ")}.
|
|
3429
|
-
`
|
|
3430
|
-
);
|
|
3431
|
-
mode = "none";
|
|
3432
|
-
}
|
|
3433
|
-
this._mode = mode;
|
|
3434
|
-
const dedup = (arr) => [...new Set(arr)];
|
|
3435
|
-
this._compiledInclude = dedup(include ?? []).map(compilePattern);
|
|
3436
|
-
this._compiledExclude = dedup(exclude ?? []).map(compilePattern);
|
|
3437
|
-
}
|
|
3438
|
-
/** Return true if the module should be exposed as a CLI command. */
|
|
3439
|
-
isExposed(moduleId) {
|
|
3440
|
-
if (this._mode === "all") return true;
|
|
3441
|
-
if (this._mode === "include") {
|
|
3442
|
-
return this._compiledInclude.some((rx) => rx.test(moduleId));
|
|
3443
|
-
}
|
|
3444
|
-
if (this._mode === "exclude") {
|
|
3445
|
-
return !this._compiledExclude.some((rx) => rx.test(moduleId));
|
|
2941
|
+
buildGroupMap() {
|
|
2942
|
+
if (this.groupMapBuilt) {
|
|
2943
|
+
return;
|
|
3446
2944
|
}
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
(this.isExposed(
|
|
2945
|
+
this.buildAliasMap();
|
|
2946
|
+
for (const descriptor of listAllDefinitions(this.registry)) {
|
|
2947
|
+
const moduleId = descriptor.moduleId;
|
|
2948
|
+
const cached = this.descriptorCache.get(moduleId);
|
|
2949
|
+
if (!cached) {
|
|
2950
|
+
continue;
|
|
2951
|
+
}
|
|
2952
|
+
if (!this.exposureFilter.isExposed(moduleId)) {
|
|
2953
|
+
continue;
|
|
2954
|
+
}
|
|
2955
|
+
const display = getDisplay(cached);
|
|
2956
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
2957
|
+
const explicitGroup = typeof cliDisplay.group === "string" && cliDisplay.group !== "" ? cliDisplay.group : void 0;
|
|
2958
|
+
if (explicitGroup !== void 0) {
|
|
2959
|
+
assertNotReserved("group", explicitGroup, moduleId);
|
|
2960
|
+
}
|
|
2961
|
+
const [group, cmd] = _GroupedModuleGroup.resolveGroup(moduleId, cached, this.groupDepth);
|
|
2962
|
+
if (group !== null && explicitGroup === void 0) {
|
|
2963
|
+
assertNotReserved("auto-group", group, moduleId);
|
|
2964
|
+
}
|
|
2965
|
+
if (group === null) {
|
|
2966
|
+
assertNotReserved("top-level", cmd, moduleId);
|
|
2967
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
2968
|
+
} else if (!/^[a-z][a-z0-9_-]*(\.[a-z][a-z0-9_-]*)*$/.test(group)) {
|
|
2969
|
+
warn(
|
|
2970
|
+
`Module '${moduleId}': group name '${group}' is not shell-safe \u2014 treating as top-level.`
|
|
2971
|
+
);
|
|
2972
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
2973
|
+
} else {
|
|
2974
|
+
if (!this.groupMap.has(group)) {
|
|
2975
|
+
this.groupMap.set(group, /* @__PURE__ */ new Map());
|
|
2976
|
+
}
|
|
2977
|
+
this.groupMap.get(group).set(cmd, [moduleId, cached]);
|
|
2978
|
+
}
|
|
3455
2979
|
}
|
|
3456
|
-
|
|
2980
|
+
this.groupMapBuilt = true;
|
|
3457
2981
|
}
|
|
3458
2982
|
/**
|
|
3459
|
-
*
|
|
2983
|
+
* List all available command names: group names + top-level module names.
|
|
3460
2984
|
*
|
|
3461
|
-
*
|
|
2985
|
+
* FE-13: the built-in subcommand list is no longer folded in here — those
|
|
2986
|
+
* commands live under the `apcli` prefix and are registered directly by
|
|
2987
|
+
* `createCli`.
|
|
3462
2988
|
*/
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
2989
|
+
listCommands() {
|
|
2990
|
+
this.buildGroupMap();
|
|
2991
|
+
const reserved = getReservedGroupNames();
|
|
2992
|
+
const groupNames = [...this.groupMap.keys()].filter(
|
|
2993
|
+
(g) => !reserved.has(g)
|
|
2994
|
+
);
|
|
2995
|
+
const topNames = [...this.topLevelModules.keys()];
|
|
2996
|
+
return [.../* @__PURE__ */ new Set([...groupNames, ...topNames])].sort();
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* Get a command by name: check builtins -> group cache -> group map -> top-level modules.
|
|
3000
|
+
*/
|
|
3001
|
+
getCommand(cmdName) {
|
|
3002
|
+
this.buildGroupMap();
|
|
3003
|
+
if (this.groupCache.has(cmdName)) {
|
|
3004
|
+
return this.groupCache.get(cmdName).command;
|
|
3468
3005
|
}
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3006
|
+
if (this.groupMap.has(cmdName)) {
|
|
3007
|
+
const lazyGrp = new LazyGroup(
|
|
3008
|
+
this.groupMap.get(cmdName),
|
|
3009
|
+
this.executor,
|
|
3010
|
+
cmdName,
|
|
3011
|
+
this.helpTextMaxLength
|
|
3474
3012
|
);
|
|
3013
|
+
this.groupCache.set(cmdName, lazyGrp);
|
|
3014
|
+
return lazyGrp.command;
|
|
3475
3015
|
}
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
include = [];
|
|
3480
|
-
}
|
|
3481
|
-
let exclude = exposeObj.exclude ?? [];
|
|
3482
|
-
if (!Array.isArray(exclude)) {
|
|
3483
|
-
warn("Invalid 'expose.exclude' (expected list), ignoring.");
|
|
3484
|
-
exclude = [];
|
|
3485
|
-
}
|
|
3486
|
-
const filterList = (arr, label) => {
|
|
3487
|
-
const result = [];
|
|
3488
|
-
for (const p of arr) {
|
|
3489
|
-
if (!p) {
|
|
3490
|
-
warn(`Empty pattern in expose.${label}, skipping.`);
|
|
3491
|
-
} else {
|
|
3492
|
-
result.push(String(p));
|
|
3493
|
-
}
|
|
3016
|
+
if (this.topLevelModules.has(cmdName)) {
|
|
3017
|
+
if (this.commandCache.has(cmdName)) {
|
|
3018
|
+
return this.commandCache.get(cmdName);
|
|
3494
3019
|
}
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3020
|
+
const [, descriptor] = this.topLevelModules.get(cmdName);
|
|
3021
|
+
const cmd = buildModuleCommand(
|
|
3022
|
+
descriptor,
|
|
3023
|
+
this.executor,
|
|
3024
|
+
this.helpTextMaxLength,
|
|
3025
|
+
cmdName
|
|
3026
|
+
);
|
|
3027
|
+
this.commandCache.set(cmdName, cmd);
|
|
3028
|
+
return cmd;
|
|
3029
|
+
}
|
|
3030
|
+
return null;
|
|
3031
|
+
}
|
|
3032
|
+
/** Expose groupMap for testing. */
|
|
3033
|
+
getGroupMap() {
|
|
3034
|
+
return this.groupMap;
|
|
3035
|
+
}
|
|
3036
|
+
/** Expose topLevelModules for testing. */
|
|
3037
|
+
getTopLevelModules() {
|
|
3038
|
+
return this.topLevelModules;
|
|
3039
|
+
}
|
|
3040
|
+
/** Expose groupMapBuilt for testing. */
|
|
3041
|
+
isGroupMapBuilt() {
|
|
3042
|
+
return this.groupMapBuilt;
|
|
3502
3043
|
}
|
|
3503
3044
|
};
|
|
3504
3045
|
|
|
3505
|
-
// src/
|
|
3046
|
+
// src/discovery.ts
|
|
3047
|
+
init_errors();
|
|
3506
3048
|
init_audit();
|
|
3507
3049
|
|
|
3508
|
-
// src/
|
|
3050
|
+
// src/system-usage.ts
|
|
3509
3051
|
init_esm_shims();
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
}
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
function
|
|
3525
|
-
|
|
3526
|
-
const
|
|
3527
|
-
|
|
3528
|
-
|
|
3052
|
+
import * as fs4 from "fs";
|
|
3053
|
+
import * as os2 from "os";
|
|
3054
|
+
import * as path4 from "path";
|
|
3055
|
+
var PERIOD_TO_MS = {
|
|
3056
|
+
"1h": 60 * 60 * 1e3,
|
|
3057
|
+
"24h": 24 * 60 * 60 * 1e3,
|
|
3058
|
+
"7d": 7 * 24 * 60 * 60 * 1e3,
|
|
3059
|
+
"30d": 30 * 24 * 60 * 60 * 1e3
|
|
3060
|
+
};
|
|
3061
|
+
var DEFAULT_AUDIT_PATH = path4.join(
|
|
3062
|
+
os2.homedir(),
|
|
3063
|
+
".apcore-cli",
|
|
3064
|
+
"audit.jsonl"
|
|
3065
|
+
);
|
|
3066
|
+
function computeSummary(options = {}) {
|
|
3067
|
+
const auditPath = options.auditPath ?? DEFAULT_AUDIT_PATH;
|
|
3068
|
+
const period = options.period ?? "24h";
|
|
3069
|
+
const cutoff = (options.now ?? /* @__PURE__ */ new Date()).getTime() - PERIOD_TO_MS[period];
|
|
3070
|
+
if (!fs4.existsSync(auditPath)) {
|
|
3071
|
+
return /* @__PURE__ */ new Map();
|
|
3529
3072
|
}
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
const rest = [];
|
|
3536
|
-
for (const o of opts) {
|
|
3537
|
-
if (o.long === "--help") helpOpts.push(o);
|
|
3538
|
-
else if (o.long === "--version") versionOpts.push(o);
|
|
3539
|
-
else rest.push(o);
|
|
3073
|
+
let raw;
|
|
3074
|
+
try {
|
|
3075
|
+
raw = fs4.readFileSync(auditPath, "utf-8");
|
|
3076
|
+
} catch {
|
|
3077
|
+
return /* @__PURE__ */ new Map();
|
|
3540
3078
|
}
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
const
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3079
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3080
|
+
const errors = /* @__PURE__ */ new Map();
|
|
3081
|
+
const latencySum = /* @__PURE__ */ new Map();
|
|
3082
|
+
for (const line of raw.split("\n")) {
|
|
3083
|
+
const trimmed = line.trim();
|
|
3084
|
+
if (!trimmed) continue;
|
|
3085
|
+
let entry;
|
|
3086
|
+
try {
|
|
3087
|
+
entry = JSON.parse(trimmed);
|
|
3088
|
+
} catch {
|
|
3089
|
+
continue;
|
|
3090
|
+
}
|
|
3091
|
+
const ts = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : NaN;
|
|
3092
|
+
if (Number.isNaN(ts) || ts < cutoff) continue;
|
|
3093
|
+
const moduleId = typeof entry.module_id === "string" ? entry.module_id : null;
|
|
3094
|
+
if (!moduleId) continue;
|
|
3095
|
+
counts.set(moduleId, (counts.get(moduleId) ?? 0) + 1);
|
|
3096
|
+
if (entry.status === "error") {
|
|
3097
|
+
errors.set(moduleId, (errors.get(moduleId) ?? 0) + 1);
|
|
3098
|
+
}
|
|
3099
|
+
const duration = entry.duration_ms;
|
|
3100
|
+
if (typeof duration === "number") {
|
|
3101
|
+
latencySum.set(moduleId, (latencySum.get(moduleId) ?? 0) + duration);
|
|
3102
|
+
}
|
|
3559
3103
|
}
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
lines.push(` ${terms[i].padEnd(w)} ${sub.description()}`);
|
|
3104
|
+
const out = /* @__PURE__ */ new Map();
|
|
3105
|
+
for (const [id, calls] of counts) {
|
|
3106
|
+
out.set(id, {
|
|
3107
|
+
module_id: id,
|
|
3108
|
+
calls,
|
|
3109
|
+
errors: errors.get(id) ?? 0,
|
|
3110
|
+
latency_ms: calls > 0 ? (latencySum.get(id) ?? 0) / calls : 0
|
|
3568
3111
|
});
|
|
3569
|
-
sections.push(lines.join("\n"));
|
|
3570
3112
|
}
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3113
|
+
return out;
|
|
3114
|
+
}
|
|
3115
|
+
function sortModulesByUsage(modules, field, options = {}) {
|
|
3116
|
+
const reverse = options.reverse ?? true;
|
|
3117
|
+
const summary = computeSummary({
|
|
3118
|
+
auditPath: options.auditPath,
|
|
3119
|
+
period: options.period
|
|
3120
|
+
});
|
|
3121
|
+
const idOf = (m) => m.moduleId ?? m.id ?? m.module_id ?? "";
|
|
3122
|
+
if (summary.size === 0) {
|
|
3123
|
+
modules.sort((a, b) => idOf(a).localeCompare(idOf(b)));
|
|
3124
|
+
if (reverse) modules.reverse();
|
|
3125
|
+
return { used: false };
|
|
3579
3126
|
}
|
|
3580
|
-
const
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3127
|
+
const key = (m) => {
|
|
3128
|
+
const id = idOf(m);
|
|
3129
|
+
const s = summary.get(id);
|
|
3130
|
+
if (!s) return 0;
|
|
3131
|
+
return field === "latency" ? s.latency_ms : field === "calls" ? s.calls : s.errors;
|
|
3132
|
+
};
|
|
3133
|
+
modules.sort((a, b) => {
|
|
3134
|
+
const diff = key(a) - key(b);
|
|
3135
|
+
if (diff !== 0) return reverse ? -diff : diff;
|
|
3136
|
+
return idOf(a).localeCompare(idOf(b));
|
|
3137
|
+
});
|
|
3138
|
+
return { used: true };
|
|
3585
3139
|
}
|
|
3586
3140
|
|
|
3587
|
-
// src/
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
var MAX_MODULE_ID_LENGTH = 192;
|
|
3592
|
-
function validateModuleId(moduleId) {
|
|
3593
|
-
if (moduleId.length > MAX_MODULE_ID_LENGTH) {
|
|
3594
|
-
process.stderr.write(
|
|
3595
|
-
`Error: Invalid module ID format: '${moduleId}'. Maximum length is ${MAX_MODULE_ID_LENGTH} characters.
|
|
3596
|
-
`
|
|
3597
|
-
);
|
|
3598
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3599
|
-
}
|
|
3600
|
-
if (!MODULE_ID_PATTERN.test(moduleId)) {
|
|
3141
|
+
// src/discovery.ts
|
|
3142
|
+
var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
3143
|
+
function validateTag(tag) {
|
|
3144
|
+
if (!TAG_PATTERN.test(tag)) {
|
|
3601
3145
|
process.stderr.write(
|
|
3602
|
-
`Error: Invalid
|
|
3146
|
+
`Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
|
|
3603
3147
|
`
|
|
3604
3148
|
);
|
|
3605
3149
|
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3606
3150
|
}
|
|
3607
3151
|
}
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
|
|
3611
|
-
var verboseHelp = false;
|
|
3612
|
-
function setAllOptionsHelp(allOptions) {
|
|
3613
|
-
verboseHelp = allOptions;
|
|
3614
|
-
}
|
|
3615
|
-
function setVerboseHelp(verbose) {
|
|
3616
|
-
setAllOptionsHelp(verbose);
|
|
3152
|
+
function collectTag(value, previous) {
|
|
3153
|
+
return previous.concat([value]);
|
|
3617
3154
|
}
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
docsUrl = url;
|
|
3155
|
+
function collectAnnotation(value, previous) {
|
|
3156
|
+
return previous.concat([value]);
|
|
3621
3157
|
}
|
|
3622
|
-
function
|
|
3623
|
-
|
|
3158
|
+
function getAnnotationFlag(moduleDef, flag) {
|
|
3159
|
+
const annotations = moduleDef.annotations;
|
|
3160
|
+
if (!annotations || typeof annotations !== "object") return false;
|
|
3161
|
+
const ann = annotations;
|
|
3162
|
+
const map = {
|
|
3163
|
+
"destructive": "destructive",
|
|
3164
|
+
"requires-approval": "requires_approval",
|
|
3165
|
+
"readonly": "readonly",
|
|
3166
|
+
"streaming": "streaming",
|
|
3167
|
+
"cacheable": "cacheable",
|
|
3168
|
+
"idempotent": "idempotent",
|
|
3169
|
+
"paginated": "paginated"
|
|
3170
|
+
};
|
|
3171
|
+
const attr = map[flag] ?? flag;
|
|
3172
|
+
return ann[attr] === true;
|
|
3624
3173
|
}
|
|
3625
|
-
function
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3174
|
+
function registerListCommand(apcliGroup, registry, exposureFilter) {
|
|
3175
|
+
const listCmd = new Command3("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--flat", "Show flat list (no grouping).", false).addOption(
|
|
3176
|
+
new Option2("--format <format>", "Output format.").choices(["table", "json", "csv", "yaml", "jsonl", "markdown", "skill"])
|
|
3177
|
+
).option("-s, --search <query>", "Filter by substring match on ID and description.").addOption(
|
|
3178
|
+
new Option2("--status <status>", "Filter by module status.").choices(["enabled", "disabled", "all"]).default("enabled")
|
|
3179
|
+
).option("-a, --annotation <flag>", "Filter by annotation flag (AND logic). Repeatable.", collectAnnotation, []).addOption(
|
|
3180
|
+
new Option2("--sort <field>", "Sort order.").choices(["id", "calls", "errors", "latency"]).default("id")
|
|
3181
|
+
).option("--reverse", "Reverse sort order.", false).option("--deprecated", "Include deprecated modules.", false).option("--deps", "Show dependency count column.", false).addOption(
|
|
3182
|
+
new Option2("--exposure <mode>", "Filter by exposure status.").choices(["exposed", "hidden", "all"]).default("exposed")
|
|
3183
|
+
).action((opts) => {
|
|
3184
|
+
for (const t of opts.tag) {
|
|
3185
|
+
validateTag(t);
|
|
3633
3186
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
);
|
|
3638
|
-
}
|
|
3639
|
-
return defaultValue;
|
|
3640
|
-
}
|
|
3641
|
-
function resolveStringOption(cliValue, envValue) {
|
|
3642
|
-
if (typeof cliValue === "string" && cliValue !== "") {
|
|
3643
|
-
return cliValue;
|
|
3644
|
-
}
|
|
3645
|
-
if (envValue !== void 0 && envValue !== "") {
|
|
3646
|
-
return envValue;
|
|
3647
|
-
}
|
|
3648
|
-
return void 0;
|
|
3649
|
-
}
|
|
3650
|
-
var VERSION = "0.0.0";
|
|
3651
|
-
try {
|
|
3652
|
-
const pkg = JSON.parse(readFileSync3(path5.resolve(__dirname2, "../package.json"), "utf-8"));
|
|
3653
|
-
VERSION = pkg.version;
|
|
3654
|
-
} catch {
|
|
3655
|
-
}
|
|
3656
|
-
function emitErrorJson(e, exitCode) {
|
|
3657
|
-
const err = e instanceof Error ? e : new Error(String(e));
|
|
3658
|
-
const errRecord = err;
|
|
3659
|
-
const code = errRecord.code ?? "UNKNOWN";
|
|
3660
|
-
const payload = {
|
|
3661
|
-
error: true,
|
|
3662
|
-
code,
|
|
3663
|
-
message: err.message,
|
|
3664
|
-
exit_code: exitCode
|
|
3665
|
-
};
|
|
3666
|
-
for (const field of ["details", "suggestion", "ai_guidance", "retryable", "user_fixable"]) {
|
|
3667
|
-
const val = errRecord[field];
|
|
3668
|
-
if (val !== void 0 && val !== null) {
|
|
3669
|
-
payload[field] = val;
|
|
3187
|
+
let modules = [];
|
|
3188
|
+
for (const m of listAllDefinitions(registry)) {
|
|
3189
|
+
modules.push(m);
|
|
3670
3190
|
}
|
|
3671
|
-
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
const code = errRecord.code;
|
|
3678
|
-
const header = code ? `Error [${code}]: ${err.message}` : `Error: ${err.message}`;
|
|
3679
|
-
process.stderr.write(header + "\n");
|
|
3680
|
-
const details = errRecord.details;
|
|
3681
|
-
if (details && typeof details === "object" && !Array.isArray(details)) {
|
|
3682
|
-
process.stderr.write("\n Details:\n");
|
|
3683
|
-
for (const [k, v] of Object.entries(details)) {
|
|
3684
|
-
process.stderr.write(` ${k}: ${v}
|
|
3685
|
-
`);
|
|
3191
|
+
if (opts.tag.length > 0) {
|
|
3192
|
+
const filterTags = new Set(opts.tag);
|
|
3193
|
+
modules = modules.filter((m) => {
|
|
3194
|
+
const mTags = m.tags ?? [];
|
|
3195
|
+
return [...filterTags].every((t) => mTags.includes(t));
|
|
3196
|
+
});
|
|
3686
3197
|
}
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
Suggestion: ${suggestion}
|
|
3692
|
-
`);
|
|
3693
|
-
}
|
|
3694
|
-
const retryable = errRecord.retryable;
|
|
3695
|
-
if (retryable !== void 0 && retryable !== null) {
|
|
3696
|
-
const label = retryable ? "Yes" : "No (same input will fail again)";
|
|
3697
|
-
process.stderr.write(` Retryable: ${label}
|
|
3698
|
-
`);
|
|
3699
|
-
}
|
|
3700
|
-
process.stderr.write(`
|
|
3701
|
-
Exit code: ${exitCode}
|
|
3702
|
-
`);
|
|
3703
|
-
}
|
|
3704
|
-
function createCli(extensionsDirOrOpts, progName, allOptions = false) {
|
|
3705
|
-
let extensionsDir;
|
|
3706
|
-
let registry;
|
|
3707
|
-
let executor;
|
|
3708
|
-
let extraCommands;
|
|
3709
|
-
let app;
|
|
3710
|
-
let expose;
|
|
3711
|
-
let apcliOption;
|
|
3712
|
-
let appVersion;
|
|
3713
|
-
let appDescription;
|
|
3714
|
-
let allowedPrefixes;
|
|
3715
|
-
let builtinGroupName;
|
|
3716
|
-
if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
|
|
3717
|
-
extensionsDir = extensionsDirOrOpts.extensionsDir;
|
|
3718
|
-
progName = extensionsDirOrOpts.progName ?? progName;
|
|
3719
|
-
allOptions = extensionsDirOrOpts.allOptions ?? extensionsDirOrOpts.verbose ?? allOptions;
|
|
3720
|
-
app = extensionsDirOrOpts.app;
|
|
3721
|
-
registry = extensionsDirOrOpts.registry;
|
|
3722
|
-
executor = extensionsDirOrOpts.executor;
|
|
3723
|
-
extraCommands = extensionsDirOrOpts.extraCommands;
|
|
3724
|
-
expose = extensionsDirOrOpts.expose;
|
|
3725
|
-
apcliOption = extensionsDirOrOpts.apcli;
|
|
3726
|
-
appVersion = extensionsDirOrOpts.version;
|
|
3727
|
-
appDescription = extensionsDirOrOpts.description;
|
|
3728
|
-
builtinGroupName = extensionsDirOrOpts.builtinGroupName;
|
|
3729
|
-
allowedPrefixes = extensionsDirOrOpts.allowedPrefixes;
|
|
3730
|
-
} else {
|
|
3731
|
-
extensionsDir = extensionsDirOrOpts;
|
|
3732
|
-
}
|
|
3733
|
-
verboseHelp = allOptions;
|
|
3734
|
-
registerConfigNamespace();
|
|
3735
|
-
try {
|
|
3736
|
-
const auditLogger = new AuditLogger();
|
|
3737
|
-
setAuditLogger(auditLogger);
|
|
3738
|
-
} catch {
|
|
3739
|
-
}
|
|
3740
|
-
const resolvedProgName = progName ?? path5.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
|
|
3741
|
-
const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
|
|
3742
|
-
setLogLevel(cliLogLevel);
|
|
3743
|
-
if (app && (registry || executor)) {
|
|
3744
|
-
process.stderr.write("Error: app is mutually exclusive with registry/executor\n");
|
|
3745
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3746
|
-
}
|
|
3747
|
-
if (app) {
|
|
3748
|
-
registry = app.registry;
|
|
3749
|
-
executor = app.executor;
|
|
3750
|
-
}
|
|
3751
|
-
if (executor && !registry) {
|
|
3752
|
-
process.stderr.write("Error: executor requires registry \u2014 pass both or neither\n");
|
|
3753
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3754
|
-
}
|
|
3755
|
-
if (executor && typeof executor.setApprovalHandler === "function") {
|
|
3756
|
-
try {
|
|
3757
|
-
const handler = new CliApprovalHandler(
|
|
3758
|
-
/*autoApprove*/
|
|
3759
|
-
false
|
|
3198
|
+
if (opts.search) {
|
|
3199
|
+
const query = opts.search.toLowerCase();
|
|
3200
|
+
modules = modules.filter(
|
|
3201
|
+
(m) => (m.moduleId ?? "").toLowerCase().includes(query) || (m.description ?? "").toLowerCase().includes(query)
|
|
3760
3202
|
);
|
|
3761
|
-
executor.setApprovalHandler(handler);
|
|
3762
|
-
} catch {
|
|
3763
3203
|
}
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
program.version(appVersion, "-V, --version", "Print version");
|
|
3769
|
-
}
|
|
3770
|
-
program.configureHelp({ formatHelp: canonicalFormatHelp });
|
|
3771
|
-
if (!registryInjected) {
|
|
3772
|
-
program.option("--extensions-dir <path>", "Path to extensions directory");
|
|
3773
|
-
program.option("--commands-dir <path>", "Path to convention-based commands directory");
|
|
3774
|
-
program.option("--binding <path>", "Path to binding.yaml for display overlay");
|
|
3775
|
-
}
|
|
3776
|
-
let apcliCfg;
|
|
3777
|
-
try {
|
|
3778
|
-
if (apcliOption instanceof ApcliGroup) {
|
|
3779
|
-
if (builtinGroupName !== void 0 && builtinGroupName !== "apcli" && apcliOption.name !== builtinGroupName) {
|
|
3780
|
-
throw new Error(
|
|
3781
|
-
`builtinGroupName=${JSON.stringify(builtinGroupName)} conflicts with the name on the supplied ApcliGroup (${JSON.stringify(apcliOption.name)}). Pass only one.`
|
|
3782
|
-
);
|
|
3783
|
-
}
|
|
3784
|
-
apcliCfg = apcliOption;
|
|
3785
|
-
} else if (apcliOption !== void 0) {
|
|
3786
|
-
apcliCfg = ApcliGroup.fromCliConfig(apcliOption, {
|
|
3787
|
-
registryInjected,
|
|
3788
|
-
name: builtinGroupName
|
|
3204
|
+
if (opts.status === "enabled") {
|
|
3205
|
+
modules = modules.filter((m) => {
|
|
3206
|
+
const enabled = m.enabled;
|
|
3207
|
+
return enabled !== false;
|
|
3789
3208
|
});
|
|
3790
|
-
} else {
|
|
3791
|
-
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
yamlVal = resolver.resolveObject("apcli");
|
|
3795
|
-
} catch {
|
|
3796
|
-
yamlVal = null;
|
|
3797
|
-
}
|
|
3798
|
-
apcliCfg = ApcliGroup.fromYaml(yamlVal, {
|
|
3799
|
-
registryInjected,
|
|
3800
|
-
name: builtinGroupName
|
|
3209
|
+
} else if (opts.status === "disabled") {
|
|
3210
|
+
modules = modules.filter((m) => {
|
|
3211
|
+
const enabled = m.enabled;
|
|
3212
|
+
return enabled === false;
|
|
3801
3213
|
});
|
|
3802
3214
|
}
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
setReservedGroupNames(/* @__PURE__ */ new Set([apcliCfg.name]));
|
|
3809
|
-
const apcliGroup = program.command(apcliCfg.name, { hidden: !apcliCfg.isGroupVisible() }).description("Built-in commands");
|
|
3810
|
-
if (registry) {
|
|
3811
|
-
program._registry = registry;
|
|
3812
|
-
if (executor) {
|
|
3813
|
-
program._executor = executor;
|
|
3814
|
-
}
|
|
3815
|
-
} else {
|
|
3816
|
-
const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
|
|
3817
|
-
void resolvedExtDir;
|
|
3818
|
-
}
|
|
3819
|
-
let exposureFilter;
|
|
3820
|
-
try {
|
|
3821
|
-
if (expose instanceof ExposureFilter) {
|
|
3822
|
-
exposureFilter = expose;
|
|
3823
|
-
} else if (typeof expose === "object" && expose !== null) {
|
|
3824
|
-
exposureFilter = ExposureFilter.fromConfig({ expose });
|
|
3825
|
-
} else {
|
|
3826
|
-
exposureFilter = new ExposureFilter();
|
|
3215
|
+
if (!opts.deprecated) {
|
|
3216
|
+
modules = modules.filter((m) => {
|
|
3217
|
+
const deprecated = m.deprecated;
|
|
3218
|
+
return deprecated !== true;
|
|
3219
|
+
});
|
|
3827
3220
|
}
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
`);
|
|
3832
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3833
|
-
}
|
|
3834
|
-
_registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter);
|
|
3835
|
-
program.addHelpText("after", [
|
|
3836
|
-
"",
|
|
3837
|
-
"Use --help --all-options to show all options (including built-in options).",
|
|
3838
|
-
"Use --help --man to display a formatted man page."
|
|
3839
|
-
].join("\n"));
|
|
3840
|
-
configureManHelp(program, resolvedProgName, appVersion ?? VERSION);
|
|
3841
|
-
if (extraCommands && extraCommands.length > 0) {
|
|
3842
|
-
const _reservedForExtra = /* @__PURE__ */ new Set([apcliCfg.name]);
|
|
3843
|
-
for (const cmd of extraCommands) {
|
|
3844
|
-
const cmdName = cmd.name();
|
|
3845
|
-
if (_reservedForExtra.has(cmdName)) {
|
|
3846
|
-
process.stderr.write(
|
|
3847
|
-
`Error: extraCommands name '${cmdName}' is reserved
|
|
3848
|
-
`
|
|
3849
|
-
);
|
|
3850
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3221
|
+
if (opts.annotation.length > 0) {
|
|
3222
|
+
for (const annFlag of opts.annotation) {
|
|
3223
|
+
modules = modules.filter((m) => getAnnotationFlag(m, annFlag));
|
|
3851
3224
|
}
|
|
3852
|
-
|
|
3853
|
-
|
|
3225
|
+
}
|
|
3226
|
+
if (opts.sort === "calls" || opts.sort === "errors" || opts.sort === "latency") {
|
|
3227
|
+
const { used } = sortModulesByUsage(modules, opts.sort, { reverse: !opts.reverse });
|
|
3228
|
+
if (!used) {
|
|
3854
3229
|
process.stderr.write(
|
|
3855
|
-
`
|
|
3230
|
+
`note: no usage data available for --sort ${opts.sort}; sorted by id. Run some modules first to populate ~/.apcore-cli/audit.jsonl.
|
|
3856
3231
|
`
|
|
3857
3232
|
);
|
|
3858
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3859
3233
|
}
|
|
3860
|
-
program.addCommand(cmd);
|
|
3861
|
-
}
|
|
3862
|
-
}
|
|
3863
|
-
program.hook("preAction", async (thisCommand) => {
|
|
3864
|
-
const opts = thisCommand.opts();
|
|
3865
|
-
const commandsDir = opts.commandsDir;
|
|
3866
|
-
const bindingPath = opts.binding;
|
|
3867
|
-
await applyToolkitIntegration(commandsDir, bindingPath, { allowedPrefixes });
|
|
3868
|
-
});
|
|
3869
|
-
return program;
|
|
3870
|
-
}
|
|
3871
|
-
var _ALWAYS_REGISTERED = /* @__PURE__ */ new Set(["exec"]);
|
|
3872
|
-
function _registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter) {
|
|
3873
|
-
const emitUnwiredError = () => {
|
|
3874
|
-
process.stderr.write(
|
|
3875
|
-
"Error: no module registry wired. In standalone mode, pass --extensions-dir <path> to enable module discovery.\n"
|
|
3876
|
-
);
|
|
3877
|
-
process.exit(EXIT_CODES.CONFIG_INVALID);
|
|
3878
|
-
};
|
|
3879
|
-
const effectiveRegistry = registry ?? {
|
|
3880
|
-
listModules: () => emitUnwiredError(),
|
|
3881
|
-
getModule: () => emitUnwiredError()
|
|
3882
|
-
};
|
|
3883
|
-
const TABLE = [
|
|
3884
|
-
{ name: "list", requiresExecutor: false, register: (g) => registerListCommand(g, effectiveRegistry, exposureFilter) },
|
|
3885
|
-
{ name: "describe", requiresExecutor: false, register: (g) => registerDescribeCommand(g, effectiveRegistry) },
|
|
3886
|
-
{ name: "exec", requiresExecutor: true, register: (g, _r, ex) => registerExecCommand(g, effectiveRegistry, ex) },
|
|
3887
|
-
{ name: "validate", requiresExecutor: true, register: (g, _r, ex) => registerValidateCommand(g, effectiveRegistry, ex) },
|
|
3888
|
-
{ name: "init", requiresExecutor: false, register: (g) => registerInitCommand(g) },
|
|
3889
|
-
{ name: "health", requiresExecutor: true, register: (g, _r, ex) => registerHealthCommand(g, ex) },
|
|
3890
|
-
{ name: "usage", requiresExecutor: true, register: (g, _r, ex) => registerUsageCommand(g, ex) },
|
|
3891
|
-
{ name: "enable", requiresExecutor: true, register: (g, _r, ex) => registerEnableCommand(g, ex) },
|
|
3892
|
-
{ name: "disable", requiresExecutor: true, register: (g, _r, ex) => registerDisableCommand(g, ex) },
|
|
3893
|
-
{ name: "reload", requiresExecutor: true, register: (g, _r, ex) => registerReloadCommand(g, ex) },
|
|
3894
|
-
{ name: "config", requiresExecutor: true, register: (g, _r, ex) => registerConfigCommand(g, ex) },
|
|
3895
|
-
{ name: "completion", requiresExecutor: false, register: (g) => registerCompletionCommand(g) },
|
|
3896
|
-
{ name: "describe-pipeline", requiresExecutor: true, register: (g, _r, ex) => registerPipelineCommand(g, ex) }
|
|
3897
|
-
];
|
|
3898
|
-
const mode = apcliCfg.resolveVisibility();
|
|
3899
|
-
for (const entry of TABLE) {
|
|
3900
|
-
let shouldRegister;
|
|
3901
|
-
if (mode === "all" || mode === "none") {
|
|
3902
|
-
shouldRegister = true;
|
|
3903
3234
|
} else {
|
|
3904
|
-
|
|
3235
|
+
modules.sort((a, b) => (a.moduleId ?? "").localeCompare(b.moduleId ?? ""));
|
|
3236
|
+
if (opts.reverse) {
|
|
3237
|
+
modules.reverse();
|
|
3238
|
+
}
|
|
3905
3239
|
}
|
|
3906
|
-
|
|
3907
|
-
if (
|
|
3908
|
-
if (
|
|
3909
|
-
|
|
3910
|
-
|
|
3911
|
-
);
|
|
3240
|
+
let showExposureCol = false;
|
|
3241
|
+
if (exposureFilter && opts.exposure !== "all") {
|
|
3242
|
+
if (opts.exposure === "exposed") {
|
|
3243
|
+
modules = modules.filter((m) => exposureFilter.isExposed(m.moduleId ?? ""));
|
|
3244
|
+
} else if (opts.exposure === "hidden") {
|
|
3245
|
+
modules = modules.filter((m) => !exposureFilter.isExposed(m.moduleId ?? ""));
|
|
3912
3246
|
}
|
|
3913
|
-
continue;
|
|
3914
3247
|
}
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
}
|
|
3918
|
-
var bindingDisplayMap = /* @__PURE__ */ new Map();
|
|
3919
|
-
function lookupBindingDisplay(moduleId) {
|
|
3920
|
-
return bindingDisplayMap.get(moduleId);
|
|
3921
|
-
}
|
|
3922
|
-
async function applyToolkitIntegration(commandsDir, bindingPath, options = {}) {
|
|
3923
|
-
if (!commandsDir && !bindingPath) {
|
|
3924
|
-
return;
|
|
3925
|
-
}
|
|
3926
|
-
let toolkit;
|
|
3927
|
-
try {
|
|
3928
|
-
const toolkitModule = "apcore-toolkit";
|
|
3929
|
-
toolkit = await import(
|
|
3930
|
-
/* @vite-ignore */
|
|
3931
|
-
toolkitModule
|
|
3932
|
-
);
|
|
3933
|
-
} catch {
|
|
3934
|
-
warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
|
|
3935
|
-
return;
|
|
3936
|
-
}
|
|
3937
|
-
if (commandsDir) {
|
|
3938
|
-
warn("Convention scanning not available in the TypeScript toolkit");
|
|
3939
|
-
}
|
|
3940
|
-
if (bindingPath) {
|
|
3941
|
-
try {
|
|
3942
|
-
await loadBindingDisplayOverlay(toolkit, bindingPath, options.allowedPrefixes);
|
|
3943
|
-
} catch (err) {
|
|
3944
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
3945
|
-
warn(`apcore-toolkit: failed to load binding '${bindingPath}': ${msg}`);
|
|
3248
|
+
if (opts.exposure === "all" && exposureFilter) {
|
|
3249
|
+
showExposureCol = true;
|
|
3946
3250
|
}
|
|
3947
|
-
|
|
3251
|
+
const fmt = resolveFormat(opts.format);
|
|
3252
|
+
const filterTagsArg = opts.tag.length > 0 ? opts.tag : void 0;
|
|
3253
|
+
void formatModuleList(modules, fmt, filterTagsArg, opts.deps, showExposureCol ? exposureFilter : void 0);
|
|
3254
|
+
});
|
|
3255
|
+
apcliGroup.addCommand(listCmd);
|
|
3948
3256
|
}
|
|
3949
|
-
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
const prefixes = allowedPrefixes && allowedPrefixes.length > 0 ? allowedPrefixes : null;
|
|
3960
|
-
const isTargetAllowed = (target) => {
|
|
3961
|
-
if (!prefixes) return true;
|
|
3962
|
-
if (typeof target !== "string" || target.length === 0) return true;
|
|
3963
|
-
return prefixes.some((p) => target.startsWith(p));
|
|
3964
|
-
};
|
|
3965
|
-
for (const mod of resolved) {
|
|
3966
|
-
if (!mod || typeof mod !== "object") continue;
|
|
3967
|
-
const entry = mod;
|
|
3968
|
-
const id = typeof entry.moduleId === "string" ? entry.moduleId : null;
|
|
3969
|
-
if (!id) continue;
|
|
3970
|
-
if (!isTargetAllowed(entry.target)) {
|
|
3971
|
-
warn(
|
|
3972
|
-
`apcore-toolkit: dropped binding entry '${id}' \u2014 target '${String(entry.target)}' is outside allowedPrefixes`
|
|
3257
|
+
function registerDescribeCommand(apcliGroup, registry) {
|
|
3258
|
+
const describeCmd = new Command3("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").addOption(
|
|
3259
|
+
new Option2("--format <format>", "Output format.").choices(["table", "json", "csv", "yaml", "jsonl", "markdown", "skill"])
|
|
3260
|
+
).action((moduleId, opts) => {
|
|
3261
|
+
validateModuleId(moduleId);
|
|
3262
|
+
const moduleDef = registry.getDefinition(moduleId);
|
|
3263
|
+
if (!moduleDef) {
|
|
3264
|
+
process.stderr.write(
|
|
3265
|
+
`Error: Module '${moduleId}' not found.
|
|
3266
|
+
`
|
|
3973
3267
|
);
|
|
3974
|
-
|
|
3975
|
-
}
|
|
3976
|
-
const meta = entry.metadata ?? {};
|
|
3977
|
-
const display = meta.display;
|
|
3978
|
-
if (display && typeof display === "object" && !Array.isArray(display)) {
|
|
3979
|
-
bindingDisplayMap.set(id, display);
|
|
3268
|
+
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
3980
3269
|
}
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
function main(progName) {
|
|
3984
|
-
verboseHelp = hasVerboseFlag();
|
|
3985
|
-
const program = createCli({
|
|
3986
|
-
progName,
|
|
3987
|
-
allOptions: verboseHelp,
|
|
3988
|
-
version: VERSION,
|
|
3989
|
-
description: `${progName ?? "apcore-cli"} \u2014 execute modules from the command line`
|
|
3270
|
+
const fmt = resolveFormat(opts.format);
|
|
3271
|
+
void formatModuleDetail(moduleDef, fmt);
|
|
3990
3272
|
});
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3273
|
+
apcliGroup.addCommand(describeCmd);
|
|
3274
|
+
}
|
|
3275
|
+
function registerExecCommand(apcliGroup, registry, executor) {
|
|
3276
|
+
const execCmd = new Command3("exec").description("Execute a module by ID with JSON input.").argument("<module-id>", "Module ID to execute").option("--format <format>", "Output format (json, table, csv, yaml, jsonl).").option("--fields <fields>", "Comma-separated dot-paths to select from the result.").option(
|
|
3277
|
+
"--input <json>",
|
|
3278
|
+
"JSON object passed as input to the module. Use '-' to read JSON from stdin."
|
|
3279
|
+
).option("-y, --yes", "Auto-approve if the module declares requires_approval.", false).option(
|
|
3280
|
+
"--approval-timeout <seconds>",
|
|
3281
|
+
"Seconds to wait for interactive approval.",
|
|
3282
|
+
parseInt
|
|
3283
|
+
).option("--sandbox", "Run module in an isolated subprocess with restricted env.", false).option("--strategy <name>", "Execution strategy (standard, parallel, sequential, etc.).").option("--trace", "Enable pipeline trace output.", false).option("--dry-run", "Validate inputs without executing the module.", false).option("--stream", "Stream output as JSONL instead of buffering.", false).action(async (moduleId, opts) => {
|
|
3284
|
+
validateModuleId(moduleId);
|
|
3285
|
+
const moduleDef = registry.getDefinition(moduleId);
|
|
3286
|
+
if (!moduleDef) {
|
|
3287
|
+
process.stderr.write(`Error: Module '${moduleId}' not found.
|
|
3288
|
+
`);
|
|
3289
|
+
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
3996
3290
|
}
|
|
3997
|
-
|
|
3998
|
-
if (
|
|
3999
|
-
|
|
3291
|
+
let merged = {};
|
|
3292
|
+
if (opts.input === "-") {
|
|
3293
|
+
merged = await collectInput("-", {}, false);
|
|
3294
|
+
} else if (opts.input !== void 0) {
|
|
3295
|
+
try {
|
|
3296
|
+
const parsed = JSON.parse(opts.input);
|
|
3297
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
3298
|
+
process.stderr.write("Error: --input JSON must be an object.\n");
|
|
3299
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3300
|
+
}
|
|
3301
|
+
merged = parsed;
|
|
3302
|
+
} catch (err) {
|
|
3303
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3304
|
+
process.stderr.write(`Error: --input is not valid JSON: ${msg}
|
|
4000
3305
|
`);
|
|
3306
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3307
|
+
}
|
|
4001
3308
|
}
|
|
4002
|
-
|
|
4003
|
-
}
|
|
4004
|
-
}
|
|
4005
|
-
function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
|
|
4006
|
-
const moduleId = moduleDef.id;
|
|
4007
|
-
let resolvedSchema = {};
|
|
4008
|
-
let schemaOptions = [];
|
|
4009
|
-
const display = getDisplay(moduleDef);
|
|
4010
|
-
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
4011
|
-
const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
|
|
4012
|
-
const cmdHelp = cliDisplay.description ?? moduleDef.description;
|
|
4013
|
-
const inputSchema = moduleDef.inputSchema;
|
|
4014
|
-
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
3309
|
+
const startTime = performance.now();
|
|
4015
3310
|
try {
|
|
4016
|
-
|
|
3311
|
+
await checkApproval(moduleDef, opts.yes, opts.approvalTimeout);
|
|
3312
|
+
if (opts.dryRun) {
|
|
3313
|
+
if (executor.validate) {
|
|
3314
|
+
const preflight = await executor.validate(moduleId, merged);
|
|
3315
|
+
formatPreflightResult(preflight, opts.format);
|
|
3316
|
+
} else {
|
|
3317
|
+
process.stdout.write(JSON.stringify({ valid: true }) + "\n");
|
|
3318
|
+
}
|
|
3319
|
+
return;
|
|
3320
|
+
}
|
|
3321
|
+
let result;
|
|
3322
|
+
if ((opts.trace || opts.strategy) && executor.callWithTrace) {
|
|
3323
|
+
const [res] = await executor.callWithTrace(moduleId, merged, { strategy: opts.strategy });
|
|
3324
|
+
result = res;
|
|
3325
|
+
} else {
|
|
3326
|
+
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
3327
|
+
const sandbox = new Sandbox2(opts.sandbox);
|
|
3328
|
+
result = await sandbox.execute(moduleId, merged, executor);
|
|
3329
|
+
}
|
|
3330
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
3331
|
+
const fmt = resolveFormat(opts.format);
|
|
3332
|
+
formatExecResult(result, fmt, opts.fields);
|
|
3333
|
+
const auditLogger = getAuditLogger();
|
|
3334
|
+
if (auditLogger) {
|
|
3335
|
+
auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
|
|
3336
|
+
}
|
|
4017
3337
|
} catch (err) {
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
3338
|
+
const exitCode = exitCodeForError(err);
|
|
3339
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
3340
|
+
try {
|
|
3341
|
+
const auditLogger = getAuditLogger();
|
|
3342
|
+
if (auditLogger) {
|
|
3343
|
+
auditLogger.logExecution(moduleId, merged, "error", exitCode, durationMs);
|
|
3344
|
+
}
|
|
3345
|
+
} catch {
|
|
4022
3346
|
}
|
|
4023
|
-
|
|
4024
|
-
process.stderr.write(`Error: ${err.message}
|
|
3347
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
|
|
4025
3348
|
`);
|
|
4026
|
-
|
|
4027
|
-
}
|
|
4028
|
-
resolvedSchema = inputSchema;
|
|
3349
|
+
process.exit(exitCode);
|
|
4029
3350
|
}
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
4034
|
-
const
|
|
4035
|
-
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
const streamOpt = new Option4("--stream", "Stream module output as JSONL (one JSON object per line, flushed immediately).").default(false);
|
|
4042
|
-
const strategyOpt = new Option4("--strategy <name>", "Execution pipeline strategy: standard (default), internal, testing, performance.").choices(["standard", "internal", "testing", "performance", "minimal"]);
|
|
4043
|
-
const approvalTimeoutOpt = new Option4("--approval-timeout <seconds>", "Override approval prompt timeout in seconds (default: 60).").argParser(parseInt);
|
|
4044
|
-
const approvalTokenOpt = new Option4("--approval-token <token>", "Resume a pending approval with the given token (for async approval flows).");
|
|
4045
|
-
if (!verbose) {
|
|
4046
|
-
inputOpt.hideHelp();
|
|
4047
|
-
yesOpt.hideHelp();
|
|
4048
|
-
largeInputOpt.hideHelp();
|
|
4049
|
-
formatOpt.hideHelp();
|
|
4050
|
-
fieldsOpt.hideHelp();
|
|
4051
|
-
dryRunOpt.hideHelp();
|
|
4052
|
-
traceOpt.hideHelp();
|
|
4053
|
-
streamOpt.hideHelp();
|
|
4054
|
-
strategyOpt.hideHelp();
|
|
4055
|
-
approvalTimeoutOpt.hideHelp();
|
|
4056
|
-
approvalTokenOpt.hideHelp();
|
|
4057
|
-
}
|
|
4058
|
-
cmd.addOption(inputOpt);
|
|
4059
|
-
cmd.addOption(yesOpt);
|
|
4060
|
-
cmd.addOption(largeInputOpt);
|
|
4061
|
-
cmd.addOption(formatOpt);
|
|
4062
|
-
cmd.addOption(fieldsOpt);
|
|
4063
|
-
cmd.addOption(sandboxOpt);
|
|
4064
|
-
cmd.addOption(dryRunOpt);
|
|
4065
|
-
cmd.addOption(traceOpt);
|
|
4066
|
-
cmd.addOption(streamOpt);
|
|
4067
|
-
cmd.addOption(strategyOpt);
|
|
4068
|
-
cmd.addOption(approvalTimeoutOpt);
|
|
4069
|
-
cmd.addOption(approvalTokenOpt);
|
|
4070
|
-
const footerParts = [];
|
|
4071
|
-
if (!verbose) {
|
|
4072
|
-
footerParts.push("Use --all-options to show all options (including built-in options).");
|
|
4073
|
-
}
|
|
4074
|
-
if (docsUrl) {
|
|
4075
|
-
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
|
|
4076
|
-
}
|
|
4077
|
-
if (footerParts.length > 0) {
|
|
4078
|
-
cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
|
|
4079
|
-
}
|
|
4080
|
-
for (const opt of schemaOptions) {
|
|
4081
|
-
if (opt.isBooleanFlag) {
|
|
4082
|
-
const flagBase = opt.name.replace(/_/g, "-");
|
|
4083
|
-
cmd.addOption(
|
|
4084
|
-
new Option4(`--${flagBase}`, opt.description).default(
|
|
4085
|
-
opt.defaultValue
|
|
4086
|
-
)
|
|
4087
|
-
);
|
|
4088
|
-
cmd.addOption(new Option4(`--no-${flagBase}`).hideHelp());
|
|
4089
|
-
} else if (opt.parseArg) {
|
|
4090
|
-
cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
|
|
4091
|
-
} else {
|
|
4092
|
-
cmd.option(opt.flags, opt.description, opt.defaultValue);
|
|
3351
|
+
});
|
|
3352
|
+
apcliGroup.addCommand(execCmd);
|
|
3353
|
+
}
|
|
3354
|
+
function registerValidateCommand(cli, registry, executor) {
|
|
3355
|
+
const validateCmd = new Command3("validate").description("Run preflight checks without executing a module.").argument("<module-id>", "Module ID to validate").option("--input <source>", "JSON input file or '-' for stdin.").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3356
|
+
validateModuleId(moduleId);
|
|
3357
|
+
const moduleDef = registry.getDefinition(moduleId);
|
|
3358
|
+
if (!moduleDef) {
|
|
3359
|
+
process.stderr.write(`Error: Module '${moduleId}' not found.
|
|
3360
|
+
`);
|
|
3361
|
+
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
4093
3362
|
}
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
const largeInput = options.largeInput;
|
|
4099
|
-
const outputFormat = options.format;
|
|
4100
|
-
const outputFields = options.fields;
|
|
4101
|
-
const sandboxEnabled = options.sandbox;
|
|
4102
|
-
const dryRun = options.dryRun;
|
|
4103
|
-
const traceFlag = options.trace;
|
|
4104
|
-
const streamFlag = options.stream;
|
|
4105
|
-
const strategyName = resolveStringOption(options.strategy, process.env.APCORE_CLI_STRATEGY);
|
|
4106
|
-
const approvalTimeout = resolveIntOption(
|
|
4107
|
-
options.approvalTimeout,
|
|
4108
|
-
process.env.APCORE_CLI_APPROVAL_TIMEOUT,
|
|
4109
|
-
60
|
|
4110
|
-
);
|
|
4111
|
-
const approvalToken = options.approvalToken;
|
|
4112
|
-
const schemaKwargs = {};
|
|
4113
|
-
for (const opt of schemaOptions) {
|
|
4114
|
-
const commanderKey = opt.name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
4115
|
-
if (commanderKey in options) {
|
|
4116
|
-
schemaKwargs[opt.name] = options[commanderKey];
|
|
4117
|
-
} else if (opt.name in options) {
|
|
4118
|
-
schemaKwargs[opt.name] = options[opt.name];
|
|
4119
|
-
}
|
|
3363
|
+
const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
|
|
3364
|
+
if (!executor.validate) {
|
|
3365
|
+
process.stderr.write("Error: Executor does not support validate.\n");
|
|
3366
|
+
process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
|
|
4120
3367
|
}
|
|
4121
|
-
let merged = {};
|
|
4122
|
-
const startTime = performance.now();
|
|
4123
3368
|
try {
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
|
|
4128
|
-
|
|
4129
|
-
|
|
4130
|
-
|
|
3369
|
+
const preflight = await executor.validate(moduleId, merged);
|
|
3370
|
+
formatPreflightResult(preflight, opts.format);
|
|
3371
|
+
process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
|
|
3372
|
+
} catch (err) {
|
|
3373
|
+
const exitCode = exitCodeForError(err);
|
|
3374
|
+
try {
|
|
3375
|
+
const auditLogger = getAuditLogger();
|
|
3376
|
+
if (auditLogger) {
|
|
3377
|
+
auditLogger.logExecution(moduleId, merged, "error", exitCode, 0);
|
|
4131
3378
|
}
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
const pureSteps = /* @__PURE__ */ new Set([
|
|
4136
|
-
"context_creation",
|
|
4137
|
-
"call_chain_guard",
|
|
4138
|
-
"module_lookup",
|
|
4139
|
-
"acl_check",
|
|
4140
|
-
"input_validation"
|
|
4141
|
-
]);
|
|
4142
|
-
const allSteps = [
|
|
4143
|
-
"context_creation",
|
|
4144
|
-
"call_chain_guard",
|
|
4145
|
-
"module_lookup",
|
|
4146
|
-
"acl_check",
|
|
4147
|
-
"approval_gate",
|
|
4148
|
-
"middleware_before",
|
|
4149
|
-
"input_validation",
|
|
4150
|
-
"execute",
|
|
4151
|
-
"output_validation",
|
|
4152
|
-
"middleware_after",
|
|
4153
|
-
"return_result"
|
|
4154
|
-
];
|
|
4155
|
-
process.stderr.write("\nPipeline preview (dry-run):\n");
|
|
4156
|
-
for (const s of allSteps) {
|
|
4157
|
-
if (pureSteps.has(s)) {
|
|
4158
|
-
process.stderr.write(` \u2713 ${s.padEnd(24)} (pure \u2014 would execute)
|
|
3379
|
+
} catch {
|
|
3380
|
+
}
|
|
3381
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
|
|
4159
3382
|
`);
|
|
4160
|
-
|
|
4161
|
-
|
|
3383
|
+
process.exit(exitCode);
|
|
3384
|
+
}
|
|
3385
|
+
});
|
|
3386
|
+
cli.addCommand(validateCmd);
|
|
3387
|
+
}
|
|
3388
|
+
|
|
3389
|
+
// src/system-cmd.ts
|
|
3390
|
+
init_esm_shims();
|
|
3391
|
+
import { Command as Command4 } from "commander";
|
|
3392
|
+
import { Config } from "apcore-js";
|
|
3393
|
+
init_errors();
|
|
3394
|
+
async function callSystemModule(executor, moduleId, inputs) {
|
|
3395
|
+
return executor.call(moduleId, inputs);
|
|
3396
|
+
}
|
|
3397
|
+
function emitResult(jsonPayload, fmt, ttyRender) {
|
|
3398
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
3399
|
+
process.stdout.write(JSON.stringify(jsonPayload, null, 2) + "\n");
|
|
3400
|
+
} else {
|
|
3401
|
+
ttyRender();
|
|
3402
|
+
}
|
|
3403
|
+
}
|
|
3404
|
+
function emitErrorAndExit(e) {
|
|
3405
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
4162
3406
|
`);
|
|
4163
|
-
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
|
|
3407
|
+
process.exit(exitCodeForError(e));
|
|
3408
|
+
}
|
|
3409
|
+
async function requireApprovalForSystemCommand(moduleId, autoApprove) {
|
|
3410
|
+
const syntheticModuleDef = {
|
|
3411
|
+
moduleId,
|
|
3412
|
+
name: moduleId,
|
|
3413
|
+
description: `system command: ${moduleId}`,
|
|
3414
|
+
annotations: { requires_approval: true }
|
|
3415
|
+
};
|
|
3416
|
+
await checkApproval(syntheticModuleDef, autoApprove, void 0);
|
|
3417
|
+
}
|
|
3418
|
+
function formatHealthSummaryTty(result) {
|
|
3419
|
+
const summary = result.summary ?? {};
|
|
3420
|
+
const modules = result.modules ?? [];
|
|
3421
|
+
if (modules.length === 0) {
|
|
3422
|
+
process.stdout.write("No modules found.\n");
|
|
3423
|
+
return;
|
|
3424
|
+
}
|
|
3425
|
+
const total = summary.total_modules ?? modules.length;
|
|
3426
|
+
process.stdout.write(`Health Overview (${total} modules)
|
|
3427
|
+
|
|
3428
|
+
`);
|
|
3429
|
+
process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
|
|
3430
|
+
`);
|
|
3431
|
+
process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
|
|
3432
|
+
`);
|
|
3433
|
+
for (const m of modules) {
|
|
3434
|
+
const top = m.top_error;
|
|
3435
|
+
const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
|
|
3436
|
+
const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
|
|
3437
|
+
process.stdout.write(
|
|
3438
|
+
` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
|
|
3439
|
+
`
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
3442
|
+
const parts = [];
|
|
3443
|
+
for (const key of ["healthy", "degraded", "error"]) {
|
|
3444
|
+
const count = summary[key];
|
|
3445
|
+
if (count) parts.push(`${count} ${key}`);
|
|
3446
|
+
}
|
|
3447
|
+
process.stdout.write(`
|
|
3448
|
+
Summary: ${parts.join(", ") || "no data"}
|
|
3449
|
+
`);
|
|
3450
|
+
}
|
|
3451
|
+
function formatHealthModuleTty(result) {
|
|
3452
|
+
process.stdout.write(`Module: ${result.module_id ?? "?"}
|
|
3453
|
+
`);
|
|
3454
|
+
process.stdout.write(`Status: ${result.status ?? "unknown"}
|
|
3455
|
+
`);
|
|
3456
|
+
const total = result.total_calls ?? 0;
|
|
3457
|
+
const errors = result.error_count ?? 0;
|
|
3458
|
+
const rate = result.error_rate ?? 0;
|
|
3459
|
+
const avg = result.avg_latency_ms ?? 0;
|
|
3460
|
+
const p99 = result.p99_latency_ms ?? 0;
|
|
3461
|
+
process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
|
|
3462
|
+
`);
|
|
3463
|
+
process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
|
|
3464
|
+
`);
|
|
3465
|
+
const recent = result.recent_errors ?? [];
|
|
3466
|
+
if (recent.length > 0) {
|
|
3467
|
+
process.stdout.write(`
|
|
3468
|
+
Recent Errors (top ${recent.length}):
|
|
3469
|
+
`);
|
|
3470
|
+
for (const e of recent) {
|
|
3471
|
+
const count = e.count ?? "?";
|
|
3472
|
+
const last = e.last_occurred ?? "?";
|
|
3473
|
+
process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
|
|
3474
|
+
`);
|
|
3475
|
+
}
|
|
3476
|
+
}
|
|
3477
|
+
}
|
|
3478
|
+
function formatUsageSummaryTty(result) {
|
|
3479
|
+
const modules = result.modules ?? [];
|
|
3480
|
+
const period = result.period ?? "?";
|
|
3481
|
+
if (modules.length === 0) {
|
|
3482
|
+
process.stdout.write(`No usage data for period ${period}.
|
|
3483
|
+
`);
|
|
3484
|
+
return;
|
|
3485
|
+
}
|
|
3486
|
+
process.stdout.write(`Usage Summary (last ${period})
|
|
3487
|
+
|
|
3488
|
+
`);
|
|
3489
|
+
process.stdout.write(` ${"Module".padEnd(24)} ${"Calls".padStart(8)} ${"Errors".padStart(8)} ${"Avg Latency".padStart(12)} ${"Trend".padStart(10)}
|
|
3490
|
+
`);
|
|
3491
|
+
process.stdout.write(` ${"-".repeat(24)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(10)}
|
|
3492
|
+
`);
|
|
3493
|
+
for (const m of modules) {
|
|
3494
|
+
const avg = `${(m.avg_latency_ms ?? 0).toFixed(0)}ms`;
|
|
3495
|
+
process.stdout.write(
|
|
3496
|
+
` ${String(m.module_id).padEnd(24)} ${String(m.call_count ?? 0).padStart(8)} ${String(m.error_count ?? 0).padStart(8)} ${avg.padStart(12)} ${String(m.trend ?? "").padStart(10)}
|
|
3497
|
+
`
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
const totalCalls = result.total_calls ?? modules.reduce((s, m) => s + (m.call_count ?? 0), 0);
|
|
3501
|
+
const totalErrors = result.total_errors ?? modules.reduce((s, m) => s + (m.error_count ?? 0), 0);
|
|
3502
|
+
process.stdout.write(`
|
|
3503
|
+
Total: ${totalCalls.toLocaleString()} calls | ${totalErrors.toLocaleString()} errors
|
|
3504
|
+
`);
|
|
3505
|
+
}
|
|
3506
|
+
function registerHealthCommand(apcliGroup, executor) {
|
|
3507
|
+
const healthCmd = new Command4("health").description("Show module health status. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed health").option("--threshold <number>", "Error rate threshold (default: 0.01).", parseFloat, 0.01).option("--all", "Include healthy modules.", false).option("--errors <count>", "Max recent errors (module detail only).", parseInt, 10).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3508
|
+
const fmt = resolveFormat(opts.format);
|
|
3509
|
+
try {
|
|
3510
|
+
if (moduleId) {
|
|
3511
|
+
const result = await callSystemModule(executor, "system.health.module", {
|
|
3512
|
+
module_id: moduleId,
|
|
3513
|
+
error_limit: opts.errors
|
|
3514
|
+
});
|
|
3515
|
+
emitResult(result, fmt, () => formatHealthModuleTty(result));
|
|
3516
|
+
} else {
|
|
3517
|
+
const result = await callSystemModule(executor, "system.health.summary", {
|
|
3518
|
+
error_rate_threshold: opts.threshold,
|
|
3519
|
+
include_healthy: opts.all
|
|
3520
|
+
});
|
|
3521
|
+
emitResult(result, fmt, () => formatHealthSummaryTty(result));
|
|
4167
3522
|
}
|
|
4168
|
-
|
|
4169
|
-
|
|
3523
|
+
} catch (e) {
|
|
3524
|
+
emitErrorAndExit(e);
|
|
3525
|
+
}
|
|
3526
|
+
});
|
|
3527
|
+
apcliGroup.addCommand(healthCmd);
|
|
3528
|
+
}
|
|
3529
|
+
function registerUsageCommand(apcliGroup, executor) {
|
|
3530
|
+
const usageCmd = new Command4("usage").description("Show module usage statistics. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed usage").option("--period <period>", "Time window: 1h, 24h, 7d, 30d.", "24h").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3531
|
+
const fmt = resolveFormat(opts.format);
|
|
3532
|
+
try {
|
|
3533
|
+
let result;
|
|
3534
|
+
if (moduleId) {
|
|
3535
|
+
result = await callSystemModule(executor, "system.usage.module", {
|
|
3536
|
+
module_id: moduleId,
|
|
3537
|
+
period: opts.period
|
|
3538
|
+
});
|
|
3539
|
+
} else {
|
|
3540
|
+
result = await callSystemModule(executor, "system.usage.summary", {
|
|
3541
|
+
period: opts.period
|
|
3542
|
+
});
|
|
4170
3543
|
}
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
const annotations = moduleDef.annotations;
|
|
4177
|
-
const isStreaming = annotations?.streaming === true;
|
|
4178
|
-
if (!isStreaming) {
|
|
4179
|
-
process.stderr.write(
|
|
4180
|
-
`Warning: Module '${moduleId}' does not declare streaming support. Falling back to standard execution.
|
|
4181
|
-
`
|
|
4182
|
-
);
|
|
4183
|
-
}
|
|
4184
|
-
if (isStreaming && executor.stream) {
|
|
4185
|
-
let chunks = 0;
|
|
4186
|
-
for await (const chunk of executor.stream(moduleId, merged)) {
|
|
4187
|
-
chunks++;
|
|
4188
|
-
process.stdout.write(JSON.stringify(chunk) + "\n");
|
|
4189
|
-
if (process.stderr.isTTY) {
|
|
4190
|
-
process.stderr.write(`\rStreaming ${moduleId}... (${chunks} chunks)`);
|
|
4191
|
-
}
|
|
4192
|
-
}
|
|
4193
|
-
if (process.stderr.isTTY) {
|
|
4194
|
-
process.stderr.write("\n");
|
|
4195
|
-
}
|
|
4196
|
-
const durationMs2 = Math.round(performance.now() - startTime);
|
|
4197
|
-
const { getAuditLogger: getAuditLogger3 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4198
|
-
const auditLogger2 = getAuditLogger3();
|
|
4199
|
-
if (auditLogger2) {
|
|
4200
|
-
auditLogger2.logExecution(moduleId, merged, "success", 0, durationMs2);
|
|
4201
|
-
}
|
|
4202
|
-
return;
|
|
3544
|
+
emitResult(result, fmt, () => {
|
|
3545
|
+
if (moduleId) {
|
|
3546
|
+
formatExecResult(result, fmt);
|
|
3547
|
+
} else {
|
|
3548
|
+
formatUsageSummaryTty(result);
|
|
4203
3549
|
}
|
|
3550
|
+
});
|
|
3551
|
+
} catch (e) {
|
|
3552
|
+
emitErrorAndExit(e);
|
|
3553
|
+
}
|
|
3554
|
+
});
|
|
3555
|
+
apcliGroup.addCommand(usageCmd);
|
|
3556
|
+
}
|
|
3557
|
+
function registerEnableCommand(apcliGroup, executor) {
|
|
3558
|
+
const enableCmd = new Command4("enable").description("Enable a disabled module at runtime.").argument("<module-id>", "Module ID to enable").requiredOption("--reason <reason>", "Reason for enabling (required for audit).").option("-y, --yes", "Skip approval prompt (audit D11-B-001 cross-SDK parity).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3559
|
+
const fmt = resolveFormat(opts.format);
|
|
3560
|
+
try {
|
|
3561
|
+
await requireApprovalForSystemCommand("system.control.toggle_feature", opts.yes);
|
|
3562
|
+
const result = await callSystemModule(executor, "system.control.toggle_feature", {
|
|
3563
|
+
module_id: moduleId,
|
|
3564
|
+
enabled: true,
|
|
3565
|
+
reason: opts.reason
|
|
3566
|
+
});
|
|
3567
|
+
emitResult(result, fmt, () => {
|
|
3568
|
+
process.stdout.write(`Module '${moduleId}' enabled.
|
|
3569
|
+
Reason: ${opts.reason}
|
|
3570
|
+
`);
|
|
3571
|
+
});
|
|
3572
|
+
} catch (e) {
|
|
3573
|
+
emitErrorAndExit(e);
|
|
3574
|
+
}
|
|
3575
|
+
});
|
|
3576
|
+
apcliGroup.addCommand(enableCmd);
|
|
3577
|
+
}
|
|
3578
|
+
function registerDisableCommand(apcliGroup, executor) {
|
|
3579
|
+
const disableCmd = new Command4("disable").description("Disable a module at runtime (calls are rejected until re-enabled).").argument("<module-id>", "Module ID to disable").requiredOption("--reason <reason>", "Reason for disabling (required for audit).").option("-y, --yes", "Skip approval prompt (audit D11-B-001 cross-SDK parity).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3580
|
+
const fmt = resolveFormat(opts.format);
|
|
3581
|
+
try {
|
|
3582
|
+
await requireApprovalForSystemCommand("system.control.toggle_feature", opts.yes);
|
|
3583
|
+
const result = await callSystemModule(executor, "system.control.toggle_feature", {
|
|
3584
|
+
module_id: moduleId,
|
|
3585
|
+
enabled: false,
|
|
3586
|
+
reason: opts.reason
|
|
3587
|
+
});
|
|
3588
|
+
emitResult(result, fmt, () => {
|
|
3589
|
+
process.stdout.write(`Module '${moduleId}' disabled.
|
|
3590
|
+
Reason: ${opts.reason}
|
|
3591
|
+
`);
|
|
3592
|
+
});
|
|
3593
|
+
} catch (e) {
|
|
3594
|
+
emitErrorAndExit(e);
|
|
3595
|
+
}
|
|
3596
|
+
});
|
|
3597
|
+
apcliGroup.addCommand(disableCmd);
|
|
3598
|
+
}
|
|
3599
|
+
function registerReloadCommand(apcliGroup, executor) {
|
|
3600
|
+
const reloadCmd = new Command4("reload").description("Hot-reload a module from disk.").argument("<module-id>", "Module ID to reload").requiredOption("--reason <reason>", "Reason for reload (required for audit).").option("-y, --yes", "Skip approval prompt (audit D11-B-001 cross-SDK parity).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
3601
|
+
const fmt = resolveFormat(opts.format);
|
|
3602
|
+
try {
|
|
3603
|
+
await requireApprovalForSystemCommand("system.control.reload_module", opts.yes);
|
|
3604
|
+
const result = await callSystemModule(executor, "system.control.reload_module", {
|
|
3605
|
+
module_id: moduleId,
|
|
3606
|
+
reason: opts.reason
|
|
3607
|
+
});
|
|
3608
|
+
emitResult(result, fmt, () => {
|
|
3609
|
+
const prev = result.previous_version ?? "?";
|
|
3610
|
+
const newVer = result.new_version ?? "?";
|
|
3611
|
+
const dur = result.reload_duration_ms ?? "?";
|
|
3612
|
+
process.stdout.write(`Module '${moduleId}' reloaded.
|
|
3613
|
+
`);
|
|
3614
|
+
process.stdout.write(` Version: ${prev} -> ${newVer}
|
|
3615
|
+
`);
|
|
3616
|
+
process.stdout.write(` Duration: ${dur}ms
|
|
3617
|
+
`);
|
|
3618
|
+
});
|
|
3619
|
+
} catch (e) {
|
|
3620
|
+
emitErrorAndExit(e);
|
|
3621
|
+
}
|
|
3622
|
+
});
|
|
3623
|
+
apcliGroup.addCommand(reloadCmd);
|
|
3624
|
+
}
|
|
3625
|
+
function registerConfigCommand(apcliGroup, executor) {
|
|
3626
|
+
const configGroup = new Command4("config").description("Read or update runtime configuration.");
|
|
3627
|
+
const configGetCmd = new Command4("get").description("Read a configuration value by dot-path key.").argument("<key>", "Configuration key (dot-path)").option("--format <format>", "Output format.", "table").action(async (key, opts) => {
|
|
3628
|
+
const fmt = resolveFormat(opts.format);
|
|
3629
|
+
try {
|
|
3630
|
+
const value = new Config().get(key);
|
|
3631
|
+
emitResult({ key, value }, fmt, () => {
|
|
3632
|
+
process.stdout.write(`${key} = ${JSON.stringify(value)}
|
|
3633
|
+
`);
|
|
3634
|
+
});
|
|
3635
|
+
} catch (e) {
|
|
3636
|
+
emitErrorAndExit(e);
|
|
3637
|
+
}
|
|
3638
|
+
});
|
|
3639
|
+
configGroup.addCommand(configGetCmd);
|
|
3640
|
+
const configSetCmd = new Command4("set").description("Update a runtime configuration value (audit-logged; client-side approval gate applies).").argument("<key>", "Configuration key (dot-path)").argument("<value>", "New value").requiredOption("--reason <reason>", "Reason for config change (required for audit).").option("-y, --yes", "Skip approval prompt (audit D11-B-001 cross-SDK parity).", false).option("--format <format>", "Output format.").action(async (key, value, opts) => {
|
|
3641
|
+
const fmt = resolveFormat(opts.format);
|
|
3642
|
+
let parsedValue;
|
|
3643
|
+
try {
|
|
3644
|
+
parsedValue = JSON.parse(value);
|
|
3645
|
+
} catch {
|
|
3646
|
+
parsedValue = value;
|
|
3647
|
+
}
|
|
3648
|
+
try {
|
|
3649
|
+
await requireApprovalForSystemCommand("system.control.update_config", opts.yes);
|
|
3650
|
+
const result = await callSystemModule(executor, "system.control.update_config", {
|
|
3651
|
+
key,
|
|
3652
|
+
value: parsedValue,
|
|
3653
|
+
reason: opts.reason
|
|
3654
|
+
});
|
|
3655
|
+
emitResult(result, fmt, () => {
|
|
3656
|
+
const old = result.old_value ?? "?";
|
|
3657
|
+
const newVal = result.new_value ?? "?";
|
|
3658
|
+
process.stdout.write(`Config updated: ${key}
|
|
3659
|
+
`);
|
|
3660
|
+
process.stdout.write(` ${JSON.stringify(old)} -> ${JSON.stringify(newVal)}
|
|
3661
|
+
`);
|
|
3662
|
+
process.stdout.write(` Reason: ${opts.reason}
|
|
3663
|
+
`);
|
|
3664
|
+
});
|
|
3665
|
+
} catch (e) {
|
|
3666
|
+
emitErrorAndExit(e);
|
|
3667
|
+
}
|
|
3668
|
+
});
|
|
3669
|
+
configGroup.addCommand(configSetCmd);
|
|
3670
|
+
apcliGroup.addCommand(configGroup);
|
|
3671
|
+
}
|
|
3672
|
+
|
|
3673
|
+
// src/strategy.ts
|
|
3674
|
+
init_esm_shims();
|
|
3675
|
+
import { Command as Command5, Option as Option3 } from "commander";
|
|
3676
|
+
function lookupStrategyInfo(executor, strategyName) {
|
|
3677
|
+
if (typeof executor.describePipeline === "function") {
|
|
3678
|
+
try {
|
|
3679
|
+
const current = executor.describePipeline();
|
|
3680
|
+
if (current && current.name === strategyName) {
|
|
3681
|
+
return { info: current, isCurrent: true };
|
|
4204
3682
|
}
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
3683
|
+
} catch {
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
const ctor = executor.constructor;
|
|
3687
|
+
if (ctor && typeof ctor.listStrategies === "function") {
|
|
3688
|
+
try {
|
|
3689
|
+
const all = ctor.listStrategies();
|
|
3690
|
+
const info = all.find((s) => s.name === strategyName) ?? null;
|
|
3691
|
+
return { info, isCurrent: false };
|
|
3692
|
+
} catch {
|
|
3693
|
+
return { info: null, isCurrent: false };
|
|
3694
|
+
}
|
|
3695
|
+
}
|
|
3696
|
+
return { info: null, isCurrent: false };
|
|
3697
|
+
}
|
|
3698
|
+
var PRESET_STEPS = {
|
|
3699
|
+
standard: [
|
|
3700
|
+
"context_creation",
|
|
3701
|
+
"call_chain_guard",
|
|
3702
|
+
"module_lookup",
|
|
3703
|
+
"acl_check",
|
|
3704
|
+
"approval_gate",
|
|
3705
|
+
"middleware_before",
|
|
3706
|
+
"input_validation",
|
|
3707
|
+
"execute",
|
|
3708
|
+
"output_validation",
|
|
3709
|
+
"middleware_after",
|
|
3710
|
+
"return_result"
|
|
3711
|
+
],
|
|
3712
|
+
internal: [
|
|
3713
|
+
"context_creation",
|
|
3714
|
+
"call_chain_guard",
|
|
3715
|
+
"module_lookup",
|
|
3716
|
+
"middleware_before",
|
|
3717
|
+
"input_validation",
|
|
3718
|
+
"execute",
|
|
3719
|
+
"output_validation",
|
|
3720
|
+
"middleware_after",
|
|
3721
|
+
"return_result"
|
|
3722
|
+
],
|
|
3723
|
+
testing: [
|
|
3724
|
+
"context_creation",
|
|
3725
|
+
"module_lookup",
|
|
3726
|
+
"middleware_before",
|
|
3727
|
+
"input_validation",
|
|
3728
|
+
"execute",
|
|
3729
|
+
"output_validation",
|
|
3730
|
+
"middleware_after",
|
|
3731
|
+
"return_result"
|
|
3732
|
+
],
|
|
3733
|
+
performance: [
|
|
3734
|
+
"context_creation",
|
|
3735
|
+
"call_chain_guard",
|
|
3736
|
+
"module_lookup",
|
|
3737
|
+
"acl_check",
|
|
3738
|
+
"approval_gate",
|
|
3739
|
+
"input_validation",
|
|
3740
|
+
"execute",
|
|
3741
|
+
"output_validation",
|
|
3742
|
+
"return_result"
|
|
3743
|
+
],
|
|
3744
|
+
minimal: [
|
|
3745
|
+
"context_creation",
|
|
3746
|
+
"module_lookup",
|
|
3747
|
+
"execute",
|
|
3748
|
+
"return_result"
|
|
3749
|
+
]
|
|
3750
|
+
};
|
|
3751
|
+
function registerPipelineCommand(cli, executor) {
|
|
3752
|
+
const pipelineCmd = new Command5("describe-pipeline").description("Show the execution pipeline steps for a strategy.").addOption(
|
|
3753
|
+
new Option3("--strategy <name>", "Strategy to describe (default: standard).").choices(["standard", "internal", "testing", "performance", "minimal"]).default("standard")
|
|
3754
|
+
).option("--format <format>", "Output format.").action((opts) => {
|
|
3755
|
+
const fmt = resolveFormat(opts.format);
|
|
3756
|
+
const { info, isCurrent } = lookupStrategyInfo(executor, opts.strategy);
|
|
3757
|
+
if (info) {
|
|
3758
|
+
const strategySteps = isCurrent ? executor.currentStrategy?.steps ?? [] : [];
|
|
3759
|
+
const header = `Pipeline: ${info.name} (${info.stepCount} steps)`;
|
|
3760
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
3761
|
+
const payload = {
|
|
3762
|
+
strategy: info.name,
|
|
3763
|
+
step_count: info.stepCount,
|
|
3764
|
+
description: info.description,
|
|
3765
|
+
steps: info.stepNames.map((name, i) => {
|
|
3766
|
+
const stepMeta = strategySteps[i];
|
|
3767
|
+
return {
|
|
3768
|
+
index: i + 1,
|
|
3769
|
+
name,
|
|
3770
|
+
pure: stepMeta?.pure ?? false,
|
|
3771
|
+
removable: stepMeta?.removable ?? true,
|
|
3772
|
+
timeout_ms: stepMeta?.timeoutMs ?? null
|
|
3773
|
+
};
|
|
3774
|
+
})
|
|
3775
|
+
};
|
|
3776
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
3777
|
+
} else {
|
|
3778
|
+
process.stdout.write(`${header}
|
|
3779
|
+
|
|
4244
3780
|
`);
|
|
4245
|
-
|
|
4246
|
-
|
|
3781
|
+
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
3782
|
+
`);
|
|
3783
|
+
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
3784
|
+
`);
|
|
3785
|
+
for (let i = 0; i < info.stepNames.length; i++) {
|
|
3786
|
+
const stepMeta = strategySteps[i];
|
|
3787
|
+
const pure = stepMeta?.pure ? "yes" : "no";
|
|
3788
|
+
const removable = stepMeta?.removable !== false ? "yes" : "no";
|
|
3789
|
+
const timeout = stepMeta?.timeoutMs ? `${stepMeta.timeoutMs}ms` : "\u2014";
|
|
3790
|
+
process.stdout.write(` ${String(i + 1).padEnd(4)} ${info.stepNames[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
|
|
4247
3791
|
`);
|
|
4248
|
-
}
|
|
4249
|
-
}
|
|
4250
|
-
}
|
|
4251
|
-
const { getAuditLogger: getAL2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4252
|
-
const al2 = getAL2();
|
|
4253
|
-
if (al2) {
|
|
4254
|
-
al2.logExecution(moduleId, merged, "success", 0, durationMs2);
|
|
4255
3792
|
}
|
|
4256
|
-
return;
|
|
4257
3793
|
}
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
|
|
4267
|
-
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
const steps = PRESET_STEPS[opts.strategy] ?? [];
|
|
3797
|
+
const pureSteps = /* @__PURE__ */ new Set([
|
|
3798
|
+
"context_creation",
|
|
3799
|
+
"call_chain_guard",
|
|
3800
|
+
"module_lookup",
|
|
3801
|
+
"acl_check",
|
|
3802
|
+
"input_validation"
|
|
3803
|
+
]);
|
|
3804
|
+
const nonRemovable = /* @__PURE__ */ new Set([
|
|
3805
|
+
"context_creation",
|
|
3806
|
+
"module_lookup",
|
|
3807
|
+
"execute",
|
|
3808
|
+
"return_result"
|
|
3809
|
+
]);
|
|
3810
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
3811
|
+
const payload = {
|
|
3812
|
+
strategy: opts.strategy,
|
|
3813
|
+
step_count: steps.length,
|
|
3814
|
+
steps: steps.map((s, i) => ({
|
|
3815
|
+
index: i + 1,
|
|
3816
|
+
name: s,
|
|
3817
|
+
pure: pureSteps.has(s),
|
|
3818
|
+
removable: !nonRemovable.has(s)
|
|
3819
|
+
}))
|
|
3820
|
+
};
|
|
3821
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
3822
|
+
} else {
|
|
3823
|
+
process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
|
|
3824
|
+
|
|
3825
|
+
`);
|
|
3826
|
+
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
3827
|
+
`);
|
|
3828
|
+
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
3829
|
+
`);
|
|
3830
|
+
for (let i = 0; i < steps.length; i++) {
|
|
3831
|
+
const pure = pureSteps.has(steps[i]) ? "yes" : "no";
|
|
3832
|
+
const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
|
|
3833
|
+
process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
|
|
4268
3834
|
`);
|
|
4269
|
-
}
|
|
4270
|
-
} else {
|
|
4271
|
-
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
4272
|
-
const sandbox = new Sandbox2(sandboxEnabled);
|
|
4273
|
-
result = await sandbox.execute(moduleId, merged, executor);
|
|
4274
3835
|
}
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
3836
|
+
}
|
|
3837
|
+
});
|
|
3838
|
+
cli.addCommand(pipelineCmd);
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
// src/main.ts
|
|
3842
|
+
import { BindingLoader, DisplayResolver } from "apcore-toolkit";
|
|
3843
|
+
init_audit();
|
|
3844
|
+
|
|
3845
|
+
// src/canonical-help.ts
|
|
3846
|
+
init_esm_shims();
|
|
3847
|
+
function resolveHelpText(cmd, section) {
|
|
3848
|
+
const bag = cmd._helpText;
|
|
3849
|
+
const v = bag?.[section];
|
|
3850
|
+
if (typeof v === "function") return v({ error: false, command: cmd });
|
|
3851
|
+
return v ?? "";
|
|
3852
|
+
}
|
|
3853
|
+
function uppercasePlaceholders(flags) {
|
|
3854
|
+
return flags.replace(/<([a-zA-Z0-9_-]+)>/g, (_, name) => `<${name.toUpperCase()}>`).replace(/\[([a-zA-Z0-9_-]+)\]/g, (_, name) => `[${name.toUpperCase()}]`);
|
|
3855
|
+
}
|
|
3856
|
+
function optionTerm(opt) {
|
|
3857
|
+
const flags = uppercasePlaceholders(opt.flags);
|
|
3858
|
+
if (!opt.short && flags.startsWith("--")) return " " + flags;
|
|
3859
|
+
return flags;
|
|
3860
|
+
}
|
|
3861
|
+
function optionDescription(opt) {
|
|
3862
|
+
let desc = opt.description;
|
|
3863
|
+
const d = opt.defaultValue;
|
|
3864
|
+
if (d !== void 0 && d !== false && d !== "" && d !== null) {
|
|
3865
|
+
desc = `${desc} [default: ${String(d)}]`;
|
|
3866
|
+
}
|
|
3867
|
+
return desc;
|
|
3868
|
+
}
|
|
3869
|
+
function reorderHelpVersionLast(opts) {
|
|
3870
|
+
const helpOpts = [];
|
|
3871
|
+
const versionOpts = [];
|
|
3872
|
+
const rest = [];
|
|
3873
|
+
for (const o of opts) {
|
|
3874
|
+
if (o.long === "--help") helpOpts.push(o);
|
|
3875
|
+
else if (o.long === "--version") versionOpts.push(o);
|
|
3876
|
+
else rest.push(o);
|
|
3877
|
+
}
|
|
3878
|
+
return [...rest, ...helpOpts, ...versionOpts];
|
|
3879
|
+
}
|
|
3880
|
+
function canonicalFormatHelp(cmd, helper) {
|
|
3881
|
+
const sections = [];
|
|
3882
|
+
const beforeAll = resolveHelpText(cmd, "beforeAll");
|
|
3883
|
+
if (beforeAll) sections.push(beforeAll);
|
|
3884
|
+
const desc = cmd.description();
|
|
3885
|
+
if (desc) sections.push(desc);
|
|
3886
|
+
const before = resolveHelpText(cmd, "before");
|
|
3887
|
+
if (before) sections.push(before);
|
|
3888
|
+
const visibleOpts = reorderHelpVersionLast(helper.visibleOptions(cmd));
|
|
3889
|
+
const visibleCmds = helper.visibleCommands(cmd);
|
|
3890
|
+
const args = cmd.registeredArguments ?? [];
|
|
3891
|
+
let usage = `Usage: ${cmd.name()}`;
|
|
3892
|
+
if (visibleOpts.length > 0) usage += " [OPTIONS]";
|
|
3893
|
+
for (const a of args) {
|
|
3894
|
+
const n = a.name().toUpperCase();
|
|
3895
|
+
usage += a.required ? ` <${n}>` : ` [${n}]`;
|
|
3896
|
+
}
|
|
3897
|
+
if (visibleCmds.length > 0) usage += " [COMMAND]";
|
|
3898
|
+
sections.push(usage);
|
|
3899
|
+
if (visibleCmds.length > 0) {
|
|
3900
|
+
const terms = visibleCmds.map((c) => c.name());
|
|
3901
|
+
const w = Math.max(...terms.map((t) => t.length));
|
|
3902
|
+
const lines = ["Commands:"];
|
|
3903
|
+
visibleCmds.forEach((sub, i) => {
|
|
3904
|
+
lines.push(` ${terms[i].padEnd(w)} ${sub.description()}`);
|
|
3905
|
+
});
|
|
3906
|
+
sections.push(lines.join("\n"));
|
|
3907
|
+
}
|
|
3908
|
+
if (visibleOpts.length > 0) {
|
|
3909
|
+
const terms = visibleOpts.map(optionTerm);
|
|
3910
|
+
const w = Math.max(...terms.map((t) => t.length));
|
|
3911
|
+
const lines = ["Options:"];
|
|
3912
|
+
visibleOpts.forEach((opt, i) => {
|
|
3913
|
+
lines.push(` ${terms[i].padEnd(w)} ${optionDescription(opt)}`);
|
|
3914
|
+
});
|
|
3915
|
+
sections.push(lines.join("\n"));
|
|
3916
|
+
}
|
|
3917
|
+
const after = resolveHelpText(cmd, "after");
|
|
3918
|
+
if (after) sections.push(after);
|
|
3919
|
+
const afterAll = resolveHelpText(cmd, "afterAll");
|
|
3920
|
+
if (afterAll) sections.push(afterAll);
|
|
3921
|
+
return sections.join("\n\n") + "\n";
|
|
3922
|
+
}
|
|
3923
|
+
|
|
3924
|
+
// src/validate.ts
|
|
3925
|
+
init_esm_shims();
|
|
3926
|
+
init_errors();
|
|
3927
|
+
var MODULE_ID_PATTERN = /^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/;
|
|
3928
|
+
var MAX_MODULE_ID_LENGTH = 192;
|
|
3929
|
+
function validateModuleId(moduleId) {
|
|
3930
|
+
if (moduleId.length > MAX_MODULE_ID_LENGTH) {
|
|
3931
|
+
process.stderr.write(
|
|
3932
|
+
`Error: Invalid module ID format: '${moduleId}'. Maximum length is ${MAX_MODULE_ID_LENGTH} characters.
|
|
3933
|
+
`
|
|
3934
|
+
);
|
|
3935
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3936
|
+
}
|
|
3937
|
+
if (!MODULE_ID_PATTERN.test(moduleId)) {
|
|
3938
|
+
process.stderr.write(
|
|
3939
|
+
`Error: Invalid module ID format: '${moduleId}'.
|
|
3940
|
+
`
|
|
3941
|
+
);
|
|
3942
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3943
|
+
}
|
|
3944
|
+
}
|
|
3945
|
+
|
|
3946
|
+
// src/main.ts
|
|
3947
|
+
var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
|
|
3948
|
+
var verboseHelp = false;
|
|
3949
|
+
function setAllOptionsHelp(allOptions) {
|
|
3950
|
+
verboseHelp = allOptions;
|
|
3951
|
+
}
|
|
3952
|
+
function setVerboseHelp(verbose) {
|
|
3953
|
+
setAllOptionsHelp(verbose);
|
|
3954
|
+
}
|
|
3955
|
+
var docsUrl = null;
|
|
3956
|
+
function setDocsUrl(url) {
|
|
3957
|
+
docsUrl = url;
|
|
3958
|
+
}
|
|
3959
|
+
function hasVerboseFlag() {
|
|
3960
|
+
return process.argv.includes("--all-options");
|
|
3961
|
+
}
|
|
3962
|
+
function resolveIntOption(cliValue, envValue, defaultValue) {
|
|
3963
|
+
if (cliValue !== void 0 && Number.isFinite(cliValue) && cliValue > 0) {
|
|
3964
|
+
return cliValue;
|
|
3965
|
+
}
|
|
3966
|
+
if (envValue !== void 0 && envValue !== "") {
|
|
3967
|
+
const parsed = parseInt(envValue, 10);
|
|
3968
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
3969
|
+
return parsed;
|
|
3970
|
+
}
|
|
3971
|
+
process.stderr.write(
|
|
3972
|
+
`Warning: invalid integer env value '${envValue}'; using default ${defaultValue}.
|
|
3973
|
+
`
|
|
3974
|
+
);
|
|
3975
|
+
}
|
|
3976
|
+
return defaultValue;
|
|
3977
|
+
}
|
|
3978
|
+
function resolveStringOption(cliValue, envValue) {
|
|
3979
|
+
if (typeof cliValue === "string" && cliValue !== "") {
|
|
3980
|
+
return cliValue;
|
|
3981
|
+
}
|
|
3982
|
+
if (envValue !== void 0 && envValue !== "") {
|
|
3983
|
+
return envValue;
|
|
3984
|
+
}
|
|
3985
|
+
return void 0;
|
|
3986
|
+
}
|
|
3987
|
+
var VERSION = "0.0.0";
|
|
3988
|
+
try {
|
|
3989
|
+
const pkg = JSON.parse(readFileSync3(path5.resolve(__dirname2, "../package.json"), "utf-8"));
|
|
3990
|
+
VERSION = pkg.version;
|
|
3991
|
+
} catch {
|
|
3992
|
+
}
|
|
3993
|
+
function validateInputSchema(schema, input) {
|
|
3994
|
+
const required = schema.required;
|
|
3995
|
+
if (required && Array.isArray(required)) {
|
|
3996
|
+
for (const field of required) {
|
|
3997
|
+
const val = input[field];
|
|
3998
|
+
if (val === null || val === void 0) {
|
|
3999
|
+
return `'${field}' is required`;
|
|
4281
4000
|
}
|
|
4282
|
-
}
|
|
4283
|
-
|
|
4284
|
-
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
const properties = schema.properties;
|
|
4004
|
+
if (properties) {
|
|
4005
|
+
for (const [field, propSchema] of Object.entries(properties)) {
|
|
4006
|
+
const val = input[field];
|
|
4007
|
+
if (val === null || val === void 0) continue;
|
|
4008
|
+
const expectedType = propSchema.type;
|
|
4009
|
+
if (!expectedType) continue;
|
|
4010
|
+
const actualType = typeof val;
|
|
4011
|
+
if (expectedType === "string" && actualType !== "string") {
|
|
4012
|
+
return `'${field}' must be a string, got ${actualType}`;
|
|
4292
4013
|
}
|
|
4293
|
-
if (
|
|
4294
|
-
|
|
4295
|
-
}
|
|
4296
|
-
|
|
4014
|
+
if ((expectedType === "integer" || expectedType === "number") && actualType !== "number") {
|
|
4015
|
+
return `'${field}' must be a number, got ${actualType}`;
|
|
4016
|
+
}
|
|
4017
|
+
if (expectedType === "boolean" && actualType !== "boolean") {
|
|
4018
|
+
return `'${field}' must be a boolean, got ${actualType}`;
|
|
4297
4019
|
}
|
|
4298
|
-
process.exit(exitCode);
|
|
4299
4020
|
}
|
|
4300
|
-
}
|
|
4301
|
-
return
|
|
4021
|
+
}
|
|
4022
|
+
return null;
|
|
4302
4023
|
}
|
|
4303
|
-
|
|
4304
|
-
const
|
|
4305
|
-
|
|
4306
|
-
|
|
4307
|
-
|
|
4024
|
+
function emitErrorJson(e, exitCode) {
|
|
4025
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
4026
|
+
const errRecord = err;
|
|
4027
|
+
const code = errRecord.code ?? "UNKNOWN";
|
|
4028
|
+
const payload = {
|
|
4029
|
+
error: true,
|
|
4030
|
+
code,
|
|
4031
|
+
message: err.message,
|
|
4032
|
+
exit_code: exitCode
|
|
4033
|
+
};
|
|
4034
|
+
for (const field of ["details", "suggestion", "ai_guidance", "retryable", "user_fixable"]) {
|
|
4035
|
+
const val = errRecord[field];
|
|
4036
|
+
if (val !== void 0 && val !== null) {
|
|
4037
|
+
payload[field] = val;
|
|
4308
4038
|
}
|
|
4309
4039
|
}
|
|
4310
|
-
|
|
4311
|
-
|
|
4040
|
+
process.stderr.write(JSON.stringify(payload) + "\n");
|
|
4041
|
+
}
|
|
4042
|
+
function emitErrorTty(e, exitCode) {
|
|
4043
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
4044
|
+
const errRecord = err;
|
|
4045
|
+
const code = errRecord.code;
|
|
4046
|
+
const header = code ? `Error [${code}]: ${err.message}` : `Error: ${err.message}`;
|
|
4047
|
+
process.stderr.write(header + "\n");
|
|
4048
|
+
const details = errRecord.details;
|
|
4049
|
+
if (details && typeof details === "object" && !Array.isArray(details)) {
|
|
4050
|
+
process.stderr.write("\n Details:\n");
|
|
4051
|
+
for (const [k, v] of Object.entries(details)) {
|
|
4052
|
+
process.stderr.write(` ${k}: ${v}
|
|
4053
|
+
`);
|
|
4054
|
+
}
|
|
4312
4055
|
}
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4056
|
+
const suggestion = errRecord.suggestion;
|
|
4057
|
+
if (suggestion) {
|
|
4058
|
+
process.stderr.write(`
|
|
4059
|
+
Suggestion: ${suggestion}
|
|
4060
|
+
`);
|
|
4061
|
+
}
|
|
4062
|
+
const retryable = errRecord.retryable;
|
|
4063
|
+
if (retryable !== void 0 && retryable !== null) {
|
|
4064
|
+
const label = retryable ? "Yes" : "No (same input will fail again)";
|
|
4065
|
+
process.stderr.write(` Retryable: ${label}
|
|
4066
|
+
`);
|
|
4067
|
+
}
|
|
4068
|
+
process.stderr.write(`
|
|
4069
|
+
Exit code: ${exitCode}
|
|
4070
|
+
`);
|
|
4071
|
+
}
|
|
4072
|
+
function createCli(extensionsDirOrOpts, progName, allOptions = false) {
|
|
4073
|
+
let extensionsDir;
|
|
4074
|
+
let registry;
|
|
4075
|
+
let executor;
|
|
4076
|
+
let extraCommands;
|
|
4077
|
+
let app;
|
|
4078
|
+
let expose;
|
|
4079
|
+
let apcliOption;
|
|
4080
|
+
let appVersion;
|
|
4081
|
+
let appDescription;
|
|
4082
|
+
let allowedPrefixes;
|
|
4083
|
+
let builtinGroupName;
|
|
4084
|
+
if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
|
|
4085
|
+
extensionsDir = extensionsDirOrOpts.extensionsDir;
|
|
4086
|
+
progName = extensionsDirOrOpts.progName ?? progName;
|
|
4087
|
+
allOptions = extensionsDirOrOpts.allOptions ?? extensionsDirOrOpts.verbose ?? allOptions;
|
|
4088
|
+
app = extensionsDirOrOpts.app;
|
|
4089
|
+
registry = extensionsDirOrOpts.registry;
|
|
4090
|
+
executor = extensionsDirOrOpts.executor;
|
|
4091
|
+
extraCommands = extensionsDirOrOpts.extraCommands;
|
|
4092
|
+
expose = extensionsDirOrOpts.expose;
|
|
4093
|
+
apcliOption = extensionsDirOrOpts.apcli;
|
|
4094
|
+
appVersion = extensionsDirOrOpts.version;
|
|
4095
|
+
appDescription = extensionsDirOrOpts.description;
|
|
4096
|
+
builtinGroupName = extensionsDirOrOpts.builtinGroupName;
|
|
4097
|
+
allowedPrefixes = extensionsDirOrOpts.allowedPrefixes;
|
|
4318
4098
|
} else {
|
|
4319
|
-
|
|
4099
|
+
extensionsDir = extensionsDirOrOpts;
|
|
4100
|
+
}
|
|
4101
|
+
verboseHelp = allOptions;
|
|
4102
|
+
registerConfigNamespace();
|
|
4103
|
+
try {
|
|
4104
|
+
const auditLogger = new AuditLogger();
|
|
4105
|
+
setAuditLogger(auditLogger);
|
|
4106
|
+
} catch {
|
|
4107
|
+
}
|
|
4108
|
+
const resolvedProgName = progName ?? path5.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
|
|
4109
|
+
const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
|
|
4110
|
+
setLogLevel(cliLogLevel);
|
|
4111
|
+
if (app && (registry || executor)) {
|
|
4112
|
+
process.stderr.write("Error: app is mutually exclusive with registry/executor\n");
|
|
4113
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4114
|
+
}
|
|
4115
|
+
if (app) {
|
|
4116
|
+
registry = app.registry;
|
|
4117
|
+
executor = app.executor;
|
|
4118
|
+
}
|
|
4119
|
+
if (executor && !registry) {
|
|
4120
|
+
process.stderr.write("Error: executor requires registry \u2014 pass both or neither\n");
|
|
4121
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4122
|
+
}
|
|
4123
|
+
if (executor && typeof executor.setApprovalHandler === "function") {
|
|
4320
4124
|
try {
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4125
|
+
const handler = new CliApprovalHandler(
|
|
4126
|
+
/*autoApprove*/
|
|
4127
|
+
false
|
|
4128
|
+
);
|
|
4129
|
+
executor.setApprovalHandler(handler);
|
|
4130
|
+
} catch {
|
|
4327
4131
|
}
|
|
4328
4132
|
}
|
|
4329
|
-
const
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4133
|
+
const registryInjected = registry !== void 0;
|
|
4134
|
+
const program = new Command6(resolvedProgName).exitOverride().helpOption("-h, --help", "Print help").addHelpCommand("help [command]", "Print this message or the help of the given subcommand(s)").description(appDescription ?? `${resolvedProgName} CLI`).option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING").option("--all-options", "Show all options in help output (including built-in options)");
|
|
4135
|
+
if (appVersion) {
|
|
4136
|
+
program.version(appVersion, "-V, --version", "Print version");
|
|
4137
|
+
}
|
|
4138
|
+
program.configureHelp({ formatHelp: canonicalFormatHelp });
|
|
4139
|
+
if (!registryInjected) {
|
|
4140
|
+
program.option("--extensions-dir <path>", "Path to extensions directory");
|
|
4141
|
+
program.option("--commands-dir <path>", "Path to convention-based commands directory");
|
|
4142
|
+
program.option("--binding <path>", "Path to binding.yaml for display overlay");
|
|
4143
|
+
}
|
|
4144
|
+
let apcliCfg;
|
|
4145
|
+
try {
|
|
4146
|
+
if (apcliOption instanceof ApcliGroup) {
|
|
4147
|
+
if (builtinGroupName !== void 0 && builtinGroupName !== "apcli" && apcliOption.name !== builtinGroupName) {
|
|
4148
|
+
throw new Error(
|
|
4149
|
+
`builtinGroupName=${JSON.stringify(builtinGroupName)} conflicts with the name on the supplied ApcliGroup (${JSON.stringify(apcliOption.name)}). Pass only one.`
|
|
4150
|
+
);
|
|
4151
|
+
}
|
|
4152
|
+
apcliCfg = apcliOption;
|
|
4153
|
+
} else if (apcliOption !== void 0) {
|
|
4154
|
+
apcliCfg = ApcliGroup.fromCliConfig(apcliOption, {
|
|
4155
|
+
registryInjected,
|
|
4156
|
+
name: builtinGroupName
|
|
4157
|
+
});
|
|
4158
|
+
} else {
|
|
4159
|
+
let yamlVal = null;
|
|
4160
|
+
try {
|
|
4161
|
+
const resolver = new ConfigResolver();
|
|
4162
|
+
yamlVal = resolver.resolveObject("apcli");
|
|
4163
|
+
} catch {
|
|
4164
|
+
yamlVal = null;
|
|
4165
|
+
}
|
|
4166
|
+
apcliCfg = ApcliGroup.fromYaml(yamlVal, {
|
|
4167
|
+
registryInjected,
|
|
4168
|
+
name: builtinGroupName
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
4171
|
+
} catch (e) {
|
|
4172
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : String(e)}
|
|
4173
|
+
`);
|
|
4335
4174
|
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4336
4175
|
}
|
|
4337
|
-
|
|
4338
|
-
|
|
4176
|
+
setReservedGroupNames(/* @__PURE__ */ new Set([apcliCfg.name]));
|
|
4177
|
+
const apcliGroup = program.command(apcliCfg.name, { hidden: !apcliCfg.isGroupVisible() }).description("Built-in commands");
|
|
4178
|
+
if (registry) {
|
|
4179
|
+
program._registry = registry;
|
|
4180
|
+
if (executor) {
|
|
4181
|
+
program._executor = executor;
|
|
4182
|
+
}
|
|
4183
|
+
} else {
|
|
4184
|
+
const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
|
|
4185
|
+
void resolvedExtDir;
|
|
4339
4186
|
}
|
|
4340
|
-
let
|
|
4187
|
+
let exposureFilter;
|
|
4341
4188
|
try {
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4189
|
+
if (expose instanceof ExposureFilter) {
|
|
4190
|
+
exposureFilter = expose;
|
|
4191
|
+
} else if (typeof expose === "object" && expose !== null) {
|
|
4192
|
+
exposureFilter = ExposureFilter.fromConfig({ expose });
|
|
4193
|
+
} else {
|
|
4194
|
+
exposureFilter = new ExposureFilter();
|
|
4195
|
+
}
|
|
4196
|
+
} catch (err) {
|
|
4197
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4198
|
+
process.stderr.write(`Error: invalid 'expose' option \u2014 ${msg}
|
|
4345
4199
|
`);
|
|
4346
4200
|
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4347
4201
|
}
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4202
|
+
_registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter);
|
|
4203
|
+
program.addHelpText("after", [
|
|
4204
|
+
"",
|
|
4205
|
+
"Use --help --all-options to show all options (including built-in options).",
|
|
4206
|
+
"Use --help --man to display a formatted man page."
|
|
4207
|
+
].join("\n"));
|
|
4208
|
+
configureManHelp(program, resolvedProgName, appVersion ?? VERSION);
|
|
4209
|
+
if (extraCommands && extraCommands.length > 0) {
|
|
4210
|
+
const _reservedForExtra = /* @__PURE__ */ new Set([apcliCfg.name]);
|
|
4211
|
+
for (const cmd of extraCommands) {
|
|
4212
|
+
const cmdName = cmd.name();
|
|
4213
|
+
if (_reservedForExtra.has(cmdName)) {
|
|
4214
|
+
process.stderr.write(
|
|
4215
|
+
`Error: extraCommands name '${cmdName}' is reserved
|
|
4351
4216
|
`
|
|
4352
|
-
|
|
4353
|
-
|
|
4217
|
+
);
|
|
4218
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4219
|
+
}
|
|
4220
|
+
const existing = program.commands.find((c) => c.name() === cmdName);
|
|
4221
|
+
if (existing) {
|
|
4222
|
+
process.stderr.write(
|
|
4223
|
+
`Error: extraCommands name '${cmdName}' collides with an existing command
|
|
4224
|
+
`
|
|
4225
|
+
);
|
|
4226
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4227
|
+
}
|
|
4228
|
+
program.addCommand(cmd);
|
|
4229
|
+
}
|
|
4354
4230
|
}
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
const onData = (chunk) => chunks.push(chunk);
|
|
4361
|
-
const onEnd = () => {
|
|
4362
|
-
cleanup();
|
|
4363
|
-
resolve2(Buffer.concat(chunks).toString("utf-8"));
|
|
4364
|
-
};
|
|
4365
|
-
const onError = (err) => {
|
|
4366
|
-
cleanup();
|
|
4367
|
-
reject(err);
|
|
4368
|
-
};
|
|
4369
|
-
const cleanup = () => {
|
|
4370
|
-
process.stdin.removeListener("data", onData);
|
|
4371
|
-
process.stdin.removeListener("end", onEnd);
|
|
4372
|
-
process.stdin.removeListener("error", onError);
|
|
4373
|
-
};
|
|
4374
|
-
process.stdin.on("data", onData);
|
|
4375
|
-
process.stdin.on("end", onEnd);
|
|
4376
|
-
process.stdin.on("error", onError);
|
|
4377
|
-
process.stdin.resume();
|
|
4231
|
+
program.hook("preAction", async (thisCommand) => {
|
|
4232
|
+
const opts = thisCommand.opts();
|
|
4233
|
+
const commandsDir = opts.commandsDir;
|
|
4234
|
+
const bindingPath = opts.binding;
|
|
4235
|
+
await applyToolkitIntegration(commandsDir, bindingPath, { allowedPrefixes });
|
|
4378
4236
|
});
|
|
4237
|
+
return program;
|
|
4379
4238
|
}
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4239
|
+
var _ALWAYS_REGISTERED = /* @__PURE__ */ new Set(["exec"]);
|
|
4240
|
+
function _registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter) {
|
|
4241
|
+
const emitUnwiredError = () => {
|
|
4242
|
+
process.stderr.write(
|
|
4243
|
+
"Error: no module registry wired. In standalone mode, pass --extensions-dir <path> to enable module discovery.\n"
|
|
4244
|
+
);
|
|
4245
|
+
process.exit(EXIT_CODES.CONFIG_INVALID);
|
|
4246
|
+
};
|
|
4247
|
+
const effectiveRegistry = registry ?? {
|
|
4248
|
+
list: () => emitUnwiredError(),
|
|
4249
|
+
getDefinition: () => emitUnwiredError()
|
|
4250
|
+
};
|
|
4251
|
+
const TABLE = [
|
|
4252
|
+
{ name: "list", requiresExecutor: false, register: (g) => registerListCommand(g, effectiveRegistry, exposureFilter) },
|
|
4253
|
+
{ name: "describe", requiresExecutor: false, register: (g) => registerDescribeCommand(g, effectiveRegistry) },
|
|
4254
|
+
{ name: "exec", requiresExecutor: true, register: (g, _r, ex) => registerExecCommand(g, effectiveRegistry, ex) },
|
|
4255
|
+
{ name: "validate", requiresExecutor: true, register: (g, _r, ex) => registerValidateCommand(g, effectiveRegistry, ex) },
|
|
4256
|
+
{ name: "init", requiresExecutor: false, register: (g) => registerInitCommand(g) },
|
|
4257
|
+
{ name: "health", requiresExecutor: true, register: (g, _r, ex) => registerHealthCommand(g, ex) },
|
|
4258
|
+
{ name: "usage", requiresExecutor: true, register: (g, _r, ex) => registerUsageCommand(g, ex) },
|
|
4259
|
+
{ name: "enable", requiresExecutor: true, register: (g, _r, ex) => registerEnableCommand(g, ex) },
|
|
4260
|
+
{ name: "disable", requiresExecutor: true, register: (g, _r, ex) => registerDisableCommand(g, ex) },
|
|
4261
|
+
{ name: "reload", requiresExecutor: true, register: (g, _r, ex) => registerReloadCommand(g, ex) },
|
|
4262
|
+
{ name: "config", requiresExecutor: true, register: (g, _r, ex) => registerConfigCommand(g, ex) },
|
|
4263
|
+
{ name: "completion", requiresExecutor: false, register: (g) => registerCompletionCommand(g) },
|
|
4264
|
+
{ name: "describe-pipeline", requiresExecutor: true, register: (g, _r, ex) => registerPipelineCommand(g, ex) }
|
|
4265
|
+
];
|
|
4266
|
+
const _SYSTEM_COMMANDS = /* @__PURE__ */ new Set(["health", "usage", "enable", "disable", "reload", "config"]);
|
|
4267
|
+
const systemModulesAvailable = (() => {
|
|
4268
|
+
if (!executor) return false;
|
|
4269
|
+
const reg = executor.registry ?? registry;
|
|
4270
|
+
if (!reg) return false;
|
|
4271
|
+
try {
|
|
4272
|
+
return reg.getDefinition("system.health.summary") != null;
|
|
4273
|
+
} catch {
|
|
4274
|
+
return false;
|
|
4387
4275
|
}
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4394
|
-
} else
|
|
4395
|
-
|
|
4276
|
+
})();
|
|
4277
|
+
const mode = apcliCfg.resolveVisibility();
|
|
4278
|
+
for (const entry of TABLE) {
|
|
4279
|
+
let shouldRegister;
|
|
4280
|
+
if (mode === "all" || mode === "none") {
|
|
4281
|
+
shouldRegister = true;
|
|
4282
|
+
} else {
|
|
4283
|
+
shouldRegister = _ALWAYS_REGISTERED.has(entry.name) || apcliCfg.isSubcommandIncluded(entry.name);
|
|
4284
|
+
}
|
|
4285
|
+
if (!shouldRegister) continue;
|
|
4286
|
+
if (_SYSTEM_COMMANDS.has(entry.name) && !systemModulesAvailable) continue;
|
|
4287
|
+
if (entry.requiresExecutor && !executor) {
|
|
4288
|
+
if (_ALWAYS_REGISTERED.has(entry.name)) {
|
|
4289
|
+
warn(
|
|
4290
|
+
`apcli.${entry.name} is in _ALWAYS_REGISTERED but no executor is wired \u2014 subcommand unavailable. Pass executor to createCli() or avoid ${entry.name} invocations.`
|
|
4291
|
+
);
|
|
4292
|
+
}
|
|
4293
|
+
continue;
|
|
4396
4294
|
}
|
|
4295
|
+
entry.register(apcliGroup, registry, executor);
|
|
4397
4296
|
}
|
|
4398
|
-
return result;
|
|
4399
4297
|
}
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
import { Command as Command6 } from "commander";
|
|
4404
|
-
init_logger();
|
|
4405
|
-
init_errors();
|
|
4406
|
-
function assertNotReserved(kind, name, moduleId) {
|
|
4407
|
-
if (!getReservedGroupNames().has(name)) return;
|
|
4408
|
-
let msg;
|
|
4409
|
-
if (kind === "group") {
|
|
4410
|
-
msg = `Error: Module '${moduleId}': display.cli.group '${name}' is reserved. Use a different CLI alias or set display.cli.group to another value.
|
|
4411
|
-
`;
|
|
4412
|
-
} else if (kind === "auto-group") {
|
|
4413
|
-
msg = `Error: Module '${moduleId}': auto-group '${name}' is reserved. Rename the module id or set display.cli.group to another value.
|
|
4414
|
-
`;
|
|
4415
|
-
} else {
|
|
4416
|
-
msg = `Error: Module '${moduleId}': top-level CLI name '${name}' is reserved. Use a different CLI alias.
|
|
4417
|
-
`;
|
|
4418
|
-
}
|
|
4419
|
-
process.stderr.write(msg);
|
|
4420
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4298
|
+
var bindingDisplayMap = /* @__PURE__ */ new Map();
|
|
4299
|
+
function lookupBindingDisplay(moduleId) {
|
|
4300
|
+
return bindingDisplayMap.get(moduleId);
|
|
4421
4301
|
}
|
|
4422
|
-
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
helpTextMaxLength;
|
|
4426
|
-
commandCache = /* @__PURE__ */ new Map();
|
|
4427
|
-
/** alias -> canonical module_id (populated lazily) */
|
|
4428
|
-
aliasMap = /* @__PURE__ */ new Map();
|
|
4429
|
-
/** module_id -> descriptor cache (populated during alias map build) */
|
|
4430
|
-
descriptorCache = /* @__PURE__ */ new Map();
|
|
4431
|
-
aliasMapBuilt = false;
|
|
4432
|
-
constructor(registry, executor, helpTextMaxLength = 1e3) {
|
|
4433
|
-
this.registry = registry;
|
|
4434
|
-
this.executor = executor;
|
|
4435
|
-
this.helpTextMaxLength = helpTextMaxLength;
|
|
4436
|
-
}
|
|
4437
|
-
/**
|
|
4438
|
-
* Build alias->module_id map from display overlay metadata.
|
|
4439
|
-
*/
|
|
4440
|
-
buildAliasMap() {
|
|
4441
|
-
if (this.aliasMapBuilt) {
|
|
4442
|
-
return;
|
|
4443
|
-
}
|
|
4444
|
-
try {
|
|
4445
|
-
for (const descriptor of this.registry.listModules()) {
|
|
4446
|
-
const moduleId = descriptor.id;
|
|
4447
|
-
this.descriptorCache.set(moduleId, descriptor);
|
|
4448
|
-
const display = getDisplay(descriptor);
|
|
4449
|
-
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
4450
|
-
const cliAlias = cliDisplay.alias;
|
|
4451
|
-
if (cliAlias && cliAlias !== moduleId) {
|
|
4452
|
-
this.aliasMap.set(cliAlias, moduleId);
|
|
4453
|
-
}
|
|
4454
|
-
}
|
|
4455
|
-
this.aliasMapBuilt = true;
|
|
4456
|
-
} catch {
|
|
4457
|
-
warn("Failed to build alias map from registry");
|
|
4458
|
-
}
|
|
4302
|
+
async function applyToolkitIntegration(commandsDir, bindingPath, options = {}) {
|
|
4303
|
+
if (!commandsDir && !bindingPath) {
|
|
4304
|
+
return;
|
|
4459
4305
|
}
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
*/
|
|
4463
|
-
listCommands() {
|
|
4464
|
-
this.buildAliasMap();
|
|
4465
|
-
const reverse = /* @__PURE__ */ new Map();
|
|
4466
|
-
for (const [alias, moduleId] of this.aliasMap) {
|
|
4467
|
-
reverse.set(moduleId, alias);
|
|
4468
|
-
}
|
|
4469
|
-
const moduleIds = this.registry.listModules().map((m) => m.id);
|
|
4470
|
-
const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
|
|
4471
|
-
return [...new Set(names)].sort();
|
|
4306
|
+
if (commandsDir) {
|
|
4307
|
+
warn("Convention scanning not available in the TypeScript toolkit");
|
|
4472
4308
|
}
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
}
|
|
4480
|
-
this.buildAliasMap();
|
|
4481
|
-
const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
|
|
4482
|
-
let moduleDef = this.descriptorCache.get(moduleId);
|
|
4483
|
-
if (!moduleDef) {
|
|
4484
|
-
moduleDef = this.registry.getModule(moduleId) ?? void 0;
|
|
4485
|
-
}
|
|
4486
|
-
if (!moduleDef) {
|
|
4487
|
-
return null;
|
|
4309
|
+
if (bindingPath) {
|
|
4310
|
+
try {
|
|
4311
|
+
await loadBindingDisplayOverlay(bindingPath, options.allowedPrefixes);
|
|
4312
|
+
} catch (err) {
|
|
4313
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4314
|
+
warn(`apcore-toolkit: failed to load binding '${bindingPath}': ${msg}`);
|
|
4488
4315
|
}
|
|
4489
|
-
const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
|
|
4490
|
-
this.commandCache.set(cmdName, cmd);
|
|
4491
|
-
return cmd;
|
|
4492
4316
|
}
|
|
4493
|
-
}
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4317
|
+
}
|
|
4318
|
+
async function loadBindingDisplayOverlay(bindingPath, allowedPrefixes) {
|
|
4319
|
+
const loader = new BindingLoader();
|
|
4320
|
+
const scanned = loader.load(bindingPath);
|
|
4321
|
+
const resolver = new DisplayResolver();
|
|
4322
|
+
const resolved = resolver.resolve(scanned, { bindingPath });
|
|
4323
|
+
const prefixes = allowedPrefixes && allowedPrefixes.length > 0 ? allowedPrefixes : null;
|
|
4324
|
+
const isTargetAllowed = (target) => {
|
|
4325
|
+
if (!prefixes) return true;
|
|
4326
|
+
if (typeof target !== "string" || target.length === 0) return true;
|
|
4327
|
+
return prefixes.some((p) => target.startsWith(p));
|
|
4328
|
+
};
|
|
4329
|
+
for (const mod of resolved) {
|
|
4330
|
+
if (!mod || typeof mod !== "object") continue;
|
|
4331
|
+
const entry = mod;
|
|
4332
|
+
const id = typeof entry.moduleId === "string" ? entry.moduleId : null;
|
|
4333
|
+
if (!id) continue;
|
|
4334
|
+
if (!isTargetAllowed(entry.target)) {
|
|
4335
|
+
warn(
|
|
4336
|
+
`apcore-toolkit: dropped binding entry '${id}' \u2014 target '${String(entry.target)}' is outside allowedPrefixes`
|
|
4511
4337
|
);
|
|
4512
|
-
|
|
4513
|
-
this.command.addCommand(cmd);
|
|
4514
|
-
}
|
|
4515
|
-
}
|
|
4516
|
-
listCommands() {
|
|
4517
|
-
return [...this.members.keys()].sort();
|
|
4518
|
-
}
|
|
4519
|
-
getCommand(cmdName) {
|
|
4520
|
-
if (this._cmdCache.has(cmdName)) {
|
|
4521
|
-
return this._cmdCache.get(cmdName);
|
|
4338
|
+
continue;
|
|
4522
4339
|
}
|
|
4523
|
-
const
|
|
4524
|
-
|
|
4525
|
-
|
|
4340
|
+
const meta = entry.metadata ?? {};
|
|
4341
|
+
const display = meta.display;
|
|
4342
|
+
if (display && typeof display === "object" && !Array.isArray(display)) {
|
|
4343
|
+
bindingDisplayMap.set(id, display);
|
|
4526
4344
|
}
|
|
4527
|
-
const [, descriptor] = entry;
|
|
4528
|
-
const cmd = buildModuleCommand(
|
|
4529
|
-
descriptor,
|
|
4530
|
-
this._executor,
|
|
4531
|
-
this._helpTextMaxLength,
|
|
4532
|
-
cmdName
|
|
4533
|
-
);
|
|
4534
|
-
this._cmdCache.set(cmdName, cmd);
|
|
4535
|
-
return cmd;
|
|
4536
|
-
}
|
|
4537
|
-
};
|
|
4538
|
-
var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
4539
|
-
/** groupName -> { cmdName -> [moduleId, descriptor] } */
|
|
4540
|
-
groupMap = /* @__PURE__ */ new Map();
|
|
4541
|
-
/** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
|
|
4542
|
-
topLevelModules = /* @__PURE__ */ new Map();
|
|
4543
|
-
/** Cached LazyGroup instances */
|
|
4544
|
-
groupCache = /* @__PURE__ */ new Map();
|
|
4545
|
-
groupMapBuilt = false;
|
|
4546
|
-
/** Exposure filter (FE-12) — controls which modules appear as CLI commands */
|
|
4547
|
-
exposureFilter;
|
|
4548
|
-
/** Effective group depth (CLAUDE.md v0.6.0): constructor arg > APCORE_CLI_GROUP_DEPTH env > 1. */
|
|
4549
|
-
groupDepth;
|
|
4550
|
-
constructor(registry, executor, helpTextMaxLength = 1e3, exposureFilter, groupDepth) {
|
|
4551
|
-
super(registry, executor, helpTextMaxLength);
|
|
4552
|
-
this.exposureFilter = exposureFilter ?? new ExposureFilter();
|
|
4553
|
-
this.groupDepth = _GroupedModuleGroup.resolveGroupDepth(groupDepth);
|
|
4554
4345
|
}
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
|
|
4346
|
+
}
|
|
4347
|
+
function main(progName) {
|
|
4348
|
+
verboseHelp = hasVerboseFlag();
|
|
4349
|
+
const program = createCli({
|
|
4350
|
+
progName,
|
|
4351
|
+
allOptions: verboseHelp,
|
|
4352
|
+
version: VERSION,
|
|
4353
|
+
description: `${progName ?? "apcore-cli"} \u2014 execute modules from the command line`
|
|
4354
|
+
});
|
|
4355
|
+
try {
|
|
4356
|
+
program.parse(process.argv);
|
|
4357
|
+
} catch (error) {
|
|
4358
|
+
if (error instanceof CommanderError) {
|
|
4359
|
+
process.exit(error.exitCode);
|
|
4562
4360
|
}
|
|
4563
|
-
const
|
|
4564
|
-
if (
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
return parsed;
|
|
4568
|
-
}
|
|
4361
|
+
const code = exitCodeForError(error);
|
|
4362
|
+
if (error instanceof Error) {
|
|
4363
|
+
process.stderr.write(`Error: ${error.message}
|
|
4364
|
+
`);
|
|
4569
4365
|
}
|
|
4570
|
-
|
|
4366
|
+
process.exit(code);
|
|
4571
4367
|
}
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4368
|
+
}
|
|
4369
|
+
function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
|
|
4370
|
+
const moduleId = moduleDef.moduleId;
|
|
4371
|
+
let resolvedSchema = {};
|
|
4372
|
+
let schemaOptions = [];
|
|
4373
|
+
const display = getDisplay(moduleDef);
|
|
4374
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
4375
|
+
const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
|
|
4376
|
+
const cmdHelp = cliDisplay.description ?? moduleDef.description;
|
|
4377
|
+
const inputSchema = moduleDef.inputSchema;
|
|
4378
|
+
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
4379
|
+
try {
|
|
4380
|
+
resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
|
|
4381
|
+
} catch (err) {
|
|
4382
|
+
if (err instanceof MaxDepthExceededError || err instanceof CircularRefError) {
|
|
4383
|
+
process.stderr.write(`Error: ${err.message}
|
|
4384
|
+
`);
|
|
4385
|
+
process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
|
|
4386
|
+
}
|
|
4387
|
+
if (err instanceof UnresolvableRefError) {
|
|
4388
|
+
process.stderr.write(`Error: ${err.message}
|
|
4389
|
+
`);
|
|
4390
|
+
process.exit(EXIT_CODES.SCHEMA_VALIDATION_ERROR);
|
|
4391
|
+
}
|
|
4392
|
+
resolvedSchema = inputSchema;
|
|
4593
4393
|
}
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
|
|
4394
|
+
schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
|
|
4395
|
+
}
|
|
4396
|
+
const cmd = new Command6(effectiveCmdName).description(cmdHelp);
|
|
4397
|
+
const inputOpt = new Option4("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
|
|
4398
|
+
const yesOpt = new Option4("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
|
|
4399
|
+
const largeInputOpt = new Option4("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
|
|
4400
|
+
const formatOpt = new Option4("--format <format>", "Output format: json, table, csv, yaml, jsonl.").choices(["json", "table", "csv", "yaml", "jsonl"]);
|
|
4401
|
+
const fieldsOpt = new Option4("--fields <fields>", "Comma-separated dot-paths to select from the result (e.g., 'status,data.count').");
|
|
4402
|
+
const sandboxOpt = new Option4("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
|
|
4403
|
+
const dryRunOpt = new Option4("--dry-run", "Run preflight checks without executing the module. Shows validation results.").default(false);
|
|
4404
|
+
const traceOpt = new Option4("--trace", "Show execution pipeline trace with per-step timing after the result.").default(false);
|
|
4405
|
+
const streamOpt = new Option4("--stream", "Stream module output as JSONL (one JSON object per line, flushed immediately).").default(false);
|
|
4406
|
+
const strategyOpt = new Option4("--strategy <name>", "Execution pipeline strategy: standard (default), internal, testing, performance.").choices(["standard", "internal", "testing", "performance", "minimal"]);
|
|
4407
|
+
const approvalTimeoutOpt = new Option4("--approval-timeout <seconds>", "Override approval prompt timeout in seconds (default: 60).").argParser(parseInt);
|
|
4408
|
+
const approvalTokenOpt = new Option4("--approval-token <token>", "Resume a pending approval with the given token (for async approval flows).");
|
|
4409
|
+
if (!verbose) {
|
|
4410
|
+
inputOpt.hideHelp();
|
|
4411
|
+
yesOpt.hideHelp();
|
|
4412
|
+
largeInputOpt.hideHelp();
|
|
4413
|
+
formatOpt.hideHelp();
|
|
4414
|
+
fieldsOpt.hideHelp();
|
|
4415
|
+
dryRunOpt.hideHelp();
|
|
4416
|
+
traceOpt.hideHelp();
|
|
4417
|
+
streamOpt.hideHelp();
|
|
4418
|
+
strategyOpt.hideHelp();
|
|
4419
|
+
approvalTimeoutOpt.hideHelp();
|
|
4420
|
+
approvalTokenOpt.hideHelp();
|
|
4421
|
+
}
|
|
4422
|
+
cmd.addOption(inputOpt);
|
|
4423
|
+
cmd.addOption(yesOpt);
|
|
4424
|
+
cmd.addOption(largeInputOpt);
|
|
4425
|
+
cmd.addOption(formatOpt);
|
|
4426
|
+
cmd.addOption(fieldsOpt);
|
|
4427
|
+
cmd.addOption(sandboxOpt);
|
|
4428
|
+
cmd.addOption(dryRunOpt);
|
|
4429
|
+
cmd.addOption(traceOpt);
|
|
4430
|
+
cmd.addOption(streamOpt);
|
|
4431
|
+
cmd.addOption(strategyOpt);
|
|
4432
|
+
cmd.addOption(approvalTimeoutOpt);
|
|
4433
|
+
cmd.addOption(approvalTokenOpt);
|
|
4434
|
+
const footerParts = [];
|
|
4435
|
+
if (!verbose) {
|
|
4436
|
+
footerParts.push("Use --all-options to show all options (including built-in options).");
|
|
4437
|
+
}
|
|
4438
|
+
if (docsUrl) {
|
|
4439
|
+
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
|
|
4440
|
+
}
|
|
4441
|
+
if (footerParts.length > 0) {
|
|
4442
|
+
cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
|
|
4443
|
+
}
|
|
4444
|
+
for (const opt of schemaOptions) {
|
|
4445
|
+
if (opt.isBooleanFlag) {
|
|
4446
|
+
const flagBase = opt.name.replace(/_/g, "-");
|
|
4447
|
+
cmd.addOption(
|
|
4448
|
+
new Option4(`--${flagBase}`, opt.description).default(
|
|
4449
|
+
opt.defaultValue
|
|
4450
|
+
)
|
|
4451
|
+
);
|
|
4452
|
+
cmd.addOption(new Option4(`--no-${flagBase}`).hideHelp());
|
|
4453
|
+
} else if (opt.parseArg) {
|
|
4454
|
+
cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
|
|
4455
|
+
} else {
|
|
4456
|
+
cmd.option(opt.flags, opt.description, opt.defaultValue);
|
|
4601
4457
|
}
|
|
4602
|
-
return [null, cliName];
|
|
4603
4458
|
}
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
|
|
4613
|
-
|
|
4459
|
+
cmd.action(async (options) => {
|
|
4460
|
+
const stdinFlag = options.input;
|
|
4461
|
+
const autoApprove = options.yes;
|
|
4462
|
+
const largeInput = options.largeInput;
|
|
4463
|
+
const outputFormat = options.format;
|
|
4464
|
+
const outputFields = options.fields;
|
|
4465
|
+
const sandboxEnabled = options.sandbox;
|
|
4466
|
+
const dryRun = options.dryRun;
|
|
4467
|
+
const traceFlag = options.trace;
|
|
4468
|
+
const streamFlag = options.stream;
|
|
4469
|
+
const strategyName = resolveStringOption(options.strategy, process.env.APCORE_CLI_STRATEGY);
|
|
4470
|
+
const approvalTimeout = resolveIntOption(
|
|
4471
|
+
options.approvalTimeout,
|
|
4472
|
+
process.env.APCORE_CLI_APPROVAL_TIMEOUT,
|
|
4473
|
+
60
|
|
4474
|
+
);
|
|
4475
|
+
const approvalToken = options.approvalToken;
|
|
4476
|
+
const schemaKwargs = {};
|
|
4477
|
+
for (const opt of schemaOptions) {
|
|
4478
|
+
const commanderKey = opt.name.replace(/_([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
4479
|
+
if (commanderKey in options) {
|
|
4480
|
+
schemaKwargs[opt.name] = options[commanderKey];
|
|
4481
|
+
} else if (opt.name in options) {
|
|
4482
|
+
schemaKwargs[opt.name] = options[opt.name];
|
|
4483
|
+
}
|
|
4614
4484
|
}
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
|
|
4485
|
+
let merged = {};
|
|
4486
|
+
const startTime = performance.now();
|
|
4487
|
+
try {
|
|
4488
|
+
merged = await collectInput(stdinFlag, schemaKwargs, largeInput);
|
|
4489
|
+
const reconverted = reconvertEnumValues(merged, schemaOptions);
|
|
4490
|
+
merged = reconverted;
|
|
4491
|
+
if (dryRun) {
|
|
4492
|
+
if (!executor.validate) {
|
|
4493
|
+
process.stderr.write("Error: Executor does not support validate.\n");
|
|
4494
|
+
process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
|
|
4495
|
+
}
|
|
4496
|
+
const preflight = await executor.validate(moduleId, merged);
|
|
4497
|
+
formatPreflightResult(preflight, outputFormat);
|
|
4498
|
+
if (traceFlag) {
|
|
4499
|
+
const pureSteps = /* @__PURE__ */ new Set([
|
|
4500
|
+
"context_creation",
|
|
4501
|
+
"call_chain_guard",
|
|
4502
|
+
"module_lookup",
|
|
4503
|
+
"acl_check",
|
|
4504
|
+
"input_validation"
|
|
4505
|
+
]);
|
|
4506
|
+
const allSteps = [
|
|
4507
|
+
"context_creation",
|
|
4508
|
+
"call_chain_guard",
|
|
4509
|
+
"module_lookup",
|
|
4510
|
+
"acl_check",
|
|
4511
|
+
"approval_gate",
|
|
4512
|
+
"middleware_before",
|
|
4513
|
+
"input_validation",
|
|
4514
|
+
"execute",
|
|
4515
|
+
"output_validation",
|
|
4516
|
+
"middleware_after",
|
|
4517
|
+
"return_result"
|
|
4518
|
+
];
|
|
4519
|
+
process.stderr.write("\nPipeline preview (dry-run):\n");
|
|
4520
|
+
for (const s of allSteps) {
|
|
4521
|
+
if (pureSteps.has(s)) {
|
|
4522
|
+
process.stderr.write(` \u2713 ${s.padEnd(24)} (pure \u2014 would execute)
|
|
4523
|
+
`);
|
|
4524
|
+
} else {
|
|
4525
|
+
process.stderr.write(` \u25CB ${s.padEnd(24)} (impure \u2014 skipped in dry-run)
|
|
4526
|
+
`);
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
|
|
4621
4531
|
}
|
|
4622
|
-
if (
|
|
4623
|
-
|
|
4532
|
+
if (resolvedSchema.properties) {
|
|
4533
|
+
const validationErr = validateInputSchema(resolvedSchema, merged);
|
|
4534
|
+
if (validationErr) {
|
|
4535
|
+
throw new SchemaValidationError(`Validation failed: ${validationErr}`);
|
|
4536
|
+
}
|
|
4624
4537
|
}
|
|
4625
|
-
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4538
|
+
if (approvalToken) {
|
|
4539
|
+
merged._approval_token = approvalToken;
|
|
4540
|
+
}
|
|
4541
|
+
await checkApproval(moduleDef, autoApprove, approvalTimeout);
|
|
4542
|
+
if (streamFlag) {
|
|
4543
|
+
if (resolveFormat(outputFormat) === "table") {
|
|
4544
|
+
process.stderr.write("Warning: Streaming mode always outputs JSONL; --format table is ignored.\n");
|
|
4545
|
+
}
|
|
4546
|
+
const annotations = moduleDef.annotations;
|
|
4547
|
+
const isStreaming = annotations?.streaming === true;
|
|
4548
|
+
if (!isStreaming) {
|
|
4549
|
+
process.stderr.write(
|
|
4550
|
+
`Warning: Module '${moduleId}' does not declare streaming support. Falling back to standard execution.
|
|
4551
|
+
`
|
|
4552
|
+
);
|
|
4553
|
+
}
|
|
4554
|
+
if (isStreaming && executor.stream) {
|
|
4555
|
+
let chunks = 0;
|
|
4556
|
+
for await (const chunk of executor.stream(moduleId, merged)) {
|
|
4557
|
+
chunks++;
|
|
4558
|
+
process.stdout.write(JSON.stringify(chunk) + "\n");
|
|
4559
|
+
if (process.stderr.isTTY) {
|
|
4560
|
+
process.stderr.write(`\rStreaming ${moduleId}... (${chunks} chunks)`);
|
|
4561
|
+
}
|
|
4562
|
+
}
|
|
4563
|
+
if (process.stderr.isTTY) {
|
|
4564
|
+
process.stderr.write("\n");
|
|
4565
|
+
}
|
|
4566
|
+
const durationMs2 = Math.round(performance.now() - startTime);
|
|
4567
|
+
const { getAuditLogger: getAuditLogger3 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4568
|
+
const auditLogger2 = getAuditLogger3();
|
|
4569
|
+
if (auditLogger2) {
|
|
4570
|
+
auditLogger2.logExecution(moduleId, merged, "success", 0, durationMs2);
|
|
4571
|
+
}
|
|
4572
|
+
return;
|
|
4573
|
+
}
|
|
4630
4574
|
}
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4575
|
+
if (traceFlag && executor.callWithTrace) {
|
|
4576
|
+
const [result2, trace] = await executor.callWithTrace(
|
|
4577
|
+
moduleId,
|
|
4578
|
+
merged,
|
|
4579
|
+
strategyName ? { strategy: strategyName } : void 0
|
|
4580
|
+
);
|
|
4581
|
+
const durationMs2 = Math.round(performance.now() - startTime);
|
|
4582
|
+
const resolved = resolveFormat(outputFormat);
|
|
4583
|
+
if (resolved === "json" || !process.stdout.isTTY) {
|
|
4584
|
+
const traceData = {
|
|
4585
|
+
strategy: trace.strategyName,
|
|
4586
|
+
total_duration_ms: trace.totalDurationMs,
|
|
4587
|
+
success: trace.success,
|
|
4588
|
+
steps: trace.steps.map((s) => ({
|
|
4589
|
+
name: s.name,
|
|
4590
|
+
duration_ms: s.durationMs,
|
|
4591
|
+
skipped: s.skipped,
|
|
4592
|
+
...s.skipped ? { skip_reason: s.skipReason ?? null } : {}
|
|
4593
|
+
}))
|
|
4594
|
+
};
|
|
4595
|
+
let output;
|
|
4596
|
+
if (typeof result2 === "object" && result2 !== null && !Array.isArray(result2)) {
|
|
4597
|
+
output = { ...result2, _trace: traceData };
|
|
4598
|
+
} else {
|
|
4599
|
+
output = { result: result2, _trace: traceData };
|
|
4600
|
+
}
|
|
4601
|
+
process.stdout.write(JSON.stringify(output, null, 2) + "\n");
|
|
4602
|
+
} else {
|
|
4603
|
+
formatExecResult(result2, outputFormat, outputFields);
|
|
4604
|
+
const stepCount = trace.steps.length;
|
|
4605
|
+
process.stderr.write(
|
|
4606
|
+
`
|
|
4607
|
+
Pipeline Trace (strategy: ${trace.strategyName}, ${stepCount} steps, ${trace.totalDurationMs.toFixed(1)}ms)
|
|
4608
|
+
`
|
|
4609
|
+
);
|
|
4610
|
+
for (const s of trace.steps) {
|
|
4611
|
+
if (s.skipped) {
|
|
4612
|
+
const reason = s.skipReason ?? "n/a";
|
|
4613
|
+
process.stderr.write(` \u25CB ${s.name.padEnd(24)} ${"\u2014".padStart(8)} skipped (${reason})
|
|
4614
|
+
`);
|
|
4615
|
+
} else {
|
|
4616
|
+
process.stderr.write(` \u2713 ${s.name.padEnd(24)} ${(s.durationMs.toFixed(1) + "ms").padStart(8)}
|
|
4617
|
+
`);
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
const { getAuditLogger: getAL2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4622
|
+
const al2 = getAL2();
|
|
4623
|
+
if (al2) {
|
|
4624
|
+
al2.logExecution(moduleId, merged, "success", 0, durationMs2);
|
|
4625
|
+
}
|
|
4626
|
+
return;
|
|
4634
4627
|
}
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4628
|
+
let result;
|
|
4629
|
+
if (strategyName && executor.callWithTrace) {
|
|
4630
|
+
const [res] = await executor.callWithTrace(
|
|
4631
|
+
moduleId,
|
|
4632
|
+
merged,
|
|
4633
|
+
{ strategy: strategyName }
|
|
4641
4634
|
);
|
|
4642
|
-
|
|
4635
|
+
result = res;
|
|
4636
|
+
if (strategyName !== "standard" && process.stderr.isTTY) {
|
|
4637
|
+
process.stderr.write(`Warning: Using '${strategyName}' strategy.
|
|
4638
|
+
`);
|
|
4639
|
+
}
|
|
4643
4640
|
} else {
|
|
4644
|
-
|
|
4645
|
-
|
|
4641
|
+
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
4642
|
+
const sandbox = new Sandbox2(sandboxEnabled);
|
|
4643
|
+
result = await sandbox.execute(moduleId, merged, executor);
|
|
4644
|
+
}
|
|
4645
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
4646
|
+
formatExecResult(result, outputFormat, outputFields);
|
|
4647
|
+
const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4648
|
+
const auditLogger = getAuditLogger2();
|
|
4649
|
+
if (auditLogger) {
|
|
4650
|
+
auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
|
|
4651
|
+
}
|
|
4652
|
+
} catch (err) {
|
|
4653
|
+
const exitCode = exitCodeForError(err);
|
|
4654
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
4655
|
+
try {
|
|
4656
|
+
const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
4657
|
+
const auditLogger = getAuditLogger2();
|
|
4658
|
+
if (auditLogger) {
|
|
4659
|
+
auditLogger.logExecution(moduleId, merged, "error", exitCode, durationMs);
|
|
4646
4660
|
}
|
|
4647
|
-
|
|
4661
|
+
} catch {
|
|
4662
|
+
}
|
|
4663
|
+
if (outputFormat === "json" || !process.stderr.isTTY) {
|
|
4664
|
+
emitErrorJson(err, exitCode);
|
|
4665
|
+
} else {
|
|
4666
|
+
emitErrorTty(err, exitCode);
|
|
4648
4667
|
}
|
|
4668
|
+
process.exit(exitCode);
|
|
4669
|
+
}
|
|
4670
|
+
});
|
|
4671
|
+
return cmd;
|
|
4672
|
+
}
|
|
4673
|
+
async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
|
|
4674
|
+
const cliKwargsNonNull = {};
|
|
4675
|
+
for (const [k, v] of Object.entries(cliKwargs)) {
|
|
4676
|
+
if (v !== null && v !== void 0) {
|
|
4677
|
+
cliKwargsNonNull[k] = v;
|
|
4649
4678
|
}
|
|
4650
|
-
this.groupMapBuilt = true;
|
|
4651
4679
|
}
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
*
|
|
4655
|
-
* FE-13: the built-in subcommand list is no longer folded in here — those
|
|
4656
|
-
* commands live under the `apcli` prefix and are registered directly by
|
|
4657
|
-
* `createCli`.
|
|
4658
|
-
*/
|
|
4659
|
-
listCommands() {
|
|
4660
|
-
this.buildGroupMap();
|
|
4661
|
-
const reserved = getReservedGroupNames();
|
|
4662
|
-
const groupNames = [...this.groupMap.keys()].filter(
|
|
4663
|
-
(g) => !reserved.has(g)
|
|
4664
|
-
);
|
|
4665
|
-
const topNames = [...this.topLevelModules.keys()];
|
|
4666
|
-
return [.../* @__PURE__ */ new Set([...groupNames, ...topNames])].sort();
|
|
4680
|
+
if (!stdinFlag) {
|
|
4681
|
+
return cliKwargsNonNull;
|
|
4667
4682
|
}
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4675
|
-
|
|
4676
|
-
|
|
4677
|
-
|
|
4678
|
-
|
|
4679
|
-
|
|
4680
|
-
|
|
4681
|
-
|
|
4682
|
-
);
|
|
4683
|
-
this.groupCache.set(cmdName, lazyGrp);
|
|
4684
|
-
return lazyGrp.command;
|
|
4685
|
-
}
|
|
4686
|
-
if (this.topLevelModules.has(cmdName)) {
|
|
4687
|
-
if (this.commandCache.has(cmdName)) {
|
|
4688
|
-
return this.commandCache.get(cmdName);
|
|
4689
|
-
}
|
|
4690
|
-
const [, descriptor] = this.topLevelModules.get(cmdName);
|
|
4691
|
-
const cmd = buildModuleCommand(
|
|
4692
|
-
descriptor,
|
|
4693
|
-
this.executor,
|
|
4694
|
-
this.helpTextMaxLength,
|
|
4695
|
-
cmdName
|
|
4696
|
-
);
|
|
4697
|
-
this.commandCache.set(cmdName, cmd);
|
|
4698
|
-
return cmd;
|
|
4683
|
+
let raw;
|
|
4684
|
+
let source;
|
|
4685
|
+
if (stdinFlag === "-") {
|
|
4686
|
+
raw = await readStdin();
|
|
4687
|
+
source = "STDIN";
|
|
4688
|
+
} else {
|
|
4689
|
+
source = `file '${stdinFlag}'`;
|
|
4690
|
+
try {
|
|
4691
|
+
raw = readFileSync3(stdinFlag, "utf-8");
|
|
4692
|
+
} catch (err) {
|
|
4693
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
4694
|
+
process.stderr.write(`Error: Could not read input ${source}: ${msg}
|
|
4695
|
+
`);
|
|
4696
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4699
4697
|
}
|
|
4700
|
-
return null;
|
|
4701
4698
|
}
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4699
|
+
const rawSize = Buffer.byteLength(raw, "utf-8");
|
|
4700
|
+
if (rawSize > 10485760 && !largeInput) {
|
|
4701
|
+
process.stderr.write(
|
|
4702
|
+
`Error: ${source} input exceeds 10MB limit. Use --large-input to override.
|
|
4703
|
+
`
|
|
4704
|
+
);
|
|
4705
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4705
4706
|
}
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
return this.topLevelModules;
|
|
4707
|
+
if (!raw) {
|
|
4708
|
+
return cliKwargsNonNull;
|
|
4709
4709
|
}
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4710
|
+
let parsed;
|
|
4711
|
+
try {
|
|
4712
|
+
parsed = JSON.parse(raw);
|
|
4713
|
+
} catch {
|
|
4714
|
+
process.stderr.write(`Error: ${source} does not contain valid JSON.
|
|
4715
|
+
`);
|
|
4716
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4713
4717
|
}
|
|
4714
|
-
|
|
4718
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
4719
|
+
process.stderr.write(
|
|
4720
|
+
`Error: ${source} JSON must be an object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.
|
|
4721
|
+
`
|
|
4722
|
+
);
|
|
4723
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
4724
|
+
}
|
|
4725
|
+
return { ...parsed, ...cliKwargsNonNull };
|
|
4726
|
+
}
|
|
4727
|
+
function readStdin() {
|
|
4728
|
+
return new Promise((resolve2, reject) => {
|
|
4729
|
+
const chunks = [];
|
|
4730
|
+
const onData = (chunk) => chunks.push(chunk);
|
|
4731
|
+
const onEnd = () => {
|
|
4732
|
+
cleanup();
|
|
4733
|
+
resolve2(Buffer.concat(chunks).toString("utf-8"));
|
|
4734
|
+
};
|
|
4735
|
+
const onError = (err) => {
|
|
4736
|
+
cleanup();
|
|
4737
|
+
reject(err);
|
|
4738
|
+
};
|
|
4739
|
+
const cleanup = () => {
|
|
4740
|
+
process.stdin.removeListener("data", onData);
|
|
4741
|
+
process.stdin.removeListener("end", onEnd);
|
|
4742
|
+
process.stdin.removeListener("error", onError);
|
|
4743
|
+
};
|
|
4744
|
+
process.stdin.on("data", onData);
|
|
4745
|
+
process.stdin.on("end", onEnd);
|
|
4746
|
+
process.stdin.on("error", onError);
|
|
4747
|
+
process.stdin.resume();
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
function reconvertEnumValues(kwargs, options) {
|
|
4751
|
+
const result = { ...kwargs };
|
|
4752
|
+
for (const opt of options) {
|
|
4753
|
+
if (!opt.enumOriginalTypes) continue;
|
|
4754
|
+
const paramName = opt.name;
|
|
4755
|
+
if (!(paramName in result) || result[paramName] === null || result[paramName] === void 0) {
|
|
4756
|
+
continue;
|
|
4757
|
+
}
|
|
4758
|
+
const strVal = String(result[paramName]);
|
|
4759
|
+
const origType = opt.enumOriginalTypes[strVal];
|
|
4760
|
+
if (origType === "int") {
|
|
4761
|
+
result[paramName] = parseInt(strVal, 10);
|
|
4762
|
+
} else if (origType === "float") {
|
|
4763
|
+
result[paramName] = parseFloat(strVal);
|
|
4764
|
+
} else if (origType === "bool") {
|
|
4765
|
+
result[paramName] = strVal.toLowerCase() === "true";
|
|
4766
|
+
}
|
|
4767
|
+
}
|
|
4768
|
+
return result;
|
|
4769
|
+
}
|
|
4715
4770
|
|
|
4716
4771
|
// src/index.ts
|
|
4717
4772
|
init_errors();
|