apcore-cli 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,11 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
+ }) : x)(function(x) {
6
+ if (typeof require !== "undefined") return require.apply(this, arguments);
7
+ throw Error('Dynamic require of "' + x + '" is not supported');
8
+ });
3
9
  var __esm = (fn, res) => function __init() {
4
10
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
5
11
  };
@@ -50,11 +56,19 @@ function exitCodeForError(error2) {
50
56
  SCHEMA_CIRCULAR_REF: EXIT_CODES.SCHEMA_CIRCULAR_REF,
51
57
  APPROVAL_DENIED: EXIT_CODES.APPROVAL_DENIED,
52
58
  APPROVAL_TIMEOUT: EXIT_CODES.APPROVAL_TIMEOUT,
59
+ APPROVAL_PENDING: EXIT_CODES.APPROVAL_DENIED,
53
60
  CONFIG_NOT_FOUND: EXIT_CODES.CONFIG_NOT_FOUND,
54
61
  CONFIG_INVALID: EXIT_CODES.CONFIG_INVALID,
55
62
  MODULE_EXECUTE_ERROR: EXIT_CODES.MODULE_EXECUTE_ERROR,
56
63
  MODULE_TIMEOUT: EXIT_CODES.MODULE_TIMEOUT,
57
- ACL_DENIED: EXIT_CODES.ACL_DENIED
64
+ ACL_DENIED: EXIT_CODES.ACL_DENIED,
65
+ // Config Bus errors (apcore >= 0.15.0)
66
+ CONFIG_NAMESPACE_RESERVED: EXIT_CODES.CONFIG_NAMESPACE_RESERVED,
67
+ CONFIG_NAMESPACE_DUPLICATE: EXIT_CODES.CONFIG_NAMESPACE_DUPLICATE,
68
+ CONFIG_ENV_PREFIX_CONFLICT: EXIT_CODES.CONFIG_ENV_PREFIX_CONFLICT,
69
+ CONFIG_MOUNT_ERROR: EXIT_CODES.CONFIG_MOUNT_ERROR,
70
+ CONFIG_BIND_ERROR: EXIT_CODES.CONFIG_BIND_ERROR,
71
+ ERROR_FORMATTER_DUPLICATE: EXIT_CODES.ERROR_FORMATTER_DUPLICATE
58
72
  };
