apcore-cli 0.6.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.
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ var __defProp = Object.defineProperty;
2
3
  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
- });
9
4
  var __esm = (fn, res) => function __init() {
10
5
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
6
  };
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
12
11
 
13
12
  // node_modules/.pnpm/tsup@8.5.1_postcss@8.5.8_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js
14
13
  import path from "path";
@@ -48,6 +47,8 @@ function exitCodeForError(error) {
48
47
  MODULE_NOT_FOUND: EXIT_CODES.MODULE_NOT_FOUND,
49
48
  MODULE_LOAD_ERROR: EXIT_CODES.MODULE_LOAD_ERROR,
50
49
  MODULE_DISABLED: EXIT_CODES.MODULE_DISABLED,
50
+ DEPENDENCY_NOT_FOUND: EXIT_CODES.DEPENDENCY_NOT_FOUND,
51
+ DEPENDENCY_VERSION_MISMATCH: EXIT_CODES.DEPENDENCY_VERSION_MISMATCH,
51
52
  SCHEMA_VALIDATION_ERROR: EXIT_CODES.SCHEMA_VALIDATION_ERROR,
52
53
  SCHEMA_CIRCULAR_REF: EXIT_CODES.SCHEMA_CIRCULAR_REF,
53
54
  APPROVAL_DENIED: EXIT_CODES.APPROVAL_DENIED,
@@ -62,6 +63,7 @@ function exitCodeForError(error) {
62
63
  CONFIG_NAMESPACE_RESERVED: EXIT_CODES.CONFIG_NAMESPACE_RESERVED,
63
64
  CONFIG_NAMESPACE_DUPLICATE: EXIT_CODES.CONFIG_NAMESPACE_DUPLICATE,
64
65
  CONFIG_ENV_PREFIX_CONFLICT: EXIT_CODES.CONFIG_ENV_PREFIX_CONFLICT,
66
+ CONFIG_ENV_MAP_CONFLICT: EXIT_CODES.CONFIG_ENV_MAP_CONFLICT,
65
67
  CONFIG_MOUNT_ERROR: EXIT_CODES.CONFIG_MOUNT_ERROR,
66
68
  CONFIG_BIND_ERROR: EXIT_CODES.CONFIG_BIND_ERROR,
67
69
  ERROR_FORMATTER_DUPLICATE: EXIT_CODES.ERROR_FORMATTER_DUPLICATE
@@ -127,6 +129,8 @@ var init_errors = __esm({
127
129
  MODULE_NOT_FOUND: 44,
128
130
  MODULE_LOAD_ERROR: 44,
129
131
  MODULE_DISABLED: 44,
132
+ DEPENDENCY_NOT_FOUND: 44,
133
+ DEPENDENCY_VERSION_MISMATCH: 44,
130
134
  SCHEMA_VALIDATION_ERROR: 45,
131
135
  APPROVAL_DENIED: 46,
132
136
  APPROVAL_TIMEOUT: 46,
@@ -147,38 +151,635 @@ var init_errors = __esm({
147
151
  }
148
152
  });
149
153
 
150
- // bin/apcore-cli.ts
151
- init_esm_shims();
152
-
153
- // src/main.ts
154
- init_esm_shims();
155
- init_errors();
156
- import { readFileSync as readFileSync3 } from "fs";
157
- import { fileURLToPath as fileURLToPath3 } from "url";
158
- import * as path4 from "path";
159
- import { Command as Command6, CommanderError, Option as Option4 } from "commander";
154
+ // src/security/sandbox.ts
155
+ var sandbox_exports = {};
156
+ __export(sandbox_exports, {
157
+ Sandbox: () => Sandbox,
158
+ runSandboxRunner: () => runSandboxRunner
159
+ });
160
+ async function runSandboxRunner(moduleId) {
161
+ const extensionsRoot = process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
162
+ const apcore = await import("apcore-js").catch(() => {
163
+ process.stderr.write(
164
+ "sandbox runner: apcore-js is not available in the sandboxed environment.\n"
165
+ );
166
+ process.exit(1);
167
+ });
168
+ if (!apcore) return;
169
+ let inputJson = "";
170
+ for await (const chunk of process.stdin) {
171
+ inputJson += chunk.toString();
172
+ }
173
+ let inputData;
174
+ try {
175
+ inputData = JSON.parse(inputJson);
176
+ } catch {
177
+ process.stderr.write("sandbox runner: failed to parse stdin as JSON.\n");
178
+ process.exit(1);
179
+ return;
180
+ }
181
+ const registry = new apcore.Registry({ extensionsDir: extensionsRoot });
182
+ await registry.discover();
183
+ const executor = new apcore.Executor({ registry });
184
+ try {
185
+ const result = await executor.call(moduleId, inputData);
186
+ process.stdout.write(JSON.stringify(result));
187
+ process.exit(0);
188
+ } catch (err) {
189
+ process.stderr.write(`sandbox runner error: ${err}
190
+ `);
191
+ process.exit(1);
192
+ }
193
+ }
194
+ function buildSandboxEnv(tmpDir) {
195
+ const env = {};
196
+ for (const key of SANDBOX_ALLOW_KEYS) {
197
+ if (process.env[key]) env[key] = process.env[key];
198
+ }
199
+ for (const [key, val] of Object.entries(process.env)) {
200
+ if (key.startsWith(SANDBOX_ALLOW_PREFIX) && !key.startsWith(SANDBOX_DENY_PREFIX) && !SANDBOX_DENY_KEYS.includes(key)) {
201
+ env[key] = val;
202
+ }
203
+ }
204
+ env.HOME = tmpDir;
205
+ env.TMPDIR = tmpDir;
206
+ return env;
207
+ }
208
+ var SANDBOX_ALLOW_KEYS, SANDBOX_ALLOW_PREFIX, SANDBOX_DENY_PREFIX, SANDBOX_DENY_KEYS, SANDBOX_OUTPUT_SIZE_LIMIT, Sandbox;
209
+ var init_sandbox = __esm({
210
+ "src/security/sandbox.ts"() {
211
+ "use strict";
212
+ init_esm_shims();
213
+ init_errors();
214
+ SANDBOX_ALLOW_KEYS = ["PATH", "LANG", "LC_ALL"];
215
+ SANDBOX_ALLOW_PREFIX = "APCORE_";
216
+ SANDBOX_DENY_PREFIX = "APCORE_AUTH_";
217
+ SANDBOX_DENY_KEYS = ["APCORE_AUTH_API_KEY"];
218
+ SANDBOX_OUTPUT_SIZE_LIMIT = 64 * 1024 * 1024;
219
+ Sandbox = class {
220
+ enabled;
221
+ timeoutSeconds;
222
+ constructor(enabled = false, timeoutSeconds = 300) {
223
+ this.enabled = enabled;
224
+ this.timeoutSeconds = timeoutSeconds;
225
+ }
226
+ /**
227
+ * Execute a module, optionally inside a sandboxed subprocess.
228
+ */
229
+ async execute(moduleId, inputData, executor) {
230
+ if (!this.enabled) {
231
+ return executor.execute(moduleId, inputData);
232
+ }
233
+ return this._sandboxedExecute(moduleId, inputData);
234
+ }
235
+ async _sandboxedExecute(moduleId, inputData) {
236
+ const { spawn } = await import("child_process");
237
+ const { tmpdir } = await import("os");
238
+ const { join: join3 } = await import("path");
239
+ const { mkdtempSync, rmSync } = await import("fs");
240
+ const tmpDir = mkdtempSync(join3(tmpdir(), "apcore_sandbox_"));
241
+ const env = buildSandboxEnv(tmpDir);
242
+ const binaryPath = process.argv[1];
243
+ const child = spawn(process.execPath, [binaryPath, "--internal-sandbox-runner", moduleId], {
244
+ env,
245
+ cwd: tmpDir,
246
+ stdio: ["pipe", "pipe", "pipe"]
247
+ });
248
+ let stdout = "";
249
+ let stderr = "";
250
+ let totalBytes = 0;
251
+ let sizeExceeded = false;
252
+ child.stdout.on("data", (chunk) => {
253
+ totalBytes += chunk.length;
254
+ if (totalBytes > SANDBOX_OUTPUT_SIZE_LIMIT) {
255
+ sizeExceeded = true;
256
+ child.kill("SIGKILL");
257
+ return;
258
+ }
259
+ stdout += chunk.toString();
260
+ });
261
+ child.stderr.on("data", (chunk) => {
262
+ stderr += chunk.toString();
263
+ });
264
+ child.stdin.write(JSON.stringify(inputData));
265
+ child.stdin.end();
266
+ return new Promise((resolve2, reject) => {
267
+ const timer = setTimeout(() => {
268
+ child.kill("SIGKILL");
269
+ reject(
270
+ new ModuleExecutionError(
271
+ `Sandbox module '${moduleId}' timed out after ${this.timeoutSeconds}s.`
272
+ )
273
+ );
274
+ }, this.timeoutSeconds * 1e3);
275
+ child.on("close", (code) => {
276
+ clearTimeout(timer);
277
+ try {
278
+ rmSync(tmpDir, { recursive: true, force: true });
279
+ } catch {
280
+ }
281
+ if (sizeExceeded) {
282
+ reject(new ModuleExecutionError(`Sandbox module '${moduleId}' output exceeded 64MiB limit.`));
283
+ return;
284
+ }
285
+ if (code !== 0) {
286
+ reject(new ModuleExecutionError(
287
+ `Sandbox module '${moduleId}' exited with code ${code}.${stderr ? ` stderr: ${stderr}` : ""}`
288
+ ));
289
+ return;
290
+ }
291
+ try {
292
+ resolve2(JSON.parse(stdout));
293
+ } catch {
294
+ reject(new ModuleExecutionError(
295
+ `Sandbox module '${moduleId}' returned non-JSON output: ${stdout.slice(0, 200)}`
296
+ ));
297
+ }
298
+ });
299
+ child.on("error", (err) => {
300
+ clearTimeout(timer);
301
+ reject(new ModuleExecutionError(`Failed to spawn sandbox process: ${err.message}`));
302
+ });
303
+ });
304
+ }
305
+ };
306
+ }
307
+ });
160
308
 
161
309
  // src/ref-resolver.ts
162
- init_esm_shims();
163
- init_errors();
310
+ function resolveRefs(schema, maxDepth = 32, moduleId = "") {
311
+ const cloned = structuredClone(schema);
312
+ const defs = cloned.$defs ?? cloned.definitions ?? {};
313
+ const result = resolveNode(
314
+ cloned,
315
+ defs,
316
+ /* @__PURE__ */ new Set(),
317
+ 0,
318
+ maxDepth,
319
+ moduleId
320
+ );
321
+ delete result.$defs;
322
+ delete result.definitions;
323
+ return result;
324
+ }
325
+ function resolveNode(node, defs, visited, depth, maxDepth, moduleId) {
326
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
327
+ return node;
328
+ }
329
+ const obj = node;
330
+ if ("$ref" in obj) {
331
+ const refPath = obj.$ref;
332
+ if (depth >= maxDepth) {
333
+ process.stderr.write(
334
+ `Error: $ref resolution depth exceeded maximum of ${maxDepth} for module '${moduleId}'.
335
+ `
336
+ );
337
+ process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
338
+ }
339
+ if (visited.has(refPath)) {
340
+ process.stderr.write(
341
+ `Error: Circular $ref detected in schema for module '${moduleId}' at path '${refPath}'.
342
+ `
343
+ );
344
+ process.exit(EXIT_CODES.SCHEMA_CIRCULAR_REF);
345
+ }
346
+ const parts = refPath.split("/");
347
+ const key = parts[parts.length - 1];
348
+ if (!(key in defs)) {
349
+ process.stderr.write(
350
+ `Error: Unresolvable $ref '${refPath}' in schema for module '${moduleId}'.
351
+ `
352
+ );
353
+ process.exit(EXIT_CODES.SCHEMA_VALIDATION_ERROR);
354
+ }
355
+ const newVisited = new Set(visited);
356
+ newVisited.add(refPath);
357
+ return resolveNode(defs[key], defs, newVisited, depth + 1, maxDepth, moduleId);
358
+ }
359
+ if ("allOf" in obj && Array.isArray(obj.allOf)) {
360
+ const merged = {
361
+ properties: {},
362
+ required: []
363
+ };
364
+ if (typeof obj.properties === "object" && obj.properties !== null) {
365
+ Object.assign(merged.properties, obj.properties);
366
+ }
367
+ if (Array.isArray(obj.required)) {
368
+ merged.required.push(...obj.required);
369
+ }
370
+ for (const subSchema of obj.allOf) {
371
+ const resolved = resolveNode(
372
+ subSchema,
373
+ defs,
374
+ visited,
375
+ depth + 1,
376
+ maxDepth,
377
+ moduleId
378
+ );
379
+ if (resolved.properties) {
380
+ Object.assign(
381
+ merged.properties,
382
+ resolved.properties
383
+ );
384
+ }
385
+ if (Array.isArray(resolved.required)) {
386
+ merged.required.push(...resolved.required);
387
+ }
388
+ }
389
+ merged.required = [...new Set(merged.required)];
390
+ for (const [k, v] of Object.entries(obj)) {
391
+ if (k !== "allOf" && !(k in merged)) {
392
+ merged[k] = v;
393
+ }
394
+ }
395
+ return merged;
396
+ }
397
+ for (const keyword of ["anyOf", "oneOf"]) {
398
+ if (keyword in obj && Array.isArray(obj[keyword])) {
399
+ const merged = {
400
+ properties: {},
401
+ required: []
402
+ };
403
+ const allRequiredSets = [];
404
+ for (const subSchema of obj[keyword]) {
405
+ const resolved = resolveNode(
406
+ subSchema,
407
+ defs,
408
+ visited,
409
+ depth + 1,
410
+ maxDepth,
411
+ moduleId
412
+ );
413
+ if (resolved.properties) {
414
+ Object.assign(
415
+ merged.properties,
416
+ resolved.properties
417
+ );
418
+ }
419
+ if (Array.isArray(resolved.required)) {
420
+ allRequiredSets.push(new Set(resolved.required));
421
+ }
422
+ }
423
+ if (allRequiredSets.length > 0) {
424
+ let intersection = allRequiredSets[0];
425
+ for (let i = 1; i < allRequiredSets.length; i++) {
426
+ intersection = new Set(
427
+ [...intersection].filter((x) => allRequiredSets[i].has(x))
428
+ );
429
+ }
430
+ merged.required = [...intersection];
431
+ } else {
432
+ merged.required = [];
433
+ }
434
+ for (const [k, v] of Object.entries(obj)) {
435
+ if (k !== keyword && !(k in merged)) {
436
+ merged[k] = v;
437
+ }
438
+ }
439
+ return merged;
440
+ }
441
+ }
442
+ if ("properties" in obj && typeof obj.properties === "object" && obj.properties !== null) {
443
+ const props = obj.properties;
444
+ for (const [propName, propSchema] of Object.entries(props)) {
445
+ props[propName] = resolveNode(
446
+ propSchema,
447
+ defs,
448
+ visited,
449
+ depth + 1,
450
+ maxDepth,
451
+ moduleId
452
+ );
453
+ }
454
+ }
455
+ return obj;
456
+ }
457
+ var init_ref_resolver = __esm({
458
+ "src/ref-resolver.ts"() {
459
+ "use strict";
460
+ init_esm_shims();
461
+ init_errors();
462
+ }
463
+ });
164
464
 
165
465
  // src/schema-parser.ts
166
- init_esm_shims();
167
- init_errors();
466
+ function mapType(propName, propSchema) {
467
+ const schemaType = propSchema.type;
468
+ if (schemaType === "string" && (propName.endsWith("_file") || propSchema["x-cli-file"] === true)) {
469
+ return "file";
470
+ }
471
+ const typeMap = {
472
+ string: "string",
473
+ integer: "int",
474
+ number: "float",
475
+ boolean: BOOLEAN_FLAG,
476
+ object: "string",
477
+ array: "string"
478
+ };
479
+ if (!schemaType) {
480
+ return "string";
481
+ }
482
+ return typeMap[schemaType] ?? "string";
483
+ }
484
+ function extractHelp(propSchema, maxLength = 1e3) {
485
+ let text = propSchema["x-llm-description"];
486
+ if (!text) {
487
+ text = propSchema.description;
488
+ }
489
+ if (!text) {
490
+ return void 0;
491
+ }
492
+ if (maxLength > 0 && text.length > maxLength) {
493
+ return text.slice(0, maxLength - 3) + "...";
494
+ }
495
+ return text;
496
+ }
497
+ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
498
+ const properties = schema.properties ?? {};
499
+ const requiredList = schema.required ?? [];
500
+ const options = [];
501
+ const flagNames = {};
502
+ for (const [propName, propSchema] of Object.entries(properties)) {
503
+ const flagName = "--" + propName.replace(/_/g, "-");
504
+ if (RESERVED_NAMES.has(propName)) {
505
+ process.stderr.write(
506
+ `Error: Module schema property '${propName}' conflicts with a reserved CLI option name. Rename the property.
507
+ `
508
+ );
509
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
510
+ }
511
+ if (flagName in flagNames) {
512
+ process.stderr.write(
513
+ `Error: Flag name collision: properties '${propName}' and '${flagNames[flagName]}' both map to '${flagName}'.
514
+ `
515
+ );
516
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
517
+ }
518
+ flagNames[flagName] = propName;
519
+ const typeResult = mapType(propName, propSchema);
520
+ const isRequired = requiredList.includes(propName);
521
+ const helpBase = extractHelp(propSchema, maxHelpLength);
522
+ const helpText = isRequired ? (helpBase ? helpBase + " " : "") + "[required]" : helpBase ?? "";
523
+ const defaultValue = propSchema.default;
524
+ if (typeResult === BOOLEAN_FLAG) {
525
+ const flagBase = propName.replace(/_/g, "-");
526
+ const noFlag = `--no-${flagBase}`;
527
+ if (noFlag in flagNames) {
528
+ process.stderr.write(
529
+ `Error: Flag name collision: boolean property '${propName}' auto-generates '${noFlag}' which is already used by property '${flagNames[noFlag]}'.
530
+ `
531
+ );
532
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
533
+ }
534
+ flagNames[noFlag] = propName;
535
+ const defaultVal = propSchema.default ?? false;
536
+ options.push({
537
+ name: propName,
538
+ flags: `--${flagBase}, --no-${flagBase}`,
539
+ description: helpText,
540
+ defaultValue: defaultVal,
541
+ required: isRequired,
542
+ isBooleanFlag: true
543
+ });
544
+ } else if ("enum" in propSchema && Array.isArray(propSchema.enum)) {
545
+ const enumValues = propSchema.enum;
546
+ if (enumValues.length === 0) {
547
+ options.push({
548
+ name: propName,
549
+ flags: `${flagName} <value>`,
550
+ description: helpText,
551
+ defaultValue,
552
+ required: isRequired
553
+ });
554
+ } else {
555
+ const stringValues = enumValues.map(String);
556
+ const enumOriginalTypes = {};
557
+ for (const v of enumValues) {
558
+ if (typeof v === "number" && Number.isInteger(v)) {
559
+ enumOriginalTypes[String(v)] = "int";
560
+ } else if (typeof v === "number") {
561
+ enumOriginalTypes[String(v)] = "float";
562
+ } else if (typeof v === "boolean") {
563
+ enumOriginalTypes[String(v)] = "bool";
564
+ }
565
+ }
566
+ options.push({
567
+ name: propName,
568
+ flags: `${flagName} <value>`,
569
+ description: helpText,
570
+ defaultValue: defaultValue !== void 0 ? String(defaultValue) : void 0,
571
+ required: isRequired,
572
+ choices: stringValues,
573
+ enumOriginalTypes: Object.keys(enumOriginalTypes).length > 0 ? enumOriginalTypes : void 0
574
+ });
575
+ }
576
+ } else {
577
+ let parseArg;
578
+ if (typeResult === "int") {
579
+ parseArg = (v) => {
580
+ const n = parseInt(v, 10);
581
+ if (isNaN(n)) throw new Error(`Invalid integer: ${v}`);
582
+ return n;
583
+ };
584
+ } else if (typeResult === "float") {
585
+ parseArg = (v) => {
586
+ const n = parseFloat(v);
587
+ if (isNaN(n)) throw new Error(`Invalid number: ${v}`);
588
+ return n;
589
+ };
590
+ }
591
+ options.push({
592
+ name: propName,
593
+ flags: `${flagName} <value>`,
594
+ description: helpText,
595
+ defaultValue,
596
+ required: isRequired,
597
+ parseArg
598
+ });
599
+ }
600
+ }
601
+ return options;
602
+ }
603
+ var BOOLEAN_FLAG, RESERVED_NAMES;
604
+ var init_schema_parser = __esm({
605
+ "src/schema-parser.ts"() {
606
+ "use strict";
607
+ init_esm_shims();
608
+ init_errors();
609
+ BOOLEAN_FLAG = /* @__PURE__ */ Symbol("BOOLEAN_FLAG");
610
+ RESERVED_NAMES = /* @__PURE__ */ new Set([
611
+ "input",
612
+ "yes",
613
+ "large_input",
614
+ "format",
615
+ "fields",
616
+ "sandbox",
617
+ "verbose",
618
+ "dry_run",
619
+ "trace",
620
+ "stream",
621
+ "strategy",
622
+ "approval_timeout",
623
+ "approval_token"
624
+ ]);
625
+ }
626
+ });
168
627
 
169
628
  // src/approval.ts
170
- init_esm_shims();
171
- init_errors();
172
629
  import * as readline from "readline";
630
+ function getAnnotation(annotations, key, defaultValue = void 0) {
631
+ if (!annotations || typeof annotations !== "object") return defaultValue;
632
+ const ann = annotations;
633
+ return key in ann ? ann[key] : defaultValue;
634
+ }
635
+ function readTimeoutFromEnv() {
636
+ const raw = process.env.APCORE_CLI_APPROVAL_TIMEOUT;
637
+ if (raw === void 0 || raw === "") return void 0;
638
+ const parsed = parseInt(raw, 10);
639
+ if (!Number.isFinite(parsed) || parsed <= 0) return void 0;
640
+ return parsed;
641
+ }
642
+ async function checkApproval(moduleDef, autoApprove, timeout) {
643
+ const annotations = moduleDef.annotations;
644
+ let requiresApproval;
645
+ if (moduleDef.requiresApproval !== void 0) {
646
+ requiresApproval = moduleDef.requiresApproval;
647
+ } else if (annotations) {
648
+ requiresApproval = getAnnotation(annotations, "requires_approval", false) === true;
649
+ } else {
650
+ return;
651
+ }
652
+ if (!requiresApproval) {
653
+ return;
654
+ }
655
+ const moduleId = moduleDef.id;
656
+ if (autoApprove) {
657
+ return;
658
+ }
659
+ const envVal = process.env.APCORE_CLI_AUTO_APPROVE ?? "";
660
+ if (envVal === "1") {
661
+ return;
662
+ }
663
+ if (envVal !== "" && envVal !== "1") {
664
+ process.stderr.write(
665
+ `Warning: APCORE_CLI_AUTO_APPROVE is set to '${envVal}', expected '1'. Ignoring.
666
+ `
667
+ );
668
+ }
669
+ if (!process.stdin.isTTY) {
670
+ throw new ApprovalDeniedError(
671
+ `Module '${moduleId}' requires approval but no interactive terminal is available. Use --yes or set APCORE_CLI_AUTO_APPROVE=1 to bypass.`
672
+ );
673
+ }
674
+ const effectiveTimeout = timeout ?? readTimeoutFromEnv() ?? 60;
675
+ await promptWithTimeout(moduleDef, effectiveTimeout);
676
+ }
677
+ async function promptWithTimeout(moduleDef, timeout) {
678
+ timeout = Math.max(1, Math.min(timeout, 3600));
679
+ const moduleId = moduleDef.id;
680
+ const annotations = moduleDef.annotations;
681
+ const message = (annotations ? getAnnotation(annotations, "approval_message") : void 0) ?? `Module '${moduleId}' requires approval to execute.`;
682
+ process.stderr.write(message + "\n");
683
+ const rl = readline.createInterface({
684
+ input: process.stdin,
685
+ output: process.stderr
686
+ });
687
+ let timer;
688
+ try {
689
+ const answer = await Promise.race([
690
+ new Promise((resolve2) => {
691
+ rl.question("Proceed? [y/N] ", (ans) => resolve2(ans));
692
+ }),
693
+ new Promise((_, reject) => {
694
+ timer = setTimeout(() => {
695
+ reject(new ApprovalTimeoutError(
696
+ `Approval prompt timed out after ${timeout} seconds.`
697
+ ));
698
+ }, timeout * 1e3);
699
+ })
700
+ ]);
701
+ if (timer) clearTimeout(timer);
702
+ const normalized = answer.trim().toLowerCase();
703
+ if (normalized === "y" || normalized === "yes") {
704
+ return;
705
+ }
706
+ throw new ApprovalDeniedError("Approval denied");
707
+ } catch (err) {
708
+ if (timer) clearTimeout(timer);
709
+ throw err;
710
+ } finally {
711
+ rl.close();
712
+ }
713
+ }
714
+ var CliApprovalHandler;
715
+ var init_approval = __esm({
716
+ "src/approval.ts"() {
717
+ "use strict";
718
+ init_esm_shims();
719
+ init_errors();
720
+ CliApprovalHandler = class {
721
+ autoApprove;
722
+ timeout;
723
+ constructor(autoApprove = false, timeout) {
724
+ this.autoApprove = autoApprove;
725
+ const resolved = timeout ?? readTimeoutFromEnv() ?? 60;
726
+ this.timeout = Math.max(1, Math.min(resolved, 3600));
727
+ }
728
+ async requestApproval(request) {
729
+ const moduleId = request.module_id ?? "unknown";
730
+ if (this.autoApprove) {
731
+ return { status: "approved", approved_by: "auto_approve" };
732
+ }
733
+ const envVal = process.env.APCORE_CLI_AUTO_APPROVE ?? "";
734
+ if (envVal === "1") {
735
+ return { status: "approved", approved_by: "env_auto_approve" };
736
+ }
737
+ if (envVal !== "" && envVal !== "1") {
738
+ process.stderr.write(
739
+ `Warning: APCORE_CLI_AUTO_APPROVE is set to '${envVal}', expected '1'. Ignoring.
740
+ `
741
+ );
742
+ }
743
+ if (!process.stdin.isTTY) {
744
+ return { status: "rejected", reason: "Non-interactive session without --yes" };
745
+ }
746
+ const annotations = request.annotations;
747
+ const extra = annotations?.extra ?? {};
748
+ const message = extra.approval_message ?? `Module '${moduleId}' requires approval to execute.`;
749
+ process.stderr.write(message + "\n");
750
+ try {
751
+ await promptWithTimeout({ id: moduleId }, this.timeout);
752
+ return { status: "approved", approved_by: "tty_user" };
753
+ } catch {
754
+ return { status: "rejected", reason: "User rejected or timed out" };
755
+ }
756
+ }
757
+ async checkApproval(_approvalId) {
758
+ return { status: "rejected", reason: "CLI does not support async approval polling" };
759
+ }
760
+ };
761
+ }
762
+ });
173
763
 
174
764
  // src/output.ts
175
- init_esm_shims();
765
+ import yaml from "js-yaml";
766
+ function csvCellString(value) {
767
+ if (value === null || value === void 0) return "";
768
+ if (typeof value === "object") return JSON.stringify(value);
769
+ return String(value);
770
+ }
176
771
  function resolveFormat(explicitFormat) {
177
772
  if (explicitFormat !== void 0) {
178
773
  return explicitFormat;
179
774
  }
180
775
  return process.stdout.isTTY ? "table" : "json";
181
776
  }
777
+ function truncate(text, maxLength = 80) {
778
+ if (text.length <= maxLength) {
779
+ return text;
780
+ }
781
+ return text.slice(0, maxLength - 3) + "...";
782
+ }
182
783
  function formatTable(headers, rows) {
183
784
  const colWidths = headers.map(
184
785
  (h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
@@ -190,6 +791,137 @@ function formatTable(headers, rows) {
190
791
  );
191
792
  return [headerLine, sep2, ...dataLines].join("\n") + "\n";
192
793
  }
794
+ function formatModuleList(modules, format, filterTags, showDeps = false, exposureFilter) {
795
+ if (format === "table") {
796
+ if (modules.length === 0 && filterTags && filterTags.length > 0) {
797
+ process.stdout.write(
798
+ `No modules found matching tags: ${filterTags.join(", ")}.
799
+ `
800
+ );
801
+ return;
802
+ }
803
+ if (modules.length === 0) {
804
+ process.stdout.write("No modules found.\n");
805
+ return;
806
+ }
807
+ const headers = ["ID", "Description", "Tags"];
808
+ if (showDeps) headers.push("Deps");
809
+ if (exposureFilter) headers.push("Exposure");
810
+ const rows = modules.map((m) => {
811
+ const base = [m.id, truncate(m.description, 80), (m.tags ?? []).join(", ")];
812
+ if (showDeps) {
813
+ const deps = m.dependencies;
814
+ base.push(String(Array.isArray(deps) ? deps.length : 0));
815
+ }
816
+ if (exposureFilter) {
817
+ base.push(exposureFilter.isExposed(m.id ?? "") ? "\u2713" : "\u2014");
818
+ }
819
+ return base;
820
+ });
821
+ process.stdout.write(formatTable(headers, rows));
822
+ } else if (format === "json") {
823
+ const result = modules.map((m) => {
824
+ const entry = {
825
+ id: m.id,
826
+ description: m.description,
827
+ tags: m.tags ?? []
828
+ };
829
+ if (showDeps) {
830
+ const deps = m.dependencies;
831
+ entry.dependency_count = Array.isArray(deps) ? deps.length : 0;
832
+ }
833
+ if (exposureFilter) {
834
+ entry.exposed = exposureFilter.isExposed(m.id ?? "");
835
+ }
836
+ return entry;
837
+ });
838
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
839
+ }
840
+ }
841
+ function annotationsToDict(annotations) {
842
+ if (!annotations) return null;
843
+ if (typeof annotations !== "object" || Array.isArray(annotations)) return null;
844
+ const result = {};
845
+ for (const [k, v] of Object.entries(annotations)) {
846
+ if (v !== null && v !== void 0 && v !== false && v !== 0 && !(Array.isArray(v) && v.length === 0)) {
847
+ result[k] = v;
848
+ }
849
+ }
850
+ return Object.keys(result).length > 0 ? result : null;
851
+ }
852
+ function formatModuleDetail(moduleDef, format) {
853
+ if (format === "table") {
854
+ process.stdout.write(`
855
+ Module: ${moduleDef.id}
856
+ `);
857
+ process.stdout.write(`
858
+ Description:
859
+ ${moduleDef.description}
860
+ `);
861
+ if (moduleDef.inputSchema && Object.keys(moduleDef.inputSchema).length > 0) {
862
+ process.stdout.write("\nInput Schema:\n");
863
+ process.stdout.write(JSON.stringify(moduleDef.inputSchema, null, 2) + "\n");
864
+ }
865
+ if (moduleDef.outputSchema && Object.keys(moduleDef.outputSchema).length > 0) {
866
+ process.stdout.write("\nOutput Schema:\n");
867
+ process.stdout.write(JSON.stringify(moduleDef.outputSchema, null, 2) + "\n");
868
+ }
869
+ const annDict = annotationsToDict(
870
+ moduleDef.annotations
871
+ );
872
+ if (annDict) {
873
+ process.stdout.write("\nAnnotations:\n");
874
+ for (const [k, v] of Object.entries(annDict)) {
875
+ process.stdout.write(` ${k}: ${v}
876
+ `);
877
+ }
878
+ }
879
+ const metadata = moduleDef.metadata;
880
+ if (metadata) {
881
+ const xFields = {};
882
+ for (const [k, v] of Object.entries(metadata)) {
883
+ if (k.startsWith("x-") || k.startsWith("x_")) {
884
+ xFields[k] = v;
885
+ }
886
+ }
887
+ if (Object.keys(xFields).length > 0) {
888
+ process.stdout.write("\nExtension Metadata:\n");
889
+ for (const [k, v] of Object.entries(xFields)) {
890
+ process.stdout.write(` ${k}: ${v}
891
+ `);
892
+ }
893
+ }
894
+ }
895
+ const tags = moduleDef.tags ?? [];
896
+ if (tags.length > 0) {
897
+ process.stdout.write(`
898
+ Tags: ${tags.join(", ")}
899
+ `);
900
+ }
901
+ } else if (format === "json") {
902
+ const result = {
903
+ id: moduleDef.id,
904
+ description: moduleDef.description
905
+ };
906
+ if (moduleDef.inputSchema) result.input_schema = moduleDef.inputSchema;
907
+ if (moduleDef.outputSchema) result.output_schema = moduleDef.outputSchema;
908
+ const annDict = annotationsToDict(
909
+ moduleDef.annotations
910
+ );
911
+ if (annDict) result.annotations = annDict;
912
+ const tags = moduleDef.tags ?? [];
913
+ if (tags.length > 0) result.tags = tags;
914
+ const metadata = moduleDef.metadata;
915
+ if (metadata) {
916
+ for (const [k, v] of Object.entries(metadata)) {
917
+ if (k.startsWith("x-") || k.startsWith("x_")) {
918
+ result[k] = v;
919
+ }
920
+ }
921
+ }
922
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
923
+ }
924
+ }
193
925
  function selectFields(result, fields) {
194
926
  const selected = {};
195
927
  for (const f of fields.split(",")) {
@@ -221,42 +953,21 @@ function formatExecResult(result, format, fields) {
221
953
  const obj = effective_result;
222
954
  const keys = Object.keys(obj);
223
955
  const header = keys.map(escapeCsvField).join(",");
224
- const row = keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
956
+ const row = keys.map((k) => escapeCsvField(csvCellString(obj[k]))).join(",");
225
957
  process.stdout.write(header + "\n" + row + "\n");
226
958
  } else if (Array.isArray(effective_result) && effective_result.length > 0 && typeof effective_result[0] === "object") {
227
959
  const keys = Object.keys(effective_result[0]);
228
960
  const header = keys.map(escapeCsvField).join(",");
229
961
  const rows = effective_result.map((item) => {
230
962
  const obj = item;
231
- return keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
963
+ return keys.map((k) => escapeCsvField(csvCellString(obj[k]))).join(",");
232
964
  });
233
965
  process.stdout.write(header + "\n" + rows.join("\n") + "\n");
234
966
  } else {
235
967
  process.stdout.write(JSON.stringify(effective_result) + "\n");
236
968
  }
237
969
  } else if (effective === "yaml") {
238
- if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
239
- const obj = effective_result;
240
- const lines = Object.entries(obj).map(([k, v]) => {
241
- if (v === null || v === void 0) return `${k}: null`;
242
- if (typeof v === "object") return `${k}: ${JSON.stringify(v)}`;
243
- return `${k}: ${v}`;
244
- });
245
- process.stdout.write(lines.join("\n") + "\n");
246
- } else if (Array.isArray(effective_result)) {
247
- for (const item of effective_result) {
248
- if (typeof item === "object" && item !== null) {
249
- const obj = item;
250
- const lines = Object.entries(obj).map(([k, v]) => ` ${k}: ${v}`);
251
- process.stdout.write("- " + lines.join("\n ") + "\n");
252
- } else {
253
- process.stdout.write(`- ${item}
254
- `);
255
- }
256
- }
257
- } else {
258
- process.stdout.write(String(effective_result) + "\n");
259
- }
970
+ process.stdout.write(yaml.dump(effective_result, { lineWidth: -1 }));
260
971
  } else if (effective === "jsonl") {
261
972
  if (Array.isArray(effective_result)) {
262
973
  for (const item of effective_result) {
@@ -279,7 +990,7 @@ function formatExecResult(result, format, fields) {
279
990
  }
280
991
  }
281
992
  function escapeCsvField(value) {
282
- if (value.includes(",") || value.includes('"') || value.includes("\n")) {
993
+ if (value.includes(",") || value.includes('"') || value.includes("\n") || value.includes("\r")) {
283
994
  return '"' + value.replace(/"/g, '""') + '"';
284
995
  }
285
996
  return value;
@@ -289,7 +1000,9 @@ function formatPreflightResult(result, format) {
289
1000
  if (resolved === "json" || !process.stdout.isTTY) {
290
1001
  const payload = {
291
1002
  valid: result.valid,
292
- requires_approval: result.requires_approval,
1003
+ // JSON output key stays snake_case for cross-language CLI contract;
1004
+ // runtime read uses camelCase to match apcore-js PreflightResult.
1005
+ requires_approval: result.requiresApproval,
293
1006
  checks: result.checks.map((c) => {
294
1007
  const entry = { check: c.check, passed: c.passed };
295
1008
  if (c.error !== void 0 && c.error !== null) {
@@ -340,26 +1053,30 @@ Result: ${tag} (${errors} error(s), ${warnings} warning(s))
340
1053
  }
341
1054
  function firstFailedExitCode(result) {
342
1055
  const checkToExit = {
343
- module_id: 2,
344
- module_lookup: 44,
345
- call_chain: 1,
346
- acl: 77,
347
- schema: 45,
348
- approval: 46,
349
- module_preflight: 1
1056
+ module_id: EXIT_CODES.INVALID_CLI_INPUT,
1057
+ module_lookup: EXIT_CODES.MODULE_NOT_FOUND,
1058
+ call_chain: EXIT_CODES.MODULE_EXECUTE_ERROR,
1059
+ acl: EXIT_CODES.ACL_DENIED,
1060
+ schema: EXIT_CODES.SCHEMA_VALIDATION_ERROR,
1061
+ approval: EXIT_CODES.APPROVAL_DENIED,
1062
+ module_preflight: EXIT_CODES.MODULE_EXECUTE_ERROR
350
1063
  };
351
1064
  for (const check of result.checks) {
352
1065
  if (!check.passed) {
353
- return checkToExit[check.check] ?? 1;
1066
+ return checkToExit[check.check] ?? EXIT_CODES.MODULE_EXECUTE_ERROR;
354
1067
  }
355
1068
  }
356
- return 1;
1069
+ return EXIT_CODES.MODULE_EXECUTE_ERROR;
357
1070
  }
1071
+ var init_output = __esm({
1072
+ "src/output.ts"() {
1073
+ "use strict";
1074
+ init_esm_shims();
1075
+ init_errors();
1076
+ }
1077
+ });
358
1078
 
359
1079
  // src/logger.ts
360
- init_esm_shims();
361
- var LEVELS = { DEBUG: 0, INFO: 1, WARNING: 2, ERROR: 3 };
362
- var currentLevel = "WARNING";
363
1080
  function setLogLevel(level) {
364
1081
  const upper = level.toUpperCase();
365
1082
  if (upper in LEVELS) {
@@ -369,44 +1086,39 @@ function setLogLevel(level) {
369
1086
  function shouldLog(level) {
370
1087
  return LEVELS[level] >= LEVELS[currentLevel];
371
1088
  }
372
- function debug(message) {
373
- if (shouldLog("DEBUG")) process.stderr.write(`DEBUG: ${message}
1089
+ function warn(message) {
1090
+ if (shouldLog("WARNING")) process.stderr.write(`WARNING: ${message}
374
1091
  `);
375
1092
  }
1093
+ var LEVELS, currentLevel;
1094
+ var init_logger = __esm({
1095
+ "src/logger.ts"() {
1096
+ "use strict";
1097
+ init_esm_shims();
1098
+ LEVELS = { DEBUG: 0, INFO: 1, WARNING: 2, ERROR: 3 };
1099
+ currentLevel = "WARNING";
1100
+ }
1101
+ });
376
1102
 
377
1103
  // src/init-cmd.ts
378
- init_esm_shims();
379
1104
  import * as fs from "fs";
380
1105
  import * as path2 from "path";
381
- var DECORATOR_TEMPLATE = `import { module } from "apcore-js";
382
- import { Type } from "@sinclair/typebox";
383
-
384
- export const {varName} = module({
385
- id: "{moduleId}",
386
- description: "{description}",
387
- inputSchema: Type.Object({}),
388
- outputSchema: Type.Object({ status: Type.String() }),
389
- execute: (_inputs) => {
390
- // TODO: implement
391
- return { status: "ok" };
392
- },
393
- });
394
- `;
395
- var CONVENTION_TEMPLATE = `/**
396
- * {description}
397
- */
398
- {cliGroupLine}
399
- export function {funcName}(): Record<string, unknown> {
400
- // TODO: implement
401
- return { status: "ok" };
1106
+ function runFsOp(op, targetPath, fn, partial) {
1107
+ try {
1108
+ return fn();
1109
+ } catch (err) {
1110
+ const msg = err instanceof Error ? err.message : String(err);
1111
+ process.stderr.write(`Error: failed to ${op} ${targetPath}: ${msg}
1112
+ `);
1113
+ if (partial && partial.length > 0) {
1114
+ process.stderr.write(
1115
+ ` Partial scaffold left on disk \u2014 you may want to remove: ${partial.join(", ")}
1116
+ `
1117
+ );
1118
+ }
1119
+ process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
1120
+ }
402
1121
  }
403
- `;
404
- var BINDING_TEMPLATE = `bindings:
405
- - module_id: "{moduleId}"
406
- target: "{target}"
407
- description: "{description}"
408
- auto_schema: true
409
- `;
410
1122
  function renderTemplate(template, context) {
411
1123
  let result = template;
412
1124
  for (const [key, value] of Object.entries(context)) {
@@ -450,7 +1162,7 @@ function registerInitCommand(cli) {
450
1162
  });
451
1163
  }
452
1164
  function createDecoratorModule(moduleId, _prefix, funcName, description, outputDir) {
453
- fs.mkdirSync(outputDir, { recursive: true });
1165
+ runFsOp("create directory", outputDir, () => fs.mkdirSync(outputDir, { recursive: true }));
454
1166
  const filename = moduleId.replace(/\./g, "_") + ".ts";
455
1167
  const filepath = path2.join(outputDir, filename);
456
1168
  const varName = funcName + "Module";
@@ -460,14 +1172,14 @@ function createDecoratorModule(moduleId, _prefix, funcName, description, outputD
460
1172
  funcName,
461
1173
  description
462
1174
  });
463
- fs.writeFileSync(filepath, content);
1175
+ runFsOp("write file", filepath, () => fs.writeFileSync(filepath, content));
464
1176
  process.stdout.write(`Created ${filepath}
465
1177
  `);
466
1178
  }
467
1179
  function createConventionModule(moduleId, prefix, funcName, description, outputDir) {
468
1180
  const prefixParts = prefix.split(".");
469
1181
  const dirPath = prefixParts.length > 1 ? path2.join(outputDir, ...prefixParts.slice(0, -1)) : outputDir;
470
- fs.mkdirSync(dirPath, { recursive: true });
1182
+ runFsOp("create directory", dirPath, () => fs.mkdirSync(dirPath, { recursive: true }));
471
1183
  let filename;
472
1184
  if (prefixParts.length > 1) {
473
1185
  filename = prefixParts[prefixParts.length - 1] + ".ts";
@@ -485,12 +1197,13 @@ function createConventionModule(moduleId, prefix, funcName, description, outputD
485
1197
  description,
486
1198
  cliGroupLine
487
1199
  });
488
- fs.writeFileSync(filepath, content);
1200
+ runFsOp("write file", filepath, () => fs.writeFileSync(filepath, content));
489
1201
  process.stdout.write(`Created ${filepath}
490
1202
  `);
491
1203
  }
492
1204
  function createBindingModule(moduleId, prefix, funcName, description, outputDir) {
493
- fs.mkdirSync(outputDir, { recursive: true });
1205
+ const partial = [];
1206
+ runFsOp("create directory", outputDir, () => fs.mkdirSync(outputDir, { recursive: true }));
494
1207
  const yamlFile = path2.join(outputDir, moduleId.replace(/\./g, "_") + ".binding.yaml");
495
1208
  const target = `commands.${prefix}:${funcName}`;
496
1209
  const yamlContent = renderTemplate(BINDING_TEMPLATE, {
@@ -498,11 +1211,12 @@ function createBindingModule(moduleId, prefix, funcName, description, outputDir)
498
1211
  target,
499
1212
  description
500
1213
  });
501
- fs.writeFileSync(yamlFile, yamlContent);
1214
+ runFsOp("write file", yamlFile, () => fs.writeFileSync(yamlFile, yamlContent));
1215
+ partial.push(yamlFile);
502
1216
  process.stdout.write(`Created ${yamlFile}
503
1217
  `);
504
1218
  const baseSrc = "commands";
505
- fs.mkdirSync(baseSrc, { recursive: true });
1219
+ runFsOp("create directory", baseSrc, () => fs.mkdirSync(baseSrc, { recursive: true }), partial);
506
1220
  const srcFile = path2.join(baseSrc, prefix.replace(/\./g, "_") + ".ts");
507
1221
  if (!fs.existsSync(srcFile)) {
508
1222
  const srcContent = `export function ${funcName}(): Record<string, unknown> {
@@ -511,69 +1225,421 @@ function createBindingModule(moduleId, prefix, funcName, description, outputDir)
511
1225
  return { status: "ok" };
512
1226
  }
513
1227
  `;
514
- fs.writeFileSync(srcFile, srcContent);
1228
+ runFsOp("write file", srcFile, () => fs.writeFileSync(srcFile, srcContent), partial);
515
1229
  process.stdout.write(`Created ${srcFile}
516
1230
  `);
517
1231
  }
518
1232
  }
1233
+ var DECORATOR_TEMPLATE, CONVENTION_TEMPLATE, BINDING_TEMPLATE;
1234
+ var init_init_cmd = __esm({
1235
+ "src/init-cmd.ts"() {
1236
+ "use strict";
1237
+ init_esm_shims();
1238
+ init_errors();
1239
+ DECORATOR_TEMPLATE = `import { module } from "apcore-js";
1240
+ import { Type } from "@sinclair/typebox";
1241
+
1242
+ export const {varName} = module({
1243
+ id: "{moduleId}",
1244
+ description: "{description}",
1245
+ inputSchema: Type.Object({}),
1246
+ outputSchema: Type.Object({ status: Type.String() }),
1247
+ execute: (_inputs) => {
1248
+ // TODO: implement
1249
+ return { status: "ok" };
1250
+ },
1251
+ });
1252
+ `;
1253
+ CONVENTION_TEMPLATE = `/**
1254
+ * {description}
1255
+ */
1256
+ {cliGroupLine}
1257
+ export function {funcName}(): Record<string, unknown> {
1258
+ // TODO: implement
1259
+ return { status: "ok" };
1260
+ }
1261
+ `;
1262
+ BINDING_TEMPLATE = `spec_version: "1.0"
1263
+ bindings:
1264
+ - module_id: "{moduleId}"
1265
+ target: "{target}"
1266
+ description: "{description}"
1267
+ auto_schema: true
1268
+ `;
1269
+ }
1270
+ });
519
1271
 
520
1272
  // src/display-helpers.ts
521
- init_esm_shims();
1273
+ function getDisplay(descriptor) {
1274
+ const metadata = descriptor.metadata ?? {};
1275
+ const display = metadata.display;
1276
+ if (display && typeof display === "object" && !Array.isArray(display)) {
1277
+ return display;
1278
+ }
1279
+ const overlay = lookupBindingDisplay(descriptor.id);
1280
+ return overlay ?? {};
1281
+ }
1282
+ var init_display_helpers = __esm({
1283
+ "src/display-helpers.ts"() {
1284
+ "use strict";
1285
+ init_esm_shims();
1286
+ init_main();
1287
+ }
1288
+ });
522
1289
 
523
1290
  // src/config.ts
524
- init_esm_shims();
525
1291
  import * as fs2 from "fs";
526
- import yaml from "js-yaml";
527
- var NAMESPACE_TO_LEGACY = {
528
- "apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
529
- "apcore-cli.auto_approve": "cli.auto_approve",
530
- "apcore-cli.help_text_max_length": "cli.help_text_max_length",
531
- "apcore-cli.logging_level": "logging.level"
532
- };
533
- var LEGACY_TO_NAMESPACE = Object.fromEntries(
534
- Object.entries(NAMESPACE_TO_LEGACY).map(([k, v]) => [v, k])
535
- );
1292
+ import { createRequire } from "module";
1293
+ import yaml2 from "js-yaml";
536
1294
  function registerConfigNamespace() {
537
1295
  try {
538
- const { Config } = __require("apcore-js");
1296
+ const nodeRequire = createRequire(import.meta.url);
1297
+ const { Config } = nodeRequire("apcore-js");
539
1298
  if (typeof Config?.registerNamespace === "function") {
540
1299
  Config.registerNamespace({
541
1300
  name: "apcore-cli",
542
1301
  envPrefix: "APCORE_CLI",
543
- defaults: {
544
- stdin_buffer_limit: 10485760,
545
- auto_approve: false,
546
- help_text_max_length: 1e3,
547
- logging_level: "WARNING",
548
- approval_timeout: 60,
549
- strategy: "standard",
550
- group_depth: 1
551
- }
1302
+ defaults: NAMESPACE_DEFAULTS
552
1303
  });
553
1304
  }
554
1305
  } catch {
555
1306
  }
556
1307
  }
1308
+ var DEFAULTS, NAMESPACE_DEFAULTS, NAMESPACE_TO_LEGACY, LEGACY_TO_NAMESPACE, ConfigResolver;
1309
+ var init_config = __esm({
1310
+ "src/config.ts"() {
1311
+ "use strict";
1312
+ init_esm_shims();
1313
+ init_logger();
1314
+ DEFAULTS = {
1315
+ "extensions.root": "./extensions",
1316
+ "logging.level": "WARNING",
1317
+ "cli.help_text_max_length": 1e3,
1318
+ // FE-11 config keys
1319
+ "cli.approval_timeout": 60,
1320
+ "cli.strategy": "standard",
1321
+ "cli.group_depth": 1,
1322
+ // Exposure filtering (FE-12)
1323
+ "expose.mode": "all",
1324
+ "expose.include": [],
1325
+ "expose.exclude": []
1326
+ // Builtin group visibility (FE-13) — apcli.* keys are NOT in DEFAULTS.
1327
+ // The runtime reads them via resolveObject('apcli') (raw yaml walk) and
1328
+ // does not use the flat-key resolve() path. Python and Rust have no such
1329
+ // entries either. (D11-008 cleanup)
1330
+ };
1331
+ NAMESPACE_DEFAULTS = {
1332
+ stdin_buffer_limit: 10485760,
1333
+ auto_approve: false,
1334
+ help_text_max_length: 1e3,
1335
+ logging_level: "WARNING",
1336
+ approval_timeout: 60,
1337
+ strategy: "standard",
1338
+ group_depth: 1,
1339
+ // FE-13 — builtin group visibility configuration
1340
+ apcli: {
1341
+ mode: null,
1342
+ include: [],
1343
+ exclude: [],
1344
+ disable_env: false
1345
+ }
1346
+ };
1347
+ NAMESPACE_TO_LEGACY = {
1348
+ "apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
1349
+ "apcore-cli.auto_approve": "cli.auto_approve",
1350
+ "apcore-cli.help_text_max_length": "cli.help_text_max_length",
1351
+ "apcore-cli.logging_level": "logging.level"
1352
+ };
1353
+ LEGACY_TO_NAMESPACE = Object.fromEntries(
1354
+ Object.entries(NAMESPACE_TO_LEGACY).map(([k, v]) => [v, k])
1355
+ );
1356
+ ConfigResolver = class {
1357
+ cliFlags;
1358
+ configPath;
1359
+ fileCache = null;
1360
+ fileCacheLoaded = false;
1361
+ /**
1362
+ * Raw parsed yaml root (pre-flatten). Populated alongside `fileCache`
1363
+ * on load. Used by `resolveObject()` to walk nested paths without
1364
+ * invoking `flattenDict` — see FE-13 spec §4.8 M1 note.
1365
+ * `null` when no config file is present or parsing fails.
1366
+ */
1367
+ _rawConfig = null;
1368
+ constructor(cliFlags, configPath) {
1369
+ this.cliFlags = cliFlags ?? {};
1370
+ this.configPath = configPath ?? "apcore.yaml";
1371
+ }
1372
+ /**
1373
+ * Resolve a single configuration key across all four tiers.
1374
+ */
1375
+ resolve(key, cliFlag, envVar) {
1376
+ if (cliFlag !== void 0 && cliFlag in this.cliFlags) {
1377
+ const value = this.cliFlags[cliFlag];
1378
+ if (value !== null && value !== void 0) {
1379
+ return value;
1380
+ }
1381
+ }
1382
+ if (envVar) {
1383
+ const envValue = process.env[envVar];
1384
+ if (envValue !== void 0 && envValue !== "") {
1385
+ return envValue;
1386
+ }
1387
+ }
1388
+ const fileValue = this.resolveFromFile(key);
1389
+ if (fileValue !== void 0) {
1390
+ return fileValue;
1391
+ }
1392
+ const altKey = NAMESPACE_TO_LEGACY[key] ?? LEGACY_TO_NAMESPACE[key];
1393
+ if (altKey) {
1394
+ const altFileValue = this.resolveFromFile(altKey);
1395
+ if (altFileValue !== void 0) {
1396
+ return altFileValue;
1397
+ }
1398
+ }
1399
+ return DEFAULTS[key];
1400
+ }
1401
+ /**
1402
+ * Load a value from the config file using a dot-separated key path.
1403
+ */
1404
+ resolveFromFile(key) {
1405
+ if (!this.fileCacheLoaded) {
1406
+ this.fileCache = this.loadConfigFile();
1407
+ this.fileCacheLoaded = true;
1408
+ }
1409
+ if (this.fileCache === null) {
1410
+ return void 0;
1411
+ }
1412
+ return this.fileCache[key];
1413
+ }
1414
+ /**
1415
+ * Resolve a configuration key to its raw nested value (FE-13).
1416
+ *
1417
+ * Unlike `resolve()`, this method does NOT flatten the yaml tree — it
1418
+ * walks the dot-separated path directly against the parsed yaml root.
1419
+ * This lets callers retrieve non-leaf structures (booleans, arrays,
1420
+ * objects) such as the `apcli` visibility config, which is naturally
1421
+ * shaped as a nested object in apcore.yaml.
1422
+ *
1423
+ * Semantics:
1424
+ * - Returns `null` when no config file is loaded or when the path is
1425
+ * not present / descends into a non-object node (including arrays).
1426
+ * - Returns the raw value (boolean / array / object / scalar) when the
1427
+ * full path resolves to a leaf or intermediate node.
1428
+ *
1429
+ * Intentionally DOES NOT consult DEFAULTS, env vars, or CLI flags — it is
1430
+ * strictly a yaml-tree accessor. Scalar `resolve()` semantics are
1431
+ * unaffected.
1432
+ */
1433
+ resolveObject(key) {
1434
+ if (!this.fileCacheLoaded) {
1435
+ this.fileCache = this.loadConfigFile();
1436
+ this.fileCacheLoaded = true;
1437
+ }
1438
+ if (this._rawConfig == null) {
1439
+ return null;
1440
+ }
1441
+ const parts = key.split(".");
1442
+ let cur = this._rawConfig;
1443
+ for (const p of parts) {
1444
+ if (cur != null && typeof cur === "object" && !Array.isArray(cur) && p in cur) {
1445
+ cur = cur[p];
1446
+ } else {
1447
+ return null;
1448
+ }
1449
+ }
1450
+ return cur;
1451
+ }
1452
+ /**
1453
+ * Load and flatten a YAML config file.
1454
+ */
1455
+ loadConfigFile() {
1456
+ let content;
1457
+ try {
1458
+ content = fs2.readFileSync(this.configPath, "utf-8");
1459
+ } catch (err) {
1460
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
1461
+ return null;
1462
+ }
1463
+ warn(
1464
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1465
+ );
1466
+ return null;
1467
+ }
1468
+ let parsed;
1469
+ try {
1470
+ parsed = yaml2.load(content);
1471
+ } catch {
1472
+ warn(
1473
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1474
+ );
1475
+ return null;
1476
+ }
1477
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1478
+ warn(
1479
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1480
+ );
1481
+ return null;
1482
+ }
1483
+ this._rawConfig = parsed;
1484
+ return this.flattenDict(parsed);
1485
+ }
1486
+ /**
1487
+ * Flatten nested dict to dot-notation keys.
1488
+ */
1489
+ flattenDict(d, prefix = "") {
1490
+ const result = {};
1491
+ for (const [key, value] of Object.entries(d)) {
1492
+ const fullKey = prefix ? `${prefix}.${key}` : key;
1493
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1494
+ Object.assign(
1495
+ result,
1496
+ this.flattenDict(value, fullKey)
1497
+ );
1498
+ } else {
1499
+ result[fullKey] = value;
1500
+ }
1501
+ }
1502
+ return result;
1503
+ }
1504
+ };
1505
+ }
1506
+ });
557
1507
 
558
1508
  // src/shell.ts
559
- init_esm_shims();
560
- init_errors();
561
- import { readFileSync as readFileSync2 } from "fs";
562
- import { fileURLToPath as fileURLToPath2 } from "url";
563
- import * as path3 from "path";
564
1509
  import { spawnSync } from "child_process";
565
1510
  import { Command, Help, Option } from "commander";
566
- var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
567
- var SHELL_VERSION = "0.0.0";
568
- try {
569
- const pkg = JSON.parse(readFileSync2(path3.resolve(__dirname2, "../package.json"), "utf-8"));
570
- SHELL_VERSION = pkg.version;
571
- } catch {
1511
+ function makeFunctionName(progName) {
1512
+ return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
1513
+ }
1514
+ function shellQuote(s) {
1515
+ return "'" + s.replace(/'/g, "'\\''") + "'";
1516
+ }
1517
+ function enumerateApcliSubcommands(apcliGroup) {
1518
+ if (!apcliGroup) return [];
1519
+ return apcliGroup.commands.filter((c) => !isCmdHidden(c)).map((c) => c.name());
1520
+ }
1521
+ function enumerateRootCommands(program) {
1522
+ return program.commands.filter((c) => !isCmdHidden(c)).map((c) => c.name());
1523
+ }
1524
+ function findApcliGroup(program) {
1525
+ return program.commands.find((c) => c.name() === "apcli");
1526
+ }
1527
+ function isCmdHidden(cmd) {
1528
+ const withHiddenFn = cmd;
1529
+ const withHiddenField = cmd;
1530
+ if (typeof withHiddenFn.hidden === "function") return !!withHiddenFn.hidden();
1531
+ return !!withHiddenField._hidden;
1532
+ }
1533
+ function generateBashCompletion(progName, program) {
1534
+ const fn = makeFunctionName(progName);
1535
+ const quoted = shellQuote(progName);
1536
+ const apcliGroup = program ? findApcliGroup(program) : void 0;
1537
+ const apcliVisible = apcliGroup !== void 0 && !isCmdHidden(apcliGroup);
1538
+ const rootCmds = program ? enumerateRootCommands(program).filter(
1539
+ (n) => n !== "apcli" || apcliVisible
1540
+ ) : [];
1541
+ const apcliCmds = apcliVisible ? enumerateApcliSubcommands(apcliGroup) : [];
1542
+ const rootOpts = rootCmds.join(" ");
1543
+ const apcliOpts = apcliCmds.join(" ");
1544
+ let body = `${fn}() {
1545
+ local cur prev opts
1546
+ COMPREPLY=()
1547
+ cur="\${COMP_WORDS[COMP_CWORD]}"
1548
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
1549
+
1550
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
1551
+ opts="${rootOpts}"
1552
+ COMPREPLY=( $(compgen -W "\${opts}" -- \${cur}) )
1553
+ return 0
1554
+ fi
1555
+ `;
1556
+ if (apcliVisible) {
1557
+ body += `
1558
+ if [[ \${COMP_CWORD} -eq 2 ]]; then
1559
+ if [[ "\${COMP_WORDS[1]}" == "apcli" ]]; then
1560
+ local apcli_cmds="${apcliOpts}"
1561
+ COMPREPLY=( $(compgen -W "\${apcli_cmds}" -- \${cur}) )
1562
+ return 0
1563
+ fi
1564
+ fi
1565
+ `;
1566
+ }
1567
+ body += `}
1568
+ complete -F ${fn} ${quoted}
1569
+ `;
1570
+ return body;
1571
+ }
1572
+ function generateZshCompletion(progName, program) {
1573
+ const fn = makeFunctionName(progName);
1574
+ const quoted = shellQuote(progName);
1575
+ const apcliGroup = program ? findApcliGroup(program) : void 0;
1576
+ const apcliVisible = apcliGroup !== void 0 && !isCmdHidden(apcliGroup);
1577
+ const rootCmds = program ? enumerateRootCommands(program).filter(
1578
+ (n) => n !== "apcli" || apcliVisible
1579
+ ) : [];
1580
+ const apcliCmds = apcliVisible ? enumerateApcliSubcommands(apcliGroup) : [];
1581
+ const rootEntries = rootCmds.map((n) => ` '${n}:${n}'`).join("\n");
1582
+ const apcliEntries = apcliCmds.map((n) => ` '${n}:${n}'`).join("\n");
1583
+ return `#compdef ${progName}
1584
+
1585
+ ${fn}() {
1586
+ local -a commands
1587
+ commands=(
1588
+ ` + (rootEntries ? rootEntries + "\n" : "") + ` )
1589
+ local -a apcli_cmds
1590
+ apcli_cmds=(
1591
+ ` + (apcliEntries ? apcliEntries + "\n" : "") + ` )
1592
+
1593
+ _arguments -C \\
1594
+ '1:command:->command' \\
1595
+ '*::arg:->args'
1596
+
1597
+ case "$state" in
1598
+ command)
1599
+ _describe -t commands '${progName} commands' commands
1600
+ ;;
1601
+ args)
1602
+ case "\${words[1]}" in
1603
+ apcli)
1604
+ _describe -t apcli_cmds '${progName} apcli commands' apcli_cmds
1605
+ ;;
1606
+ esac
1607
+ ;;
1608
+ esac
1609
+ }
1610
+
1611
+ compdef ${fn} ${quoted}
1612
+ `;
1613
+ }
1614
+ function generateFishCompletion(progName, program) {
1615
+ const quoted = shellQuote(progName);
1616
+ const apcliGroup = program ? findApcliGroup(program) : void 0;
1617
+ const apcliVisible = apcliGroup !== void 0 && !isCmdHidden(apcliGroup);
1618
+ const rootCmds = program ? enumerateRootCommands(program).filter(
1619
+ (n) => n !== "apcli" || apcliVisible
1620
+ ) : [];
1621
+ const apcliCmds = apcliVisible ? enumerateApcliSubcommands(apcliGroup) : [];
1622
+ const lines = [];
1623
+ lines.push(`# Fish completions for ${progName}`);
1624
+ for (const name of rootCmds) {
1625
+ lines.push(
1626
+ `complete -c ${quoted} -n "__fish_use_subcommand" -a ${name} -d "${name}"`
1627
+ );
1628
+ }
1629
+ if (apcliVisible && apcliCmds.length > 0) {
1630
+ lines.push("");
1631
+ for (const name of apcliCmds) {
1632
+ lines.push(
1633
+ `complete -c ${quoted} -n "__fish_seen_subcommand_from apcli" -a ${name} -d "${name}"`
1634
+ );
1635
+ }
1636
+ }
1637
+ return lines.join("\n") + "\n";
572
1638
  }
573
1639
  function roffEscape(s) {
574
1640
  return s.replace(/\\/g, "\\\\").replace(/-/g, "\\-").replace(/'/g, "\\(aq");
575
1641
  }
576
- function buildProgramManPage(program, progName, version, description, docsUrl) {
1642
+ function buildProgramManPage(program, progName, version, description, docsUrl2) {
577
1643
  const help = new Help();
578
1644
  const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
579
1645
  const s = [];
@@ -663,18 +1729,18 @@ ${meaning}`);
663
1729
  }
664
1730
  s.push(".SH SEE ALSO");
665
1731
  s.push(`\\fB${progName} \\-\\-help \\-\\-verbose\\fR for full option list.`);
666
- if (docsUrl) {
1732
+ if (docsUrl2) {
667
1733
  s.push(`.PP
668
- Full documentation at \\fI${roffEscape(docsUrl)}\\fR`);
1734
+ Full documentation at \\fI${roffEscape(docsUrl2)}\\fR`);
669
1735
  }
670
1736
  return s.join("\n");
671
1737
  }
672
- function configureManHelp(program, progName, version, description, docsUrl) {
1738
+ function configureManHelp(program, progName, version, description, docsUrl2) {
673
1739
  const manOpt = new Option("--man", "Output man page in roff format (use with --help)").hideHelp();
674
1740
  program.addOption(manOpt);
675
1741
  program.addHelpText("beforeAll", () => {
676
1742
  if (program.opts().man) {
677
- const roff = buildProgramManPage(program, progName, version, description, docsUrl) + "\n";
1743
+ const roff = buildProgramManPage(program, progName, version, description, docsUrl2) + "\n";
678
1744
  if (process.stdout.isTTY) {
679
1745
  const pagers = [
680
1746
  { cmd: "mandoc", args: ["-a"] },
@@ -710,119 +1776,777 @@ function configureManHelp(program, progName, version, description, docsUrl) {
710
1776
  return "";
711
1777
  });
712
1778
  }
713
-
714
- // src/discovery.ts
715
- init_esm_shims();
716
- init_errors();
717
- import { Command as Command2, Option as Option2 } from "commander";
718
- function registerValidateCommand(cli, registry, executor) {
719
- const validateCmd = new Command2("validate").description("Run preflight checks without executing a module.").argument("<module-id>", "Module ID to validate").option("--input <source>", "JSON input file or '-' for stdin.").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
720
- validateModuleId(moduleId);
721
- const moduleDef = registry.getModule(moduleId);
722
- if (!moduleDef) {
723
- process.stderr.write(`Error: Module '${moduleId}' not found.
724
- `);
725
- process.exit(EXIT_CODES.MODULE_NOT_FOUND);
726
- }
727
- const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
728
- if (!executor.validate) {
729
- process.stderr.write("Error: Executor does not support validate.\n");
730
- process.exit(1);
1779
+ function findRootProgram(host) {
1780
+ let cur = host;
1781
+ while (cur.parent) cur = cur.parent;
1782
+ return cur;
1783
+ }
1784
+ function registerCompletionCommand(host) {
1785
+ const completionCmd = new Command("completion").description("Generate a shell completion script and print it to stdout.").argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
1786
+ const validShells = ["bash", "zsh", "fish"];
1787
+ if (!validShells.includes(shell)) {
1788
+ process.stderr.write(
1789
+ `Error: Unknown shell '${shell}'. Expected: bash, zsh, or fish.
1790
+ `
1791
+ );
1792
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
731
1793
  }
732
- const preflight = await executor.validate(moduleId, merged);
733
- formatPreflightResult(preflight, opts.format);
734
- process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
1794
+ const root = findRootProgram(host);
1795
+ const resolved = root.name() || "apcore-cli";
1796
+ const generators = {
1797
+ bash: () => generateBashCompletion(resolved, root),
1798
+ zsh: () => generateZshCompletion(resolved, root),
1799
+ fish: () => generateFishCompletion(resolved, root)
1800
+ };
1801
+ process.stdout.write(generators[shell]());
735
1802
  });
736
- cli.addCommand(validateCmd);
737
- }
738
-
739
- // src/system-cmd.ts
740
- init_esm_shims();
741
- import { Command as Command3 } from "commander";
742
- async function callSystemModule(executor, moduleId, inputs) {
743
- if (executor.call) {
744
- return executor.call(moduleId, inputs);
745
- }
746
- return executor.execute(moduleId, inputs);
1803
+ host.addCommand(completionCmd);
747
1804
  }
748
- function formatHealthSummaryTty(result) {
749
- const summary = result.summary ?? {};
750
- const modules = result.modules ?? [];
751
- if (modules.length === 0) {
752
- process.stdout.write("No modules found.\n");
753
- return;
1805
+ var init_shell = __esm({
1806
+ "src/shell.ts"() {
1807
+ "use strict";
1808
+ init_esm_shims();
1809
+ init_errors();
754
1810
  }
755
- const total = summary.total_modules ?? modules.length;
756
- process.stdout.write(`Health Overview (${total} modules)
1811
+ });
757
1812
 
758
- `);
759
- process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
760
- `);
761
- process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
762
- `);
763
- for (const m of modules) {
764
- const top = m.top_error;
765
- const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
766
- const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
767
- process.stdout.write(
768
- ` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
769
- `
770
- );
771
- }
772
- const parts = [];
773
- for (const key of ["healthy", "degraded", "error"]) {
774
- const count = summary[key];
775
- if (count) parts.push(`${count} ${key}`);
776
- }
777
- process.stdout.write(`
778
- Summary: ${parts.join(", ") || "no data"}
779
- `);
1813
+ // src/security/audit.ts
1814
+ var audit_exports = {};
1815
+ __export(audit_exports, {
1816
+ AuditLogger: () => AuditLogger,
1817
+ canonicalizeForHash: () => canonicalizeForHash,
1818
+ getAuditLogger: () => getAuditLogger,
1819
+ setAuditLogger: () => setAuditLogger
1820
+ });
1821
+ import * as crypto from "crypto";
1822
+ import * as fs3 from "fs";
1823
+ import * as os from "os";
1824
+ import * as path3 from "path";
1825
+ function setAuditLogger(auditLogger) {
1826
+ _auditLogger = auditLogger;
780
1827
  }
781
- function formatHealthModuleTty(result) {
782
- process.stdout.write(`Module: ${result.module_id ?? "?"}
783
- `);
784
- process.stdout.write(`Status: ${result.status ?? "unknown"}
785
- `);
786
- const total = result.total_calls ?? 0;
787
- const errors = result.error_count ?? 0;
788
- const rate = result.error_rate ?? 0;
789
- const avg = result.avg_latency_ms ?? 0;
790
- const p99 = result.p99_latency_ms ?? 0;
791
- process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
792
- `);
793
- process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
794
- `);
795
- const recent = result.recent_errors ?? [];
796
- if (recent.length > 0) {
797
- process.stdout.write(`
798
- Recent Errors (top ${recent.length}):
799
- `);
800
- for (const e of recent) {
801
- const count = e.count ?? "?";
802
- const last = e.last_occurred ?? "?";
803
- process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
804
- `);
805
- }
1828
+ function getAuditLogger() {
1829
+ return _auditLogger;
1830
+ }
1831
+ function canonicalizeForHash(value) {
1832
+ if (value === null || typeof value !== "object") return value;
1833
+ if (Array.isArray(value)) return value.map(canonicalizeForHash);
1834
+ const src = value;
1835
+ const sorted = {};
1836
+ for (const key of Object.keys(src).sort()) {
1837
+ sorted[key] = canonicalizeForHash(src[key]);
806
1838
  }
1839
+ return sorted;
807
1840
  }
808
- function formatUsageSummaryTty(result) {
809
- const modules = result.modules ?? [];
810
- const period = result.period ?? "?";
811
- if (modules.length === 0) {
812
- process.stdout.write(`No usage data for period ${period}.
813
- `);
814
- return;
1841
+ var _auditLogger, AuditLogger;
1842
+ var init_audit = __esm({
1843
+ "src/security/audit.ts"() {
1844
+ "use strict";
1845
+ init_esm_shims();
1846
+ init_logger();
1847
+ _auditLogger = null;
1848
+ AuditLogger = class _AuditLogger {
1849
+ static DEFAULT_PATH = path3.join(
1850
+ os.homedir(),
1851
+ ".apcore-cli",
1852
+ "audit.jsonl"
1853
+ );
1854
+ logPath;
1855
+ writeFailureWarned = false;
1856
+ constructor(path5) {
1857
+ this.logPath = path5 ?? _AuditLogger.DEFAULT_PATH;
1858
+ this.ensureDirectory();
1859
+ }
1860
+ ensureDirectory() {
1861
+ const dir = path3.dirname(this.logPath);
1862
+ try {
1863
+ fs3.mkdirSync(dir, { recursive: true });
1864
+ try {
1865
+ fs3.chmodSync(dir, 448);
1866
+ } catch {
1867
+ }
1868
+ } catch {
1869
+ }
1870
+ }
1871
+ logExecution(moduleId, inputData, status, exitCode, durationMs) {
1872
+ const entry = {
1873
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1874
+ user: this.getUser(),
1875
+ module_id: moduleId,
1876
+ input_hash: this.hashInput(inputData),
1877
+ status,
1878
+ exit_code: exitCode,
1879
+ duration_ms: durationMs
1880
+ };
1881
+ try {
1882
+ fs3.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
1883
+ try {
1884
+ fs3.chmodSync(this.logPath, 384);
1885
+ } catch {
1886
+ }
1887
+ } catch (err) {
1888
+ if (!this.writeFailureWarned) {
1889
+ this.writeFailureWarned = true;
1890
+ warn(`Could not write audit log: ${err}`);
1891
+ }
1892
+ }
1893
+ }
1894
+ hashInput(inputData) {
1895
+ const salt = crypto.randomBytes(16);
1896
+ const payload = JSON.stringify(canonicalizeForHash(inputData));
1897
+ return crypto.createHash("sha256").update(Buffer.concat([salt, Buffer.from(payload, "utf-8")])).digest("hex");
1898
+ }
1899
+ getUser() {
1900
+ try {
1901
+ return os.userInfo().username;
1902
+ } catch {
1903
+ return process.env.USER ?? process.env.USERNAME ?? "unknown";
1904
+ }
1905
+ }
1906
+ };
815
1907
  }
816
- process.stdout.write(`Usage Summary (last ${period})
1908
+ });
817
1909
 
818
- `);
819
- process.stdout.write(` ${"Module".padEnd(24)} ${"Calls".padStart(8)} ${"Errors".padStart(8)} ${"Avg Latency".padStart(12)} ${"Trend".padStart(10)}
820
- `);
821
- process.stdout.write(` ${"-".repeat(24)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(10)}
822
- `);
823
- for (const m of modules) {
824
- const avg = `${(m.avg_latency_ms ?? 0).toFixed(0)}ms`;
825
- process.stdout.write(
1910
+ // src/security/config-encryptor.ts
1911
+ import * as crypto2 from "crypto";
1912
+ import * as os2 from "os";
1913
+ async function getKeytar() {
1914
+ if (keytarModule) return keytarModule;
1915
+ try {
1916
+ keytarModule = await import("keytar");
1917
+ return keytarModule;
1918
+ } catch {
1919
+ return null;
1920
+ }
1921
+ }
1922
+ var PBKDF2_ITERATIONS, V1_STATIC_SALT, keytarModule, ConfigEncryptor;
1923
+ var init_config_encryptor = __esm({
1924
+ "src/security/config-encryptor.ts"() {
1925
+ "use strict";
1926
+ init_esm_shims();
1927
+ init_errors();
1928
+ init_logger();
1929
+ PBKDF2_ITERATIONS = 6e5;
1930
+ V1_STATIC_SALT = Buffer.from("apcore-cli-config-v1");
1931
+ keytarModule = null;
1932
+ ConfigEncryptor = class _ConfigEncryptor {
1933
+ static SERVICE_NAME = "apcore-cli";
1934
+ // One-shot flag so the "obfuscation only" warning fires exactly once
1935
+ // per process instead of once per encrypt/decrypt call.
1936
+ static weakFallbackWarned = false;
1937
+ /**
1938
+ * Encrypt and store a configuration value.
1939
+ *
1940
+ * Cross-SDK contract (D10-003, 2026-04-26): when the OS keyring is
1941
+ * detected as available but `setPassword` then throws (locked keyring,
1942
+ * transient backend failure, permission revoked, etc.), the error is
1943
+ * propagated wrapped in a `ConfigDecryptionError`. Previously TS
1944
+ * caught the exception and silently fell through to AES file encryption
1945
+ * — a quiet downgrade that surprised users who expected a hard failure.
1946
+ * Python lets the keyring exception propagate raw; Rust returns
1947
+ * `ConfigDecryptionError::KeyringError`. The fall-through to AES is
1948
+ * still reached when `getKeytar()` returns `null` (keyring
1949
+ * genuinely unavailable on this platform / install).
1950
+ */
1951
+ async store(key, value) {
1952
+ const keytar = await getKeytar();
1953
+ if (keytar) {
1954
+ try {
1955
+ await keytar.setPassword(_ConfigEncryptor.SERVICE_NAME, key, value);
1956
+ return `keyring:${key}`;
1957
+ } catch (err) {
1958
+ const detail = err instanceof Error ? err.message : String(err);
1959
+ throw new ConfigDecryptionError(
1960
+ `Failed to store '${key}' in OS keyring: ${detail}. Unset APCORE_CLI_CONFIG_PASSPHRASE-aware backends or unlock the keyring before retrying.`
1961
+ );
1962
+ }
1963
+ }
1964
+ warn("OS keyring unavailable. Using file-based encryption.");
1965
+ const ciphertext = this.aesEncrypt(value);
1966
+ return `enc:v2:${ciphertext.toString("base64")}`;
1967
+ }
1968
+ /**
1969
+ * Retrieve and decrypt a configuration value.
1970
+ */
1971
+ async retrieve(configValue, key) {
1972
+ if (configValue.startsWith("keyring:")) {
1973
+ const keytar = await getKeytar();
1974
+ if (!keytar) {
1975
+ throw new ConfigDecryptionError(
1976
+ `Keyring module not available to retrieve '${key}'.`
1977
+ );
1978
+ }
1979
+ try {
1980
+ const refKey = configValue.slice("keyring:".length);
1981
+ const result = await keytar.getPassword(
1982
+ _ConfigEncryptor.SERVICE_NAME,
1983
+ refKey
1984
+ );
1985
+ if (result === null || result === void 0) {
1986
+ throw new ConfigDecryptionError(
1987
+ `Keyring entry not found for '${refKey}'.`
1988
+ );
1989
+ }
1990
+ return result;
1991
+ } catch (err) {
1992
+ if (err instanceof ConfigDecryptionError) throw err;
1993
+ throw new ConfigDecryptionError(
1994
+ `Failed to retrieve from keyring: ${err}`
1995
+ );
1996
+ }
1997
+ }
1998
+ if (configValue.startsWith("enc:v2:")) {
1999
+ const data = Buffer.from(configValue.slice("enc:v2:".length), "base64");
2000
+ try {
2001
+ return this.aesDecrypt(data);
2002
+ } catch {
2003
+ throw new ConfigDecryptionError(
2004
+ `Failed to decrypt configuration value '${key}'. Re-configure with 'apcore-cli config set ${key}'.`
2005
+ );
2006
+ }
2007
+ }
2008
+ if (configValue.startsWith("enc:")) {
2009
+ const data = Buffer.from(configValue.slice("enc:".length), "base64");
2010
+ try {
2011
+ return this.aesDecryptV1(data);
2012
+ } catch {
2013
+ throw new ConfigDecryptionError(
2014
+ `Failed to decrypt configuration value '${key}'. Re-configure with 'apcore-cli config set ${key}'.`
2015
+ );
2016
+ }
2017
+ }
2018
+ return configValue;
2019
+ }
2020
+ // Derive an AES-256 key with a provided salt (v2 format).
2021
+ //
2022
+ // Order of preference:
2023
+ // 1. APCORE_CLI_CONFIG_PASSPHRASE env var — a real secret supplied by
2024
+ // the user; produces a key an attacker with filesystem read cannot
2025
+ // reconstruct without also knowing the passphrase.
2026
+ // 2. hostname + username — obfuscation-only derivation for backward
2027
+ // compatibility. Emits a loud stderr warning on first use so
2028
+ // operators know the stored value is NOT protected against a
2029
+ // filesystem-read attacker.
2030
+ deriveKey(salt) {
2031
+ const passphrase = process.env.APCORE_CLI_CONFIG_PASSPHRASE;
2032
+ if (passphrase && passphrase.length > 0) {
2033
+ return crypto2.pbkdf2Sync(passphrase, salt, PBKDF2_ITERATIONS, 32, "sha256");
2034
+ }
2035
+ if (!_ConfigEncryptor.weakFallbackWarned) {
2036
+ warn(
2037
+ "APCORE_CLI_CONFIG_PASSPHRASE is not set. The `enc:v2:` fallback uses a key derived from hostname+username (non-secret inputs) and is OBFUSCATION ONLY \u2014 an attacker with filesystem read access can reconstruct the key. Set APCORE_CLI_CONFIG_PASSPHRASE or ensure the OS keyring is available for real encryption."
2038
+ );
2039
+ _ConfigEncryptor.weakFallbackWarned = true;
2040
+ }
2041
+ const hostname2 = os2.hostname();
2042
+ const username = process.env.USER ?? process.env.USERNAME ?? "unknown";
2043
+ const material = `${hostname2}:${username}`;
2044
+ return crypto2.pbkdf2Sync(material, salt, PBKDF2_ITERATIONS, 32, "sha256");
2045
+ }
2046
+ aesEncrypt(plaintext) {
2047
+ const salt = crypto2.randomBytes(16);
2048
+ const key = this.deriveKey(salt);
2049
+ const nonce = crypto2.randomBytes(12);
2050
+ const cipher = crypto2.createCipheriv("aes-256-gcm", key, nonce);
2051
+ const ct = Buffer.concat([
2052
+ cipher.update(plaintext, "utf-8"),
2053
+ cipher.final()
2054
+ ]);
2055
+ const tag = cipher.getAuthTag();
2056
+ return Buffer.concat([salt, nonce, tag, ct]);
2057
+ }
2058
+ aesDecrypt(data) {
2059
+ const salt = data.subarray(0, 16);
2060
+ const nonce = data.subarray(16, 28);
2061
+ const tag = data.subarray(28, 44);
2062
+ const ct = data.subarray(44);
2063
+ const key = this.deriveKey(salt);
2064
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
2065
+ decipher.setAuthTag(tag);
2066
+ return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf-8");
2067
+ }
2068
+ /** Decrypt legacy v1-format values: nonce(12)+tag(16)+ct, static salt. */
2069
+ aesDecryptV1(data) {
2070
+ const nonce = data.subarray(0, 12);
2071
+ const tag = data.subarray(12, 28);
2072
+ const ct = data.subarray(28);
2073
+ const hostname2 = os2.hostname();
2074
+ const username = process.env.USER ?? process.env.USERNAME ?? "unknown";
2075
+ const passphrase = process.env.APCORE_CLI_CONFIG_PASSPHRASE;
2076
+ const materials = passphrase ? [passphrase, `${hostname2}:${username}`] : [`${hostname2}:${username}`];
2077
+ for (const material of materials) {
2078
+ for (const iterations of [6e5, 1e5]) {
2079
+ try {
2080
+ const key = crypto2.pbkdf2Sync(material, V1_STATIC_SALT, iterations, 32, "sha256");
2081
+ const decipher = crypto2.createDecipheriv("aes-256-gcm", key, nonce);
2082
+ decipher.setAuthTag(tag);
2083
+ return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf-8");
2084
+ } catch {
2085
+ }
2086
+ }
2087
+ }
2088
+ throw new Error("v1 decryption failed with all material+iteration combinations");
2089
+ }
2090
+ };
2091
+ }
2092
+ });
2093
+
2094
+ // src/security/auth.ts
2095
+ var AuthProvider;
2096
+ var init_auth = __esm({
2097
+ "src/security/auth.ts"() {
2098
+ "use strict";
2099
+ init_esm_shims();
2100
+ init_errors();
2101
+ init_config_encryptor();
2102
+ AuthProvider = class {
2103
+ config;
2104
+ encryptor;
2105
+ constructor(config, encryptor) {
2106
+ this.config = config;
2107
+ this.encryptor = encryptor ?? new ConfigEncryptor();
2108
+ }
2109
+ /**
2110
+ * Retrieve the API key from the configured sources.
2111
+ * Handles keyring: and enc: prefixes via ConfigEncryptor.
2112
+ */
2113
+ async getApiKey() {
2114
+ const result = this.config.resolve(
2115
+ "auth.api_key",
2116
+ "--api-key",
2117
+ "APCORE_AUTH_API_KEY"
2118
+ );
2119
+ if (result === null || result === void 0) {
2120
+ return null;
2121
+ }
2122
+ const strResult = String(result);
2123
+ if (strResult.startsWith("keyring:") || strResult.startsWith("enc:")) {
2124
+ try {
2125
+ return await this.encryptor.retrieve(strResult, "auth.api_key");
2126
+ } catch (err) {
2127
+ if (err instanceof ConfigDecryptionError) {
2128
+ throw new AuthenticationError(
2129
+ "Failed to decrypt stored API key. Re-configure with 'apcore-cli config set auth.api_key'."
2130
+ );
2131
+ }
2132
+ throw err;
2133
+ }
2134
+ }
2135
+ return strResult;
2136
+ }
2137
+ /**
2138
+ * Add authentication headers to an outgoing request.
2139
+ *
2140
+ * Cross-SDK contract (D10-002, 2026-04-26): the input `headers` object
2141
+ * is mutated **in place** and the same reference is returned. Callers
2142
+ * that share the headers reference (the documented pattern in
2143
+ * apcore-cli/docs/features/security.md §AuthProvider) can read
2144
+ * `headers.Authorization` after the call without re-binding the
2145
+ * return value. Python and Rust both mutate-and-return; TS previously
2146
+ * spread into a new object, which silently broke shared-reference
2147
+ * callers.
2148
+ */
2149
+ async authenticateRequest(headers) {
2150
+ const key = await this.getApiKey();
2151
+ if (!key) {
2152
+ throw new AuthenticationError(
2153
+ "Remote registry requires authentication. Set --api-key, APCORE_AUTH_API_KEY, or auth.api_key in config."
2154
+ );
2155
+ }
2156
+ if (/[\r\n]/.test(key)) {
2157
+ throw new AuthenticationError(
2158
+ "Malformed API key: contains invalid characters (CR/LF). Re-configure with 'apcore-cli config set auth.api_key'."
2159
+ );
2160
+ }
2161
+ headers.Authorization = `Bearer ${key.trim()}`;
2162
+ return headers;
2163
+ }
2164
+ /**
2165
+ * Handle an HTTP response status code for auth-related errors.
2166
+ */
2167
+ handleResponse(statusCode) {
2168
+ if (statusCode === 401 || statusCode === 403) {
2169
+ throw new AuthenticationError(
2170
+ "Authentication failed. Verify your API key."
2171
+ );
2172
+ }
2173
+ }
2174
+ };
2175
+ }
2176
+ });
2177
+
2178
+ // src/security/index.ts
2179
+ var security_exports = {};
2180
+ __export(security_exports, {
2181
+ AuditLogger: () => AuditLogger,
2182
+ AuthProvider: () => AuthProvider,
2183
+ ConfigEncryptor: () => ConfigEncryptor,
2184
+ Sandbox: () => Sandbox,
2185
+ getAuditLogger: () => getAuditLogger,
2186
+ setAuditLogger: () => setAuditLogger
2187
+ });
2188
+ var init_security = __esm({
2189
+ "src/security/index.ts"() {
2190
+ "use strict";
2191
+ init_esm_shims();
2192
+ init_audit();
2193
+ init_auth();
2194
+ init_config_encryptor();
2195
+ init_sandbox();
2196
+ }
2197
+ });
2198
+
2199
+ // src/discovery.ts
2200
+ import { Command as Command2, Option as Option2 } from "commander";
2201
+ function validateTag(tag) {
2202
+ if (!TAG_PATTERN.test(tag)) {
2203
+ process.stderr.write(
2204
+ `Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
2205
+ `
2206
+ );
2207
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2208
+ }
2209
+ }
2210
+ function collectTag(value, previous) {
2211
+ return previous.concat([value]);
2212
+ }
2213
+ function collectAnnotation(value, previous) {
2214
+ return previous.concat([value]);
2215
+ }
2216
+ function getAnnotationFlag(moduleDef, flag) {
2217
+ const annotations = moduleDef.annotations;
2218
+ if (!annotations || typeof annotations !== "object") return false;
2219
+ const ann = annotations;
2220
+ const map = {
2221
+ "destructive": "destructive",
2222
+ "requires-approval": "requires_approval",
2223
+ "readonly": "readonly",
2224
+ "streaming": "streaming",
2225
+ "cacheable": "cacheable",
2226
+ "idempotent": "idempotent",
2227
+ "paginated": "paginated"
2228
+ };
2229
+ const attr = map[flag] ?? flag;
2230
+ return ann[attr] === true;
2231
+ }
2232
+ function registerListCommand(apcliGroup, registry, exposureFilter) {
2233
+ const listCmd = new Command2("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--flat", "Show flat list (no grouping).", false).option("--format <format>", "Output format.", void 0).option("-s, --search <query>", "Filter by substring match on ID and description.").addOption(
2234
+ new Option2("--status <status>", "Filter by module status.").choices(["enabled", "disabled", "all"]).default("enabled")
2235
+ ).option("-a, --annotation <flag>", "Filter by annotation flag (AND logic). Repeatable.", collectAnnotation, []).addOption(
2236
+ new Option2("--sort <field>", "Sort order.").choices(["id", "calls", "errors", "latency"]).default("id")
2237
+ ).option("--reverse", "Reverse sort order.", false).option("--deprecated", "Include deprecated modules.", false).option("--deps", "Show dependency count column.", false).addOption(
2238
+ new Option2("--exposure <mode>", "Filter by exposure status.").choices(["exposed", "hidden", "all"]).default("exposed")
2239
+ ).action((opts) => {
2240
+ for (const t of opts.tag) {
2241
+ validateTag(t);
2242
+ }
2243
+ let modules = [];
2244
+ for (const m of registry.listModules()) {
2245
+ modules.push(m);
2246
+ }
2247
+ if (opts.tag.length > 0) {
2248
+ const filterTags = new Set(opts.tag);
2249
+ modules = modules.filter((m) => {
2250
+ const mTags = m.tags ?? [];
2251
+ return [...filterTags].every((t) => mTags.includes(t));
2252
+ });
2253
+ }
2254
+ if (opts.search) {
2255
+ const query = opts.search.toLowerCase();
2256
+ modules = modules.filter(
2257
+ (m) => (m.id ?? "").toLowerCase().includes(query) || (m.description ?? "").toLowerCase().includes(query)
2258
+ );
2259
+ }
2260
+ if (opts.status === "enabled") {
2261
+ modules = modules.filter((m) => {
2262
+ const enabled = m.enabled;
2263
+ return enabled !== false;
2264
+ });
2265
+ } else if (opts.status === "disabled") {
2266
+ modules = modules.filter((m) => {
2267
+ const enabled = m.enabled;
2268
+ return enabled === false;
2269
+ });
2270
+ }
2271
+ if (!opts.deprecated) {
2272
+ modules = modules.filter((m) => {
2273
+ const deprecated = m.deprecated;
2274
+ return deprecated !== true;
2275
+ });
2276
+ }
2277
+ if (opts.annotation.length > 0) {
2278
+ for (const annFlag of opts.annotation) {
2279
+ modules = modules.filter((m) => getAnnotationFlag(m, annFlag));
2280
+ }
2281
+ }
2282
+ if (opts.sort === "calls" || opts.sort === "errors" || opts.sort === "latency") {
2283
+ process.stderr.write(
2284
+ `Warning: Usage data not available; sorting by id. Sort by ${opts.sort} requires system.usage modules.
2285
+ `
2286
+ );
2287
+ }
2288
+ modules.sort((a, b) => (a.id ?? "").localeCompare(b.id ?? ""));
2289
+ if (opts.reverse) {
2290
+ modules.reverse();
2291
+ }
2292
+ let showExposureCol = false;
2293
+ if (exposureFilter && opts.exposure !== "all") {
2294
+ if (opts.exposure === "exposed") {
2295
+ modules = modules.filter((m) => exposureFilter.isExposed(m.id ?? ""));
2296
+ } else if (opts.exposure === "hidden") {
2297
+ modules = modules.filter((m) => !exposureFilter.isExposed(m.id ?? ""));
2298
+ }
2299
+ }
2300
+ if (opts.exposure === "all" && exposureFilter) {
2301
+ showExposureCol = true;
2302
+ }
2303
+ const fmt = resolveFormat(opts.format);
2304
+ const filterTagsArg = opts.tag.length > 0 ? opts.tag : void 0;
2305
+ formatModuleList(modules, fmt, filterTagsArg, opts.deps, showExposureCol ? exposureFilter : void 0);
2306
+ });
2307
+ apcliGroup.addCommand(listCmd);
2308
+ }
2309
+ function registerDescribeCommand(apcliGroup, registry) {
2310
+ const describeCmd = new Command2("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").option("--format <format>", "Output format.", void 0).action((moduleId, opts) => {
2311
+ validateModuleId(moduleId);
2312
+ const moduleDef = registry.getModule(moduleId);
2313
+ if (!moduleDef) {
2314
+ process.stderr.write(
2315
+ `Error: Module '${moduleId}' not found.
2316
+ `
2317
+ );
2318
+ process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2319
+ }
2320
+ const fmt = resolveFormat(opts.format);
2321
+ formatModuleDetail(moduleDef, fmt);
2322
+ });
2323
+ apcliGroup.addCommand(describeCmd);
2324
+ }
2325
+ function registerExecCommand(apcliGroup, registry, executor) {
2326
+ const execCmd = new Command2("exec").description("Execute a module by ID with JSON input.").argument("<module-id>", "Module ID to execute").option("--format <format>", "Output format (json, table, csv, yaml, jsonl).").option("--fields <fields>", "Comma-separated dot-paths to select from the result.").option(
2327
+ "--input <json>",
2328
+ "JSON object passed as input to the module. Use '-' to read JSON from stdin."
2329
+ ).option("-y, --yes", "Auto-approve if the module declares requires_approval.", false).option(
2330
+ "--approval-timeout <seconds>",
2331
+ "Seconds to wait for interactive approval.",
2332
+ parseInt
2333
+ ).option("--sandbox", "Run module in an isolated subprocess with restricted env.", false).option("--strategy <name>", "Execution strategy (standard, parallel, sequential, etc.).").option("--trace", "Enable pipeline trace output.", false).option("--dry-run", "Validate inputs without executing the module.", false).option("--stream", "Stream output as JSONL instead of buffering.", false).action(async (moduleId, opts) => {
2334
+ validateModuleId(moduleId);
2335
+ const moduleDef = registry.getModule(moduleId);
2336
+ if (!moduleDef) {
2337
+ process.stderr.write(`Error: Module '${moduleId}' not found.
2338
+ `);
2339
+ process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2340
+ }
2341
+ let merged = {};
2342
+ if (opts.input === "-") {
2343
+ merged = await collectInput("-", {}, false);
2344
+ } else if (opts.input !== void 0) {
2345
+ try {
2346
+ const parsed = JSON.parse(opts.input);
2347
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
2348
+ process.stderr.write("Error: --input JSON must be an object.\n");
2349
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2350
+ }
2351
+ merged = parsed;
2352
+ } catch (err) {
2353
+ const msg = err instanceof Error ? err.message : String(err);
2354
+ process.stderr.write(`Error: --input is not valid JSON: ${msg}
2355
+ `);
2356
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2357
+ }
2358
+ }
2359
+ const startTime = performance.now();
2360
+ try {
2361
+ await checkApproval(moduleDef, opts.yes, opts.approvalTimeout);
2362
+ if (opts.dryRun) {
2363
+ if (executor.validate) {
2364
+ const preflight = await executor.validate(moduleId, merged);
2365
+ formatPreflightResult(preflight, opts.format);
2366
+ } else {
2367
+ process.stdout.write(JSON.stringify({ valid: true }) + "\n");
2368
+ }
2369
+ return;
2370
+ }
2371
+ let result;
2372
+ if (opts.strategy && executor.callWithTrace) {
2373
+ const [res] = await executor.callWithTrace(moduleId, merged, { strategy: opts.strategy });
2374
+ result = res;
2375
+ } else {
2376
+ const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
2377
+ const sandbox = new Sandbox2(opts.sandbox);
2378
+ result = await sandbox.execute(moduleId, merged, executor);
2379
+ }
2380
+ const durationMs = Math.round(performance.now() - startTime);
2381
+ const fmt = resolveFormat(opts.format);
2382
+ formatExecResult(result, fmt, opts.fields);
2383
+ const auditLogger = getAuditLogger();
2384
+ if (auditLogger) {
2385
+ auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
2386
+ }
2387
+ } catch (err) {
2388
+ const exitCode = exitCodeForError(err);
2389
+ const durationMs = Math.round(performance.now() - startTime);
2390
+ try {
2391
+ const auditLogger = getAuditLogger();
2392
+ if (auditLogger) {
2393
+ auditLogger.logExecution(moduleId, merged, "error", exitCode, durationMs);
2394
+ }
2395
+ } catch {
2396
+ }
2397
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
2398
+ `);
2399
+ process.exit(exitCode);
2400
+ }
2401
+ });
2402
+ apcliGroup.addCommand(execCmd);
2403
+ }
2404
+ function registerValidateCommand(cli, registry, executor) {
2405
+ const validateCmd = new Command2("validate").description("Run preflight checks without executing a module.").argument("<module-id>", "Module ID to validate").option("--input <source>", "JSON input file or '-' for stdin.").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
2406
+ validateModuleId(moduleId);
2407
+ const moduleDef = registry.getModule(moduleId);
2408
+ if (!moduleDef) {
2409
+ process.stderr.write(`Error: Module '${moduleId}' not found.
2410
+ `);
2411
+ process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2412
+ }
2413
+ const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
2414
+ if (!executor.validate) {
2415
+ process.stderr.write("Error: Executor does not support validate.\n");
2416
+ process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
2417
+ }
2418
+ try {
2419
+ const preflight = await executor.validate(moduleId, merged);
2420
+ formatPreflightResult(preflight, opts.format);
2421
+ process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
2422
+ } catch (err) {
2423
+ const exitCode = exitCodeForError(err);
2424
+ try {
2425
+ const auditLogger = getAuditLogger();
2426
+ if (auditLogger) {
2427
+ auditLogger.logExecution(moduleId, merged, "error", exitCode, 0);
2428
+ }
2429
+ } catch {
2430
+ }
2431
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : err}
2432
+ `);
2433
+ process.exit(exitCode);
2434
+ }
2435
+ });
2436
+ cli.addCommand(validateCmd);
2437
+ }
2438
+ var TAG_PATTERN;
2439
+ var init_discovery = __esm({
2440
+ "src/discovery.ts"() {
2441
+ "use strict";
2442
+ init_esm_shims();
2443
+ init_approval();
2444
+ init_errors();
2445
+ init_main();
2446
+ init_output();
2447
+ init_audit();
2448
+ TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
2449
+ }
2450
+ });
2451
+
2452
+ // src/system-cmd.ts
2453
+ import { Command as Command3 } from "commander";
2454
+ async function callSystemModule(executor, moduleId, inputs) {
2455
+ if (executor.call) {
2456
+ return executor.call(moduleId, inputs);
2457
+ }
2458
+ return executor.execute(moduleId, inputs);
2459
+ }
2460
+ function emitResult(jsonPayload, fmt, ttyRender) {
2461
+ if (fmt === "json" || !process.stdout.isTTY) {
2462
+ process.stdout.write(JSON.stringify(jsonPayload, null, 2) + "\n");
2463
+ } else {
2464
+ ttyRender();
2465
+ }
2466
+ }
2467
+ function emitErrorAndExit(e) {
2468
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2469
+ `);
2470
+ process.exit(exitCodeForError(e));
2471
+ }
2472
+ function formatHealthSummaryTty(result) {
2473
+ const summary = result.summary ?? {};
2474
+ const modules = result.modules ?? [];
2475
+ if (modules.length === 0) {
2476
+ process.stdout.write("No modules found.\n");
2477
+ return;
2478
+ }
2479
+ const total = summary.total_modules ?? modules.length;
2480
+ process.stdout.write(`Health Overview (${total} modules)
2481
+
2482
+ `);
2483
+ process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
2484
+ `);
2485
+ process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
2486
+ `);
2487
+ for (const m of modules) {
2488
+ const top = m.top_error;
2489
+ const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
2490
+ const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
2491
+ process.stdout.write(
2492
+ ` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
2493
+ `
2494
+ );
2495
+ }
2496
+ const parts = [];
2497
+ for (const key of ["healthy", "degraded", "error"]) {
2498
+ const count = summary[key];
2499
+ if (count) parts.push(`${count} ${key}`);
2500
+ }
2501
+ process.stdout.write(`
2502
+ Summary: ${parts.join(", ") || "no data"}
2503
+ `);
2504
+ }
2505
+ function formatHealthModuleTty(result) {
2506
+ process.stdout.write(`Module: ${result.module_id ?? "?"}
2507
+ `);
2508
+ process.stdout.write(`Status: ${result.status ?? "unknown"}
2509
+ `);
2510
+ const total = result.total_calls ?? 0;
2511
+ const errors = result.error_count ?? 0;
2512
+ const rate = result.error_rate ?? 0;
2513
+ const avg = result.avg_latency_ms ?? 0;
2514
+ const p99 = result.p99_latency_ms ?? 0;
2515
+ process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
2516
+ `);
2517
+ process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
2518
+ `);
2519
+ const recent = result.recent_errors ?? [];
2520
+ if (recent.length > 0) {
2521
+ process.stdout.write(`
2522
+ Recent Errors (top ${recent.length}):
2523
+ `);
2524
+ for (const e of recent) {
2525
+ const count = e.count ?? "?";
2526
+ const last = e.last_occurred ?? "?";
2527
+ process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
2528
+ `);
2529
+ }
2530
+ }
2531
+ }
2532
+ function formatUsageSummaryTty(result) {
2533
+ const modules = result.modules ?? [];
2534
+ const period = result.period ?? "?";
2535
+ if (modules.length === 0) {
2536
+ process.stdout.write(`No usage data for period ${period}.
2537
+ `);
2538
+ return;
2539
+ }
2540
+ process.stdout.write(`Usage Summary (last ${period})
2541
+
2542
+ `);
2543
+ process.stdout.write(` ${"Module".padEnd(24)} ${"Calls".padStart(8)} ${"Errors".padStart(8)} ${"Avg Latency".padStart(12)} ${"Trend".padStart(10)}
2544
+ `);
2545
+ process.stdout.write(` ${"-".repeat(24)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(10)}
2546
+ `);
2547
+ for (const m of modules) {
2548
+ const avg = `${(m.avg_latency_ms ?? 0).toFixed(0)}ms`;
2549
+ process.stdout.write(
826
2550
  ` ${String(m.module_id).padEnd(24)} ${String(m.call_count ?? 0).padStart(8)} ${String(m.error_count ?? 0).padStart(8)} ${avg.padStart(12)} ${String(m.trend ?? "").padStart(10)}
827
2551
  `
828
2552
  );
@@ -833,17 +2557,7 @@ function formatUsageSummaryTty(result) {
833
2557
  Total: ${totalCalls.toLocaleString()} calls | ${totalErrors.toLocaleString()} errors
834
2558
  `);
835
2559
  }
836
- async function registerSystemCommands(cli, executor) {
837
- try {
838
- if (executor.validate) {
839
- await executor.validate("system.health.summary", {});
840
- } else {
841
- await callSystemModule(executor, "system.health.summary", { include_healthy: true });
842
- }
843
- } catch {
844
- debug("System modules not available; skipping system command registration.");
845
- return;
846
- }
2560
+ function registerHealthCommand(apcliGroup, executor) {
847
2561
  const healthCmd = new Command3("health").description("Show module health status. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed health").option("--threshold <number>", "Error rate threshold (default: 0.01).", parseFloat, 0.01).option("--all", "Include healthy modules.", false).option("--errors <count>", "Max recent errors (module detail only).", parseInt, 10).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
848
2562
  const fmt = resolveFormat(opts.format);
849
2563
  try {
@@ -852,29 +2566,21 @@ async function registerSystemCommands(cli, executor) {
852
2566
  module_id: moduleId,
853
2567
  error_limit: opts.errors
854
2568
  });
855
- if (fmt === "json" || !process.stdout.isTTY) {
856
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
857
- } else {
858
- formatHealthModuleTty(result);
859
- }
2569
+ emitResult(result, fmt, () => formatHealthModuleTty(result));
860
2570
  } else {
861
2571
  const result = await callSystemModule(executor, "system.health.summary", {
862
2572
  error_rate_threshold: opts.threshold,
863
2573
  include_healthy: opts.all
864
2574
  });
865
- if (fmt === "json" || !process.stdout.isTTY) {
866
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
867
- } else {
868
- formatHealthSummaryTty(result);
869
- }
2575
+ emitResult(result, fmt, () => formatHealthSummaryTty(result));
870
2576
  }
871
2577
  } catch (e) {
872
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
873
- `);
874
- process.exit(1);
2578
+ emitErrorAndExit(e);
875
2579
  }
876
2580
  });
877
- cli.addCommand(healthCmd);
2581
+ apcliGroup.addCommand(healthCmd);
2582
+ }
2583
+ function registerUsageCommand(apcliGroup, executor) {
878
2584
  const usageCmd = new Command3("usage").description("Show module usage statistics. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed usage").option("--period <period>", "Time window: 1h, 24h, 7d, 30d.", "24h").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
879
2585
  const fmt = resolveFormat(opts.format);
880
2586
  try {
@@ -889,24 +2595,21 @@ async function registerSystemCommands(cli, executor) {
889
2595
  period: opts.period
890
2596
  });
891
2597
  }
892
- if (fmt === "json" || !process.stdout.isTTY) {
893
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
894
- } else if (moduleId) {
895
- formatExecResult(result, fmt);
896
- } else {
897
- formatUsageSummaryTty(result);
898
- }
2598
+ emitResult(result, fmt, () => {
2599
+ if (moduleId) {
2600
+ formatExecResult(result, fmt);
2601
+ } else {
2602
+ formatUsageSummaryTty(result);
2603
+ }
2604
+ });
899
2605
  } catch (e) {
900
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
901
- `);
902
- process.exit(1);
2606
+ emitErrorAndExit(e);
903
2607
  }
904
2608
  });
905
- cli.addCommand(usageCmd);
906
- const enableCmd = new Command3("enable").description("Enable a disabled module at runtime.").argument("<module-id>", "Module ID to enable").requiredOption("--reason <reason>", "Reason for enabling (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
907
- if (!opts.yes) {
908
- process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
909
- }
2609
+ apcliGroup.addCommand(usageCmd);
2610
+ }
2611
+ function registerEnableCommand(apcliGroup, executor) {
2612
+ const enableCmd = new Command3("enable").description("Enable a disabled module at runtime.").argument("<module-id>", "Module ID to enable").requiredOption("--reason <reason>", "Reason for enabling (required for audit).").option("-y, --yes", "Signal explicit intent (forwarded to server-side approval gate).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
910
2613
  const fmt = resolveFormat(opts.format);
911
2614
  try {
912
2615
  const result = await callSystemModule(executor, "system.control.toggle_feature", {
@@ -914,24 +2617,19 @@ async function registerSystemCommands(cli, executor) {
914
2617
  enabled: true,
915
2618
  reason: opts.reason
916
2619
  });
917
- if (fmt === "json" || !process.stdout.isTTY) {
918
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
919
- } else {
2620
+ emitResult(result, fmt, () => {
920
2621
  process.stdout.write(`Module '${moduleId}' enabled.
921
2622
  Reason: ${opts.reason}
922
2623
  `);
923
- }
2624
+ });
924
2625
  } catch (e) {
925
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
926
- `);
927
- process.exit(1);
2626
+ emitErrorAndExit(e);
928
2627
  }
929
2628
  });
930
- cli.addCommand(enableCmd);
931
- const disableCmd = new Command3("disable").description("Disable a module at runtime (calls are rejected until re-enabled).").argument("<module-id>", "Module ID to disable").requiredOption("--reason <reason>", "Reason for disabling (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
932
- if (!opts.yes) {
933
- process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
934
- }
2629
+ apcliGroup.addCommand(enableCmd);
2630
+ }
2631
+ function registerDisableCommand(apcliGroup, executor) {
2632
+ const disableCmd = new Command3("disable").description("Disable a module at runtime (calls are rejected until re-enabled).").argument("<module-id>", "Module ID to disable").requiredOption("--reason <reason>", "Reason for disabling (required for audit).").option("-y, --yes", "Signal explicit intent (forwarded to server-side approval gate).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
935
2633
  const fmt = resolveFormat(opts.format);
936
2634
  try {
937
2635
  const result = await callSystemModule(executor, "system.control.toggle_feature", {
@@ -939,33 +2637,26 @@ async function registerSystemCommands(cli, executor) {
939
2637
  enabled: false,
940
2638
  reason: opts.reason
941
2639
  });
942
- if (fmt === "json" || !process.stdout.isTTY) {
943
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
944
- } else {
2640
+ emitResult(result, fmt, () => {
945
2641
  process.stdout.write(`Module '${moduleId}' disabled.
946
2642
  Reason: ${opts.reason}
947
2643
  `);
948
- }
2644
+ });
949
2645
  } catch (e) {
950
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
951
- `);
952
- process.exit(1);
2646
+ emitErrorAndExit(e);
953
2647
  }
954
2648
  });
955
- cli.addCommand(disableCmd);
956
- const reloadCmd = new Command3("reload").description("Hot-reload a module from disk.").argument("<module-id>", "Module ID to reload").requiredOption("--reason <reason>", "Reason for reload (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
957
- if (!opts.yes) {
958
- process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
959
- }
2649
+ apcliGroup.addCommand(disableCmd);
2650
+ }
2651
+ function registerReloadCommand(apcliGroup, executor) {
2652
+ const reloadCmd = new Command3("reload").description("Hot-reload a module from disk.").argument("<module-id>", "Module ID to reload").requiredOption("--reason <reason>", "Reason for reload (required for audit).").option("-y, --yes", "Signal explicit intent (forwarded to server-side approval gate).", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
960
2653
  const fmt = resolveFormat(opts.format);
961
2654
  try {
962
2655
  const result = await callSystemModule(executor, "system.control.reload_module", {
963
2656
  module_id: moduleId,
964
2657
  reason: opts.reason
965
2658
  });
966
- if (fmt === "json" || !process.stdout.isTTY) {
967
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
968
- } else {
2659
+ emitResult(result, fmt, () => {
969
2660
  const prev = result.previous_version ?? "?";
970
2661
  const newVer = result.new_version ?? "?";
971
2662
  const dur = result.reload_duration_ms ?? "?";
@@ -975,34 +2666,30 @@ async function registerSystemCommands(cli, executor) {
975
2666
  `);
976
2667
  process.stdout.write(` Duration: ${dur}ms
977
2668
  `);
978
- }
2669
+ });
979
2670
  } catch (e) {
980
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
981
- `);
982
- process.exit(1);
2671
+ emitErrorAndExit(e);
983
2672
  }
984
2673
  });
985
- cli.addCommand(reloadCmd);
2674
+ apcliGroup.addCommand(reloadCmd);
2675
+ }
2676
+ function registerConfigCommand(apcliGroup, executor) {
986
2677
  const configGroup = new Command3("config").description("Read or update runtime configuration.");
987
2678
  const configGetCmd = new Command3("get").description("Read a configuration value by dot-path key.").argument("<key>", "Configuration key (dot-path)").option("--format <format>", "Output format.", "table").action(async (key, opts) => {
988
2679
  const fmt = resolveFormat(opts.format);
989
2680
  try {
990
2681
  const result = await callSystemModule(executor, "system.config.get", { key });
991
2682
  const value = result?.value ?? result;
992
- if (fmt === "json" || !process.stdout.isTTY) {
993
- process.stdout.write(JSON.stringify({ key, value }, null, 2) + "\n");
994
- } else {
2683
+ emitResult({ key, value }, fmt, () => {
995
2684
  process.stdout.write(`${key} = ${JSON.stringify(value)}
996
2685
  `);
997
- }
2686
+ });
998
2687
  } catch (e) {
999
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
1000
- `);
1001
- process.exit(1);
2688
+ emitErrorAndExit(e);
1002
2689
  }
1003
2690
  });
1004
2691
  configGroup.addCommand(configGetCmd);
1005
- const configSetCmd = new Command3("set").description("Update a runtime configuration value (requires approval).").argument("<key>", "Configuration key (dot-path)").argument("<value>", "New value").requiredOption("--reason <reason>", "Reason for config change (required for audit).").option("--format <format>", "Output format.").action(async (key, value, opts) => {
2692
+ const configSetCmd = new Command3("set").description("Update a runtime configuration value (audit-logged; server-side approval gate applies).").argument("<key>", "Configuration key (dot-path)").argument("<value>", "New value").requiredOption("--reason <reason>", "Reason for config change (required for audit).").option("--format <format>", "Output format.").action(async (key, value, opts) => {
1006
2693
  const fmt = resolveFormat(opts.format);
1007
2694
  let parsedValue;
1008
2695
  try {
@@ -1016,9 +2703,7 @@ async function registerSystemCommands(cli, executor) {
1016
2703
  value: parsedValue,
1017
2704
  reason: opts.reason
1018
2705
  });
1019
- if (fmt === "json" || !process.stdout.isTTY) {
1020
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1021
- } else {
2706
+ emitResult(result, fmt, () => {
1022
2707
  const old = result.old_value ?? "?";
1023
2708
  const newVal = result.new_value ?? "?";
1024
2709
  process.stdout.write(`Config updated: ${key}
@@ -1027,257 +2712,915 @@ async function registerSystemCommands(cli, executor) {
1027
2712
  `);
1028
2713
  process.stdout.write(` Reason: ${opts.reason}
1029
2714
  `);
1030
- }
2715
+ });
1031
2716
  } catch (e) {
1032
- process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
1033
- `);
1034
- process.exit(1);
2717
+ emitErrorAndExit(e);
1035
2718
  }
1036
2719
  });
1037
2720
  configGroup.addCommand(configSetCmd);
1038
- cli.addCommand(configGroup);
2721
+ apcliGroup.addCommand(configGroup);
1039
2722
  }
2723
+ var init_system_cmd = __esm({
2724
+ "src/system-cmd.ts"() {
2725
+ "use strict";
2726
+ init_esm_shims();
2727
+ init_errors();
2728
+ init_output();
2729
+ }
2730
+ });
1040
2731
 
1041
2732
  // src/strategy.ts
1042
- init_esm_shims();
1043
2733
  import { Command as Command4, Option as Option3 } from "commander";
1044
- var PRESET_STEPS = {
1045
- standard: [
1046
- "context_creation",
1047
- "call_chain_guard",
1048
- "module_lookup",
1049
- "acl_check",
1050
- "approval_gate",
1051
- "middleware_before",
1052
- "input_validation",
1053
- "execute",
1054
- "output_validation",
1055
- "middleware_after",
1056
- "return_result"
1057
- ],
1058
- internal: [
1059
- "context_creation",
1060
- "call_chain_guard",
1061
- "module_lookup",
1062
- "middleware_before",
1063
- "input_validation",
1064
- "execute",
1065
- "output_validation",
1066
- "middleware_after",
1067
- "return_result"
1068
- ],
1069
- testing: [
1070
- "context_creation",
1071
- "module_lookup",
1072
- "middleware_before",
1073
- "input_validation",
1074
- "execute",
1075
- "output_validation",
1076
- "middleware_after",
1077
- "return_result"
1078
- ],
1079
- performance: [
1080
- "context_creation",
1081
- "call_chain_guard",
1082
- "module_lookup",
1083
- "acl_check",
1084
- "approval_gate",
1085
- "input_validation",
1086
- "execute",
1087
- "output_validation",
1088
- "return_result"
1089
- ],
1090
- minimal: [
1091
- "context_creation",
1092
- "module_lookup",
1093
- "execute",
1094
- "return_result"
1095
- ]
1096
- };
2734
+ function lookupStrategyInfo(executor, strategyName) {
2735
+ if (typeof executor.describePipeline === "function") {
2736
+ try {
2737
+ const current = executor.describePipeline();
2738
+ if (current && current.name === strategyName) {
2739
+ return { info: current, isCurrent: true };
2740
+ }
2741
+ } catch {
2742
+ }
2743
+ }
2744
+ const ctor = executor.constructor;
2745
+ if (ctor && typeof ctor.listStrategies === "function") {
2746
+ try {
2747
+ const all = ctor.listStrategies();
2748
+ const info = all.find((s) => s.name === strategyName) ?? null;
2749
+ return { info, isCurrent: false };
2750
+ } catch {
2751
+ return { info: null, isCurrent: false };
2752
+ }
2753
+ }
2754
+ return { info: null, isCurrent: false };
2755
+ }
1097
2756
  function registerPipelineCommand(cli, executor) {
1098
2757
  const pipelineCmd = new Command4("describe-pipeline").description("Show the execution pipeline steps for a strategy.").addOption(
1099
2758
  new Option3("--strategy <name>", "Strategy to describe (default: standard).").choices(["standard", "internal", "testing", "performance", "minimal"]).default("standard")
1100
2759
  ).option("--format <format>", "Output format.").action((opts) => {
1101
2760
  const fmt = resolveFormat(opts.format);
1102
- let strategyObj = null;
1103
- const ex = executor;
1104
- if (typeof ex._resolve_strategy_name === "function" || typeof ex._resolveStrategyName === "function") {
1105
- try {
1106
- const fn = ex._resolve_strategy_name ?? ex._resolveStrategyName;
1107
- strategyObj = fn(opts.strategy);
1108
- } catch {
1109
- strategyObj = null;
1110
- }
1111
- }
1112
- if (!strategyObj) {
1113
- const steps = PRESET_STEPS[opts.strategy] ?? [];
1114
- const pureSteps = /* @__PURE__ */ new Set([
1115
- "context_creation",
1116
- "call_chain_guard",
1117
- "module_lookup",
1118
- "acl_check",
1119
- "input_validation"
1120
- ]);
1121
- const nonRemovable = /* @__PURE__ */ new Set([
1122
- "context_creation",
1123
- "module_lookup",
1124
- "execute",
1125
- "return_result"
1126
- ]);
2761
+ const { info, isCurrent } = lookupStrategyInfo(executor, opts.strategy);
2762
+ if (info) {
2763
+ const strategySteps = isCurrent ? executor.currentStrategy?.steps ?? [] : [];
2764
+ const header = `Pipeline: ${info.name} (${info.stepCount} steps)`;
1127
2765
  if (fmt === "json" || !process.stdout.isTTY) {
1128
2766
  const payload = {
1129
- strategy: opts.strategy,
1130
- step_count: steps.length,
1131
- steps: steps.map((s, i) => ({
1132
- index: i + 1,
1133
- name: s,
1134
- pure: pureSteps.has(s),
1135
- removable: !nonRemovable.has(s)
1136
- }))
2767
+ strategy: info.name,
2768
+ step_count: info.stepCount,
2769
+ description: info.description,
2770
+ steps: info.stepNames.map((name, i) => {
2771
+ const stepMeta = strategySteps[i];
2772
+ return {
2773
+ index: i + 1,
2774
+ name,
2775
+ pure: stepMeta?.pure ?? false,
2776
+ removable: stepMeta?.removable ?? true,
2777
+ timeout_ms: stepMeta?.timeoutMs ?? null
2778
+ };
2779
+ })
1137
2780
  };
1138
2781
  process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
1139
2782
  } else {
1140
- process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
2783
+ process.stdout.write(`${header}
1141
2784
 
1142
2785
  `);
1143
2786
  process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
1144
2787
  `);
1145
2788
  process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
1146
2789
  `);
1147
- for (let i = 0; i < steps.length; i++) {
1148
- const pure = pureSteps.has(steps[i]) ? "yes" : "no";
1149
- const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
1150
- process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
2790
+ for (let i = 0; i < info.stepNames.length; i++) {
2791
+ const stepMeta = strategySteps[i];
2792
+ const pure = stepMeta?.pure ? "yes" : "no";
2793
+ const removable = stepMeta?.removable !== false ? "yes" : "no";
2794
+ const timeout = stepMeta?.timeoutMs ? `${stepMeta.timeoutMs}ms` : "\u2014";
2795
+ process.stdout.write(` ${String(i + 1).padEnd(4)} ${info.stepNames[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
1151
2796
  `);
1152
2797
  }
1153
2798
  }
1154
2799
  return;
1155
2800
  }
1156
- const stepsInfo = strategyObj.steps.map((step) => ({
1157
- name: step.name,
1158
- pure: step.pure ?? false,
1159
- removable: step.removable ?? true,
1160
- timeout_ms: step.timeout_ms ?? null
1161
- }));
2801
+ const steps = PRESET_STEPS[opts.strategy] ?? [];
2802
+ const pureSteps = /* @__PURE__ */ new Set([
2803
+ "context_creation",
2804
+ "call_chain_guard",
2805
+ "module_lookup",
2806
+ "acl_check",
2807
+ "input_validation"
2808
+ ]);
2809
+ const nonRemovable = /* @__PURE__ */ new Set([
2810
+ "context_creation",
2811
+ "module_lookup",
2812
+ "execute",
2813
+ "return_result"
2814
+ ]);
1162
2815
  if (fmt === "json" || !process.stdout.isTTY) {
1163
2816
  const payload = {
1164
2817
  strategy: opts.strategy,
1165
- step_count: stepsInfo.length,
1166
- steps: stepsInfo.map((s, i) => ({ index: i + 1, ...s }))
2818
+ step_count: steps.length,
2819
+ steps: steps.map((s, i) => ({
2820
+ index: i + 1,
2821
+ name: s,
2822
+ pure: pureSteps.has(s),
2823
+ removable: !nonRemovable.has(s)
2824
+ }))
1167
2825
  };
1168
2826
  process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
1169
2827
  } else {
1170
- process.stdout.write(`Pipeline: ${opts.strategy} (${stepsInfo.length} steps)
2828
+ process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
1171
2829
 
1172
2830
  `);
1173
2831
  process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
1174
2832
  `);
1175
2833
  process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
1176
2834
  `);
1177
- for (let i = 0; i < stepsInfo.length; i++) {
1178
- const s = stepsInfo[i];
1179
- const pure = s.pure ? "yes" : "no";
1180
- const removable = s.removable ? "yes" : "no";
1181
- const timeout = s.timeout_ms !== null ? `${s.timeout_ms}ms` : "\u2014";
1182
- process.stdout.write(` ${String(i + 1).padEnd(4)} ${s.name.padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
2835
+ for (let i = 0; i < steps.length; i++) {
2836
+ const pure = pureSteps.has(steps[i]) ? "yes" : "no";
2837
+ const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
2838
+ process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
1183
2839
  `);
1184
2840
  }
1185
- }
2841
+ }
2842
+ });
2843
+ cli.addCommand(pipelineCmd);
2844
+ }
2845
+ var PRESET_STEPS;
2846
+ var init_strategy = __esm({
2847
+ "src/strategy.ts"() {
2848
+ "use strict";
2849
+ init_esm_shims();
2850
+ init_output();
2851
+ PRESET_STEPS = {
2852
+ standard: [
2853
+ "context_creation",
2854
+ "call_chain_guard",
2855
+ "module_lookup",
2856
+ "acl_check",
2857
+ "approval_gate",
2858
+ "middleware_before",
2859
+ "input_validation",
2860
+ "execute",
2861
+ "output_validation",
2862
+ "middleware_after",
2863
+ "return_result"
2864
+ ],
2865
+ internal: [
2866
+ "context_creation",
2867
+ "call_chain_guard",
2868
+ "module_lookup",
2869
+ "middleware_before",
2870
+ "input_validation",
2871
+ "execute",
2872
+ "output_validation",
2873
+ "middleware_after",
2874
+ "return_result"
2875
+ ],
2876
+ testing: [
2877
+ "context_creation",
2878
+ "module_lookup",
2879
+ "middleware_before",
2880
+ "input_validation",
2881
+ "execute",
2882
+ "output_validation",
2883
+ "middleware_after",
2884
+ "return_result"
2885
+ ],
2886
+ performance: [
2887
+ "context_creation",
2888
+ "call_chain_guard",
2889
+ "module_lookup",
2890
+ "acl_check",
2891
+ "approval_gate",
2892
+ "input_validation",
2893
+ "execute",
2894
+ "output_validation",
2895
+ "return_result"
2896
+ ],
2897
+ minimal: [
2898
+ "context_creation",
2899
+ "module_lookup",
2900
+ "execute",
2901
+ "return_result"
2902
+ ]
2903
+ };
2904
+ }
2905
+ });
2906
+
2907
+ // src/builtin-group.ts
2908
+ var RESERVED_GROUP_NAMES, VALID_USER_MODES, APCLI_SUBCOMMAND_NAMES, ApcliGroup;
2909
+ var init_builtin_group = __esm({
2910
+ "src/builtin-group.ts"() {
2911
+ "use strict";
2912
+ init_esm_shims();
2913
+ init_errors();
2914
+ init_logger();
2915
+ RESERVED_GROUP_NAMES = /* @__PURE__ */ new Set(["apcli"]);
2916
+ VALID_USER_MODES = /* @__PURE__ */ new Set([
2917
+ "all",
2918
+ "none",
2919
+ "include",
2920
+ "exclude"
2921
+ ]);
2922
+ APCLI_SUBCOMMAND_NAMES = /* @__PURE__ */ new Set([
2923
+ "list",
2924
+ "describe",
2925
+ "exec",
2926
+ "validate",
2927
+ "init",
2928
+ "health",
2929
+ "usage",
2930
+ "enable",
2931
+ "disable",
2932
+ "reload",
2933
+ "config",
2934
+ "completion",
2935
+ "describe-pipeline"
2936
+ ]);
2937
+ ApcliGroup = class _ApcliGroup {
2938
+ _mode;
2939
+ _include;
2940
+ _exclude;
2941
+ _disableEnv;
2942
+ _registryInjected;
2943
+ _fromCliConfig;
2944
+ constructor(init) {
2945
+ this._mode = init.mode;
2946
+ this._include = init.include;
2947
+ this._exclude = init.exclude;
2948
+ this._disableEnv = init.disableEnv;
2949
+ this._registryInjected = init.registryInjected;
2950
+ this._fromCliConfig = init.fromCliConfig;
2951
+ }
2952
+ /**
2953
+ * Tier 1 constructor — config came from `createCli({ apcli })`.
2954
+ *
2955
+ * A non-auto mode from this tier wins over env var and yaml.
2956
+ */
2957
+ static fromCliConfig(config, opts) {
2958
+ return _ApcliGroup._build(
2959
+ config,
2960
+ opts,
2961
+ /*fromCliConfig*/
2962
+ true
2963
+ );
2964
+ }
2965
+ /**
2966
+ * Tier 3 constructor — config came from `apcore.yaml`.
2967
+ *
2968
+ * Env var (Tier 2) may override the yaml-supplied mode.
2969
+ */
2970
+ static fromYaml(config, opts) {
2971
+ return _ApcliGroup._build(
2972
+ config,
2973
+ opts,
2974
+ /*fromCliConfig*/
2975
+ false
2976
+ );
2977
+ }
2978
+ /**
2979
+ * Non-panicking Tier 3 factory (A-001 parity with Rust's `try_from_yaml`).
2980
+ * Returns `[instance, null]` on success or `[null, errorMessage]` on invalid input.
2981
+ * Use this in programmatic contexts where throwing/exiting is unwanted.
2982
+ */
2983
+ static tryFromYaml(config, opts) {
2984
+ if (config !== null && config !== void 0 && typeof config !== "boolean" && typeof config !== "object") {
2985
+ return [null, `apcore.yaml 'apcli:' must be a bool, object, or null; got ${typeof config}`];
2986
+ }
2987
+ if (config !== null && config !== void 0 && typeof config === "object" && !Array.isArray(config)) {
2988
+ const mode = config["mode"];
2989
+ if (mode !== void 0 && mode !== null) {
2990
+ const validModes = ["all", "none", "include", "exclude"];
2991
+ if (typeof mode !== "string" || !validModes.includes(mode)) {
2992
+ return [null, `Invalid apcli mode: '${mode}'. Must be one of: all, none, include, exclude.`];
2993
+ }
2994
+ }
2995
+ }
2996
+ return [_ApcliGroup.fromYaml(config, opts), null];
2997
+ }
2998
+ // -------------------------------------------------------------------------
2999
+ // Internal builder — shared by both factories
3000
+ // -------------------------------------------------------------------------
3001
+ static _build(config, opts, fromCliConfig) {
3002
+ if (config === true) {
3003
+ return new _ApcliGroup({
3004
+ mode: "all",
3005
+ include: [],
3006
+ exclude: [],
3007
+ disableEnv: false,
3008
+ registryInjected: opts.registryInjected,
3009
+ fromCliConfig
3010
+ });
3011
+ }
3012
+ if (config === false) {
3013
+ return new _ApcliGroup({
3014
+ mode: "none",
3015
+ include: [],
3016
+ exclude: [],
3017
+ disableEnv: false,
3018
+ registryInjected: opts.registryInjected,
3019
+ fromCliConfig
3020
+ });
3021
+ }
3022
+ if (config === void 0 || config === null) {
3023
+ return new _ApcliGroup({
3024
+ mode: "auto",
3025
+ include: [],
3026
+ exclude: [],
3027
+ disableEnv: false,
3028
+ registryInjected: opts.registryInjected,
3029
+ fromCliConfig
3030
+ });
3031
+ }
3032
+ if (typeof config !== "object" || Array.isArray(config)) {
3033
+ process.stderr.write(
3034
+ `Error: apcli config must be a boolean or object; got ${Array.isArray(config) ? "array" : typeof config}.
3035
+ `
3036
+ );
3037
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3038
+ }
3039
+ const cfg = config;
3040
+ let mode;
3041
+ if (cfg.mode === void 0 || cfg.mode === null) {
3042
+ mode = "auto";
3043
+ } else if (typeof cfg.mode !== "string") {
3044
+ process.stderr.write(
3045
+ `Error: apcli.mode must be a string; got ${typeof cfg.mode}. Expected one of all|none|include|exclude.
3046
+ `
3047
+ );
3048
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3049
+ } else if (!VALID_USER_MODES.has(cfg.mode)) {
3050
+ process.stderr.write(
3051
+ `Error: apcli.mode '${cfg.mode}' is invalid. Expected one of all|none|include|exclude.
3052
+ `
3053
+ );
3054
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3055
+ } else {
3056
+ mode = cfg.mode;
3057
+ }
3058
+ const include = _ApcliGroup._normalizeList(cfg.include, "include");
3059
+ const exclude = _ApcliGroup._normalizeList(cfg.exclude, "exclude");
3060
+ const rawDisableEnv = cfg.disableEnv !== void 0 ? cfg.disableEnv : cfg["disable_env"];
3061
+ let disableEnv = false;
3062
+ if (rawDisableEnv !== void 0) {
3063
+ if (typeof rawDisableEnv === "boolean") {
3064
+ disableEnv = rawDisableEnv;
3065
+ } else {
3066
+ warn(
3067
+ `apcli.disable_env must be boolean; got ${typeof rawDisableEnv}. Treating as false.`
3068
+ );
3069
+ }
3070
+ }
3071
+ return new _ApcliGroup({
3072
+ mode,
3073
+ include,
3074
+ exclude,
3075
+ disableEnv,
3076
+ registryInjected: opts.registryInjected,
3077
+ fromCliConfig
3078
+ });
3079
+ }
3080
+ /**
3081
+ * Normalize an include/exclude list. Non-array → warn and return [].
3082
+ *
3083
+ * Unknown but well-formed entries emit a WARNING (spec §7 error table,
3084
+ * T-APCLI-25) but are retained in the returned list for forward-compat —
3085
+ * if apcore-cli later adds a subcommand named `foo`, existing configs
3086
+ * continue to work without a config change. At runtime, unknown names
3087
+ * simply never match any registered subcommand.
3088
+ */
3089
+ static _normalizeList(raw, label) {
3090
+ if (raw === void 0 || raw === null) return [];
3091
+ if (!Array.isArray(raw)) {
3092
+ warn(`apcli.${label} must be a list; got ${typeof raw}. Ignoring.`);
3093
+ return [];
3094
+ }
3095
+ const out = [];
3096
+ for (const entry of raw) {
3097
+ if (typeof entry === "string" && entry.length > 0) {
3098
+ if (!APCLI_SUBCOMMAND_NAMES.has(entry)) {
3099
+ warn(
3100
+ `Unknown apcli subcommand '${entry}' in ${label} list \u2014 ignoring.`
3101
+ );
3102
+ }
3103
+ out.push(entry);
3104
+ } else {
3105
+ warn(`apcli.${label} contains non-string entry; skipping.`);
3106
+ }
3107
+ }
3108
+ return out;
3109
+ }
3110
+ // -------------------------------------------------------------------------
3111
+ // Public API
3112
+ // -------------------------------------------------------------------------
3113
+ /**
3114
+ * Resolve effective visibility mode after applying tier precedence.
3115
+ *
3116
+ * Returns one of `"all" | "none" | "include" | "exclude"` — never `"auto"`.
3117
+ *
3118
+ * Tier order (spec §4.4):
3119
+ * 1. CliConfig non-auto wins outright.
3120
+ * 2. `APCORE_CLI_APCLI` env var (unless sealed by disableEnv).
3121
+ * 3. yaml non-auto.
3122
+ * 4. Auto-detect from registryInjected.
3123
+ */
3124
+ resolveVisibility() {
3125
+ if (this._fromCliConfig && this._mode !== "auto") {
3126
+ return this._mode;
3127
+ }
3128
+ if (!this._disableEnv) {
3129
+ const envMode = this._parseEnv(process.env.APCORE_CLI_APCLI);
3130
+ if (envMode !== null) {
3131
+ return envMode;
3132
+ }
3133
+ }
3134
+ if (this._mode !== "auto") {
3135
+ return this._mode;
3136
+ }
3137
+ return this._registryInjected ? "none" : "all";
3138
+ }
3139
+ /**
3140
+ * True iff `subcommand` passes the include/exclude filter.
3141
+ *
3142
+ * Callers MUST first check {@link resolveVisibility} — this method throws
3143
+ * under modes `"all"` or `"none"` (caller bug per spec §4.6).
3144
+ */
3145
+ isSubcommandIncluded(subcommand) {
3146
+ const mode = this.resolveVisibility();
3147
+ if (mode === "include") return this._include.includes(subcommand);
3148
+ if (mode === "exclude") return !this._exclude.includes(subcommand);
3149
+ throw new Error(
3150
+ `isSubcommandIncluded called under mode '${mode}'; caller should bypass.`
3151
+ );
3152
+ }
3153
+ /** True iff the `apcli` group itself should appear in root `--help`. */
3154
+ isGroupVisible() {
3155
+ return this.resolveVisibility() !== "none";
3156
+ }
3157
+ // -------------------------------------------------------------------------
3158
+ // Env parser (Tier 2) — co-located per spec §4.4
3159
+ // -------------------------------------------------------------------------
3160
+ /**
3161
+ * Parse APCORE_CLI_APCLI. Case-insensitive.
3162
+ *
3163
+ * - `show` / `1` / `true` → `"all"`
3164
+ * - `hide` / `0` / `false` → `"none"`
3165
+ * - Empty / unset → `null`
3166
+ * - Anything else → warn and return `null`
3167
+ */
3168
+ _parseEnv(raw) {
3169
+ if (raw === void 0 || raw === "") return null;
3170
+ const normalized = raw.toLowerCase();
3171
+ if (normalized === "show" || normalized === "1" || normalized === "true") {
3172
+ return "all";
3173
+ }
3174
+ if (normalized === "hide" || normalized === "0" || normalized === "false") {
3175
+ return "none";
3176
+ }
3177
+ warn(
3178
+ `Unknown APCORE_CLI_APCLI value '${raw}', ignoring. Expected: show, hide, 1, 0, true, false.`
3179
+ );
3180
+ return null;
3181
+ }
3182
+ };
3183
+ }
3184
+ });
3185
+
3186
+ // src/exposure.ts
3187
+ function escapeRegex(str) {
3188
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3189
+ }
3190
+ function compilePattern(pattern) {
3191
+ const sentinel = "\0GLOB\0";
3192
+ const escaped = pattern.replaceAll("**", sentinel);
3193
+ const parts = escaped.split("*");
3194
+ const regexParts = parts.map((p) => {
3195
+ const restored = p.replaceAll(sentinel, "**");
3196
+ return escapeRegex(restored);
1186
3197
  });
1187
- cli.addCommand(pipelineCmd);
3198
+ let regex = regexParts.join("[^.]*");
3199
+ regex = regex.replaceAll("\\*\\*", ".+");
3200
+ return new RegExp(`^${regex}$`);
1188
3201
  }
3202
+ var ExposureFilter;
3203
+ var init_exposure = __esm({
3204
+ "src/exposure.ts"() {
3205
+ "use strict";
3206
+ init_esm_shims();
3207
+ init_logger();
3208
+ ExposureFilter = class _ExposureFilter {
3209
+ static VALID_MODES = ["all", "include", "exclude", "none"];
3210
+ _mode;
3211
+ _compiledInclude;
3212
+ _compiledExclude;
3213
+ constructor(mode = "all", include, exclude) {
3214
+ if (!_ExposureFilter.VALID_MODES.includes(mode)) {
3215
+ process.stderr.write(
3216
+ `Warning: Unknown ExposureFilter mode '${mode}' \u2014 defaulting to 'none'. Valid modes: ${_ExposureFilter.VALID_MODES.join(", ")}.
3217
+ `
3218
+ );
3219
+ mode = "none";
3220
+ }
3221
+ this._mode = mode;
3222
+ const dedup = (arr) => [...new Set(arr)];
3223
+ this._compiledInclude = dedup(include ?? []).map(compilePattern);
3224
+ this._compiledExclude = dedup(exclude ?? []).map(compilePattern);
3225
+ }
3226
+ /** Return true if the module should be exposed as a CLI command. */
3227
+ isExposed(moduleId) {
3228
+ if (this._mode === "all") return true;
3229
+ if (this._mode === "include") {
3230
+ return this._compiledInclude.some((rx) => rx.test(moduleId));
3231
+ }
3232
+ if (this._mode === "exclude") {
3233
+ return !this._compiledExclude.some((rx) => rx.test(moduleId));
3234
+ }
3235
+ return false;
3236
+ }
3237
+ /** Partition moduleIds into [exposed, hidden] lists. */
3238
+ filterModules(moduleIds) {
3239
+ const exposed = [];
3240
+ const hidden = [];
3241
+ for (const mid of moduleIds) {
3242
+ (this.isExposed(mid) ? exposed : hidden).push(mid);
3243
+ }
3244
+ return [exposed, hidden];
3245
+ }
3246
+ /**
3247
+ * Create an ExposureFilter from a parsed config dict.
3248
+ *
3249
+ * Expected: `{ expose: { mode: "include", include: ["admin.*"] } }`
3250
+ */
3251
+ static fromConfig(config) {
3252
+ const expose = config.expose ?? {};
3253
+ if (typeof expose !== "object" || expose === null || Array.isArray(expose)) {
3254
+ warn("Invalid 'expose' config (expected dict), using mode: all.");
3255
+ return new _ExposureFilter();
3256
+ }
3257
+ const exposeObj = expose;
3258
+ const mode = exposeObj.mode ?? "all";
3259
+ if (!["all", "include", "exclude"].includes(mode)) {
3260
+ throw new Error(
3261
+ `Invalid expose mode: '${mode}'. Must be one of: all, include, exclude.`
3262
+ );
3263
+ }
3264
+ let include = exposeObj.include ?? [];
3265
+ if (!Array.isArray(include)) {
3266
+ warn("Invalid 'expose.include' (expected list), ignoring.");
3267
+ include = [];
3268
+ }
3269
+ let exclude = exposeObj.exclude ?? [];
3270
+ if (!Array.isArray(exclude)) {
3271
+ warn("Invalid 'expose.exclude' (expected list), ignoring.");
3272
+ exclude = [];
3273
+ }
3274
+ const filterList = (arr, label) => {
3275
+ const result = [];
3276
+ for (const p of arr) {
3277
+ if (!p) {
3278
+ warn(`Empty pattern in expose.${label}, skipping.`);
3279
+ } else {
3280
+ result.push(String(p));
3281
+ }
3282
+ }
3283
+ return result;
3284
+ };
3285
+ return new _ExposureFilter(
3286
+ mode,
3287
+ filterList(include, "include"),
3288
+ filterList(exclude, "exclude")
3289
+ );
3290
+ }
3291
+ };
3292
+ }
3293
+ });
1189
3294
 
1190
- // src/cli.ts
1191
- init_esm_shims();
1192
- import { Command as Command5 } from "commander";
1193
- var BUILTIN_COMMANDS = [
1194
- "completion",
1195
- "config",
1196
- "describe",
1197
- "describe-pipeline",
1198
- "disable",
1199
- "enable",
1200
- "exec",
1201
- "health",
1202
- "init",
1203
- "list",
1204
- "man",
1205
- "reload",
1206
- "usage",
1207
- "validate"
1208
- ];
3295
+ // src/canonical-help.ts
3296
+ function resolveHelpText(cmd, section) {
3297
+ const bag = cmd._helpText;
3298
+ const v = bag?.[section];
3299
+ if (typeof v === "function") return v({ error: false, command: cmd });
3300
+ return v ?? "";
3301
+ }
3302
+ function uppercasePlaceholders(flags) {
3303
+ return flags.replace(/<([a-zA-Z0-9_-]+)>/g, (_, name) => `<${name.toUpperCase()}>`).replace(/\[([a-zA-Z0-9_-]+)\]/g, (_, name) => `[${name.toUpperCase()}]`);
3304
+ }
3305
+ function optionTerm(opt) {
3306
+ const flags = uppercasePlaceholders(opt.flags);
3307
+ if (!opt.short && flags.startsWith("--")) return " " + flags;
3308
+ return flags;
3309
+ }
3310
+ function optionDescription(opt) {
3311
+ let desc = opt.description;
3312
+ const d = opt.defaultValue;
3313
+ if (d !== void 0 && d !== false && d !== "" && d !== null) {
3314
+ desc = `${desc} [default: ${String(d)}]`;
3315
+ }
3316
+ return desc;
3317
+ }
3318
+ function reorderHelpVersionLast(opts) {
3319
+ const helpOpts = [];
3320
+ const versionOpts = [];
3321
+ const rest = [];
3322
+ for (const o of opts) {
3323
+ if (o.long === "--help") helpOpts.push(o);
3324
+ else if (o.long === "--version") versionOpts.push(o);
3325
+ else rest.push(o);
3326
+ }
3327
+ return [...rest, ...helpOpts, ...versionOpts];
3328
+ }
3329
+ function canonicalFormatHelp(cmd, helper) {
3330
+ const sections = [];
3331
+ const beforeAll = resolveHelpText(cmd, "beforeAll");
3332
+ if (beforeAll) sections.push(beforeAll);
3333
+ const desc = cmd.description();
3334
+ if (desc) sections.push(desc);
3335
+ const before = resolveHelpText(cmd, "before");
3336
+ if (before) sections.push(before);
3337
+ const visibleOpts = reorderHelpVersionLast(helper.visibleOptions(cmd));
3338
+ const visibleCmds = helper.visibleCommands(cmd);
3339
+ const args = cmd.registeredArguments ?? [];
3340
+ let usage = `Usage: ${cmd.name()}`;
3341
+ if (visibleOpts.length > 0) usage += " [OPTIONS]";
3342
+ for (const a of args) {
3343
+ const n = a.name().toUpperCase();
3344
+ usage += a.required ? ` <${n}>` : ` [${n}]`;
3345
+ }
3346
+ if (visibleCmds.length > 0) usage += " [COMMAND]";
3347
+ sections.push(usage);
3348
+ if (visibleCmds.length > 0) {
3349
+ const terms = visibleCmds.map((c) => c.name());
3350
+ const w = Math.max(...terms.map((t) => t.length));
3351
+ const lines = ["Commands:"];
3352
+ visibleCmds.forEach((sub, i) => {
3353
+ lines.push(` ${terms[i].padEnd(w)} ${sub.description()}`);
3354
+ });
3355
+ sections.push(lines.join("\n"));
3356
+ }
3357
+ if (visibleOpts.length > 0) {
3358
+ const terms = visibleOpts.map(optionTerm);
3359
+ const w = Math.max(...terms.map((t) => t.length));
3360
+ const lines = ["Options:"];
3361
+ visibleOpts.forEach((opt, i) => {
3362
+ lines.push(` ${terms[i].padEnd(w)} ${optionDescription(opt)}`);
3363
+ });
3364
+ sections.push(lines.join("\n"));
3365
+ }
3366
+ const after = resolveHelpText(cmd, "after");
3367
+ if (after) sections.push(after);
3368
+ const afterAll = resolveHelpText(cmd, "afterAll");
3369
+ if (afterAll) sections.push(afterAll);
3370
+ return sections.join("\n\n") + "\n";
3371
+ }
3372
+ var init_canonical_help = __esm({
3373
+ "src/canonical-help.ts"() {
3374
+ "use strict";
3375
+ init_esm_shims();
3376
+ }
3377
+ });
1209
3378
 
1210
3379
  // src/main.ts
1211
- var __dirname3 = path4.dirname(fileURLToPath3(import.meta.url));
1212
- var verboseHelp = false;
3380
+ var main_exports = {};
3381
+ __export(main_exports, {
3382
+ applyToolkitIntegration: () => applyToolkitIntegration,
3383
+ buildModuleCommand: () => buildModuleCommand,
3384
+ clearBindingDisplayMap: () => clearBindingDisplayMap,
3385
+ collectInput: () => collectInput,
3386
+ createCli: () => createCli,
3387
+ docsUrl: () => docsUrl,
3388
+ emitErrorJson: () => emitErrorJson,
3389
+ emitErrorTty: () => emitErrorTty,
3390
+ lookupBindingDisplay: () => lookupBindingDisplay,
3391
+ main: () => main,
3392
+ reconvertEnumValues: () => reconvertEnumValues,
3393
+ resolveIntOption: () => resolveIntOption,
3394
+ resolveStringOption: () => resolveStringOption,
3395
+ setDocsUrl: () => setDocsUrl,
3396
+ setVerboseHelp: () => setVerboseHelp,
3397
+ validateModuleId: () => validateModuleId,
3398
+ verboseHelp: () => verboseHelp
3399
+ });
3400
+ import { readFileSync as readFileSync2 } from "fs";
3401
+ import { fileURLToPath as fileURLToPath2 } from "url";
3402
+ import * as path4 from "path";
3403
+ import { Command as Command5, CommanderError, Option as Option4 } from "commander";
3404
+ function setVerboseHelp(verbose) {
3405
+ verboseHelp = verbose;
3406
+ }
3407
+ function setDocsUrl(url) {
3408
+ docsUrl = url;
3409
+ }
1213
3410
  function hasVerboseFlag() {
1214
3411
  return process.argv.includes("--verbose");
1215
3412
  }
1216
- var VERSION = "0.0.0";
1217
- try {
1218
- const pkg = JSON.parse(readFileSync3(path4.resolve(__dirname3, "../package.json"), "utf-8"));
1219
- VERSION = pkg.version;
1220
- } catch {
3413
+ function resolveIntOption(cliValue, envValue, defaultValue) {
3414
+ if (cliValue !== void 0 && Number.isFinite(cliValue) && cliValue > 0) {
3415
+ return cliValue;
3416
+ }
3417
+ if (envValue !== void 0 && envValue !== "") {
3418
+ const parsed = parseInt(envValue, 10);
3419
+ if (Number.isFinite(parsed) && parsed > 0) {
3420
+ return parsed;
3421
+ }
3422
+ process.stderr.write(
3423
+ `Warning: invalid integer env value '${envValue}'; using default ${defaultValue}.
3424
+ `
3425
+ );
3426
+ }
3427
+ return defaultValue;
3428
+ }
3429
+ function resolveStringOption(cliValue, envValue) {
3430
+ if (typeof cliValue === "string" && cliValue !== "") {
3431
+ return cliValue;
3432
+ }
3433
+ if (envValue !== void 0 && envValue !== "") {
3434
+ return envValue;
3435
+ }
3436
+ return void 0;
3437
+ }
3438
+ function emitErrorJson(e, exitCode) {
3439
+ const err = e instanceof Error ? e : new Error(String(e));
3440
+ const errRecord = err;
3441
+ const code = errRecord.code ?? "UNKNOWN";
3442
+ const payload = {
3443
+ error: true,
3444
+ code,
3445
+ message: err.message,
3446
+ exit_code: exitCode
3447
+ };
3448
+ for (const field of ["details", "suggestion", "ai_guidance", "retryable", "user_fixable"]) {
3449
+ const val = errRecord[field];
3450
+ if (val !== void 0 && val !== null) {
3451
+ payload[field] = val;
3452
+ }
3453
+ }
3454
+ process.stderr.write(JSON.stringify(payload) + "\n");
3455
+ }
3456
+ function emitErrorTty(e, exitCode) {
3457
+ const err = e instanceof Error ? e : new Error(String(e));
3458
+ const errRecord = err;
3459
+ const code = errRecord.code;
3460
+ const header = code ? `Error [${code}]: ${err.message}` : `Error: ${err.message}`;
3461
+ process.stderr.write(header + "\n");
3462
+ const details = errRecord.details;
3463
+ if (details && typeof details === "object" && !Array.isArray(details)) {
3464
+ process.stderr.write("\n Details:\n");
3465
+ for (const [k, v] of Object.entries(details)) {
3466
+ process.stderr.write(` ${k}: ${v}
3467
+ `);
3468
+ }
3469
+ }
3470
+ const suggestion = errRecord.suggestion;
3471
+ if (suggestion) {
3472
+ process.stderr.write(`
3473
+ Suggestion: ${suggestion}
3474
+ `);
3475
+ }
3476
+ const retryable = errRecord.retryable;
3477
+ if (retryable !== void 0 && retryable !== null) {
3478
+ const label = retryable ? "Yes" : "No (same input will fail again)";
3479
+ process.stderr.write(` Retryable: ${label}
3480
+ `);
3481
+ }
3482
+ process.stderr.write(`
3483
+ Exit code: ${exitCode}
3484
+ `);
1221
3485
  }
1222
3486
  function createCli(extensionsDirOrOpts, progName, verbose = false) {
1223
3487
  let extensionsDir;
1224
3488
  let registry;
1225
3489
  let executor;
1226
3490
  let extraCommands;
3491
+ let app;
3492
+ let expose;
3493
+ let apcliOption;
1227
3494
  if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
1228
3495
  extensionsDir = extensionsDirOrOpts.extensionsDir;
1229
3496
  progName = extensionsDirOrOpts.progName ?? progName;
1230
3497
  verbose = extensionsDirOrOpts.verbose ?? verbose;
3498
+ app = extensionsDirOrOpts.app;
1231
3499
  registry = extensionsDirOrOpts.registry;
1232
3500
  executor = extensionsDirOrOpts.executor;
1233
3501
  extraCommands = extensionsDirOrOpts.extraCommands;
3502
+ expose = extensionsDirOrOpts.expose;
3503
+ apcliOption = extensionsDirOrOpts.apcli;
1234
3504
  } else {
1235
3505
  extensionsDir = extensionsDirOrOpts;
1236
3506
  }
1237
3507
  verboseHelp = verbose;
1238
3508
  registerConfigNamespace();
3509
+ try {
3510
+ const auditLogger = new AuditLogger();
3511
+ setAuditLogger(auditLogger);
3512
+ } catch {
3513
+ }
1239
3514
  const resolvedProgName = progName ?? path4.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
1240
3515
  const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
1241
3516
  setLogLevel(cliLogLevel);
1242
- const program = new Command6(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)");
3517
+ if (app && (registry || executor)) {
3518
+ process.stderr.write("Error: app is mutually exclusive with registry/executor\n");
3519
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3520
+ }
3521
+ if (app) {
3522
+ registry = app.registry;
3523
+ executor = app.executor;
3524
+ }
1243
3525
  if (executor && !registry) {
1244
- throw new Error("executor requires registry \u2014 pass both or neither");
3526
+ process.stderr.write("Error: executor requires registry \u2014 pass both or neither\n");
3527
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3528
+ }
3529
+ if (executor && typeof executor.setApprovalHandler === "function") {
3530
+ try {
3531
+ const handler = new CliApprovalHandler(
3532
+ /*autoApprove*/
3533
+ false
3534
+ );
3535
+ executor.setApprovalHandler(handler);
3536
+ } catch {
3537
+ }
3538
+ }
3539
+ const registryInjected = registry !== void 0;
3540
+ const program = new Command5(resolvedProgName).exitOverride().version(VERSION, "-V, --version", "Print version").helpOption("-h, --help", "Print help").addHelpCommand("help [command]", "Print this message or the help of the given subcommand(s)").description("apcore CLI \u2014 execute apcore modules from the command line").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING").option("--verbose", "Show all options in help output (including built-in apcore options)");
3541
+ program.configureHelp({ formatHelp: canonicalFormatHelp });
3542
+ if (!registryInjected) {
3543
+ program.option("--extensions-dir <path>", "Path to extensions directory");
3544
+ program.option("--commands-dir <path>", "Path to convention-based commands directory");
3545
+ program.option("--binding <path>", "Path to binding.yaml for display overlay");
3546
+ }
3547
+ let apcliCfg;
3548
+ if (apcliOption instanceof ApcliGroup) {
3549
+ apcliCfg = apcliOption;
3550
+ } else if (apcliOption !== void 0) {
3551
+ apcliCfg = ApcliGroup.fromCliConfig(apcliOption, { registryInjected });
3552
+ } else {
3553
+ let yamlVal = null;
3554
+ try {
3555
+ const resolver = new ConfigResolver();
3556
+ yamlVal = resolver.resolveObject("apcli");
3557
+ } catch {
3558
+ yamlVal = null;
3559
+ }
3560
+ apcliCfg = ApcliGroup.fromYaml(yamlVal, { registryInjected });
1245
3561
  }
3562
+ const apcliGroup = program.command("apcli", { hidden: !apcliCfg.isGroupVisible() }).description("apcore-cli built-in commands");
1246
3563
  if (registry) {
1247
3564
  program._registry = registry;
1248
3565
  if (executor) {
1249
3566
  program._executor = executor;
1250
- registerValidateCommand(program, registry, executor);
1251
- void registerSystemCommands(program, executor);
1252
- registerPipelineCommand(program, executor);
1253
3567
  }
1254
3568
  } else {
1255
3569
  const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
1256
3570
  void resolvedExtDir;
1257
3571
  }
3572
+ let exposureFilter;
3573
+ try {
3574
+ if (expose instanceof ExposureFilter) {
3575
+ exposureFilter = expose;
3576
+ } else if (typeof expose === "object" && expose !== null) {
3577
+ exposureFilter = ExposureFilter.fromConfig({ expose });
3578
+ } else {
3579
+ exposureFilter = new ExposureFilter();
3580
+ }
3581
+ } catch (err) {
3582
+ const msg = err instanceof Error ? err.message : String(err);
3583
+ process.stderr.write(`Error: invalid 'expose' option \u2014 ${msg}
3584
+ `);
3585
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3586
+ }
3587
+ _registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter);
3588
+ _registerDeprecationShims(program, apcliGroup, registryInjected, resolvedProgName);
1258
3589
  program.addHelpText("after", [
1259
3590
  "",
1260
3591
  "Use --help --verbose to show all options (including built-in apcore options).",
1261
3592
  "Use --help --man to display a formatted man page."
1262
3593
  ].join("\n"));
1263
- registerInitCommand(program);
1264
3594
  configureManHelp(program, resolvedProgName, VERSION);
1265
3595
  if (extraCommands && extraCommands.length > 0) {
1266
- const existingNames = /* @__PURE__ */ new Set([
1267
- ...BUILTIN_COMMANDS,
1268
- ...program.commands.map((c) => c.name())
1269
- ]);
1270
3596
  for (const cmd of extraCommands) {
1271
3597
  const cmdName = cmd.name();
1272
- if (existingNames.has(cmdName)) {
3598
+ if (RESERVED_GROUP_NAMES.has(cmdName)) {
1273
3599
  process.stderr.write(
1274
- `Warning: Extra command '${cmdName}' collides with a built-in command and will be skipped.
3600
+ `Error: extraCommands name '${cmdName}' is reserved
1275
3601
  `
1276
3602
  );
1277
- continue;
3603
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3604
+ }
3605
+ const existing = program.commands.find((c) => c.name() === cmdName);
3606
+ if (existing) {
3607
+ const isShim = existing.__isDeprecationShim === true;
3608
+ if (isShim) {
3609
+ warn(
3610
+ `extraCommands '${cmdName}' overrides the deprecation shim for the same name. The shim will be removed.`
3611
+ );
3612
+ const cmds = program.commands;
3613
+ const idx = cmds.indexOf(existing);
3614
+ if (idx >= 0) cmds.splice(idx, 1);
3615
+ } else {
3616
+ process.stderr.write(
3617
+ `Error: extraCommands name '${cmdName}' collides with an existing command
3618
+ `
3619
+ );
3620
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3621
+ }
1278
3622
  }
1279
3623
  program.addCommand(cmd);
1280
- existingNames.add(cmdName);
1281
3624
  }
1282
3625
  }
1283
3626
  program.hook("preAction", async (thisCommand) => {
@@ -1288,25 +3631,132 @@ function createCli(extensionsDirOrOpts, progName, verbose = false) {
1288
3631
  });
1289
3632
  return program;
1290
3633
  }
3634
+ function _registerApcliSubcommands(apcliGroup, apcliCfg, registry, executor, exposureFilter) {
3635
+ const emitUnwiredError = () => {
3636
+ process.stderr.write(
3637
+ "Error: no apcore-js registry wired. In standalone mode, pass --extensions-dir <path> to enable module discovery.\n"
3638
+ );
3639
+ process.exit(EXIT_CODES.CONFIG_INVALID);
3640
+ };
3641
+ const effectiveRegistry = registry ?? {
3642
+ listModules: () => emitUnwiredError(),
3643
+ getModule: () => emitUnwiredError()
3644
+ };
3645
+ const TABLE = [
3646
+ { name: "list", requiresExecutor: false, register: (g) => registerListCommand(g, effectiveRegistry, exposureFilter) },
3647
+ { name: "describe", requiresExecutor: false, register: (g) => registerDescribeCommand(g, effectiveRegistry) },
3648
+ { name: "exec", requiresExecutor: true, register: (g, _r, ex) => registerExecCommand(g, effectiveRegistry, ex) },
3649
+ { name: "validate", requiresExecutor: true, register: (g, _r, ex) => registerValidateCommand(g, effectiveRegistry, ex) },
3650
+ { name: "init", requiresExecutor: false, register: (g) => registerInitCommand(g) },
3651
+ { name: "health", requiresExecutor: true, register: (g, _r, ex) => registerHealthCommand(g, ex) },
3652
+ { name: "usage", requiresExecutor: true, register: (g, _r, ex) => registerUsageCommand(g, ex) },
3653
+ { name: "enable", requiresExecutor: true, register: (g, _r, ex) => registerEnableCommand(g, ex) },
3654
+ { name: "disable", requiresExecutor: true, register: (g, _r, ex) => registerDisableCommand(g, ex) },
3655
+ { name: "reload", requiresExecutor: true, register: (g, _r, ex) => registerReloadCommand(g, ex) },
3656
+ { name: "config", requiresExecutor: true, register: (g, _r, ex) => registerConfigCommand(g, ex) },
3657
+ { name: "completion", requiresExecutor: false, register: (g) => registerCompletionCommand(g) },
3658
+ { name: "describe-pipeline", requiresExecutor: true, register: (g, _r, ex) => registerPipelineCommand(g, ex) }
3659
+ ];
3660
+ const mode = apcliCfg.resolveVisibility();
3661
+ for (const entry of TABLE) {
3662
+ let shouldRegister;
3663
+ if (mode === "all" || mode === "none") {
3664
+ shouldRegister = true;
3665
+ } else {
3666
+ shouldRegister = _ALWAYS_REGISTERED.has(entry.name) || apcliCfg.isSubcommandIncluded(entry.name);
3667
+ }
3668
+ if (!shouldRegister) continue;
3669
+ if (entry.requiresExecutor && !executor) {
3670
+ if (_ALWAYS_REGISTERED.has(entry.name)) {
3671
+ warn(
3672
+ `apcli.${entry.name} is in _ALWAYS_REGISTERED but no executor is wired \u2014 subcommand unavailable. Pass executor to createCli() or avoid ${entry.name} invocations.`
3673
+ );
3674
+ }
3675
+ continue;
3676
+ }
3677
+ entry.register(apcliGroup, registry, executor);
3678
+ }
3679
+ }
3680
+ function _registerDeprecationShims(root, apcliGroup, registryInjected, cliName) {
3681
+ if (registryInjected) return;
3682
+ for (const name of _DEPRECATED_ROOT_COMMANDS) {
3683
+ const apcliSub = apcliGroup.commands.find((c) => c.name() === name);
3684
+ if (!apcliSub) continue;
3685
+ if (root.commands.some((c) => c.name() === name)) continue;
3686
+ const shim = root.command(name).description(`[DEPRECATED] Use '${cliName} apcli ${name}' instead.`).allowUnknownOption(true).allowExcessArguments(true).helpOption(false);
3687
+ shim.__isDeprecationShim = true;
3688
+ shim.action(async function() {
3689
+ process.stderr.write(
3690
+ `WARNING: '${name}' as a root-level command is deprecated. Use '${cliName} apcli ${name}' instead.
3691
+ Will be removed in v0.8. See: https://aiperceivable.github.io/apcore-cli/features/builtin-group/#11-migration
3692
+ `
3693
+ );
3694
+ const tail = _collectShimForwardArgs(this);
3695
+ await apcliSub.parseAsync(tail, { from: "user" });
3696
+ });
3697
+ }
3698
+ }
3699
+ function _collectShimForwardArgs(shim) {
3700
+ const shimArgs = (shim.args ?? []).slice();
3701
+ if (shimArgs.length > 0) return shimArgs;
3702
+ const shimName = shim.name();
3703
+ const idx = process.argv.indexOf(shimName);
3704
+ if (idx < 0) return [];
3705
+ return process.argv.slice(idx + 1);
3706
+ }
3707
+ function lookupBindingDisplay(moduleId) {
3708
+ return bindingDisplayMap.get(moduleId);
3709
+ }
3710
+ function clearBindingDisplayMap() {
3711
+ bindingDisplayMap.clear();
3712
+ }
1291
3713
  async function applyToolkitIntegration(commandsDir, bindingPath) {
1292
3714
  if (!commandsDir && !bindingPath) {
1293
3715
  return;
1294
3716
  }
3717
+ let toolkit;
1295
3718
  try {
1296
3719
  const toolkitModule = "apcore-toolkit";
1297
- const toolkit = await import(
3720
+ toolkit = await import(
1298
3721
  /* @vite-ignore */
1299
3722
  toolkitModule
1300
3723
  );
1301
- if (commandsDir) {
1302
- console.warn("Convention scanning not yet available in TypeScript toolkit");
3724
+ } catch {
3725
+ warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
3726
+ return;
3727
+ }
3728
+ if (commandsDir) {
3729
+ warn("Convention scanning not available in the TypeScript toolkit");
3730
+ }
3731
+ if (bindingPath) {
3732
+ try {
3733
+ await loadBindingDisplayOverlay(toolkit, bindingPath);
3734
+ } catch (err) {
3735
+ const msg = err instanceof Error ? err.message : String(err);
3736
+ warn(`apcore-toolkit: failed to load binding '${bindingPath}': ${msg}`);
1303
3737
  }
1304
- if (bindingPath) {
1305
- const resolver = new toolkit.DisplayResolver();
1306
- void resolver;
3738
+ }
3739
+ }
3740
+ async function loadBindingDisplayOverlay(toolkit, bindingPath) {
3741
+ const BindingLoaderCtor = toolkit.BindingLoader;
3742
+ const DisplayResolverCtor = toolkit.DisplayResolver;
3743
+ if (!BindingLoaderCtor || !DisplayResolverCtor) {
3744
+ return;
3745
+ }
3746
+ const loader = new BindingLoaderCtor();
3747
+ const scanned = loader.load(bindingPath);
3748
+ const resolver = new DisplayResolverCtor();
3749
+ const resolved = resolver.resolve(scanned, { bindingPath });
3750
+ for (const mod of resolved) {
3751
+ if (!mod || typeof mod !== "object") continue;
3752
+ const entry = mod;
3753
+ const id = typeof entry.moduleId === "string" ? entry.moduleId : null;
3754
+ if (!id) continue;
3755
+ const meta = entry.metadata ?? {};
3756
+ const display = meta.display;
3757
+ if (display && typeof display === "object" && !Array.isArray(display)) {
3758
+ bindingDisplayMap.set(id, display);
1307
3759
  }
1308
- } catch {
1309
- console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
1310
3760
  }
1311
3761
  }
1312
3762
  function main(progName) {
@@ -1326,10 +3776,302 @@ function main(progName) {
1326
3776
  process.exit(code);
1327
3777
  }
1328
3778
  }
3779
+ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
3780
+ const moduleId = moduleDef.id;
3781
+ let resolvedSchema = {};
3782
+ let schemaOptions = [];
3783
+ const display = getDisplay(moduleDef);
3784
+ const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
3785
+ const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
3786
+ const cmdHelp = cliDisplay.description ?? moduleDef.description;
3787
+ const inputSchema = moduleDef.inputSchema;
3788
+ if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
3789
+ try {
3790
+ resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
3791
+ } catch {
3792
+ resolvedSchema = inputSchema;
3793
+ }
3794
+ schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
3795
+ }
3796
+ const cmd = new Command5(effectiveCmdName).description(cmdHelp);
3797
+ const inputOpt = new Option4("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
3798
+ const yesOpt = new Option4("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
3799
+ const largeInputOpt = new Option4("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
3800
+ const formatOpt = new Option4("--format <format>", "Output format: json, table, csv, yaml, jsonl.").choices(["json", "table", "csv", "yaml", "jsonl"]);
3801
+ const fieldsOpt = new Option4("--fields <fields>", "Comma-separated dot-paths to select from the result (e.g., 'status,data.count').");
3802
+ const sandboxOpt = new Option4("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
3803
+ const dryRunOpt = new Option4("--dry-run", "Run preflight checks without executing the module. Shows validation results.").default(false);
3804
+ const traceOpt = new Option4("--trace", "Show execution pipeline trace with per-step timing after the result.").default(false);
3805
+ const streamOpt = new Option4("--stream", "Stream module output as JSONL (one JSON object per line, flushed immediately).").default(false);
3806
+ const strategyOpt = new Option4("--strategy <name>", "Execution pipeline strategy: standard (default), internal, testing, performance.").choices(["standard", "internal", "testing", "performance", "minimal"]);
3807
+ const approvalTimeoutOpt = new Option4("--approval-timeout <seconds>", "Override approval prompt timeout in seconds (default: 60).").argParser(parseInt);
3808
+ const approvalTokenOpt = new Option4("--approval-token <token>", "Resume a pending approval with the given token (for async approval flows).");
3809
+ if (!verbose) {
3810
+ inputOpt.hideHelp();
3811
+ yesOpt.hideHelp();
3812
+ largeInputOpt.hideHelp();
3813
+ formatOpt.hideHelp();
3814
+ fieldsOpt.hideHelp();
3815
+ dryRunOpt.hideHelp();
3816
+ traceOpt.hideHelp();
3817
+ streamOpt.hideHelp();
3818
+ strategyOpt.hideHelp();
3819
+ approvalTimeoutOpt.hideHelp();
3820
+ approvalTokenOpt.hideHelp();
3821
+ }
3822
+ cmd.addOption(inputOpt);
3823
+ cmd.addOption(yesOpt);
3824
+ cmd.addOption(largeInputOpt);
3825
+ cmd.addOption(formatOpt);
3826
+ cmd.addOption(fieldsOpt);
3827
+ cmd.addOption(sandboxOpt);
3828
+ cmd.addOption(dryRunOpt);
3829
+ cmd.addOption(traceOpt);
3830
+ cmd.addOption(streamOpt);
3831
+ cmd.addOption(strategyOpt);
3832
+ cmd.addOption(approvalTimeoutOpt);
3833
+ cmd.addOption(approvalTokenOpt);
3834
+ const footerParts = [];
3835
+ if (!verbose) {
3836
+ footerParts.push("Use --verbose to show all options (including built-in apcore options).");
3837
+ }
3838
+ if (docsUrl) {
3839
+ footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
3840
+ }
3841
+ if (footerParts.length > 0) {
3842
+ cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
3843
+ }
3844
+ for (const opt of schemaOptions) {
3845
+ if (opt.parseArg) {
3846
+ cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
3847
+ } else {
3848
+ cmd.option(opt.flags, opt.description, opt.defaultValue);
3849
+ }
3850
+ }
3851
+ cmd.action(async (options) => {
3852
+ const stdinFlag = options.input;
3853
+ const autoApprove = options.yes;
3854
+ const largeInput = options.largeInput;
3855
+ const outputFormat = options.format;
3856
+ const outputFields = options.fields;
3857
+ const sandboxEnabled = options.sandbox;
3858
+ const dryRun = options.dryRun;
3859
+ const traceFlag = options.trace;
3860
+ const streamFlag = options.stream;
3861
+ const strategyName = resolveStringOption(options.strategy, process.env.APCORE_CLI_STRATEGY);
3862
+ const approvalTimeout = resolveIntOption(
3863
+ options.approvalTimeout,
3864
+ process.env.APCORE_CLI_APPROVAL_TIMEOUT,
3865
+ 60
3866
+ );
3867
+ const approvalToken = options.approvalToken;
3868
+ const schemaKwargs = {};
3869
+ const builtinKeys = /* @__PURE__ */ new Set([
3870
+ "input",
3871
+ "yes",
3872
+ "largeInput",
3873
+ "format",
3874
+ "fields",
3875
+ "sandbox",
3876
+ "verbose",
3877
+ "dryRun",
3878
+ "trace",
3879
+ "stream",
3880
+ "strategy",
3881
+ "approvalTimeout",
3882
+ "approvalToken"
3883
+ ]);
3884
+ for (const [k, v] of Object.entries(options)) {
3885
+ if (!builtinKeys.has(k)) {
3886
+ schemaKwargs[k] = v;
3887
+ }
3888
+ }
3889
+ let merged = {};
3890
+ const startTime = performance.now();
3891
+ try {
3892
+ merged = await collectInput(stdinFlag, schemaKwargs, largeInput);
3893
+ const reconverted = reconvertEnumValues(merged, schemaOptions);
3894
+ merged = reconverted;
3895
+ if (dryRun) {
3896
+ if (!executor.validate) {
3897
+ process.stderr.write("Error: Executor does not support validate.\n");
3898
+ process.exit(EXIT_CODES.MODULE_EXECUTE_ERROR);
3899
+ }
3900
+ const preflight = await executor.validate(moduleId, merged);
3901
+ formatPreflightResult(preflight, outputFormat);
3902
+ if (traceFlag) {
3903
+ const pureSteps = /* @__PURE__ */ new Set([
3904
+ "context_creation",
3905
+ "call_chain_guard",
3906
+ "module_lookup",
3907
+ "acl_check",
3908
+ "input_validation"
3909
+ ]);
3910
+ const allSteps = [
3911
+ "context_creation",
3912
+ "call_chain_guard",
3913
+ "module_lookup",
3914
+ "acl_check",
3915
+ "approval_gate",
3916
+ "middleware_before",
3917
+ "input_validation",
3918
+ "execute",
3919
+ "output_validation",
3920
+ "middleware_after",
3921
+ "return_result"
3922
+ ];
3923
+ process.stderr.write("\nPipeline preview (dry-run):\n");
3924
+ for (const s of allSteps) {
3925
+ if (pureSteps.has(s)) {
3926
+ process.stderr.write(` \u2713 ${s.padEnd(24)} (pure \u2014 would execute)
3927
+ `);
3928
+ } else {
3929
+ process.stderr.write(` \u25CB ${s.padEnd(24)} (impure \u2014 skipped in dry-run)
3930
+ `);
3931
+ }
3932
+ }
3933
+ }
3934
+ process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
3935
+ }
3936
+ if (approvalToken) {
3937
+ merged._approval_token = approvalToken;
3938
+ }
3939
+ await checkApproval(moduleDef, autoApprove, approvalTimeout);
3940
+ if (streamFlag) {
3941
+ if (resolveFormat(outputFormat) === "table") {
3942
+ process.stderr.write("Warning: Streaming mode always outputs JSONL; --format table is ignored.\n");
3943
+ }
3944
+ const annotations = moduleDef.annotations;
3945
+ const isStreaming = annotations?.streaming === true;
3946
+ if (!isStreaming) {
3947
+ process.stderr.write(
3948
+ `Warning: Module '${moduleId}' does not declare streaming support. Falling back to standard execution.
3949
+ `
3950
+ );
3951
+ }
3952
+ if (isStreaming && executor.stream) {
3953
+ let chunks = 0;
3954
+ for await (const chunk of executor.stream(moduleId, merged)) {
3955
+ chunks++;
3956
+ process.stdout.write(JSON.stringify(chunk) + "\n");
3957
+ if (process.stderr.isTTY) {
3958
+ process.stderr.write(`\rStreaming ${moduleId}... (${chunks} chunks)`);
3959
+ }
3960
+ }
3961
+ if (process.stderr.isTTY) {
3962
+ process.stderr.write("\n");
3963
+ }
3964
+ const durationMs2 = Math.round(performance.now() - startTime);
3965
+ const { getAuditLogger: getAuditLogger3 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3966
+ const auditLogger2 = getAuditLogger3();
3967
+ if (auditLogger2) {
3968
+ auditLogger2.logExecution(moduleId, merged, "success", 0, durationMs2);
3969
+ }
3970
+ return;
3971
+ }
3972
+ }
3973
+ if (traceFlag && executor.callWithTrace) {
3974
+ const [result2, trace] = await executor.callWithTrace(
3975
+ moduleId,
3976
+ merged,
3977
+ strategyName ? { strategy: strategyName } : void 0
3978
+ );
3979
+ const durationMs2 = Math.round(performance.now() - startTime);
3980
+ const resolved = resolveFormat(outputFormat);
3981
+ if (resolved === "json" || !process.stdout.isTTY) {
3982
+ const traceData = {
3983
+ strategy: trace.strategyName,
3984
+ total_duration_ms: trace.totalDurationMs,
3985
+ success: trace.success,
3986
+ steps: trace.steps.map((s) => ({
3987
+ name: s.name,
3988
+ duration_ms: s.durationMs,
3989
+ skipped: s.skipped,
3990
+ ...s.skipped ? { skip_reason: s.skipReason ?? null } : {}
3991
+ }))
3992
+ };
3993
+ let output;
3994
+ if (typeof result2 === "object" && result2 !== null && !Array.isArray(result2)) {
3995
+ output = { ...result2, _trace: traceData };
3996
+ } else {
3997
+ output = { result: result2, _trace: traceData };
3998
+ }
3999
+ process.stdout.write(JSON.stringify(output, null, 2) + "\n");
4000
+ } else {
4001
+ formatExecResult(result2, outputFormat, outputFields);
4002
+ const stepCount = trace.steps.length;
4003
+ process.stderr.write(
4004
+ `
4005
+ Pipeline Trace (strategy: ${trace.strategyName}, ${stepCount} steps, ${trace.totalDurationMs.toFixed(1)}ms)
4006
+ `
4007
+ );
4008
+ for (const s of trace.steps) {
4009
+ if (s.skipped) {
4010
+ const reason = s.skipReason ?? "n/a";
4011
+ process.stderr.write(` \u25CB ${s.name.padEnd(24)} ${"\u2014".padStart(8)} skipped (${reason})
4012
+ `);
4013
+ } else {
4014
+ process.stderr.write(` \u2713 ${s.name.padEnd(24)} ${(s.durationMs.toFixed(1) + "ms").padStart(8)}
4015
+ `);
4016
+ }
4017
+ }
4018
+ }
4019
+ const { getAuditLogger: getAL2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
4020
+ const al2 = getAL2();
4021
+ if (al2) {
4022
+ al2.logExecution(moduleId, merged, "success", 0, durationMs2);
4023
+ }
4024
+ return;
4025
+ }
4026
+ let result;
4027
+ if (strategyName && executor.callWithTrace) {
4028
+ const [res] = await executor.callWithTrace(
4029
+ moduleId,
4030
+ merged,
4031
+ { strategy: strategyName }
4032
+ );
4033
+ result = res;
4034
+ if (strategyName !== "standard" && process.stderr.isTTY) {
4035
+ process.stderr.write(`Warning: Using '${strategyName}' strategy.
4036
+ `);
4037
+ }
4038
+ } else {
4039
+ const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
4040
+ const sandbox = new Sandbox2(sandboxEnabled);
4041
+ result = await sandbox.execute(moduleId, merged, executor);
4042
+ }
4043
+ const durationMs = Math.round(performance.now() - startTime);
4044
+ formatExecResult(result, outputFormat, outputFields);
4045
+ const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
4046
+ const auditLogger = getAuditLogger2();
4047
+ if (auditLogger) {
4048
+ auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
4049
+ }
4050
+ } catch (err) {
4051
+ const exitCode = exitCodeForError(err);
4052
+ const durationMs = Math.round(performance.now() - startTime);
4053
+ try {
4054
+ const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
4055
+ const auditLogger = getAuditLogger2();
4056
+ if (auditLogger) {
4057
+ auditLogger.logExecution(moduleId, merged, "error", exitCode, durationMs);
4058
+ }
4059
+ } catch {
4060
+ }
4061
+ if (outputFormat === "json" || !process.stderr.isTTY) {
4062
+ emitErrorJson(err, exitCode);
4063
+ } else {
4064
+ emitErrorTty(err, exitCode);
4065
+ }
4066
+ process.exit(exitCode);
4067
+ }
4068
+ });
4069
+ return cmd;
4070
+ }
1329
4071
  function validateModuleId(moduleId) {
1330
- if (moduleId.length > 128) {
4072
+ if (moduleId.length > 192) {
1331
4073
  process.stderr.write(
1332
- `Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
4074
+ `Error: Invalid module ID format: '${moduleId}'. Maximum length is 192 characters.
1333
4075
  `
1334
4076
  );
1335
4077
  process.exit(EXIT_CODES.INVALID_CLI_INPUT);
@@ -1352,45 +4094,57 @@ async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
1352
4094
  if (!stdinFlag) {
1353
4095
  return cliKwargsNonNull;
1354
4096
  }
4097
+ let raw;
4098
+ let source;
1355
4099
  if (stdinFlag === "-") {
1356
- const raw = await readStdin();
1357
- const rawSize = Buffer.byteLength(raw, "utf-8");
1358
- if (rawSize > 10485760 && !largeInput) {
1359
- process.stderr.write(
1360
- "Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
1361
- );
1362
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1363
- }
1364
- if (!raw) {
1365
- return cliKwargsNonNull;
1366
- }
1367
- let stdinData;
4100
+ raw = await readStdin();
4101
+ source = "STDIN";
4102
+ } else {
4103
+ source = `file '${stdinFlag}'`;
1368
4104
  try {
1369
- stdinData = JSON.parse(raw);
1370
- } catch {
1371
- process.stderr.write(
1372
- "Error: STDIN does not contain valid JSON.\n"
1373
- );
4105
+ raw = readFileSync2(stdinFlag, "utf-8");
4106
+ } catch (err) {
4107
+ const msg = err instanceof Error ? err.message : String(err);
4108
+ process.stderr.write(`Error: Could not read input ${source}: ${msg}
4109
+ `);
1374
4110
  process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1375
4111
  }
1376
- if (typeof stdinData !== "object" || stdinData === null || Array.isArray(stdinData)) {
1377
- process.stderr.write(
1378
- `Error: STDIN JSON must be an object, got ${Array.isArray(stdinData) ? "array" : typeof stdinData}.
4112
+ }
4113
+ const rawSize = Buffer.byteLength(raw, "utf-8");
4114
+ if (rawSize > 10485760 && !largeInput) {
4115
+ process.stderr.write(
4116
+ `Error: ${source} input exceeds 10MB limit. Use --large-input to override.
1379
4117
  `
1380
- );
1381
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1382
- }
1383
- return { ...stdinData, ...cliKwargsNonNull };
4118
+ );
4119
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
4120
+ }
4121
+ if (!raw) {
4122
+ return cliKwargsNonNull;
4123
+ }
4124
+ let parsed;
4125
+ try {
4126
+ parsed = JSON.parse(raw);
4127
+ } catch {
4128
+ process.stderr.write(`Error: ${source} does not contain valid JSON.
4129
+ `);
4130
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1384
4131
  }
1385
- return cliKwargsNonNull;
4132
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
4133
+ process.stderr.write(
4134
+ `Error: ${source} JSON must be an object, got ${Array.isArray(parsed) ? "array" : typeof parsed}.
4135
+ `
4136
+ );
4137
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
4138
+ }
4139
+ return { ...parsed, ...cliKwargsNonNull };
1386
4140
  }
1387
4141
  function readStdin() {
1388
- return new Promise((resolve3, reject) => {
4142
+ return new Promise((resolve2, reject) => {
1389
4143
  const chunks = [];
1390
4144
  const onData = (chunk) => chunks.push(chunk);
1391
4145
  const onEnd = () => {
1392
4146
  cleanup();
1393
- resolve3(Buffer.concat(chunks).toString("utf-8"));
4147
+ resolve2(Buffer.concat(chunks).toString("utf-8"));
1394
4148
  };
1395
4149
  const onError = (err) => {
1396
4150
  cleanup();
@@ -1407,7 +4161,97 @@ function readStdin() {
1407
4161
  process.stdin.resume();
1408
4162
  });
1409
4163
  }
4164
+ function reconvertEnumValues(kwargs, options) {
4165
+ const result = { ...kwargs };
4166
+ for (const opt of options) {
4167
+ if (!opt.enumOriginalTypes) continue;
4168
+ const paramName = opt.name;
4169
+ if (!(paramName in result) || result[paramName] === null || result[paramName] === void 0) {
4170
+ continue;
4171
+ }
4172
+ const strVal = String(result[paramName]);
4173
+ const origType = opt.enumOriginalTypes[strVal];
4174
+ if (origType === "int") {
4175
+ result[paramName] = parseInt(strVal, 10);
4176
+ } else if (origType === "float") {
4177
+ result[paramName] = parseFloat(strVal);
4178
+ } else if (origType === "bool") {
4179
+ result[paramName] = strVal.toLowerCase() === "true";
4180
+ }
4181
+ }
4182
+ return result;
4183
+ }
4184
+ var __dirname2, verboseHelp, docsUrl, VERSION, _ALWAYS_REGISTERED, _DEPRECATED_ROOT_COMMANDS, bindingDisplayMap;
4185
+ var init_main = __esm({
4186
+ "src/main.ts"() {
4187
+ "use strict";
4188
+ init_esm_shims();
4189
+ init_errors();
4190
+ init_ref_resolver();
4191
+ init_schema_parser();
4192
+ init_approval();
4193
+ init_output();
4194
+ init_logger();
4195
+ init_init_cmd();
4196
+ init_display_helpers();
4197
+ init_config();
4198
+ init_approval();
4199
+ init_shell();
4200
+ init_discovery();
4201
+ init_system_cmd();
4202
+ init_strategy();
4203
+ init_builtin_group();
4204
+ init_exposure();
4205
+ init_audit();
4206
+ init_canonical_help();
4207
+ __dirname2 = path4.dirname(fileURLToPath2(import.meta.url));
4208
+ verboseHelp = false;
4209
+ docsUrl = null;
4210
+ VERSION = "0.0.0";
4211
+ try {
4212
+ const pkg = JSON.parse(readFileSync2(path4.resolve(__dirname2, "../package.json"), "utf-8"));
4213
+ VERSION = pkg.version;
4214
+ } catch {
4215
+ }
4216
+ _ALWAYS_REGISTERED = /* @__PURE__ */ new Set(["exec"]);
4217
+ _DEPRECATED_ROOT_COMMANDS = [
4218
+ "list",
4219
+ "describe",
4220
+ "exec",
4221
+ "init",
4222
+ "validate",
4223
+ "health",
4224
+ "usage",
4225
+ "enable",
4226
+ "disable",
4227
+ "reload",
4228
+ "config",
4229
+ "completion",
4230
+ "describe-pipeline"
4231
+ ];
4232
+ bindingDisplayMap = /* @__PURE__ */ new Map();
4233
+ }
4234
+ });
1410
4235
 
1411
4236
  // bin/apcore-cli.ts
1412
- main("apcore-cli");
4237
+ init_esm_shims();
4238
+ var sandboxIdx = process.argv.indexOf("--internal-sandbox-runner");
4239
+ if (sandboxIdx !== -1) {
4240
+ const moduleId = process.argv[sandboxIdx + 1];
4241
+ if (!moduleId) {
4242
+ process.stderr.write("--internal-sandbox-runner requires a module_id argument.\n");
4243
+ process.exit(1);
4244
+ }
4245
+ Promise.resolve().then(() => (init_sandbox(), sandbox_exports)).then(({ runSandboxRunner: runSandboxRunner2 }) => {
4246
+ runSandboxRunner2(moduleId).catch((err) => {
4247
+ process.stderr.write(`sandbox runner fatal: ${err}
4248
+ `);
4249
+ process.exit(1);
4250
+ });
4251
+ });
4252
+ } else {
4253
+ Promise.resolve().then(() => (init_main(), main_exports)).then(({ main: main2 }) => {
4254
+ main2("apcore-cli");
4255
+ });
4256
+ }
1413
4257
  //# sourceMappingURL=apcore-cli.js.map