apcore-cli 0.5.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 +821 -6
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/index.d.ts +144 -22
- package/dist/index.js +1408 -313
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -142,6 +142,7 @@ var init_errors = __esm({
|
|
|
142
142
|
CONFIG_NAMESPACE_RESERVED: 78,
|
|
143
143
|
CONFIG_NAMESPACE_DUPLICATE: 78,
|
|
144
144
|
CONFIG_ENV_PREFIX_CONFLICT: 78,
|
|
145
|
+
CONFIG_ENV_MAP_CONFLICT: 78,
|
|
145
146
|
CONFIG_MOUNT_ERROR: 66,
|
|
146
147
|
CONFIG_BIND_ERROR: 65,
|
|
147
148
|
ERROR_FORMATTER_DUPLICATE: 70,
|
|
@@ -515,7 +516,7 @@ init_errors();
|
|
|
515
516
|
import { readFileSync as readFileSync3 } from "fs";
|
|
516
517
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
517
518
|
import * as path6 from "path";
|
|
518
|
-
import { Command as
|
|
519
|
+
import { Command as Command6, CommanderError, Option as Option4 } from "commander";
|
|
519
520
|
|
|
520
521
|
// src/ref-resolver.ts
|
|
521
522
|
init_esm_shims();
|
|
@@ -733,7 +734,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
|
|
|
733
734
|
flags: `--${flagBase}, --no-${flagBase}`,
|
|
734
735
|
description: helpText,
|
|
735
736
|
defaultValue: defaultVal,
|
|
736
|
-
required:
|
|
737
|
+
required: isRequired,
|
|
737
738
|
isBooleanFlag: true
|
|
738
739
|
});
|
|
739
740
|
} else if ("enum" in propSchema && Array.isArray(propSchema.enum)) {
|
|
@@ -744,7 +745,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
|
|
|
744
745
|
flags: `${flagName} <value>`,
|
|
745
746
|
description: helpText,
|
|
746
747
|
defaultValue,
|
|
747
|
-
required:
|
|
748
|
+
required: isRequired
|
|
748
749
|
});
|
|
749
750
|
} else {
|
|
750
751
|
const stringValues = enumValues.map(String);
|
|
@@ -763,7 +764,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
|
|
|
763
764
|
flags: `${flagName} <value>`,
|
|
764
765
|
description: helpText,
|
|
765
766
|
defaultValue: defaultValue !== void 0 ? String(defaultValue) : void 0,
|
|
766
|
-
required:
|
|
767
|
+
required: isRequired,
|
|
767
768
|
choices: stringValues,
|
|
768
769
|
enumOriginalTypes: Object.keys(enumOriginalTypes).length > 0 ? enumOriginalTypes : void 0
|
|
769
770
|
});
|
|
@@ -788,7 +789,7 @@ function schemaToCliOptions(schema, maxHelpLength = 1e3) {
|
|
|
788
789
|
flags: `${flagName} <value>`,
|
|
789
790
|
description: helpText,
|
|
790
791
|
defaultValue,
|
|
791
|
-
required:
|
|
792
|
+
required: isRequired,
|
|
792
793
|
parseArg
|
|
793
794
|
});
|
|
794
795
|
}
|
|
@@ -805,7 +806,41 @@ function getAnnotation(annotations, key, defaultValue = void 0) {
|
|
|
805
806
|
const ann = annotations;
|
|
806
807
|
return key in ann ? ann[key] : defaultValue;
|
|
807
808
|
}
|
|
808
|
-
|
|
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) {
|
|
809
844
|
const annotations = moduleDef.annotations;
|
|
810
845
|
let requiresApproval;
|
|
811
846
|
if (moduleDef.requiresApproval !== void 0) {
|
|
@@ -839,7 +874,7 @@ async function checkApproval(moduleDef, autoApprove) {
|
|
|
839
874
|
);
|
|
840
875
|
process.exit(EXIT_CODES.APPROVAL_DENIED);
|
|
841
876
|
}
|
|
842
|
-
await promptWithTimeout(moduleDef,
|
|
877
|
+
await promptWithTimeout(moduleDef, timeout);
|
|
843
878
|
}
|
|
844
879
|
async function promptWithTimeout(moduleDef, timeout) {
|
|
845
880
|
timeout = Math.max(1, Math.min(timeout, 3600));
|
|
@@ -912,7 +947,7 @@ function formatTable(headers, rows) {
|
|
|
912
947
|
);
|
|
913
948
|
return [headerLine, sep2, ...dataLines].join("\n") + "\n";
|
|
914
949
|
}
|
|
915
|
-
function formatModuleList(modules, format, filterTags) {
|
|
950
|
+
function formatModuleList(modules, format, filterTags, showDeps = false) {
|
|
916
951
|
if (format === "table") {
|
|
917
952
|
if (modules.length === 0 && filterTags && filterTags.length > 0) {
|
|
918
953
|
process.stdout.write(
|
|
@@ -925,19 +960,29 @@ function formatModuleList(modules, format, filterTags) {
|
|
|
925
960
|
process.stdout.write("No modules found.\n");
|
|
926
961
|
return;
|
|
927
962
|
}
|
|
928
|
-
const headers = ["ID", "Description", "Tags"];
|
|
929
|
-
const rows = modules.map((m) =>
|
|
930
|
-
m.id,
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
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
|
+
});
|
|
934
972
|
process.stdout.write(formatTable(headers, rows));
|
|
935
973
|
} else if (format === "json") {
|
|
936
|
-
const result = modules.map((m) =>
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
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
|
+
});
|
|
941
986
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
942
987
|
}
|
|
943
988
|
}
|
|
@@ -1025,24 +1070,171 @@ Tags: ${tags.join(", ")}
|
|
|
1025
1070
|
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1026
1071
|
}
|
|
1027
1072
|
}
|
|
1028
|
-
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) {
|
|
1029
1091
|
if (result === null || result === void 0) {
|
|
1030
1092
|
return;
|
|
1031
1093
|
}
|
|
1094
|
+
let effective_result = result;
|
|
1095
|
+
if (fields && typeof result === "object" && !Array.isArray(result) && result !== null) {
|
|
1096
|
+
effective_result = selectFields(result, fields);
|
|
1097
|
+
}
|
|
1032
1098
|
const effective = resolveFormat(format);
|
|
1033
|
-
if (effective === "
|
|
1034
|
-
|
|
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);
|
|
1035
1150
|
const headers = ["Key", "Value"];
|
|
1036
1151
|
const rows = entries.map(([k, v]) => [String(k), String(v)]);
|
|
1037
1152
|
process.stdout.write(formatTable(headers, rows));
|
|
1038
|
-
} else if (typeof
|
|
1039
|
-
process.stdout.write(JSON.stringify(
|
|
1040
|
-
} else if (typeof
|
|
1041
|
-
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");
|
|
1042
1185
|
} else {
|
|
1043
|
-
|
|
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
|
+
`);
|
|
1044
1219
|
}
|
|
1045
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
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
return 1;
|
|
1237
|
+
}
|
|
1046
1238
|
|
|
1047
1239
|
// src/logger.ts
|
|
1048
1240
|
init_esm_shims();
|
|
@@ -1254,7 +1446,14 @@ var DEFAULTS = {
|
|
|
1254
1446
|
"apcore-cli.stdin_buffer_limit": 10485760,
|
|
1255
1447
|
"apcore-cli.auto_approve": false,
|
|
1256
1448
|
"apcore-cli.help_text_max_length": 1e3,
|
|
1257
|
-
"apcore-cli.logging_level": "WARNING"
|
|
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
|
|
1258
1457
|
};
|
|
1259
1458
|
var NAMESPACE_TO_LEGACY = {
|
|
1260
1459
|
"apcore-cli.stdin_buffer_limit": "cli.stdin_buffer_limit",
|
|
@@ -1276,7 +1475,10 @@ function registerConfigNamespace() {
|
|
|
1276
1475
|
stdin_buffer_limit: 10485760,
|
|
1277
1476
|
auto_approve: false,
|
|
1278
1477
|
help_text_max_length: 1e3,
|
|
1279
|
-
logging_level: "WARNING"
|
|
1478
|
+
logging_level: "WARNING",
|
|
1479
|
+
approval_timeout: 60,
|
|
1480
|
+
strategy: "standard",
|
|
1481
|
+
group_depth: 1
|
|
1280
1482
|
}
|
|
1281
1483
|
});
|
|
1282
1484
|
}
|
|
@@ -1844,288 +2046,610 @@ function registerShellCommands(cli, progName = "apcore-cli") {
|
|
|
1844
2046
|
cli.addCommand(manCmd);
|
|
1845
2047
|
}
|
|
1846
2048
|
|
|
1847
|
-
// src/
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
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
|
+
}
|
|
1852
2062
|
}
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
docsUrl = url;
|
|
2063
|
+
function collectTag(value, previous) {
|
|
2064
|
+
return previous.concat([value]);
|
|
1856
2065
|
}
|
|
1857
|
-
function
|
|
1858
|
-
return
|
|
2066
|
+
function collectAnnotation(value, previous) {
|
|
2067
|
+
return previous.concat([value]);
|
|
1859
2068
|
}
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
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;
|
|
1865
2083
|
}
|
|
1866
|
-
function
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
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);
|
|
2092
|
+
}
|
|
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);
|
|
1887
2145
|
});
|
|
1888
|
-
|
|
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);
|
|
1889
2161
|
}
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
toolkitModule
|
|
1899
|
-
);
|
|
1900
|
-
if (commandsDir) {
|
|
1901
|
-
console.warn("Convention scanning not yet available in TypeScript toolkit");
|
|
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);
|
|
1902
2170
|
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
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);
|
|
1906
2175
|
}
|
|
1907
|
-
|
|
1908
|
-
|
|
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);
|
|
1909
2189
|
}
|
|
2190
|
+
return executor.execute(moduleId, inputs);
|
|
1910
2191
|
}
|
|
1911
|
-
function
|
|
1912
|
-
|
|
1913
|
-
const
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
if (error2 instanceof Error) {
|
|
1922
|
-
process.stderr.write(`Error: ${error2.message}
|
|
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
|
+
|
|
1923
2202
|
`);
|
|
1924
|
-
|
|
1925
|
-
|
|
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}`);
|
|
1926
2220
|
}
|
|
2221
|
+
process.stdout.write(`
|
|
2222
|
+
Summary: ${parts.join(", ") || "no data"}
|
|
2223
|
+
`);
|
|
1927
2224
|
}
|
|
1928
|
-
function
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
const
|
|
1934
|
-
const
|
|
1935
|
-
const
|
|
1936
|
-
const
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
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
|
+
`);
|
|
1942
2249
|
}
|
|
1943
|
-
schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
|
|
1944
|
-
}
|
|
1945
|
-
const cmd = new Command2(effectiveCmdName).description(cmdHelp);
|
|
1946
|
-
const inputOpt = new Option2("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
|
|
1947
|
-
const yesOpt = new Option2("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
|
|
1948
|
-
const largeInputOpt = new Option2("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
|
|
1949
|
-
const formatOpt = new Option2("--format <format>", "Set output format: 'json' for machine-readable, 'table' for human-readable");
|
|
1950
|
-
const sandboxOpt = new Option2("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
|
|
1951
|
-
if (!verbose) {
|
|
1952
|
-
inputOpt.hideHelp();
|
|
1953
|
-
yesOpt.hideHelp();
|
|
1954
|
-
largeInputOpt.hideHelp();
|
|
1955
|
-
formatOpt.hideHelp();
|
|
1956
|
-
}
|
|
1957
|
-
cmd.addOption(inputOpt);
|
|
1958
|
-
cmd.addOption(yesOpt);
|
|
1959
|
-
cmd.addOption(largeInputOpt);
|
|
1960
|
-
cmd.addOption(formatOpt);
|
|
1961
|
-
cmd.addOption(sandboxOpt);
|
|
1962
|
-
const footerParts = [];
|
|
1963
|
-
if (!verbose) {
|
|
1964
|
-
footerParts.push("Use --verbose to show all options (including built-in apcore options).");
|
|
1965
2250
|
}
|
|
1966
|
-
|
|
1967
|
-
|
|
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;
|
|
1968
2259
|
}
|
|
1969
|
-
|
|
1970
|
-
|
|
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
|
+
);
|
|
1971
2273
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
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", {});
|
|
1975
2284
|
} else {
|
|
1976
|
-
|
|
2285
|
+
await callSystemModule(executor, "system.health.summary", { include_healthy: true });
|
|
1977
2286
|
}
|
|
2287
|
+
} catch {
|
|
2288
|
+
debug("System modules not available; skipping system command registration.");
|
|
2289
|
+
return;
|
|
1978
2290
|
}
|
|
1979
|
-
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
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);
|
|
2293
|
+
try {
|
|
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);
|
|
2303
|
+
}
|
|
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");
|
|
2311
|
+
} else {
|
|
2312
|
+
formatHealthSummaryTty(result);
|
|
2313
|
+
}
|
|
1990
2314
|
}
|
|
2315
|
+
} catch (e) {
|
|
2316
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
2317
|
+
`);
|
|
2318
|
+
process.exit(1);
|
|
1991
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);
|
|
1992
2324
|
try {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
if (auditLogger) {
|
|
2004
|
-
auditLogger.logExecution(moduleId, reconverted, "success", 0, durationMs);
|
|
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
|
+
});
|
|
2005
2335
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
auditLogger.logExecution(moduleId, {}, "error", code, 0);
|
|
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);
|
|
2013
2342
|
}
|
|
2014
|
-
|
|
2015
|
-
|
|
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}
|
|
2016
2366
|
`);
|
|
2017
2367
|
}
|
|
2018
|
-
|
|
2368
|
+
} catch (e) {
|
|
2369
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
2370
|
+
`);
|
|
2371
|
+
process.exit(1);
|
|
2019
2372
|
}
|
|
2020
2373
|
});
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
process.stderr.write(
|
|
2026
|
-
`Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
|
|
2027
|
-
`
|
|
2028
|
-
);
|
|
2029
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2030
|
-
}
|
|
2031
|
-
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
|
|
2032
|
-
process.stderr.write(
|
|
2033
|
-
`Error: Invalid module ID format: '${moduleId}'.
|
|
2034
|
-
`
|
|
2035
|
-
);
|
|
2036
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2037
|
-
}
|
|
2038
|
-
}
|
|
2039
|
-
async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
|
|
2040
|
-
const cliKwargsNonNull = {};
|
|
2041
|
-
for (const [k, v] of Object.entries(cliKwargs)) {
|
|
2042
|
-
if (v !== null && v !== void 0) {
|
|
2043
|
-
cliKwargsNonNull[k] = v;
|
|
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");
|
|
2044
2378
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
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);
|
|
2057
2397
|
}
|
|
2058
|
-
|
|
2059
|
-
|
|
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");
|
|
2060
2403
|
}
|
|
2061
|
-
|
|
2404
|
+
const fmt = resolveFormat(opts.format);
|
|
2062
2405
|
try {
|
|
2063
|
-
|
|
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);
|
|
2064
2454
|
} catch {
|
|
2065
|
-
|
|
2066
|
-
"Error: STDIN does not contain valid JSON.\n"
|
|
2067
|
-
);
|
|
2068
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2455
|
+
parsedValue = value;
|
|
2069
2456
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
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);
|
|
2076
2479
|
}
|
|
2077
|
-
return { ...stdinData, ...cliKwargsNonNull };
|
|
2078
|
-
}
|
|
2079
|
-
return cliKwargsNonNull;
|
|
2080
|
-
}
|
|
2081
|
-
function readStdin() {
|
|
2082
|
-
return new Promise((resolve3, reject) => {
|
|
2083
|
-
const chunks = [];
|
|
2084
|
-
const onData = (chunk) => chunks.push(chunk);
|
|
2085
|
-
const onEnd = () => {
|
|
2086
|
-
cleanup();
|
|
2087
|
-
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
2088
|
-
};
|
|
2089
|
-
const onError = (err) => {
|
|
2090
|
-
cleanup();
|
|
2091
|
-
reject(err);
|
|
2092
|
-
};
|
|
2093
|
-
const cleanup = () => {
|
|
2094
|
-
process.stdin.removeListener("data", onData);
|
|
2095
|
-
process.stdin.removeListener("end", onEnd);
|
|
2096
|
-
process.stdin.removeListener("error", onError);
|
|
2097
|
-
};
|
|
2098
|
-
process.stdin.on("data", onData);
|
|
2099
|
-
process.stdin.on("end", onEnd);
|
|
2100
|
-
process.stdin.on("error", onError);
|
|
2101
|
-
process.stdin.resume();
|
|
2102
2480
|
});
|
|
2481
|
+
configGroup.addCommand(configSetCmd);
|
|
2482
|
+
cli.addCommand(configGroup);
|
|
2103
2483
|
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
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
|
+
}
|
|
2111
2555
|
}
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
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;
|
|
2120
2599
|
}
|
|
2121
|
-
|
|
2122
|
-
|
|
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);
|
|
2123
2632
|
}
|
|
2124
2633
|
|
|
2125
2634
|
// src/cli.ts
|
|
2126
2635
|
init_esm_shims();
|
|
2127
|
-
import { Command as
|
|
2128
|
-
var BUILTIN_COMMANDS = [
|
|
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
|
+
];
|
|
2129
2653
|
var LazyModuleGroup = class {
|
|
2130
2654
|
registry;
|
|
2131
2655
|
executor;
|
|
@@ -2208,7 +2732,7 @@ var LazyGroup = class {
|
|
|
2208
2732
|
this.members = members;
|
|
2209
2733
|
this._executor = executor;
|
|
2210
2734
|
this._helpTextMaxLength = helpTextMaxLength;
|
|
2211
|
-
this.command = new
|
|
2735
|
+
this.command = new Command5(name).description(`${name} commands`);
|
|
2212
2736
|
for (const [cmdName, [, descriptor]] of this.members) {
|
|
2213
2737
|
const cmd = buildModuleCommand(
|
|
2214
2738
|
descriptor,
|
|
@@ -2252,8 +2776,13 @@ var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
|
2252
2776
|
groupMapBuilt = false;
|
|
2253
2777
|
/**
|
|
2254
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").
|
|
2255
2784
|
*/
|
|
2256
|
-
static resolveGroup(moduleId, descriptor) {
|
|
2785
|
+
static resolveGroup(moduleId, descriptor, groupDepth = 1) {
|
|
2257
2786
|
if (!moduleId) {
|
|
2258
2787
|
warn("Empty module_id encountered in resolveGroup");
|
|
2259
2788
|
return [null, ""];
|
|
@@ -2269,9 +2798,10 @@ var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
|
2269
2798
|
}
|
|
2270
2799
|
const cliName = cliDisplay.alias ?? moduleId;
|
|
2271
2800
|
if (cliName.includes(".")) {
|
|
2272
|
-
const
|
|
2273
|
-
const
|
|
2274
|
-
const
|
|
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(".");
|
|
2275
2805
|
return [group, cmd];
|
|
2276
2806
|
}
|
|
2277
2807
|
return [null, cliName];
|
|
@@ -2377,58 +2907,615 @@ var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
|
2377
2907
|
}
|
|
2378
2908
|
};
|
|
2379
2909
|
|
|
2380
|
-
// src/
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
function validateTag(tag) {
|
|
2386
|
-
if (!TAG_PATTERN.test(tag)) {
|
|
2387
|
-
process.stderr.write(
|
|
2388
|
-
`Error: Invalid tag format: '${tag}'. Tags must match [a-z][a-z0-9_-]*.
|
|
2389
|
-
`
|
|
2390
|
-
);
|
|
2391
|
-
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
2392
|
-
}
|
|
2910
|
+
// src/main.ts
|
|
2911
|
+
var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
|
|
2912
|
+
var verboseHelp = false;
|
|
2913
|
+
function setVerboseHelp(verbose) {
|
|
2914
|
+
verboseHelp = verbose;
|
|
2393
2915
|
}
|
|
2394
|
-
|
|
2395
|
-
|
|
2916
|
+
var docsUrl = null;
|
|
2917
|
+
function setDocsUrl(url) {
|
|
2918
|
+
docsUrl = url;
|
|
2396
2919
|
}
|
|
2397
|
-
function
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
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
|
+
`);
|
|
2401
2981
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2982
|
+
}
|
|
2983
|
+
const suggestion = errRecord.suggestion;
|
|
2984
|
+
if (suggestion) {
|
|
2985
|
+
process.stderr.write(`
|
|
2986
|
+
Suggestion: ${suggestion}
|
|
2987
|
+
`);
|
|
2988
|
+
}
|
|
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
|
+
`);
|
|
2998
|
+
}
|
|
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;
|
|
3013
|
+
}
|
|
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);
|
|
2405
3030
|
}
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
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;
|
|
3055
|
+
}
|
|
3056
|
+
program.addCommand(cmd);
|
|
3057
|
+
existingNames.add(cmdName);
|
|
2413
3058
|
}
|
|
2414
|
-
|
|
2415
|
-
|
|
3059
|
+
}
|
|
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);
|
|
2416
3065
|
});
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
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");
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
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
|
+
}
|
|
3105
|
+
}
|
|
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);
|
|
3122
|
+
}
|
|
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)) {
|
|
2422
3188
|
process.stderr.write(
|
|
2423
|
-
`Error: Module '${moduleId}'
|
|
3189
|
+
`Error: Module '${moduleId}' schema property '${opt.name}' conflicts with a reserved CLI option name. Rename the property.
|
|
2424
3190
|
`
|
|
2425
3191
|
);
|
|
2426
|
-
process.exit(EXIT_CODES.
|
|
3192
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
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;
|
|
3234
|
+
}
|
|
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);
|
|
3406
|
+
}
|
|
3407
|
+
} catch {
|
|
3408
|
+
}
|
|
3409
|
+
if (outputFormat === "json" || !process.stderr.isTTY) {
|
|
3410
|
+
emitErrorJson(err, exitCode);
|
|
3411
|
+
} else {
|
|
3412
|
+
emitErrorTty(err, exitCode);
|
|
3413
|
+
}
|
|
3414
|
+
process.exit(exitCode);
|
|
2427
3415
|
}
|
|
2428
|
-
const fmt = resolveFormat(opts.format);
|
|
2429
|
-
formatModuleDetail(moduleDef, fmt);
|
|
2430
3416
|
});
|
|
2431
|
-
|
|
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);
|
|
3426
|
+
}
|
|
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);
|
|
3433
|
+
}
|
|
3434
|
+
}
|
|
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;
|
|
3440
|
+
}
|
|
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) {
|
|
3449
|
+
process.stderr.write(
|
|
3450
|
+
"Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
|
|
3451
|
+
);
|
|
3452
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3453
|
+
}
|
|
3454
|
+
if (!raw) {
|
|
3455
|
+
return cliKwargsNonNull;
|
|
3456
|
+
}
|
|
3457
|
+
let stdinData;
|
|
3458
|
+
try {
|
|
3459
|
+
stdinData = JSON.parse(raw);
|
|
3460
|
+
} catch {
|
|
3461
|
+
process.stderr.write(
|
|
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}.
|
|
3469
|
+
`
|
|
3470
|
+
);
|
|
3471
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
3472
|
+
}
|
|
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();
|
|
3498
|
+
});
|
|
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;
|
|
2432
3519
|
}
|
|
2433
3520
|
|
|
2434
3521
|
// src/index.ts
|
|
@@ -2441,6 +3528,7 @@ export {
|
|
|
2441
3528
|
AuthProvider,
|
|
2442
3529
|
AuthenticationError,
|
|
2443
3530
|
BUILTIN_COMMANDS,
|
|
3531
|
+
CliApprovalHandler,
|
|
2444
3532
|
ConfigDecryptionError,
|
|
2445
3533
|
ConfigEncryptor,
|
|
2446
3534
|
ConfigResolver,
|
|
@@ -2462,12 +3550,16 @@ export {
|
|
|
2462
3550
|
createCli,
|
|
2463
3551
|
debug,
|
|
2464
3552
|
docsUrl,
|
|
3553
|
+
emitErrorJson,
|
|
3554
|
+
emitErrorTty,
|
|
2465
3555
|
error,
|
|
2466
3556
|
exitCodeForError,
|
|
2467
3557
|
extractHelp,
|
|
3558
|
+
firstFailedExitCode,
|
|
2468
3559
|
formatExecResult,
|
|
2469
3560
|
formatModuleDetail,
|
|
2470
3561
|
formatModuleList,
|
|
3562
|
+
formatPreflightResult,
|
|
2471
3563
|
getAuditLogger,
|
|
2472
3564
|
getCliDisplayFields,
|
|
2473
3565
|
getDisplay,
|
|
@@ -2479,7 +3571,10 @@ export {
|
|
|
2479
3571
|
registerConfigNamespace,
|
|
2480
3572
|
registerDiscoveryCommands,
|
|
2481
3573
|
registerInitCommand,
|
|
3574
|
+
registerPipelineCommand,
|
|
2482
3575
|
registerShellCommands,
|
|
3576
|
+
registerSystemCommands,
|
|
3577
|
+
registerValidateCommand,
|
|
2483
3578
|
resolveFormat,
|
|
2484
3579
|
resolveRefs,
|
|
2485
3580
|
schemaToCliOptions,
|