59
73
  if (code && code in codeMap) {
60
74
  return codeMap[code];
@@ -124,6 +138,14 @@ var init_errors = __esm({
124
138
  CONFIG_INVALID: 47,
125
139
  SCHEMA_CIRCULAR_REF: 48,
126
140
  ACL_DENIED: 77,
141
+ // Config Bus errors (apcore >= 0.15.0)
142
+ CONFIG_NAMESPACE_RESERVED: 78,
143
+ CONFIG_NAMESPACE_DUPLICATE: 78,
144
+ CONFIG_ENV_PREFIX_CONFLICT: 78,
145
+ CONFIG_ENV_MAP_CONFLICT: 78,
146
+ CONFIG_MOUNT_ERROR: 66,
147
+ CONFIG_BIND_ERROR: 65,
148
+ ERROR_FORMATTER_DUPLICATE: 70,
127
149
  KEYBOARD_INTERRUPT: 130
128
150
  };
129
151
  }
@@ -137,9 +159,9 @@ __export(audit_exports, {
137
159
  setAuditLogger: () => setAuditLogger
138
160
  });
139
161
  import * as crypto from "crypto";
140
- import * as fs2 from "fs";
162
+ import * as fs3 from "fs";
141
163
  import * as os from "os";
142
- import * as path3 from "path";
164
+ import * as path4 from "path";
143
165
  function setAuditLogger(auditLogger) {
144
166
  _auditLogger = auditLogger;
145
167
  }
@@ -153,7 +175,7 @@ var init_audit = __esm({
153
175
  init_esm_shims();
154
176
  _auditLogger = null;
155
177
  AuditLogger = class _AuditLogger {
156
- static DEFAULT_PATH = path3.join(
178
+ static DEFAULT_PATH = path4.join(
157
179
  os.homedir(),
158
180
  ".apcore-cli",
159
181
  "audit.jsonl"
@@ -165,7 +187,7 @@ var init_audit = __esm({
165
187
  }
166
188
  ensureDirectory() {
167
189
  try {
168
- fs2.mkdirSync(path3.dirname(this.logPath), { recursive: true });
190
+ fs3.mkdirSync(path4.dirname(this.logPath), { recursive: true });
169
191
  } catch {
170
192
  }
171
193
  }
@@ -180,7 +202,7 @@ var init_audit = __esm({
180
202
  duration_ms: durationMs
181
203
  };
182
204
  try {
183
- fs2.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
205
+ fs3.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
184
206
  } catch (err) {
185
207
  console.warn(`Could not write audit log: ${err}`);
186
208
  }
@@ -381,9 +403,9 @@ var init_auth = __esm({
381
403
 
382
404
  // src/security/sandbox.ts
383
405
  import * as child_process from "child_process";
384
- import * as fs3 from "fs";
406
+ import * as fs4 from "fs";
385
407
  import * as os3 from "os";
386
- import * as path4 from "path";
408
+ import * as path5 from "path";
387
409
  var Sandbox;
388
410
  var init_sandbox = __esm({
389
411
  "src/security/sandbox.ts"() {
@@ -416,8 +438,8 @@ var init_sandbox = __esm({
416
438
  env[key] = value;
417
439
  }
418
440
  }
419
- const tmpDir = fs3.mkdtempSync(
420
- path4.join(os3.tmpdir(), "apcore_sandbox_")
441
+ const tmpDir = fs4.mkdtempSync(
442
+ path5.join(os3.tmpdir(), "apcore_sandbox_")
421
443
  );
422
444
  try {
423
445
  env.HOME = tmpDir;
@@ -455,7 +477,7 @@ var init_sandbox = __esm({
455
477
  );
456
478
  } finally {
457
479
  try {
458
- fs3.rmSync(tmpDir, { recursive: true, force: true });
480
+ fs4.rmSync(tmpDir, { recursive: true, force: true });
459
481
  } catch {
460
482
  }
461
483
  }
@@ -491,10 +513,10 @@ init_esm_shims();
491
513
  // src/main.ts
492
514
  init_esm_shims();
493
515
  init_errors();
494
- import { readFileSync } from "fs";
495
- import { fileURLToPath as fileURLToPath2 } from "url";
496
- import * as path5 from "path";
497
- import { Command, CommanderError, Option } from "commander";
516
+ import { readFileSync as readFileSync3 } from "fs";
517
+ import { fileURLToPath as fileURLToPath3 } from "url";
518
+ import * as path6 from "path";
519
+ import { Command as Command6, CommanderError, Option as Option4 } from "commander";
498
520
 
499
521
  // src/ref-resolver.ts
500
522
  init_esm_shims();
@@ -712,7 +734,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
712
734
  flags: `--${flagBase}, --no-${flagBase}`,
713
735
  description: helpText,
714
736
  defaultValue: defaultVal,
715
- required: false,
737
+ required: isRequired,
716
738
  isBooleanFlag: true
717
739
  });
718
740
  } else if ("enum" in propSchema && Array.isArray(propSchema.enum)) {
@@ -723,7 +745,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
723
745
  flags: `${flagName} <value>`,
724
746
  description: helpText,
725
747
  defaultValue,
726
- required: false
748
+ required: isRequired
727
749
  });
728
750
  } else {
729
751
  const stringValues = enumValues.map(String);
@@ -742,7 +764,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
742
764
  flags: `${flagName} <value>`,
743
765
  description: helpText,
744
766
  defaultValue: defaultValue !== void 0 ? String(defaultValue) : void 0,
745
- required: false,
767
+ required: isRequired,
746
768
  choices: stringValues,
747
769
  enumOriginalTypes: Object.keys(enumOriginalTypes).length > 0 ? enumOriginalTypes : void 0
748
770
  });
@@ -767,7 +789,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
767
789
  flags: `${flagName} <value>`,
768
790
  description: helpText,
769
791
  defaultValue,
770
- required: false,
792
+ required: isRequired,
771
793
  parseArg
772
794
  });
773
795
  }
@@ -784,7 +806,41 @@ function getAnnotation(annotations, key, defaultValue = void 0) {
784
806
  const ann = annotations;
785
807
  return key in ann ? ann[key] : defaultValue;
786
808
  }
787
- async function checkApproval(moduleDef, autoApprove) {
809
+ var CliApprovalHandler = class {
810
+ autoApprove;
811
+ timeout;
812
+ constructor(autoApprove = false, timeout = 60) {
813
+ this.autoApprove = autoApprove;
814
+ this.timeout = Math.max(1, Math.min(timeout, 3600));
815
+ }
816
+ async requestApproval(request) {
817
+ const moduleId = request.module_id ?? "unknown";
818
+ if (this.autoApprove) {
819
+ return { status: "approved", approved_by: "auto_approve" };
820
+ }
821
+ const envVal = process.env.APCORE_CLI_AUTO_APPROVE ?? "";
822
+ if (envVal === "1") {
823
+ return { status: "approved", approved_by: "env_auto_approve" };
824
+ }
825
+ if (!process.stdin.isTTY) {
826
+ return { status: "rejected", reason: "Non-interactive session without --yes" };
827
+ }
828
+ const annotations = request.annotations;
829
+ const extra = annotations?.extra ?? {};
830
+ const message = extra.approval_message ?? `Module '${moduleId}' requires approval to execute.`;
831
+ process.stderr.write(message + "\n");
832
+ try {
833
+ await promptWithTimeout({ id: moduleId }, this.timeout);
834
+ return { status: "approved", approved_by: "tty_user" };
835
+ } catch {
836
+ return { status: "rejected", reason: "User rejected or timed out" };
837
+ }
838
+ }
839
+ async checkApproval(_approvalId) {
840
+ return { status: "rejected", reason: "CLI does not support async approval polling" };
841
+ }
842
+ };
843
+ async function checkApproval(moduleDef, autoApprove, timeout = 60) {
788
844
  const annotations = moduleDef.annotations;
789
845
  let requiresApproval;
790
846
  if (moduleDef.requiresApproval !== void 0) {
@@ -818,7 +874,7 @@ async function checkApproval(moduleDef, autoApprove) {
818
874
  );
819
875
  process.exit(EXIT_CODES.APPROVAL_DENIED);
820
876
  }
821
- await promptWithTimeout(moduleDef, 60);
877
+ await promptWithTimeout(moduleDef, timeout);
822
878
  }
823
879
  async function promptWithTimeout(moduleDef, timeout) {
824
880
  timeout = Math.max(1, Math.min(timeout, 3600));
@@ -891,7 +947,7 @@ function formatTable(headers, rows) {
891
947
  );
892
948
  return [headerLine, sep2, ...dataLines].join("\n") + "\n";
893
949
  }
894
- function formatModuleList(modules, format, filterTags) {
950
+ function formatModuleList(modules, format, filterTags, showDeps = false) {
895
951
  if (format === "table") {
896
952
  if (modules.length === 0 && filterTags && filterTags.length > 0) {
897
953
  process.stdout.write(
@@ -904,19 +960,29 @@ function formatModuleList(modules, format, filterTags) {
904
960
  process.stdout.write("No modules found.\n");
905
961
  return;
906
962
  }
907
- const headers = ["ID", "Description", "Tags"];
908
- const rows = modules.map((m) => [
909
- m.id,
910
- truncate(m.description, 80),
911
- (m.tags ?? []).join(", ")
912
- ]);
963
+ const headers = showDeps ? ["ID", "Description", "Tags", "Deps"] : ["ID", "Description", "Tags"];
964
+ const rows = modules.map((m) => {
965
+ const base = [m.id, truncate(m.description, 80), (m.tags ?? []).join(", ")];
966
+ if (showDeps) {
967
+ const deps = m.dependencies;
968
+ base.push(String(Array.isArray(deps) ? deps.length : 0));
969
+ }
970
+ return base;
971
+ });
913
972
  process.stdout.write(formatTable(headers, rows));
914
973
  } else if (format === "json") {
915
- const result = modules.map((m) => ({
916
- id: m.id,
917
- description: m.description,
918
- tags: m.tags ?? []
919
- }));
974
+ const result = modules.map((m) => {
975
+ const entry = {
976
+ id: m.id,
977
+ description: m.description,
978
+ tags: m.tags ?? []
979
+ };
980
+ if (showDeps) {
981
+ const deps = m.dependencies;
982
+ entry.dependency_count = Array.isArray(deps) ? deps.length : 0;
983
+ }
984
+ return entry;
985
+ });
920
986
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
921
987
  }
922
988
  }
@@ -1004,23 +1070,170 @@ Tags: ${tags.join(", ")}
1004
1070
  process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1005
1071
  }
1006
1072
  }
1007
- function formatExecResult(result, format) {
1073
+ function selectFields(result, fields) {
1074
+ const selected = {};
1075
+ for (const f of fields.split(",")) {
1076
+ const key = f.trim();
1077
+ let val = result;
1078
+ for (const part of key.split(".")) {
1079
+ if (val && typeof val === "object" && !Array.isArray(val)) {
1080
+ val = val[part];
1081
+ } else {
1082
+ val = void 0;
1083
+ break;
1084
+ }
1085
+ }
1086
+ selected[key] = val;
1087
+ }
1088
+ return selected;
1089
+ }
1090
+ function formatExecResult(result, format, fields) {
1008
1091
  if (result === null || result === void 0) {
1009
1092
  return;
1010
1093
  }
1094
+ let effective_result = result;
1095
+ if (fields && typeof result === "object" && !Array.isArray(result) && result !== null) {
1096
+ effective_result = selectFields(result, fields);
1097
+ }
1011
1098
  const effective = resolveFormat(format);
1012
- if (effective === "table" && typeof result === "object" && !Array.isArray(result)) {
1013
- const entries = Object.entries(result);
1099
+ if (effective === "csv") {
1100
+ if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
1101
+ const obj = effective_result;
1102
+ const keys = Object.keys(obj);
1103
+ const header = keys.map(escapeCsvField).join(",");
1104
+ const row = keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
1105
+ process.stdout.write(header + "\n" + row + "\n");
1106
+ } else if (Array.isArray(effective_result) && effective_result.length > 0 && typeof effective_result[0] === "object") {
1107
+ const keys = Object.keys(effective_result[0]);
1108
+ const header = keys.map(escapeCsvField).join(",");
1109
+ const rows = effective_result.map((item) => {
1110
+ const obj = item;
1111
+ return keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
1112
+ });
1113
+ process.stdout.write(header + "\n" + rows.join("\n") + "\n");
1114
+ } else {
1115
+ process.stdout.write(JSON.stringify(effective_result) + "\n");
1116
+ }
1117
+ } else if (effective === "yaml") {
1118
+ if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
1119
+ const obj = effective_result;
1120
+ const lines = Object.entries(obj).map(([k, v]) => {
1121
+ if (v === null || v === void 0) return `${k}: null`;
1122
+ if (typeof v === "object") return `${k}: ${JSON.stringify(v)}`;
1123
+ return `${k}: ${v}`;
1124
+ });
1125
+ process.stdout.write(lines.join("\n") + "\n");
1126
+ } else if (Array.isArray(effective_result)) {
1127
+ for (const item of effective_result) {
1128
+ if (typeof item === "object" && item !== null) {
1129
+ const obj = item;
1130
+ const lines = Object.entries(obj).map(([k, v]) => ` ${k}: ${v}`);
1131
+ process.stdout.write("- " + lines.join("\n ") + "\n");
1132
+ } else {
1133
+ process.stdout.write(`- ${item}
1134
+ `);
1135
+ }
1136
+ }
1137
+ } else {
1138
+ process.stdout.write(String(effective_result) + "\n");
1139
+ }
1140
+ } else if (effective === "jsonl") {
1141
+ if (Array.isArray(effective_result)) {
1142
+ for (const item of effective_result) {
1143
+ process.stdout.write(JSON.stringify(item) + "\n");
1144
+ }
1145
+ } else {
1146
+ process.stdout.write(JSON.stringify(effective_result) + "\n");
1147
+ }
1148
+ } else if (effective === "table" && typeof effective_result === "object" && !Array.isArray(effective_result)) {
1149
+ const entries = Object.entries(effective_result);
1014
1150
  const headers = ["Key", "Value"];
1015
1151
  const rows = entries.map(([k, v]) => [String(k), String(v)]);
1016
1152
  process.stdout.write(formatTable(headers, rows));
1017
- } else if (typeof result === "object") {
1018
- process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1019
- } else if (typeof result === "string") {
1020
- process.stdout.write(result + "\n");
1153
+ } else if (typeof effective_result === "object") {
1154
+ process.stdout.write(JSON.stringify(effective_result, null, 2) + "\n");
1155
+ } else if (typeof effective_result === "string") {
1156
+ process.stdout.write(effective_result + "\n");
1157
+ } else {
1158
+ process.stdout.write(String(effective_result) + "\n");
1159
+ }
1160
+ }
1161
+ function escapeCsvField(value) {
1162
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
1163
+ return '"' + value.replace(/"/g, '""') + '"';
1164
+ }
1165
+ return value;
1166
+ }
1167
+ function formatPreflightResult(result, format) {
1168
+ const resolved = resolveFormat(format);
1169
+ if (resolved === "json" || !process.stdout.isTTY) {
1170
+ const payload = {
1171
+ valid: result.valid,
1172
+ requires_approval: result.requires_approval,
1173
+ checks: result.checks.map((c) => {
1174
+ const entry = { check: c.check, passed: c.passed };
1175
+ if (c.error !== void 0 && c.error !== null) {
1176
+ entry.error = c.error;
1177
+ }
1178
+ if (c.warnings && c.warnings.length > 0) {
1179
+ entry.warnings = c.warnings;
1180
+ }
1181
+ return entry;
1182
+ })
1183
+ };
1184
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
1021
1185
  } else {
1022
- process.stdout.write(String(result) + "\n");
1186
+ for (const c of result.checks) {
1187
+ const hasWarnings = (c.warnings?.length ?? 0) > 0;
1188
+ let sym;
1189
+ if (c.passed && hasWarnings) {
1190
+ sym = "\u26A0";
1191
+ } else if (c.passed) {
1192
+ sym = "\u2713";
1193
+ } else if (c.passed === false) {
1194
+ sym = "\u2717";
1195
+ } else {
1196
+ sym = "\u25CB";
1197
+ }
1198
+ let status = ` ${sym} ${c.check.padEnd(20)}`;
1199
+ if (c.error) {
1200
+ const detail = typeof c.error === "object" ? JSON.stringify(c.error) : String(c.error);
1201
+ status += ` ${detail}`;
1202
+ } else if (c.passed && !hasWarnings) {
1203
+ status += " OK";
1204
+ } else if (!c.passed) {
1205
+ status += " Skipped";
1206
+ }
1207
+ process.stdout.write(status + "\n");
1208
+ for (const w of c.warnings ?? []) {
1209
+ process.stdout.write(` Warning: ${w}
1210
+ `);
1211
+ }
1212
+ }
1213
+ const errors = result.checks.filter((c) => !c.passed).length;
1214
+ const warnings = result.checks.reduce((sum, c) => sum + (c.warnings?.length ?? 0), 0);
1215
+ const tag = result.valid ? "PASS" : "FAIL";
1216
+ process.stdout.write(`
1217
+ Result: ${tag} (${errors} error(s), ${warnings} warning(s))
1218
+ `);
1219
+ }
1220
+ }
1221
+ function firstFailedExitCode(result) {
1222
+ const checkToExit = {
1223
+ module_id: 2,
1224
+ module_lookup: 44,
1225
+ call_chain: 1,
1226
+ acl: 77,
1227
+ schema: 45,
1228
+ approval: 46,
1229
+ module_preflight: 1
1230
+ };
1231
+ for (const check of result.checks) {
1232
+ if (!check.passed) {
1233
+ return checkToExit[check.check] ?? 1;
1234
+ }
1023
1235
  }
1236
+ return 1;
1024
1237
  }
1025
1238
 
1026
1239
  // src/logger.ts
@@ -1218,1120 +1431,2091 @@ function getCliDisplayFields(descriptor) {
1218
1431
  return [name, desc, tags];
1219
1432
  }
1220
1433
 
1221
- // src/main.ts
1222
- var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
1223
- var verboseHelp = false;
1224
- function setVerboseHelp(verbose) {
1225
- verboseHelp = verbose;
1226
- }
1227
- var docsUrl = null;
1228
- function setDocsUrl(url) {
1229
- docsUrl = url;
1230
- }
1231
- function hasVerboseFlag() {
1232
- return process.argv.includes("--verbose");
1233
- }
1234
- var VERSION = "0.0.0";
1235
- try {
1236
- const pkg = JSON.parse(readFileSync(path5.resolve(__dirname2, "../package.json"), "utf-8"));
1237
- VERSION = pkg.version;
1238
- } catch {
1239
- }
1240
- function createCli(extensionsDir, progName, verbose = false) {
1241
- verboseHelp = verbose;
1242
- const resolvedProgName = progName ?? path5.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
1243
- const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
1244
- setLogLevel(cliLogLevel);
1245
- const program = new Command(resolvedProgName).exitOverride().version(VERSION, "--version", `Show ${resolvedProgName} version`).description("apcore CLI \u2014 execute apcore modules from the command line").option("--extensions-dir <path>", "Path to extensions directory").option("--commands-dir <path>", "Path to convention-based commands directory").option("--binding <path>", "Path to binding.yaml for display overlay").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING").option("--verbose", "Show all options in help output (including built-in apcore options)");
1246
- const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
1247
- void resolvedExtDir;
1248
- registerInitCommand(program);
1249
- program.hook("preAction", async (thisCommand) => {
1250
- const opts = thisCommand.opts();
1251
- const commandsDir = opts.commandsDir;
1252
- const bindingPath = opts.binding;
1253
- await applyToolkitIntegration(commandsDir, bindingPath);
1254
- });
1255
- return program;
1256
- }
1257
- async function applyToolkitIntegration(commandsDir, bindingPath) {
1258
- if (!commandsDir && !bindingPath) {
1259
- return;
1260
- }
1434
+ // src/config.ts
1435
+ init_esm_shims();
1436
+ import * as fs2 from "fs";
1437
+ import yaml from "js-yaml";
1438
+ var DEFAULTS = {
1439
+ "extensions.root": "./extensions",
1440
+ "logging.level": "WARNING",
1441
+ "sandbox.enabled": false,
1442
+ "cli.stdin_buffer_limit": 10485760,
1443
+ "cli.auto_approve": false,
1444
+ "cli.help_text_max_length": 1e3,
1445
+ // Namespace-mode aliases (apcore >= 0.15.0 Config Bus)
1446
+ "apcore-cli.stdin_buffer_limit": 10485760,
1447
+ "apcore-cli.auto_approve": false,
1448
+ "apcore-cli.help_text_max_length": 1e3,
1449
+ "apcore-cli.logging_level": "WARNING",
1450
+ // FE-11 config keys
1451
+ "cli.approval_timeout": 60,
1452
+ "cli.strategy": "standard",
1453
+ "cli.group_depth": 1,
1454
+ "apcore-cli.approval_timeout": 60,
1455
+ "apcore-cli.strategy": "standard",
1456
+ "apcore-cli.group_depth": 1
1457
+ };
1458
+ var NAMESPACE_TO_LEGACY = {
1459
+ "apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
1460
+ "apcore-cli.auto_approve": "cli.auto_approve",
1461
+ "apcore-cli.help_text_max_length": "cli.help_text_max_length",
1462
+ "apcore-cli.logging_level": "logging.level"
1463
+ };
1464
+ var LEGACY_TO_NAMESPACE = Object.fromEntries(
1465
+ Object.entries(NAMESPACE_TO_LEGACY).map(([k, v]) => [v, k])
1466
+ );
1467
+ function registerConfigNamespace() {
1261
1468
  try {
1262
- const toolkitModule = "apcore-toolkit";
1263
- const toolkit = await import(
1264
- /* @vite-ignore */
1265
- toolkitModule
1266
- );
1267
- if (commandsDir) {
1268
- console.warn("Convention scanning not yet available in TypeScript toolkit");
1269
- }
1270
- if (bindingPath) {
1271
- const resolver = new toolkit.DisplayResolver();
1272
- void resolver;
1469
+ const { Config } = __require("apcore-js");
1470
+ if (typeof Config?.registerNamespace === "function") {
1471
+ Config.registerNamespace({
1472
+ name: "apcore-cli",
1473
+ envPrefix: "APCORE_CLI",
1474
+ defaults: {
1475
+ stdin_buffer_limit: 10485760,
1476
+ auto_approve: false,
1477
+ help_text_max_length: 1e3,
1478
+ logging_level: "WARNING",
1479
+ approval_timeout: 60,
1480
+ strategy: "standard",
1481
+ group_depth: 1
1482
+ }
1483
+ });
1273
1484
  }
1274
1485
  } catch {
1275
- console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
1276
1486
  }
1277
1487
  }
1278
- function main(progName) {
1279
- verboseHelp = hasVerboseFlag();
1280
- const program = createCli(void 0, progName, verboseHelp);
1281
- try {
1282
- program.parse(process.argv);
1283
- } catch (error2) {
1284
- if (error2 instanceof CommanderError) {
1285
- process.exit(error2.exitCode);
1488
+ var ConfigResolver = class {
1489
+ cliFlags;
1490
+ configPath;
1491
+ fileCache = null;
1492
+ fileCacheLoaded = false;
1493
+ constructor(cliFlags, configPath) {
1494
+ this.cliFlags = cliFlags ?? {};
1495
+ this.configPath = configPath ?? "apcore.yaml";
1496
+ }
1497
+ /**
1498
+ * Resolve a single configuration key across all four tiers.
1499
+ */
1500
+ resolve(key, cliFlag, envVar) {
1501
+ const flagKey = cliFlag ?? key;
1502
+ if (flagKey in this.cliFlags) {
1503
+ const value = this.cliFlags[flagKey];
1504
+ if (value !== null && value !== void 0) {
1505
+ return value;
1506
+ }
1286
1507
  }
1287
- const code = exitCodeForError(error2);
1288
- if (error2 instanceof Error) {
1289
- process.stderr.write(`Error: ${error2.message}
1290
- `);
1508
+ if (envVar) {
1509
+ const envValue = process.env[envVar];
1510
+ if (envValue !== void 0 && envValue !== "") {
1511
+ return envValue;
1512
+ }
1291
1513
  }
1292
- process.exit(code);
1293
- }
1294
- }
1295
- function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
1296
- const moduleId = moduleDef.id;
1297
- let resolvedSchema = {};
1298
- let schemaOptions = [];
1299
- const display = getDisplay(moduleDef);
1300
- const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
1301
- const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
1302
- const cmdHelp = cliDisplay.description ?? moduleDef.description;
1303
- const inputSchema = moduleDef.inputSchema;
1304
- if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
1305
- try {
1306
- resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
1307
- } catch {
1308
- resolvedSchema = inputSchema;
1514
+ const fileValue = this.resolveFromFile(key);
1515
+ if (fileValue !== void 0) {
1516
+ return fileValue;
1309
1517
  }
1310
- schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
1311
- }
1312
- const cmd = new Command(effectiveCmdName).description(cmdHelp);
1313
- const inputOpt = new Option("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
1314
- const yesOpt = new Option("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
1315
- const largeInputOpt = new Option("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
1316
- const formatOpt = new Option("--format <format>", "Set output format: 'json' for machine-readable, 'table' for human-readable");
1317
- const sandboxOpt = new Option("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
1318
- if (!verbose) {
1319
- inputOpt.hideHelp();
1320
- yesOpt.hideHelp();
1321
- largeInputOpt.hideHelp();
1322
- formatOpt.hideHelp();
1323
- }
1324
- cmd.addOption(inputOpt);
1325
- cmd.addOption(yesOpt);
1326
- cmd.addOption(largeInputOpt);
1327
- cmd.addOption(formatOpt);
1328
- cmd.addOption(sandboxOpt);
1329
- const footerParts = [];
1330
- if (!verbose) {
1331
- footerParts.push("Use --verbose to show all options (including built-in apcore options).");
1332
- }
1333
- if (docsUrl) {
1334
- footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
1335
- }
1336
- if (footerParts.length > 0) {
1337
- cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
1338
- }
1339
- for (const opt of schemaOptions) {
1340
- if (opt.parseArg) {
1341
- cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
1342
- } else {
1343
- cmd.option(opt.flags, opt.description, opt.defaultValue);
1518
+ const altKey = NAMESPACE_TO_LEGACY[key] ?? LEGACY_TO_NAMESPACE[key];
1519
+ if (altKey) {
1520
+ const altFileValue = this.resolveFromFile(altKey);
1521
+ if (altFileValue !== void 0) {
1522
+ return altFileValue;
1523
+ }
1344
1524
  }
1525
+ return DEFAULTS[key];
1345
1526
  }
1346
- cmd.action(async (options) => {
1347
- const stdinFlag = options.input;
1348
- const autoApprove = options.yes;
1349
- const largeInput = options.largeInput;
1350
- const outputFormat = options.format;
1351
- const sandboxEnabled = options.sandbox;
1352
- const schemaKwargs = {};
1353
- const builtinKeys = /* @__PURE__ */ new Set(["input", "yes", "largeInput", "format", "sandbox", "verbose"]);
1354
- for (const [k, v] of Object.entries(options)) {
1355
- if (!builtinKeys.has(k)) {
1356
- schemaKwargs[k] = v;
1357
- }
1527
+ /**
1528
+ * Load a value from the config file using a dot-separated key path.
1529
+ */
1530
+ resolveFromFile(key) {
1531
+ if (!this.fileCacheLoaded) {
1532
+ this.fileCache = this.loadConfigFile();
1533
+ this.fileCacheLoaded = true;
1534
+ }
1535
+ if (this.fileCache === null) {
1536
+ return void 0;
1358
1537
  }
1538
+ return this.fileCache[key];
1539
+ }
1540
+ /**
1541
+ * Load and flatten a YAML config file.
1542
+ */
1543
+ loadConfigFile() {
1544
+ let content;
1359
1545
  try {
1360
- const merged = await collectInput(stdinFlag, schemaKwargs, largeInput);
1361
- const reconverted = reconvertEnumValues(merged, schemaOptions);
1362
- await checkApproval(moduleDef, autoApprove);
1363
- const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
1364
- const sandbox = new Sandbox2(sandboxEnabled);
1365
- const startTime = performance.now();
1366
- const result = await sandbox.execute(moduleId, reconverted, executor);
1367
- const durationMs = Math.round(performance.now() - startTime);
1368
- const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
1369
- const auditLogger = getAuditLogger2();
1370
- if (auditLogger) {
1371
- auditLogger.logExecution(moduleId, reconverted, "success", 0, durationMs);
1372
- }
1373
- formatExecResult(result, outputFormat);
1546
+ content = fs2.readFileSync(this.configPath, "utf-8");
1374
1547
  } catch (err) {
1375
- const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
1376
- const auditLogger = getAuditLogger2();
1377
- const code = exitCodeForError(err);
1378
- if (auditLogger) {
1379
- auditLogger.logExecution(moduleId, {}, "error", code, 0);
1380
- }
1381
- if (err instanceof Error) {
1382
- process.stderr.write(`Error: ${err.message}
1383
- `);
1548
+ if (err instanceof Error && "code" in err && err.code === "ENOENT") {
1549
+ return null;
1384
1550
  }
1385
- process.exit(code);
1386
- }
1387
- });
1388
- return cmd;
1389
- }
1390
- function validateModuleId(moduleId) {
1391
- if (moduleId.length > 128) {
1392
- process.stderr.write(
1393
- `Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
1394
- `
1395
- );
1396
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1397
- }
1398
- if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
1399
- process.stderr.write(
1400
- `Error: Invalid module ID format: '${moduleId}'.
1401
- `
1402
- );
1403
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1404
- }
1405
- }
1406
- async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
1407
- const cliKwargsNonNull = {};
1408
- for (const [k, v] of Object.entries(cliKwargs)) {
1409
- if (v !== null && v !== void 0) {
1410
- cliKwargsNonNull[k] = v;
1411
- }
1412
- }
1413
- if (!stdinFlag) {
1414
- return cliKwargsNonNull;
1415
- }
1416
- if (stdinFlag === "-") {
1417
- const raw = await readStdin();
1418
- const rawSize = Buffer.byteLength(raw, "utf-8");
1419
- if (rawSize > 10485760 && !largeInput) {
1420
- process.stderr.write(
1421
- "Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
1551
+ console.warn(
1552
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1422
1553
  );
1423
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1424
- }
1425
- if (!raw) {
1426
- return cliKwargsNonNull;
1554
+ return null;
1427
1555
  }
1428
- let stdinData;
1556
+ let parsed;
1429
1557
  try {
1430
- stdinData = JSON.parse(raw);
1558
+ parsed = yaml.load(content);
1431
1559
  } catch {
1432
- process.stderr.write(
1433
- "Error: STDIN does not contain valid JSON.\n"
1560
+ console.warn(
1561
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1434
1562
  );
1435
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1563
+ return null;
1436
1564
  }
1437
- if (typeof stdinData !== "object" || stdinData === null || Array.isArray(stdinData)) {
1438
- process.stderr.write(
1439
- `Error: STDIN JSON must be an object, got ${Array.isArray(stdinData) ? "array" : typeof stdinData}.
1440
- `
1565
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1566
+ console.warn(
1567
+ `Configuration file '${this.configPath}' is malformed, using defaults.`
1441
1568
  );
1442
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
1443
- }
1444
- return { ...stdinData, ...cliKwargsNonNull };
1445
- }
1446
- return cliKwargsNonNull;
1447
- }
1448
- function readStdin() {
1449
- return new Promise((resolve3, reject) => {
1450
- const chunks = [];
1451
- const onData = (chunk) => chunks.push(chunk);
1452
- const onEnd = () => {
1453
- cleanup();
1454
- resolve3(Buffer.concat(chunks).toString("utf-8"));
1455
- };
1456
- const onError = (err) => {
1457
- cleanup();
1458
- reject(err);
1459
- };
1460
- const cleanup = () => {
1461
- process.stdin.removeListener("data", onData);
1462
- process.stdin.removeListener("end", onEnd);
1463
- process.stdin.removeListener("error", onError);
1464
- };
1465
- process.stdin.on("data", onData);
1466
- process.stdin.on("end", onEnd);
1467
- process.stdin.on("error", onError);
1468
- process.stdin.resume();
1469
- });
1470
- }
1471
- function reconvertEnumValues(kwargs, options) {
1472
- const result = { ...kwargs };
1473
- for (const opt of options) {
1474
- if (!opt.enumOriginalTypes) continue;
1475
- const paramName = opt.name;
1476
- if (!(paramName in result) || result[paramName] === null || result[paramName] === void 0) {
1477
- continue;
1478
- }
1479
- const strVal = String(result[paramName]);
1480
- const origType = opt.enumOriginalTypes[strVal];
1481
- if (origType === "int") {
1482
- result[paramName] = parseInt(strVal, 10);
1483
- } else if (origType === "float") {
1484
- result[paramName] = parseFloat(strVal);
1485
- } else if (origType === "bool") {
1486
- result[paramName] = strVal.toLowerCase() === "true";
1569
+ return null;
1487
1570
  }
1488
- }
1489
- return result;
1490
- }
1491
-
1492
- // src/cli.ts
1493
- init_esm_shims();
1494
- import { Command as Command2 } from "commander";
1495
- var BUILTIN_COMMANDS = ["completion", "describe", "exec", "init", "list", "man"];
1496
- var LazyModuleGroup = class {
1497
- registry;
1498
- executor;
1499
- helpTextMaxLength;
1500
- commandCache = /* @__PURE__ */ new Map();
1501
- /** alias -> canonical module_id (populated lazily) */
1502
- aliasMap = /* @__PURE__ */ new Map();
1503
- /** module_id -> descriptor cache (populated during alias map build) */
1504
- descriptorCache = /* @__PURE__ */ new Map();
1505
- aliasMapBuilt = false;
1506
- constructor(registry, executor, helpTextMaxLength = 1e3) {
1507
- this.registry = registry;
1508
- this.executor = executor;
1509
- this.helpTextMaxLength = helpTextMaxLength;
1571
+ return this.flattenDict(parsed);
1510
1572
  }
1511
1573
  /**
1512
- * Build alias->module_id map from display overlay metadata.
1574
+ * Flatten nested dict to dot-notation keys.
1513
1575
  */
1514
- buildAliasMap() {
1515
- if (this.aliasMapBuilt) {
1516
- return;
1517
- }
1518
- try {
1519
- for (const descriptor of this.registry.listModules()) {
1520
- const moduleId = descriptor.id;
1521
- this.descriptorCache.set(moduleId, descriptor);
1522
- const display = getDisplay(descriptor);
1523
- const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
1524
- const cliAlias = cliDisplay.alias;
1525
- if (cliAlias && cliAlias !== moduleId) {
1526
- this.aliasMap.set(cliAlias, moduleId);
1527
- }
1576
+ flattenDict(d, prefix = "") {
1577
+ const result = {};
1578
+ for (const [key, value] of Object.entries(d)) {
1579
+ const fullKey = prefix ? `${prefix}.${key}` : key;
1580
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1581
+ Object.assign(
1582
+ result,
1583
+ this.flattenDict(value, fullKey)
1584
+ );
1585
+ } else {
1586
+ result[fullKey] = value;
1528
1587
  }
1529
- this.aliasMapBuilt = true;
1530
- } catch {
1531
- warn("Failed to build alias map from registry");
1532
- }
1533
- }
1534
- /**
1535
- * List all available command names from the Registry.
1536
- */
1537
- listCommands() {
1538
- this.buildAliasMap();
1539
- const reverse = /* @__PURE__ */ new Map();
1540
- for (const [alias, moduleId] of this.aliasMap) {
1541
- reverse.set(moduleId, alias);
1542
- }
1543
- const moduleIds = this.registry.listModules().map((m) => m.id);
1544
- const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
1545
- return [...new Set(names)].sort();
1546
- }
1547
- /**
1548
- * Get or lazily build a Commander Command for the given module.
1549
- */
1550
- getCommand(cmdName) {
1551
- if (this.commandCache.has(cmdName)) {
1552
- return this.commandCache.get(cmdName);
1553
- }
1554
- this.buildAliasMap();
1555
- const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
1556
- let moduleDef = this.descriptorCache.get(moduleId);
1557
- if (!moduleDef) {
1558
- moduleDef = this.registry.getModule(moduleId) ?? void 0;
1559
- }
1560
- if (!moduleDef) {
1561
- return null;
1562
- }
1563
- const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
1564
- this.commandCache.set(cmdName, cmd);
1565
- return cmd;
1566
- }
1567
- };
1568
- var LazyGroup = class {
1569
- members;
1570
- _executor;
1571
- _helpTextMaxLength;
1572
- _cmdCache = /* @__PURE__ */ new Map();
1573
- command;
1574
- constructor(members, executor, name, helpTextMaxLength = 1e3) {
1575
- this.members = members;
1576
- this._executor = executor;
1577
- this._helpTextMaxLength = helpTextMaxLength;
1578
- this.command = new Command2(name).description(`${name} commands`);
1579
- for (const [cmdName, [, descriptor]] of this.members) {
1580
- const cmd = buildModuleCommand(
1581
- descriptor,
1582
- this._executor,
1583
- this._helpTextMaxLength,
1584
- cmdName
1585
- );
1586
- this._cmdCache.set(cmdName, cmd);
1587
- this.command.addCommand(cmd);
1588
- }
1589
- }
1590
- listCommands() {
1591
- return [...this.members.keys()].sort();
1592
- }
1593
- getCommand(cmdName) {
1594
- if (this._cmdCache.has(cmdName)) {
1595
- return this._cmdCache.get(cmdName);
1596
- }
1597
- const entry = this.members.get(cmdName);
1598
- if (!entry) {
1599
- return null;
1600
1588
  }
1601
- const [, descriptor] = entry;
1602
- const cmd = buildModuleCommand(
1603
- descriptor,
1604
- this._executor,
1605
- this._helpTextMaxLength,
1606
- cmdName
1607
- );
1608
- this._cmdCache.set(cmdName, cmd);
1609
- return cmd;
1589
+ return result;
1610
1590
  }
1611
1591
  };
1612
- var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
1613
- /** groupName -> { cmdName -> [moduleId, descriptor] } */
1614
- groupMap = /* @__PURE__ */ new Map();
1615
- /** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
1616
- topLevelModules = /* @__PURE__ */ new Map();
1617
- /** Cached LazyGroup instances */
1618
- groupCache = /* @__PURE__ */ new Map();
1619
- groupMapBuilt = false;
1620
- /**
1621
- * Determine (groupName | null, commandName) for a module from its display overlay.
1622
- */
1623
- static resolveGroup(moduleId, descriptor) {
1624
- if (!moduleId) {
1625
- warn("Empty module_id encountered in resolveGroup");
1626
- return [null, ""];
1627
- }
1628
- const display = getDisplay(descriptor);
1629
- const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
1630
- const explicitGroup = cliDisplay.group;
1631
- if (typeof explicitGroup === "string" && explicitGroup !== "") {
1632
- return [explicitGroup, cliDisplay.alias ?? moduleId];
1633
- }
1634
- if (explicitGroup === "") {
1635
- return [null, cliDisplay.alias ?? moduleId];
1636
- }
1637
- const cliName = cliDisplay.alias ?? moduleId;
1638
- if (cliName.includes(".")) {
1639
- const dotIdx = cliName.indexOf(".");
1640
- const group = cliName.substring(0, dotIdx);
1641
- const cmd = cliName.substring(dotIdx + 1);
1642
- return [group, cmd];
1592
+
1593
+ // src/shell.ts
1594
+ init_esm_shims();
1595
+ init_errors();
1596
+ import { readFileSync as readFileSync2 } from "fs";
1597
+ import { fileURLToPath as fileURLToPath2 } from "url";
1598
+ import * as path3 from "path";
1599
+ import { spawnSync } from "child_process";
1600
+ import { Command, Help, Option } from "commander";
1601
+ var __dirname2 = path3.dirname(fileURLToPath2(import.meta.url));
1602
+ var SHELL_VERSION = "0.0.0";
1603
+ try {
1604
+ const pkg = JSON.parse(readFileSync2(path3.resolve(__dirname2, "../package.json"), "utf-8"));
1605
+ SHELL_VERSION = pkg.version;
1606
+ } catch {
1607
+ }
1608
+ function makeFunctionName(progName) {
1609
+ return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
1610
+ }
1611
+ function shellQuote(s) {
1612
+ return "'" + s.replace(/'/g, "'\\''") + "'";
1613
+ }
1614
+ function generateBashCompletion(progName) {
1615
+ const fn = makeFunctionName(progName);
1616
+ const quoted = shellQuote(progName);
1617
+ const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
1618
+ const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
1619
+ const m=require('fs').readFileSync('/dev/stdin','utf8');
1620
+ const ids=JSON.parse(m).map(x=>x.id);
1621
+ const g=new Set(),t=[];
1622
+ ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
1623
+ console.log([...g].sort().concat(t.sort()).join(' '))
1624
+ " 2>/dev/null`;
1625
+ const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
1626
+ const m=require('fs').readFileSync('/dev/stdin','utf8');
1627
+ const g=process.env._APCORE_GRP;
1628
+ JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
1629
+ " 2>/dev/null`;
1630
+ return `${fn}() {
1631
+ local cur prev opts
1632
+ COMPREPLY=()
1633
+ cur="\${COMP_WORDS[COMP_CWORD]}"
1634
+ prev="\${COMP_WORDS[COMP_CWORD-1]}"
1635
+
1636
+ if [[ \${COMP_CWORD} -eq 1 ]]; then
1637
+ opts="completion describe exec init list man"
1638
+ local groups_and_top=$(${groupsAndTopCmd})
1639
+ COMPREPLY=( $(compgen -W "\${opts} \${groups_and_top}" -- \${cur}) )
1640
+ return 0
1641
+ fi
1642
+
1643
+ if [[ \${COMP_CWORD} -eq 2 ]]; then
1644
+ if [[ "\${COMP_WORDS[1]}" == "exec" ]]; then
1645
+ local modules=$(${moduleListCmd})
1646
+ COMPREPLY=( $(compgen -W "\${modules}" -- \${cur}) )
1647
+ return 0
1648
+ fi
1649
+ export _APCORE_GRP="\${COMP_WORDS[1]}"
1650
+ local group_cmds=$(${groupCmdsCmd})
1651
+ COMPREPLY=( $(compgen -W "\${group_cmds}" -- \${cur}) )
1652
+ return 0
1653
+ fi
1654
+ }
1655
+ complete -F ${fn} ${quoted}
1656
+ `;
1657
+ }
1658
+ function generateZshCompletion(progName) {
1659
+ const fn = makeFunctionName(progName);
1660
+ const quoted = shellQuote(progName);
1661
+ const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
1662
+ const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
1663
+ const m=require('fs').readFileSync('/dev/stdin','utf8');
1664
+ const ids=JSON.parse(m).map(x=>x.id);
1665
+ const g=new Set(),t=[];
1666
+ ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
1667
+ console.log([...g].sort().concat(t.sort()).join(' '))
1668
+ " 2>/dev/null`;
1669
+ const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
1670
+ const m=require('fs').readFileSync('/dev/stdin','utf8');
1671
+ const g=process.env._APCORE_GRP;
1672
+ JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
1673
+ " 2>/dev/null`;
1674
+ return `#compdef ${progName}
1675
+
1676
+ ${fn}() {
1677
+ local -a commands
1678
+ commands=(
1679
+ 'list:List available modules'
1680
+ 'describe:Show module metadata and schema'
1681
+ 'completion:Generate shell completion script'
1682
+ 'init:Scaffolding commands'
1683
+ 'man:Generate man page'
1684
+ )
1685
+
1686
+ _arguments -C \\
1687
+ '1:command:->command' \\
1688
+ '*::arg:->args'
1689
+
1690
+ case "$state" in
1691
+ command)
1692
+ _describe -t commands '${progName} commands' commands
1693
+ local -a groups_and_top
1694
+ groups_and_top=($(${groupsAndTopCmd}))
1695
+ compadd -a groups_and_top
1696
+ ;;
1697
+ args)
1698
+ case "\${words[1]}" in
1699
+ exec)
1700
+ local modules
1701
+ modules=($(${moduleListCmd}))
1702
+ compadd -a modules
1703
+ ;;
1704
+ *)
1705
+ export _APCORE_GRP="\${words[1]}"
1706
+ local -a group_cmds
1707
+ group_cmds=($(${groupCmdsCmd}))
1708
+ compadd -a group_cmds
1709
+ ;;
1710
+ esac
1711
+ ;;
1712
+ esac
1713
+ }
1714
+
1715
+ compdef ${fn} ${quoted}
1716
+ `;
1717
+ }
1718
+ function generateFishCompletion(progName) {
1719
+ const quoted = shellQuote(progName);
1720
+ const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e \\"process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})\\" 2>/dev/null`;
1721
+ const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e \\"const m=require('fs').readFileSync('/dev/stdin','utf8');const ids=JSON.parse(m).map(x=>x.id);const g=new Set(),t=[];ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});console.log([...g].sort().concat(t.sort()).join('\\\\n'))\\" 2>/dev/null`;
1722
+ return `# Fish completions for ${progName}
1723
+ complete -c ${quoted} -n "__fish_use_subcommand" -a list -d "List available modules"
1724
+ complete -c ${quoted} -n "__fish_use_subcommand" -a describe -d "Show module metadata and schema"
1725
+ complete -c ${quoted} -n "__fish_use_subcommand" -a completion -d "Generate shell completion script"
1726
+ complete -c ${quoted} -n "__fish_use_subcommand" -a init -d "Scaffolding commands"
1727
+ complete -c ${quoted} -n "__fish_use_subcommand" -a man -d "Generate man page"
1728
+ complete -c ${quoted} -n "__fish_use_subcommand" -a "(${groupsAndTopCmd})" -d "Module group"
1729
+
1730
+ complete -c ${quoted} -n "__fish_seen_subcommand_from exec" -a "(${moduleListCmd})"
1731
+
1732
+ function __apcore_group_cmds
1733
+ set -l grp (commandline -opc)[2]
1734
+ set -x _APCORE_GRP $grp
1735
+ ${quoted} list --format json 2>/dev/null | node -e "
1736
+ const m=require('fs').readFileSync('/dev/stdin','utf8');
1737
+ const g=process.env._APCORE_GRP;
1738
+ JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
1739
+ " 2>/dev/null
1740
+ end
1741
+
1742
+ # Group subcommand completion \u2014 matches when position 1 is not a builtin
1743
+ complete -c ${quoted} -n "not __fish_use_subcommand; and not __fish_seen_subcommand_from list describe completion init man exec" -a "(__apcore_group_cmds)"
1744
+ `;
1745
+ }
1746
+ function buildSynopsis(command, progName, commandName) {
1747
+ if (!command) {
1748
+ return `\\fB${progName} ${commandName}\\fR [OPTIONS]`;
1749
+ }
1750
+ const parts = [`\\fB${progName} ${commandName}\\fR`];
1751
+ for (const opt of command.options) {
1752
+ const flag = opt.long ?? opt.short ?? "";
1753
+ if (opt.isBoolean?.()) {
1754
+ parts.push(`[${flag}]`);
1755
+ } else if (opt.required) {
1756
+ const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
1757
+ parts.push(`${flag} \\fI${typeName}\\fR`);
1758
+ } else {
1759
+ const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
1760
+ parts.push(`[${flag} \\fI${typeName}\\fR]`);
1761
+ }
1762
+ }
1763
+ for (const arg of command.registeredArguments ?? []) {
1764
+ const meta = arg.name().toUpperCase();
1765
+ if (arg.required) {
1766
+ parts.push(`\\fI${meta}\\fR`);
1767
+ } else {
1768
+ parts.push(`[\\fI${meta}\\fR]`);
1769
+ }
1770
+ }
1771
+ return parts.join(" ");
1772
+ }
1773
+ function generateManPage(commandName, command, progName, version = SHELL_VERSION) {
1774
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1775
+ const title = `${progName}-${commandName}`.toUpperCase();
1776
+ const pkgLabel = `${progName} ${version}`;
1777
+ const manualLabel = `${progName} Manual`;
1778
+ const sections = [];
1779
+ sections.push(`.TH "${title}" "1" "${today}" "${pkgLabel}" "${manualLabel}"`);
1780
+ sections.push(".SH NAME");
1781
+ const desc = command?.description() ?? commandName;
1782
+ const nameDesc = desc.split("\n")[0].replace(/\.$/, "");
1783
+ sections.push(`${progName}-${commandName} \\- ${nameDesc}`);
1784
+ sections.push(".SH SYNOPSIS");
1785
+ sections.push(buildSynopsis(command, progName, commandName));
1786
+ if (command?.description()) {
1787
+ sections.push(".SH DESCRIPTION");
1788
+ sections.push(
1789
+ command.description().replace(/\\/g, "\\\\").replace(/-/g, "\\-")
1790
+ );
1791
+ }
1792
+ if (command && command.options.length > 0) {
1793
+ sections.push(".SH OPTIONS");
1794
+ for (const opt of command.options) {
1795
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
1796
+ sections.push(".TP");
1797
+ if (opt.isBoolean?.()) {
1798
+ sections.push(`\\fB${flag}\\fR`);
1799
+ } else {
1800
+ sections.push(`\\fB${flag}\\fR \\fIVALUE\\fR`);
1801
+ }
1802
+ if (opt.description) {
1803
+ sections.push(opt.description);
1804
+ }
1805
+ if (opt.defaultValue !== void 0 && !opt.isBoolean?.()) {
1806
+ sections.push(`Default: ${opt.defaultValue}.`);
1807
+ }
1808
+ }
1809
+ }
1810
+ sections.push(".SH ENVIRONMENT");
1811
+ sections.push(".TP");
1812
+ sections.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
1813
+ sections.push(
1814
+ "Path to the apcore extensions directory. Overrides the default \\fI./extensions\\fR."
1815
+ );
1816
+ sections.push(".TP");
1817
+ sections.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
1818
+ sections.push(
1819
+ "Set to \\fB1\\fR to bypass approval prompts for modules that require human-in-the-loop confirmation."
1820
+ );
1821
+ sections.push(".TP");
1822
+ sections.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
1823
+ sections.push(
1824
+ "CLI-specific logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Takes priority over \\fBAPCORE_LOGGING_LEVEL\\fR. Default: WARNING."
1825
+ );
1826
+ sections.push(".TP");
1827
+ sections.push("\\fBAPCORE_LOGGING_LEVEL\\fR");
1828
+ sections.push(
1829
+ "Global apcore logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Used as fallback when \\fBAPCORE_CLI_LOGGING_LEVEL\\fR is not set. Default: WARNING."
1830
+ );
1831
+ sections.push(".TP");
1832
+ sections.push("\\fBAPCORE_AUTH_API_KEY\\fR");
1833
+ sections.push(
1834
+ "API key for authenticating with the apcore registry."
1835
+ );
1836
+ sections.push(".SH EXIT CODES");
1837
+ const exitCodes = [
1838
+ ["0", "Success."],
1839
+ ["1", "Module execution error."],
1840
+ ["2", "Invalid CLI input or missing argument."],
1841
+ ["44", "Module not found, disabled, or failed to load."],
1842
+ ["45", "Input failed JSON Schema validation."],
1843
+ [
1844
+ "46",
1845
+ "Approval denied, timed out, or no interactive terminal available."
1846
+ ],
1847
+ [
1848
+ "47",
1849
+ "Configuration error (extensions directory not found or unreadable)."
1850
+ ],
1851
+ ["48", "Schema contains a circular \\fB$ref\\fR."],
1852
+ ["77", "ACL denied \u2014 insufficient permissions for this module."],
1853
+ ["130", "Execution cancelled by user (SIGINT / Ctrl\\-C)."]
1854
+ ];
1855
+ for (const [code, meaning] of exitCodes) {
1856
+ sections.push(`.TP
1857
+ \\fB${code}\\fR
1858
+ ${meaning}`);
1859
+ }
1860
+ sections.push(".SH SEE ALSO");
1861
+ sections.push(
1862
+ [
1863
+ `\\fB${progName}\\fR(1)`,
1864
+ `\\fB${progName}\\-list\\fR(1)`,
1865
+ `\\fB${progName}\\-describe\\fR(1)`,
1866
+ `\\fB${progName}\\-completion\\fR(1)`
1867
+ ].join(", ")
1868
+ );
1869
+ return sections.join("\n");
1870
+ }
1871
+ function roffEscape(s) {
1872
+ return s.replace(/\\/g, "\\\\").replace(/-/g, "\\-").replace(/'/g, "\\(aq");
1873
+ }
1874
+ function buildProgramManPage(program, progName, version, description, docsUrl2) {
1875
+ const help = new Help();
1876
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1877
+ const s = [];
1878
+ const resolvedDesc = description ?? program.description() ?? `${progName} CLI`;
1879
+ s.push(`.TH "${progName.toUpperCase()}" "1" "${today}" "${progName} ${version}" "${progName} Manual"`);
1880
+ s.push(".SH NAME");
1881
+ s.push(`${progName} \\- ${roffEscape(resolvedDesc)}`);
1882
+ s.push(".SH SYNOPSIS");
1883
+ s.push(`\\fB${progName}\\fR [\\fIglobal\\-options\\fR] \\fIcommand\\fR [\\fIcommand\\-options\\fR]`);
1884
+ if (resolvedDesc) {
1885
+ s.push(".SH DESCRIPTION");
1886
+ s.push(roffEscape(resolvedDesc));
1887
+ }
1888
+ const globalOpts = help.visibleOptions(program).filter((o) => !["help", "version", "all", "man"].includes(o.long?.replace("--", "") ?? ""));
1889
+ if (globalOpts.length > 0) {
1890
+ s.push(".SH GLOBAL OPTIONS");
1891
+ for (const opt of globalOpts) {
1892
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
1893
+ s.push(".TP");
1894
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
1895
+ if (opt.description) s.push(roffEscape(opt.description));
1896
+ }
1897
+ }
1898
+ const allCommands = help.visibleCommands(program);
1899
+ if (allCommands.length > 0) {
1900
+ s.push(".SH COMMANDS");
1901
+ for (const cmd of allCommands) {
1902
+ if (cmd.name() === "help") continue;
1903
+ const desc = help.subcommandDescription(cmd);
1904
+ s.push(".TP");
1905
+ s.push(`\\fB${progName} ${roffEscape(cmd.name())}\\fR`);
1906
+ if (desc) s.push(roffEscape(desc));
1907
+ const cmdHelp = new Help();
1908
+ const opts = cmdHelp.visibleOptions(cmd).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
1909
+ for (const opt of opts) {
1910
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
1911
+ s.push(".RS");
1912
+ s.push(".TP");
1913
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
1914
+ if (opt.description) s.push(roffEscape(opt.description));
1915
+ s.push(".RE");
1916
+ }
1917
+ const subCmds = cmdHelp.visibleCommands(cmd).filter((c) => c.name() !== "help");
1918
+ for (const sub of subCmds) {
1919
+ const subDesc = help.subcommandDescription(sub);
1920
+ s.push(".TP");
1921
+ s.push(`\\fB${progName} ${roffEscape(cmd.name())} ${roffEscape(sub.name())}\\fR`);
1922
+ if (subDesc) s.push(roffEscape(subDesc));
1923
+ const subOpts = cmdHelp.visibleOptions(sub).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
1924
+ for (const opt of subOpts) {
1925
+ const flag = [opt.short, opt.long].filter(Boolean).join(", ");
1926
+ s.push(".RS");
1927
+ s.push(".TP");
1928
+ s.push(`\\fB${roffEscape(flag)}\\fR`);
1929
+ if (opt.description) s.push(roffEscape(opt.description));
1930
+ s.push(".RE");
1931
+ }
1932
+ }
1933
+ }
1934
+ }
1935
+ s.push(".SH ENVIRONMENT");
1936
+ s.push(".TP");
1937
+ s.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
1938
+ s.push("Path to the apcore extensions directory.");
1939
+ s.push(".TP");
1940
+ s.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
1941
+ s.push("Set to \\fB1\\fR to bypass approval prompts.");
1942
+ s.push(".TP");
1943
+ s.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
1944
+ s.push("CLI\\-specific logging verbosity (DEBUG|INFO|WARNING|ERROR).");
1945
+ s.push(".SH EXIT CODES");
1946
+ const exitCodes = [
1947
+ ["0", "Success."],
1948
+ ["1", "Module execution error."],
1949
+ ["2", "Invalid CLI input or missing argument."],
1950
+ ["44", "Module not found, disabled, or failed to load."],
1951
+ ["45", "Input failed JSON Schema validation."],
1952
+ ["46", "Approval denied or timed out."],
1953
+ ["47", "Configuration error."],
1954
+ ["77", "ACL denied."],
1955
+ ["130", "Cancelled by user (SIGINT)."]
1956
+ ];
1957
+ for (const [code, meaning] of exitCodes) {
1958
+ s.push(`.TP
1959
+ \\fB${code}\\fR
1960
+ ${meaning}`);
1961
+ }
1962
+ s.push(".SH SEE ALSO");
1963
+ s.push(`\\fB${progName} \\-\\-help \\-\\-verbose\\fR for full option list.`);
1964
+ if (docsUrl2) {
1965
+ s.push(`.PP
1966
+ Full documentation at \\fI${roffEscape(docsUrl2)}\\fR`);
1967
+ }
1968
+ return s.join("\n");
1969
+ }
1970
+ function configureManHelp(program, progName, version, description, docsUrl2) {
1971
+ const manOpt = new Option("--man", "Output man page in roff format (use with --help)").hideHelp();
1972
+ program.addOption(manOpt);
1973
+ program.addHelpText("beforeAll", () => {
1974
+ if (program.opts().man) {
1975
+ const roff = buildProgramManPage(program, progName, version, description, docsUrl2) + "\n";
1976
+ if (process.stdout.isTTY) {
1977
+ const pagers = [
1978
+ { cmd: "mandoc", args: ["-a"] },
1979
+ { cmd: "groff", args: ["-man", "-Tutf8"] }
1980
+ ];
1981
+ let rendered = false;
1982
+ for (const { cmd, args } of pagers) {
1983
+ const result = spawnSync(cmd, args, {
1984
+ input: roff,
1985
+ stdio: ["pipe", "pipe", "pipe"],
1986
+ encoding: "utf-8"
1987
+ });
1988
+ if (result.status === 0 && result.stdout) {
1989
+ const pager = process.env.PAGER || "less";
1990
+ const pagerResult = spawnSync(pager, ["-R"], {
1991
+ input: result.stdout,
1992
+ stdio: ["pipe", "inherit", "inherit"]
1993
+ });
1994
+ if (pagerResult.status !== null) {
1995
+ rendered = true;
1996
+ break;
1997
+ }
1998
+ }
1999
+ }
2000
+ if (!rendered) {
2001
+ process.stdout.write(roff);
2002
+ }
2003
+ } else {
2004
+ process.stdout.write(roff);
2005
+ }
2006
+ process.exit(0);
2007
+ }
2008
+ return "";
2009
+ });
2010
+ }
2011
+ function registerShellCommands(cli, progName = "apcore-cli") {
2012
+ const completionCmd = new Command("completion").description(
2013
+ "Generate a shell completion script and print it to stdout."
2014
+ ).argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
2015
+ const validShells = ["bash", "zsh", "fish"];
2016
+ if (!validShells.includes(shell)) {
2017
+ process.stderr.write(
2018
+ `Error: Unknown shell '${shell}'. Expected: bash, zsh, or fish.
2019
+ `
2020
+ );
2021
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2022
+ }
2023
+ const resolved = cli.name() || progName;
2024
+ const generators = {
2025
+ bash: () => generateBashCompletion(resolved),
2026
+ zsh: () => generateZshCompletion(resolved),
2027
+ fish: () => generateFishCompletion(resolved)
2028
+ };
2029
+ process.stdout.write(generators[shell]());
2030
+ });
2031
+ cli.addCommand(completionCmd);
2032
+ const manCmd = new Command("man").description("Generate a roff man page for COMMAND and print it to stdout.").argument("<command>", "Command to generate man page for").action((commandName) => {
2033
+ const knownBuiltins = /* @__PURE__ */ new Set(["completion", "describe", "exec", "init", "list", "man"]);
2034
+ const cmd = cli.commands.find((c) => c.name() === commandName) ?? null;
2035
+ if (!cmd && !knownBuiltins.has(commandName)) {
2036
+ process.stderr.write(
2037
+ `Error: Unknown command '${commandName}'.
2038
+ `
2039
+ );
2040
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2041
+ }
2042
+ const resolved = cli.name() || progName;
2043
+ const roff = generateManPage(commandName, cmd, resolved);
2044
+ process.stdout.write(roff);
2045
+ });
2046
+ cli.addCommand(manCmd);
2047
+ }
2048
+
2049
+ // src/discovery.ts
2050
+ init_esm_shims();
2051
+ init_errors();
2052
+ import { Command as Command2, Option as Option2 } from "commander";
2053
+ var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
2054
+ function validateTag(tag) {
2055
+ if (!TAG_PATTERN.test(tag)) {
2056
+ process.stderr.write(
2057
+ `Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
2058
+ `
2059
+ );
2060
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2061
+ }
2062
+ }
2063
+ function collectTag(value, previous) {
2064
+ return previous.concat([value]);
2065
+ }
2066
+ function collectAnnotation(value, previous) {
2067
+ return previous.concat([value]);
2068
+ }
2069
+ function getAnnotationFlag(moduleDef, flag) {
2070
+ const annotations = moduleDef.annotations;
2071
+ if (!annotations || typeof annotations !== "object") return false;
2072
+ const ann = annotations;
2073
+ const map = {
2074
+ "destructive": "destructive",
2075
+ "requires-approval": "requires_approval",
2076
+ "readonly": "readonly",
2077
+ "streaming": "streaming",
2078
+ "cacheable": "cacheable",
2079
+ "idempotent": "idempotent"
2080
+ };
2081
+ const attr = map[flag] ?? flag;
2082
+ return ann[attr] === true;
2083
+ }
2084
+ function registerDiscoveryCommands(cli, registry) {
2085
+ 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(
2086
+ new Option2("--status <status>", "Filter by module status.").choices(["enabled", "disabled", "all"]).default("enabled")
2087
+ ).option("-a, --annotation <flag>", "Filter by annotation flag (AND logic). Repeatable.", collectAnnotation, []).addOption(
2088
+ new Option2("--sort <field>", "Sort order.").choices(["id", "calls", "errors", "latency"]).default("id")
2089
+ ).option("--reverse", "Reverse sort order.", false).option("--deprecated", "Include deprecated modules.", false).option("--deps", "Show dependency count column.", false).action((opts) => {
2090
+ for (const t of opts.tag) {
2091
+ validateTag(t);
1643
2092
  }
1644
- return [null, cliName];
2093
+ let modules = [];
2094
+ for (const m of registry.listModules()) {
2095
+ modules.push(m);
2096
+ }
2097
+ if (opts.tag.length > 0) {
2098
+ const filterTags = new Set(opts.tag);
2099
+ modules = modules.filter((m) => {
2100
+ const mTags = m.tags ?? [];
2101
+ return [...filterTags].every((t) => mTags.includes(t));
2102
+ });
2103
+ }
2104
+ if (opts.search) {
2105
+ const query = opts.search.toLowerCase();
2106
+ modules = modules.filter(
2107
+ (m) => (m.id ?? "").toLowerCase().includes(query) || (m.description ?? "").toLowerCase().includes(query)
2108
+ );
2109
+ }
2110
+ if (opts.status === "enabled") {
2111
+ modules = modules.filter((m) => {
2112
+ const enabled = m.enabled;
2113
+ return enabled !== false;
2114
+ });
2115
+ } else if (opts.status === "disabled") {
2116
+ modules = modules.filter((m) => {
2117
+ const enabled = m.enabled;
2118
+ return enabled === false;
2119
+ });
2120
+ }
2121
+ if (!opts.deprecated) {
2122
+ modules = modules.filter((m) => {
2123
+ const deprecated = m.deprecated;
2124
+ return deprecated !== true;
2125
+ });
2126
+ }
2127
+ if (opts.annotation.length > 0) {
2128
+ for (const annFlag of opts.annotation) {
2129
+ modules = modules.filter((m) => getAnnotationFlag(m, annFlag));
2130
+ }
2131
+ }
2132
+ if (opts.sort === "calls" || opts.sort === "errors" || opts.sort === "latency") {
2133
+ process.stderr.write(
2134
+ `Warning: Usage data not available; sorting by id. Sort by ${opts.sort} requires system.usage modules.
2135
+ `
2136
+ );
2137
+ }
2138
+ modules.sort((a, b) => (a.id ?? "").localeCompare(b.id ?? ""));
2139
+ if (opts.reverse) {
2140
+ modules.reverse();
2141
+ }
2142
+ const fmt = resolveFormat(opts.format);
2143
+ const filterTagsArg = opts.tag.length > 0 ? opts.tag : void 0;
2144
+ formatModuleList(modules, fmt, filterTagsArg, opts.deps);
2145
+ });
2146
+ cli.addCommand(listCmd);
2147
+ 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) => {
2148
+ validateModuleId(moduleId);
2149
+ const moduleDef = registry.getModule(moduleId);
2150
+ if (!moduleDef) {
2151
+ process.stderr.write(
2152
+ `Error: Module '${moduleId}' not found.
2153
+ `
2154
+ );
2155
+ process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2156
+ }
2157
+ const fmt = resolveFormat(opts.format);
2158
+ formatModuleDetail(moduleDef, fmt);
2159
+ });
2160
+ cli.addCommand(describeCmd);
2161
+ }
2162
+ function registerValidateCommand(cli, registry, executor) {
2163
+ 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) => {
2164
+ validateModuleId(moduleId);
2165
+ const moduleDef = registry.getModule(moduleId);
2166
+ if (!moduleDef) {
2167
+ process.stderr.write(`Error: Module '${moduleId}' not found.
2168
+ `);
2169
+ process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2170
+ }
2171
+ const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
2172
+ if (!executor.validate) {
2173
+ process.stderr.write("Error: Executor does not support validate.\n");
2174
+ process.exit(1);
2175
+ }
2176
+ const preflight = await executor.validate(moduleId, merged);
2177
+ formatPreflightResult(preflight, opts.format);
2178
+ process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
2179
+ });
2180
+ cli.addCommand(validateCmd);
2181
+ }
2182
+
2183
+ // src/system-cmd.ts
2184
+ init_esm_shims();
2185
+ import { Command as Command3 } from "commander";
2186
+ async function callSystemModule(executor, moduleId, inputs) {
2187
+ if (executor.call) {
2188
+ return executor.call(moduleId, inputs);
1645
2189
  }
1646
- /**
1647
- * Build the group map from registry modules.
1648
- */
1649
- buildGroupMap() {
1650
- if (this.groupMapBuilt) {
1651
- return;
2190
+ return executor.execute(moduleId, inputs);
2191
+ }
2192
+ function formatHealthSummaryTty(result) {
2193
+ const summary = result.summary ?? {};
2194
+ const modules = result.modules ?? [];
2195
+ if (modules.length === 0) {
2196
+ process.stdout.write("No modules found.\n");
2197
+ return;
2198
+ }
2199
+ const total = summary.total_modules ?? modules.length;
2200
+ process.stdout.write(`Health Overview (${total} modules)
2201
+
2202
+ `);
2203
+ process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
2204
+ `);
2205
+ process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
2206
+ `);
2207
+ for (const m of modules) {
2208
+ const top = m.top_error;
2209
+ const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
2210
+ const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
2211
+ process.stdout.write(
2212
+ ` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
2213
+ `
2214
+ );
2215
+ }
2216
+ const parts = [];
2217
+ for (const key of ["healthy", "degraded", "error"]) {
2218
+ const count = summary[key];
2219
+ if (count) parts.push(`${count} ${key}`);
2220
+ }
2221
+ process.stdout.write(`
2222
+ Summary: ${parts.join(", ") || "no data"}
2223
+ `);
2224
+ }
2225
+ function formatHealthModuleTty(result) {
2226
+ process.stdout.write(`Module: ${result.module_id ?? "?"}
2227
+ `);
2228
+ process.stdout.write(`Status: ${result.status ?? "unknown"}
2229
+ `);
2230
+ const total = result.total_calls ?? 0;
2231
+ const errors = result.error_count ?? 0;
2232
+ const rate = result.error_rate ?? 0;
2233
+ const avg = result.avg_latency_ms ?? 0;
2234
+ const p99 = result.p99_latency_ms ?? 0;
2235
+ process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
2236
+ `);
2237
+ process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
2238
+ `);
2239
+ const recent = result.recent_errors ?? [];
2240
+ if (recent.length > 0) {
2241
+ process.stdout.write(`
2242
+ Recent Errors (top ${recent.length}):
2243
+ `);
2244
+ for (const e of recent) {
2245
+ const count = e.count ?? "?";
2246
+ const last = e.last_occurred ?? "?";
2247
+ process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
2248
+ `);
2249
+ }
2250
+ }
2251
+ }
2252
+ function formatUsageSummaryTty(result) {
2253
+ const modules = result.modules ?? [];
2254
+ const period = result.period ?? "?";
2255
+ if (modules.length === 0) {
2256
+ process.stdout.write(`No usage data for period ${period}.
2257
+ `);
2258
+ return;
2259
+ }
2260
+ process.stdout.write(`Usage Summary (last ${period})
2261
+
2262
+ `);
2263
+ process.stdout.write(` ${"Module".padEnd(24)} ${"Calls".padStart(8)} ${"Errors".padStart(8)} ${"Avg Latency".padStart(12)} ${"Trend".padStart(10)}
2264
+ `);
2265
+ process.stdout.write(` ${"-".repeat(24)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(10)}
2266
+ `);
2267
+ for (const m of modules) {
2268
+ const avg = `${(m.avg_latency_ms ?? 0).toFixed(0)}ms`;
2269
+ process.stdout.write(
2270
+ ` ${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)}
2271
+ `
2272
+ );
2273
+ }
2274
+ const totalCalls = result.total_calls ?? modules.reduce((s, m) => s + (m.call_count ?? 0), 0);
2275
+ const totalErrors = result.total_errors ?? modules.reduce((s, m) => s + (m.error_count ?? 0), 0);
2276
+ process.stdout.write(`
2277
+ Total: ${totalCalls.toLocaleString()} calls | ${totalErrors.toLocaleString()} errors
2278
+ `);
2279
+ }
2280
+ async function registerSystemCommands(cli, executor) {
2281
+ try {
2282
+ if (executor.validate) {
2283
+ await executor.validate("system.health.summary", {});
2284
+ } else {
2285
+ await callSystemModule(executor, "system.health.summary", { include_healthy: true });
1652
2286
  }
2287
+ } catch {
2288
+ debug("System modules not available; skipping system command registration.");
2289
+ return;
2290
+ }
2291
+ 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) => {
2292
+ const fmt = resolveFormat(opts.format);
1653
2293
  try {
1654
- this.buildAliasMap();
1655
- for (const descriptor of this.registry.listModules()) {
1656
- const moduleId = descriptor.id;
1657
- const cached = this.descriptorCache.get(moduleId);
1658
- if (!cached) {
1659
- continue;
2294
+ if (moduleId) {
2295
+ const result = await callSystemModule(executor, "system.health.module", {
2296
+ module_id: moduleId,
2297
+ error_limit: opts.errors
2298
+ });
2299
+ if (fmt === "json" || !process.stdout.isTTY) {
2300
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2301
+ } else {
2302
+ formatHealthModuleTty(result);
1660
2303
  }
1661
- const [group, cmd] = _GroupedModuleGroup.resolveGroup(moduleId, cached);
1662
- if (group === null) {
1663
- this.topLevelModules.set(cmd, [moduleId, cached]);
1664
- } else if (!/^[a-z][a-z0-9_-]*$/.test(group)) {
1665
- warn(
1666
- `Module '${moduleId}': group name '${group}' is not shell-safe \u2014 treating as top-level.`
1667
- );
1668
- this.topLevelModules.set(cmd, [moduleId, cached]);
2304
+ } else {
2305
+ const result = await callSystemModule(executor, "system.health.summary", {
2306
+ error_rate_threshold: opts.threshold,
2307
+ include_healthy: opts.all
2308
+ });
2309
+ if (fmt === "json" || !process.stdout.isTTY) {
2310
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
1669
2311
  } else {
1670
- if (!this.groupMap.has(group)) {
1671
- this.groupMap.set(group, /* @__PURE__ */ new Map());
1672
- }
1673
- this.groupMap.get(group).set(cmd, [moduleId, cached]);
2312
+ formatHealthSummaryTty(result);
1674
2313
  }
1675
2314
  }
1676
- for (const groupName of this.groupMap.keys()) {
1677
- if (BUILTIN_COMMANDS.includes(groupName)) {
1678
- warn(
1679
- `Group name '${groupName}' collides with a built-in command and will be ignored`
1680
- );
2315
+ } catch (e) {
2316
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2317
+ `);
2318
+ process.exit(1);
2319
+ }
2320
+ });
2321
+ cli.addCommand(healthCmd);
2322
+ 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) => {
2323
+ const fmt = resolveFormat(opts.format);
2324
+ try {
2325
+ let result;
2326
+ if (moduleId) {
2327
+ result = await callSystemModule(executor, "system.usage.module", {
2328
+ module_id: moduleId,
2329
+ period: opts.period
2330
+ });
2331
+ } else {
2332
+ result = await callSystemModule(executor, "system.usage.summary", {
2333
+ period: opts.period
2334
+ });
2335
+ }
2336
+ if (fmt === "json" || !process.stdout.isTTY) {
2337
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2338
+ } else if (moduleId) {
2339
+ formatExecResult(result, fmt);
2340
+ } else {
2341
+ formatUsageSummaryTty(result);
2342
+ }
2343
+ } catch (e) {
2344
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2345
+ `);
2346
+ process.exit(1);
2347
+ }
2348
+ });
2349
+ cli.addCommand(usageCmd);
2350
+ 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) => {
2351
+ if (!opts.yes) {
2352
+ process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
2353
+ }
2354
+ const fmt = resolveFormat(opts.format);
2355
+ try {
2356
+ const result = await callSystemModule(executor, "system.control.toggle_feature", {
2357
+ module_id: moduleId,
2358
+ enabled: true,
2359
+ reason: opts.reason
2360
+ });
2361
+ if (fmt === "json" || !process.stdout.isTTY) {
2362
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2363
+ } else {
2364
+ process.stdout.write(`Module '${moduleId}' enabled.
2365
+ Reason: ${opts.reason}
2366
+ `);
2367
+ }
2368
+ } catch (e) {
2369
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2370
+ `);
2371
+ process.exit(1);
2372
+ }
2373
+ });
2374
+ cli.addCommand(enableCmd);
2375
+ 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) => {
2376
+ if (!opts.yes) {
2377
+ process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
2378
+ }
2379
+ const fmt = resolveFormat(opts.format);
2380
+ try {
2381
+ const result = await callSystemModule(executor, "system.control.toggle_feature", {
2382
+ module_id: moduleId,
2383
+ enabled: false,
2384
+ reason: opts.reason
2385
+ });
2386
+ if (fmt === "json" || !process.stdout.isTTY) {
2387
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2388
+ } else {
2389
+ process.stdout.write(`Module '${moduleId}' disabled.
2390
+ Reason: ${opts.reason}
2391
+ `);
2392
+ }
2393
+ } catch (e) {
2394
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2395
+ `);
2396
+ process.exit(1);
2397
+ }
2398
+ });
2399
+ cli.addCommand(disableCmd);
2400
+ 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) => {
2401
+ if (!opts.yes) {
2402
+ process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
2403
+ }
2404
+ const fmt = resolveFormat(opts.format);
2405
+ try {
2406
+ const result = await callSystemModule(executor, "system.control.reload_module", {
2407
+ module_id: moduleId,
2408
+ reason: opts.reason
2409
+ });
2410
+ if (fmt === "json" || !process.stdout.isTTY) {
2411
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2412
+ } else {
2413
+ const prev = result.previous_version ?? "?";
2414
+ const newVer = result.new_version ?? "?";
2415
+ const dur = result.reload_duration_ms ?? "?";
2416
+ process.stdout.write(`Module '${moduleId}' reloaded.
2417
+ `);
2418
+ process.stdout.write(` Version: ${prev} -> ${newVer}
2419
+ `);
2420
+ process.stdout.write(` Duration: ${dur}ms
2421
+ `);
2422
+ }
2423
+ } catch (e) {
2424
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2425
+ `);
2426
+ process.exit(1);
2427
+ }
2428
+ });
2429
+ cli.addCommand(reloadCmd);
2430
+ const configGroup = new Command3("config").description("Read or update runtime configuration.");
2431
+ 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) => {
2432
+ const fmt = resolveFormat(opts.format);
2433
+ try {
2434
+ const result = await callSystemModule(executor, "system.config.get", { key });
2435
+ const value = result?.value ?? result;
2436
+ if (fmt === "json" || !process.stdout.isTTY) {
2437
+ process.stdout.write(JSON.stringify({ key, value }, null, 2) + "\n");
2438
+ } else {
2439
+ process.stdout.write(`${key} = ${JSON.stringify(value)}
2440
+ `);
2441
+ }
2442
+ } catch (e) {
2443
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2444
+ `);
2445
+ process.exit(1);
2446
+ }
2447
+ });
2448
+ configGroup.addCommand(configGetCmd);
2449
+ 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) => {
2450
+ const fmt = resolveFormat(opts.format);
2451
+ let parsedValue;
2452
+ try {
2453
+ parsedValue = JSON.parse(value);
2454
+ } catch {
2455
+ parsedValue = value;
2456
+ }
2457
+ try {
2458
+ const result = await callSystemModule(executor, "system.control.update_config", {
2459
+ key,
2460
+ value: parsedValue,
2461
+ reason: opts.reason
2462
+ });
2463
+ if (fmt === "json" || !process.stdout.isTTY) {
2464
+ process.stdout.write(JSON.stringify(result, null, 2) + "\n");
2465
+ } else {
2466
+ const old = result.old_value ?? "?";
2467
+ const newVal = result.new_value ?? "?";
2468
+ process.stdout.write(`Config updated: ${key}
2469
+ `);
2470
+ process.stdout.write(` ${JSON.stringify(old)} -> ${JSON.stringify(newVal)}
2471
+ `);
2472
+ process.stdout.write(` Reason: ${opts.reason}
2473
+ `);
2474
+ }
2475
+ } catch (e) {
2476
+ process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
2477
+ `);
2478
+ process.exit(1);
2479
+ }
2480
+ });
2481
+ configGroup.addCommand(configSetCmd);
2482
+ cli.addCommand(configGroup);
2483
+ }
2484
+
2485
+ // src/strategy.ts
2486
+ init_esm_shims();
2487
+ import { Command as Command4, Option as Option3 } from "commander";
2488
+ var PRESET_STEPS = {
2489
+ standard: [
2490
+ "context_creation",
2491
+ "call_chain_guard",
2492
+ "module_lookup",
2493
+ "acl_check",
2494
+ "approval_gate",
2495
+ "middleware_before",
2496
+ "input_validation",
2497
+ "execute",
2498
+ "output_validation",
2499
+ "middleware_after",
2500
+ "return_result"
2501
+ ],
2502
+ internal: [
2503
+ "context_creation",
2504
+ "call_chain_guard",
2505
+ "module_lookup",
2506
+ "middleware_before",
2507
+ "input_validation",
2508
+ "execute",
2509
+ "output_validation",
2510
+ "middleware_after",
2511
+ "return_result"
2512
+ ],
2513
+ testing: [
2514
+ "context_creation",
2515
+ "module_lookup",
2516
+ "middleware_before",
2517
+ "input_validation",
2518
+ "execute",
2519
+ "output_validation",
2520
+ "middleware_after",
2521
+ "return_result"
2522
+ ],
2523
+ performance: [
2524
+ "context_creation",
2525
+ "call_chain_guard",
2526
+ "module_lookup",
2527
+ "acl_check",
2528
+ "approval_gate",
2529
+ "input_validation",
2530
+ "execute",
2531
+ "output_validation",
2532
+ "return_result"
2533
+ ],
2534
+ minimal: [
2535
+ "context_creation",
2536
+ "module_lookup",
2537
+ "execute",
2538
+ "return_result"
2539
+ ]
2540
+ };
2541
+ function registerPipelineCommand(cli, executor) {
2542
+ const pipelineCmd = new Command4("describe-pipeline").description("Show the execution pipeline steps for a strategy.").addOption(
2543
+ new Option3("--strategy <name>", "Strategy to describe (default: standard).").choices(["standard", "internal", "testing", "performance", "minimal"]).default("standard")
2544
+ ).option("--format <format>", "Output format.").action((opts) => {
2545
+ const fmt = resolveFormat(opts.format);
2546
+ let strategyObj = null;
2547
+ const ex = executor;
2548
+ if (typeof ex._resolve_strategy_name === "function" || typeof ex._resolveStrategyName === "function") {
2549
+ try {
2550
+ const fn = ex._resolve_strategy_name ?? ex._resolveStrategyName;
2551
+ strategyObj = fn(opts.strategy);
2552
+ } catch {
2553
+ strategyObj = null;
2554
+ }
2555
+ }
2556
+ if (!strategyObj) {
2557
+ const steps = PRESET_STEPS[opts.strategy] ?? [];
2558
+ const pureSteps = /* @__PURE__ */ new Set([
2559
+ "context_creation",
2560
+ "call_chain_guard",
2561
+ "module_lookup",
2562
+ "acl_check",
2563
+ "input_validation"
2564
+ ]);
2565
+ const nonRemovable = /* @__PURE__ */ new Set([
2566
+ "context_creation",
2567
+ "module_lookup",
2568
+ "execute",
2569
+ "return_result"
2570
+ ]);
2571
+ if (fmt === "json" || !process.stdout.isTTY) {
2572
+ const payload = {
2573
+ strategy: opts.strategy,
2574
+ step_count: steps.length,
2575
+ steps: steps.map((s, i) => ({
2576
+ index: i + 1,
2577
+ name: s,
2578
+ pure: pureSteps.has(s),
2579
+ removable: !nonRemovable.has(s)
2580
+ }))
2581
+ };
2582
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
2583
+ } else {
2584
+ process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
2585
+
2586
+ `);
2587
+ process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
2588
+ `);
2589
+ process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
2590
+ `);
2591
+ for (let i = 0; i < steps.length; i++) {
2592
+ const pure = pureSteps.has(steps[i]) ? "yes" : "no";
2593
+ const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
2594
+ process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
2595
+ `);
2596
+ }
2597
+ }
2598
+ return;
2599
+ }
2600
+ const stepsInfo = strategyObj.steps.map((step) => ({
2601
+ name: step.name,
2602
+ pure: step.pure ?? false,
2603
+ removable: step.removable ?? true,
2604
+ timeout_ms: step.timeout_ms ?? null
2605
+ }));
2606
+ if (fmt === "json" || !process.stdout.isTTY) {
2607
+ const payload = {
2608
+ strategy: opts.strategy,
2609
+ step_count: stepsInfo.length,
2610
+ steps: stepsInfo.map((s, i) => ({ index: i + 1, ...s }))
2611
+ };
2612
+ process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
2613
+ } else {
2614
+ process.stdout.write(`Pipeline: ${opts.strategy} (${stepsInfo.length} steps)
2615
+
2616
+ `);
2617
+ process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
2618
+ `);
2619
+ process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
2620
+ `);
2621
+ for (let i = 0; i < stepsInfo.length; i++) {
2622
+ const s = stepsInfo[i];
2623
+ const pure = s.pure ? "yes" : "no";
2624
+ const removable = s.removable ? "yes" : "no";
2625
+ const timeout = s.timeout_ms !== null ? `${s.timeout_ms}ms` : "\u2014";
2626
+ process.stdout.write(` ${String(i + 1).padEnd(4)} ${s.name.padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
2627
+ `);
2628
+ }
2629
+ }
2630
+ });
2631
+ cli.addCommand(pipelineCmd);
2632
+ }
2633
+
2634
+ // src/cli.ts
2635
+ init_esm_shims();
2636
+ import { Command as Command5 } from "commander";
2637
+ var BUILTIN_COMMANDS = [
2638
+ "completion",
2639
+ "config",
2640
+ "describe",
2641
+ "describe-pipeline",
2642
+ "disable",
2643
+ "enable",
2644
+ "exec",
2645
+ "health",
2646
+ "init",
2647
+ "list",
2648
+ "man",
2649
+ "reload",
2650
+ "usage",
2651
+ "validate"
2652
+ ];
2653
+ var LazyModuleGroup = class {
2654
+ registry;
2655
+ executor;
2656
+ helpTextMaxLength;
2657
+ commandCache = /* @__PURE__ */ new Map();
2658
+ /** alias -> canonical module_id (populated lazily) */
2659
+ aliasMap = /* @__PURE__ */ new Map();
2660
+ /** module_id -> descriptor cache (populated during alias map build) */
2661
+ descriptorCache = /* @__PURE__ */ new Map();
2662
+ aliasMapBuilt = false;
2663
+ constructor(registry, executor, helpTextMaxLength = 1e3) {
2664
+ this.registry = registry;
2665
+ this.executor = executor;
2666
+ this.helpTextMaxLength = helpTextMaxLength;
2667
+ }
2668
+ /**
2669
+ * Build alias->module_id map from display overlay metadata.
2670
+ */
2671
+ buildAliasMap() {
2672
+ if (this.aliasMapBuilt) {
2673
+ return;
2674
+ }
2675
+ try {
2676
+ for (const descriptor of this.registry.listModules()) {
2677
+ const moduleId = descriptor.id;
2678
+ this.descriptorCache.set(moduleId, descriptor);
2679
+ const display = getDisplay(descriptor);
2680
+ const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
2681
+ const cliAlias = cliDisplay.alias;
2682
+ if (cliAlias && cliAlias !== moduleId) {
2683
+ this.aliasMap.set(cliAlias, moduleId);
1681
2684
  }
1682
2685
  }
1683
- this.groupMapBuilt = true;
2686
+ this.aliasMapBuilt = true;
1684
2687
  } catch {
1685
- warn("Failed to build group map");
2688
+ warn("Failed to build alias map from registry");
1686
2689
  }
1687
2690
  }
1688
2691
  /**
1689
- * List all available command names: builtins + group names + top-level module names.
2692
+ * List all available command names from the Registry.
1690
2693
  */
1691
2694
  listCommands() {
1692
- this.buildGroupMap();
1693
- const groupNames = [...this.groupMap.keys()].filter(
1694
- (g) => !BUILTIN_COMMANDS.includes(g)
1695
- );
1696
- const topNames = [...this.topLevelModules.keys()];
1697
- return [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...groupNames, ...topNames])].sort();
2695
+ this.buildAliasMap();
2696
+ const reverse = /* @__PURE__ */ new Map();
2697
+ for (const [alias, moduleId] of this.aliasMap) {
2698
+ reverse.set(moduleId, alias);
2699
+ }
2700
+ const moduleIds = this.registry.listModules().map((m) => m.id);
2701
+ const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
2702
+ return [...new Set(names)].sort();
1698
2703
  }
1699
2704
  /**
1700
- * Get a command by name: check builtins -> group cache -> group map -> top-level modules.
2705
+ * Get or lazily build a Commander Command for the given module.
1701
2706
  */
1702
2707
  getCommand(cmdName) {
1703
- this.buildGroupMap();
1704
- if (this.groupCache.has(cmdName)) {
1705
- return this.groupCache.get(cmdName).command;
2708
+ if (this.commandCache.has(cmdName)) {
2709
+ return this.commandCache.get(cmdName);
1706
2710
  }
1707
- if (this.groupMap.has(cmdName)) {
1708
- const lazyGrp = new LazyGroup(
1709
- this.groupMap.get(cmdName),
1710
- this.executor,
1711
- cmdName,
1712
- this.helpTextMaxLength
1713
- );
1714
- this.groupCache.set(cmdName, lazyGrp);
1715
- return lazyGrp.command;
2711
+ this.buildAliasMap();
2712
+ const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
2713
+ let moduleDef = this.descriptorCache.get(moduleId);
2714
+ if (!moduleDef) {
2715
+ moduleDef = this.registry.getModule(moduleId) ?? void 0;
1716
2716
  }
1717
- if (this.topLevelModules.has(cmdName)) {
1718
- if (this.commandCache.has(cmdName)) {
1719
- return this.commandCache.get(cmdName);
1720
- }
1721
- const [, descriptor] = this.topLevelModules.get(cmdName);
2717
+ if (!moduleDef) {
2718
+ return null;
2719
+ }
2720
+ const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
2721
+ this.commandCache.set(cmdName, cmd);
2722
+ return cmd;
2723
+ }
2724
+ };
2725
+ var LazyGroup = class {
2726
+ members;
2727
+ _executor;
2728
+ _helpTextMaxLength;
2729
+ _cmdCache = /* @__PURE__ */ new Map();
2730
+ command;
2731
+ constructor(members, executor, name, helpTextMaxLength = 1e3) {
2732
+ this.members = members;
2733
+ this._executor = executor;
2734
+ this._helpTextMaxLength = helpTextMaxLength;
2735
+ this.command = new Command5(name).description(`${name} commands`);
2736
+ for (const [cmdName, [, descriptor]] of this.members) {
1722
2737
  const cmd = buildModuleCommand(
1723
2738
  descriptor,
1724
- this.executor,
1725
- this.helpTextMaxLength,
2739
+ this._executor,
2740
+ this._helpTextMaxLength,
1726
2741
  cmdName
1727
2742
  );
1728
- this.commandCache.set(cmdName, cmd);
1729
- return cmd;
2743
+ this._cmdCache.set(cmdName, cmd);
2744
+ this.command.addCommand(cmd);
1730
2745
  }
1731
- return null;
1732
- }
1733
- /** Expose groupMap for testing. */
1734
- getGroupMap() {
1735
- return this.groupMap;
1736
2746
  }
1737
- /** Expose topLevelModules for testing. */
1738
- getTopLevelModules() {
1739
- return this.topLevelModules;
2747
+ listCommands() {
2748
+ return [...this.members.keys()].sort();
1740
2749
  }
1741
- /** Expose groupMapBuilt for testing. */
1742
- isGroupMapBuilt() {
1743
- return this.groupMapBuilt;
2750
+ getCommand(cmdName) {
2751
+ if (this._cmdCache.has(cmdName)) {
2752
+ return this._cmdCache.get(cmdName);
2753
+ }
2754
+ const entry = this.members.get(cmdName);
2755
+ if (!entry) {
2756
+ return null;
2757
+ }
2758
+ const [, descriptor] = entry;
2759
+ const cmd = buildModuleCommand(
2760
+ descriptor,
2761
+ this._executor,
2762
+ this._helpTextMaxLength,
2763
+ cmdName
2764
+ );
2765
+ this._cmdCache.set(cmdName, cmd);
2766
+ return cmd;
1744
2767
  }
1745
2768
  };
1746
-
1747
- // src/config.ts
1748
- init_esm_shims();
1749
- import * as fs4 from "fs";
1750
- import yaml from "js-yaml";
1751
- var DEFAULTS = {
1752
- "extensions.root": "./extensions",
1753
- "logging.level": "WARNING",
1754
- "sandbox.enabled": false,
1755
- "cli.stdin_buffer_limit": 10485760,
1756
- "cli.auto_approve": false,
1757
- "cli.help_text_max_length": 1e3
1758
- };
1759
- var ConfigResolver = class {
1760
- cliFlags;
1761
- configPath;
1762
- fileCache = null;
1763
- fileCacheLoaded = false;
1764
- constructor(cliFlags, configPath) {
1765
- this.cliFlags = cliFlags ?? {};
1766
- this.configPath = configPath ?? "apcore.yaml";
1767
- }
2769
+ var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
2770
+ /** groupName -> { cmdName -> [moduleId, descriptor] } */
2771
+ groupMap = /* @__PURE__ */ new Map();
2772
+ /** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
2773
+ topLevelModules = /* @__PURE__ */ new Map();
2774
+ /** Cached LazyGroup instances */
2775
+ groupCache = /* @__PURE__ */ new Map();
2776
+ groupMapBuilt = false;
1768
2777
  /**
1769
- * Resolve a single configuration key across all four tiers.
2778
+ * Determine (groupName | null, commandName) for a module from its display overlay.
2779
+ *
2780
+ * @param groupDepth Number of dotted segments to consume as the group prefix.
2781
+ * Defaults to 1 (e.g., "math.add" → group="math", cmd="add").
2782
+ * Set to 2 for multi-level grouping (e.g., "math.trig.sin" →
2783
+ * group="math.trig", cmd="sin").
1770
2784
  */
1771
- resolve(key, cliFlag, envVar) {
1772
- const flagKey = cliFlag ?? key;
1773
- if (flagKey in this.cliFlags) {
1774
- const value = this.cliFlags[flagKey];
1775
- if (value !== null && value !== void 0) {
1776
- return value;
1777
- }
1778
- }
1779
- if (envVar) {
1780
- const envValue = process.env[envVar];
1781
- if (envValue !== void 0 && envValue !== "") {
1782
- return envValue;
1783
- }
2785
+ static resolveGroup(moduleId, descriptor, groupDepth = 1) {
2786
+ if (!moduleId) {
2787
+ warn("Empty module_id encountered in resolveGroup");
2788
+ return [null, ""];
1784
2789
  }
1785
- const fileValue = this.resolveFromFile(key);
1786
- if (fileValue !== void 0) {
1787
- return fileValue;
2790
+ const display = getDisplay(descriptor);
2791
+ const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
2792
+ const explicitGroup = cliDisplay.group;
2793
+ if (typeof explicitGroup === "string" && explicitGroup !== "") {
2794
+ return [explicitGroup, cliDisplay.alias ?? moduleId];
1788
2795
  }
1789
- return DEFAULTS[key];
1790
- }
1791
- /**
1792
- * Load a value from the config file using a dot-separated key path.
1793
- */
1794
- resolveFromFile(key) {
1795
- if (!this.fileCacheLoaded) {
1796
- this.fileCache = this.loadConfigFile();
1797
- this.fileCacheLoaded = true;
2796
+ if (explicitGroup === "") {
2797
+ return [null, cliDisplay.alias ?? moduleId];
1798
2798
  }
1799
- if (this.fileCache === null) {
1800
- return void 0;
2799
+ const cliName = cliDisplay.alias ?? moduleId;
2800
+ if (cliName.includes(".")) {
2801
+ const parts = cliName.split(".");
2802
+ const depth = Math.max(1, Math.min(groupDepth, parts.length - 1));
2803
+ const group = parts.slice(0, depth).join(".");
2804
+ const cmd = parts.slice(depth).join(".");
2805
+ return [group, cmd];
1801
2806
  }
1802
- return this.fileCache[key];
2807
+ return [null, cliName];
1803
2808
  }
1804
2809
  /**
1805
- * Load and flatten a YAML config file.
2810
+ * Build the group map from registry modules.
1806
2811
  */
1807
- loadConfigFile() {
1808
- let content;
1809
- try {
1810
- content = fs4.readFileSync(this.configPath, "utf-8");
1811
- } catch (err) {
1812
- if (err instanceof Error && "code" in err && err.code === "ENOENT") {
1813
- return null;
1814
- }
1815
- console.warn(
1816
- `Configuration file '${this.configPath}' is malformed, using defaults.`
1817
- );
1818
- return null;
2812
+ buildGroupMap() {
2813
+ if (this.groupMapBuilt) {
2814
+ return;
1819
2815
  }
1820
- let parsed;
1821
2816
  try {
1822
- parsed = yaml.load(content);
1823
- } catch {
1824
- console.warn(
1825
- `Configuration file '${this.configPath}' is malformed, using defaults.`
1826
- );
1827
- return null;
1828
- }
1829
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1830
- console.warn(
1831
- `Configuration file '${this.configPath}' is malformed, using defaults.`
1832
- );
1833
- return null;
1834
- }
1835
- return this.flattenDict(parsed);
1836
- }
1837
- /**
1838
- * Flatten nested dict to dot-notation keys.
1839
- */
1840
- flattenDict(d, prefix = "") {
1841
- const result = {};
1842
- for (const [key, value] of Object.entries(d)) {
1843
- const fullKey = prefix ? `${prefix}.${key}` : key;
1844
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
1845
- Object.assign(
1846
- result,
1847
- this.flattenDict(value, fullKey)
1848
- );
1849
- } else {
1850
- result[fullKey] = value;
2817
+ this.buildAliasMap();
2818
+ for (const descriptor of this.registry.listModules()) {
2819
+ const moduleId = descriptor.id;
2820
+ const cached = this.descriptorCache.get(moduleId);
2821
+ if (!cached) {
2822
+ continue;
2823
+ }
2824
+ const [group, cmd] = _GroupedModuleGroup.resolveGroup(moduleId, cached);
2825
+ if (group === null) {
2826
+ this.topLevelModules.set(cmd, [moduleId, cached]);
2827
+ } else if (!/^[a-z][a-z0-9_-]*$/.test(group)) {
2828
+ warn(
2829
+ `Module '${moduleId}': group name '${group}' is not shell-safe \u2014 treating as top-level.`
2830
+ );
2831
+ this.topLevelModules.set(cmd, [moduleId, cached]);
2832
+ } else {
2833
+ if (!this.groupMap.has(group)) {
2834
+ this.groupMap.set(group, /* @__PURE__ */ new Map());
2835
+ }
2836
+ this.groupMap.get(group).set(cmd, [moduleId, cached]);
2837
+ }
2838
+ }
2839
+ for (const groupName of this.groupMap.keys()) {
2840
+ if (BUILTIN_COMMANDS.includes(groupName)) {
2841
+ warn(
2842
+ `Group name '${groupName}' collides with a built-in command and will be ignored`
2843
+ );
2844
+ }
1851
2845
  }
2846
+ this.groupMapBuilt = true;
2847
+ } catch {
2848
+ warn("Failed to build group map");
1852
2849
  }
1853
- return result;
1854
2850
  }
1855
- };
1856
-
1857
- // src/discovery.ts
1858
- init_esm_shims();
1859
- init_errors();
1860
- import { Command as Command3 } from "commander";
1861
- var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
1862
- function validateTag(tag) {
1863
- if (!TAG_PATTERN.test(tag)) {
1864
- process.stderr.write(
1865
- `Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
1866
- `
2851
+ /**
2852
+ * List all available command names: builtins + group names + top-level module names.
2853
+ */
2854
+ listCommands() {
2855
+ this.buildGroupMap();
2856
+ const groupNames = [...this.groupMap.keys()].filter(
2857
+ (g) => !BUILTIN_COMMANDS.includes(g)
1867
2858
  );
1868
- process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2859
+ const topNames = [...this.topLevelModules.keys()];
2860
+ return [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...groupNames, ...topNames])].sort();
1869
2861
  }
1870
- }
1871
- function collectTag(value, previous) {
1872
- return previous.concat([value]);
1873
- }
1874
- function registerDiscoveryCommands(cli, registry) {
1875
- const listCmd = new Command3("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--format <format>", "Output format.", void 0).action((opts) => {
1876
- for (const t of opts.tag) {
1877
- validateTag(t);
1878
- }
1879
- const modules = [];
1880
- for (const m of registry.listModules()) {
1881
- modules.push(m);
2862
+ /**
2863
+ * Get a command by name: check builtins -> group cache -> group map -> top-level modules.
2864
+ */
2865
+ getCommand(cmdName) {
2866
+ this.buildGroupMap();
2867
+ if (this.groupCache.has(cmdName)) {
2868
+ return this.groupCache.get(cmdName).command;
1882
2869
  }
1883
- let filtered = modules;
1884
- if (opts.tag.length > 0) {
1885
- const filterTags = new Set(opts.tag);
1886
- filtered = modules.filter((m) => {
1887
- const mTags = m.tags ?? [];
1888
- return [...filterTags].every((t) => mTags.includes(t));
1889
- });
2870
+ if (this.groupMap.has(cmdName)) {
2871
+ const lazyGrp = new LazyGroup(
2872
+ this.groupMap.get(cmdName),
2873
+ this.executor,
2874
+ cmdName,
2875
+ this.helpTextMaxLength
2876
+ );
2877
+ this.groupCache.set(cmdName, lazyGrp);
2878
+ return lazyGrp.command;
1890
2879
  }
1891
- const fmt = resolveFormat(opts.format);
1892
- formatModuleList(filtered, fmt, opts.tag.length > 0 ? opts.tag : void 0);
1893
- });
1894
- cli.addCommand(listCmd);
1895
- const describeCmd = new Command3("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) => {
1896
- validateModuleId(moduleId);
1897
- const moduleDef = registry.getModule(moduleId);
1898
- if (!moduleDef) {
1899
- process.stderr.write(
1900
- `Error: Module '${moduleId}' not found.
1901
- `
2880
+ if (this.topLevelModules.has(cmdName)) {
2881
+ if (this.commandCache.has(cmdName)) {
2882
+ return this.commandCache.get(cmdName);
2883
+ }
2884
+ const [, descriptor] = this.topLevelModules.get(cmdName);
2885
+ const cmd = buildModuleCommand(
2886
+ descriptor,
2887
+ this.executor,
2888
+ this.helpTextMaxLength,
2889
+ cmdName
1902
2890
  );
1903
- process.exit(EXIT_CODES.MODULE_NOT_FOUND);
2891
+ this.commandCache.set(cmdName, cmd);
2892
+ return cmd;
1904
2893
  }
1905
- const fmt = resolveFormat(opts.format);
1906
- formatModuleDetail(moduleDef, fmt);
1907
- });
1908
- cli.addCommand(describeCmd);
1909
- }
1910
-
1911
- // src/shell.ts
1912
- init_esm_shims();
1913
- init_errors();
1914
- import { readFileSync as readFileSync3 } from "fs";
1915
- import { fileURLToPath as fileURLToPath3 } from "url";
1916
- import * as path6 from "path";
1917
- import { Command as Command4, Help, Option as Option2 } from "commander";
1918
- var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
1919
- var SHELL_VERSION = "0.0.0";
1920
- try {
1921
- const pkg = JSON.parse(readFileSync3(path6.resolve(__dirname3, "../package.json"), "utf-8"));
1922
- SHELL_VERSION = pkg.version;
1923
- } catch {
1924
- }
1925
- function makeFunctionName(progName) {
1926
- return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
1927
- }
1928
- function shellQuote(s) {
1929
- return "'" + s.replace(/'/g, "'\\''") + "'";
1930
- }
1931
- function generateBashCompletion(progName) {
1932
- const fn = makeFunctionName(progName);
1933
- const quoted = shellQuote(progName);
1934
- const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
1935
- const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
1936
- const m=require('fs').readFileSync('/dev/stdin','utf8');
1937
- const ids=JSON.parse(m).map(x=>x.id);
1938
- const g=new Set(),t=[];
1939
- ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
1940
- console.log([...g].sort().concat(t.sort()).join(' '))
1941
- " 2>/dev/null`;
1942
- const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
1943
- const m=require('fs').readFileSync('/dev/stdin','utf8');
1944
- const g=process.env._APCORE_GRP;
1945
- JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
1946
- " 2>/dev/null`;
1947
- return `${fn}() {
1948
- local cur prev opts
1949
- COMPREPLY=()
1950
- cur="\${COMP_WORDS[COMP_CWORD]}"
1951
- prev="\${COMP_WORDS[COMP_CWORD-1]}"
1952
-
1953
- if [[ \${COMP_CWORD} -eq 1 ]]; then
1954
- opts="completion describe exec init list man"
1955
- local groups_and_top=$(${groupsAndTopCmd})
1956
- COMPREPLY=( $(compgen -W "\${opts} \${groups_and_top}" -- \${cur}) )
1957
- return 0
1958
- fi
1959
-
1960
- if [[ \${COMP_CWORD} -eq 2 ]]; then
1961
- if [[ "\${COMP_WORDS[1]}" == "exec" ]]; then
1962
- local modules=$(${moduleListCmd})
1963
- COMPREPLY=( $(compgen -W "\${modules}" -- \${cur}) )
1964
- return 0
1965
- fi
1966
- export _APCORE_GRP="\${COMP_WORDS[1]}"
1967
- local group_cmds=$(${groupCmdsCmd})
1968
- COMPREPLY=( $(compgen -W "\${group_cmds}" -- \${cur}) )
1969
- return 0
1970
- fi
1971
- }
1972
- complete -F ${fn} ${quoted}
1973
- `;
1974
- }
1975
- function generateZshCompletion(progName) {
1976
- const fn = makeFunctionName(progName);
1977
- const quoted = shellQuote(progName);
1978
- const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
1979
- const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
1980
- const m=require('fs').readFileSync('/dev/stdin','utf8');
1981
- const ids=JSON.parse(m).map(x=>x.id);
1982
- const g=new Set(),t=[];
1983
- ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
1984
- console.log([...g].sort().concat(t.sort()).join(' '))
1985
- " 2>/dev/null`;
1986
- const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
1987
- const m=require('fs').readFileSync('/dev/stdin','utf8');
1988
- const g=process.env._APCORE_GRP;
1989
- JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
1990
- " 2>/dev/null`;
1991
- return `#compdef ${progName}
1992
-
1993
- ${fn}() {
1994
- local -a commands
1995
- commands=(
1996
- 'list:List available modules'
1997
- 'describe:Show module metadata and schema'
1998
- 'completion:Generate shell completion script'
1999
- 'init:Scaffolding commands'
2000
- 'man:Generate man page'
2001
- )
2002
-
2003
- _arguments -C \\
2004
- '1:command:->command' \\
2005
- '*::arg:->args'
2006
-
2007
- case "$state" in
2008
- command)
2009
- _describe -t commands '${progName} commands' commands
2010
- local -a groups_and_top
2011
- groups_and_top=($(${groupsAndTopCmd}))
2012
- compadd -a groups_and_top
2013
- ;;
2014
- args)
2015
- case "\${words[1]}" in
2016
- exec)
2017
- local modules
2018
- modules=($(${moduleListCmd}))
2019
- compadd -a modules
2020
- ;;
2021
- *)
2022
- export _APCORE_GRP="\${words[1]}"
2023
- local -a group_cmds
2024
- group_cmds=($(${groupCmdsCmd}))
2025
- compadd -a group_cmds
2026
- ;;
2027
- esac
2028
- ;;
2029
- esac
2030
- }
2031
-
2032
- compdef ${fn} ${quoted}
2033
- `;
2034
- }
2035
- function generateFishCompletion(progName) {
2036
- const quoted = shellQuote(progName);
2037
- const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e \\"process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})\\" 2>/dev/null`;
2038
- const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e \\"const m=require('fs').readFileSync('/dev/stdin','utf8');const ids=JSON.parse(m).map(x=>x.id);const g=new Set(),t=[];ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});console.log([...g].sort().concat(t.sort()).join('\\\\n'))\\" 2>/dev/null`;
2039
- return `# Fish completions for ${progName}
2040
- complete -c ${quoted} -n "__fish_use_subcommand" -a list -d "List available modules"
2041
- complete -c ${quoted} -n "__fish_use_subcommand" -a describe -d "Show module metadata and schema"
2042
- complete -c ${quoted} -n "__fish_use_subcommand" -a completion -d "Generate shell completion script"
2043
- complete -c ${quoted} -n "__fish_use_subcommand" -a init -d "Scaffolding commands"
2044
- complete -c ${quoted} -n "__fish_use_subcommand" -a man -d "Generate man page"
2045
- complete -c ${quoted} -n "__fish_use_subcommand" -a "(${groupsAndTopCmd})" -d "Module group"
2046
-
2047
- complete -c ${quoted} -n "__fish_seen_subcommand_from exec" -a "(${moduleListCmd})"
2048
-
2049
- function __apcore_group_cmds
2050
- set -l grp (commandline -opc)[2]
2051
- set -x _APCORE_GRP $grp
2052
- ${quoted} list --format json 2>/dev/null | node -e "
2053
- const m=require('fs').readFileSync('/dev/stdin','utf8');
2054
- const g=process.env._APCORE_GRP;
2055
- JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
2056
- " 2>/dev/null
2057
- end
2894
+ return null;
2895
+ }
2896
+ /** Expose groupMap for testing. */
2897
+ getGroupMap() {
2898
+ return this.groupMap;
2899
+ }
2900
+ /** Expose topLevelModules for testing. */
2901
+ getTopLevelModules() {
2902
+ return this.topLevelModules;
2903
+ }
2904
+ /** Expose groupMapBuilt for testing. */
2905
+ isGroupMapBuilt() {
2906
+ return this.groupMapBuilt;
2907
+ }
2908
+ };
2058
2909
 
2059
- # Group subcommand completion \u2014 matches when position 1 is not a builtin
2060
- complete -c ${quoted} -n "not __fish_use_subcommand; and not __fish_seen_subcommand_from list describe completion init man exec" -a "(__apcore_group_cmds)"
2061
- `;
2910
+ // src/main.ts
2911
+ var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
2912
+ var verboseHelp = false;
2913
+ function setVerboseHelp(verbose) {
2914
+ verboseHelp = verbose;
2062
2915
  }
2063
- function buildSynopsis(command, progName, commandName) {
2064
- if (!command) {
2065
- return `\\fB${progName} ${commandName}\\fR [OPTIONS]`;
2066
- }
2067
- const parts = [`\\fB${progName} ${commandName}\\fR`];
2068
- for (const opt of command.options) {
2069
- const flag = opt.long ?? opt.short ?? "";
2070
- if (opt.isBoolean?.()) {
2071
- parts.push(`[${flag}]`);
2072
- } else if (opt.required) {
2073
- const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
2074
- parts.push(`${flag} \\fI${typeName}\\fR`);
2075
- } else {
2076
- const typeName = (opt.argChoices ? "CHOICE" : "VALUE").toUpperCase();
2077
- parts.push(`[${flag} \\fI${typeName}\\fR]`);
2916
+ var docsUrl = null;
2917
+ function setDocsUrl(url) {
2918
+ docsUrl = url;
2919
+ }
2920
+ function hasVerboseFlag() {
2921
+ return process.argv.includes("--verbose");
2922
+ }
2923
+ var VERSION = "0.0.0";
2924
+ try {
2925
+ const pkg = JSON.parse(readFileSync3(path6.resolve(__dirname3, "../package.json"), "utf-8"));
2926
+ VERSION = pkg.version;
2927
+ } catch {
2928
+ }
2929
+ var ERROR_CODE_MAP = {
2930
+ MODULE_NOT_FOUND: 44,
2931
+ MODULE_LOAD_ERROR: 44,
2932
+ MODULE_DISABLED: 44,
2933
+ SCHEMA_VALIDATION_ERROR: 45,
2934
+ SCHEMA_CIRCULAR_REF: 48,
2935
+ APPROVAL_DENIED: 46,
2936
+ APPROVAL_TIMEOUT: 46,
2937
+ APPROVAL_PENDING: 46,
2938
+ CONFIG_NOT_FOUND: 47,
2939
+ CONFIG_INVALID: 47,
2940
+ MODULE_EXECUTE_ERROR: 1,
2941
+ MODULE_TIMEOUT: 1,
2942
+ ACL_DENIED: 77,
2943
+ CONFIG_NAMESPACE_RESERVED: 78,
2944
+ CONFIG_NAMESPACE_DUPLICATE: 78,
2945
+ CONFIG_ENV_PREFIX_CONFLICT: 78,
2946
+ CONFIG_ENV_MAP_CONFLICT: 78,
2947
+ CONFIG_MOUNT_ERROR: 66,
2948
+ CONFIG_BIND_ERROR: 65,
2949
+ ERROR_FORMATTER_DUPLICATE: 70
2950
+ };
2951
+ function emitErrorJson(e, exitCode) {
2952
+ const err = e instanceof Error ? e : new Error(String(e));
2953
+ const errRecord = err;
2954
+ const code = errRecord.code ?? "UNKNOWN";
2955
+ const payload = {
2956
+ error: true,
2957
+ code,
2958
+ message: err.message,
2959
+ exit_code: exitCode
2960
+ };
2961
+ for (const field of ["details", "suggestion", "ai_guidance", "retryable", "user_fixable"]) {
2962
+ const val = errRecord[field];
2963
+ if (val !== void 0 && val !== null) {
2964
+ payload[field] = val;
2965
+ }
2966
+ }
2967
+ process.stderr.write(JSON.stringify(payload) + "\n");
2968
+ }
2969
+ function emitErrorTty(e, exitCode) {
2970
+ const err = e instanceof Error ? e : new Error(String(e));
2971
+ const errRecord = err;
2972
+ const code = errRecord.code;
2973
+ const header = code ? `Error [${code}]: ${err.message}` : `Error: ${err.message}`;
2974
+ process.stderr.write(header + "\n");
2975
+ const details = errRecord.details;
2976
+ if (details && typeof details === "object" && !Array.isArray(details)) {
2977
+ process.stderr.write("\n Details:\n");
2978
+ for (const [k, v] of Object.entries(details)) {
2979
+ process.stderr.write(` ${k}: ${v}
2980
+ `);
2078
2981
  }
2079
2982
  }
2080
- for (const arg of command.registeredArguments ?? []) {
2081
- const meta = arg.name().toUpperCase();
2082
- if (arg.required) {
2083
- parts.push(`\\fI${meta}\\fR`);
2084
- } else {
2085
- parts.push(`[\\fI${meta}\\fR]`);
2086
- }
2983
+ const suggestion = errRecord.suggestion;
2984
+ if (suggestion) {
2985
+ process.stderr.write(`
2986
+ Suggestion: ${suggestion}
2987
+ `);
2087
2988
  }
2088
- return parts.join(" ");
2989
+ const retryable = errRecord.retryable;
2990
+ if (retryable !== void 0 && retryable !== null) {
2991
+ const label = retryable ? "Yes" : "No (same input will fail again)";
2992
+ process.stderr.write(` Retryable: ${label}
2993
+ `);
2994
+ }
2995
+ process.stderr.write(`
2996
+ Exit code: ${exitCode}
2997
+ `);
2089
2998
  }
2090
- function generateManPage(commandName, command, progName, version = SHELL_VERSION) {
2091
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2092
- const title = `${progName}-${commandName}`.toUpperCase();
2093
- const pkgLabel = `${progName} ${version}`;
2094
- const manualLabel = `${progName} Manual`;
2095
- const sections = [];
2096
- sections.push(`.TH "${title}" "1" "${today}" "${pkgLabel}" "${manualLabel}"`);
2097
- sections.push(".SH NAME");
2098
- const desc = command?.description() ?? commandName;
2099
- const nameDesc = desc.split("\n")[0].replace(/\.$/, "");
2100
- sections.push(`${progName}-${commandName} \\- ${nameDesc}`);
2101
- sections.push(".SH SYNOPSIS");
2102
- sections.push(buildSynopsis(command, progName, commandName));
2103
- if (command?.description()) {
2104
- sections.push(".SH DESCRIPTION");
2105
- sections.push(
2106
- command.description().replace(/\\/g, "\\\\").replace(/-/g, "\\-")
2107
- );
2999
+ function createCli(extensionsDirOrOpts, progName, verbose = false) {
3000
+ let extensionsDir;
3001
+ let registry;
3002
+ let executor;
3003
+ let extraCommands;
3004
+ if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
3005
+ extensionsDir = extensionsDirOrOpts.extensionsDir;
3006
+ progName = extensionsDirOrOpts.progName ?? progName;
3007
+ verbose = extensionsDirOrOpts.verbose ?? verbose;
3008
+ registry = extensionsDirOrOpts.registry;
3009
+ executor = extensionsDirOrOpts.executor;
3010
+ extraCommands = extensionsDirOrOpts.extraCommands;
3011
+ } else {
3012
+ extensionsDir = extensionsDirOrOpts;
2108
3013
  }
2109
- if (command && command.options.length > 0) {
2110
- sections.push(".SH OPTIONS");
2111
- for (const opt of command.options) {
2112
- const flag = [opt.short, opt.long].filter(Boolean).join(", ");
2113
- sections.push(".TP");
2114
- if (opt.isBoolean?.()) {
2115
- sections.push(`\\fB${flag}\\fR`);
2116
- } else {
2117
- sections.push(`\\fB${flag}\\fR \\fIVALUE\\fR`);
2118
- }
2119
- if (opt.description) {
2120
- sections.push(opt.description);
2121
- }
2122
- if (opt.defaultValue !== void 0 && !opt.isBoolean?.()) {
2123
- sections.push(`Default: ${opt.defaultValue}.`);
3014
+ verboseHelp = verbose;
3015
+ registerConfigNamespace();
3016
+ const resolvedProgName = progName ?? path6.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
3017
+ const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
3018
+ setLogLevel(cliLogLevel);
3019
+ 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)");
3020
+ if (executor && !registry) {
3021
+ throw new Error("executor requires registry \u2014 pass both or neither");
3022
+ }
3023
+ if (registry) {
3024
+ program._registry = registry;
3025
+ if (executor) {
3026
+ program._executor = executor;
3027
+ registerValidateCommand(program, registry, executor);
3028
+ void registerSystemCommands(program, executor);
3029
+ registerPipelineCommand(program, executor);
3030
+ }
3031
+ } else {
3032
+ const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
3033
+ void resolvedExtDir;
3034
+ }
3035
+ program.addHelpText("after", [
3036
+ "",
3037
+ "Use --help --verbose to show all options (including built-in apcore options).",
3038
+ "Use --help --man to display a formatted man page."
3039
+ ].join("\n"));
3040
+ registerInitCommand(program);
3041
+ configureManHelp(program, resolvedProgName, VERSION);
3042
+ if (extraCommands && extraCommands.length > 0) {
3043
+ const existingNames = /* @__PURE__ */ new Set([
3044
+ ...BUILTIN_COMMANDS,
3045
+ ...program.commands.map((c) => c.name())
3046
+ ]);
3047
+ for (const cmd of extraCommands) {
3048
+ const cmdName = cmd.name();
3049
+ if (existingNames.has(cmdName)) {
3050
+ process.stderr.write(
3051
+ `Warning: Extra command '${cmdName}' collides with a built-in command and will be skipped.
3052
+ `
3053
+ );
3054
+ continue;
2124
3055
  }
3056
+ program.addCommand(cmd);
3057
+ existingNames.add(cmdName);
2125
3058
  }
2126
3059
  }
2127
- sections.push(".SH ENVIRONMENT");
2128
- sections.push(".TP");
2129
- sections.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
2130
- sections.push(
2131
- "Path to the apcore extensions directory. Overrides the default \\fI./extensions\\fR."
2132
- );
2133
- sections.push(".TP");
2134
- sections.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
2135
- sections.push(
2136
- "Set to \\fB1\\fR to bypass approval prompts for modules that require human-in-the-loop confirmation."
2137
- );
2138
- sections.push(".TP");
2139
- sections.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
2140
- sections.push(
2141
- "CLI-specific logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Takes priority over \\fBAPCORE_LOGGING_LEVEL\\fR. Default: WARNING."
2142
- );
2143
- sections.push(".TP");
2144
- sections.push("\\fBAPCORE_LOGGING_LEVEL\\fR");
2145
- sections.push(
2146
- "Global apcore logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Used as fallback when \\fBAPCORE_CLI_LOGGING_LEVEL\\fR is not set. Default: WARNING."
2147
- );
2148
- sections.push(".TP");
2149
- sections.push("\\fBAPCORE_AUTH_API_KEY\\fR");
2150
- sections.push(
2151
- "API key for authenticating with the apcore registry."
2152
- );
2153
- sections.push(".SH EXIT CODES");
2154
- const exitCodes = [
2155
- ["0", "Success."],
2156
- ["1", "Module execution error."],
2157
- ["2", "Invalid CLI input or missing argument."],
2158
- ["44", "Module not found, disabled, or failed to load."],
2159
- ["45", "Input failed JSON Schema validation."],
2160
- [
2161
- "46",
2162
- "Approval denied, timed out, or no interactive terminal available."
2163
- ],
2164
- [
2165
- "47",
2166
- "Configuration error (extensions directory not found or unreadable)."
2167
- ],
2168
- ["48", "Schema contains a circular \\fB$ref\\fR."],
2169
- ["77", "ACL denied \u2014 insufficient permissions for this module."],
2170
- ["130", "Execution cancelled by user (SIGINT / Ctrl\\-C)."]
2171
- ];
2172
- for (const [code, meaning] of exitCodes) {
2173
- sections.push(`.TP
2174
- \\fB${code}\\fR
2175
- ${meaning}`);
3060
+ program.hook("preAction", async (thisCommand) => {
3061
+ const opts = thisCommand.opts();
3062
+ const commandsDir = opts.commandsDir;
3063
+ const bindingPath = opts.binding;
3064
+ await applyToolkitIntegration(commandsDir, bindingPath);
3065
+ });
3066
+ return program;
3067
+ }
3068
+ async function applyToolkitIntegration(commandsDir, bindingPath) {
3069
+ if (!commandsDir && !bindingPath) {
3070
+ return;
3071
+ }
3072
+ try {
3073
+ const toolkitModule = "apcore-toolkit";
3074
+ const toolkit = await import(
3075
+ /* @vite-ignore */
3076
+ toolkitModule
3077
+ );
3078
+ if (commandsDir) {
3079
+ console.warn("Convention scanning not yet available in TypeScript toolkit");
3080
+ }
3081
+ if (bindingPath) {
3082
+ const resolver = new toolkit.DisplayResolver();
3083
+ void resolver;
3084
+ }
3085
+ } catch {
3086
+ console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
2176
3087
  }
2177
- sections.push(".SH SEE ALSO");
2178
- sections.push(
2179
- [
2180
- `\\fB${progName}\\fR(1)`,
2181
- `\\fB${progName}\\-list\\fR(1)`,
2182
- `\\fB${progName}\\-describe\\fR(1)`,
2183
- `\\fB${progName}\\-completion\\fR(1)`
2184
- ].join(", ")
2185
- );
2186
- return sections.join("\n");
2187
3088
  }
2188
- function roffEscape(s) {
2189
- return s.replace(/\\/g, "\\\\").replace(/-/g, "\\-").replace(/'/g, "\\(aq");
3089
+ function main(progName) {
3090
+ verboseHelp = hasVerboseFlag();
3091
+ const program = createCli(void 0, progName, verboseHelp);
3092
+ try {
3093
+ program.parse(process.argv);
3094
+ } catch (error2) {
3095
+ if (error2 instanceof CommanderError) {
3096
+ process.exit(error2.exitCode);
3097
+ }
3098
+ const code = exitCodeForError(error2);
3099
+ if (error2 instanceof Error) {
3100
+ process.stderr.write(`Error: ${error2.message}
3101
+ `);
3102
+ }
3103
+ process.exit(code);
3104
+ }
2190
3105
  }
2191
- function buildProgramManPage(program, progName, version, description, docsUrl2) {
2192
- const help = new Help();
2193
- const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
2194
- const s = [];
2195
- const resolvedDesc = description ?? program.description() ?? `${progName} CLI`;
2196
- s.push(`.TH "${progName.toUpperCase()}" "1" "${today}" "${progName} ${version}" "${progName} Manual"`);
2197
- s.push(".SH NAME");
2198
- s.push(`${progName} \\- ${roffEscape(resolvedDesc)}`);
2199
- s.push(".SH SYNOPSIS");
2200
- s.push(`\\fB${progName}\\fR [\\fIglobal\\-options\\fR] \\fIcommand\\fR [\\fIcommand\\-options\\fR]`);
2201
- if (resolvedDesc) {
2202
- s.push(".SH DESCRIPTION");
2203
- s.push(roffEscape(resolvedDesc));
3106
+ function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName, verbose = verboseHelp) {
3107
+ const moduleId = moduleDef.id;
3108
+ let resolvedSchema = {};
3109
+ let schemaOptions = [];
3110
+ const display = getDisplay(moduleDef);
3111
+ const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
3112
+ const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
3113
+ const cmdHelp = cliDisplay.description ?? moduleDef.description;
3114
+ const inputSchema = moduleDef.inputSchema;
3115
+ if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
3116
+ try {
3117
+ resolvedSchema = resolveRefs(inputSchema, 32, moduleId);
3118
+ } catch {
3119
+ resolvedSchema = inputSchema;
3120
+ }
3121
+ schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
2204
3122
  }
2205
- const globalOpts = help.visibleOptions(program).filter((o) => !["help", "version", "all", "man"].includes(o.long?.replace("--", "") ?? ""));
2206
- if (globalOpts.length > 0) {
2207
- s.push(".SH GLOBAL OPTIONS");
2208
- for (const opt of globalOpts) {
2209
- const flag = [opt.short, opt.long].filter(Boolean).join(", ");
2210
- s.push(".TP");
2211
- s.push(`\\fB${roffEscape(flag)}\\fR`);
2212
- if (opt.description) s.push(roffEscape(opt.description));
3123
+ const cmd = new Command6(effectiveCmdName).description(cmdHelp);
3124
+ const inputOpt = new Option4("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
3125
+ const yesOpt = new Option4("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
3126
+ const largeInputOpt = new Option4("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
3127
+ const formatOpt = new Option4("--format <format>", "Output format: json, table, csv, yaml, jsonl.").choices(["json", "table", "csv", "yaml", "jsonl"]);
3128
+ const fieldsOpt = new Option4("--fields <fields>", "Comma-separated dot-paths to select from the result (e.g., 'status,data.count').");
3129
+ const sandboxOpt = new Option4("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
3130
+ const dryRunOpt = new Option4("--dry-run", "Run preflight checks without executing the module. Shows validation results.").default(false);
3131
+ const traceOpt = new Option4("--trace", "Show execution pipeline trace with per-step timing after the result.").default(false);
3132
+ const streamOpt = new Option4("--stream", "Stream module output as JSONL (one JSON object per line, flushed immediately).").default(false);
3133
+ const strategyOpt = new Option4("--strategy <name>", "Execution pipeline strategy: standard (default), internal, testing, performance.").choices(["standard", "internal", "testing", "performance", "minimal"]);
3134
+ const approvalTimeoutOpt = new Option4("--approval-timeout <seconds>", "Override approval prompt timeout in seconds (default: 60).").argParser(parseInt);
3135
+ const approvalTokenOpt = new Option4("--approval-token <token>", "Resume a pending approval with the given token (for async approval flows).");
3136
+ if (!verbose) {
3137
+ inputOpt.hideHelp();
3138
+ yesOpt.hideHelp();
3139
+ largeInputOpt.hideHelp();
3140
+ formatOpt.hideHelp();
3141
+ fieldsOpt.hideHelp();
3142
+ dryRunOpt.hideHelp();
3143
+ traceOpt.hideHelp();
3144
+ streamOpt.hideHelp();
3145
+ strategyOpt.hideHelp();
3146
+ approvalTimeoutOpt.hideHelp();
3147
+ approvalTokenOpt.hideHelp();
3148
+ }
3149
+ cmd.addOption(inputOpt);
3150
+ cmd.addOption(yesOpt);
3151
+ cmd.addOption(largeInputOpt);
3152
+ cmd.addOption(formatOpt);
3153
+ cmd.addOption(fieldsOpt);
3154
+ cmd.addOption(sandboxOpt);
3155
+ cmd.addOption(dryRunOpt);
3156
+ cmd.addOption(traceOpt);
3157
+ cmd.addOption(streamOpt);
3158
+ cmd.addOption(strategyOpt);
3159
+ cmd.addOption(approvalTimeoutOpt);
3160
+ cmd.addOption(approvalTokenOpt);
3161
+ const footerParts = [];
3162
+ if (!verbose) {
3163
+ footerParts.push("Use --verbose to show all options (including built-in apcore options).");
3164
+ }
3165
+ if (docsUrl) {
3166
+ footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
3167
+ }
3168
+ if (footerParts.length > 0) {
3169
+ cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
3170
+ }
3171
+ const reservedNames = /* @__PURE__ */ new Set([
3172
+ "input",
3173
+ "yes",
3174
+ "largeInput",
3175
+ "format",
3176
+ "fields",
3177
+ "sandbox",
3178
+ "verbose",
3179
+ "dryRun",
3180
+ "trace",
3181
+ "stream",
3182
+ "strategy",
3183
+ "approvalTimeout",
3184
+ "approvalToken"
3185
+ ]);
3186
+ for (const opt of schemaOptions) {
3187
+ if (reservedNames.has(opt.name)) {
3188
+ process.stderr.write(
3189
+ `Error: Module '${moduleId}' schema property '${opt.name}' conflicts with a reserved CLI option name. Rename the property.
3190
+ `
3191
+ );
3192
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2213
3193
  }
2214
3194
  }
2215
- const allCommands = help.visibleCommands(program);
2216
- if (allCommands.length > 0) {
2217
- s.push(".SH COMMANDS");
2218
- for (const cmd of allCommands) {
2219
- if (cmd.name() === "help") continue;
2220
- const desc = help.subcommandDescription(cmd);
2221
- s.push(".TP");
2222
- s.push(`\\fB${progName} ${roffEscape(cmd.name())}\\fR`);
2223
- if (desc) s.push(roffEscape(desc));
2224
- const cmdHelp = new Help();
2225
- const opts = cmdHelp.visibleOptions(cmd).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
2226
- for (const opt of opts) {
2227
- const flag = [opt.short, opt.long].filter(Boolean).join(", ");
2228
- s.push(".RS");
2229
- s.push(".TP");
2230
- s.push(`\\fB${roffEscape(flag)}\\fR`);
2231
- if (opt.description) s.push(roffEscape(opt.description));
2232
- s.push(".RE");
3195
+ for (const opt of schemaOptions) {
3196
+ if (opt.parseArg) {
3197
+ cmd.option(opt.flags, opt.description, opt.parseArg, opt.defaultValue);
3198
+ } else {
3199
+ cmd.option(opt.flags, opt.description, opt.defaultValue);
3200
+ }
3201
+ }
3202
+ cmd.action(async (options) => {
3203
+ const stdinFlag = options.input;
3204
+ const autoApprove = options.yes;
3205
+ const largeInput = options.largeInput;
3206
+ const outputFormat = options.format;
3207
+ const outputFields = options.fields;
3208
+ const sandboxEnabled = options.sandbox;
3209
+ const dryRun = options.dryRun;
3210
+ const traceFlag = options.trace;
3211
+ const streamFlag = options.stream;
3212
+ const strategyName = options.strategy;
3213
+ const approvalTimeout = options.approvalTimeout ?? 60;
3214
+ const approvalToken = options.approvalToken;
3215
+ const schemaKwargs = {};
3216
+ const builtinKeys = /* @__PURE__ */ new Set([
3217
+ "input",
3218
+ "yes",
3219
+ "largeInput",
3220
+ "format",
3221
+ "fields",
3222
+ "sandbox",
3223
+ "verbose",
3224
+ "dryRun",
3225
+ "trace",
3226
+ "stream",
3227
+ "strategy",
3228
+ "approvalTimeout",
3229
+ "approvalToken"
3230
+ ]);
3231
+ for (const [k, v] of Object.entries(options)) {
3232
+ if (!builtinKeys.has(k)) {
3233
+ schemaKwargs[k] = v;
2233
3234
  }
2234
- const subCmds = cmdHelp.visibleCommands(cmd).filter((c) => c.name() !== "help");
2235
- for (const sub of subCmds) {
2236
- const subDesc = help.subcommandDescription(sub);
2237
- s.push(".TP");
2238
- s.push(`\\fB${progName} ${roffEscape(cmd.name())} ${roffEscape(sub.name())}\\fR`);
2239
- if (subDesc) s.push(roffEscape(subDesc));
2240
- const subOpts = cmdHelp.visibleOptions(sub).filter((o) => !["help", "version"].includes(o.long?.replace("--", "") ?? ""));
2241
- for (const opt of subOpts) {
2242
- const flag = [opt.short, opt.long].filter(Boolean).join(", ");
2243
- s.push(".RS");
2244
- s.push(".TP");
2245
- s.push(`\\fB${roffEscape(flag)}\\fR`);
2246
- if (opt.description) s.push(roffEscape(opt.description));
2247
- s.push(".RE");
3235
+ }
3236
+ let merged = {};
3237
+ try {
3238
+ merged = await collectInput(stdinFlag, schemaKwargs, largeInput);
3239
+ const reconverted = reconvertEnumValues(merged, schemaOptions);
3240
+ merged = reconverted;
3241
+ if (dryRun) {
3242
+ if (!executor.validate) {
3243
+ process.stderr.write("Error: Executor does not support validate.\n");
3244
+ process.exit(1);
3245
+ }
3246
+ const preflight = await executor.validate(moduleId, merged);
3247
+ formatPreflightResult(preflight, outputFormat);
3248
+ if (traceFlag) {
3249
+ const pureSteps = /* @__PURE__ */ new Set([
3250
+ "context_creation",
3251
+ "call_chain_guard",
3252
+ "module_lookup",
3253
+ "acl_check",
3254
+ "input_validation"
3255
+ ]);
3256
+ const allSteps = [
3257
+ "context_creation",
3258
+ "call_chain_guard",
3259
+ "module_lookup",
3260
+ "acl_check",
3261
+ "approval_gate",
3262
+ "middleware_before",
3263
+ "input_validation",
3264
+ "execute",
3265
+ "output_validation",
3266
+ "middleware_after",
3267
+ "return_result"
3268
+ ];
3269
+ process.stderr.write("\nPipeline preview (dry-run):\n");
3270
+ for (const s of allSteps) {
3271
+ if (pureSteps.has(s)) {
3272
+ process.stderr.write(` \u2713 ${s.padEnd(24)} (pure \u2014 would execute)
3273
+ `);
3274
+ } else {
3275
+ process.stderr.write(` \u25CB ${s.padEnd(24)} (impure \u2014 skipped in dry-run)
3276
+ `);
3277
+ }
3278
+ }
3279
+ }
3280
+ process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
3281
+ }
3282
+ if (approvalToken) {
3283
+ merged._approval_token = approvalToken;
3284
+ }
3285
+ await checkApproval(moduleDef, autoApprove, approvalTimeout);
3286
+ const startTime = performance.now();
3287
+ if (streamFlag) {
3288
+ if (resolveFormat(outputFormat) === "table") {
3289
+ process.stderr.write("Warning: Streaming mode always outputs JSONL; --format table is ignored.\n");
3290
+ }
3291
+ const annotations = moduleDef.annotations;
3292
+ const isStreaming = annotations?.streaming === true;
3293
+ if (!isStreaming) {
3294
+ process.stderr.write(
3295
+ `Warning: Module '${moduleId}' does not declare streaming support. Falling back to standard execution.
3296
+ `
3297
+ );
3298
+ }
3299
+ if (isStreaming && executor.stream) {
3300
+ let chunks = 0;
3301
+ for await (const chunk of executor.stream(moduleId, merged)) {
3302
+ chunks++;
3303
+ process.stdout.write(JSON.stringify(chunk) + "\n");
3304
+ if (process.stderr.isTTY) {
3305
+ process.stderr.write(`\rStreaming ${moduleId}... (${chunks} chunks)`);
3306
+ }
3307
+ }
3308
+ if (process.stderr.isTTY) {
3309
+ process.stderr.write("\n");
3310
+ }
3311
+ const durationMs2 = Math.round(performance.now() - startTime);
3312
+ const { getAuditLogger: getAuditLogger3 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3313
+ const auditLogger2 = getAuditLogger3();
3314
+ if (auditLogger2) {
3315
+ auditLogger2.logExecution(moduleId, merged, "success", 0, durationMs2);
3316
+ }
3317
+ return;
3318
+ }
3319
+ }
3320
+ if (traceFlag && executor.callWithTrace) {
3321
+ const [result2, trace] = await executor.callWithTrace(
3322
+ moduleId,
3323
+ merged,
3324
+ strategyName ? { strategy: strategyName } : void 0
3325
+ );
3326
+ const durationMs2 = Math.round(performance.now() - startTime);
3327
+ const { getAuditLogger: getAuditLogger3 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3328
+ const auditLogger2 = getAuditLogger3();
3329
+ if (auditLogger2) {
3330
+ auditLogger2.logExecution(moduleId, merged, "success", 0, durationMs2);
3331
+ }
3332
+ const resolved = resolveFormat(outputFormat);
3333
+ if (resolved === "json" || !process.stdout.isTTY) {
3334
+ const traceData = {
3335
+ strategy: trace.strategy_name,
3336
+ total_duration_ms: trace.total_duration_ms,
3337
+ success: trace.success,
3338
+ steps: trace.steps.map((s) => ({
3339
+ name: s.name,
3340
+ duration_ms: s.duration_ms,
3341
+ skipped: s.skipped,
3342
+ ...s.skipped ? { skip_reason: s.skip_reason } : {}
3343
+ }))
3344
+ };
3345
+ let output;
3346
+ if (typeof result2 === "object" && result2 !== null && !Array.isArray(result2)) {
3347
+ output = { ...result2, _trace: traceData };
3348
+ } else {
3349
+ output = { result: result2, _trace: traceData };
3350
+ }
3351
+ process.stdout.write(JSON.stringify(output, null, 2) + "\n");
3352
+ } else {
3353
+ formatExecResult(result2, outputFormat, outputFields);
3354
+ const stepCount = trace.steps.length;
3355
+ process.stderr.write(
3356
+ `
3357
+ Pipeline Trace (strategy: ${trace.strategy_name}, ${stepCount} steps, ${trace.total_duration_ms.toFixed(1)}ms)
3358
+ `
3359
+ );
3360
+ for (const s of trace.steps) {
3361
+ if (s.skipped) {
3362
+ const reason = s.skip_reason ?? "n/a";
3363
+ process.stderr.write(` \u25CB ${s.name.padEnd(24)} ${"\u2014".padStart(8)} skipped (${reason})
3364
+ `);
3365
+ } else {
3366
+ process.stderr.write(` \u2713 ${s.name.padEnd(24)} ${(s.duration_ms.toFixed(1) + "ms").padStart(8)}
3367
+ `);
3368
+ }
3369
+ }
3370
+ }
3371
+ return;
3372
+ }
3373
+ let result;
3374
+ if (strategyName && executor.callWithTrace) {
3375
+ const [res] = await executor.callWithTrace(
3376
+ moduleId,
3377
+ merged,
3378
+ { strategy: strategyName }
3379
+ );
3380
+ result = res;
3381
+ if (strategyName !== "standard" && process.stderr.isTTY) {
3382
+ process.stderr.write(`Warning: Using '${strategyName}' strategy.
3383
+ `);
3384
+ }
3385
+ } else {
3386
+ const { Sandbox: Sandbox2 } = await Promise.resolve().then(() => (init_security(), security_exports));
3387
+ const sandbox = new Sandbox2(sandboxEnabled);
3388
+ result = await sandbox.execute(moduleId, merged, executor);
3389
+ }
3390
+ const durationMs = Math.round(performance.now() - startTime);
3391
+ const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3392
+ const auditLogger = getAuditLogger2();
3393
+ if (auditLogger) {
3394
+ auditLogger.logExecution(moduleId, merged, "success", 0, durationMs);
3395
+ }
3396
+ formatExecResult(result, outputFormat, outputFields);
3397
+ } catch (err) {
3398
+ const errRecord = err;
3399
+ const errorCode = typeof errRecord?.code === "string" ? errRecord.code : void 0;
3400
+ const exitCode = errorCode && errorCode in ERROR_CODE_MAP ? ERROR_CODE_MAP[errorCode] : exitCodeForError(err);
3401
+ try {
3402
+ const { getAuditLogger: getAuditLogger2 } = await Promise.resolve().then(() => (init_audit(), audit_exports));
3403
+ const auditLogger = getAuditLogger2();
3404
+ if (auditLogger) {
3405
+ auditLogger.logExecution(moduleId, merged, "error", exitCode, 0);
2248
3406
  }
3407
+ } catch {
2249
3408
  }
3409
+ if (outputFormat === "json" || !process.stderr.isTTY) {
3410
+ emitErrorJson(err, exitCode);
3411
+ } else {
3412
+ emitErrorTty(err, exitCode);
3413
+ }
3414
+ process.exit(exitCode);
2250
3415
  }
3416
+ });
3417
+ return cmd;
3418
+ }
3419
+ function validateModuleId(moduleId) {
3420
+ if (moduleId.length > 128) {
3421
+ process.stderr.write(
3422
+ `Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
3423
+ `
3424
+ );
3425
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2251
3426
  }
2252
- s.push(".SH ENVIRONMENT");
2253
- s.push(".TP");
2254
- s.push("\\fBAPCORE_EXTENSIONS_ROOT\\fR");
2255
- s.push("Path to the apcore extensions directory.");
2256
- s.push(".TP");
2257
- s.push("\\fBAPCORE_CLI_AUTO_APPROVE\\fR");
2258
- s.push("Set to \\fB1\\fR to bypass approval prompts.");
2259
- s.push(".TP");
2260
- s.push("\\fBAPCORE_CLI_LOGGING_LEVEL\\fR");
2261
- s.push("CLI\\-specific logging verbosity (DEBUG|INFO|WARNING|ERROR).");
2262
- s.push(".SH EXIT CODES");
2263
- const exitCodes = [
2264
- ["0", "Success."],
2265
- ["1", "Module execution error."],
2266
- ["2", "Invalid CLI input or missing argument."],
2267
- ["44", "Module not found, disabled, or failed to load."],
2268
- ["45", "Input failed JSON Schema validation."],
2269
- ["46", "Approval denied or timed out."],
2270
- ["47", "Configuration error."],
2271
- ["77", "ACL denied."],
2272
- ["130", "Cancelled by user (SIGINT)."]
2273
- ];
2274
- for (const [code, meaning] of exitCodes) {
2275
- s.push(`.TP
2276
- \\fB${code}\\fR
2277
- ${meaning}`);
2278
- }
2279
- s.push(".SH SEE ALSO");
2280
- s.push(`\\fB${progName} \\-\\-help \\-\\-verbose\\fR for full option list.`);
2281
- if (docsUrl2) {
2282
- s.push(`.PP
2283
- Full documentation at \\fI${roffEscape(docsUrl2)}\\fR`);
3427
+ if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
3428
+ process.stderr.write(
3429
+ `Error: Invalid module ID format: '${moduleId}'.
3430
+ `
3431
+ );
3432
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2284
3433
  }
2285
- return s.join("\n");
2286
3434
  }
2287
- function configureManHelp(program, progName, version, description, docsUrl2) {
2288
- const manOpt = new Option2("--man", "Output man page in roff format (use with --help)").hideHelp();
2289
- program.addOption(manOpt);
2290
- program.addHelpText("beforeAll", () => {
2291
- if (program.opts().man) {
2292
- process.stdout.write(buildProgramManPage(program, progName, version, description, docsUrl2));
2293
- process.stdout.write("\n");
2294
- process.exit(0);
3435
+ async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
3436
+ const cliKwargsNonNull = {};
3437
+ for (const [k, v] of Object.entries(cliKwargs)) {
3438
+ if (v !== null && v !== void 0) {
3439
+ cliKwargsNonNull[k] = v;
2295
3440
  }
2296
- return "";
2297
- });
2298
- }
2299
- function registerShellCommands(cli, progName = "apcore-cli") {
2300
- const completionCmd = new Command4("completion").description(
2301
- "Generate a shell completion script and print it to stdout."
2302
- ).argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
2303
- const validShells = ["bash", "zsh", "fish"];
2304
- if (!validShells.includes(shell)) {
3441
+ }
3442
+ if (!stdinFlag) {
3443
+ return cliKwargsNonNull;
3444
+ }
3445
+ if (stdinFlag === "-") {
3446
+ const raw = await readStdin();
3447
+ const rawSize = Buffer.byteLength(raw, "utf-8");
3448
+ if (rawSize > 10485760 && !largeInput) {
2305
3449
  process.stderr.write(
2306
- `Error: Unknown shell '${shell}'. Expected: bash, zsh, or fish.
2307
- `
3450
+ "Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
2308
3451
  );
2309
3452
  process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2310
3453
  }
2311
- const resolved = cli.name() || progName;
2312
- const generators = {
2313
- bash: () => generateBashCompletion(resolved),
2314
- zsh: () => generateZshCompletion(resolved),
2315
- fish: () => generateFishCompletion(resolved)
2316
- };
2317
- process.stdout.write(generators[shell]());
2318
- });
2319
- cli.addCommand(completionCmd);
2320
- const manCmd = new Command4("man").description("Generate a roff man page for COMMAND and print it to stdout.").argument("<command>", "Command to generate man page for").action((commandName) => {
2321
- const knownBuiltins = /* @__PURE__ */ new Set(["completion", "describe", "exec", "init", "list", "man"]);
2322
- const cmd = cli.commands.find((c) => c.name() === commandName) ?? null;
2323
- if (!cmd && !knownBuiltins.has(commandName)) {
3454
+ if (!raw) {
3455
+ return cliKwargsNonNull;
3456
+ }
3457
+ let stdinData;
3458
+ try {
3459
+ stdinData = JSON.parse(raw);
3460
+ } catch {
2324
3461
  process.stderr.write(
2325
- `Error: Unknown command '${commandName}'.
3462
+ "Error: STDIN does not contain valid JSON.\n"
3463
+ );
3464
+ process.exit(EXIT_CODES.INVALID_CLI_INPUT);
3465
+ }
3466
+ if (typeof stdinData !== "object" || stdinData === null || Array.isArray(stdinData)) {
3467
+ process.stderr.write(
3468
+ `Error: STDIN JSON must be an object, got ${Array.isArray(stdinData) ? "array" : typeof stdinData}.
2326
3469
  `
2327
3470
  );
2328
3471
  process.exit(EXIT_CODES.INVALID_CLI_INPUT);
2329
3472
  }
2330
- const resolved = cli.name() || progName;
2331
- const roff = generateManPage(commandName, cmd, resolved);
2332
- process.stdout.write(roff);
3473
+ return { ...stdinData, ...cliKwargsNonNull };
3474
+ }
3475
+ return cliKwargsNonNull;
3476
+ }
3477
+ function readStdin() {
3478
+ return new Promise((resolve3, reject) => {
3479
+ const chunks = [];
3480
+ const onData = (chunk) => chunks.push(chunk);
3481
+ const onEnd = () => {
3482
+ cleanup();
3483
+ resolve3(Buffer.concat(chunks).toString("utf-8"));
3484
+ };
3485
+ const onError = (err) => {
3486
+ cleanup();
3487
+ reject(err);
3488
+ };
3489
+ const cleanup = () => {
3490
+ process.stdin.removeListener("data", onData);
3491
+ process.stdin.removeListener("end", onEnd);
3492
+ process.stdin.removeListener("error", onError);
3493
+ };
3494
+ process.stdin.on("data", onData);
3495
+ process.stdin.on("end", onEnd);
3496
+ process.stdin.on("error", onError);
3497
+ process.stdin.resume();
2333
3498
  });
2334
- cli.addCommand(manCmd);
3499
+ }
3500
+ function reconvertEnumValues(kwargs, options) {
3501
+ const result = { ...kwargs };
3502
+ for (const opt of options) {
3503
+ if (!opt.enumOriginalTypes) continue;
3504
+ const paramName = opt.name;
3505
+ if (!(paramName in result) || result[paramName] === null || result[paramName] === void 0) {
3506
+ continue;
3507
+ }
3508
+ const strVal = String(result[paramName]);
3509
+ const origType = opt.enumOriginalTypes[strVal];
3510
+ if (origType === "int") {
3511
+ result[paramName] = parseInt(strVal, 10);
3512
+ } else if (origType === "float") {
3513
+ result[paramName] = parseFloat(strVal);
3514
+ } else if (origType === "bool") {
3515
+ result[paramName] = strVal.toLowerCase() === "true";
3516
+ }
3517
+ }
3518
+ return result;
2335
3519
  }
2336
3520
 
2337
3521
  // src/index.ts
@@ -2344,6 +3528,7 @@ export {
2344
3528
  AuthProvider,
2345
3529
  AuthenticationError,
2346
3530
  BUILTIN_COMMANDS,
3531
+ CliApprovalHandler,
2347
3532
  ConfigDecryptionError,
2348
3533
  ConfigEncryptor,
2349
3534
  ConfigResolver,
@@ -2365,12 +3550,16 @@ export {
2365
3550
  createCli,
2366
3551
  debug,
2367
3552
  docsUrl,
3553
+ emitErrorJson,
3554
+ emitErrorTty,
2368
3555
  error,
2369
3556
  exitCodeForError,
2370
3557
  extractHelp,
3558
+ firstFailedExitCode,
2371
3559
  formatExecResult,
2372
3560
  formatModuleDetail,
2373
3561
  formatModuleList,
3562
+ formatPreflightResult,
2374
3563
  getAuditLogger,
2375
3564
  getCliDisplayFields,
2376
3565
  getDisplay,
@@ -2379,9 +3568,13 @@ export {
2379
3568
  main,
2380
3569
  mapType,
2381
3570
  reconvertEnumValues,
3571
+ registerConfigNamespace,
2382
3572
  registerDiscoveryCommands,
2383
3573
  registerInitCommand,
3574
+ registerPipelineCommand,
2384
3575
  registerShellCommands,
3576
+ registerSystemCommands,
3577
+ registerValidateCommand,
2385
3578
  resolveFormat,
2386
3579
  resolveRefs,
2387
3580
  schemaToCliOptions,