apcore-cli 0.3.2 → 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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ All notable changes to apcore-cli (TypeScript SDK) will be documented in this fi
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.4.0] - 2026-03-29
9
+
10
+ ### Added
11
+ - **Verbose help mode** — Built-in apcore options (`--input`, `--yes`, `--large-input`, `--format`, `--sandbox`) are now hidden from `--help` output by default. Pass `--help --verbose` to display the full option list including built-in options.
12
+ - **Universal man page generation** — `buildProgramManPage()` generates a complete roff man page covering all registered commands. `configureManHelp()` adds `--help --man` support to any Commander program, enabling downstream projects to get man pages for free.
13
+ - **Documentation URL support** — `setDocsUrl()` sets a base URL for online docs. Per-command help shows `Docs: {url}/commands/{name}`, man page SEE ALSO includes `Full documentation at {url}`. No default — disabled when not set.
14
+
15
+ ### Changed
16
+ - `buildModuleCommand()` accepts optional `verboseHelp` parameter to control built-in option visibility in help.
17
+ - `--sandbox` is now always hidden from help (not yet implemented). Only four built-in options (`--input`, `--yes`, `--large-input`, `--format`) toggle with `--verbose`.
18
+ - Improved built-in option descriptions for clarity (e.g., `--input` now reads "Read JSON input from a file path, or use '-' to read from stdin pipe").
19
+
8
20
  ## [0.3.2] - 2026-03-28
9
21
 
10
22
  ### Fixed
