apcore-cli 0.4.0 → 0.5.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/dist/bin/apcore-cli.js +225 -9
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/index.d.ts +12 -1
- package/dist/index.js +1122 -1024
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -50,11 +56,19 @@ function exitCodeForError(error2) {
|
|
|
50
56
|
SCHEMA_CIRCULAR_REF: EXIT_CODES.SCHEMA_CIRCULAR_REF,
|
|
51
57
|
APPROVAL_DENIED: EXIT_CODES.APPROVAL_DENIED,
|
|
52
58
|
APPROVAL_TIMEOUT: EXIT_CODES.APPROVAL_TIMEOUT,
|
|
59
|
+
APPROVAL_PENDING: EXIT_CODES.APPROVAL_DENIED,
|
|
53
60
|
CONFIG_NOT_FOUND: EXIT_CODES.CONFIG_NOT_FOUND,
|
|
54
61
|
CONFIG_INVALID: EXIT_CODES.CONFIG_INVALID,
|
|
55
62
|
MODULE_EXECUTE_ERROR: EXIT_CODES.MODULE_EXECUTE_ERROR,
|
|
56
63
|
MODULE_TIMEOUT: EXIT_CODES.MODULE_TIMEOUT,
|
|
57
|
-
ACL_DENIED: EXIT_CODES.ACL_DENIED
|
|
64
|
+
ACL_DENIED: EXIT_CODES.ACL_DENIED,
|
|
65
|
+
// Config Bus errors (apcore >= 0.15.0)
|
|
66
|
+
CONFIG_NAMESPACE_RESERVED: EXIT_CODES.CONFIG_NAMESPACE_RESERVED,
|
|
67
|
+
CONFIG_NAMESPACE_DUPLICATE: EXIT_CODES.CONFIG_NAMESPACE_DUPLICATE,
|
|
68
|
+
CONFIG_ENV_PREFIX_CONFLICT: EXIT_CODES.CONFIG_ENV_PREFIX_CONFLICT,
|
|
69
|
+
CONFIG_MOUNT_ERROR: EXIT_CODES.CONFIG_MOUNT_ERROR,
|
|
70
|
+
CONFIG_BIND_ERROR: EXIT_CODES.CONFIG_BIND_ERROR,
|
|
71
|
+
ERROR_FORMATTER_DUPLICATE: EXIT_CODES.ERROR_FORMATTER_DUPLICATE
|
|
58
72
|
};
|
|
59
73
|
if (code && code in codeMap) {
|
|
60
74
|
return codeMap[code];
|
|
@@ -124,6 +138,13 @@ var init_errors = __esm({
|
|
|
124
138
|
CONFIG_INVALID: 47,
|
|
125
139
|
SCHEMA_CIRCULAR_REF: 48,
|
|
126
140
|
ACL_DENIED: 77,
|
|
141
|
+
// Config Bus errors (apcore >= 0.15.0)
|
|
142
|
+
CONFIG_NAMESPACE_RESERVED: 78,
|
|
143
|
+
CONFIG_NAMESPACE_DUPLICATE: 78,
|
|
144
|
+
CONFIG_ENV_PREFIX_CONFLICT: 78,
|
|
145
|
+
CONFIG_MOUNT_ERROR: 66,
|
|
146
|
+
CONFIG_BIND_ERROR: 65,
|
|
147
|
+
ERROR_FORMATTER_DUPLICATE: 70,
|
|
127
148
|
KEYBOARD_INTERRUPT: 130
|
|
128
149
|
};
|
|
129
150
|
}
|
|
@@ -137,9 +158,9 @@ __export(audit_exports, {
|
|
|
137
158
|
setAuditLogger: () => setAuditLogger
|
|
138
159
|
});
|
|
139
160
|
import * as crypto from "crypto";
|
|
140
|
-
import * as
|
|
161
|
+
import * as fs3 from "fs";
|
|
141
162
|
import * as os from "os";
|
|
142
|
-
import * as
|
|
163
|
+
import * as path4 from "path";
|
|
143
164
|
function setAuditLogger(auditLogger) {
|
|
144
165
|
_auditLogger = auditLogger;
|
|
145
166
|
}
|
|
@@ -153,7 +174,7 @@ var init_audit = __esm({
|
|
|
153
174
|
init_esm_shims();
|
|
154
175
|
_auditLogger = null;
|
|
155
176
|
AuditLogger = class _AuditLogger {
|
|
156
|
-
static DEFAULT_PATH =
|
|
177
|
+
static DEFAULT_PATH = path4.join(
|
|
157
178
|
os.homedir(),
|
|
158
179
|
".apcore-cli",
|
|
159
180
|
"audit.jsonl"
|
|
@@ -165,7 +186,7 @@ var init_audit = __esm({
|
|
|
165
186
|
}
|
|
166
187
|
ensureDirectory() {
|
|
167
188
|
try {
|
|
168
|
-
|
|
189
|
+
fs3.mkdirSync(path4.dirname(this.logPath), { recursive: true });
|
|
169
190
|
} catch {
|
|
170
191
|
}
|
|
171
192
|
}
|
|
@@ -180,7 +201,7 @@ var init_audit = __esm({
|
|
|
180
201
|
duration_ms: durationMs
|
|
181
202
|
};
|
|
182
203
|
try {
|
|
183
|
-
|
|
204
|
+
fs3.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
|
|
184
205
|
} catch (err) {
|
|
185
206
|
console.warn(`Could not write audit log: ${err}`);
|
|
186
207
|
}
|
|
@@ -381,9 +402,9 @@ var init_auth = __esm({
|
|
|
381
402
|
|
|
382
403
|
// src/security/sandbox.ts
|
|
383
404
|
import * as child_process from "child_process";
|
|
384
|
-
import * as
|
|
405
|
+
import * as fs4 from "fs";
|
|
385
406
|
import * as os3 from "os";
|
|
386
|
-
import * as
|
|
407
|
+
import * as path5 from "path";
|
|
387
408
|
var Sandbox;
|
|
388
409
|
var init_sandbox = __esm({
|
|
389
410
|
"src/security/sandbox.ts"() {
|
|
@@ -416,8 +437,8 @@ var init_sandbox = __esm({
|
|
|
416
437
|
env[key] = value;
|
|
417
438
|
}
|
|
418
439
|
}
|
|
419
|
-
const tmpDir =
|
|
420
|
-
|
|
440
|
+
const tmpDir = fs4.mkdtempSync(
|
|
441
|
+
path5.join(os3.tmpdir(), "apcore_sandbox_")
|
|
421
442
|
);
|
|
422
443
|
try {
|
|
423
444
|
env.HOME = tmpDir;
|
|
@@ -455,7 +476,7 @@ var init_sandbox = __esm({
|
|
|
455
476
|
);
|
|
456
477
|
} finally {
|
|
457
478
|
try {
|
|
458
|
-
|
|
479
|
+
fs4.rmSync(tmpDir, { recursive: true, force: true });
|
|
459
480
|
} catch {
|
|
460
481
|
}
|
|
461
482
|
}
|
|
@@ -491,10 +512,10 @@ init_esm_shims();
|
|
|
491
512
|
// src/main.ts
|
|
492
513
|
init_esm_shims();
|
|
493
514
|
init_errors();
|
|
494
|
-
import { readFileSync } from "fs";
|
|
495
|
-
import { fileURLToPath as
|
|
496
|
-
import * as
|
|
497
|
-
import { Command, CommanderError, Option } from "commander";
|
|
515
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
516
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
517
|
+
import * as path6 from "path";
|
|
518
|
+
import { Command as Command2, CommanderError, Option as Option2 } from "commander";
|
|
498
519
|
|
|
499
520
|
// src/ref-resolver.ts
|
|
500
521
|
init_esm_shims();
|
|
@@ -1218,1120 +1239,1196 @@ function getCliDisplayFields(descriptor) {
|
|
|
1218
1239
|
return [name, desc, tags];
|
|
1219
1240
|
}
|
|
1220
1241
|
|
|
1221
|
-
// src/
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
registerInitCommand(program);
|
|
1249
|
-
program.hook("preAction", async (thisCommand) => {
|
|
1250
|
-
const opts = thisCommand.opts();
|
|
1251
|
-
const commandsDir = opts.commandsDir;
|
|
1252
|
-
const bindingPath = opts.binding;
|
|
1253
|
-
await applyToolkitIntegration(commandsDir, bindingPath);
|
|
1254
|
-
});
|
|
1255
|
-
return program;
|
|
1256
|
-
}
|
|
1257
|
-
async function applyToolkitIntegration(commandsDir, bindingPath) {
|
|
1258
|
-
if (!commandsDir && !bindingPath) {
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1242
|
+
// src/config.ts
|
|
1243
|
+
init_esm_shims();
|
|
1244
|
+
import * as fs2 from "fs";
|
|
1245
|
+
import yaml from "js-yaml";
|
|
1246
|
+
var DEFAULTS = {
|
|
1247
|
+
"extensions.root": "./extensions",
|
|
1248
|
+
"logging.level": "WARNING",
|
|
1249
|
+
"sandbox.enabled": false,
|
|
1250
|
+
"cli.stdin_buffer_limit": 10485760,
|
|
1251
|
+
"cli.auto_approve": false,
|
|
1252
|
+
"cli.help_text_max_length": 1e3,
|
|
1253
|
+
// Namespace-mode aliases (apcore >= 0.15.0 Config Bus)
|
|
1254
|
+
"apcore-cli.stdin_buffer_limit": 10485760,
|
|
1255
|
+
"apcore-cli.auto_approve": false,
|
|
1256
|
+
"apcore-cli.help_text_max_length": 1e3,
|
|
1257
|
+
"apcore-cli.logging_level": "WARNING"
|
|
1258
|
+
};
|
|
1259
|
+
var NAMESPACE_TO_LEGACY = {
|
|
1260
|
+
"apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
|
|
1261
|
+
"apcore-cli.auto_approve": "cli.auto_approve",
|
|
1262
|
+
"apcore-cli.help_text_max_length": "cli.help_text_max_length",
|
|
1263
|
+
"apcore-cli.logging_level": "logging.level"
|
|
1264
|
+
};
|
|
1265
|
+
var LEGACY_TO_NAMESPACE = Object.fromEntries(
|
|
1266
|
+
Object.entries(NAMESPACE_TO_LEGACY).map(([k, v]) => [v, k])
|
|
1267
|
+
);
|
|
1268
|
+
function registerConfigNamespace() {
|
|
1261
1269
|
try {
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1270
|
+
const { Config } = __require("apcore-js");
|
|
1271
|
+
if (typeof Config?.registerNamespace === "function") {
|
|
1272
|
+
Config.registerNamespace({
|
|
1273
|
+
name: "apcore-cli",
|
|
1274
|
+
envPrefix: "APCORE_CLI",
|
|
1275
|
+
defaults: {
|
|
1276
|
+
stdin_buffer_limit: 10485760,
|
|
1277
|
+
auto_approve: false,
|
|
1278
|
+
help_text_max_length: 1e3,
|
|
1279
|
+
logging_level: "WARNING"
|
|
1280
|
+
}
|
|
1281
|
+
});
|
|
1273
1282
|
}
|
|
1274
1283
|
} catch {
|
|
1275
|
-
console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
|
|
1276
1284
|
}
|
|
1277
1285
|
}
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
+
var ConfigResolver = class {
|
|
1287
|
+
cliFlags;
|
|
1288
|
+
configPath;
|
|
1289
|
+
fileCache = null;
|
|
1290
|
+
fileCacheLoaded = false;
|
|
1291
|
+
constructor(cliFlags, configPath) {
|
|
1292
|
+
this.cliFlags = cliFlags ?? {};
|
|
1293
|
+
this.configPath = configPath ?? "apcore.yaml";
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Resolve a single configuration key across all four tiers.
|
|
1297
|
+
*/
|
|
1298
|
+
resolve(key, cliFlag, envVar) {
|
|
1299
|
+
const flagKey = cliFlag ?? key;
|
|
1300
|
+
if (flagKey in this.cliFlags) {
|
|
1301
|
+
const value = this.cliFlags[flagKey];
|
|
1302
|
+
if (value !== null && value !== void 0) {
|
|
1303
|
+
return value;
|
|
1304
|
+
}
|
|
1286
1305
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1306
|
+
if (envVar) {
|
|
1307
|
+
const envValue = process.env[envVar];
|
|
1308
|
+
if (envValue !== void 0 && envValue !== "") {
|
|
1309
|
+
return envValue;
|
|
1310
|
+
}
|
|
1291
1311
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
|
|
1296
|
-
const moduleId = moduleDef.id;
|
|
1297
|
-
let resolvedSchema = {};
|
|
1298
|
-
let schemaOptions = [];
|
|
1299
|
-
const display = getDisplay(moduleDef);
|
|
1300
|
-
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1301
|
-
const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
|
|
1302
|
-
const cmdHelp = cliDisplay.description ?? moduleDef.description;
|
|
1303
|
-
const inputSchema = moduleDef.inputSchema;
|
|
1304
|
-
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
1305
|
-
try {
|
|
1306
|
-
resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
|
|
1307
|
-
} catch {
|
|
1308
|
-
resolvedSchema = inputSchema;
|
|
1312
|
+
const fileValue = this.resolveFromFile(key);
|
|
1313
|
+
if (fileValue !== void 0) {
|
|
1314
|
+
return fileValue;
|
|
1309
1315
|
}
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
const formatOpt = new Option("--format <format>", "Set output format: 'json' for machine-readable, 'table' for human-readable");
|
|
1317
|
-
const sandboxOpt = new Option("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
|
|
1318
|
-
if (!verbose) {
|
|
1319
|
-
inputOpt.hideHelp();
|
|
1320
|
-
yesOpt.hideHelp();
|
|
1321
|
-
largeInputOpt.hideHelp();
|
|
1322
|
-
formatOpt.hideHelp();
|
|
1323
|
-
}
|
|
1324
|
-
cmd.addOption(inputOpt);
|
|
1325
|
-
cmd.addOption(yesOpt);
|
|
1326
|
-
cmd.addOption(largeInputOpt);
|
|
1327
|
-
cmd.addOption(formatOpt);
|
|
1328
|
-
cmd.addOption(sandboxOpt);
|
|
1329
|
-
const footerParts = [];
|
|
1330
|
-
if (!verbose) {
|
|
1331
|
-
footerParts.push("Use --verbose to show all options (including built-in apcore options).");
|
|
1332
|
-
}
|
|
1333
|
-
if (docsUrl) {
|
|
1334
|
-
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
|
|
1335
|
-
}
|
|
1336
|
-
if (footerParts.length > 0) {
|
|
1337
|
-
cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
|
|
1338
|
-
}
|
|
1339
|
-
for (const opt of schemaOptions) {
|
|
1340
|
-
if (opt.parseArg) {
|
|
1341
|
-
cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
|
|
1342
|
-
} else {
|
|
1343
|
-
cmd.option(opt.flags, opt.description, opt.defaultValue);
|
|
1316
|
+
const altKey = NAMESPACE_TO_LEGACY[key] ?? LEGACY_TO_NAMESPACE[key];
|
|
1317
|
+
if (altKey) {
|
|
1318
|
+
const altFileValue = this.resolveFromFile(altKey);
|
|
1319
|
+
if (altFileValue !== void 0) {
|
|
1320
|
+
return altFileValue;
|
|
1321
|
+
}
|
|
1344
1322
|
}
|
|
1323
|
+
return DEFAULTS[key];
|
|
1345
1324
|
}
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
schemaKwargs[k] = v;
|
|
1357
|
-
}
|
|
1325
|
+
/**
|
|
1326
|
+
* Load a value from the config file using a dot-separated key path.
|
|
1327
|
+
*/
|
|
1328
|
+
resolveFromFile(key) {
|
|
1329
|
+
if (!this.fileCacheLoaded) {
|
|
1330
|
+
this.fileCache = this.loadConfigFile();
|
|
1331
|
+
this.fileCacheLoaded = true;
|
|
1332
|
+
}
|
|
1333
|
+
if (this.fileCache === null) {
|
|
1334
|
+
return void 0;
|
|
1358
1335
|
}
|
|
1336
|
+
return this.fileCache[key];
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Load and flatten a YAML config file.
|
|
1340
|
+
*/
|
|
1341
|
+
loadConfigFile() {
|
|
1342
|
+
let content;
|
|
1359
1343
|
try {
|
|
1360
|
-
|
|
1361
|
-
const reconverted = reconvertEnumValues(merged, schemaOptions);
|
|
1362
|
-
await checkApproval(moduleDef, autoApprove);
|
|
1363
|
-
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
1364
|
-
const sandbox = new Sandbox2(sandboxEnabled);
|
|
1365
|
-
const startTime = performance.now();
|
|
1366
|
-
const result = await sandbox.execute(moduleId, reconverted, executor);
|
|
1367
|
-
const durationMs = Math.round(performance.now() - startTime);
|
|
1368
|
-
const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
1369
|
-
const auditLogger = getAuditLogger2();
|
|
1370
|
-
if (auditLogger) {
|
|
1371
|
-
auditLogger.logExecution(moduleId, reconverted, "success", 0, durationMs);
|
|
1372
|
-
}
|
|
1373
|
-
formatExecResult(result, outputFormat);
|
|
1344
|
+
content = fs2.readFileSync(this.configPath, "utf-8");
|
|
1374
1345
|
} catch (err) {
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
const code = exitCodeForError(err);
|
|
1378
|
-
if (auditLogger) {
|
|
1379
|
-
auditLogger.logExecution(moduleId, {}, "error", code, 0);
|
|
1380
|
-
}
|
|
1381
|
-
if (err instanceof Error) {
|
|
1382
|
-
process.stderr.write(`Error: ${err.message}
|
|
1383
|
-
`);
|
|
1346
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
1347
|
+
return null;
|
|
1384
1348
|
}
|
|
1385
|
-
|
|
1349
|
+
console.warn(
|
|
1350
|
+
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1351
|
+
);
|
|
1352
|
+
return null;
|
|
1386
1353
|
}
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
);
|
|
1396
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1397
|
-
}
|
|
1398
|
-
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
|
|
1399
|
-
process.stderr.write(
|
|
1400
|
-
`Error: Invalid module ID format: '${moduleId}'.
|
|
1401
|
-
`
|
|
1402
|
-
);
|
|
1403
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
|
|
1407
|
-
const cliKwargsNonNull = {};
|
|
1408
|
-
for (const [k, v] of Object.entries(cliKwargs)) {
|
|
1409
|
-
if (v !== null && v !== void 0) {
|
|
1410
|
-
cliKwargsNonNull[k] = v;
|
|
1354
|
+
let parsed;
|
|
1355
|
+
try {
|
|
1356
|
+
parsed = yaml.load(content);
|
|
1357
|
+
} catch {
|
|
1358
|
+
console.warn(
|
|
1359
|
+
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1360
|
+
);
|
|
1361
|
+
return null;
|
|
1411
1362
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
}
|
|
1416
|
-
if (stdinFlag === "-") {
|
|
1417
|
-
const raw = await readStdin();
|
|
1418
|
-
const rawSize = Buffer.byteLength(raw, "utf-8");
|
|
1419
|
-
if (rawSize > 10485760 && !largeInput) {
|
|
1420
|
-
process.stderr.write(
|
|
1421
|
-
"Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
|
|
1422
|
-
);
|
|
1423
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1424
|
-
}
|
|
1425
|
-
if (!raw) {
|
|
1426
|
-
return cliKwargsNonNull;
|
|
1427
|
-
}
|
|
1428
|
-
let stdinData;
|
|
1429
|
-
try {
|
|
1430
|
-
stdinData = JSON.parse(raw);
|
|
1431
|
-
} catch {
|
|
1432
|
-
process.stderr.write(
|
|
1433
|
-
"Error: STDIN does not contain valid JSON.\n"
|
|
1363
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1364
|
+
console.warn(
|
|
1365
|
+
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1434
1366
|
);
|
|
1435
|
-
|
|
1367
|
+
return null;
|
|
1436
1368
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1369
|
+
return this.flattenDict(parsed);
|
|
1370
|
+
}
|
|
1371
|
+
/**
|
|
1372
|
+
* Flatten nested dict to dot-notation keys.
|
|
1373
|
+
*/
|
|
1374
|
+
flattenDict(d, prefix = "") {
|
|
1375
|
+
const result = {};
|
|
1376
|
+
for (const [key, value] of Object.entries(d)) {
|
|
1377
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
1378
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1379
|
+
Object.assign(
|
|
1380
|
+
result,
|
|
1381
|
+
this.flattenDict(value, fullKey)
|
|
1382
|
+
);
|
|
1383
|
+
} else {
|
|
1384
|
+
result[fullKey] = value;
|
|
1385
|
+
}
|
|
1443
1386
|
}
|
|
1444
|
-
return
|
|
1387
|
+
return result;
|
|
1445
1388
|
}
|
|
1446
|
-
|
|
1389
|
+
};
|
|
1390
|
+
|
|
1391
|
+
// src/shell.ts
|
|
1392
|
+
init_esm_shims();
|
|
1393
|
+
init_errors();
|
|
1394
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1395
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
1396
|
+
import * as path3 from "path";
|
|
1397
|
+
import { spawnSync } from "child_process";
|
|
1398
|
+
import { Command, Help, Option } from "commander";
|
|
1399
|
+
var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
|
|
1400
|
+
var SHELL_VERSION = "0.0.0";
|
|
1401
|
+
try {
|
|
1402
|
+
const pkg = JSON.parse(readFileSync2(path3.resolve(__dirname2, "../package.json"), "utf-8"));
|
|
1403
|
+
SHELL_VERSION = pkg.version;
|
|
1404
|
+
} catch {
|
|
1447
1405
|
}
|
|
1448
|
-
function
|
|
1449
|
-
return
|
|
1450
|
-
const chunks = [];
|
|
1451
|
-
const onData = (chunk) => chunks.push(chunk);
|
|
1452
|
-
const onEnd = () => {
|
|
1453
|
-
cleanup();
|
|
1454
|
-
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
1455
|
-
};
|
|
1456
|
-
const onError = (err) => {
|
|
1457
|
-
cleanup();
|
|
1458
|
-
reject(err);
|
|
1459
|
-
};
|
|
1460
|
-
const cleanup = () => {
|
|
1461
|
-
process.stdin.removeListener("data", onData);
|
|
1462
|
-
process.stdin.removeListener("end", onEnd);
|
|
1463
|
-
process.stdin.removeListener("error", onError);
|
|
1464
|
-
};
|
|
1465
|
-
process.stdin.on("data", onData);
|
|
1466
|
-
process.stdin.on("end", onEnd);
|
|
1467
|
-
process.stdin.on("error", onError);
|
|
1468
|
-
process.stdin.resume();
|
|
1469
|
-
});
|
|
1406
|
+
function makeFunctionName(progName) {
|
|
1407
|
+
return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
|
|
1470
1408
|
}
|
|
1471
|
-
function
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1409
|
+
function shellQuote(s) {
|
|
1410
|
+
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
1411
|
+
}
|
|
1412
|
+
function generateBashCompletion(progName) {
|
|
1413
|
+
const fn = makeFunctionName(progName);
|
|
1414
|
+
const quoted = shellQuote(progName);
|
|
1415
|
+
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1416
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1417
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1418
|
+
const ids=JSON.parse(m).map(x=>x.id);
|
|
1419
|
+
const g=new Set(),t=[];
|
|
1420
|
+
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1421
|
+
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1422
|
+
" 2>/dev/null`;
|
|
1423
|
+
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1424
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1425
|
+
const g=process.env._APCORE_GRP;
|
|
1426
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1427
|
+
" 2>/dev/null`;
|
|
1428
|
+
return `${fn}() {
|
|
1429
|
+
local cur prev opts
|
|
1430
|
+
COMPREPLY=()
|
|
1431
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1432
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1433
|
+
|
|
1434
|
+
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
1435
|
+
opts="completion describe exec init list man"
|
|
1436
|
+
local groups_and_top=$(${groupsAndTopCmd})
|
|
1437
|
+
COMPREPLY=( $(compgen -W "\${opts} \${groups_and_top}" -- \${cur}) )
|
|
1438
|
+
return 0
|
|
1439
|
+
fi
|
|
1440
|
+
|
|
1441
|
+
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
1442
|
+
if [[ "\${COMP_WORDS[1]}" == "exec" ]]; then
|
|
1443
|
+
local modules=$(${moduleListCmd})
|
|
1444
|
+
COMPREPLY=( $(compgen -W "\${modules}" -- \${cur}) )
|
|
1445
|
+
return 0
|
|
1446
|
+
fi
|
|
1447
|
+
export _APCORE_GRP="\${COMP_WORDS[1]}"
|
|
1448
|
+
local group_cmds=$(${groupCmdsCmd})
|
|
1449
|
+
COMPREPLY=( $(compgen -W "\${group_cmds}" -- \${cur}) )
|
|
1450
|
+
return 0
|
|
1451
|
+
fi
|
|
1490
1452
|
}
|
|
1453
|
+
complete -F ${fn} ${quoted}
|
|
1454
|
+
`;
|
|
1455
|
+
}
|
|
1456
|
+
function generateZshCompletion(progName) {
|
|
1457
|
+
const fn = makeFunctionName(progName);
|
|
1458
|
+
const quoted = shellQuote(progName);
|
|
1459
|
+
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1460
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1461
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1462
|
+
const ids=JSON.parse(m).map(x=>x.id);
|
|
1463
|
+
const g=new Set(),t=[];
|
|
1464
|
+
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1465
|
+
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1466
|
+
" 2>/dev/null`;
|
|
1467
|
+
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1468
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1469
|
+
const g=process.env._APCORE_GRP;
|
|
1470
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1471
|
+
" 2>/dev/null`;
|
|
1472
|
+
return `#compdef ${progName}
|
|
1491
1473
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1474
|
+
${fn}() {
|
|
1475
|
+
local -a commands
|
|
1476
|
+
commands=(
|
|
1477
|
+
'list:List available modules'
|
|
1478
|
+
'describe:Show module metadata and schema'
|
|
1479
|
+
'completion:Generate shell completion script'
|
|
1480
|
+
'init:Scaffolding commands'
|
|
1481
|
+
'man:Generate man page'
|
|
1482
|
+
)
|
|
1483
|
+
|
|
1484
|
+
_arguments -C \\
|
|
1485
|
+
'1:command:->command' \\
|
|
1486
|
+
'*::arg:->args'
|
|
1487
|
+
|
|
1488
|
+
case "$state" in
|
|
1489
|
+
command)
|
|
1490
|
+
_describe -t commands '${progName} commands' commands
|
|
1491
|
+
local -a groups_and_top
|
|
1492
|
+
groups_and_top=($(${groupsAndTopCmd}))
|
|
1493
|
+
compadd -a groups_and_top
|
|
1494
|
+
;;
|
|
1495
|
+
args)
|
|
1496
|
+
case "\${words[1]}" in
|
|
1497
|
+
exec)
|
|
1498
|
+
local modules
|
|
1499
|
+
modules=($(${moduleListCmd}))
|
|
1500
|
+
compadd -a modules
|
|
1501
|
+
;;
|
|
1502
|
+
*)
|
|
1503
|
+
export _APCORE_GRP="\${words[1]}"
|
|
1504
|
+
local -a group_cmds
|
|
1505
|
+
group_cmds=($(${groupCmdsCmd}))
|
|
1506
|
+
compadd -a group_cmds
|
|
1507
|
+
;;
|
|
1508
|
+
esac
|
|
1509
|
+
;;
|
|
1510
|
+
esac
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
compdef ${fn} ${quoted}
|
|
1514
|
+
`;
|
|
1515
|
+
}
|
|
1516
|
+
function generateFishCompletion(progName) {
|
|
1517
|
+
const quoted = shellQuote(progName);
|
|
1518
|
+
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e \\"process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})\\" 2>/dev/null`;
|
|
1519
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e \\"const m=require('fs').readFileSync('/dev/stdin','utf8');const ids=JSON.parse(m).map(x=>x.id);const g=new Set(),t=[];ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});console.log([...g].sort().concat(t.sort()).join('\\\\n'))\\" 2>/dev/null`;
|
|
1520
|
+
return `# Fish completions for ${progName}
|
|
1521
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a list -d "List available modules"
|
|
1522
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a describe -d "Show module metadata and schema"
|
|
1523
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a completion -d "Generate shell completion script"
|
|
1524
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a init -d "Scaffolding commands"
|
|
1525
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a man -d "Generate man page"
|
|
1526
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a "(${groupsAndTopCmd})" -d "Module group"
|
|
1527
|
+
|
|
1528
|
+
complete -c ${quoted} -n "__fish_seen_subcommand_from exec" -a "(${moduleListCmd})"
|
|
1529
|
+
|
|
1530
|
+
function __apcore_group_cmds
|
|
1531
|
+
set -l grp (commandline -opc)[2]
|
|
1532
|
+
set -x _APCORE_GRP $grp
|
|
1533
|
+
${quoted} list --format json 2>/dev/null | node -e "
|
|
1534
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1535
|
+
const g=process.env._APCORE_GRP;
|
|
1536
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1537
|
+
" 2>/dev/null
|
|
1538
|
+
end
|
|
1539
|
+
|
|
1540
|
+
# Group subcommand completion \u2014 matches when position 1 is not a builtin
|
|
1541
|
+
complete -c ${quoted} -n "not __fish_use_subcommand; and not __fish_seen_subcommand_from list describe completion init man exec" -a "(__apcore_group_cmds)"
|
|
1542
|
+
`;
|
|
1543
|
+
}
|
|
1544
|
+
function buildSynopsis(command, progName, commandName) {
|
|
1545
|
+
if (!command) {
|
|
1546
|
+
return `\\fB${progName} ${commandName}\\fR [OPTIONS]`;
|
|
1510
1547
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
const display = getDisplay(descriptor);
|
|
1523
|
-
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1524
|
-
const cliAlias = cliDisplay.alias;
|
|
1525
|
-
if (cliAlias && cliAlias !== moduleId) {
|
|
1526
|
-
this.aliasMap.set(cliAlias, moduleId);
|
|
1527
|
-
}
|
|
1528
|
-
}
|
|
1529
|
-
this.aliasMapBuilt = true;
|
|
1530
|
-
} catch {
|
|
1531
|
-
warn("Failed to build alias map from registry");
|
|
1548
|
+
const parts = [`\\fB${progName} ${commandName}\\fR`];
|
|
1549
|
+
for (const opt of command.options) {
|
|
1550
|
+
const flag = opt.long ?? opt.short ?? "";
|
|
1551
|
+
if (opt.isBoolean?.()) {
|
|
1552
|
+
parts.push(`[${flag}]`);
|
|
1553
|
+
} else if (opt.required) {
|
|
1554
|
+
const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
|
|
1555
|
+
parts.push(`${flag} \\fI${typeName}\\fR`);
|
|
1556
|
+
} else {
|
|
1557
|
+
const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
|
|
1558
|
+
parts.push(`[${flag} \\fI${typeName}\\fR]`);
|
|
1532
1559
|
}
|
|
1533
1560
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
for (const [alias, moduleId] of this.aliasMap) {
|
|
1541
|
-
reverse.set(moduleId, alias);
|
|
1561
|
+
for (const arg of command.registeredArguments ?? []) {
|
|
1562
|
+
const meta = arg.name().toUpperCase();
|
|
1563
|
+
if (arg.required) {
|
|
1564
|
+
parts.push(`\\fI${meta}\\fR`);
|
|
1565
|
+
} else {
|
|
1566
|
+
parts.push(`[\\fI${meta}\\fR]`);
|
|
1542
1567
|
}
|
|
1543
|
-
const moduleIds = this.registry.listModules().map((m) => m.id);
|
|
1544
|
-
const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
|
|
1545
|
-
return [...new Set(names)].sort();
|
|
1546
1568
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1569
|
+
return parts.join(" ");
|
|
1570
|
+
}
|
|
1571
|
+
function generateManPage(commandName, command, progName, version = SHELL_VERSION) {
|
|
1572
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1573
|
+
const title = `${progName}-${commandName}`.toUpperCase();
|
|
1574
|
+
const pkgLabel = `${progName} ${version}`;
|
|
1575
|
+
const manualLabel = `${progName} Manual`;
|
|
1576
|
+
const sections = [];
|
|
1577
|
+
sections.push(`.TH "${title}" "1" "${today}" "${pkgLabel}" "${manualLabel}"`);
|
|
1578
|
+
sections.push(".SH NAME");
|
|
1579
|
+
const desc = command?.description() ?? commandName;
|
|
1580
|
+
const nameDesc = desc.split("\n")[0].replace(/\.$/, "");
|
|
1581
|
+
sections.push(`${progName}-${commandName} \\- ${nameDesc}`);
|
|
1582
|
+
sections.push(".SH SYNOPSIS");
|
|
1583
|
+
sections.push(buildSynopsis(command, progName, commandName));
|
|
1584
|
+
if (command?.description()) {
|
|
1585
|
+
sections.push(".SH DESCRIPTION");
|
|
1586
|
+
sections.push(
|
|
1587
|
+
command.description().replace(/\\/g, "\\\\").replace(/-/g, "\\-")
|
|
1588
|
+
);
|
|
1566
1589
|
}
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
this._helpTextMaxLength,
|
|
1584
|
-
cmdName
|
|
1585
|
-
);
|
|
1586
|
-
this._cmdCache.set(cmdName, cmd);
|
|
1587
|
-
this.command.addCommand(cmd);
|
|
1590
|
+
if (command && command.options.length > 0) {
|
|
1591
|
+
sections.push(".SH OPTIONS");
|
|
1592
|
+
for (const opt of command.options) {
|
|
1593
|
+
const flag = [opt.short, opt.long].filter(Boolean).join(", ");
|
|
1594
|
+
sections.push(".TP");
|
|
1595
|
+
if (opt.isBoolean?.()) {
|
|
1596
|
+
sections.push(`\\fB${flag}\\fR`);
|
|
1597
|
+
} else {
|
|
1598
|
+
sections.push(`\\fB${flag}\\fR \\fIVALUE\\fR`);
|
|
1599
|
+
}
|
|
1600
|
+
if (opt.description) {
|
|
1601
|
+
sections.push(opt.description);
|
|
1602
|
+
}
|
|
1603
|
+
if (opt.defaultValue !== void 0 && !opt.isBoolean?.()) {
|
|
1604
|
+
sections.push(`Default: ${opt.defaultValue}.`);
|
|
1605
|
+
}
|
|
1588
1606
|
}
|
|
1589
1607
|
}
|
|
1590
|
-
|
|
1591
|
-
|
|
1608
|
+
sections.push(".SH ENVIRONMENT");
|
|
1609
|
+
sections.push(".TP");
|
|
1610
|
+
sections.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
|
|
1611
|
+
sections.push(
|
|
1612
|
+
"Path to the apcore extensions directory. Overrides the default \\fI./extensions\\fR."
|
|
1613
|
+
);
|
|
1614
|
+
sections.push(".TP");
|
|
1615
|
+
sections.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
|
|
1616
|
+
sections.push(
|
|
1617
|
+
"Set to \\fB1\\fR to bypass approval prompts for modules that require human-in-the-loop confirmation."
|
|
1618
|
+
);
|
|
1619
|
+
sections.push(".TP");
|
|
1620
|
+
sections.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
|
|
1621
|
+
sections.push(
|
|
1622
|
+
"CLI-specific logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Takes priority over \\fBAPCORE_LOGGING_LEVEL\\fR. Default: WARNING."
|
|
1623
|
+
);
|
|
1624
|
+
sections.push(".TP");
|
|
1625
|
+
sections.push("\\fBAPCORE_LOGGING_LEVEL\\fR");
|
|
1626
|
+
sections.push(
|
|
1627
|
+
"Global apcore logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Used as fallback when \\fBAPCORE_CLI_LOGGING_LEVEL\\fR is not set. Default: WARNING."
|
|
1628
|
+
);
|
|
1629
|
+
sections.push(".TP");
|
|
1630
|
+
sections.push("\\fBAPCORE_AUTH_API_KEY\\fR");
|
|
1631
|
+
sections.push(
|
|
1632
|
+
"API key for authenticating with the apcore registry."
|
|
1633
|
+
);
|
|
1634
|
+
sections.push(".SH EXIT CODES");
|
|
1635
|
+
const exitCodes = [
|
|
1636
|
+
["0", "Success."],
|
|
1637
|
+
["1", "Module execution error."],
|
|
1638
|
+
["2", "Invalid CLI input or missing argument."],
|
|
1639
|
+
["44", "Module not found, disabled, or failed to load."],
|
|
1640
|
+
["45", "Input failed JSON Schema validation."],
|
|
1641
|
+
[
|
|
1642
|
+
"46",
|
|
1643
|
+
"Approval denied, timed out, or no interactive terminal available."
|
|
1644
|
+
],
|
|
1645
|
+
[
|
|
1646
|
+
"47",
|
|
1647
|
+
"Configuration error (extensions directory not found or unreadable)."
|
|
1648
|
+
],
|
|
1649
|
+
["48", "Schema contains a circular \\fB$ref\\fR."],
|
|
1650
|
+
["77", "ACL denied \u2014 insufficient permissions for this module."],
|
|
1651
|
+
["130", "Execution cancelled by user (SIGINT / Ctrl\\-C)."]
|
|
1652
|
+
];
|
|
1653
|
+
for (const [code, meaning] of exitCodes) {
|
|
1654
|
+
sections.push(`.TP
|
|
1655
|
+
\\fB${code}\\fR
|
|
1656
|
+
${meaning}`);
|
|
1592
1657
|
}
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1658
|
+
sections.push(".SH SEE ALSO");
|
|
1659
|
+
sections.push(
|
|
1660
|
+
[
|
|
1661
|
+
`\\fB${progName}\\fR(1)`,
|
|
1662
|
+
`\\fB${progName}\\-list\\fR(1)`,
|
|
1663
|
+
`\\fB${progName}\\-describe\\fR(1)`,
|
|
1664
|
+
`\\fB${progName}\\-completion\\fR(1)`
|
|
1665
|
+
].join(", ")
|
|
1666
|
+
);
|
|
1667
|
+
return sections.join("\n");
|
|
1668
|
+
}
|
|
1669
|
+
function roffEscape(s) {
|
|
1670
|
+
return s.replace(/\\/g, "\\\\").replace(/-/g, "\\-").replace(/'/g, "\\(aq");
|
|
1671
|
+
}
|
|
1672
|
+
function buildProgramManPage(program, progName, version, description, docsUrl2) {
|
|
1673
|
+
const help = new Help();
|
|
1674
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1675
|
+
const s = [];
|
|
1676
|
+
const resolvedDesc = description ?? program.description() ?? `${progName} CLI`;
|
|
1677
|
+
s.push(`.TH "${progName.toUpperCase()}" "1" "${today}" "${progName} ${version}" "${progName} Manual"`);
|
|
1678
|
+
s.push(".SH NAME");
|
|
1679
|
+
s.push(`${progName} \\- ${roffEscape(resolvedDesc)}`);
|
|
1680
|
+
s.push(".SH SYNOPSIS");
|
|
1681
|
+
s.push(`\\fB${progName}\\fR [\\fIglobal\\-options\\fR] \\fIcommand\\fR [\\fIcommand\\-options\\fR]`);
|
|
1682
|
+
if (resolvedDesc) {
|
|
1683
|
+
s.push(".SH DESCRIPTION");
|
|
1684
|
+
s.push(roffEscape(resolvedDesc));
|
|
1610
1685
|
}
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
groupMapBuilt = false;
|
|
1620
|
-
/**
|
|
1621
|
-
* Determine (groupName | null, commandName) for a module from its display overlay.
|
|
1622
|
-
*/
|
|
1623
|
-
static resolveGroup(moduleId, descriptor) {
|
|
1624
|
-
if (!moduleId) {
|
|
1625
|
-
warn("Empty module_id encountered in resolveGroup");
|
|
1626
|
-
return [null, ""];
|
|
1627
|
-
}
|
|
1628
|
-
const display = getDisplay(descriptor);
|
|
1629
|
-
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1630
|
-
const explicitGroup = cliDisplay.group;
|
|
1631
|
-
if (typeof explicitGroup === "string" && explicitGroup !== "") {
|
|
1632
|
-
return [explicitGroup, cliDisplay.alias ?? moduleId];
|
|
1633
|
-
}
|
|
1634
|
-
if (explicitGroup === "") {
|
|
1635
|
-
return [null, cliDisplay.alias ?? moduleId];
|
|
1636
|
-
}
|
|
1637
|
-
const cliName = cliDisplay.alias ?? moduleId;
|
|
1638
|
-
if (cliName.includes(".")) {
|
|
1639
|
-
const dotIdx = cliName.indexOf(".");
|
|
1640
|
-
const group = cliName.substring(0, dotIdx);
|
|
1641
|
-
const cmd = cliName.substring(dotIdx + 1);
|
|
1642
|
-
return [group, cmd];
|
|
1686
|
+
const globalOpts = help.visibleOptions(program).filter((o) => !["help", "version", "all", "man"].includes(o.long?.replace("--", "") ?? ""));
|
|
1687
|
+
if (globalOpts.length > 0) {
|
|
1688
|
+
s.push(".SH GLOBAL OPTIONS");
|
|
1689
|
+
for (const opt of globalOpts) {
|
|
1690
|
+
const flag = [opt.short, opt.long].filter(Boolean).join(", ");
|
|
1691
|
+
s.push(".TP");
|
|
1692
|
+
s.push(`\\fB${roffEscape(flag)}\\fR`);
|
|
1693
|
+
if (opt.description) s.push(roffEscape(opt.description));
|
|
1643
1694
|
}
|
|
1644
|
-
return [null, cliName];
|
|
1645
1695
|
}
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1696
|
+
const allCommands = help.visibleCommands(program);
|
|
1697
|
+
if (allCommands.length > 0) {
|
|
1698
|
+
s.push(".SH COMMANDS");
|
|
1699
|
+
for (const cmd of allCommands) {
|
|
1700
|
+
if (cmd.name() === "help") continue;
|
|
1701
|
+
const desc = help.subcommandDescription(cmd);
|
|
1702
|
+
s.push(".TP");
|
|
1703
|
+
s.push(`\\fB${progName} ${roffEscape(cmd.name())}\\fR`);
|
|
1704
|
+
if (desc) s.push(roffEscape(desc));
|
|
1705
|
+
const cmdHelp = new Help();
|
|
1706
|
+
const opts = cmdHelp.visibleOptions(cmd).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
|
|
1707
|
+
for (const opt of opts) {
|
|
1708
|
+
const flag = [opt.short, opt.long].filter(Boolean).join(", ");
|
|
1709
|
+
s.push(".RS");
|
|
1710
|
+
s.push(".TP");
|
|
1711
|
+
s.push(`\\fB${roffEscape(flag)}\\fR`);
|
|
1712
|
+
if (opt.description) s.push(roffEscape(opt.description));
|
|
1713
|
+
s.push(".RE");
|
|
1714
|
+
}
|
|
1715
|
+
const subCmds = cmdHelp.visibleCommands(cmd).filter((c) => c.name() !== "help");
|
|
1716
|
+
for (const sub of subCmds) {
|
|
1717
|
+
const subDesc = help.subcommandDescription(sub);
|
|
1718
|
+
s.push(".TP");
|
|
1719
|
+
s.push(`\\fB${progName} ${roffEscape(cmd.name())} ${roffEscape(sub.name())}\\fR`);
|
|
1720
|
+
if (subDesc) s.push(roffEscape(subDesc));
|
|
1721
|
+
const subOpts = cmdHelp.visibleOptions(sub).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
|
|
1722
|
+
for (const opt of subOpts) {
|
|
1723
|
+
const flag = [opt.short, opt.long].filter(Boolean).join(", ");
|
|
1724
|
+
s.push(".RS");
|
|
1725
|
+
s.push(".TP");
|
|
1726
|
+
s.push(`\\fB${roffEscape(flag)}\\fR`);
|
|
1727
|
+
if (opt.description) s.push(roffEscape(opt.description));
|
|
1728
|
+
s.push(".RE");
|
|
1660
1729
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
s.push(".SH ENVIRONMENT");
|
|
1734
|
+
s.push(".TP");
|
|
1735
|
+
s.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
|
|
1736
|
+
s.push("Path to the apcore extensions directory.");
|
|
1737
|
+
s.push(".TP");
|
|
1738
|
+
s.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
|
|
1739
|
+
s.push("Set to \\fB1\\fR to bypass approval prompts.");
|
|
1740
|
+
s.push(".TP");
|
|
1741
|
+
s.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
|
|
1742
|
+
s.push("CLI\\-specific logging verbosity (DEBUG|INFO|WARNING|ERROR).");
|
|
1743
|
+
s.push(".SH EXIT CODES");
|
|
1744
|
+
const exitCodes = [
|
|
1745
|
+
["0", "Success."],
|
|
1746
|
+
["1", "Module execution error."],
|
|
1747
|
+
["2", "Invalid CLI input or missing argument."],
|
|
1748
|
+
["44", "Module not found, disabled, or failed to load."],
|
|
1749
|
+
["45", "Input failed JSON Schema validation."],
|
|
1750
|
+
["46", "Approval denied or timed out."],
|
|
1751
|
+
["47", "Configuration error."],
|
|
1752
|
+
["77", "ACL denied."],
|
|
1753
|
+
["130", "Cancelled by user (SIGINT)."]
|
|
1754
|
+
];
|
|
1755
|
+
for (const [code, meaning] of exitCodes) {
|
|
1756
|
+
s.push(`.TP
|
|
1757
|
+
\\fB${code}\\fR
|
|
1758
|
+
${meaning}`);
|
|
1759
|
+
}
|
|
1760
|
+
s.push(".SH SEE ALSO");
|
|
1761
|
+
s.push(`\\fB${progName} \\-\\-help \\-\\-verbose\\fR for full option list.`);
|
|
1762
|
+
if (docsUrl2) {
|
|
1763
|
+
s.push(`.PP
|
|
1764
|
+
Full documentation at \\fI${roffEscape(docsUrl2)}\\fR`);
|
|
1765
|
+
}
|
|
1766
|
+
return s.join("\n");
|
|
1767
|
+
}
|
|
1768
|
+
function configureManHelp(program, progName, version, description, docsUrl2) {
|
|
1769
|
+
const manOpt = new Option("--man", "Output man page in roff format (use with --help)").hideHelp();
|
|
1770
|
+
program.addOption(manOpt);
|
|
1771
|
+
program.addHelpText("beforeAll", () => {
|
|
1772
|
+
if (program.opts().man) {
|
|
1773
|
+
const roff = buildProgramManPage(program, progName, version, description, docsUrl2) + "\n";
|
|
1774
|
+
if (process.stdout.isTTY) {
|
|
1775
|
+
const pagers = [
|
|
1776
|
+
{ cmd: "mandoc", args: ["-a"] },
|
|
1777
|
+
{ cmd: "groff", args: ["-man", "-Tutf8"] }
|
|
1778
|
+
];
|
|
1779
|
+
let rendered = false;
|
|
1780
|
+
for (const { cmd, args } of pagers) {
|
|
1781
|
+
const result = spawnSync(cmd, args, {
|
|
1782
|
+
input: roff,
|
|
1783
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1784
|
+
encoding: "utf-8"
|
|
1785
|
+
});
|
|
1786
|
+
if (result.status === 0 && result.stdout) {
|
|
1787
|
+
const pager = process.env.PAGER || "less";
|
|
1788
|
+
const pagerResult = spawnSync(pager, ["-R"], {
|
|
1789
|
+
input: result.stdout,
|
|
1790
|
+
stdio: ["pipe", "inherit", "inherit"]
|
|
1791
|
+
});
|
|
1792
|
+
if (pagerResult.status !== null) {
|
|
1793
|
+
rendered = true;
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1672
1796
|
}
|
|
1673
|
-
this.groupMap.get(group).set(cmd, [moduleId, cached]);
|
|
1674
1797
|
}
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
if (BUILTIN_COMMANDS.includes(groupName)) {
|
|
1678
|
-
warn(
|
|
1679
|
-
`Group name '${groupName}' collides with a built-in command and will be ignored`
|
|
1680
|
-
);
|
|
1798
|
+
if (!rendered) {
|
|
1799
|
+
process.stdout.write(roff);
|
|
1681
1800
|
}
|
|
1801
|
+
} else {
|
|
1802
|
+
process.stdout.write(roff);
|
|
1682
1803
|
}
|
|
1683
|
-
|
|
1684
|
-
}
|
|
1685
|
-
|
|
1804
|
+
process.exit(0);
|
|
1805
|
+
}
|
|
1806
|
+
return "";
|
|
1807
|
+
});
|
|
1808
|
+
}
|
|
1809
|
+
function registerShellCommands(cli, progName = "apcore-cli") {
|
|
1810
|
+
const completionCmd = new Command("completion").description(
|
|
1811
|
+
"Generate a shell completion script and print it to stdout."
|
|
1812
|
+
).argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
|
|
1813
|
+
const validShells = ["bash", "zsh", "fish"];
|
|
1814
|
+
if (!validShells.includes(shell)) {
|
|
1815
|
+
process.stderr.write(
|
|
1816
|
+
`Error: Unknown shell '${shell}'. Expected: bash, zsh, or fish.
|
|
1817
|
+
`
|
|
1818
|
+
);
|
|
1819
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1820
|
+
}
|
|
1821
|
+
const resolved = cli.name() || progName;
|
|
1822
|
+
const generators = {
|
|
1823
|
+
bash: () => generateBashCompletion(resolved),
|
|
1824
|
+
zsh: () => generateZshCompletion(resolved),
|
|
1825
|
+
fish: () => generateFishCompletion(resolved)
|
|
1826
|
+
};
|
|
1827
|
+
process.stdout.write(generators[shell]());
|
|
1828
|
+
});
|
|
1829
|
+
cli.addCommand(completionCmd);
|
|
1830
|
+
const manCmd = new Command("man").description("Generate a roff man page for COMMAND and print it to stdout.").argument("<command>", "Command to generate man page for").action((commandName) => {
|
|
1831
|
+
const knownBuiltins = /* @__PURE__ */ new Set(["completion", "describe", "exec", "init", "list", "man"]);
|
|
1832
|
+
const cmd = cli.commands.find((c) => c.name() === commandName) ?? null;
|
|
1833
|
+
if (!cmd && !knownBuiltins.has(commandName)) {
|
|
1834
|
+
process.stderr.write(
|
|
1835
|
+
`Error: Unknown command '${commandName}'.
|
|
1836
|
+
`
|
|
1837
|
+
);
|
|
1838
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1686
1839
|
}
|
|
1840
|
+
const resolved = cli.name() || progName;
|
|
1841
|
+
const roff = generateManPage(commandName, cmd, resolved);
|
|
1842
|
+
process.stdout.write(roff);
|
|
1843
|
+
});
|
|
1844
|
+
cli.addCommand(manCmd);
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
// src/main.ts
|
|
1848
|
+
var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
|
|
1849
|
+
var verboseHelp = false;
|
|
1850
|
+
function setVerboseHelp(verbose) {
|
|
1851
|
+
verboseHelp = verbose;
|
|
1852
|
+
}
|
|
1853
|
+
var docsUrl = null;
|
|
1854
|
+
function setDocsUrl(url) {
|
|
1855
|
+
docsUrl = url;
|
|
1856
|
+
}
|
|
1857
|
+
function hasVerboseFlag() {
|
|
1858
|
+
return process.argv.includes("--verbose");
|
|
1859
|
+
}
|
|
1860
|
+
var VERSION = "0.0.0";
|
|
1861
|
+
try {
|
|
1862
|
+
const pkg = JSON.parse(readFileSync3(path6.resolve(__dirname3, "../package.json"), "utf-8"));
|
|
1863
|
+
VERSION = pkg.version;
|
|
1864
|
+
} catch {
|
|
1865
|
+
}
|
|
1866
|
+
function createCli(extensionsDir, progName, verbose = false) {
|
|
1867
|
+
verboseHelp = verbose;
|
|
1868
|
+
registerConfigNamespace();
|
|
1869
|
+
const resolvedProgName = progName ?? path6.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
|
|
1870
|
+
const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
|
|
1871
|
+
setLogLevel(cliLogLevel);
|
|
1872
|
+
const program = new Command2(resolvedProgName).exitOverride().version(VERSION, "--version", `Show ${resolvedProgName} version`).description("apcore CLI \u2014 execute apcore modules from the command line").option("--extensions-dir <path>", "Path to extensions directory").option("--commands-dir <path>", "Path to convention-based commands directory").option("--binding <path>", "Path to binding.yaml for display overlay").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING").option("--verbose", "Show all options in help output (including built-in apcore options)");
|
|
1873
|
+
const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
|
|
1874
|
+
void resolvedExtDir;
|
|
1875
|
+
program.addHelpText("after", [
|
|
1876
|
+
"",
|
|
1877
|
+
"Use --help --verbose to show all options (including built-in apcore options).",
|
|
1878
|
+
"Use --help --man to display a formatted man page."
|
|
1879
|
+
].join("\n"));
|
|
1880
|
+
registerInitCommand(program);
|
|
1881
|
+
configureManHelp(program, resolvedProgName, VERSION);
|
|
1882
|
+
program.hook("preAction", async (thisCommand) => {
|
|
1883
|
+
const opts = thisCommand.opts();
|
|
1884
|
+
const commandsDir = opts.commandsDir;
|
|
1885
|
+
const bindingPath = opts.binding;
|
|
1886
|
+
await applyToolkitIntegration(commandsDir, bindingPath);
|
|
1887
|
+
});
|
|
1888
|
+
return program;
|
|
1889
|
+
}
|
|
1890
|
+
async function applyToolkitIntegration(commandsDir, bindingPath) {
|
|
1891
|
+
if (!commandsDir && !bindingPath) {
|
|
1892
|
+
return;
|
|
1687
1893
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
const groupNames = [...this.groupMap.keys()].filter(
|
|
1694
|
-
(g) => !BUILTIN_COMMANDS.includes(g)
|
|
1894
|
+
try {
|
|
1895
|
+
const toolkitModule = "apcore-toolkit";
|
|
1896
|
+
const toolkit = await import(
|
|
1897
|
+
/* @vite-ignore */
|
|
1898
|
+
toolkitModule
|
|
1695
1899
|
);
|
|
1696
|
-
|
|
1697
|
-
|
|
1900
|
+
if (commandsDir) {
|
|
1901
|
+
console.warn("Convention scanning not yet available in TypeScript toolkit");
|
|
1902
|
+
}
|
|
1903
|
+
if (bindingPath) {
|
|
1904
|
+
const resolver = new toolkit.DisplayResolver();
|
|
1905
|
+
void resolver;
|
|
1906
|
+
}
|
|
1907
|
+
} catch {
|
|
1908
|
+
console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
|
|
1698
1909
|
}
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1910
|
+
}
|
|
1911
|
+
function main(progName) {
|
|
1912
|
+
verboseHelp = hasVerboseFlag();
|
|
1913
|
+
const program = createCli(void 0, progName, verboseHelp);
|
|
1914
|
+
try {
|
|
1915
|
+
program.parse(process.argv);
|
|
1916
|
+
} catch (error2) {
|
|
1917
|
+
if (error2 instanceof CommanderError) {
|
|
1918
|
+
process.exit(error2.exitCode);
|
|
1706
1919
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
cmdName,
|
|
1712
|
-
this.helpTextMaxLength
|
|
1713
|
-
);
|
|
1714
|
-
this.groupCache.set(cmdName, lazyGrp);
|
|
1715
|
-
return lazyGrp.command;
|
|
1920
|
+
const code = exitCodeForError(error2);
|
|
1921
|
+
if (error2 instanceof Error) {
|
|
1922
|
+
process.stderr.write(`Error: ${error2.message}
|
|
1923
|
+
`);
|
|
1716
1924
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1925
|
+
process.exit(code);
|
|
1926
|
+
}
|
|
1927
|
+
}
|
|
1928
|
+
function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
|
|
1929
|
+
const moduleId = moduleDef.id;
|
|
1930
|
+
let resolvedSchema = {};
|
|
1931
|
+
let schemaOptions = [];
|
|
1932
|
+
const display = getDisplay(moduleDef);
|
|
1933
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1934
|
+
const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
|
|
1935
|
+
const cmdHelp = cliDisplay.description ?? moduleDef.description;
|
|
1936
|
+
const inputSchema = moduleDef.inputSchema;
|
|
1937
|
+
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
1938
|
+
try {
|
|
1939
|
+
resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
|
|
1940
|
+
} catch {
|
|
1941
|
+
resolvedSchema = inputSchema;
|
|
1730
1942
|
}
|
|
1731
|
-
|
|
1943
|
+
schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
|
|
1732
1944
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1945
|
+
const cmd = new Command2(effectiveCmdName).description(cmdHelp);
|
|
1946
|
+
const inputOpt = new Option2("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
|
|
1947
|
+
const yesOpt = new Option2("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
|
|
1948
|
+
const largeInputOpt = new Option2("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
|
|
1949
|
+
const formatOpt = new Option2("--format <format>", "Set output format: 'json' for machine-readable, 'table' for human-readable");
|
|
1950
|
+
const sandboxOpt = new Option2("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
|
|
1951
|
+
if (!verbose) {
|
|
1952
|
+
inputOpt.hideHelp();
|
|
1953
|
+
yesOpt.hideHelp();
|
|
1954
|
+
largeInputOpt.hideHelp();
|
|
1955
|
+
formatOpt.hideHelp();
|
|
1736
1956
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1957
|
+
cmd.addOption(inputOpt);
|
|
1958
|
+
cmd.addOption(yesOpt);
|
|
1959
|
+
cmd.addOption(largeInputOpt);
|
|
1960
|
+
cmd.addOption(formatOpt);
|
|
1961
|
+
cmd.addOption(sandboxOpt);
|
|
1962
|
+
const footerParts = [];
|
|
1963
|
+
if (!verbose) {
|
|
1964
|
+
footerParts.push("Use --verbose to show all options (including built-in apcore options).");
|
|
1740
1965
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
return this.groupMapBuilt;
|
|
1966
|
+
if (docsUrl) {
|
|
1967
|
+
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
|
|
1744
1968
|
}
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
// src/config.ts
|
|
1748
|
-
init_esm_shims();
|
|
1749
|
-
import * as fs4 from "fs";
|
|
1750
|
-
import yaml from "js-yaml";
|
|
1751
|
-
var DEFAULTS = {
|
|
1752
|
-
"extensions.root": "./extensions",
|
|
1753
|
-
"logging.level": "WARNING",
|
|
1754
|
-
"sandbox.enabled": false,
|
|
1755
|
-
"cli.stdin_buffer_limit": 10485760,
|
|
1756
|
-
"cli.auto_approve": false,
|
|
1757
|
-
"cli.help_text_max_length": 1e3
|
|
1758
|
-
};
|
|
1759
|
-
var ConfigResolver = class {
|
|
1760
|
-
cliFlags;
|
|
1761
|
-
configPath;
|
|
1762
|
-
fileCache = null;
|
|
1763
|
-
fileCacheLoaded = false;
|
|
1764
|
-
constructor(cliFlags, configPath) {
|
|
1765
|
-
this.cliFlags = cliFlags ?? {};
|
|
1766
|
-
this.configPath = configPath ?? "apcore.yaml";
|
|
1969
|
+
if (footerParts.length > 0) {
|
|
1970
|
+
cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
|
|
1767
1971
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
if (flagKey in this.cliFlags) {
|
|
1774
|
-
const value = this.cliFlags[flagKey];
|
|
1775
|
-
if (value !== null && value !== void 0) {
|
|
1776
|
-
return value;
|
|
1777
|
-
}
|
|
1778
|
-
}
|
|
1779
|
-
if (envVar) {
|
|
1780
|
-
const envValue = process.env[envVar];
|
|
1781
|
-
if (envValue !== void 0 && envValue !== "") {
|
|
1782
|
-
return envValue;
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
const fileValue = this.resolveFromFile(key);
|
|
1786
|
-
if (fileValue !== void 0) {
|
|
1787
|
-
return fileValue;
|
|
1972
|
+
for (const opt of schemaOptions) {
|
|
1973
|
+
if (opt.parseArg) {
|
|
1974
|
+
cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
|
|
1975
|
+
} else {
|
|
1976
|
+
cmd.option(opt.flags, opt.description, opt.defaultValue);
|
|
1788
1977
|
}
|
|
1789
|
-
return DEFAULTS[key];
|
|
1790
1978
|
}
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1979
|
+
cmd.action(async (options) => {
|
|
1980
|
+
const stdinFlag = options.input;
|
|
1981
|
+
const autoApprove = options.yes;
|
|
1982
|
+
const largeInput = options.largeInput;
|
|
1983
|
+
const outputFormat = options.format;
|
|
1984
|
+
const sandboxEnabled = options.sandbox;
|
|
1985
|
+
const schemaKwargs = {};
|
|
1986
|
+
const builtinKeys = /* @__PURE__ */ new Set(["input", "yes", "largeInput", "format", "sandbox", "verbose"]);
|
|
1987
|
+
for (const [k, v] of Object.entries(options)) {
|
|
1988
|
+
if (!builtinKeys.has(k)) {
|
|
1989
|
+
schemaKwargs[k] = v;
|
|
1990
|
+
}
|
|
1801
1991
|
}
|
|
1802
|
-
return this.fileCache[key];
|
|
1803
|
-
}
|
|
1804
|
-
/**
|
|
1805
|
-
* Load and flatten a YAML config file.
|
|
1806
|
-
*/
|
|
1807
|
-
loadConfigFile() {
|
|
1808
|
-
let content;
|
|
1809
1992
|
try {
|
|
1810
|
-
|
|
1993
|
+
const merged = await collectInput(stdinFlag, schemaKwargs, largeInput);
|
|
1994
|
+
const reconverted = reconvertEnumValues(merged, schemaOptions);
|
|
1995
|
+
await checkApproval(moduleDef, autoApprove);
|
|
1996
|
+
const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
|
|
1997
|
+
const sandbox = new Sandbox2(sandboxEnabled);
|
|
1998
|
+
const startTime = performance.now();
|
|
1999
|
+
const result = await sandbox.execute(moduleId, reconverted, executor);
|
|
2000
|
+
const durationMs = Math.round(performance.now() - startTime);
|
|
2001
|
+
const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2002
|
+
const auditLogger = getAuditLogger2();
|
|
2003
|
+
if (auditLogger) {
|
|
2004
|
+
auditLogger.logExecution(moduleId, reconverted, "success", 0, durationMs);
|
|
2005
|
+
}
|
|
2006
|
+
formatExecResult(result, outputFormat);
|
|
1811
2007
|
} catch (err) {
|
|
1812
|
-
|
|
1813
|
-
|
|
2008
|
+
const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
|
|
2009
|
+
const auditLogger = getAuditLogger2();
|
|
2010
|
+
const code = exitCodeForError(err);
|
|
2011
|
+
if (auditLogger) {
|
|
2012
|
+
auditLogger.logExecution(moduleId, {}, "error", code, 0);
|
|
1814
2013
|
}
|
|
1815
|
-
|
|
1816
|
-
`
|
|
1817
|
-
|
|
1818
|
-
return null;
|
|
1819
|
-
}
|
|
1820
|
-
let parsed;
|
|
1821
|
-
try {
|
|
1822
|
-
parsed = yaml.load(content);
|
|
1823
|
-
} catch {
|
|
1824
|
-
console.warn(
|
|
1825
|
-
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1826
|
-
);
|
|
1827
|
-
return null;
|
|
1828
|
-
}
|
|
1829
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1830
|
-
console.warn(
|
|
1831
|
-
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1832
|
-
);
|
|
1833
|
-
return null;
|
|
1834
|
-
}
|
|
1835
|
-
return this.flattenDict(parsed);
|
|
1836
|
-
}
|
|
1837
|
-
/**
|
|
1838
|
-
* Flatten nested dict to dot-notation keys.
|
|
1839
|
-
*/
|
|
1840
|
-
flattenDict(d, prefix = "") {
|
|
1841
|
-
const result = {};
|
|
1842
|
-
for (const [key, value] of Object.entries(d)) {
|
|
1843
|
-
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
1844
|
-
if (typeof value === "object" && value !== null && !Array.isArray(value)) {
|
|
1845
|
-
Object.assign(
|
|
1846
|
-
result,
|
|
1847
|
-
this.flattenDict(value, fullKey)
|
|
1848
|
-
);
|
|
1849
|
-
} else {
|
|
1850
|
-
result[fullKey] = value;
|
|
2014
|
+
if (err instanceof Error) {
|
|
2015
|
+
process.stderr.write(`Error: ${err.message}
|
|
2016
|
+
`);
|
|
1851
2017
|
}
|
|
2018
|
+
process.exit(code);
|
|
1852
2019
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
init_esm_shims();
|
|
1859
|
-
init_errors();
|
|
1860
|
-
import { Command as Command3 } from "commander";
|
|
1861
|
-
var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
1862
|
-
function validateTag(tag) {
|
|
1863
|
-
if (!TAG_PATTERN.test(tag)) {
|
|
2020
|
+
});
|
|
2021
|
+
return cmd;
|
|
2022
|
+
}
|
|
2023
|
+
function validateModuleId(moduleId) {
|
|
2024
|
+
if (moduleId.length > 128) {
|
|
1864
2025
|
process.stderr.write(
|
|
1865
|
-
`Error: Invalid
|
|
2026
|
+
`Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
|
|
2027
|
+
`
|
|
2028
|
+
);
|
|
2029
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2030
|
+
}
|
|
2031
|
+
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
|
|
2032
|
+
process.stderr.write(
|
|
2033
|
+
`Error: Invalid module ID format: '${moduleId}'.
|
|
1866
2034
|
`
|
|
1867
2035
|
);
|
|
1868
2036
|
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1869
2037
|
}
|
|
1870
2038
|
}
|
|
1871
|
-
function
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
for (const t of opts.tag) {
|
|
1877
|
-
validateTag(t);
|
|
2039
|
+
async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
|
|
2040
|
+
const cliKwargsNonNull = {};
|
|
2041
|
+
for (const [k, v] of Object.entries(cliKwargs)) {
|
|
2042
|
+
if (v !== null && v !== void 0) {
|
|
2043
|
+
cliKwargsNonNull[k] = v;
|
|
1878
2044
|
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
2045
|
+
}
|
|
2046
|
+
if (!stdinFlag) {
|
|
2047
|
+
return cliKwargsNonNull;
|
|
2048
|
+
}
|
|
2049
|
+
if (stdinFlag === "-") {
|
|
2050
|
+
const raw = await readStdin();
|
|
2051
|
+
const rawSize = Buffer.byteLength(raw, "utf-8");
|
|
2052
|
+
if (rawSize > 10485760 && !largeInput) {
|
|
2053
|
+
process.stderr.write(
|
|
2054
|
+
"Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
|
|
2055
|
+
);
|
|
2056
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1882
2057
|
}
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
const filterTags = new Set(opts.tag);
|
|
1886
|
-
filtered = modules.filter((m) => {
|
|
1887
|
-
const mTags = m.tags ?? [];
|
|
1888
|
-
return [...filterTags].every((t) => mTags.includes(t));
|
|
1889
|
-
});
|
|
2058
|
+
if (!raw) {
|
|
2059
|
+
return cliKwargsNonNull;
|
|
1890
2060
|
}
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
const describeCmd = new Command3("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").option("--format <format>", "Output format.", void 0).action((moduleId, opts) => {
|
|
1896
|
-
validateModuleId(moduleId);
|
|
1897
|
-
const moduleDef = registry.getModule(moduleId);
|
|
1898
|
-
if (!moduleDef) {
|
|
2061
|
+
let stdinData;
|
|
2062
|
+
try {
|
|
2063
|
+
stdinData = JSON.parse(raw);
|
|
2064
|
+
} catch {
|
|
1899
2065
|
process.stderr.write(
|
|
1900
|
-
|
|
2066
|
+
"Error: STDIN does not contain valid JSON.\n"
|
|
2067
|
+
);
|
|
2068
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2069
|
+
}
|
|
2070
|
+
if (typeof stdinData !== "object" || stdinData === null || Array.isArray(stdinData)) {
|
|
2071
|
+
process.stderr.write(
|
|
2072
|
+
`Error: STDIN JSON must be an object, got ${Array.isArray(stdinData) ? "array" : typeof stdinData}.
|
|
1901
2073
|
`
|
|
1902
2074
|
);
|
|
1903
|
-
process.exit(EXIT_CODES.
|
|
2075
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1904
2076
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
cli.addCommand(describeCmd);
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
// src/shell.ts
|
|
1912
|
-
init_esm_shims();
|
|
1913
|
-
init_errors();
|
|
1914
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
1915
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1916
|
-
import * as path6 from "path";
|
|
1917
|
-
import { Command as Command4, Help, Option as Option2 } from "commander";
|
|
1918
|
-
var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
|
|
1919
|
-
var SHELL_VERSION = "0.0.0";
|
|
1920
|
-
try {
|
|
1921
|
-
const pkg = JSON.parse(readFileSync3(path6.resolve(__dirname3, "../package.json"), "utf-8"));
|
|
1922
|
-
SHELL_VERSION = pkg.version;
|
|
1923
|
-
} catch {
|
|
1924
|
-
}
|
|
1925
|
-
function makeFunctionName(progName) {
|
|
1926
|
-
return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
|
|
1927
|
-
}
|
|
1928
|
-
function shellQuote(s) {
|
|
1929
|
-
return "'" + s.replace(/'/g, "'\\''") + "'";
|
|
1930
|
-
}
|
|
1931
|
-
function generateBashCompletion(progName) {
|
|
1932
|
-
const fn = makeFunctionName(progName);
|
|
1933
|
-
const quoted = shellQuote(progName);
|
|
1934
|
-
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1935
|
-
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1936
|
-
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1937
|
-
const ids=JSON.parse(m).map(x=>x.id);
|
|
1938
|
-
const g=new Set(),t=[];
|
|
1939
|
-
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1940
|
-
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1941
|
-
" 2>/dev/null`;
|
|
1942
|
-
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1943
|
-
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1944
|
-
const g=process.env._APCORE_GRP;
|
|
1945
|
-
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1946
|
-
" 2>/dev/null`;
|
|
1947
|
-
return `${fn}() {
|
|
1948
|
-
local cur prev opts
|
|
1949
|
-
COMPREPLY=()
|
|
1950
|
-
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1951
|
-
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1952
|
-
|
|
1953
|
-
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
1954
|
-
opts="completion describe exec init list man"
|
|
1955
|
-
local groups_and_top=$(${groupsAndTopCmd})
|
|
1956
|
-
COMPREPLY=( $(compgen -W "\${opts} \${groups_and_top}" -- \${cur}) )
|
|
1957
|
-
return 0
|
|
1958
|
-
fi
|
|
1959
|
-
|
|
1960
|
-
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
1961
|
-
if [[ "\${COMP_WORDS[1]}" == "exec" ]]; then
|
|
1962
|
-
local modules=$(${moduleListCmd})
|
|
1963
|
-
COMPREPLY=( $(compgen -W "\${modules}" -- \${cur}) )
|
|
1964
|
-
return 0
|
|
1965
|
-
fi
|
|
1966
|
-
export _APCORE_GRP="\${COMP_WORDS[1]}"
|
|
1967
|
-
local group_cmds=$(${groupCmdsCmd})
|
|
1968
|
-
COMPREPLY=( $(compgen -W "\${group_cmds}" -- \${cur}) )
|
|
1969
|
-
return 0
|
|
1970
|
-
fi
|
|
1971
|
-
}
|
|
1972
|
-
complete -F ${fn} ${quoted}
|
|
1973
|
-
`;
|
|
1974
|
-
}
|
|
1975
|
-
function generateZshCompletion(progName) {
|
|
1976
|
-
const fn = makeFunctionName(progName);
|
|
1977
|
-
const quoted = shellQuote(progName);
|
|
1978
|
-
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1979
|
-
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1980
|
-
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1981
|
-
const ids=JSON.parse(m).map(x=>x.id);
|
|
1982
|
-
const g=new Set(),t=[];
|
|
1983
|
-
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1984
|
-
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1985
|
-
" 2>/dev/null`;
|
|
1986
|
-
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1987
|
-
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1988
|
-
const g=process.env._APCORE_GRP;
|
|
1989
|
-
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1990
|
-
" 2>/dev/null`;
|
|
1991
|
-
return `#compdef ${progName}
|
|
1992
|
-
|
|
1993
|
-
${fn}() {
|
|
1994
|
-
local -a commands
|
|
1995
|
-
commands=(
|
|
1996
|
-
'list:List available modules'
|
|
1997
|
-
'describe:Show module metadata and schema'
|
|
1998
|
-
'completion:Generate shell completion script'
|
|
1999
|
-
'init:Scaffolding commands'
|
|
2000
|
-
'man:Generate man page'
|
|
2001
|
-
)
|
|
2002
|
-
|
|
2003
|
-
_arguments -C \\
|
|
2004
|
-
'1:command:->command' \\
|
|
2005
|
-
'*::arg:->args'
|
|
2006
|
-
|
|
2007
|
-
case "$state" in
|
|
2008
|
-
command)
|
|
2009
|
-
_describe -t commands '${progName} commands' commands
|
|
2010
|
-
local -a groups_and_top
|
|
2011
|
-
groups_and_top=($(${groupsAndTopCmd}))
|
|
2012
|
-
compadd -a groups_and_top
|
|
2013
|
-
;;
|
|
2014
|
-
args)
|
|
2015
|
-
case "\${words[1]}" in
|
|
2016
|
-
exec)
|
|
2017
|
-
local modules
|
|
2018
|
-
modules=($(${moduleListCmd}))
|
|
2019
|
-
compadd -a modules
|
|
2020
|
-
;;
|
|
2021
|
-
*)
|
|
2022
|
-
export _APCORE_GRP="\${words[1]}"
|
|
2023
|
-
local -a group_cmds
|
|
2024
|
-
group_cmds=($(${groupCmdsCmd}))
|
|
2025
|
-
compadd -a group_cmds
|
|
2026
|
-
;;
|
|
2027
|
-
esac
|
|
2028
|
-
;;
|
|
2029
|
-
esac
|
|
2077
|
+
return { ...stdinData, ...cliKwargsNonNull };
|
|
2078
|
+
}
|
|
2079
|
+
return cliKwargsNonNull;
|
|
2030
2080
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2081
|
+
function readStdin() {
|
|
2082
|
+
return new Promise((resolve3, reject) => {
|
|
2083
|
+
const chunks = [];
|
|
2084
|
+
const onData = (chunk) => chunks.push(chunk);
|
|
2085
|
+
const onEnd = () => {
|
|
2086
|
+
cleanup();
|
|
2087
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
2088
|
+
};
|
|
2089
|
+
const onError = (err) => {
|
|
2090
|
+
cleanup();
|
|
2091
|
+
reject(err);
|
|
2092
|
+
};
|
|
2093
|
+
const cleanup = () => {
|
|
2094
|
+
process.stdin.removeListener("data", onData);
|
|
2095
|
+
process.stdin.removeListener("end", onEnd);
|
|
2096
|
+
process.stdin.removeListener("error", onError);
|
|
2097
|
+
};
|
|
2098
|
+
process.stdin.on("data", onData);
|
|
2099
|
+
process.stdin.on("end", onEnd);
|
|
2100
|
+
process.stdin.on("error", onError);
|
|
2101
|
+
process.stdin.resume();
|
|
2102
|
+
});
|
|
2034
2103
|
}
|
|
2035
|
-
function
|
|
2036
|
-
const
|
|
2037
|
-
const
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
const g=process.env._APCORE_GRP;
|
|
2055
|
-
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
2056
|
-
" 2>/dev/null
|
|
2057
|
-
end
|
|
2058
|
-
|
|
2059
|
-
# Group subcommand completion \u2014 matches when position 1 is not a builtin
|
|
2060
|
-
complete -c ${quoted} -n "not __fish_use_subcommand; and not __fish_seen_subcommand_from list describe completion init man exec" -a "(__apcore_group_cmds)"
|
|
2061
|
-
`;
|
|
2104
|
+
function reconvertEnumValues(kwargs, options) {
|
|
2105
|
+
const result = { ...kwargs };
|
|
2106
|
+
for (const opt of options) {
|
|
2107
|
+
if (!opt.enumOriginalTypes) continue;
|
|
2108
|
+
const paramName = opt.name;
|
|
2109
|
+
if (!(paramName in result) || result[paramName] === null || result[paramName] === void 0) {
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
const strVal = String(result[paramName]);
|
|
2113
|
+
const origType = opt.enumOriginalTypes[strVal];
|
|
2114
|
+
if (origType === "int") {
|
|
2115
|
+
result[paramName] = parseInt(strVal, 10);
|
|
2116
|
+
} else if (origType === "float") {
|
|
2117
|
+
result[paramName] = parseFloat(strVal);
|
|
2118
|
+
} else if (origType === "bool") {
|
|
2119
|
+
result[paramName] = strVal.toLowerCase() === "true";
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
return result;
|
|
2062
2123
|
}
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2124
|
+
|
|
2125
|
+
// src/cli.ts
|
|
2126
|
+
init_esm_shims();
|
|
2127
|
+
import { Command as Command3 } from "commander";
|
|
2128
|
+
var BUILTIN_COMMANDS = ["completion", "describe", "exec", "init", "list", "man"];
|
|
2129
|
+
var LazyModuleGroup = class {
|
|
2130
|
+
registry;
|
|
2131
|
+
executor;
|
|
2132
|
+
helpTextMaxLength;
|
|
2133
|
+
commandCache = /* @__PURE__ */ new Map();
|
|
2134
|
+
/** alias -> canonical module_id (populated lazily) */
|
|
2135
|
+
aliasMap = /* @__PURE__ */ new Map();
|
|
2136
|
+
/** module_id -> descriptor cache (populated during alias map build) */
|
|
2137
|
+
descriptorCache = /* @__PURE__ */ new Map();
|
|
2138
|
+
aliasMapBuilt = false;
|
|
2139
|
+
constructor(registry, executor, helpTextMaxLength = 1e3) {
|
|
2140
|
+
this.registry = registry;
|
|
2141
|
+
this.executor = executor;
|
|
2142
|
+
this.helpTextMaxLength = helpTextMaxLength;
|
|
2066
2143
|
}
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2144
|
+
/**
|
|
2145
|
+
* Build alias->module_id map from display overlay metadata.
|
|
2146
|
+
*/
|
|
2147
|
+
buildAliasMap() {
|
|
2148
|
+
if (this.aliasMapBuilt) {
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
try {
|
|
2152
|
+
for (const descriptor of this.registry.listModules()) {
|
|
2153
|
+
const moduleId = descriptor.id;
|
|
2154
|
+
this.descriptorCache.set(moduleId, descriptor);
|
|
2155
|
+
const display = getDisplay(descriptor);
|
|
2156
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
2157
|
+
const cliAlias = cliDisplay.alias;
|
|
2158
|
+
if (cliAlias && cliAlias !== moduleId) {
|
|
2159
|
+
this.aliasMap.set(cliAlias, moduleId);
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
this.aliasMapBuilt = true;
|
|
2163
|
+
} catch {
|
|
2164
|
+
warn("Failed to build alias map from registry");
|
|
2078
2165
|
}
|
|
2079
2166
|
}
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2167
|
+
/**
|
|
2168
|
+
* List all available command names from the Registry.
|
|
2169
|
+
*/
|
|
2170
|
+
listCommands() {
|
|
2171
|
+
this.buildAliasMap();
|
|
2172
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
2173
|
+
for (const [alias, moduleId] of this.aliasMap) {
|
|
2174
|
+
reverse.set(moduleId, alias);
|
|
2086
2175
|
}
|
|
2176
|
+
const moduleIds = this.registry.listModules().map((m) => m.id);
|
|
2177
|
+
const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
|
|
2178
|
+
return [...new Set(names)].sort();
|
|
2087
2179
|
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
);
|
|
2180
|
+
/**
|
|
2181
|
+
* Get or lazily build a Commander Command for the given module.
|
|
2182
|
+
*/
|
|
2183
|
+
getCommand(cmdName) {
|
|
2184
|
+
if (this.commandCache.has(cmdName)) {
|
|
2185
|
+
return this.commandCache.get(cmdName);
|
|
2186
|
+
}
|
|
2187
|
+
this.buildAliasMap();
|
|
2188
|
+
const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
|
|
2189
|
+
let moduleDef = this.descriptorCache.get(moduleId);
|
|
2190
|
+
if (!moduleDef) {
|
|
2191
|
+
moduleDef = this.registry.getModule(moduleId) ?? void 0;
|
|
2192
|
+
}
|
|
2193
|
+
if (!moduleDef) {
|
|
2194
|
+
return null;
|
|
2195
|
+
}
|
|
2196
|
+
const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
|
|
2197
|
+
this.commandCache.set(cmdName, cmd);
|
|
2198
|
+
return cmd;
|
|
2108
2199
|
}
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2200
|
+
};
|
|
2201
|
+
var LazyGroup = class {
|
|
2202
|
+
members;
|
|
2203
|
+
_executor;
|
|
2204
|
+
_helpTextMaxLength;
|
|
2205
|
+
_cmdCache = /* @__PURE__ */ new Map();
|
|
2206
|
+
command;
|
|
2207
|
+
constructor(members, executor, name, helpTextMaxLength = 1e3) {
|
|
2208
|
+
this.members = members;
|
|
2209
|
+
this._executor = executor;
|
|
2210
|
+
this._helpTextMaxLength = helpTextMaxLength;
|
|
2211
|
+
this.command = new Command3(name).description(`${name} commands`);
|
|
2212
|
+
for (const [cmdName, [, descriptor]] of this.members) {
|
|
2213
|
+
const cmd = buildModuleCommand(
|
|
2214
|
+
descriptor,
|
|
2215
|
+
this._executor,
|
|
2216
|
+
this._helpTextMaxLength,
|
|
2217
|
+
cmdName
|
|
2218
|
+
);
|
|
2219
|
+
this._cmdCache.set(cmdName, cmd);
|
|
2220
|
+
this.command.addCommand(cmd);
|
|
2125
2221
|
}
|
|
2126
2222
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
sections.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
|
|
2130
|
-
sections.push(
|
|
2131
|
-
"Path to the apcore extensions directory. Overrides the default \\fI./extensions\\fR."
|
|
2132
|
-
);
|
|
2133
|
-
sections.push(".TP");
|
|
2134
|
-
sections.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
|
|
2135
|
-
sections.push(
|
|
2136
|
-
"Set to \\fB1\\fR to bypass approval prompts for modules that require human-in-the-loop confirmation."
|
|
2137
|
-
);
|
|
2138
|
-
sections.push(".TP");
|
|
2139
|
-
sections.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
|
|
2140
|
-
sections.push(
|
|
2141
|
-
"CLI-specific logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Takes priority over \\fBAPCORE_LOGGING_LEVEL\\fR. Default: WARNING."
|
|
2142
|
-
);
|
|
2143
|
-
sections.push(".TP");
|
|
2144
|
-
sections.push("\\fBAPCORE_LOGGING_LEVEL\\fR");
|
|
2145
|
-
sections.push(
|
|
2146
|
-
"Global apcore logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Used as fallback when \\fBAPCORE_CLI_LOGGING_LEVEL\\fR is not set. Default: WARNING."
|
|
2147
|
-
);
|
|
2148
|
-
sections.push(".TP");
|
|
2149
|
-
sections.push("\\fBAPCORE_AUTH_API_KEY\\fR");
|
|
2150
|
-
sections.push(
|
|
2151
|
-
"API key for authenticating with the apcore registry."
|
|
2152
|
-
);
|
|
2153
|
-
sections.push(".SH EXIT CODES");
|
|
2154
|
-
const exitCodes = [
|
|
2155
|
-
["0", "Success."],
|
|
2156
|
-
["1", "Module execution error."],
|
|
2157
|
-
["2", "Invalid CLI input or missing argument."],
|
|
2158
|
-
["44", "Module not found, disabled, or failed to load."],
|
|
2159
|
-
["45", "Input failed JSON Schema validation."],
|
|
2160
|
-
[
|
|
2161
|
-
"46",
|
|
2162
|
-
"Approval denied, timed out, or no interactive terminal available."
|
|
2163
|
-
],
|
|
2164
|
-
[
|
|
2165
|
-
"47",
|
|
2166
|
-
"Configuration error (extensions directory not found or unreadable)."
|
|
2167
|
-
],
|
|
2168
|
-
["48", "Schema contains a circular \\fB$ref\\fR."],
|
|
2169
|
-
["77", "ACL denied \u2014 insufficient permissions for this module."],
|
|
2170
|
-
["130", "Execution cancelled by user (SIGINT / Ctrl\\-C)."]
|
|
2171
|
-
];
|
|
2172
|
-
for (const [code, meaning] of exitCodes) {
|
|
2173
|
-
sections.push(`.TP
|
|
2174
|
-
\\fB${code}\\fR
|
|
2175
|
-
${meaning}`);
|
|
2223
|
+
listCommands() {
|
|
2224
|
+
return [...this.members.keys()].sort();
|
|
2176
2225
|
}
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
const s = [];
|
|
2195
|
-
const resolvedDesc = description ?? program.description() ?? `${progName} CLI`;
|
|
2196
|
-
s.push(`.TH "${progName.toUpperCase()}" "1" "${today}" "${progName} ${version}" "${progName} Manual"`);
|
|
2197
|
-
s.push(".SH NAME");
|
|
2198
|
-
s.push(`${progName} \\- ${roffEscape(resolvedDesc)}`);
|
|
2199
|
-
s.push(".SH SYNOPSIS");
|
|
2200
|
-
s.push(`\\fB${progName}\\fR [\\fIglobal\\-options\\fR] \\fIcommand\\fR [\\fIcommand\\-options\\fR]`);
|
|
2201
|
-
if (resolvedDesc) {
|
|
2202
|
-
s.push(".SH DESCRIPTION");
|
|
2203
|
-
s.push(roffEscape(resolvedDesc));
|
|
2226
|
+
getCommand(cmdName) {
|
|
2227
|
+
if (this._cmdCache.has(cmdName)) {
|
|
2228
|
+
return this._cmdCache.get(cmdName);
|
|
2229
|
+
}
|
|
2230
|
+
const entry = this.members.get(cmdName);
|
|
2231
|
+
if (!entry) {
|
|
2232
|
+
return null;
|
|
2233
|
+
}
|
|
2234
|
+
const [, descriptor] = entry;
|
|
2235
|
+
const cmd = buildModuleCommand(
|
|
2236
|
+
descriptor,
|
|
2237
|
+
this._executor,
|
|
2238
|
+
this._helpTextMaxLength,
|
|
2239
|
+
cmdName
|
|
2240
|
+
);
|
|
2241
|
+
this._cmdCache.set(cmdName, cmd);
|
|
2242
|
+
return cmd;
|
|
2204
2243
|
}
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2244
|
+
};
|
|
2245
|
+
var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
2246
|
+
/** groupName -> { cmdName -> [moduleId, descriptor] } */
|
|
2247
|
+
groupMap = /* @__PURE__ */ new Map();
|
|
2248
|
+
/** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
|
|
2249
|
+
topLevelModules = /* @__PURE__ */ new Map();
|
|
2250
|
+
/** Cached LazyGroup instances */
|
|
2251
|
+
groupCache = /* @__PURE__ */ new Map();
|
|
2252
|
+
groupMapBuilt = false;
|
|
2253
|
+
/**
|
|
2254
|
+
* Determine (groupName | null, commandName) for a module from its display overlay.
|
|
2255
|
+
*/
|
|
2256
|
+
static resolveGroup(moduleId, descriptor) {
|
|
2257
|
+
if (!moduleId) {
|
|
2258
|
+
warn("Empty module_id encountered in resolveGroup");
|
|
2259
|
+
return [null, ""];
|
|
2260
|
+
}
|
|
2261
|
+
const display = getDisplay(descriptor);
|
|
2262
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
2263
|
+
const explicitGroup = cliDisplay.group;
|
|
2264
|
+
if (typeof explicitGroup === "string" && explicitGroup !== "") {
|
|
2265
|
+
return [explicitGroup, cliDisplay.alias ?? moduleId];
|
|
2266
|
+
}
|
|
2267
|
+
if (explicitGroup === "") {
|
|
2268
|
+
return [null, cliDisplay.alias ?? moduleId];
|
|
2269
|
+
}
|
|
2270
|
+
const cliName = cliDisplay.alias ?? moduleId;
|
|
2271
|
+
if (cliName.includes(".")) {
|
|
2272
|
+
const dotIdx = cliName.indexOf(".");
|
|
2273
|
+
const group = cliName.substring(0, dotIdx);
|
|
2274
|
+
const cmd = cliName.substring(dotIdx + 1);
|
|
2275
|
+
return [group, cmd];
|
|
2213
2276
|
}
|
|
2277
|
+
return [null, cliName];
|
|
2214
2278
|
}
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
const
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
if (
|
|
2232
|
-
|
|
2279
|
+
/**
|
|
2280
|
+
* Build the group map from registry modules.
|
|
2281
|
+
*/
|
|
2282
|
+
buildGroupMap() {
|
|
2283
|
+
if (this.groupMapBuilt) {
|
|
2284
|
+
return;
|
|
2285
|
+
}
|
|
2286
|
+
try {
|
|
2287
|
+
this.buildAliasMap();
|
|
2288
|
+
for (const descriptor of this.registry.listModules()) {
|
|
2289
|
+
const moduleId = descriptor.id;
|
|
2290
|
+
const cached = this.descriptorCache.get(moduleId);
|
|
2291
|
+
if (!cached) {
|
|
2292
|
+
continue;
|
|
2293
|
+
}
|
|
2294
|
+
const [group, cmd] = _GroupedModuleGroup.resolveGroup(moduleId, cached);
|
|
2295
|
+
if (group === null) {
|
|
2296
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
2297
|
+
} else if (!/^[a-z][a-z0-9_-]*$/.test(group)) {
|
|
2298
|
+
warn(
|
|
2299
|
+
`Module '${moduleId}': group name '${group}' is not shell-safe \u2014 treating as top-level.`
|
|
2300
|
+
);
|
|
2301
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
2302
|
+
} else {
|
|
2303
|
+
if (!this.groupMap.has(group)) {
|
|
2304
|
+
this.groupMap.set(group, /* @__PURE__ */ new Map());
|
|
2305
|
+
}
|
|
2306
|
+
this.groupMap.get(group).set(cmd, [moduleId, cached]);
|
|
2307
|
+
}
|
|
2233
2308
|
}
|
|
2234
|
-
const
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
if (subDesc) s.push(roffEscape(subDesc));
|
|
2240
|
-
const subOpts = cmdHelp.visibleOptions(sub).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
|
|
2241
|
-
for (const opt of subOpts) {
|
|
2242
|
-
const flag = [opt.short, opt.long].filter(Boolean).join(", ");
|
|
2243
|
-
s.push(".RS");
|
|
2244
|
-
s.push(".TP");
|
|
2245
|
-
s.push(`\\fB${roffEscape(flag)}\\fR`);
|
|
2246
|
-
if (opt.description) s.push(roffEscape(opt.description));
|
|
2247
|
-
s.push(".RE");
|
|
2309
|
+
for (const groupName of this.groupMap.keys()) {
|
|
2310
|
+
if (BUILTIN_COMMANDS.includes(groupName)) {
|
|
2311
|
+
warn(
|
|
2312
|
+
`Group name '${groupName}' collides with a built-in command and will be ignored`
|
|
2313
|
+
);
|
|
2248
2314
|
}
|
|
2249
2315
|
}
|
|
2316
|
+
this.groupMapBuilt = true;
|
|
2317
|
+
} catch {
|
|
2318
|
+
warn("Failed to build group map");
|
|
2250
2319
|
}
|
|
2251
2320
|
}
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
s.push(".SH EXIT CODES");
|
|
2263
|
-
const exitCodes = [
|
|
2264
|
-
["0", "Success."],
|
|
2265
|
-
["1", "Module execution error."],
|
|
2266
|
-
["2", "Invalid CLI input or missing argument."],
|
|
2267
|
-
["44", "Module not found, disabled, or failed to load."],
|
|
2268
|
-
["45", "Input failed JSON Schema validation."],
|
|
2269
|
-
["46", "Approval denied or timed out."],
|
|
2270
|
-
["47", "Configuration error."],
|
|
2271
|
-
["77", "ACL denied."],
|
|
2272
|
-
["130", "Cancelled by user (SIGINT)."]
|
|
2273
|
-
];
|
|
2274
|
-
for (const [code, meaning] of exitCodes) {
|
|
2275
|
-
s.push(`.TP
|
|
2276
|
-
\\fB${code}\\fR
|
|
2277
|
-
${meaning}`);
|
|
2321
|
+
/**
|
|
2322
|
+
* List all available command names: builtins + group names + top-level module names.
|
|
2323
|
+
*/
|
|
2324
|
+
listCommands() {
|
|
2325
|
+
this.buildGroupMap();
|
|
2326
|
+
const groupNames = [...this.groupMap.keys()].filter(
|
|
2327
|
+
(g) => !BUILTIN_COMMANDS.includes(g)
|
|
2328
|
+
);
|
|
2329
|
+
const topNames = [...this.topLevelModules.keys()];
|
|
2330
|
+
return [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...groupNames, ...topNames])].sort();
|
|
2278
2331
|
}
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2332
|
+
/**
|
|
2333
|
+
* Get a command by name: check builtins -> group cache -> group map -> top-level modules.
|
|
2334
|
+
*/
|
|
2335
|
+
getCommand(cmdName) {
|
|
2336
|
+
this.buildGroupMap();
|
|
2337
|
+
if (this.groupCache.has(cmdName)) {
|
|
2338
|
+
return this.groupCache.get(cmdName).command;
|
|
2339
|
+
}
|
|
2340
|
+
if (this.groupMap.has(cmdName)) {
|
|
2341
|
+
const lazyGrp = new LazyGroup(
|
|
2342
|
+
this.groupMap.get(cmdName),
|
|
2343
|
+
this.executor,
|
|
2344
|
+
cmdName,
|
|
2345
|
+
this.helpTextMaxLength
|
|
2346
|
+
);
|
|
2347
|
+
this.groupCache.set(cmdName, lazyGrp);
|
|
2348
|
+
return lazyGrp.command;
|
|
2349
|
+
}
|
|
2350
|
+
if (this.topLevelModules.has(cmdName)) {
|
|
2351
|
+
if (this.commandCache.has(cmdName)) {
|
|
2352
|
+
return this.commandCache.get(cmdName);
|
|
2353
|
+
}
|
|
2354
|
+
const [, descriptor] = this.topLevelModules.get(cmdName);
|
|
2355
|
+
const cmd = buildModuleCommand(
|
|
2356
|
+
descriptor,
|
|
2357
|
+
this.executor,
|
|
2358
|
+
this.helpTextMaxLength,
|
|
2359
|
+
cmdName
|
|
2360
|
+
);
|
|
2361
|
+
this.commandCache.set(cmdName, cmd);
|
|
2362
|
+
return cmd;
|
|
2363
|
+
}
|
|
2364
|
+
return null;
|
|
2365
|
+
}
|
|
2366
|
+
/** Expose groupMap for testing. */
|
|
2367
|
+
getGroupMap() {
|
|
2368
|
+
return this.groupMap;
|
|
2369
|
+
}
|
|
2370
|
+
/** Expose topLevelModules for testing. */
|
|
2371
|
+
getTopLevelModules() {
|
|
2372
|
+
return this.topLevelModules;
|
|
2373
|
+
}
|
|
2374
|
+
/** Expose groupMapBuilt for testing. */
|
|
2375
|
+
isGroupMapBuilt() {
|
|
2376
|
+
return this.groupMapBuilt;
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// src/discovery.ts
|
|
2381
|
+
init_esm_shims();
|
|
2382
|
+
init_errors();
|
|
2383
|
+
import { Command as Command4 } from "commander";
|
|
2384
|
+
var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
2385
|
+
function validateTag(tag) {
|
|
2386
|
+
if (!TAG_PATTERN.test(tag)) {
|
|
2387
|
+
process.stderr.write(
|
|
2388
|
+
`Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
|
|
2389
|
+
`
|
|
2390
|
+
);
|
|
2391
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2284
2392
|
}
|
|
2285
|
-
return s.join("\n");
|
|
2286
2393
|
}
|
|
2287
|
-
function
|
|
2288
|
-
|
|
2289
|
-
program.addOption(manOpt);
|
|
2290
|
-
program.addHelpText("beforeAll", () => {
|
|
2291
|
-
if (program.opts().man) {
|
|
2292
|
-
process.stdout.write(buildProgramManPage(program, progName, version, description, docsUrl2));
|
|
2293
|
-
process.stdout.write("\n");
|
|
2294
|
-
process.exit(0);
|
|
2295
|
-
}
|
|
2296
|
-
return "";
|
|
2297
|
-
});
|
|
2394
|
+
function collectTag(value, previous) {
|
|
2395
|
+
return previous.concat([value]);
|
|
2298
2396
|
}
|
|
2299
|
-
function
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
const validShells = ["bash", "zsh", "fish"];
|
|
2304
|
-
if (!validShells.includes(shell)) {
|
|
2305
|
-
process.stderr.write(
|
|
2306
|
-
`Error: Unknown shell '${shell}'. Expected: bash, zsh, or fish.
|
|
2307
|
-
`
|
|
2308
|
-
);
|
|
2309
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2397
|
+
function registerDiscoveryCommands(cli, registry) {
|
|
2398
|
+
const listCmd = new Command4("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--format <format>", "Output format.", void 0).action((opts) => {
|
|
2399
|
+
for (const t of opts.tag) {
|
|
2400
|
+
validateTag(t);
|
|
2310
2401
|
}
|
|
2311
|
-
const
|
|
2312
|
-
const
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2402
|
+
const modules = [];
|
|
2403
|
+
for (const m of registry.listModules()) {
|
|
2404
|
+
modules.push(m);
|
|
2405
|
+
}
|
|
2406
|
+
let filtered = modules;
|
|
2407
|
+
if (opts.tag.length > 0) {
|
|
2408
|
+
const filterTags = new Set(opts.tag);
|
|
2409
|
+
filtered = modules.filter((m) => {
|
|
2410
|
+
const mTags = m.tags ?? [];
|
|
2411
|
+
return [...filterTags].every((t) => mTags.includes(t));
|
|
2412
|
+
});
|
|
2413
|
+
}
|
|
2414
|
+
const fmt = resolveFormat(opts.format);
|
|
2415
|
+
formatModuleList(filtered, fmt, opts.tag.length > 0 ? opts.tag : void 0);
|
|
2318
2416
|
});
|
|
2319
|
-
cli.addCommand(
|
|
2320
|
-
const
|
|
2321
|
-
|
|
2322
|
-
const
|
|
2323
|
-
if (!
|
|
2417
|
+
cli.addCommand(listCmd);
|
|
2418
|
+
const describeCmd = new Command4("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").option("--format <format>", "Output format.", void 0).action((moduleId, opts) => {
|
|
2419
|
+
validateModuleId(moduleId);
|
|
2420
|
+
const moduleDef = registry.getModule(moduleId);
|
|
2421
|
+
if (!moduleDef) {
|
|
2324
2422
|
process.stderr.write(
|
|
2325
|
-
`Error:
|
|
2423
|
+
`Error: Module '${moduleId}' not found.
|
|
2326
2424
|
`
|
|
2327
2425
|
);
|
|
2328
|
-
process.exit(EXIT_CODES.
|
|
2426
|
+
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
2329
2427
|
}
|
|
2330
|
-
const
|
|
2331
|
-
|
|
2332
|
-
process.stdout.write(roff);
|
|
2428
|
+
const fmt = resolveFormat(opts.format);
|
|
2429
|
+
formatModuleDetail(moduleDef, fmt);
|
|
2333
2430
|
});
|
|
2334
|
-
cli.addCommand(
|
|
2431
|
+
cli.addCommand(describeCmd);
|
|
2335
2432
|
}
|
|
2336
2433
|
|
|
2337
2434
|
// src/index.ts
|
|
@@ -2379,6 +2476,7 @@ export {
|
|
|
2379
2476
|
main,
|
|
2380
2477
|
mapType,
|
|
2381
2478
|
reconvertEnumValues,
|
|
2479
|
+
registerConfigNamespace,
|
|
2382
2480
|
registerDiscoveryCommands,
|
|
2383
2481
|
registerInitCommand,
|
|
2384
2482
|
registerShellCommands,
|