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/CHANGELOG.md +39 -0
- package/README.md +20 -1
- package/dist/bin/apcore-cli.js +1043 -12
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/index.d.ts +155 -22
- package/dist/index.js +2234 -1041
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
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
|
|
162
|
+
import * as fs3 from "fs";
|
|
141
163
|
import * as os from "os";
|
|
142
|
-
import * as
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
406
|
+
import * as fs4 from "fs";
|
|
385
407
|
import * as os3 from "os";
|
|
386
|
-
import * as
|
|
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 =
|
|
420
|
-
|
|
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
|
-
|
|
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
|
|
496
|
-
import * as
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
911
|
-
|
|
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
|
-
|
|
917
|
-
|
|
918
|
-
|
|
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
|
|
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 === "
|
|
1013
|
-
|
|
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
|
|
1018
|
-
process.stdout.write(JSON.stringify(
|
|
1019
|
-
} else if (typeof
|
|
1020
|
-
process.stdout.write(
|
|
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
|
-
|
|
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/
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
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
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
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
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
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
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1508
|
+
if (envVar) {
|
|
1509
|
+
const envValue = process.env[envVar];
|
|
1510
|
+
if (envValue !== void 0 && envValue !== "") {
|
|
1511
|
+
return envValue;
|
|
1512
|
+
}
|
|
1291
1513
|
}
|
|
1292
|
-
|
|
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
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1424
|
-
}
|
|
1425
|
-
if (!raw) {
|
|
1426
|
-
return cliKwargsNonNull;
|
|
1554
|
+
return null;
|
|
1427
1555
|
}
|
|
1428
|
-
let
|
|
1556
|
+
let parsed;
|
|
1429
1557
|
try {
|
|
1430
|
-
|
|
1558
|
+
parsed = yaml.load(content);
|
|
1431
1559
|
} catch {
|
|
1432
|
-
|
|
1433
|
-
|
|
1560
|
+
console.warn(
|
|
1561
|
+
`Configuration file '${this.configPath}' is malformed, using defaults.`
|
|
1434
1562
|
);
|
|
1435
|
-
|
|
1563
|
+
return null;
|
|
1436
1564
|
}
|
|
1437
|
-
if (typeof
|
|
1438
|
-
|
|
1439
|
-
`
|
|
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
|
-
|
|
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
|
-
*
|
|
1574
|
+
* Flatten nested dict to dot-notation keys.
|
|
1513
1575
|
*/
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
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
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
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
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
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.
|
|
2686
|
+
this.aliasMapBuilt = true;
|
|
1684
2687
|
} catch {
|
|
1685
|
-
warn("Failed to build
|
|
2688
|
+
warn("Failed to build alias map from registry");
|
|
1686
2689
|
}
|
|
1687
2690
|
}
|
|
1688
2691
|
/**
|
|
1689
|
-
* List all available command names
|
|
2692
|
+
* List all available command names from the Registry.
|
|
1690
2693
|
*/
|
|
1691
2694
|
listCommands() {
|
|
1692
|
-
this.
|
|
1693
|
-
const
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
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
|
|
2705
|
+
* Get or lazily build a Commander Command for the given module.
|
|
1701
2706
|
*/
|
|
1702
2707
|
getCommand(cmdName) {
|
|
1703
|
-
this.
|
|
1704
|
-
|
|
1705
|
-
return this.groupCache.get(cmdName).command;
|
|
2708
|
+
if (this.commandCache.has(cmdName)) {
|
|
2709
|
+
return this.commandCache.get(cmdName);
|
|
1706
2710
|
}
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
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 (
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
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.
|
|
1725
|
-
this.
|
|
2739
|
+
this._executor,
|
|
2740
|
+
this._helpTextMaxLength,
|
|
1726
2741
|
cmdName
|
|
1727
2742
|
);
|
|
1728
|
-
this.
|
|
1729
|
-
|
|
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
|
-
|
|
1738
|
-
|
|
1739
|
-
return this.topLevelModules;
|
|
2747
|
+
listCommands() {
|
|
2748
|
+
return [...this.members.keys()].sort();
|
|
1740
2749
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
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
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
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
|
|
1786
|
-
|
|
1787
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1800
|
-
|
|
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
|
|
2807
|
+
return [null, cliName];
|
|
1803
2808
|
}
|
|
1804
2809
|
/**
|
|
1805
|
-
*
|
|
2810
|
+
* Build the group map from registry modules.
|
|
1806
2811
|
*/
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
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
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
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
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
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
|
-
|
|
2859
|
+
const topNames = [...this.topLevelModules.keys()];
|
|
2860
|
+
return [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...groupNames, ...topNames])].sort();
|
|
1869
2861
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
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
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
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
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
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
|
-
|
|
2891
|
+
this.commandCache.set(cmdName, cmd);
|
|
2892
|
+
return cmd;
|
|
1904
2893
|
}
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
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
|
-
|
|
2060
|
-
|
|
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
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
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
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
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
|
-
|
|
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
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
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
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
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
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
);
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
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
|
|
2189
|
-
|
|
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
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
const
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
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
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
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
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
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
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
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
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
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
|
|
2288
|
-
const
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
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
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
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
|
-
|
|
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,
|