package/README.md CHANGED
@@ -148,6 +148,8 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
148
148
  | `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
149
149
  | `--version` | | Show version and exit |
150
150
  | `--help` | | Show help and exit |
151
+ | `--verbose` | | Show all options in help (including built-in apcore options) |
152
+ | `--man` | | Output man page in roff format (use with `--help`) |
151
153
 
152
154
  ### Built-in Commands
153
155
 
@@ -161,7 +163,7 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
161
163
 
162
164
  ### Module Execution Options
163
165
 
164
- When executing a module (e.g. `apcore-cli math.add`), these built-in options are always available:
166
+ When executing a module (e.g. `apcore-cli math.add`), these built-in options are available (hidden by default; use `--verbose` to show in `--help`):
165
167
 
166
168
  | Option | Description |
167
169
  |--------|-------------|
@@ -169,7 +171,7 @@ When executing a module (e.g. `apcore-cli math.add`), these built-in options are
169
171
  | `--yes` / `-y` | Bypass approval prompts |
170
172
  | `--large-input` | Allow STDIN input larger than 10MB |
171
173
  | `--format` | Output format: `json` or `table` |
172
- | `--sandbox` | Run module in subprocess sandbox |
174
+ | `--sandbox` | Run module in subprocess sandbox (not yet implemented — always hidden) |
173
175
 
174
176
  Schema-generated flags (e.g. `--a`, `--b`) are added automatically from the module's `input_schema`.
175
177
 
@@ -234,7 +236,8 @@ cli:
234
236
  - **Schema validation** -- inputs validated against JSON Schema before execution, with `$ref`/`allOf`/`anyOf`/`oneOf` resolution
235
237
  - **Security** -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
236
238
  - **Shell completions** -- `apcore-cli completion bash|zsh|fish` generates completion scripts with dynamic module ID completion
237
- - **Man pages** -- `apcore-cli man <command>` generates roff-formatted man pages
239
+ - **Man pages** -- `apcore-cli man <command>` for single commands, or `--help --man` for a complete program man page. `configureManHelp()` provides one-line integration for downstream projects
240
+ - **Documentation URL** -- `setDocsUrl()` adds doc links to help footers and man pages
238
241
  - **Audit logging** -- all executions logged to `~/.apcore-cli/audit.jsonl` with SHA-256 input hashing
239
242
 
240
243
  ## How It Works
@@ -274,7 +277,7 @@ apcore Registry + Executor (your modules, unchanged)
274
277
 
275
278
  **Classes:** `LazyModuleGroup`, `ConfigResolver`, `AuthProvider`, `ConfigEncryptor`, `AuditLogger`, `Sandbox`
276
279
 
277
- **Functions:** `createCli`, `main`, `buildModuleCommand`, `validateModuleId`, `collectInput`, `schemaToCliOptions`, `reconvertEnumValues`, `resolveRefs`, `checkApproval`, `resolveFormat`, `formatModuleList`, `formatModuleDetail`, `formatExecResult`, `registerDiscoveryCommands`, `registerShellCommands`, `setAuditLogger`, `getAuditLogger`, `exitCodeForError`, `mapType`, `extractHelp`, `truncate`
280
+ **Functions:** `createCli`, `main`, `buildModuleCommand`, `validateModuleId`, `collectInput`, `schemaToCliOptions`, `reconvertEnumValues`, `resolveRefs`, `checkApproval`, `resolveFormat`, `formatModuleList`, `formatModuleDetail`, `formatExecResult`, `registerDiscoveryCommands`, `registerShellCommands`, `setAuditLogger`, `getAuditLogger`, `setVerboseHelp`, `setDocsUrl`, `buildProgramManPage`, `configureManHelp`, `exitCodeForError`, `mapType`, `extractHelp`, `truncate`
278
281
 
279
282
  **Errors:** `ApprovalTimeoutError`, `ApprovalDeniedError`, `AuthenticationError`, `ConfigDecryptionError`, `ModuleExecutionError`, `ModuleNotFoundError`, `SchemaValidationError`
280
283
 
@@ -1,5 +1,11 @@
1
1
  #!/usr/bin/env node
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
  };
@@ -46,11 +52,19 @@ function exitCodeForError(error) {
46
52
  SCHEMA_CIRCULAR_REF: EXIT_CODES.SCHEMA_CIRCULAR_REF,
47
53
  APPROVAL_DENIED: EXIT_CODES.APPROVAL_DENIED,
48
54
  APPROVAL_TIMEOUT: EXIT_CODES.APPROVAL_TIMEOUT,
55
+ APPROVAL_PENDING: EXIT_CODES.APPROVAL_DENIED,
49
56
  CONFIG_NOT_FOUND: EXIT_CODES.CONFIG_NOT_FOUND,
50
57
  CONFIG_INVALID: EXIT_CODES.CONFIG_INVALID,
51
58
  MODULE_EXECUTE_ERROR: EXIT_CODES.MODULE_EXECUTE_ERROR,
52
59
  MODULE_TIMEOUT: EXIT_CODES.MODULE_TIMEOUT,
53
- ACL_DENIED: EXIT_CODES.ACL_DENIED
60
+ ACL_DENIED: EXIT_CODES.ACL_DENIED,
61
+ // Config Bus errors (apcore >= 0.15.0)
62
+ CONFIG_NAMESPACE_RESERVED: EXIT_CODES.CONFIG_NAMESPACE_RESERVED,
63
+ CONFIG_NAMESPACE_DUPLICATE: EXIT_CODES.CONFIG_NAMESPACE_DUPLICATE,
64
+ CONFIG_ENV_PREFIX_CONFLICT: EXIT_CODES.CONFIG_ENV_PREFIX_CONFLICT,
65
+ CONFIG_MOUNT_ERROR: EXIT_CODES.CONFIG_MOUNT_ERROR,
66
+ CONFIG_BIND_ERROR: EXIT_CODES.CONFIG_BIND_ERROR,
67
+ ERROR_FORMATTER_DUPLICATE: EXIT_CODES.ERROR_FORMATTER_DUPLICATE
54
68
  };
55
69
  if (code && code in codeMap) {
56
70
  return codeMap[code];
@@ -120,6 +134,13 @@ var init_errors = __esm({
120
134
  CONFIG_INVALID: 47,
121
135
  SCHEMA_CIRCULAR_REF: 48,
122
136
  ACL_DENIED: 77,
137
+ // Config Bus errors (apcore >= 0.15.0)
138
+ CONFIG_NAMESPACE_RESERVED: 78,
139
+ CONFIG_NAMESPACE_DUPLICATE: 78,
140
+ CONFIG_ENV_PREFIX_CONFLICT: 78,
141
+ CONFIG_MOUNT_ERROR: 66,
142
+ CONFIG_BIND_ERROR: 65,
143
+ ERROR_FORMATTER_DUPLICATE: 70,
123
144
  KEYBOARD_INTERRUPT: 130
124
145
  };
125
146
  }
@@ -131,10 +152,10 @@ init_esm_shims();
131
152
  // src/main.ts
132
153
  init_esm_shims();
133
154
  init_errors();
134
- import { readFileSync } from "fs";
135
- import { fileURLToPath as fileURLToPath2 } from "url";
136
- import * as path3 from "path";
137
- import { Command, CommanderError } from "commander";
155
+ import { readFileSync as readFileSync3 } from "fs";
156
+ import { fileURLToPath as fileURLToPath3 } from "url";
157
+ import * as path4 from "path";
158
+ import { Command as Command2, CommanderError, Option as Option2 } from "commander";
138
159
 
139
160
  // src/ref-resolver.ts
140
161
  init_esm_shims();
@@ -309,22 +330,222 @@ function createBindingModule(moduleId, prefix, funcName, description, outputDir)
309
330
  // src/display-helpers.ts
310
331
  init_esm_shims();
311
332
 
312
- // src/main.ts
333
+ // src/config.ts
334
+ init_esm_shims();
335
+ import * as fs2 from "fs";
336
+ import yaml from "js-yaml";
337
+ var NAMESPACE_TO_LEGACY = {
338
+ "apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
339
+ "apcore-cli.auto_approve": "cli.auto_approve",
340
+ "apcore-cli.help_text_max_length": "cli.help_text_max_length",
341
+ "apcore-cli.logging_level": "logging.level"
342
+ };
343
+ var LEGACY_TO_NAMESPACE = Object.fromEntries(
344
+ Object.entries(NAMESPACE_TO_LEGACY).map(([k, v]) => [v, k])
345
+ );
346
+ function registerConfigNamespace() {
347
+ try {
348
+ const { Config } = __require("apcore-js");
349
+ if (typeof Config?.registerNamespace === "function") {
350
+ Config.registerNamespace({
351
+ name: "apcore-cli",
352
+ envPrefix: "APCORE_CLI",
353
+ defaults: {
354
+ stdin_buffer_limit: 10485760,
355
+ auto_approve: false,
356
+ help_text_max_length: 1e3,
357
+ logging_level: "WARNING"
358
+ }
359
+ });
360
+ }
361
+ } catch {
362
+ }
363
+ }
364
+
365
+ // src/shell.ts
366
+ init_esm_shims();
367
+ init_errors();
368
+ import { readFileSync as readFileSync2 } from "fs";
369
+ import { fileURLToPath as fileURLToPath2 } from "url";
370
+ import * as path3 from "path";
371
+ import { spawnSync } from "child_process";
372
+ import { Command, Help, Option } from "commander";
313
373
  var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
374
+ var SHELL_VERSION = "0.0.0";
375
+ try {
376
+ const pkg = JSON.parse(readFileSync2(path3.resolve(__dirname2, "../package.json"), "utf-8"));
377
+ SHELL_VERSION = pkg.version;
378
+ } catch {
379
+ }
380
+ function roffEscape(s) {
381
+ return s.replace(/\\/g, "\\\\").replace(/-/g, "\\-").replace(/'/g, "\\(aq");
382
+ }
383
+ function buildProgramManPage(program, progName, version, description, docsUrl) {
384
+ const help = new Help();
385
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
386
+ const s = [];
387
+ const resolvedDesc = description ?? program.description() ?? `${progName} CLI`;
388
+ s.push(`.TH "${progName.toUpperCase()}" "1" "${today}" "${progName} ${version}" "${progName} Manual"`);
389
+ s.push(".SH NAME");
390
+ s.push(`${progName} \\- ${roffEscape(resolvedDesc)}`);
391
+ s.push(".SH SYNOPSIS");
392
+ s.push(`\\fB${progName}\\fR [\\fIglobal\\-options\\fR] \\fIcommand\\fR [\\fIcommand\\-options\\fR]`);
393
+ if (resolvedDesc) {
394
+ s.push(".SH DESCRIPTION");
395
+ s.push(roffEscape(resolvedDesc));
396
+ }
397
+ const globalOpts = help.visibleOptions(program).filter((o) => !["help", "version", "all", "man"].includes(o.long?.replace("--", "") ?? ""));
398
+ if (globalOpts.length > 0) {
399
+ s.push(".SH GLOBAL OPTIONS");
400
+ for (const opt of globalOpts) {
401
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
402
+ s.push(".TP");
403
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
404
+ if (opt.description) s.push(roffEscape(opt.description));
405
+ }
406
+ }
407
+ const allCommands = help.visibleCommands(program);
408
+ if (allCommands.length > 0) {
409
+ s.push(".SH COMMANDS");
410
+ for (const cmd of allCommands) {
411
+ if (cmd.name() === "help") continue;
412
+ const desc = help.subcommandDescription(cmd);
413
+ s.push(".TP");
414
+ s.push(`\\fB${progName} ${roffEscape(cmd.name())}\\fR`);
415
+ if (desc) s.push(roffEscape(desc));
416
+ const cmdHelp = new Help();
417
+ const opts = cmdHelp.visibleOptions(cmd).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
418
+ for (const opt of opts) {
419
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
420
+ s.push(".RS");
421
+ s.push(".TP");
422
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
423
+ if (opt.description) s.push(roffEscape(opt.description));
424
+ s.push(".RE");
425
+ }
426
+ const subCmds = cmdHelp.visibleCommands(cmd).filter((c) => c.name() !== "help");
427
+ for (const sub of subCmds) {
428
+ const subDesc = help.subcommandDescription(sub);
429
+ s.push(".TP");
430
+ s.push(`\\fB${progName} ${roffEscape(cmd.name())} ${roffEscape(sub.name())}\\fR`);
431
+ if (subDesc) s.push(roffEscape(subDesc));
432
+ const subOpts = cmdHelp.visibleOptions(sub).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
433
+ for (const opt of subOpts) {
434
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
435
+ s.push(".RS");
436
+ s.push(".TP");
437
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
438
+ if (opt.description) s.push(roffEscape(opt.description));
439
+ s.push(".RE");
440
+ }
441
+ }
442
+ }
443
+ }
444
+ s.push(".SH ENVIRONMENT");
445
+ s.push(".TP");
446
+ s.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
447
+ s.push("Path to the apcore extensions directory.");
448
+ s.push(".TP");
449
+ s.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
450
+ s.push("Set to \\fB1\\fR to bypass approval prompts.");
451
+ s.push(".TP");
452
+ s.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
453
+ s.push("CLI\\-specific logging verbosity (DEBUG|INFO|WARNING|ERROR).");
454
+ s.push(".SH EXIT CODES");
455
+ const exitCodes = [
456
+ ["0", "Success."],
457
+ ["1", "Module execution error."],
458
+ ["2", "Invalid CLI input or missing argument."],
459
+ ["44", "Module not found, disabled, or failed to load."],
460
+ ["45", "Input failed JSON Schema validation."],
461
+ ["46", "Approval denied or timed out."],
462
+ ["47", "Configuration error."],
463
+ ["77", "ACL denied."],
464
+ ["130", "Cancelled by user (SIGINT)."]
465
+ ];
466
+ for (const [code, meaning] of exitCodes) {
467
+ s.push(`.TP
468
+ \\fB${code}\\fR
469
+ ${meaning}`);
470
+ }
471
+ s.push(".SH SEE ALSO");
472
+ s.push(`\\fB${progName} \\-\\-help \\-\\-verbose\\fR for full option list.`);
473
+ if (docsUrl) {
474
+ s.push(`.PP
475
+ Full documentation at \\fI${roffEscape(docsUrl)}\\fR`);
476
+ }
477
+ return s.join("\n");
478
+ }
479
+ function configureManHelp(program, progName, version, description, docsUrl) {
480
+ const manOpt = new Option("--man", "Output man page in roff format (use with --help)").hideHelp();
481
+ program.addOption(manOpt);
482
+ program.addHelpText("beforeAll", () => {
483
+ if (program.opts().man) {
484
+ const roff = buildProgramManPage(program, progName, version, description, docsUrl) + "\n";
485
+ if (process.stdout.isTTY) {
486
+ const pagers = [
487
+ { cmd: "mandoc", args: ["-a"] },
488
+ { cmd: "groff", args: ["-man", "-Tutf8"] }
489
+ ];
490
+ let rendered = false;
491
+ for (const { cmd, args } of pagers) {
492
+ const result = spawnSync(cmd, args, {
493
+ input: roff,
494
+ stdio: ["pipe", "pipe", "pipe"],
495
+ encoding: "utf-8"
496
+ });
497
+ if (result.status === 0 && result.stdout) {
498
+ const pager = process.env.PAGER || "less";
499
+ const pagerResult = spawnSync(pager, ["-R"], {
500
+ input: result.stdout,
501
+ stdio: ["pipe", "inherit", "inherit"]
502
+ });
503
+ if (pagerResult.status !== null) {
504
+ rendered = true;
505
+ break;
506
+ }
507
+ }
508
+ }
509
+ if (!rendered) {
510
+ process.stdout.write(roff);
511
+ }
512
+ } else {
513
+ process.stdout.write(roff);
514
+ }
515
+ process.exit(0);
516
+ }
517
+ return "";
518
+ });
519
+ }
520
+
521
+ // src/main.ts
522
+ var __dirname3 = path4.dirname(fileURLToPath3(import.meta.url));
523
+ var verboseHelp = false;
524
+ function hasVerboseFlag() {
525
+ return process.argv.includes("--verbose");
526
+ }
314
527
  var VERSION = "0.0.0";
315
528
  try {
316
- const pkg = JSON.parse(readFileSync(path3.resolve(__dirname2, "../package.json"), "utf-8"));
529
+ const pkg = JSON.parse(readFileSync3(path4.resolve(__dirname3, "../package.json"), "utf-8"));
317
530
  VERSION = pkg.version;
318
531
  } catch {
319
532
  }
320
- function createCli(extensionsDir, progName) {
321
- const resolvedProgName = progName ?? path3.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
533
+ function createCli(extensionsDir, progName, verbose = false) {
534
+ verboseHelp = verbose;
535
+ registerConfigNamespace();
536
+ const resolvedProgName = progName ?? path4.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
322
537
  const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
323
538
  setLogLevel(cliLogLevel);
324
- const program = new Command(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");
539
+ 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)");
325
540
  const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
326
541
  void resolvedExtDir;
542
+ program.addHelpText("after", [
543
+ "",
544
+ "Use --help --verbose to show all options (including built-in apcore options).",
545
+ "Use --help --man to display a formatted man page."
546
+ ].join("\n"));
327
547
  registerInitCommand(program);
548
+ configureManHelp(program, resolvedProgName, VERSION);
328
549
  program.hook("preAction", async (thisCommand) => {
329
550
  const opts = thisCommand.opts();
330
551
  const commandsDir = opts.commandsDir;
@@ -355,7 +576,8 @@ async function applyToolkitIntegration(commandsDir, bindingPath) {
355
576
  }
356
577
  }
357
578
  function main(progName) {
358
- const program = createCli(void 0, progName);
579
+ verboseHelp = hasVerboseFlag();
580
+ const program = createCli(void 0, progName, verboseHelp);
359
581
  try {
360
582
  program.parse(process.argv);
361
583
  } catch (error) {