apcore-cli 0.5.0 → 0.7.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/index.d.ts CHANGED
@@ -1,5 +1,146 @@
1
1
  import { Command } from 'commander';
2
2
 
3
+ /**
4
+ * Determines which modules are exposed as CLI commands.
5
+ *
6
+ * Filtering modes:
7
+ * - `all`: every discovered module becomes a CLI command (default).
8
+ * - `include`: only modules matching at least one include pattern are exposed.
9
+ * - `exclude`: all modules are exposed except those matching any exclude pattern.
10
+ */
11
+ declare class ExposureFilter {
12
+ static readonly VALID_MODES: readonly ["all", "include", "exclude", "none"];
13
+ readonly _mode: string;
14
+ private readonly _compiledInclude;
15
+ private readonly _compiledExclude;
16
+ constructor(mode?: string, include?: string[], exclude?: string[]);
17
+ /** Return true if the module should be exposed as a CLI command. */
18
+ isExposed(moduleId: string): boolean;
19
+ /** Partition moduleIds into [exposed, hidden] lists. */
20
+ filterModules(moduleIds: string[]): [string[], string[]];
21
+ /**
22
+ * Create an ExposureFilter from a parsed config dict.
23
+ *
24
+ * Expected: `{ expose: { mode: "include", include: ["admin.*"] } }`
25
+ */
26
+ static fromConfig(config: Record<string, unknown>): ExposureFilter;
27
+ }
28
+
29
+ /**
30
+ * Built-in Command Group (FE-13).
31
+ *
32
+ * Encapsulates visibility resolution and subcommand filtering for the
33
+ * reserved `apcli` group. Instantiated once by createCli() and attached
34
+ * to the root command.
35
+ *
36
+ * Shape mirrors `src/exposure.ts` ExposureFilter: private constructor,
37
+ * named static factories, and a small set of predicate methods.
38
+ * See the feature spec §4.2–4.7 for authoritative semantics.
39
+ */
40
+ /**
41
+ * Resolved visibility mode.
42
+ *
43
+ * `"auto"` is an internal sentinel — it is never returned from
44
+ * {@link ApcliGroup.resolveVisibility} and is rejected when supplied via
45
+ * user config (CliConfig or apcore.yaml).
46
+ */
47
+ type ApcliMode = "auto" | "all" | "none" | "include" | "exclude";
48
+ /**
49
+ * User-facing apcli config shape.
50
+ *
51
+ * Boolean shorthand maps to `{mode: "all"}` / `{mode: "none"}`.
52
+ * Object form rejects `"auto"` per spec §4.2 (internal sentinel only).
53
+ */
54
+ type ApcliConfig = boolean | {
55
+ mode?: Exclude<ApcliMode, "auto">;
56
+ include?: string[];
57
+ exclude?: string[];
58
+ disableEnv?: boolean;
59
+ };
60
+ /** Set of group names reserved by apcore-cli (checked in cli.ts). */
61
+ declare const RESERVED_GROUP_NAMES: ReadonlySet<string>;
62
+ /**
63
+ * Visibility configuration for the built-in `apcli` command group.
64
+ *
65
+ * Instantiated via {@link ApcliGroup.fromCliConfig} (Tier 1) or
66
+ * {@link ApcliGroup.fromYaml} (Tier 3). The constructor is private to
67
+ * preserve the Tier-1-vs-Tier-3 flag distinction.
68
+ */
69
+ declare class ApcliGroup {
70
+ private readonly _mode;
71
+ private readonly _include;
72
+ private readonly _exclude;
73
+ private readonly _disableEnv;
74
+ private readonly _registryInjected;
75
+ private readonly _fromCliConfig;
76
+ private constructor();
77
+ /**
78
+ * Tier 1 constructor — config came from `createCli({ apcli })`.
79
+ *
80
+ * A non-auto mode from this tier wins over env var and yaml.
81
+ */
82
+ static fromCliConfig(config: ApcliConfig | undefined, opts: {
83
+ registryInjected: boolean;
84
+ }): ApcliGroup;
85
+ /**
86
+ * Tier 3 constructor — config came from `apcore.yaml`.
87
+ *
88
+ * Env var (Tier 2) may override the yaml-supplied mode.
89
+ */
90
+ static fromYaml(config: unknown, opts: {
91
+ registryInjected: boolean;
92
+ }): ApcliGroup;
93
+ /**
94
+ * Non-panicking Tier 3 factory (A-001 parity with Rust's `try_from_yaml`).
95
+ * Returns `[instance, null]` on success or `[null, errorMessage]` on invalid input.
96
+ * Use this in programmatic contexts where throwing/exiting is unwanted.
97
+ */
98
+ static tryFromYaml(config: unknown, opts: {
99
+ registryInjected: boolean;
100
+ }): [ApcliGroup, null] | [null, string];
101
+ private static _build;
102
+ /**
103
+ * Normalize an include/exclude list. Non-array → warn and return [].
104
+ *
105
+ * Unknown but well-formed entries emit a WARNING (spec §7 error table,
106
+ * T-APCLI-25) but are retained in the returned list for forward-compat —
107
+ * if apcore-cli later adds a subcommand named `foo`, existing configs
108
+ * continue to work without a config change. At runtime, unknown names
109
+ * simply never match any registered subcommand.
110
+ */
111
+ private static _normalizeList;
112
+ /**
113
+ * Resolve effective visibility mode after applying tier precedence.
114
+ *
115
+ * Returns one of `"all" | "none" | "include" | "exclude"` — never `"auto"`.
116
+ *
117
+ * Tier order (spec §4.4):
118
+ * 1. CliConfig non-auto wins outright.
119
+ * 2. `APCORE_CLI_APCLI` env var (unless sealed by disableEnv).
120
+ * 3. yaml non-auto.
121
+ * 4. Auto-detect from registryInjected.
122
+ */
123
+ resolveVisibility(): "all" | "none" | "include" | "exclude";
124
+ /**
125
+ * True iff `subcommand` passes the include/exclude filter.
126
+ *
127
+ * Callers MUST first check {@link resolveVisibility} — this method throws
128
+ * under modes `"all"` or `"none"` (caller bug per spec §4.6).
129
+ */
130
+ isSubcommandIncluded(subcommand: string): boolean;
131
+ /** True iff the `apcli` group itself should appear in root `--help`. */
132
+ isGroupVisible(): boolean;
133
+ /**
134
+ * Parse APCORE_CLI_APCLI. Case-insensitive.
135
+ *
136
+ * - `show` / `1` / `true` → `"all"`
137
+ * - `hide` / `0` / `false` → `"none"`
138
+ * - Empty / unset → `null`
139
+ * - Anything else → warn and return `null`
140
+ */
141
+ private _parseEnv;
142
+ }
143
+
3
144
  /**
4
145
  * LazyModuleGroup — Dynamic command loading from Registry.
5
146
  *
@@ -14,9 +155,72 @@ interface Registry {
14
155
  listModules(): ModuleDescriptor[];
15
156
  getModule(moduleId: string): ModuleDescriptor | null;
16
157
  }
17
- /** Placeholder for apcore-js Executor. */
158
+ /** Strategy info returned by Executor.describePipeline(). */
159
+ interface StrategyInfo {
160
+ name: string;
161
+ stepCount: number;
162
+ stepNames: string[];
163
+ description: string;
164
+ }
165
+ /** A step in the executor strategy (shape parity with apcore-js Step). */
166
+ interface StrategyStep {
167
+ name: string;
168
+ pure?: boolean;
169
+ removable: boolean;
170
+ timeoutMs?: number;
171
+ }
172
+ /** Placeholder for apcore-js Executor. Shape-compatible with apcore-js >= 0.19.0. */
18
173
  interface Executor {
19
174
  execute(moduleId: string, input: Record<string, unknown>): Promise<unknown>;
175
+ /** Validate inputs without executing. Returns a PreflightResult. */
176
+ validate?(moduleId: string, input: Record<string, unknown>): Promise<PreflightResult>;
177
+ /** Execute with pipeline trace. Returns [result, PipelineTrace]. */
178
+ callWithTrace?(moduleId: string, input: Record<string, unknown>, options?: {
179
+ strategy?: string;
180
+ }): Promise<[unknown, PipelineTrace]>;
181
+ /** Stream execution — async iterator of chunks. */
182
+ stream?(moduleId: string, input: Record<string, unknown>): AsyncIterable<unknown>;
183
+ /** Call a module (synchronous-style, used by system commands). */
184
+ call?(moduleId: string, input: Record<string, unknown>): Promise<unknown>;
185
+ /**
186
+ * Describe the executor's currently-set strategy. Returns StrategyInfo
187
+ * (apcore-js >= 0.18.0). Takes no arguments — to introspect a different
188
+ * strategy, use `Executor.listStrategies()` (static) via `executor.constructor`.
189
+ */
190
+ describePipeline?(): StrategyInfo;
191
+ /** The current execution strategy object, exposing step metadata. */
192
+ currentStrategy?: {
193
+ readonly steps: readonly StrategyStep[];
194
+ };
195
+ }
196
+ /** Result of a preflight validation check. */
197
+ interface PreflightCheck {
198
+ readonly check: string;
199
+ readonly passed: boolean;
200
+ readonly error?: unknown;
201
+ readonly warnings?: string[];
202
+ }
203
+ /** Result of executor.validate() — parity with apcore-js PreflightResult. */
204
+ interface PreflightResult {
205
+ readonly valid: boolean;
206
+ readonly requiresApproval: boolean;
207
+ readonly checks: readonly PreflightCheck[];
208
+ readonly errors?: ReadonlyArray<Record<string, unknown>>;
209
+ }
210
+ /** A single step in a pipeline trace — parity with apcore-js StepTrace. */
211
+ interface PipelineTraceStep {
212
+ readonly name: string;
213
+ readonly durationMs: number;
214
+ readonly skipped: boolean;
215
+ readonly skipReason?: string | null;
216
+ }
217
+ /** Pipeline execution trace returned by callWithTrace() — parity with apcore-js PipelineTrace. */
218
+ interface PipelineTrace {
219
+ readonly moduleId?: string;
220
+ readonly strategyName: string;
221
+ readonly totalDurationMs: number;
222
+ readonly success: boolean;
223
+ readonly steps: readonly PipelineTraceStep[];
20
224
  }
