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/bin/apcore-cli.js
CHANGED
|
@@ -138,6 +138,7 @@ var init_errors = __esm({
|
|
|
138
138
|
CONFIG_NAMESPACE_RESERVED: 78,
|
|
139
139
|
CONFIG_NAMESPACE_DUPLICATE: 78,
|
|
140
140
|
CONFIG_ENV_PREFIX_CONFLICT: 78,
|
|
141
|
+
CONFIG_ENV_MAP_CONFLICT: 78,
|
|
141
142
|
CONFIG_MOUNT_ERROR: 66,
|
|
142
143
|
CONFIG_BIND_ERROR: 65,
|
|
143
144
|
ERROR_FORMATTER_DUPLICATE: 70,
|
|
@@ -155,7 +156,7 @@ init_errors();
|
|
|
155
156
|
import { readFileSync as readFileSync3 } from "fs";
|
|
156
157
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
157
158
|
import * as path4 from "path";
|
|
158
|
-
import { Command as
|
|
159
|
+
import { Command as Command6, CommanderError, Option as Option4 } from "commander";
|
|
159
160
|
|
|
160
161
|
// src/ref-resolver.ts
|
|
161
162
|
init_esm_shims();
|
|
@@ -172,6 +173,188 @@ import * as readline from "readline";
|
|
|
172
173
|
|
|
173
174
|
// src/output.ts
|
|
174
175
|
init_esm_shims();
|
|
176
|
+
function resolveFormat(explicitFormat) {
|
|
177
|
+
if (explicitFormat !== void 0) {
|
|
178
|
+
return explicitFormat;
|
|
179
|
+
}
|
|
180
|
+
return process.stdout.isTTY ? "table" : "json";
|
|
181
|
+
}
|
|
182
|
+
function formatTable(headers, rows) {
|
|
183
|
+
const colWidths = headers.map(
|
|
184
|
+
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
185
|
+
);
|
|
186
|
+
const sep2 = colWidths.map((w) => "-".repeat(w)).join(" ");
|
|
187
|
+
const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(" ");
|
|
188
|
+
const dataLines = rows.map(
|
|
189
|
+
(row) => row.map((cell, i) => (cell ?? "").padEnd(colWidths[i])).join(" ")
|
|
190
|
+
);
|
|
191
|
+
return [headerLine, sep2, ...dataLines].join("\n") + "\n";
|
|
192
|
+
}
|
|
193
|
+
function selectFields(result, fields) {
|
|
194
|
+
const selected = {};
|
|
195
|
+
for (const f of fields.split(",")) {
|
|
196
|
+
const key = f.trim();
|
|
197
|
+
let val = result;
|
|
198
|
+
for (const part of key.split(".")) {
|
|
199
|
+
if (val && typeof val === "object" && !Array.isArray(val)) {
|
|
200
|
+
val = val[part];
|
|
201
|
+
} else {
|
|
202
|
+
val = void 0;
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
selected[key] = val;
|
|
207
|
+
}
|
|
208
|
+
return selected;
|
|
209
|
+
}
|
|
210
|
+
function formatExecResult(result, format, fields) {
|
|
211
|
+
if (result === null || result === void 0) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
let effective_result = result;
|
|
215
|
+
if (fields && typeof result === "object" && !Array.isArray(result) && result !== null) {
|
|
216
|
+
effective_result = selectFields(result, fields);
|
|
217
|
+
}
|
|
218
|
+
const effective = resolveFormat(format);
|
|
219
|
+
if (effective === "csv") {
|
|
220
|
+
if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
|
|
221
|
+
const obj = effective_result;
|
|
222
|
+
const keys = Object.keys(obj);
|
|
223
|
+
const header = keys.map(escapeCsvField).join(",");
|
|
224
|
+
const row = keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
|
|
225
|
+
process.stdout.write(header + "\n" + row + "\n");
|
|
226
|
+
} else if (Array.isArray(effective_result) && effective_result.length > 0 && typeof effective_result[0] === "object") {
|
|
227
|
+
const keys = Object.keys(effective_result[0]);
|
|
228
|
+
const header = keys.map(escapeCsvField).join(",");
|
|
229
|
+
const rows = effective_result.map((item) => {
|
|
230
|
+
const obj = item;
|
|
231
|
+
return keys.map((k) => escapeCsvField(String(obj[k]))).join(",");
|
|
232
|
+
});
|
|
233
|
+
process.stdout.write(header + "\n" + rows.join("\n") + "\n");
|
|
234
|
+
} else {
|
|
235
|
+
process.stdout.write(JSON.stringify(effective_result) + "\n");
|
|
236
|
+
}
|
|
237
|
+
} else if (effective === "yaml") {
|
|
238
|
+
if (typeof effective_result === "object" && !Array.isArray(effective_result) && effective_result !== null) {
|
|
239
|
+
const obj = effective_result;
|
|
240
|
+
const lines = Object.entries(obj).map(([k, v]) => {
|
|
241
|
+
if (v === null || v === void 0) return `${k}: null`;
|
|
242
|
+
if (typeof v === "object") return `${k}: ${JSON.stringify(v)}`;
|
|
243
|
+
return `${k}: ${v}`;
|
|
244
|
+
});
|
|
245
|
+
process.stdout.write(lines.join("\n") + "\n");
|
|
246
|
+
} else if (Array.isArray(effective_result)) {
|
|
247
|
+
for (const item of effective_result) {
|
|
248
|
+
if (typeof item === "object" && item !== null) {
|
|
249
|
+
const obj = item;
|
|
250
|
+
const lines = Object.entries(obj).map(([k, v]) => ` ${k}: ${v}`);
|
|
251
|
+
process.stdout.write("- " + lines.join("\n ") + "\n");
|
|
252
|
+
} else {
|
|
253
|
+
process.stdout.write(`- ${item}
|
|
254
|
+
`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
process.stdout.write(String(effective_result) + "\n");
|
|
259
|
+
}
|
|
260
|
+
} else if (effective === "jsonl") {
|
|
261
|
+
if (Array.isArray(effective_result)) {
|
|
262
|
+
for (const item of effective_result) {
|
|
263
|
+
process.stdout.write(JSON.stringify(item) + "\n");
|
|
264
|
+
}
|
|
265
|
+
} else {
|
|
266
|
+
process.stdout.write(JSON.stringify(effective_result) + "\n");
|
|
267
|
+
}
|
|
268
|
+
} else if (effective === "table" && typeof effective_result === "object" && !Array.isArray(effective_result)) {
|
|
269
|
+
const entries = Object.entries(effective_result);
|
|
270
|
+
const headers = ["Key", "Value"];
|
|
271
|
+
const rows = entries.map(([k, v]) => [String(k), String(v)]);
|
|
272
|
+
process.stdout.write(formatTable(headers, rows));
|
|
273
|
+
} else if (typeof effective_result === "object") {
|
|
274
|
+
process.stdout.write(JSON.stringify(effective_result, null, 2) + "\n");
|
|
275
|
+
} else if (typeof effective_result === "string") {
|
|
276
|
+
process.stdout.write(effective_result + "\n");
|
|
277
|
+
} else {
|
|
278
|
+
process.stdout.write(String(effective_result) + "\n");
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function escapeCsvField(value) {
|
|
282
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
283
|
+
return '"' + value.replace(/"/g, '""') + '"';
|
|
284
|
+
}
|
|
285
|
+
return value;
|
|
286
|
+
}
|
|
287
|
+
function formatPreflightResult(result, format) {
|
|
288
|
+
const resolved = resolveFormat(format);
|
|
289
|
+
if (resolved === "json" || !process.stdout.isTTY) {
|
|
290
|
+
const payload = {
|
|
291
|
+
valid: result.valid,
|
|
292
|
+
requires_approval: result.requires_approval,
|
|
293
|
+
checks: result.checks.map((c) => {
|
|
294
|
+
const entry = { check: c.check, passed: c.passed };
|
|
295
|
+
if (c.error !== void 0 && c.error !== null) {
|
|
296
|
+
entry.error = c.error;
|
|
297
|
+
}
|
|
298
|
+
if (c.warnings && c.warnings.length > 0) {
|
|
299
|
+
entry.warnings = c.warnings;
|
|
300
|
+
}
|
|
301
|
+
return entry;
|
|
302
|
+
})
|
|
303
|
+
};
|
|
304
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
305
|
+
} else {
|
|
306
|
+
for (const c of result.checks) {
|
|
307
|
+
const hasWarnings = (c.warnings?.length ?? 0) > 0;
|
|
308
|
+
let sym;
|
|
309
|
+
if (c.passed && hasWarnings) {
|
|
310
|
+
sym = "\u26A0";
|
|
311
|
+
} else if (c.passed) {
|
|
312
|
+
sym = "\u2713";
|
|
313
|
+
} else if (c.passed === false) {
|
|
314
|
+
sym = "\u2717";
|
|
315
|
+
} else {
|
|
316
|
+
sym = "\u25CB";
|
|
317
|
+
}
|
|
318
|
+
let status = ` ${sym} ${c.check.padEnd(20)}`;
|
|
319
|
+
if (c.error) {
|
|
320
|
+
const detail = typeof c.error === "object" ? JSON.stringify(c.error) : String(c.error);
|
|
321
|
+
status += ` ${detail}`;
|
|
322
|
+
} else if (c.passed && !hasWarnings) {
|
|
323
|
+
status += " OK";
|
|
324
|
+
} else if (!c.passed) {
|
|
325
|
+
status += " Skipped";
|
|
326
|
+
}
|
|
327
|
+
process.stdout.write(status + "\n");
|
|
328
|
+
for (const w of c.warnings ?? []) {
|
|
329
|
+
process.stdout.write(` Warning: ${w}
|
|
330
|
+
`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const errors = result.checks.filter((c) => !c.passed).length;
|
|
334
|
+
const warnings = result.checks.reduce((sum, c) => sum + (c.warnings?.length ?? 0), 0);
|
|
335
|
+
const tag = result.valid ? "PASS" : "FAIL";
|
|
336
|
+
process.stdout.write(`
|
|
337
|
+
Result: ${tag} (${errors} error(s), ${warnings} warning(s))
|
|
338
|
+
`);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
function firstFailedExitCode(result) {
|
|
342
|
+
const checkToExit = {
|
|
343
|
+
module_id: 2,
|
|
344
|
+
module_lookup: 44,
|
|
345
|
+
call_chain: 1,
|
|
346
|
+
acl: 77,
|
|
347
|
+
schema: 45,
|
|
348
|
+
approval: 46,
|
|
349
|
+
module_preflight: 1
|
|
350
|
+
};
|
|
351
|
+
for (const check of result.checks) {
|
|
352
|
+
if (!check.passed) {
|
|
353
|
+
return checkToExit[check.check] ?? 1;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return 1;
|
|
357
|
+
}
|
|
175
358
|
|
|
176
359
|
// src/logger.ts
|
|
177
360
|
init_esm_shims();
|
|
@@ -183,6 +366,13 @@ function setLogLevel(level) {
|
|
|
183
366
|
currentLevel = upper;
|
|
184
367
|
}
|
|
185
368
|
}
|
|
369
|
+
function shouldLog(level) {
|
|
370
|
+
return LEVELS[level] >= LEVELS[currentLevel];
|
|
371
|
+
}
|
|
372
|
+
function debug(message) {
|
|
373
|
+
if (shouldLog("DEBUG")) process.stderr.write(`DEBUG: ${message}
|
|
374
|
+
`);
|
|
375
|
+
}
|
|
186
376
|
|
|
187
377
|
// src/init-cmd.ts
|
|
188
378
|
init_esm_shims();
|
|
@@ -354,7 +544,10 @@ function registerConfigNamespace() {
|
|
|
354
544
|
stdin_buffer_limit: 10485760,
|
|
355
545
|
auto_approve: false,
|
|
356
546
|
help_text_max_length: 1e3,
|
|
357
|
-
logging_level: "WARNING"
|
|
547
|
+
logging_level: "WARNING",
|
|
548
|
+
approval_timeout: 60,
|
|
549
|
+
strategy: "standard",
|
|
550
|
+
group_depth: 1
|
|
358
551
|
}
|
|
359
552
|
});
|
|
360
553
|
}
|
|
@@ -518,6 +711,502 @@ function configureManHelp(program, progName, version, description, docsUrl) {
|
|
|
518
711
|
});
|
|
519
712
|
}
|
|
520
713
|
|
|
714
|
+
// src/discovery.ts
|
|
715
|
+
init_esm_shims();
|
|
716
|
+
init_errors();
|
|
717
|
+
import { Command as Command2, Option as Option2 } from "commander";
|
|
718
|
+
function registerValidateCommand(cli, registry, executor) {
|
|
719
|
+
const validateCmd = new Command2("validate").description("Run preflight checks without executing a module.").argument("<module-id>", "Module ID to validate").option("--input <source>", "JSON input file or '-' for stdin.").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
720
|
+
validateModuleId(moduleId);
|
|
721
|
+
const moduleDef = registry.getModule(moduleId);
|
|
722
|
+
if (!moduleDef) {
|
|
723
|
+
process.stderr.write(`Error: Module '${moduleId}' not found.
|
|
724
|
+
`);
|
|
725
|
+
process.exit(EXIT_CODES.MODULE_NOT_FOUND);
|
|
726
|
+
}
|
|
727
|
+
const merged = opts.input ? await collectInput(opts.input, {}, false) : {};
|
|
728
|
+
if (!executor.validate) {
|
|
729
|
+
process.stderr.write("Error: Executor does not support validate.\n");
|
|
730
|
+
process.exit(1);
|
|
731
|
+
}
|
|
732
|
+
const preflight = await executor.validate(moduleId, merged);
|
|
733
|
+
formatPreflightResult(preflight, opts.format);
|
|
734
|
+
process.exit(preflight.valid ? 0 : firstFailedExitCode(preflight));
|
|
735
|
+
});
|
|
736
|
+
cli.addCommand(validateCmd);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
// src/system-cmd.ts
|
|
740
|
+
init_esm_shims();
|
|
741
|
+
import { Command as Command3 } from "commander";
|
|
742
|
+
async function callSystemModule(executor, moduleId, inputs) {
|
|
743
|
+
if (executor.call) {
|
|
744
|
+
return executor.call(moduleId, inputs);
|
|
745
|
+
}
|
|
746
|
+
return executor.execute(moduleId, inputs);
|
|
747
|
+
}
|
|
748
|
+
function formatHealthSummaryTty(result) {
|
|
749
|
+
const summary = result.summary ?? {};
|
|
750
|
+
const modules = result.modules ?? [];
|
|
751
|
+
if (modules.length === 0) {
|
|
752
|
+
process.stdout.write("No modules found.\n");
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const total = summary.total_modules ?? modules.length;
|
|
756
|
+
process.stdout.write(`Health Overview (${total} modules)
|
|
757
|
+
|
|
758
|
+
`);
|
|
759
|
+
process.stdout.write(` ${"Module".padEnd(28)} ${"Status".padEnd(12)} ${"Error Rate".padEnd(12)} Top Error
|
|
760
|
+
`);
|
|
761
|
+
process.stdout.write(` ${"-".repeat(28)} ${"-".repeat(12)} ${"-".repeat(12)} ${"-".repeat(20)}
|
|
762
|
+
`);
|
|
763
|
+
for (const m of modules) {
|
|
764
|
+
const top = m.top_error;
|
|
765
|
+
const topStr = top ? `${top.code} (${top.count ?? "?"})` : "\u2014";
|
|
766
|
+
const rate = `${((m.error_rate ?? 0) * 100).toFixed(1)}%`;
|
|
767
|
+
process.stdout.write(
|
|
768
|
+
` ${String(m.module_id).padEnd(28)} ${String(m.status).padEnd(12)} ${rate.padEnd(12)} ${topStr}
|
|
769
|
+
`
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
const parts = [];
|
|
773
|
+
for (const key of ["healthy", "degraded", "error"]) {
|
|
774
|
+
const count = summary[key];
|
|
775
|
+
if (count) parts.push(`${count} ${key}`);
|
|
776
|
+
}
|
|
777
|
+
process.stdout.write(`
|
|
778
|
+
Summary: ${parts.join(", ") || "no data"}
|
|
779
|
+
`);
|
|
780
|
+
}
|
|
781
|
+
function formatHealthModuleTty(result) {
|
|
782
|
+
process.stdout.write(`Module: ${result.module_id ?? "?"}
|
|
783
|
+
`);
|
|
784
|
+
process.stdout.write(`Status: ${result.status ?? "unknown"}
|
|
785
|
+
`);
|
|
786
|
+
const total = result.total_calls ?? 0;
|
|
787
|
+
const errors = result.error_count ?? 0;
|
|
788
|
+
const rate = result.error_rate ?? 0;
|
|
789
|
+
const avg = result.avg_latency_ms ?? 0;
|
|
790
|
+
const p99 = result.p99_latency_ms ?? 0;
|
|
791
|
+
process.stdout.write(`Calls: ${total.toLocaleString()} total | ${errors.toLocaleString()} errors | ${(rate * 100).toFixed(1)}% error rate
|
|
792
|
+
`);
|
|
793
|
+
process.stdout.write(`Latency: ${avg.toFixed(0)}ms avg | ${p99.toFixed(0)}ms p99
|
|
794
|
+
`);
|
|
795
|
+
const recent = result.recent_errors ?? [];
|
|
796
|
+
if (recent.length > 0) {
|
|
797
|
+
process.stdout.write(`
|
|
798
|
+
Recent Errors (top ${recent.length}):
|
|
799
|
+
`);
|
|
800
|
+
for (const e of recent) {
|
|
801
|
+
const count = e.count ?? "?";
|
|
802
|
+
const last = e.last_occurred ?? "?";
|
|
803
|
+
process.stdout.write(` ${String(e.code ?? "?").padEnd(24)} x${count} (last: ${last})
|
|
804
|
+
`);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function formatUsageSummaryTty(result) {
|
|
809
|
+
const modules = result.modules ?? [];
|
|
810
|
+
const period = result.period ?? "?";
|
|
811
|
+
if (modules.length === 0) {
|
|
812
|
+
process.stdout.write(`No usage data for period ${period}.
|
|
813
|
+
`);
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
816
|
+
process.stdout.write(`Usage Summary (last ${period})
|
|
817
|
+
|
|
818
|
+
`);
|
|
819
|
+
process.stdout.write(` ${"Module".padEnd(24)} ${"Calls".padStart(8)} ${"Errors".padStart(8)} ${"Avg Latency".padStart(12)} ${"Trend".padStart(10)}
|
|
820
|
+
`);
|
|
821
|
+
process.stdout.write(` ${"-".repeat(24)} ${"-".repeat(8)} ${"-".repeat(8)} ${"-".repeat(12)} ${"-".repeat(10)}
|
|
822
|
+
`);
|
|
823
|
+
for (const m of modules) {
|
|
824
|
+
const avg = `${(m.avg_latency_ms ?? 0).toFixed(0)}ms`;
|
|
825
|
+
process.stdout.write(
|
|
826
|
+
` ${String(m.module_id).padEnd(24)} ${String(m.call_count ?? 0).padStart(8)} ${String(m.error_count ?? 0).padStart(8)} ${avg.padStart(12)} ${String(m.trend ?? "").padStart(10)}
|
|
827
|
+
`
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
const totalCalls = result.total_calls ?? modules.reduce((s, m) => s + (m.call_count ?? 0), 0);
|
|
831
|
+
const totalErrors = result.total_errors ?? modules.reduce((s, m) => s + (m.error_count ?? 0), 0);
|
|
832
|
+
process.stdout.write(`
|
|
833
|
+
Total: ${totalCalls.toLocaleString()} calls | ${totalErrors.toLocaleString()} errors
|
|
834
|
+
`);
|
|
835
|
+
}
|
|
836
|
+
async function registerSystemCommands(cli, executor) {
|
|
837
|
+
try {
|
|
838
|
+
if (executor.validate) {
|
|
839
|
+
await executor.validate("system.health.summary", {});
|
|
840
|
+
} else {
|
|
841
|
+
await callSystemModule(executor, "system.health.summary", { include_healthy: true });
|
|
842
|
+
}
|
|
843
|
+
} catch {
|
|
844
|
+
debug("System modules not available; skipping system command registration.");
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
const healthCmd = new Command3("health").description("Show module health status. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed health").option("--threshold <number>", "Error rate threshold (default: 0.01).", parseFloat, 0.01).option("--all", "Include healthy modules.", false).option("--errors <count>", "Max recent errors (module detail only).", parseInt, 10).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
848
|
+
const fmt = resolveFormat(opts.format);
|
|
849
|
+
try {
|
|
850
|
+
if (moduleId) {
|
|
851
|
+
const result = await callSystemModule(executor, "system.health.module", {
|
|
852
|
+
module_id: moduleId,
|
|
853
|
+
error_limit: opts.errors
|
|
854
|
+
});
|
|
855
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
856
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
857
|
+
} else {
|
|
858
|
+
formatHealthModuleTty(result);
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
const result = await callSystemModule(executor, "system.health.summary", {
|
|
862
|
+
error_rate_threshold: opts.threshold,
|
|
863
|
+
include_healthy: opts.all
|
|
864
|
+
});
|
|
865
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
866
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
867
|
+
} else {
|
|
868
|
+
formatHealthSummaryTty(result);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
} catch (e) {
|
|
872
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
873
|
+
`);
|
|
874
|
+
process.exit(1);
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
cli.addCommand(healthCmd);
|
|
878
|
+
const usageCmd = new Command3("usage").description("Show module usage statistics. Optionally specify a module ID for details.").argument("[module-id]", "Module ID for detailed usage").option("--period <period>", "Time window: 1h, 24h, 7d, 30d.", "24h").option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
879
|
+
const fmt = resolveFormat(opts.format);
|
|
880
|
+
try {
|
|
881
|
+
let result;
|
|
882
|
+
if (moduleId) {
|
|
883
|
+
result = await callSystemModule(executor, "system.usage.module", {
|
|
884
|
+
module_id: moduleId,
|
|
885
|
+
period: opts.period
|
|
886
|
+
});
|
|
887
|
+
} else {
|
|
888
|
+
result = await callSystemModule(executor, "system.usage.summary", {
|
|
889
|
+
period: opts.period
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
893
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
894
|
+
} else if (moduleId) {
|
|
895
|
+
formatExecResult(result, fmt);
|
|
896
|
+
} else {
|
|
897
|
+
formatUsageSummaryTty(result);
|
|
898
|
+
}
|
|
899
|
+
} catch (e) {
|
|
900
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
901
|
+
`);
|
|
902
|
+
process.exit(1);
|
|
903
|
+
}
|
|
904
|
+
});
|
|
905
|
+
cli.addCommand(usageCmd);
|
|
906
|
+
const enableCmd = new Command3("enable").description("Enable a disabled module at runtime.").argument("<module-id>", "Module ID to enable").requiredOption("--reason <reason>", "Reason for enabling (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
907
|
+
if (!opts.yes) {
|
|
908
|
+
process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
|
|
909
|
+
}
|
|
910
|
+
const fmt = resolveFormat(opts.format);
|
|
911
|
+
try {
|
|
912
|
+
const result = await callSystemModule(executor, "system.control.toggle_feature", {
|
|
913
|
+
module_id: moduleId,
|
|
914
|
+
enabled: true,
|
|
915
|
+
reason: opts.reason
|
|
916
|
+
});
|
|
917
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
918
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
919
|
+
} else {
|
|
920
|
+
process.stdout.write(`Module '${moduleId}' enabled.
|
|
921
|
+
Reason: ${opts.reason}
|
|
922
|
+
`);
|
|
923
|
+
}
|
|
924
|
+
} catch (e) {
|
|
925
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
926
|
+
`);
|
|
927
|
+
process.exit(1);
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
cli.addCommand(enableCmd);
|
|
931
|
+
const disableCmd = new Command3("disable").description("Disable a module at runtime (calls are rejected until re-enabled).").argument("<module-id>", "Module ID to disable").requiredOption("--reason <reason>", "Reason for disabling (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
932
|
+
if (!opts.yes) {
|
|
933
|
+
process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
|
|
934
|
+
}
|
|
935
|
+
const fmt = resolveFormat(opts.format);
|
|
936
|
+
try {
|
|
937
|
+
const result = await callSystemModule(executor, "system.control.toggle_feature", {
|
|
938
|
+
module_id: moduleId,
|
|
939
|
+
enabled: false,
|
|
940
|
+
reason: opts.reason
|
|
941
|
+
});
|
|
942
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
943
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
944
|
+
} else {
|
|
945
|
+
process.stdout.write(`Module '${moduleId}' disabled.
|
|
946
|
+
Reason: ${opts.reason}
|
|
947
|
+
`);
|
|
948
|
+
}
|
|
949
|
+
} catch (e) {
|
|
950
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
951
|
+
`);
|
|
952
|
+
process.exit(1);
|
|
953
|
+
}
|
|
954
|
+
});
|
|
955
|
+
cli.addCommand(disableCmd);
|
|
956
|
+
const reloadCmd = new Command3("reload").description("Hot-reload a module from disk.").argument("<module-id>", "Module ID to reload").requiredOption("--reason <reason>", "Reason for reload (required for audit).").option("-y, --yes", "Skip approval prompt.", false).option("--format <format>", "Output format.").action(async (moduleId, opts) => {
|
|
957
|
+
if (!opts.yes) {
|
|
958
|
+
process.stderr.write("Note: This command requires approval. Use --yes to bypass.\n");
|
|
959
|
+
}
|
|
960
|
+
const fmt = resolveFormat(opts.format);
|
|
961
|
+
try {
|
|
962
|
+
const result = await callSystemModule(executor, "system.control.reload_module", {
|
|
963
|
+
module_id: moduleId,
|
|
964
|
+
reason: opts.reason
|
|
965
|
+
});
|
|
966
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
967
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
968
|
+
} else {
|
|
969
|
+
const prev = result.previous_version ?? "?";
|
|
970
|
+
const newVer = result.new_version ?? "?";
|
|
971
|
+
const dur = result.reload_duration_ms ?? "?";
|
|
972
|
+
process.stdout.write(`Module '${moduleId}' reloaded.
|
|
973
|
+
`);
|
|
974
|
+
process.stdout.write(` Version: ${prev} -> ${newVer}
|
|
975
|
+
`);
|
|
976
|
+
process.stdout.write(` Duration: ${dur}ms
|
|
977
|
+
`);
|
|
978
|
+
}
|
|
979
|
+
} catch (e) {
|
|
980
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
981
|
+
`);
|
|
982
|
+
process.exit(1);
|
|
983
|
+
}
|
|
984
|
+
});
|
|
985
|
+
cli.addCommand(reloadCmd);
|
|
986
|
+
const configGroup = new Command3("config").description("Read or update runtime configuration.");
|
|
987
|
+
const configGetCmd = new Command3("get").description("Read a configuration value by dot-path key.").argument("<key>", "Configuration key (dot-path)").option("--format <format>", "Output format.", "table").action(async (key, opts) => {
|
|
988
|
+
const fmt = resolveFormat(opts.format);
|
|
989
|
+
try {
|
|
990
|
+
const result = await callSystemModule(executor, "system.config.get", { key });
|
|
991
|
+
const value = result?.value ?? result;
|
|
992
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
993
|
+
process.stdout.write(JSON.stringify({ key, value }, null, 2) + "\n");
|
|
994
|
+
} else {
|
|
995
|
+
process.stdout.write(`${key} = ${JSON.stringify(value)}
|
|
996
|
+
`);
|
|
997
|
+
}
|
|
998
|
+
} catch (e) {
|
|
999
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
1000
|
+
`);
|
|
1001
|
+
process.exit(1);
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
configGroup.addCommand(configGetCmd);
|
|
1005
|
+
const configSetCmd = new Command3("set").description("Update a runtime configuration value (requires approval).").argument("<key>", "Configuration key (dot-path)").argument("<value>", "New value").requiredOption("--reason <reason>", "Reason for config change (required for audit).").option("--format <format>", "Output format.").action(async (key, value, opts) => {
|
|
1006
|
+
const fmt = resolveFormat(opts.format);
|
|
1007
|
+
let parsedValue;
|
|
1008
|
+
try {
|
|
1009
|
+
parsedValue = JSON.parse(value);
|
|
1010
|
+
} catch {
|
|
1011
|
+
parsedValue = value;
|
|
1012
|
+
}
|
|
1013
|
+
try {
|
|
1014
|
+
const result = await callSystemModule(executor, "system.control.update_config", {
|
|
1015
|
+
key,
|
|
1016
|
+
value: parsedValue,
|
|
1017
|
+
reason: opts.reason
|
|
1018
|
+
});
|
|
1019
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
1020
|
+
process.stdout.write(JSON.stringify(result, null, 2) + "\n");
|
|
1021
|
+
} else {
|
|
1022
|
+
const old = result.old_value ?? "?";
|
|
1023
|
+
const newVal = result.new_value ?? "?";
|
|
1024
|
+
process.stdout.write(`Config updated: ${key}
|
|
1025
|
+
`);
|
|
1026
|
+
process.stdout.write(` ${JSON.stringify(old)} -> ${JSON.stringify(newVal)}
|
|
1027
|
+
`);
|
|
1028
|
+
process.stdout.write(` Reason: ${opts.reason}
|
|
1029
|
+
`);
|
|
1030
|
+
}
|
|
1031
|
+
} catch (e) {
|
|
1032
|
+
process.stderr.write(`Error: ${e instanceof Error ? e.message : e}
|
|
1033
|
+
`);
|
|
1034
|
+
process.exit(1);
|
|
1035
|
+
}
|
|
1036
|
+
});
|
|
1037
|
+
configGroup.addCommand(configSetCmd);
|
|
1038
|
+
cli.addCommand(configGroup);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
// src/strategy.ts
|
|
1042
|
+
init_esm_shims();
|
|
1043
|
+
import { Command as Command4, Option as Option3 } from "commander";
|
|
1044
|
+
var PRESET_STEPS = {
|
|
1045
|
+
standard: [
|
|
1046
|
+
"context_creation",
|
|
1047
|
+
"call_chain_guard",
|
|
1048
|
+
"module_lookup",
|
|
1049
|
+
"acl_check",
|
|
1050
|
+
"approval_gate",
|
|
1051
|
+
"middleware_before",
|
|
1052
|
+
"input_validation",
|
|
1053
|
+
"execute",
|
|
1054
|
+
"output_validation",
|
|
1055
|
+
"middleware_after",
|
|
1056
|
+
"return_result"
|
|
1057
|
+
],
|
|
1058
|
+
internal: [
|
|
1059
|
+
"context_creation",
|
|
1060
|
+
"call_chain_guard",
|
|
1061
|
+
"module_lookup",
|
|
1062
|
+
"middleware_before",
|
|
1063
|
+
"input_validation",
|
|
1064
|
+
"execute",
|
|
1065
|
+
"output_validation",
|
|
1066
|
+
"middleware_after",
|
|
1067
|
+
"return_result"
|
|
1068
|
+
],
|
|
1069
|
+
testing: [
|
|
1070
|
+
"context_creation",
|
|
1071
|
+
"module_lookup",
|
|
1072
|
+
"middleware_before",
|
|
1073
|
+
"input_validation",
|
|
1074
|
+
"execute",
|
|
1075
|
+
"output_validation",
|
|
1076
|
+
"middleware_after",
|
|
1077
|
+
"return_result"
|
|
1078
|
+
],
|
|
1079
|
+
performance: [
|
|
1080
|
+
"context_creation",
|
|
1081
|
+
"call_chain_guard",
|
|
1082
|
+
"module_lookup",
|
|
1083
|
+
"acl_check",
|
|
1084
|
+
"approval_gate",
|
|
1085
|
+
"input_validation",
|
|
1086
|
+
"execute",
|
|
1087
|
+
"output_validation",
|
|
1088
|
+
"return_result"
|
|
1089
|
+
],
|
|
1090
|
+
minimal: [
|
|
1091
|
+
"context_creation",
|
|
1092
|
+
"module_lookup",
|
|
1093
|
+
"execute",
|
|
1094
|
+
"return_result"
|
|
1095
|
+
]
|
|
1096
|
+
};
|
|
1097
|
+
function registerPipelineCommand(cli, executor) {
|
|
1098
|
+
const pipelineCmd = new Command4("describe-pipeline").description("Show the execution pipeline steps for a strategy.").addOption(
|
|
1099
|
+
new Option3("--strategy <name>", "Strategy to describe (default: standard).").choices(["standard", "internal", "testing", "performance", "minimal"]).default("standard")
|
|
1100
|
+
).option("--format <format>", "Output format.").action((opts) => {
|
|
1101
|
+
const fmt = resolveFormat(opts.format);
|
|
1102
|
+
let strategyObj = null;
|
|
1103
|
+
const ex = executor;
|
|
1104
|
+
if (typeof ex._resolve_strategy_name === "function" || typeof ex._resolveStrategyName === "function") {
|
|
1105
|
+
try {
|
|
1106
|
+
const fn = ex._resolve_strategy_name ?? ex._resolveStrategyName;
|
|
1107
|
+
strategyObj = fn(opts.strategy);
|
|
1108
|
+
} catch {
|
|
1109
|
+
strategyObj = null;
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
if (!strategyObj) {
|
|
1113
|
+
const steps = PRESET_STEPS[opts.strategy] ?? [];
|
|
1114
|
+
const pureSteps = /* @__PURE__ */ new Set([
|
|
1115
|
+
"context_creation",
|
|
1116
|
+
"call_chain_guard",
|
|
1117
|
+
"module_lookup",
|
|
1118
|
+
"acl_check",
|
|
1119
|
+
"input_validation"
|
|
1120
|
+
]);
|
|
1121
|
+
const nonRemovable = /* @__PURE__ */ new Set([
|
|
1122
|
+
"context_creation",
|
|
1123
|
+
"module_lookup",
|
|
1124
|
+
"execute",
|
|
1125
|
+
"return_result"
|
|
1126
|
+
]);
|
|
1127
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
1128
|
+
const payload = {
|
|
1129
|
+
strategy: opts.strategy,
|
|
1130
|
+
step_count: steps.length,
|
|
1131
|
+
steps: steps.map((s, i) => ({
|
|
1132
|
+
index: i + 1,
|
|
1133
|
+
name: s,
|
|
1134
|
+
pure: pureSteps.has(s),
|
|
1135
|
+
removable: !nonRemovable.has(s)
|
|
1136
|
+
}))
|
|
1137
|
+
};
|
|
1138
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
1139
|
+
} else {
|
|
1140
|
+
process.stdout.write(`Pipeline: ${opts.strategy} (${steps.length} steps)
|
|
1141
|
+
|
|
1142
|
+
`);
|
|
1143
|
+
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
1144
|
+
`);
|
|
1145
|
+
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
1146
|
+
`);
|
|
1147
|
+
for (let i = 0; i < steps.length; i++) {
|
|
1148
|
+
const pure = pureSteps.has(steps[i]) ? "yes" : "no";
|
|
1149
|
+
const removable = nonRemovable.has(steps[i]) ? "no" : "yes";
|
|
1150
|
+
process.stdout.write(` ${String(i + 1).padEnd(4)} ${steps[i].padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} \u2014
|
|
1151
|
+
`);
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const stepsInfo = strategyObj.steps.map((step) => ({
|
|
1157
|
+
name: step.name,
|
|
1158
|
+
pure: step.pure ?? false,
|
|
1159
|
+
removable: step.removable ?? true,
|
|
1160
|
+
timeout_ms: step.timeout_ms ?? null
|
|
1161
|
+
}));
|
|
1162
|
+
if (fmt === "json" || !process.stdout.isTTY) {
|
|
1163
|
+
const payload = {
|
|
1164
|
+
strategy: opts.strategy,
|
|
1165
|
+
step_count: stepsInfo.length,
|
|
1166
|
+
steps: stepsInfo.map((s, i) => ({ index: i + 1, ...s }))
|
|
1167
|
+
};
|
|
1168
|
+
process.stdout.write(JSON.stringify(payload, null, 2) + "\n");
|
|
1169
|
+
} else {
|
|
1170
|
+
process.stdout.write(`Pipeline: ${opts.strategy} (${stepsInfo.length} steps)
|
|
1171
|
+
|
|
1172
|
+
`);
|
|
1173
|
+
process.stdout.write(` ${"#".padEnd(4)} ${"Step".padEnd(28)} ${"Pure".padEnd(6)} ${"Removable".padEnd(11)} Timeout
|
|
1174
|
+
`);
|
|
1175
|
+
process.stdout.write(` ${"-".repeat(4)} ${"-".repeat(28)} ${"-".repeat(6)} ${"-".repeat(11)} ${"-".repeat(8)}
|
|
1176
|
+
`);
|
|
1177
|
+
for (let i = 0; i < stepsInfo.length; i++) {
|
|
1178
|
+
const s = stepsInfo[i];
|
|
1179
|
+
const pure = s.pure ? "yes" : "no";
|
|
1180
|
+
const removable = s.removable ? "yes" : "no";
|
|
1181
|
+
const timeout = s.timeout_ms !== null ? `${s.timeout_ms}ms` : "\u2014";
|
|
1182
|
+
process.stdout.write(` ${String(i + 1).padEnd(4)} ${s.name.padEnd(28)} ${pure.padEnd(6)} ${removable.padEnd(11)} ${timeout}
|
|
1183
|
+
`);
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
cli.addCommand(pipelineCmd);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
// src/cli.ts
|
|
1191
|
+
init_esm_shims();
|
|
1192
|
+
import { Command as Command5 } from "commander";
|
|
1193
|
+
var BUILTIN_COMMANDS = [
|
|
1194
|
+
"completion",
|
|
1195
|
+
"config",
|
|
1196
|
+
"describe",
|
|
1197
|
+
"describe-pipeline",
|
|
1198
|
+
"disable",
|
|
1199
|
+
"enable",
|
|
1200
|
+
"exec",
|
|
1201
|
+
"health",
|
|
1202
|
+
"init",
|
|
1203
|
+
"list",
|
|
1204
|
+
"man",
|
|
1205
|
+
"reload",
|
|
1206
|
+
"usage",
|
|
1207
|
+
"validate"
|
|
1208
|
+
];
|
|
1209
|
+
|
|
521
1210
|
// src/main.ts
|
|
522
1211
|
var __dirname3 = path4.dirname(fileURLToPath3(import.meta.url));
|
|
523
1212
|
var verboseHelp = false;
|
|
@@ -530,15 +1219,42 @@ try {
|
|
|
530
1219
|
VERSION = pkg.version;
|
|
531
1220
|
} catch {
|
|
532
1221
|
}
|
|
533
|
-
function createCli(
|
|
1222
|
+
function createCli(extensionsDirOrOpts, progName, verbose = false) {
|
|
1223
|
+
let extensionsDir;
|
|
1224
|
+
let registry;
|
|
1225
|
+
let executor;
|
|
1226
|
+
let extraCommands;
|
|
1227
|
+
if (typeof extensionsDirOrOpts === "object" && extensionsDirOrOpts !== null) {
|
|
1228
|
+
extensionsDir = extensionsDirOrOpts.extensionsDir;
|
|
1229
|
+
progName = extensionsDirOrOpts.progName ?? progName;
|
|
1230
|
+
verbose = extensionsDirOrOpts.verbose ?? verbose;
|
|
1231
|
+
registry = extensionsDirOrOpts.registry;
|
|
1232
|
+
executor = extensionsDirOrOpts.executor;
|
|
1233
|
+
extraCommands = extensionsDirOrOpts.extraCommands;
|
|
1234
|
+
} else {
|
|
1235
|
+
extensionsDir = extensionsDirOrOpts;
|
|
1236
|
+
}
|
|
534
1237
|
verboseHelp = verbose;
|
|
535
1238
|
registerConfigNamespace();
|
|
536
1239
|
const resolvedProgName = progName ?? path4.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
|
|
537
1240
|
const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
|
|
538
1241
|
setLogLevel(cliLogLevel);
|
|
539
|
-
const program = new
|
|
540
|
-
|
|
541
|
-
|
|
1242
|
+
const program = new Command6(resolvedProgName).exitOverride().version(VERSION, "--version", `Show ${resolvedProgName} version`).description("apcore CLI \u2014 execute apcore modules from the command line").option("--extensions-dir <path>", "Path to extensions directory").option("--commands-dir <path>", "Path to convention-based commands directory").option("--binding <path>", "Path to binding.yaml for display overlay").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING").option("--verbose", "Show all options in help output (including built-in apcore options)");
|
|
1243
|
+
if (executor && !registry) {
|
|
1244
|
+
throw new Error("executor requires registry \u2014 pass both or neither");
|
|
1245
|
+
}
|
|
1246
|
+
if (registry) {
|
|
1247
|
+
program._registry = registry;
|
|
1248
|
+
if (executor) {
|
|
1249
|
+
program._executor = executor;
|
|
1250
|
+
registerValidateCommand(program, registry, executor);
|
|
1251
|
+
void registerSystemCommands(program, executor);
|
|
1252
|
+
registerPipelineCommand(program, executor);
|
|
1253
|
+
}
|
|
1254
|
+
} else {
|
|
1255
|
+
const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
|
|
1256
|
+
void resolvedExtDir;
|
|
1257
|
+
}
|
|
542
1258
|
program.addHelpText("after", [
|
|
543
1259
|
"",
|
|
544
1260
|
"Use --help --verbose to show all options (including built-in apcore options).",
|
|
@@ -546,6 +1262,24 @@ function createCli(extensionsDir, progName, verbose = false) {
|
|
|
546
1262
|
].join("\n"));
|
|
547
1263
|
registerInitCommand(program);
|
|
548
1264
|
configureManHelp(program, resolvedProgName, VERSION);
|
|
1265
|
+
if (extraCommands && extraCommands.length > 0) {
|
|
1266
|
+
const existingNames = /* @__PURE__ */ new Set([
|
|
1267
|
+
...BUILTIN_COMMANDS,
|
|
1268
|
+
...program.commands.map((c) => c.name())
|
|
1269
|
+
]);
|
|
1270
|
+
for (const cmd of extraCommands) {
|
|
1271
|
+
const cmdName = cmd.name();
|
|
1272
|
+
if (existingNames.has(cmdName)) {
|
|
1273
|
+
process.stderr.write(
|
|
1274
|
+
`Warning: Extra command '${cmdName}' collides with a built-in command and will be skipped.
|
|
1275
|
+
`
|
|
1276
|
+
);
|
|
1277
|
+
continue;
|
|
1278
|
+
}
|
|
1279
|
+
program.addCommand(cmd);
|
|
1280
|
+
existingNames.add(cmdName);
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
549
1283
|
program.hook("preAction", async (thisCommand) => {
|
|
550
1284
|
const opts = thisCommand.opts();
|
|
551
1285
|
const commandsDir = opts.commandsDir;
|
|
@@ -592,6 +1326,87 @@ function main(progName) {
|
|
|
592
1326
|
process.exit(code);
|
|
593
1327
|
}
|
|
594
1328
|
}
|
|
1329
|
+
function validateModuleId(moduleId) {
|
|
1330
|
+
if (moduleId.length > 128) {
|
|
1331
|
+
process.stderr.write(
|
|
1332
|
+
`Error: Invalid module ID format: '${moduleId}'. Maximum length is 128 characters.
|
|
1333
|
+
`
|
|
1334
|
+
);
|
|
1335
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1336
|
+
}
|
|
1337
|
+
if (!/^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$/.test(moduleId)) {
|
|
1338
|
+
process.stderr.write(
|
|
1339
|
+
`Error: Invalid module ID format: '${moduleId}'.
|
|
1340
|
+
`
|
|
1341
|
+
);
|
|
1342
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
async function collectInput(stdinFlag, cliKwargs = {}, largeInput) {
|
|
1346
|
+
const cliKwargsNonNull = {};
|
|
1347
|
+
for (const [k, v] of Object.entries(cliKwargs)) {
|
|
1348
|
+
if (v !== null && v !== void 0) {
|
|
1349
|
+
cliKwargsNonNull[k] = v;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
if (!stdinFlag) {
|
|
1353
|
+
return cliKwargsNonNull;
|
|
1354
|
+
}
|
|
1355
|
+
if (stdinFlag === "-") {
|
|
1356
|
+
const raw = await readStdin();
|
|
1357
|
+
const rawSize = Buffer.byteLength(raw, "utf-8");
|
|
1358
|
+
if (rawSize > 10485760 && !largeInput) {
|
|
1359
|
+
process.stderr.write(
|
|
1360
|
+
"Error: STDIN input exceeds 10MB limit. Use --large-input to override.\n"
|
|
1361
|
+
);
|
|
1362
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1363
|
+
}
|
|
1364
|
+
if (!raw) {
|
|
1365
|
+
return cliKwargsNonNull;
|
|
1366
|
+
}
|
|
1367
|
+
let stdinData;
|
|
1368
|
+
try {
|
|
1369
|
+
stdinData = JSON.parse(raw);
|
|
1370
|
+
} catch {
|
|
1371
|
+
process.stderr.write(
|
|
1372
|
+
"Error: STDIN does not contain valid JSON.\n"
|
|
1373
|
+
);
|
|
1374
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1375
|
+
}
|
|
1376
|
+
if (typeof stdinData !== "object" || stdinData === null || Array.isArray(stdinData)) {
|
|
1377
|
+
process.stderr.write(
|
|
1378
|
+
`Error: STDIN JSON must be an object, got ${Array.isArray(stdinData) ? "array" : typeof stdinData}.
|
|
1379
|
+
`
|
|
1380
|
+
);
|
|
1381
|
+
process.exit(EXIT_CODES.INVALID_CLI_INPUT);
|
|
1382
|
+
}
|
|
1383
|
+
return { ...stdinData, ...cliKwargsNonNull };
|
|
1384
|
+
}
|
|
1385
|
+
return cliKwargsNonNull;
|
|
1386
|
+
}
|
|
1387
|
+
function readStdin() {
|
|
1388
|
+
return new Promise((resolve3, reject) => {
|
|
1389
|
+
const chunks = [];
|
|
1390
|
+
const onData = (chunk) => chunks.push(chunk);
|
|
1391
|
+
const onEnd = () => {
|
|
1392
|
+
cleanup();
|
|
1393
|
+
resolve3(Buffer.concat(chunks).toString("utf-8"));
|
|
1394
|
+
};
|
|
1395
|
+
const onError = (err) => {
|
|
1396
|
+
cleanup();
|
|
1397
|
+
reject(err);
|
|
1398
|
+
};
|
|
1399
|
+
const cleanup = () => {
|
|
1400
|
+
process.stdin.removeListener("data", onData);
|
|
1401
|
+
process.stdin.removeListener("end", onEnd);
|
|
1402
|
+
process.stdin.removeListener("error", onError);
|
|
1403
|
+
};
|
|
1404
|
+
process.stdin.on("data", onData);
|
|
1405
|
+
process.stdin.on("end", onEnd);
|
|
1406
|
+
process.stdin.on("error", onError);
|
|
1407
|
+
process.stdin.resume();
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
595
1410
|
|
|
596
1411
|
// bin/apcore-cli.ts
|
|
597
1412
|
main("apcore-cli");
|