apcore-cli 0.8.1 → 0.9.1
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 +70 -0
- package/README.md +16 -12
- package/dist/bin/apcore-cli.js +173 -81
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/index.d.ts +56 -6
- package/dist/index.js +174 -81
- package/dist/index.js.map +1 -1
- package/package.json +3 -8
package/dist/bin/apcore-cli.js
CHANGED
|
@@ -35,6 +35,12 @@ function exitCodeForError(error) {
|
|
|
35
35
|
if (error instanceof SchemaValidationError) {
|
|
36
36
|
return EXIT_CODES.SCHEMA_VALIDATION_ERROR;
|
|
37
37
|
}
|
|
38
|
+
if (error instanceof MaxDepthExceededError || error instanceof CircularRefError) {
|
|
39
|
+
return EXIT_CODES.SCHEMA_CIRCULAR_REF;
|
|
40
|
+
}
|
|
41
|
+
if (error instanceof UnresolvableRefError) {
|
|
42
|
+
return EXIT_CODES.SCHEMA_VALIDATION_ERROR;
|
|
43
|
+
}
|
|
38
44
|
if (error instanceof ModuleNotFoundError) {
|
|
39
45
|
return EXIT_CODES.MODULE_NOT_FOUND;
|
|
40
46
|
}
|
|
@@ -74,7 +80,7 @@ function exitCodeForError(error) {
|
|
|
74
80
|
}
|
|
75
81
|
return EXIT_CODES.MODULE_EXECUTE_ERROR;
|
|
76
82
|
}
|
|
77
|
-
var ApprovalTimeoutError, AuthenticationError, ConfigDecryptionError, ModuleExecutionError, ApprovalDeniedError, SchemaValidationError, ModuleNotFoundError, EXIT_CODES;
|
|
83
|
+
var ApprovalTimeoutError, AuthenticationError, ConfigDecryptionError, ModuleExecutionError, ApprovalDeniedError, SchemaValidationError, MaxDepthExceededError, CircularRefError, UnresolvableRefError, ModuleNotFoundError, EXIT_CODES;
|
|
78
84
|
var init_errors = __esm({
|
|
79
85
|
"src/errors.ts"() {
|
|
80
86
|
"use strict";
|
|
@@ -110,11 +116,30 @@ var init_errors = __esm({
|
|
|
110
116
|
}
|
|
111
117
|
};
|
|
112
118
|
SchemaValidationError = class extends Error {
|
|
119
|
+
code = "SCHEMA_VALIDATION_ERROR";
|
|
113
120
|
constructor(message = "Schema validation failed") {
|
|
114
121
|
super(message);
|
|
115
122
|
this.name = "SchemaValidationError";
|
|
116
123
|
}
|
|
117
124
|
};
|
|
125
|
+
MaxDepthExceededError = class extends Error {
|
|
126
|
+
constructor(message = "Schema $ref resolution depth exceeded") {
|
|
127
|
+
super(message);
|
|
128
|
+
this.name = "MaxDepthExceededError";
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
CircularRefError = class extends Error {
|
|
132
|
+
constructor(message = "Circular $ref detected in schema") {
|
|
133
|
+
super(message);
|
|
134
|
+
this.name = "CircularRefError";
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
UnresolvableRefError = class extends Error {
|
|
138
|
+
constructor(message = "Unresolvable $ref in schema") {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = "UnresolvableRefError";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
118
143
|
ModuleNotFoundError = class extends Error {
|
|
119
144
|
constructor(message = "Module not found") {
|
|
120
145
|
super(message);
|
|
@@ -155,6 +180,7 @@ var init_errors = __esm({
|
|
|
155
180
|
var sandbox_exports = {};
|
|
156
181
|
__export(sandbox_exports, {
|
|
157
182
|
Sandbox: () => Sandbox,
|
|
183
|
+
_buildSandboxEnvForTesting: () => _buildSandboxEnvForTesting,
|
|
158
184
|
runSandboxRunner: () => runSandboxRunner
|
|
159
185
|
});
|
|
160
186
|
import { spawn } from "child_process";
|
|
@@ -195,10 +221,13 @@ async function runSandboxRunner(moduleId) {
|
|
|
195
221
|
process.exit(1);
|
|
196
222
|
}
|
|
197
223
|
}
|
|
224
|
+
function _buildSandboxEnvForTesting(tmpDir) {
|
|
225
|
+
return buildSandboxEnv(tmpDir);
|
|
226
|
+
}
|
|
198
227
|
function buildSandboxEnv(tmpDir) {
|
|
199
228
|
const env = {};
|
|
200
229
|
for (const key of SANDBOX_ALLOW_KEYS) {
|
|
201
|
-
if (process.env[key]) env[key] = process.env[key];
|
|
230
|
+
if (process.env[key] !== void 0) env[key] = process.env[key];
|
|
202
231
|
}
|
|
203
232
|
for (const [key, val] of Object.entries(process.env)) {
|
|
204
233
|
if (key.startsWith(SANDBOX_ALLOW_PREFIX) && !key.startsWith(SANDBOX_DENY_PREFIX) && !SANDBOX_DENY_KEYS.includes(key)) {
|
|
@@ -303,11 +332,20 @@ var init_sandbox = __esm({
|
|
|
303
332
|
}
|
|
304
333
|
stderr += chunk.toString();
|
|
305
334
|
});
|
|
335
|
+
child.stdin.on("error", () => {
|
|
336
|
+
});
|
|
306
337
|
child.stdin.write(JSON.stringify(inputData));
|
|
307
338
|
child.stdin.end();
|
|
308
339
|
return new Promise((resolve2, reject) => {
|
|
340
|
+
const cleanup = () => {
|
|
341
|
+
try {
|
|
342
|
+
rmSync(tmpDir, { recursive: true, force: true });
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
};
|
|
309
346
|
const timer = setTimeout(() => {
|
|
310
347
|
child.kill("SIGKILL");
|
|
348
|
+
cleanup();
|
|
311
349
|
reject(
|
|
312
350
|
new ModuleExecutionError(
|
|
313
351
|
`Sandbox module '${moduleId}' timed out after ${this.timeoutSeconds}s.`
|
|
@@ -316,10 +354,7 @@ var init_sandbox = __esm({
|
|
|
316
354
|
}, this.timeoutSeconds * 1e3);
|
|
317
355
|
child.on("close", (code) => {
|
|
318
356
|
clearTimeout(timer);
|
|
319
|
-
|
|
320
|
-
rmSync(tmpDir, { recursive: true, force: true });
|
|
321
|
-
} catch {
|
|
322
|
-
}
|
|
357
|
+
cleanup();
|
|
323
358
|
if (sizeExceeded) {
|
|
324
359
|
const limitMiB = Math.floor(outputCap / (1024 * 1024));
|
|
325
360
|
reject(new ModuleExecutionError(
|
|
@@ -343,6 +378,7 @@ var init_sandbox = __esm({
|
|
|
343
378
|
});
|
|
344
379
|
child.on("error", (err) => {
|
|
345
380
|
clearTimeout(timer);
|
|
381
|
+
cleanup();
|
|
346
382
|
reject(new ModuleExecutionError(`Failed to spawn sandbox process: ${err.message}`));
|
|
347
383
|
});
|
|
348
384
|
});
|
|
@@ -375,27 +411,21 @@ function resolveNode(node, defs, visited, depth, maxDepth, moduleId) {
|
|
|
375
411
|
if ("$ref" in obj) {
|
|
376
412
|
const refPath = obj.$ref;
|
|
377
413
|
if (depth >= maxDepth) {
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
`
|
|
414
|
+
throw new MaxDepthExceededError(
|
|
415
|
+
`$ref resolution depth exceeded maximum of ${maxDepth} for module '${moduleId}'.`
|
|
381
416
|
);
|
|
382
|
-
process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
|
|
383
417
|
}
|
|
384
418
|
if (visited.has(refPath)) {
|
|
385
|
-
|
|
386
|
-
`
|
|
387
|
-
`
|
|
419
|
+
throw new CircularRefError(
|
|
420
|
+
`Circular $ref detected in schema for module '${moduleId}' at path '${refPath}'.`
|
|
388
421
|
);
|
|
389
|
-
process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
|
|
390
422
|
}
|
|
391
423
|
const parts = refPath.split("/");
|
|
392
424
|
const key = parts[parts.length - 1];
|
|
393
425
|
if (!(key in defs)) {
|
|
394
|
-
|
|
395
|
-
`
|
|
396
|
-
`
|
|
426
|
+
throw new UnresolvableRefError(
|
|
427
|
+
`Unresolvable $ref '${refPath}' in schema for module '${moduleId}'.`
|
|
397
428
|
);
|
|
398
|
-
process.exit(EXIT_CODES.SCHEMA_VALIDATION_ERROR);
|
|
399
429
|
}
|
|
400
430
|
const newVisited = new Set(visited);
|
|
401
431
|
newVisited.add(refPath);
|
|
@@ -496,17 +526,12 @@ function resolveNode(node, defs, visited, depth, maxDepth, moduleId) {
|
|
|
496
526
|
return merged;
|
|
497
527
|
}
|
|
498
528
|
}
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
visited,
|
|
506
|
-
depth,
|
|
507
|
-
maxDepth,
|
|
508
|
-
moduleId
|
|
509
|
-
);
|
|
529
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
530
|
+
if (k === "allOf" || k === "anyOf" || k === "oneOf" || k === "$ref") {
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
if (typeof v === "object" && v !== null && !Array.isArray(v)) {
|
|
534
|
+
obj[k] = resolveNode(v, defs, visited, depth, maxDepth, moduleId);
|
|
510
535
|
}
|
|
511
536
|
}
|
|
512
537
|
return obj;
|
|
@@ -703,7 +728,7 @@ var init_schema_parser = __esm({
|
|
|
703
728
|
"format",
|
|
704
729
|
"fields",
|
|
705
730
|
"sandbox",
|
|
706
|
-
"
|
|
731
|
+
"all_options",
|
|
707
732
|
"dry_run",
|
|
708
733
|
"trace",
|
|
709
734
|
"stream",
|
|
@@ -816,6 +841,14 @@ var init_approval = __esm({
|
|
|
816
841
|
}
|
|
817
842
|
async requestApproval(request) {
|
|
818
843
|
const moduleId = request.module_id ?? "unknown";
|
|
844
|
+
if (request.requires_approval === false) {
|
|
845
|
+
return { status: "approved", approved_by: "not_required" };
|
|
846
|
+
}
|
|
847
|
+
const moduleDef = request.module_def;
|
|
848
|
+
const annotationsForCheck = moduleDef?.annotations;
|
|
849
|
+
if (annotationsForCheck && annotationsForCheck.requires_approval === false) {
|
|
850
|
+
return { status: "approved", approved_by: "not_required" };
|
|
851
|
+
}
|
|
819
852
|
if (this.autoApprove) {
|
|
820
853
|
return { status: "approved", approved_by: "auto_approve" };
|
|
821
854
|
}
|
|
@@ -852,6 +885,7 @@ var init_approval = __esm({
|
|
|
852
885
|
|
|
853
886
|
// src/output.ts
|
|
854
887
|
import yaml from "js-yaml";
|
|
888
|
+
import { formatCsv, formatJsonl } from "apcore-toolkit";
|
|
855
889
|
function descriptorToScanned(m) {
|
|
856
890
|
const metadata = m.metadata ?? {};
|
|
857
891
|
const display = metadata["display"] ?? null;
|
|
@@ -872,11 +906,6 @@ function descriptorToScanned(m) {
|
|
|
872
906
|
warnings: []
|
|
873
907
|
};
|
|
874
908
|
}
|
|
875
|
-
function csvCellString(value) {
|
|
876
|
-
if (value === null || value === void 0) return "";
|
|
877
|
-
if (typeof value === "object") return JSON.stringify(value);
|
|
878
|
-
return String(value);
|
|
879
|
-
}
|
|
880
909
|
function resolveFormat(explicitFormat) {
|
|
881
910
|
if (explicitFormat !== void 0) {
|
|
882
911
|
return explicitFormat;
|
|
@@ -1079,30 +1108,18 @@ function formatExecResult(result, format, fields) {
|
|
|
1079
1108
|
}
|
|
1080
1109
|
const effective = resolveFormat(format);
|
|
1081
1110
|
if (effective === "csv") {
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
const header = keys.map(escapeCsvField).join(",");
|
|
1086
|
-
const row = keys.map((k) => escapeCsvField(csvCellString(obj[k]))).join(",");
|
|
1087
|
-
process.stdout.write(header + "\n" + row + "\n");
|
|
1088
|
-
} else if (Array.isArray(effective_result) && effective_result.length > 0 && typeof effective_result[0] === "object") {
|
|
1089
|
-
const keys = Object.keys(effective_result[0]);
|
|
1090
|
-
const header = keys.map(escapeCsvField).join(",");
|
|
1091
|
-
const rows = effective_result.map((item) => {
|
|
1092
|
-
const obj = item;
|
|
1093
|
-
return keys.map((k) => escapeCsvField(csvCellString(obj[k]))).join(",");
|
|
1094
|
-
});
|
|
1095
|
-
process.stdout.write(header + "\n" + rows.join("\n") + "\n");
|
|
1111
|
+
const rows = toRowsForTabular(effective_result);
|
|
1112
|
+
if (rows !== null) {
|
|
1113
|
+
process.stdout.write(formatCsv(rows));
|
|
1096
1114
|
} else {
|
|
1097
1115
|
process.stdout.write(JSON.stringify(effective_result) + "\n");
|
|
1098
1116
|
}
|
|
1099
1117
|
} else if (effective === "yaml") {
|
|
1100
1118
|
process.stdout.write(yaml.dump(effective_result, { lineWidth: -1 }));
|
|
1101
1119
|
} else if (effective === "jsonl") {
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
}
|
|
1120
|
+
const rows = toRowsForTabular(effective_result);
|
|
1121
|
+
if (rows !== null) {
|
|
1122
|
+
process.stdout.write(formatJsonl(rows));
|
|
1106
1123
|
} else {
|
|
1107
1124
|
process.stdout.write(JSON.stringify(effective_result) + "\n");
|
|
1108
1125
|
}
|
|
@@ -1119,11 +1136,19 @@ function formatExecResult(result, format, fields) {
|
|
|
1119
1136
|
process.stdout.write(String(effective_result) + "\n");
|
|
1120
1137
|
}
|
|
1121
1138
|
}
|
|
1122
|
-
function
|
|
1123
|
-
if (value
|
|
1124
|
-
|
|
1139
|
+
function toRowsForTabular(value) {
|
|
1140
|
+
if (value === null || value === void 0) return null;
|
|
1141
|
+
if (Array.isArray(value)) {
|
|
1142
|
+
if (value.length === 0) return null;
|
|
1143
|
+
if (!value.every((item) => typeof item === "object" && item !== null && !Array.isArray(item))) {
|
|
1144
|
+
return null;
|
|
1145
|
+
}
|
|
1146
|
+
return value;
|
|
1147
|
+
}
|
|
1148
|
+
if (typeof value === "object") {
|
|
1149
|
+
return [value];
|
|
1125
1150
|
}
|
|
1126
|
-
return
|
|
1151
|
+
return null;
|
|
1127
1152
|
}
|
|
1128
1153
|
function formatPreflightResult(result, format) {
|
|
1129
1154
|
const resolved = resolveFormat(format);
|
|
@@ -1204,7 +1229,7 @@ var init_output = __esm({
|
|
|
1204
1229
|
"use strict";
|
|
1205
1230
|
init_esm_shims();
|
|
1206
1231
|
init_errors();
|
|
1207
|
-
TOOLKIT_MISSING_HINT = "The 'markdown' and 'skill' output formats require the apcore-toolkit peer dependency. Install with: npm install apcore-toolkit@^0.
|
|
1232
|
+
TOOLKIT_MISSING_HINT = "The 'markdown' and 'skill' output formats require the apcore-toolkit peer dependency. Install with: npm install apcore-toolkit@^0.7";
|
|
1208
1233
|
}
|
|
1209
1234
|
});
|
|
1210
1235
|
|
|
@@ -1235,7 +1260,7 @@ function renderTemplate(template, context) {
|
|
|
1235
1260
|
return result;
|
|
1236
1261
|
}
|
|
1237
1262
|
function registerInitCommand(cli) {
|
|
1238
|
-
const initGroup = cli.command("init").description("Scaffold new
|
|
1263
|
+
const initGroup = cli.command("init").description("Scaffold new modules.");
|
|
1239
1264
|
initGroup.command("module <module-id>").description("Create a new module from a template.\n\nMODULE_ID is the module identifier (e.g., ops.deploy, user.create).").option(
|
|
1240
1265
|
"--style <style>",
|
|
1241
1266
|
"Module style: decorator (@module), convention (plain function), or binding (YAML).",
|
|
@@ -1811,7 +1836,7 @@ function buildProgramManPage(program, progName, version, description, docsUrl2)
|
|
|
1811
1836
|
s.push(".SH ENVIRONMENT");
|
|
1812
1837
|
s.push(".TP");
|
|
1813
1838
|
s.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
|
|
1814
|
-
s.push("Path to the
|
|
1839
|
+
s.push("Path to the extensions directory.");
|
|
1815
1840
|
s.push(".TP");
|
|
1816
1841
|
s.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
|
|
1817
1842
|
s.push("Set to \\fB1\\fR to bypass approval prompts.");
|
|
@@ -1836,7 +1861,7 @@ function buildProgramManPage(program, progName, version, description, docsUrl2)
|
|
|
1836
1861
|
${meaning}`);
|
|
1837
1862
|
}
|
|
1838
1863
|
s.push(".SH SEE ALSO");
|
|
1839
|
-
s.push(`\\fB${progName} \\-\\-help \\-\\-
|
|
1864
|
+
s.push(`\\fB${progName} \\-\\-help \\-\\-all\\-options\\fR for full option list.`);
|
|
1840
1865
|
if (docsUrl2) {
|
|
1841
1866
|
s.push(`.PP
|
|
1842
1867
|
Full documentation at \\fI${roffEscape(docsUrl2)}\\fR`);
|
|
@@ -2243,7 +2268,7 @@ var init_config_encryptor = __esm({
|
|
|
2243
2268
|
_ConfigEncryptor.weakFallbackWarned = true;
|
|
2244
2269
|
}
|
|
2245
2270
|
const hostname2 = os3.hostname();
|
|
2246
|
-
const username = process.env.USER ?? process.env.USERNAME ?? "unknown";
|
|
2271
|
+
const username = process.env.USER ?? process.env.LOGNAME ?? process.env.USERNAME ?? "unknown";
|
|
2247
2272
|
const material = `${hostname2}:${username}`;
|
|
2248
2273
|
return crypto2.pbkdf2Sync(material, salt, PBKDF2_ITERATIONS, 32, "sha256");
|
|
2249
2274
|
}
|
|
@@ -2275,7 +2300,7 @@ var init_config_encryptor = __esm({
|
|
|
2275
2300
|
const tag = data.subarray(12, 28);
|
|
2276
2301
|
const ct = data.subarray(28);
|
|
2277
2302
|
const hostname2 = os3.hostname();
|
|
2278
|
-
const username = process.env.USER ?? process.env.USERNAME ?? "unknown";
|
|
2303
|
+
const username = process.env.USER ?? process.env.LOGNAME ?? process.env.USERNAME ?? "unknown";
|
|
2279
2304
|
const passphrase = process.env.APCORE_CLI_CONFIG_PASSPHRASE;
|
|
2280
2305
|
const materials = passphrase ? [passphrase, `${hostname2}:${username}`] : [`${hostname2}:${username}`];
|
|
2281
2306
|
for (const material of materials) {
|
|
@@ -2305,10 +2330,26 @@ var init_auth = __esm({
|
|
|
2305
2330
|
init_config_encryptor();
|
|
2306
2331
|
AuthProvider = class {
|
|
2307
2332
|
config;
|
|
2308
|
-
|
|
2333
|
+
_encryptor;
|
|
2309
2334
|
constructor(config, encryptor) {
|
|
2310
2335
|
this.config = config;
|
|
2311
|
-
this.
|
|
2336
|
+
this._encryptor = encryptor;
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* Resolve the active ConfigEncryptor instance.
|
|
2340
|
+
*
|
|
2341
|
+
* D11-005 (2026-05-12): three-tier fallback chain matching Python's
|
|
2342
|
+
* `_get_encryptor` (auth.py:33): explicit constructor arg > peer attribute
|
|
2343
|
+
* `config.encryptor` (set by embedders injecting forced-AES test fixtures
|
|
2344
|
+
* or shared instances) > fresh `new ConfigEncryptor()`. Previously TS
|
|
2345
|
+
* skipped the peer-attribute tier, silently giving embedders a different
|
|
2346
|
+
* encryptor than the one they wired on the config.
|
|
2347
|
+
*/
|
|
2348
|
+
getEncryptor() {
|
|
2349
|
+
if (this._encryptor) return this._encryptor;
|
|
2350
|
+
const fromConfig = this.config.encryptor;
|
|
2351
|
+
if (fromConfig) return fromConfig;
|
|
2352
|
+
return new ConfigEncryptor();
|
|
2312
2353
|
}
|
|
2313
2354
|
/**
|
|
2314
2355
|
* Retrieve the API key from the configured sources.
|
|
@@ -2326,11 +2367,11 @@ var init_auth = __esm({
|
|
|
2326
2367
|
const strResult = String(result);
|
|
2327
2368
|
if (strResult.startsWith("keyring:") || strResult.startsWith("enc:")) {
|
|
2328
2369
|
try {
|
|
2329
|
-
return await this.
|
|
2370
|
+
return await this.getEncryptor().retrieve(strResult, "auth.api_key");
|
|
2330
2371
|
} catch (err) {
|
|
2331
2372
|
if (err instanceof ConfigDecryptionError) {
|
|
2332
2373
|
throw new AuthenticationError(
|
|
2333
|
-
"Failed to decrypt stored API key. Re-
|
|
2374
|
+
"Failed to decrypt stored API key. Re-store with 'apcli config set auth.api_key'."
|
|
2334
2375
|
);
|
|
2335
2376
|
}
|
|
2336
2377
|
throw err;
|
|
@@ -2359,7 +2400,7 @@ var init_auth = __esm({
|
|
|
2359
2400
|
}
|
|
2360
2401
|
if (/[\r\n]/.test(key)) {
|
|
2361
2402
|
throw new AuthenticationError(
|
|
2362
|
-
"Malformed API key: contains invalid characters (CR/LF). Re-
|
|
2403
|
+
"Malformed API key: contains invalid characters (CR/LF). Re-store with 'apcli config set auth.api_key'."
|
|
2363
2404
|
);
|
|
2364
2405
|
}
|
|
2365
2406
|
headers.Authorization = `Bearer ${key.trim()}`;
|
|
@@ -2581,7 +2622,7 @@ function registerExecCommand(apcliGroup, registry, executor) {
|
|
|
2581
2622
|
return;
|
|
2582
2623
|
}
|
|
2583
2624
|
let result;
|
|
2584
|
-
if (opts.strategy && executor.callWithTrace) {
|
|
2625
|
+
if ((opts.trace || opts.strategy) && executor.callWithTrace) {
|
|
2585
2626
|
const [res] = await executor.callWithTrace(moduleId, merged, { strategy: opts.strategy });
|
|
2586
2627
|
result = res;
|
|
2587
2628
|
} else {
|
|
@@ -3697,6 +3738,7 @@ __export(main_exports, {
|
|
|
3697
3738
|
reconvertEnumValues: () => reconvertEnumValues,
|
|
3698
3739
|
resolveIntOption: () => resolveIntOption,
|
|
3699
3740
|
resolveStringOption: () => resolveStringOption,
|
|
3741
|
+
setAllOptionsHelp: () => setAllOptionsHelp,
|
|
3700
3742
|
setDocsUrl: () => setDocsUrl,
|
|
3701
3743
|
setVerboseHelp: () => setVerboseHelp,
|
|
3702
3744
|
validateModuleId: () => validateModuleId
|
|
@@ -3705,14 +3747,17 @@ import { readFileSync as readFileSync3 } from "fs";
|
|
|
3705
3747
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3706
3748
|
import * as path5 from "path";
|
|
3707
3749
|
import { Command as Command5, CommanderError, Option as Option4 } from "commander";
|
|
3750
|
+
function setAllOptionsHelp(allOptions) {
|
|
3751
|
+
verboseHelp = allOptions;
|
|
3752
|
+
}
|
|
3708
3753
|
function setVerboseHelp(verbose) {
|
|
3709
|
-
|
|
3754
|
+
setAllOptionsHelp(verbose);
|
|
3710
3755
|
}
|
|
3711
3756
|
function setDocsUrl(url) {
|
|
3712
3757
|
docsUrl = url;
|
|
3713
3758
|
}
|
|
3714
3759
|
function hasVerboseFlag() {
|
|
3715
|
-
return process.argv.includes("--
|
|
3760
|
+
return process.argv.includes("--all-options");
|
|
3716
3761
|
}
|
|
3717
3762
|
function resolveIntOption(cliValue, envValue, defaultValue) {
|
|
3718
3763
|
if (cliValue !== void 0 && Number.isFinite(cliValue) && cliValue > 0) {
|
|
@@ -3739,6 +3784,37 @@ function resolveStringOption(cliValue, envValue) {
|
|
|
3739
3784
|
}
|
|
3740
3785
|
return void 0;
|
|
3741
3786
|
}
|
|
3787
|
+
function validateInputSchema(schema, input) {
|
|
3788
|
+
const required = schema.required;
|
|
3789
|
+
if (required && Array.isArray(required)) {
|
|
3790
|
+
for (const field of required) {
|
|
3791
|
+
const val = input[field];
|
|
3792
|
+
if (val === null || val === void 0) {
|
|
3793
|
+
return `'${field}' is required`;
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
const properties = schema.properties;
|
|
3798
|
+
if (properties) {
|
|
3799
|
+
for (const [field, propSchema] of Object.entries(properties)) {
|
|
3800
|
+
const val = input[field];
|
|
3801
|
+
if (val === null || val === void 0) continue;
|
|
3802
|
+
const expectedType = propSchema.type;
|
|
3803
|
+
if (!expectedType) continue;
|
|
3804
|
+
const actualType = typeof val;
|
|
3805
|
+
if (expectedType === "string" && actualType !== "string") {
|
|
3806
|
+
return `'${field}' must be a string, got ${actualType}`;
|
|
3807
|
+
}
|
|
3808
|
+
if ((expectedType === "integer" || expectedType === "number") && actualType !== "number") {
|
|
3809
|
+
return `'${field}' must be a number, got ${actualType}`;
|
|
3810
|
+
}
|
|
3811
|
+
if (expectedType === "boolean" && actualType !== "boolean") {
|
|
3812
|
+
return `'${field}' must be a boolean, got ${actualType}`;
|
|
3813
|
+
}
|
|
3814
|
+
}
|
|
3815
|
+
}
|
|
3816
|
+
return null;
|
|
3817
|
+
}
|
|
3742
3818
|
function emitErrorJson(e, exitCode) {
|
|
3743
3819
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
3744
3820
|
const errRecord = err;
|
|
@@ -3787,7 +3863,7 @@ function emitErrorTty(e, exitCode) {
|
|
|
3787
3863
|
Exit code: ${exitCode}
|
|
3788
3864
|
`);
|
|
3789
3865
|
}
|
|
3790
|
-
function createCli(extensionsDirOrOpts, progName,
|
|
3866
|
+
function createCli(extensionsDirOrOpts, progName, allOptions = false) {
|
|
3791
3867
|
let extensionsDir;
|
|
3792
3868
|
let registry;
|
|
3793
3869
|
let executor;
|
|
@@ -3802,7 +3878,7 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
|
3802
3878
|
if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
|
|
3803
3879
|
extensionsDir = extensionsDirOrOpts.extensionsDir;
|
|
3804
3880
|
progName = extensionsDirOrOpts.progName ?? progName;
|
|
3805
|
-
|
|
3881
|
+
allOptions = extensionsDirOrOpts.allOptions ?? extensionsDirOrOpts.verbose ?? allOptions;
|
|
3806
3882
|
app = extensionsDirOrOpts.app;
|
|
3807
3883
|
registry = extensionsDirOrOpts.registry;
|
|
3808
3884
|
executor = extensionsDirOrOpts.executor;
|
|
@@ -3816,7 +3892,7 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
|
3816
3892
|
} else {
|
|
3817
3893
|
extensionsDir = extensionsDirOrOpts;
|
|
3818
3894
|
}
|
|
3819
|
-
verboseHelp =
|
|
3895
|
+
verboseHelp = allOptions;
|
|
3820
3896
|
registerConfigNamespace();
|
|
3821
3897
|
try {
|
|
3822
3898
|
const auditLogger = new AuditLogger();
|
|
@@ -3849,7 +3925,7 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
|
3849
3925
|
}
|
|
3850
3926
|
}
|
|
3851
3927
|
const registryInjected = registry !== void 0;
|
|
3852
|
-
const program = new Command5(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("--
|
|
3928
|
+
const program = new Command5(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)");
|
|
3853
3929
|
if (appVersion) {
|
|
3854
3930
|
program.version(appVersion, "-V, --version", "Print version");
|
|
3855
3931
|
}
|
|
@@ -3920,7 +3996,7 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
|
3920
3996
|
_registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter);
|
|
3921
3997
|
program.addHelpText("after", [
|
|
3922
3998
|
"",
|
|
3923
|
-
"Use --help --
|
|
3999
|
+
"Use --help --all-options to show all options (including built-in options).",
|
|
3924
4000
|
"Use --help --man to display a formatted man page."
|
|
3925
4001
|
].join("\n"));
|
|
3926
4002
|
configureManHelp(program, resolvedProgName, appVersion ?? VERSION);
|
|
@@ -3957,7 +4033,7 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
|
3957
4033
|
function _registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter) {
|
|
3958
4034
|
const emitUnwiredError = () => {
|
|
3959
4035
|
process.stderr.write(
|
|
3960
|
-
"Error: no
|
|
4036
|
+
"Error: no module registry wired. In standalone mode, pass --extensions-dir <path> to enable module discovery.\n"
|
|
3961
4037
|
);
|
|
3962
4038
|
process.exit(EXIT_CODES.CONFIG_INVALID);
|
|
3963
4039
|
};
|
|
@@ -4071,9 +4147,9 @@ function main(progName) {
|
|
|
4071
4147
|
verboseHelp = hasVerboseFlag();
|
|
4072
4148
|
const program = createCli({
|
|
4073
4149
|
progName,
|
|
4074
|
-
|
|
4150
|
+
allOptions: verboseHelp,
|
|
4075
4151
|
version: VERSION,
|
|
4076
|
-
description: `${progName ?? "apcore-cli"} \u2014 execute
|
|
4152
|
+
description: `${progName ?? "apcore-cli"} \u2014 execute modules from the command line`
|
|
4077
4153
|
});
|
|
4078
4154
|
try {
|
|
4079
4155
|
program.parse(process.argv);
|
|
@@ -4101,7 +4177,17 @@ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdNam
|
|
|
4101
4177
|
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
4102
4178
|
try {
|
|
4103
4179
|
resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
|
|
4104
|
-
} catch {
|
|
4180
|
+
} catch (err) {
|
|
4181
|
+
if (err instanceof MaxDepthExceededError || err instanceof CircularRefError) {
|
|
4182
|
+
process.stderr.write(`Error: ${err.message}
|
|
4183
|
+
`);
|
|
4184
|
+
process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
|
|
4185
|
+
}
|
|
4186
|
+
if (err instanceof UnresolvableRefError) {
|
|
4187
|
+
process.stderr.write(`Error: ${err.message}
|
|
4188
|
+
`);
|
|
4189
|
+
process.exit(EXIT_CODES.SCHEMA_VALIDATION_ERROR);
|
|
4190
|
+
}
|
|
4105
4191
|
resolvedSchema = inputSchema;
|
|
4106
4192
|
}
|
|
4107
4193
|
schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
|
|
@@ -4146,7 +4232,7 @@ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdNam
|
|
|
4146
4232
|
cmd.addOption(approvalTokenOpt);
|
|
4147
4233
|
const footerParts = [];
|
|
4148
4234
|
if (!verbose) {
|
|
4149
|
-
footerParts.push("Use --
|
|
4235
|
+
footerParts.push("Use --all-options to show all options (including built-in options).");
|
|
4150
4236
|
}
|
|
4151
4237
|
if (docsUrl) {
|
|
4152
4238
|
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
|
|
@@ -4242,6 +4328,12 @@ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdNam
|
|
|
4242
4328
|
}
|
|
4243
4329
|
process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
|
|
4244
4330
|
}
|
|
4331
|
+
if (resolvedSchema.properties) {
|
|
4332
|
+
const validationErr = validateInputSchema(resolvedSchema, merged);
|
|
4333
|
+
if (validationErr) {
|
|
4334
|
+
throw new SchemaValidationError(`Validation failed: ${validationErr}`);
|
|
4335
|
+
}
|
|
4336
|
+
}
|
|
4245
4337
|
if (approvalToken) {
|
|
4246
4338
|
merged._approval_token = approvalToken;
|
|
4247
4339
|
}
|