21
225
  /** Placeholder for apcore-js ModuleDescriptor. */
22
226
  interface ModuleDescriptor {
@@ -30,8 +234,6 @@ interface ModuleDescriptor {
30
234
  annotations?: Record<string, unknown>;
31
235
  metadata?: Record<string, unknown>;
32
236
  }
33
- /** Built-in command names that cannot be overridden by modules. */
34
- declare const BUILTIN_COMMANDS: string[];
35
237
  /**
36
238
  * Dynamically loads apcore modules as Commander subcommands from Registry.
37
239
  */
@@ -87,16 +289,39 @@ declare class GroupedModuleGroup extends LazyModuleGroup {
87
289
  /** Cached LazyGroup instances */
88
290
  private groupCache;
89
291
  private groupMapBuilt;
292
+ /** Exposure filter (FE-12) — controls which modules appear as CLI commands */
293
+ exposureFilter: ExposureFilter;
294
+ /** Effective group depth (CLAUDE.md v0.6.0): constructor arg > APCORE_CLI_GROUP_DEPTH env > 1. */
295
+ readonly groupDepth: number;
296
+ constructor(registry: Registry, executor: Executor, helpTextMaxLength?: number, exposureFilter?: ExposureFilter, groupDepth?: number);
297
+ /**
298
+ * Resolve group depth from constructor arg > APCORE_CLI_GROUP_DEPTH env > default 1.
299
+ * Invalid env values (non-integer, non-positive) fall through to the default.
300
+ */
301
+ static resolveGroupDepth(explicit: number | undefined): number;
90
302
  /**
91
303
  * Determine (groupName | null, commandName) for a module from its display overlay.
304
+ *
305
+ * @param groupDepth Number of dotted segments to consume as the group prefix.
306
+ * Defaults to 1 (e.g., "math.add" → group="math", cmd="add").
307
+ * Set to 2 for multi-level grouping (e.g., "math.trig.sin" →
308
+ * group="math.trig", cmd="sin").
92
309
  */
93
- static resolveGroup(moduleId: string, descriptor: ModuleDescriptor): [string | null, string];
310
+ static resolveGroup(moduleId: string, descriptor: ModuleDescriptor, groupDepth?: number): [string | null, string];
94
311
  /**
95
312
  * Build the group map from registry modules.
313
+ *
314
+ * FE-13: hard-fails with exit 2 when a module resolves to the reserved
315
+ * `apcli` namespace in any of three ways — explicit `display.cli.group`,
316
+ * auto-grouped dotted prefix, or top-level alias/id. See spec §4.10.
96
317
  */
97
318
  buildGroupMap(): void;
98
319
  /**
99
- * List all available command names: builtins + group names + top-level module names.
320
+ * List all available command names: group names + top-level module names.
321
+ *
322
+ * FE-13: the built-in subcommand list is no longer folded in here — those
323
+ * commands live under the `apcli` prefix and are registered directly by
324
+ * `createCli`.
100
325
  */
101
326
  listCommands(): string[];
102
327
  /**
@@ -111,18 +336,8 @@ declare class GroupedModuleGroup extends LazyModuleGroup {
111
336
  isGroupMapBuilt(): boolean;
112
337
  }
113
338
 
114
- /**
115
- * CLI entry point — createCli / main equivalents.
116
- *
117
- * Protocol spec: CLI bootstrapping & command registration
118
- */
119
-
120
- /** Whether --verbose was passed (controls help detail level). */
121
- declare let verboseHelp: boolean;
122
339
  /** Set the verbose help flag. When false, built-in options are hidden from help. */
123
340
  declare function setVerboseHelp(verbose: boolean): void;
124
- /** Base URL for online documentation. Null means no docs link shown. */
125
- declare let docsUrl: string | null;
126
341
  /**
127
342
  * Set the base URL for online documentation links shown in help and man pages.
128
343
  * Pass null to disable. Command-level help appends `/commands/{name}` automatically.
@@ -152,18 +367,65 @@ interface OptionConfig {
152
367
  parseArg?: (value: string) => unknown;
153
368
  }
154
369
  /**
155
- * Build and return the top-level Commander program.
156
- *
157
- * @param extensionsDir Path to the extensions directory (default: ./extensions)
158
- * @param progName Program name shown in help (default: apcore-cli)
370
+ * Emit structured JSON error to stderr for AI agents.
371
+ */
372
+ declare function emitErrorJson(e: unknown, exitCode: number): void;
373
+ /**
374
+ * Emit human-readable error to stderr with guidance fields.
159
375
  */
160
- declare function createCli(extensionsDir?: string, progName?: string, verbose?: boolean): Command;
376
+ declare function emitErrorTty(e: unknown, exitCode: number): void;
161
377
  /**
162
- * Optionally apply apcore-toolkit features (DisplayResolver, RegistryWriter).
378
+ * APCore unified client facade (apcore-js >= 0.18.0).
379
+ * Exposes registry and executor as top-level properties.
380
+ */
381
+ interface APCore {
382
+ registry: Registry;
383
+ executor: Executor;
384
+ }
385
+ /** Options for createCli. */
386
+ interface CreateCliOptions {
387
+ extensionsDir?: string;
388
+ progName?: string;
389
+ verbose?: boolean;
390
+ /**
391
+ * APCore unified client instance (apcore-js >= 0.18.0).
392
+ * Mutually exclusive with registry/executor — providing app alongside
393
+ * either of those will throw.
394
+ */
395
+ app?: APCore;
396
+ /** Pre-populated Registry instance. Skips filesystem discovery when provided. */
397
+ registry?: Registry;
398
+ /** Pre-built Executor instance. Used alongside registry. */
399
+ executor?: Executor;
400
+ /** Extra commands to register after built-in commands (FE-11 F11). */
401
+ extraCommands?: Command[];
402
+ /** Exposure filter config or instance (FE-12). */
403
+ expose?: Record<string, unknown> | ExposureFilter;
404
+ /** Path to convention-based commands directory (apcore-toolkit ConventionScanner). */
405
+ commandsDir?: string;
406
+ /** Path to binding.yaml for display overlay (apcore-toolkit DisplayResolver). */
407
+ bindingPath?: string;
408
+ /**
409
+ * Built-in apcli group configuration (FE-13).
410
+ *
411
+ * Accepts:
412
+ * - `true` / `false` (shorthand for `{mode: "all"}` / `{mode: "none"}`)
413
+ * - A config object (see {@link ApcliConfig})
414
+ * - A pre-built {@link ApcliGroup} instance (Tier 1 override)
415
+ *
416
+ * When absent, Tier 3 (apcore.yaml `apcli:` block) is consulted, falling
417
+ * back to auto-detect: standalone → visible, embedded → hidden.
418
+ */
419
+ apcli?: ApcliConfig | ApcliGroup;
420
+ }
421
+ /**
422
+ * Build and return the top-level Commander program.
163
423
  *
164
- * Uses dynamic import so the dependency remains optional — if apcore-toolkit
165
- * is not installed, a warning is printed and the CLI continues without it.
424
+ * @param extensionsDirOrOpts Path to extensions directory, or a CreateCliOptions object.
425
+ * @param progName Program name shown in help (default: apcore-cli)
426
+ * @param verbose Show verbose help output
166
427
  */
428
+ declare function createCli(extensionsDirOrOpts?: string | CreateCliOptions, progName?: string, verbose?: boolean): Command;
167
429
  declare function applyToolkitIntegration(commandsDir?: string, bindingPath?: string): Promise<void>;
168
430
  /**
169
431
  * Parse argv and run the CLI. Handles top-level error catching and exit codes.
@@ -171,11 +433,18 @@ declare function applyToolkitIntegration(commandsDir?: string, bindingPath?: str
171
433
  declare function main(progName?: string): void;
172
434
  /**
173
435
  * Build a Commander Command for a single apcore module.
436
+ *
437
+ * Includes all 11 FE-11 options: --dry-run, --trace, --stream, --strategy,
438
+ * --approval-timeout, --approval-token, --fields, and enhanced --format choices.
174
439
  */
175
440
  declare function buildModuleCommand(moduleDef: ModuleDescriptor, executor: Executor, helpTextMaxLength?: number, cmdName?: string, verbose?: boolean): Command;
176
441
  /**
177
442
  * Validate that a module ID conforms to the expected format.
178
- * Pattern: [a-z][a-z0-9_]*(.[a-z][a-z0-9_])* — max 128 chars.
443
+ * Pattern: [a-z][a-z0-9_]*(.[a-z][a-z0-9_])* — max 192 chars.
444
+ *
445
+ * Length limit tracks PROTOCOL_SPEC §2.7 EBNF constraint #1 — bumped from
446
+ * 128 to 192 in spec 1.6.0-draft to accommodate Java/.NET deep-namespace
447
+ * FQN-derived IDs. Filesystem-safe (192 + ".binding.yaml".length = 205 < 255).
179
448
  */
180
449
  declare function validateModuleId(moduleId: string): void;
181
450
  /**
@@ -189,35 +458,54 @@ declare function collectInput(stdinFlag?: string, cliKwargs?: Record<string, unk
189
458
  declare function reconvertEnumValues(kwargs: Record<string, unknown>, options: OptionConfig[]): Record<string, unknown>;
190
459
 
191
460
  /**
192
- * Display overlay helpers shared resolution logic for CLI surfaces.
193
- */
194
-
195
- /**
196
- * Extract resolved display overlay from a ModuleDescriptor's metadata.
197
- */
198
- declare function getDisplay(descriptor: ModuleDescriptor): Record<string, unknown>;
199
- /**
200
- * Return [displayName, description, tags] resolved from the display overlay.
461
+ * Interactive approval prompts with timeout.
201
462
  *
202
- * Falls back to scanner-provided values when no overlay is present.
463
+ * Protocol spec: Approval workflow
203
464
  */
204
- declare function getCliDisplayFields(descriptor: ModuleDescriptor): [string, string, string[]];
205
465
 
206
466
  /**
207
- * Init command scaffold new apcore modules (Phase 1).
208
- */
209
-
467
+ * CLI ApprovalHandler that prompts in TTY, auto-denies in non-TTY.
468
+ *
469
+ * Implements the apcore ApprovalHandler protocol:
470
+ * - `requestApproval(request) -> ApprovalResult`
471
+ * - `checkApproval(approvalId) -> ApprovalResult`
472
+ *
473
+ * Pass to Executor via `executor.setApprovalHandler(handler)`.
474
+ */
475
+ declare class CliApprovalHandler {
476
+ autoApprove: boolean;
477
+ timeout: number;
478
+ constructor(autoApprove?: boolean, timeout?: number);
479
+ requestApproval(request: Record<string, unknown>): Promise<Record<string, unknown>>;
480
+ checkApproval(_approvalId: string): Promise<Record<string, unknown>>;
481
+ }
210
482
  /**
211
- * Register the init command group on the CLI program.
483
+ * Check if module requires approval and handle accordingly.
484
+ * Returns normally if approved (or approval not required).
485
+ * Throws ApprovalDeniedError or ApprovalTimeoutError on denial/timeout so the
486
+ * caller (buildModuleCommand action) can run audit flush and tear-down before
487
+ * the process exits via the shared error path.
212
488
  */
213
- declare function registerInitCommand(cli: Command): void;
489
+ declare function checkApproval(moduleDef: ModuleDescriptor, autoApprove: boolean, timeout?: number): Promise<void>;
214
490
 
215
491
  /**
216
492
  * ConfigResolver — 4-tier config resolution (CLI flag > env > file > default).
217
493
  *
218
494
  * Protocol spec: Configuration resolution
219
495
  */
220
- /** Default configuration values. */
496
+ /**
497
+ * Default configuration values.
498
+ *
499
+ * Audit D9 (config cleanup, v0.6.x): the entries `sandbox.enabled`,
500
+ * `cli.auto_approve`, `cli.stdin_buffer_limit`, and the eight `apcore-cli.*`
501
+ * namespace aliases were removed because no production code path reads
502
+ * them via `resolve()`. Sandbox is configured via the `--sandbox` CLI flag,
503
+ * auto-approve via `--yes`, the stdin buffer is hard-coded, and namespace
504
+ * aliases are registered separately by `apcore-js`'s Config Bus when
505
+ * `registerConfigNamespace()` runs at `createCli` startup. The cross-key
506
+ * file-lookup mechanism (`NAMESPACE_TO_LEGACY` / `LEGACY_TO_NAMESPACE`)
507
+ * still works regardless — it does not depend on these DEFAULTS entries.
508
+ */
221
509
  declare const DEFAULTS: Record<string, unknown>;
222
510
  /**
223
511
  * Register the apcore-cli Config Bus namespace (apcore >= 0.15.0).
@@ -236,6 +524,13 @@ declare class ConfigResolver {
236
524
  private readonly configPath;
237
525
  private fileCache;
238
526
  private fileCacheLoaded;
527
+ /**
528
+ * Raw parsed yaml root (pre-flatten). Populated alongside `fileCache`
529
+ * on load. Used by `resolveObject()` to walk nested paths without
530
+ * invoking `flattenDict` — see FE-13 spec §4.8 M1 note.
531
+ * `null` when no config file is present or parsing fails.
532
+ */
533
+ private _rawConfig;
239
534
  constructor(cliFlags?: Record<string, unknown>, configPath?: string);
240
535
  /**
241
536
  * Resolve a single configuration key across all four tiers.
@@ -245,6 +540,26 @@ declare class ConfigResolver {
245
540
  * Load a value from the config file using a dot-separated key path.
246
541
  */
247
542
  private resolveFromFile;
543
+ /**
544
+ * Resolve a configuration key to its raw nested value (FE-13).
545
+ *
546
+ * Unlike `resolve()`, this method does NOT flatten the yaml tree — it
547
+ * walks the dot-separated path directly against the parsed yaml root.
548
+ * This lets callers retrieve non-leaf structures (booleans, arrays,
549
+ * objects) such as the `apcli` visibility config, which is naturally
550
+ * shaped as a nested object in apcore.yaml.
551
+ *
552
+ * Semantics:
553
+ * - Returns `null` when no config file is loaded or when the path is
554
+ * not present / descends into a non-object node (including arrays).
555
+ * - Returns the raw value (boolean / array / object / scalar) when the
556
+ * full path resolves to a leaf or intermediate node.
557
+ *
558
+ * Intentionally DOES NOT consult DEFAULTS, env vars, or CLI flags — it is
559
+ * strictly a yaml-tree accessor. Scalar `resolve()` semantics are
560
+ * unaffected.
561
+ */
562
+ resolveObject(key: string): unknown;
248
563
  /**
249
564
  * Load and flatten a YAML config file.
250
565
  */
@@ -256,42 +571,41 @@ declare class ConfigResolver {
256
571
  }
257
572
 
258
573
  /**
259
- * Discovery commands list and describe modules.
260
- *
261
- * Protocol spec: Module discovery & introspection
574
+ * Register the `list` subcommand on the given group (FE-13).
262
575
  */
263
-
576
+ declare function registerListCommand(apcliGroup: Command, registry: Registry, exposureFilter?: ExposureFilter): void;
264
577
  /**
265
- * Register list and describe commands on the CLI group.
578
+ * Register the `describe` subcommand on the given group (FE-13).
266
579
  */
267
- declare function registerDiscoveryCommands(cli: Command, registry: Registry): void;
268
-
580
+ declare function registerDescribeCommand(apcliGroup: Command, registry: Registry): void;
269
581
  /**
270
- * TTY-adaptive output formatting (table/json).
582
+ * Register the `exec` subcommand on the given group (FE-13).
271
583
  *
272
- * Protocol spec: Output formatting
273
- */
274
-
275
- /**
276
- * Resolve output format with TTY-adaptive default.
277
- */
278
- declare function resolveFormat(explicitFormat?: string): string;
279
- /**
280
- * Truncate text to maxLength, appending '...' if needed.
584
+ * Generic dispatch: `apcli exec <module-id> [--format fmt] [--input json]`.
585
+ * Unlike the per-module commands built by `buildModuleCommand`, this command
586
+ * does not derive options from the module's input schema — inputs are passed
587
+ * as a JSON object via `--input`. This mirrors the apcli-flavoured generic
588
+ * dispatch contract in the builtin-group feature spec.
281
589
  */
282
- declare function truncate(text: string, maxLength?: number): string;
590
+ declare function registerExecCommand(apcliGroup: Command, registry: Registry, executor: Executor): void;
283
591
  /**
284
- * Format and print a list of modules.
592
+ * Register the standalone validate command.
285
593
  */
286
- declare function formatModuleList(modules: ModuleDescriptor[], format: string, filterTags?: string[]): void;
594
+ declare function registerValidateCommand(cli: Command, registry: Registry, executor: Executor): void;
595
+
287
596
  /**
288
- * Format and print full module metadata.
597
+ * TTY-adaptive output formatting (table/json/csv/yaml/jsonl).
598
+ *
599
+ * Protocol spec: Output formatting (FE-09 enhanced)
289
600
  */
290
- declare function formatModuleDetail(moduleDef: ModuleDescriptor, format: string): void;
601
+
291
602
  /**
292
603
  * Format and print module execution result.
604
+ *
605
+ * Supports formats: json, table, csv, yaml, jsonl.
606
+ * The `fields` option allows dot-path field selection on dict results.
293
607
  */
294
- declare function formatExecResult(result: unknown, format?: string): void;
608
+ declare function formatExecResult(result: unknown, format?: string, fields?: string): void;
295
609
 
296
610
  /**
297
611
  * JSON Schema $ref resolver.
@@ -310,17 +624,6 @@ declare function resolveRefs(schema: Record<string, unknown>, maxDepth?: number,
310
624
  * Protocol spec: Schema-driven argument parsing
311
625
  */
312
626
 
313
- /** Sentinel type marker for boolean flags. */
314
- declare const BOOLEAN_FLAG: unique symbol;
315
- type TypeResult = "string" | "int" | "float" | typeof BOOLEAN_FLAG | "file";
316
- /**
317
- * Map JSON Schema type to a type identifier.
318
- */
319
- declare function mapType(propName: string, propSchema: Record<string, unknown>): TypeResult;
320
- /**
321
- * Extract help text from schema property, preferring x-llm-description.
322
- */
323
- declare function extractHelp(propSchema: Record<string, unknown>, maxLength?: number): string | undefined;
324
627
  /**
325
628
  * Convert a JSON Schema `properties` object into an array of
326
629
  * Commander option configurations.
@@ -328,42 +631,86 @@ declare function extractHelp(propSchema: Record<string, unknown>, maxLength?: nu
328
631
  declare function schemaToCliOptions(schema: Record<string, unknown>, maxHelpLength?: number): OptionConfig[];
329
632
 
330
633
  /**
331
- * Interactive approval prompts with timeout.
634
+ * Shell completion + man page generation.
332
635
  *
333
- * Protocol spec: Approval workflow
636
+ * Protocol spec: Shell integration
334
637
  */
335
638
 
336
639
  /**
337
- * Check if module requires approval and handle accordingly.
338
- * Returns normally if approved (or approval not required).
339
- * Calls process.exit(46) if denied/timed out/non-TTY.
640
+ * Configure --help --man support on a Commander program.
641
+ * When --man is passed with --help, outputs a complete roff man page
642
+ * covering all registered commands (including downstream business commands).
643
+ *
644
+ * Usage in downstream projects:
645
+ * configureManHelp(program, 'reach', '0.2.0', 'ReachForge: The Social Influence Engine', 'https://reachforge.dev/docs');
646
+ */
647
+ declare function configureManHelp(program: Command, progName: string, version: string, description?: string, docsUrl?: string): void;
648
+ /**
649
+ * Register the `completion` subcommand on `host` (typically the apcli group
650
+ * per spec §4.1, or the root program during the transition period).
651
+ *
652
+ * The completion-script generator enumerates the actually-registered set of
653
+ * subcommands from the root program's Commander tree at generation time
654
+ * (spec §4.13).
340
655
  */
341
- declare function checkApproval(moduleDef: ModuleDescriptor, autoApprove: boolean): Promise<void>;
656
+ declare function registerCompletionCommand(host: Command): void;
342
657
 
343
658
  /**
344
- * Shell completion + man page generation.
659
+ * System management commands health, usage, enable, disable, reload, config (FE-11 F2).
345
660
  *
346
- * Protocol spec: Shell integration
661
+ * Each registrar delegates to the corresponding system.* module via the
662
+ * executor. The six registrars are pure attach-only operations and are
663
+ * dispatched by `createCli()`'s FE-13 apcli group integration.
347
664
  */
348
665
 
349
666
  /**
350
- * Build a complete roff man page for the entire program.
351
- * Covers all registered commands including downstream business commands.
667
+ * Attach the `health` subcommand to the passed apcliGroup.
352
668
  */
353
- declare function buildProgramManPage(program: Command, progName: string, version: string, description?: string, docsUrl?: string): string;
669
+ declare function registerHealthCommand(apcliGroup: Command, executor: Executor): void;
354
670
  /**
355
- * Configure --help --man support on a Commander program.
356
- * When --man is passed with --help, outputs a complete roff man page
357
- * covering all registered commands (including downstream business commands).
671
+ * Attach the `usage` subcommand to the passed apcliGroup.
672
+ */
673
+ declare function registerUsageCommand(apcliGroup: Command, executor: Executor): void;
674
+ /**
675
+ * Attach the `enable` subcommand to the passed apcliGroup.
676
+ */
677
+ declare function registerEnableCommand(apcliGroup: Command, executor: Executor): void;
678
+ /**
679
+ * Attach the `disable` subcommand to the passed apcliGroup.
680
+ */
681
+ declare function registerDisableCommand(apcliGroup: Command, executor: Executor): void;
682
+ /**
683
+ * Attach the `reload` subcommand to the passed apcliGroup.
684
+ */
685
+ declare function registerReloadCommand(apcliGroup: Command, executor: Executor): void;
686
+ /**
687
+ * Attach the `config` subcommand (with `get` and `set` children) to the
688
+ * passed apcliGroup.
358
689
  *
359
- * Usage in downstream projects:
360
- * configureManHelp(program, 'reach', '0.2.0', 'ReachForge: The Social Influence Engine', 'https://reachforge.dev/docs');
690
+ * Signature note: config reads/writes go through `executor.call()` to
691
+ * `system.config.get` / `system.control.update_config`, so this registrar
692
+ * takes an {@link Executor} (not a Registry). The FE-13 dispatcher table
693
+ * entry for `config` sets `requiresExecutor: true` accordingly.
361
694
  */
362
- declare function configureManHelp(program: Command, progName: string, version: string, description?: string, docsUrl?: string): void;
695
+ declare function registerConfigCommand(apcliGroup: Command, executor: Executor): void;
696
+
697
+ /**
698
+ * Pipeline strategy commands — describe-pipeline (FE-11 F8).
699
+ */
700
+
363
701
  /**
364
- * Register completion and man commands.
702
+ * Register the describe-pipeline command.
365
703
  */
366
- declare function registerShellCommands(cli: Command, progName?: string): void;
704
+ declare function registerPipelineCommand(cli: Command, executor: Executor): void;
705
+
706
+ /**
707
+ * Init command — scaffold new apcore modules (Phase 1).
708
+ */
709
+
710
+ /**
711
+ * Register the init command group on the CLI program.
712
+ */
713
+ declare function registerInitCommand(cli: Command): void;
367
714
 
368
715
  /**
369
716
  * Error classes and exit code mapping for apcore-cli.
@@ -406,6 +753,8 @@ declare const EXIT_CODES: {
406
753
  readonly MODULE_NOT_FOUND: 44;
407
754
  readonly MODULE_LOAD_ERROR: 44;
408
755
  readonly MODULE_DISABLED: 44;
756
+ readonly DEPENDENCY_NOT_FOUND: 44;
757
+ readonly DEPENDENCY_VERSION_MISMATCH: 44;
409
758
  readonly SCHEMA_VALIDATION_ERROR: 45;
410
759
  readonly APPROVAL_DENIED: 46;
411
760
  readonly APPROVAL_TIMEOUT: 46;
@@ -416,6 +765,7 @@ declare const EXIT_CODES: {
416
765
  readonly CONFIG_NAMESPACE_RESERVED: 78;
417
766
  readonly CONFIG_NAMESPACE_DUPLICATE: 78;
418
767
  readonly CONFIG_ENV_PREFIX_CONFLICT: 78;
768
+ readonly CONFIG_ENV_MAP_CONFLICT: 78;
419
769
  readonly CONFIG_MOUNT_ERROR: 66;
420
770
  readonly CONFIG_BIND_ERROR: 65;
421
771
  readonly ERROR_FORMATTER_DUPLICATE: 70;
@@ -440,10 +790,6 @@ declare const LEVELS: {
440
790
  type LogLevel = keyof typeof LEVELS;
441
791
  declare function setLogLevel(level: string): void;
442
792
  declare function getLogLevel(): LogLevel;
443
- declare function debug(message: string): void;
444
- declare function info(message: string): void;
445
- declare function warn(message: string): void;
446
- declare function error(message: string): void;
447
793
 
448
794
  /**
449
795
  * AuditLogger — JSONL audit trail.
@@ -462,6 +808,7 @@ declare function getAuditLogger(): AuditLogger | null;
462
808
  declare class AuditLogger {
463
809
  static readonly DEFAULT_PATH: string;
464
810
  private readonly logPath;
811
+ private writeFailureWarned;
465
812
  constructor(path?: string);
466
813
  private ensureDirectory;
467
814
  logExecution(moduleId: string, inputData: Record<string, unknown>, status: ExecutionStatus, exitCode: number, durationMs: number): void;
@@ -469,19 +816,31 @@ declare class AuditLogger {
469
816
  private getUser;
470
817
  }
471
818
 
472
- /**
473
- * ConfigEncryptor — Keyring + AES-256-GCM fallback.
474
- *
475
- * Protocol spec: Security — config encryption
476
- */
477
819
  /**
478
820
  * Encrypts and decrypts configuration values. Prefers OS keyring for key
479
- * storage, falling back to AES-256-GCM with a derived key.
821
+ * storage, falling back to AES-256-GCM with a key derived from
822
+ * APCORE_CLI_CONFIG_PASSPHRASE when set, or from hostname+username
823
+ * (obfuscation-only) as a last resort.
824
+ *
825
+ * Wire format v2: enc:v2:<base64(salt(16)+nonce(12)+tag(16)+ciphertext)>
826
+ * Legacy format: enc:<base64(nonce(12)+tag(16)+ciphertext)> (read-only)
480
827
  */
481
828
  declare class ConfigEncryptor {
482
829
  static readonly SERVICE_NAME = "apcore-cli";
830
+ private static weakFallbackWarned;
483
831
  /**
484
832
  * Encrypt and store a configuration value.
833
+ *
834
+ * Cross-SDK contract (D10-003, 2026-04-26): when the OS keyring is
835
+ * detected as available but `setPassword` then throws (locked keyring,
836
+ * transient backend failure, permission revoked, etc.), the error is
837
+ * propagated wrapped in a `ConfigDecryptionError`. Previously TS
838
+ * caught the exception and silently fell through to AES file encryption
839
+ * — a quiet downgrade that surprised users who expected a hard failure.
840
+ * Python lets the keyring exception propagate raw; Rust returns
841
+ * `ConfigDecryptionError::KeyringError`. The fall-through to AES is
842
+ * still reached when `getKeytar()` returns `null` (keyring
843
+ * genuinely unavailable on this platform / install).
485
844
  */
486
845
  store(key: string, value: string): Promise<string>;
487
846
  /**
@@ -491,6 +850,8 @@ declare class ConfigEncryptor {
491
850
  private deriveKey;
492
851
  private aesEncrypt;
493
852
  private aesDecrypt;
853
+ /** Decrypt legacy v1-format values: nonce(12)+tag(16)+ct, static salt. */
854
+ private aesDecryptV1;
494
855
  }
495
856
 
496
857
  /**
@@ -513,6 +874,15 @@ declare class AuthProvider {
513
874
  getApiKey(): Promise<string | null>;
514
875
  /**
515
876
  * Add authentication headers to an outgoing request.
877
+ *
878
+ * Cross-SDK contract (D10-002, 2026-04-26): the input `headers` object
879
+ * is mutated **in place** and the same reference is returned. Callers
880
+ * that share the headers reference (the documented pattern in
881
+ * apcore-cli/docs/features/security.md §AuthProvider) can read
882
+ * `headers.Authorization` after the call without re-binding the
883
+ * return value. Python and Rust both mutate-and-return; TS previously
884
+ * spread into a new object, which silently broke shared-reference
885
+ * callers.
516
886
  */
517
887
  authenticateRequest(headers: Record<string, string>): Promise<Record<string, string>>;
518
888
  /**
@@ -524,23 +894,31 @@ declare class AuthProvider {
524
894
  /**
525
895
  * Sandbox — Subprocess isolation for module execution.
526
896
  *
527
- * Protocol spec: Security — sandboxed execution
897
+ * Protocol spec: Security — sandboxed execution (tech-design §8.6.4).
898
+ *
899
+ * Uses a re-exec model: spawns `node <this-binary> --internal-sandbox-runner
900
+ * <module_id>` with a stripped environment and isolated HOME/TMPDIR.
901
+ * The child reads JSON from stdin, runs the module via a fresh
902
+ * Registry+Executor, and writes JSON to stdout.
528
903
  */
529
904
 
530
905
  /**
531
906
  * Executes modules in an isolated subprocess to limit the blast radius
532
907
  * of untrusted or third-party modules.
533
908
  *
534
- * When disabled, delegates directly to the Executor.
909
+ * When disabled (the default), delegates directly to the Executor.
910
+ * When enabled, spawns a restricted child process via re-exec with
911
+ * `--internal-sandbox-runner <module_id>`.
535
912
  */
536
913
  declare class Sandbox {
537
914
  private readonly enabled;
538
- constructor(enabled?: boolean);
915
+ private readonly timeoutSeconds;
916
+ constructor(enabled?: boolean, timeoutSeconds?: number);
539
917
  /**
540
918
  * Execute a module, optionally inside a sandboxed subprocess.
541
919
  */
542
920
  execute(moduleId: string, inputData: Record<string, unknown>, executor: Executor): Promise<unknown>;
543
- private sandboxedExecute;
921
+ private _sandboxedExecute;
544
922
  }
545
923
 
546
- export { ApprovalDeniedError, ApprovalTimeoutError, AuditLogger, AuthProvider, AuthenticationError, BUILTIN_COMMANDS, ConfigDecryptionError, ConfigEncryptor, ConfigResolver, DEFAULTS, EXIT_CODES, type Executor, type ExitCode, GroupedModuleGroup, LazyGroup, LazyModuleGroup, type ModuleDescriptor, ModuleExecutionError, ModuleNotFoundError, type OptionConfig, type Registry, Sandbox, SchemaValidationError, applyToolkitIntegration, buildModuleCommand, buildProgramManPage, checkApproval, collectInput, configureManHelp, createCli, debug, docsUrl, error, exitCodeForError, extractHelp, formatExecResult, formatModuleDetail, formatModuleList, getAuditLogger, getCliDisplayFields, getDisplay, getLogLevel, info, main, mapType, reconvertEnumValues, registerConfigNamespace, registerDiscoveryCommands, registerInitCommand, registerShellCommands, resolveFormat, resolveRefs, schemaToCliOptions, setAuditLogger, setDocsUrl, setLogLevel, setVerboseHelp, truncate, validateModuleId, verboseHelp, warn };
924
+ export { type APCore, type ApcliConfig, ApcliGroup, type ApcliMode, ApprovalDeniedError, ApprovalTimeoutError, AuditLogger, AuthProvider, AuthenticationError, CliApprovalHandler, ConfigDecryptionError, ConfigEncryptor, ConfigResolver, type CreateCliOptions, DEFAULTS, EXIT_CODES, type Executor, type ExitCode, ExposureFilter, GroupedModuleGroup, LazyGroup, LazyModuleGroup, type ModuleDescriptor, ModuleExecutionError, ModuleNotFoundError, type OptionConfig, type PipelineTrace, type PipelineTraceStep, type PreflightCheck, type PreflightResult, RESERVED_GROUP_NAMES, type Registry, Sandbox, SchemaValidationError, type StrategyInfo, type StrategyStep, applyToolkitIntegration, buildModuleCommand, checkApproval, collectInput, configureManHelp, createCli, emitErrorJson, emitErrorTty, exitCodeForError, formatExecResult, getAuditLogger, getLogLevel, main, reconvertEnumValues, registerCompletionCommand, registerConfigCommand, registerConfigNamespace, registerDescribeCommand, registerDisableCommand, registerEnableCommand, registerExecCommand, registerHealthCommand, registerInitCommand, registerListCommand, registerPipelineCommand, registerReloadCommand, registerUsageCommand, registerValidateCommand, resolveRefs, schemaToCliOptions, setAuditLogger, setDocsUrl, setLogLevel, setVerboseHelp, validateModuleId };