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.
@@ -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
- try {
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
- process.stderr.write(
379
- `Error: $ref resolution depth exceeded maximum of ${maxDepth} for module '${moduleId}'.
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
- process.stderr.write(
386
- `Error: Circular $ref detected in schema for module '${moduleId}' at path '${refPath}'.
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
- process.stderr.write(
395
- `Error: Unresolvable $ref '${refPath}' in schema for module '${moduleId}'.
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
- if ("properties" in obj && typeof obj.properties === "object" && obj.properties !== null) {
500
- const props = obj.properties;
501
- for (const [propName, propSchema] of Object.entries(props)) {
502
- props[propName] = resolveNode(
503
- propSchema,
504
- defs,
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
- "verbose",
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
- if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
1083
- const obj = effective_result;
1084
- const keys = Object.keys(obj);
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
- if (Array.isArray(effective_result)) {
1103
- for (const item of effective_result) {
1104
- process.stdout.write(JSON.stringify(item) + "\n");
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 escapeCsvField(value) {
1123
- if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
1124
- return '"' + value.replace(/"/g, '""') + '"';
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 value;
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.6";
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 apcore modules.");
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 apcore extensions directory.");
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 \\-\\-verbose\\fR for full option list.`);
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
- encryptor;
2333
+ _encryptor;
2309
2334
  constructor(config, encryptor) {
2310
2335
  this.config = config;
2311
- this.encryptor = encryptor ?? new ConfigEncryptor();
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.encryptor.retrieve(strResult, "auth.api_key");
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-configure with 'apcore-cli config set auth.api_key'."
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-configure with 'apcore-cli config set auth.api_key'."
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
- verboseHelp = verbose;
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("--verbose");
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, verbose = false) {
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
- verbose = extensionsDirOrOpts.verbose ?? verbose;
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 = verbose;
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("--verbose", "Show all options in help output (including built-in options)");
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 --verbose to show all options (including built-in options).",
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 apcore-js registry wired. In standalone mode, pass --extensions-dir <path> to enable module discovery.\n"
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
- verbose: verboseHelp,
4150
+ allOptions: verboseHelp,
4075
4151
  version: VERSION,
4076
- description: `${progName ?? "apcore-cli"} \u2014 execute apcore modules from the command line`
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 --verbose to show all options (including built-in apcore options).");
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
  }