apcore-cli 0.2.0 → 0.3.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/LICENSE +1 -1
- package/README.md +13 -10
- package/dist/bin/apcore-cli.js +181 -6
- package/dist/bin/apcore-cli.js.map +1 -1
- package/dist/src/index.d.ts +101 -14
- package/dist/src/index.js +537 -54
- package/dist/src/index.js.map +1 -1
- package/package.json +8 -2
package/dist/src/index.js
CHANGED
|
@@ -137,9 +137,9 @@ __export(audit_exports, {
|
|
|
137
137
|
setAuditLogger: () => setAuditLogger
|
|
138
138
|
});
|
|
139
139
|
import * as crypto from "crypto";
|
|
140
|
-
import * as
|
|
140
|
+
import * as fs2 from "fs";
|
|
141
141
|
import * as os from "os";
|
|
142
|
-
import * as
|
|
142
|
+
import * as path3 from "path";
|
|
143
143
|
function setAuditLogger(auditLogger) {
|
|
144
144
|
_auditLogger = auditLogger;
|
|
145
145
|
}
|
|
@@ -153,19 +153,19 @@ var init_audit = __esm({
|
|
|
153
153
|
init_esm_shims();
|
|
154
154
|
_auditLogger = null;
|
|
155
155
|
AuditLogger = class _AuditLogger {
|
|
156
|
-
static DEFAULT_PATH =
|
|
156
|
+
static DEFAULT_PATH = path3.join(
|
|
157
157
|
os.homedir(),
|
|
158
158
|
".apcore-cli",
|
|
159
159
|
"audit.jsonl"
|
|
160
160
|
);
|
|
161
161
|
logPath;
|
|
162
|
-
constructor(
|
|
163
|
-
this.logPath =
|
|
162
|
+
constructor(path7) {
|
|
163
|
+
this.logPath = path7 ?? _AuditLogger.DEFAULT_PATH;
|
|
164
164
|
this.ensureDirectory();
|
|
165
165
|
}
|
|
166
166
|
ensureDirectory() {
|
|
167
167
|
try {
|
|
168
|
-
|
|
168
|
+
fs2.mkdirSync(path3.dirname(this.logPath), { recursive: true });
|
|
169
169
|
} catch {
|
|
170
170
|
}
|
|
171
171
|
}
|
|
@@ -180,7 +180,7 @@ var init_audit = __esm({
|
|
|
180
180
|
duration_ms: durationMs
|
|
181
181
|
};
|
|
182
182
|
try {
|
|
183
|
-
|
|
183
|
+
fs2.appendFileSync(this.logPath, JSON.stringify(entry) + "\n");
|
|
184
184
|
} catch (err) {
|
|
185
185
|
console.warn(`Could not write audit log: ${err}`);
|
|
186
186
|
}
|
|
@@ -381,9 +381,9 @@ var init_auth = __esm({
|
|
|
381
381
|
|
|
382
382
|
// src/security/sandbox.ts
|
|
383
383
|
import * as child_process from "child_process";
|
|
384
|
-
import * as
|
|
384
|
+
import * as fs3 from "fs";
|
|
385
385
|
import * as os3 from "os";
|
|
386
|
-
import * as
|
|
386
|
+
import * as path4 from "path";
|
|
387
387
|
var Sandbox;
|
|
388
388
|
var init_sandbox = __esm({
|
|
389
389
|
"src/security/sandbox.ts"() {
|
|
@@ -416,8 +416,8 @@ var init_sandbox = __esm({
|
|
|
416
416
|
env[key] = value;
|
|
417
417
|
}
|
|
418
418
|
}
|
|
419
|
-
const tmpDir =
|
|
420
|
-
|
|
419
|
+
const tmpDir = fs3.mkdtempSync(
|
|
420
|
+
path4.join(os3.tmpdir(), "apcore_sandbox_")
|
|
421
421
|
);
|
|
422
422
|
try {
|
|
423
423
|
env.HOME = tmpDir;
|
|
@@ -455,7 +455,7 @@ var init_sandbox = __esm({
|
|
|
455
455
|
);
|
|
456
456
|
} finally {
|
|
457
457
|
try {
|
|
458
|
-
|
|
458
|
+
fs3.rmSync(tmpDir, { recursive: true, force: true });
|
|
459
459
|
} catch {
|
|
460
460
|
}
|
|
461
461
|
}
|
|
@@ -493,7 +493,7 @@ init_esm_shims();
|
|
|
493
493
|
init_errors();
|
|
494
494
|
import { readFileSync } from "fs";
|
|
495
495
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
496
|
-
import * as
|
|
496
|
+
import * as path5 from "path";
|
|
497
497
|
import { Command, CommanderError } from "commander";
|
|
498
498
|
|
|
499
499
|
// src/ref-resolver.ts
|
|
@@ -663,7 +663,7 @@ function mapType(propName, propSchema) {
|
|
|
663
663
|
}
|
|
664
664
|
return typeMap[schemaType] ?? "string";
|
|
665
665
|
}
|
|
666
|
-
function extractHelp(propSchema) {
|
|
666
|
+
function extractHelp(propSchema, maxLength = 1e3) {
|
|
667
667
|
let text = propSchema["x-llm-description"];
|
|
668
668
|
if (!text) {
|
|
669
669
|
text = propSchema.description;
|
|
@@ -671,13 +671,13 @@ function extractHelp(propSchema) {
|
|
|
671
671
|
if (!text) {
|
|
672
672
|
return void 0;
|
|
673
673
|
}
|
|
674
|
-
if (text.length >
|
|
675
|
-
return text.slice(0,
|
|
674
|
+
if (maxLength > 0 && text.length > maxLength) {
|
|
675
|
+
return text.slice(0, maxLength - 3) + "...";
|
|
676
676
|
}
|
|
677
677
|
return text;
|
|
678
678
|
}
|
|
679
679
|
var RESERVED_NAMES = /* @__PURE__ */ new Set(["input", "yes", "large_input", "format", "sandbox"]);
|
|
680
|
-
function schemaToCliOptions(schema) {
|
|
680
|
+
function schemaToCliOptions(schema, maxHelpLength = 1e3) {
|
|
681
681
|
const properties = schema.properties ?? {};
|
|
682
682
|
const requiredList = schema.required ?? [];
|
|
683
683
|
const options = [];
|
|
@@ -701,7 +701,7 @@ function schemaToCliOptions(schema) {
|
|
|
701
701
|
}
|
|
702
702
|
const typeResult = mapType(propName, propSchema);
|
|
703
703
|
const isRequired = requiredList.includes(propName);
|
|
704
|
-
const helpBase = extractHelp(propSchema);
|
|
704
|
+
const helpBase = extractHelp(propSchema, maxHelpLength);
|
|
705
705
|
const helpText = isRequired ? (helpBase ? helpBase + " " : "") + "[required]" : helpBase ?? "";
|
|
706
706
|
const defaultValue = propSchema.default;
|
|
707
707
|
if (typeResult === BOOLEAN_FLAG) {
|
|
@@ -884,12 +884,12 @@ function formatTable(headers, rows) {
|
|
|
884
884
|
const colWidths = headers.map(
|
|
885
885
|
(h, i) => Math.max(h.length, ...rows.map((r) => (r[i] ?? "").length))
|
|
886
886
|
);
|
|
887
|
-
const
|
|
887
|
+
const sep2 = colWidths.map((w) => "-".repeat(w)).join(" ");
|
|
888
888
|
const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(" ");
|
|
889
889
|
const dataLines = rows.map(
|
|
890
890
|
(row) => row.map((cell, i) => (cell ?? "").padEnd(colWidths[i])).join(" ")
|
|
891
891
|
);
|
|
892
|
-
return [headerLine,
|
|
892
|
+
return [headerLine, sep2, ...dataLines].join("\n") + "\n";
|
|
893
893
|
}
|
|
894
894
|
function formatModuleList(modules, format, filterTags) {
|
|
895
895
|
if (format === "table") {
|
|
@@ -1056,18 +1056,209 @@ function error(message) {
|
|
|
1056
1056
|
`);
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
|
+
// src/init-cmd.ts
|
|
1060
|
+
init_esm_shims();
|
|
1061
|
+
import * as fs from "fs";
|
|
1062
|
+
import * as path2 from "path";
|
|
1063
|
+
var DECORATOR_TEMPLATE = `import { module } from "apcore-js";
|
|
1064
|
+
import { Type } from "@sinclair/typebox";
|
|
1065
|
+
|
|
1066
|
+
export const {varName} = module({
|
|
1067
|
+
id: "{moduleId}",
|
|
1068
|
+
description: "{description}",
|
|
1069
|
+
inputSchema: Type.Object({}),
|
|
1070
|
+
outputSchema: Type.Object({ status: Type.String() }),
|
|
1071
|
+
execute: (_inputs) => {
|
|
1072
|
+
// TODO: implement
|
|
1073
|
+
return { status: "ok" };
|
|
1074
|
+
},
|
|
1075
|
+
});
|
|
1076
|
+
`;
|
|
1077
|
+
var CONVENTION_TEMPLATE = `/**
|
|
1078
|
+
* {description}
|
|
1079
|
+
*/
|
|
1080
|
+
{cliGroupLine}
|
|
1081
|
+
export function {funcName}(): Record<string, unknown> {
|
|
1082
|
+
// TODO: implement
|
|
1083
|
+
return { status: "ok" };
|
|
1084
|
+
}
|
|
1085
|
+
`;
|
|
1086
|
+
var BINDING_TEMPLATE = `bindings:
|
|
1087
|
+
- module_id: "{moduleId}"
|
|
1088
|
+
target: "{target}"
|
|
1089
|
+
description: "{description}"
|
|
1090
|
+
auto_schema: true
|
|
1091
|
+
`;
|
|
1092
|
+
function renderTemplate(template, context) {
|
|
1093
|
+
let result = template;
|
|
1094
|
+
for (const [key, value] of Object.entries(context)) {
|
|
1095
|
+
result = result.split(`{${key}}`).join(value);
|
|
1096
|
+
}
|
|
1097
|
+
return result;
|
|
1098
|
+
}
|
|
1099
|
+
function registerInitCommand(cli) {
|
|
1100
|
+
const initGroup = cli.command("init").description("Scaffold new apcore modules.");
|
|
1101
|
+
initGroup.command("module <module-id>").description("Create a new module from a template.\n\nMODULE_ID is the module identifier (e.g., ops.deploy, user.create).").option(
|
|
1102
|
+
"--style <style>",
|
|
1103
|
+
"Module style: decorator (@module), convention (plain function), or binding (YAML).",
|
|
1104
|
+
"convention"
|
|
1105
|
+
).option("--dir <path>", "Output directory. Default: extensions/ or commands/.").option("-d, --description <text>", "Module description.", "TODO: add description").action((moduleId, opts) => {
|
|
1106
|
+
const lastDot = moduleId.lastIndexOf(".");
|
|
1107
|
+
const prefix = lastDot >= 0 ? moduleId.substring(0, lastDot) : moduleId;
|
|
1108
|
+
const funcName = lastDot >= 0 ? moduleId.substring(lastDot + 1) : moduleId;
|
|
1109
|
+
const style = opts.style;
|
|
1110
|
+
const description = opts.description;
|
|
1111
|
+
const dir = opts.dir ?? (style === "decorator" ? "extensions" : style === "binding" ? "bindings" : "commands");
|
|
1112
|
+
if (dir.split(path2.sep).includes("..") || dir.split("/").includes("..")) {
|
|
1113
|
+
process.stderr.write(`Error: Output directory must not contain '..' path components.
|
|
1114
|
+
`);
|
|
1115
|
+
process.exit(2);
|
|
1116
|
+
}
|
|
1117
|
+
switch (style) {
|
|
1118
|
+
case "decorator":
|
|
1119
|
+
createDecoratorModule(moduleId, prefix, funcName, description, dir);
|
|
1120
|
+
break;
|
|
1121
|
+
case "convention":
|
|
1122
|
+
createConventionModule(moduleId, prefix, funcName, description, dir);
|
|
1123
|
+
break;
|
|
1124
|
+
case "binding":
|
|
1125
|
+
createBindingModule(moduleId, prefix, funcName, description, dir);
|
|
1126
|
+
break;
|
|
1127
|
+
default:
|
|
1128
|
+
process.stderr.write(`Error: Unknown style '${style}'
|
|
1129
|
+
`);
|
|
1130
|
+
process.exit(2);
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
function createDecoratorModule(moduleId, _prefix, funcName, description, outputDir) {
|
|
1135
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1136
|
+
const filename = moduleId.replace(/\./g, "_") + ".ts";
|
|
1137
|
+
const filepath = path2.join(outputDir, filename);
|
|
1138
|
+
const varName = funcName + "Module";
|
|
1139
|
+
const content = renderTemplate(DECORATOR_TEMPLATE, {
|
|
1140
|
+
moduleId,
|
|
1141
|
+
varName,
|
|
1142
|
+
funcName,
|
|
1143
|
+
description
|
|
1144
|
+
});
|
|
1145
|
+
fs.writeFileSync(filepath, content);
|
|
1146
|
+
process.stdout.write(`Created ${filepath}
|
|
1147
|
+
`);
|
|
1148
|
+
}
|
|
1149
|
+
function createConventionModule(moduleId, prefix, funcName, description, outputDir) {
|
|
1150
|
+
const prefixParts = prefix.split(".");
|
|
1151
|
+
const dirPath = prefixParts.length > 1 ? path2.join(outputDir, ...prefixParts.slice(0, -1)) : outputDir;
|
|
1152
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
1153
|
+
let filename;
|
|
1154
|
+
if (prefixParts.length > 1) {
|
|
1155
|
+
filename = prefixParts[prefixParts.length - 1] + ".ts";
|
|
1156
|
+
} else {
|
|
1157
|
+
filename = prefix + ".ts";
|
|
1158
|
+
}
|
|
1159
|
+
if (prefix === funcName) {
|
|
1160
|
+
filename = prefix + ".ts";
|
|
1161
|
+
}
|
|
1162
|
+
const filepath = path2.join(dirPath, filename);
|
|
1163
|
+
const cliGroupLine = moduleId.includes(".") ? `export const CLI_GROUP = "${prefixParts[0]}";
|
|
1164
|
+
` : "";
|
|
1165
|
+
const content = renderTemplate(CONVENTION_TEMPLATE, {
|
|
1166
|
+
funcName,
|
|
1167
|
+
description,
|
|
1168
|
+
cliGroupLine
|
|
1169
|
+
});
|
|
1170
|
+
fs.writeFileSync(filepath, content);
|
|
1171
|
+
process.stdout.write(`Created ${filepath}
|
|
1172
|
+
`);
|
|
1173
|
+
}
|
|
1174
|
+
function createBindingModule(moduleId, prefix, funcName, description, outputDir) {
|
|
1175
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
1176
|
+
const yamlFile = path2.join(outputDir, moduleId.replace(/\./g, "_") + ".binding.yaml");
|
|
1177
|
+
const target = `commands.${prefix}:${funcName}`;
|
|
1178
|
+
const yamlContent = renderTemplate(BINDING_TEMPLATE, {
|
|
1179
|
+
moduleId,
|
|
1180
|
+
target,
|
|
1181
|
+
description
|
|
1182
|
+
});
|
|
1183
|
+
fs.writeFileSync(yamlFile, yamlContent);
|
|
1184
|
+
process.stdout.write(`Created ${yamlFile}
|
|
1185
|
+
`);
|
|
1186
|
+
const baseSrc = "commands";
|
|
1187
|
+
fs.mkdirSync(baseSrc, { recursive: true });
|
|
1188
|
+
const srcFile = path2.join(baseSrc, prefix.replace(/\./g, "_") + ".ts");
|
|
1189
|
+
if (!fs.existsSync(srcFile)) {
|
|
1190
|
+
const srcContent = `export function ${funcName}(): Record<string, unknown> {
|
|
1191
|
+
/** ${description} */
|
|
1192
|
+
// TODO: implement
|
|
1193
|
+
return { status: "ok" };
|
|
1194
|
+
}
|
|
1195
|
+
`;
|
|
1196
|
+
fs.writeFileSync(srcFile, srcContent);
|
|
1197
|
+
process.stdout.write(`Created ${srcFile}
|
|
1198
|
+
`);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
// src/display-helpers.ts
|
|
1203
|
+
init_esm_shims();
|
|
1204
|
+
function getDisplay(descriptor) {
|
|
1205
|
+
const metadata = descriptor.metadata ?? {};
|
|
1206
|
+
const display = metadata.display;
|
|
1207
|
+
if (display && typeof display === "object" && !Array.isArray(display)) {
|
|
1208
|
+
return display;
|
|
1209
|
+
}
|
|
1210
|
+
return {};
|
|
1211
|
+
}
|
|
1212
|
+
function getCliDisplayFields(descriptor) {
|
|
1213
|
+
const display = getDisplay(descriptor);
|
|
1214
|
+
const cli = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1215
|
+
const name = cli.alias ?? display.alias ?? descriptor.id;
|
|
1216
|
+
const desc = cli.description ?? descriptor.description;
|
|
1217
|
+
const tags = display.tags ?? descriptor.tags ?? [];
|
|
1218
|
+
return [name, desc, tags];
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1059
1221
|
// src/main.ts
|
|
1060
|
-
var __dirname2 =
|
|
1061
|
-
var pkg = JSON.parse(readFileSync(
|
|
1222
|
+
var __dirname2 = path5.dirname(fileURLToPath2(import.meta.url));
|
|
1223
|
+
var pkg = JSON.parse(readFileSync(path5.resolve(__dirname2, "../package.json"), "utf-8"));
|
|
1062
1224
|
var VERSION = pkg.version;
|
|
1063
1225
|
function createCli(extensionsDir, progName) {
|
|
1064
|
-
const resolvedProgName = progName ??
|
|
1226
|
+
const resolvedProgName = progName ?? path5.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
|
|
1065
1227
|
const cliLogLevel = process.env.APCORE_CLI_LOGGING_LEVEL ?? process.env.APCORE_LOGGING_LEVEL ?? "WARNING";
|
|
1066
1228
|
setLogLevel(cliLogLevel);
|
|
1067
|
-
const program = new Command(resolvedProgName).exitOverride().version(VERSION, "--version", `Show ${resolvedProgName} version`).description("apcore CLI \u2014 execute apcore modules from the command line").option("--extensions-dir <path>", "Path to extensions directory").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING");
|
|
1068
|
-
|
|
1229
|
+
const program = new Command(resolvedProgName).exitOverride().version(VERSION, "--version", `Show ${resolvedProgName} version`).description("apcore CLI \u2014 execute apcore modules from the command line").option("--extensions-dir <path>", "Path to extensions directory").option("--commands-dir <path>", "Path to convention-based commands directory").option("--binding <path>", "Path to binding.yaml for display overlay").option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING");
|
|
1230
|
+
const resolvedExtDir = extensionsDir ?? process.env.APCORE_EXTENSIONS_ROOT ?? "./extensions";
|
|
1231
|
+
void resolvedExtDir;
|
|
1232
|
+
registerInitCommand(program);
|
|
1233
|
+
program.hook("preAction", async (thisCommand) => {
|
|
1234
|
+
const opts = thisCommand.opts();
|
|
1235
|
+
const commandsDir = opts.commandsDir;
|
|
1236
|
+
const bindingPath = opts.binding;
|
|
1237
|
+
await applyToolkitIntegration(commandsDir, bindingPath);
|
|
1238
|
+
});
|
|
1069
1239
|
return program;
|
|
1070
1240
|
}
|
|
1241
|
+
async function applyToolkitIntegration(commandsDir, bindingPath) {
|
|
1242
|
+
if (!commandsDir && !bindingPath) {
|
|
1243
|
+
return;
|
|
1244
|
+
}
|
|
1245
|
+
try {
|
|
1246
|
+
const toolkitModule = "apcore-toolkit";
|
|
1247
|
+
const toolkit = await import(
|
|
1248
|
+
/* @vite-ignore */
|
|
1249
|
+
toolkitModule
|
|
1250
|
+
);
|
|
1251
|
+
if (commandsDir) {
|
|
1252
|
+
console.warn("Convention scanning not yet available in TypeScript toolkit");
|
|
1253
|
+
}
|
|
1254
|
+
if (bindingPath) {
|
|
1255
|
+
const resolver = new toolkit.DisplayResolver();
|
|
1256
|
+
void resolver;
|
|
1257
|
+
}
|
|
1258
|
+
} catch {
|
|
1259
|
+
console.warn("apcore-toolkit not installed \u2014 toolkit features unavailable");
|
|
1260
|
+
}
|
|
1261
|
+
}
|
|
1071
1262
|
function main(progName) {
|
|
1072
1263
|
const program = createCli(void 0, progName);
|
|
1073
1264
|
try {
|
|
@@ -1084,10 +1275,14 @@ function main(progName) {
|
|
|
1084
1275
|
process.exit(code);
|
|
1085
1276
|
}
|
|
1086
1277
|
}
|
|
1087
|
-
function buildModuleCommand(moduleDef, executor) {
|
|
1278
|
+
function buildModuleCommand(moduleDef, executor, helpTextMaxLength = 1e3, cmdName) {
|
|
1088
1279
|
const moduleId = moduleDef.id;
|
|
1089
1280
|
let resolvedSchema = {};
|
|
1090
1281
|
let schemaOptions = [];
|
|
1282
|
+
const display = getDisplay(moduleDef);
|
|
1283
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1284
|
+
const effectiveCmdName = cmdName ?? cliDisplay.alias ?? moduleId;
|
|
1285
|
+
const cmdHelp = cliDisplay.description ?? moduleDef.description;
|
|
1091
1286
|
const inputSchema = moduleDef.inputSchema;
|
|
1092
1287
|
if (inputSchema && typeof inputSchema === "object" && inputSchema.properties) {
|
|
1093
1288
|
try {
|
|
@@ -1095,9 +1290,9 @@ function buildModuleCommand(moduleDef, executor) {
|
|
|
1095
1290
|
} catch {
|
|
1096
1291
|
resolvedSchema = inputSchema;
|
|
1097
1292
|
}
|
|
1098
|
-
schemaOptions = schemaToCliOptions(resolvedSchema);
|
|
1293
|
+
schemaOptions = schemaToCliOptions(resolvedSchema, helpTextMaxLength);
|
|
1099
1294
|
}
|
|
1100
|
-
const cmd = new Command(
|
|
1295
|
+
const cmd = new Command(effectiveCmdName).description(cmdHelp);
|
|
1101
1296
|
cmd.option("--input <source>", "Read input from STDIN ('-')");
|
|
1102
1297
|
cmd.option("-y, --yes", "Bypass approval prompts", false);
|
|
1103
1298
|
cmd.option("--large-input", "Allow STDIN input larger than 10MB", false);
|
|
@@ -1258,51 +1453,270 @@ function reconvertEnumValues(kwargs, options) {
|
|
|
1258
1453
|
|
|
1259
1454
|
// src/cli.ts
|
|
1260
1455
|
init_esm_shims();
|
|
1456
|
+
import { Command as Command2 } from "commander";
|
|
1457
|
+
var BUILTIN_COMMANDS = ["completion", "describe", "exec", "init", "list", "man"];
|
|
1261
1458
|
var LazyModuleGroup = class {
|
|
1262
1459
|
registry;
|
|
1263
1460
|
executor;
|
|
1461
|
+
helpTextMaxLength;
|
|
1264
1462
|
commandCache = /* @__PURE__ */ new Map();
|
|
1265
|
-
|
|
1463
|
+
/** alias -> canonical module_id (populated lazily) */
|
|
1464
|
+
aliasMap = /* @__PURE__ */ new Map();
|
|
1465
|
+
/** module_id -> descriptor cache (populated during alias map build) */
|
|
1466
|
+
descriptorCache = /* @__PURE__ */ new Map();
|
|
1467
|
+
aliasMapBuilt = false;
|
|
1468
|
+
constructor(registry, executor, helpTextMaxLength = 1e3) {
|
|
1266
1469
|
this.registry = registry;
|
|
1267
1470
|
this.executor = executor;
|
|
1471
|
+
this.helpTextMaxLength = helpTextMaxLength;
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Build alias->module_id map from display overlay metadata.
|
|
1475
|
+
*/
|
|
1476
|
+
buildAliasMap() {
|
|
1477
|
+
if (this.aliasMapBuilt) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
try {
|
|
1481
|
+
for (const descriptor of this.registry.listModules()) {
|
|
1482
|
+
const moduleId = descriptor.id;
|
|
1483
|
+
this.descriptorCache.set(moduleId, descriptor);
|
|
1484
|
+
const display = getDisplay(descriptor);
|
|
1485
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1486
|
+
const cliAlias = cliDisplay.alias;
|
|
1487
|
+
if (cliAlias && cliAlias !== moduleId) {
|
|
1488
|
+
this.aliasMap.set(cliAlias, moduleId);
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
this.aliasMapBuilt = true;
|
|
1492
|
+
} catch {
|
|
1493
|
+
warn("Failed to build alias map from registry");
|
|
1494
|
+
}
|
|
1268
1495
|
}
|
|
1269
1496
|
/**
|
|
1270
1497
|
* List all available command names from the Registry.
|
|
1271
|
-
*
|
|
1272
|
-
* TODO: Implement registry enumeration.
|
|
1273
1498
|
*/
|
|
1274
1499
|
listCommands() {
|
|
1275
|
-
|
|
1500
|
+
this.buildAliasMap();
|
|
1501
|
+
const reverse = /* @__PURE__ */ new Map();
|
|
1502
|
+
for (const [alias, moduleId] of this.aliasMap) {
|
|
1503
|
+
reverse.set(moduleId, alias);
|
|
1504
|
+
}
|
|
1505
|
+
const moduleIds = this.registry.listModules().map((m) => m.id);
|
|
1506
|
+
const names = moduleIds.map((mid) => reverse.get(mid) ?? mid);
|
|
1507
|
+
return [...new Set(names)].sort();
|
|
1276
1508
|
}
|
|
1277
1509
|
/**
|
|
1278
1510
|
* Get or lazily build a Commander Command for the given module.
|
|
1279
|
-
*
|
|
1280
|
-
* TODO: Implement lazy command construction with schema-based options.
|
|
1281
1511
|
*/
|
|
1282
1512
|
getCommand(cmdName) {
|
|
1283
1513
|
if (this.commandCache.has(cmdName)) {
|
|
1284
1514
|
return this.commandCache.get(cmdName);
|
|
1285
1515
|
}
|
|
1286
|
-
|
|
1516
|
+
this.buildAliasMap();
|
|
1517
|
+
const moduleId = this.aliasMap.get(cmdName) ?? cmdName;
|
|
1518
|
+
let moduleDef = this.descriptorCache.get(moduleId);
|
|
1519
|
+
if (!moduleDef) {
|
|
1520
|
+
moduleDef = this.registry.getModule(moduleId) ?? void 0;
|
|
1521
|
+
}
|
|
1287
1522
|
if (!moduleDef) {
|
|
1288
1523
|
return null;
|
|
1289
1524
|
}
|
|
1290
|
-
const cmd = buildModuleCommand(moduleDef, this.executor);
|
|
1525
|
+
const cmd = buildModuleCommand(moduleDef, this.executor, this.helpTextMaxLength, cmdName);
|
|
1291
1526
|
this.commandCache.set(cmdName, cmd);
|
|
1292
1527
|
return cmd;
|
|
1293
1528
|
}
|
|
1294
1529
|
};
|
|
1530
|
+
var LazyGroup = class {
|
|
1531
|
+
members;
|
|
1532
|
+
_executor;
|
|
1533
|
+
_helpTextMaxLength;
|
|
1534
|
+
_cmdCache = /* @__PURE__ */ new Map();
|
|
1535
|
+
command;
|
|
1536
|
+
constructor(members, executor, name, helpTextMaxLength = 1e3) {
|
|
1537
|
+
this.members = members;
|
|
1538
|
+
this._executor = executor;
|
|
1539
|
+
this._helpTextMaxLength = helpTextMaxLength;
|
|
1540
|
+
this.command = new Command2(name).description(`${name} commands`);
|
|
1541
|
+
for (const [cmdName, [, descriptor]] of this.members) {
|
|
1542
|
+
const cmd = buildModuleCommand(
|
|
1543
|
+
descriptor,
|
|
1544
|
+
this._executor,
|
|
1545
|
+
this._helpTextMaxLength,
|
|
1546
|
+
cmdName
|
|
1547
|
+
);
|
|
1548
|
+
this._cmdCache.set(cmdName, cmd);
|
|
1549
|
+
this.command.addCommand(cmd);
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
listCommands() {
|
|
1553
|
+
return [...this.members.keys()].sort();
|
|
1554
|
+
}
|
|
1555
|
+
getCommand(cmdName) {
|
|
1556
|
+
if (this._cmdCache.has(cmdName)) {
|
|
1557
|
+
return this._cmdCache.get(cmdName);
|
|
1558
|
+
}
|
|
1559
|
+
const entry = this.members.get(cmdName);
|
|
1560
|
+
if (!entry) {
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
const [, descriptor] = entry;
|
|
1564
|
+
const cmd = buildModuleCommand(
|
|
1565
|
+
descriptor,
|
|
1566
|
+
this._executor,
|
|
1567
|
+
this._helpTextMaxLength,
|
|
1568
|
+
cmdName
|
|
1569
|
+
);
|
|
1570
|
+
this._cmdCache.set(cmdName, cmd);
|
|
1571
|
+
return cmd;
|
|
1572
|
+
}
|
|
1573
|
+
};
|
|
1574
|
+
var GroupedModuleGroup = class _GroupedModuleGroup extends LazyModuleGroup {
|
|
1575
|
+
/** groupName -> { cmdName -> [moduleId, descriptor] } */
|
|
1576
|
+
groupMap = /* @__PURE__ */ new Map();
|
|
1577
|
+
/** cmdName -> [moduleId, descriptor] for top-level (ungrouped) modules */
|
|
1578
|
+
topLevelModules = /* @__PURE__ */ new Map();
|
|
1579
|
+
/** Cached LazyGroup instances */
|
|
1580
|
+
groupCache = /* @__PURE__ */ new Map();
|
|
1581
|
+
groupMapBuilt = false;
|
|
1582
|
+
/**
|
|
1583
|
+
* Determine (groupName | null, commandName) for a module from its display overlay.
|
|
1584
|
+
*/
|
|
1585
|
+
static resolveGroup(moduleId, descriptor) {
|
|
1586
|
+
if (!moduleId) {
|
|
1587
|
+
warn("Empty module_id encountered in resolveGroup");
|
|
1588
|
+
return [null, ""];
|
|
1589
|
+
}
|
|
1590
|
+
const display = getDisplay(descriptor);
|
|
1591
|
+
const cliDisplay = display.cli && typeof display.cli === "object" && !Array.isArray(display.cli) ? display.cli : {};
|
|
1592
|
+
const explicitGroup = cliDisplay.group;
|
|
1593
|
+
if (typeof explicitGroup === "string" && explicitGroup !== "") {
|
|
1594
|
+
return [explicitGroup, cliDisplay.alias ?? moduleId];
|
|
1595
|
+
}
|
|
1596
|
+
if (explicitGroup === "") {
|
|
1597
|
+
return [null, cliDisplay.alias ?? moduleId];
|
|
1598
|
+
}
|
|
1599
|
+
const cliName = cliDisplay.alias ?? moduleId;
|
|
1600
|
+
if (cliName.includes(".")) {
|
|
1601
|
+
const dotIdx = cliName.indexOf(".");
|
|
1602
|
+
const group = cliName.substring(0, dotIdx);
|
|
1603
|
+
const cmd = cliName.substring(dotIdx + 1);
|
|
1604
|
+
return [group, cmd];
|
|
1605
|
+
}
|
|
1606
|
+
return [null, cliName];
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Build the group map from registry modules.
|
|
1610
|
+
*/
|
|
1611
|
+
buildGroupMap() {
|
|
1612
|
+
if (this.groupMapBuilt) {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
try {
|
|
1616
|
+
this.buildAliasMap();
|
|
1617
|
+
for (const descriptor of this.registry.listModules()) {
|
|
1618
|
+
const moduleId = descriptor.id;
|
|
1619
|
+
const cached = this.descriptorCache.get(moduleId);
|
|
1620
|
+
if (!cached) {
|
|
1621
|
+
continue;
|
|
1622
|
+
}
|
|
1623
|
+
const [group, cmd] = _GroupedModuleGroup.resolveGroup(moduleId, cached);
|
|
1624
|
+
if (group === null) {
|
|
1625
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
1626
|
+
} else if (!/^[a-z][a-z0-9_-]*$/.test(group)) {
|
|
1627
|
+
warn(
|
|
1628
|
+
`Module '${moduleId}': group name '${group}' is not shell-safe \u2014 treating as top-level.`
|
|
1629
|
+
);
|
|
1630
|
+
this.topLevelModules.set(cmd, [moduleId, cached]);
|
|
1631
|
+
} else {
|
|
1632
|
+
if (!this.groupMap.has(group)) {
|
|
1633
|
+
this.groupMap.set(group, /* @__PURE__ */ new Map());
|
|
1634
|
+
}
|
|
1635
|
+
this.groupMap.get(group).set(cmd, [moduleId, cached]);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
for (const groupName of this.groupMap.keys()) {
|
|
1639
|
+
if (BUILTIN_COMMANDS.includes(groupName)) {
|
|
1640
|
+
warn(
|
|
1641
|
+
`Group name '${groupName}' collides with a built-in command and will be ignored`
|
|
1642
|
+
);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
this.groupMapBuilt = true;
|
|
1646
|
+
} catch {
|
|
1647
|
+
warn("Failed to build group map");
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* List all available command names: builtins + group names + top-level module names.
|
|
1652
|
+
*/
|
|
1653
|
+
listCommands() {
|
|
1654
|
+
this.buildGroupMap();
|
|
1655
|
+
const groupNames = [...this.groupMap.keys()].filter(
|
|
1656
|
+
(g) => !BUILTIN_COMMANDS.includes(g)
|
|
1657
|
+
);
|
|
1658
|
+
const topNames = [...this.topLevelModules.keys()];
|
|
1659
|
+
return [.../* @__PURE__ */ new Set([...BUILTIN_COMMANDS, ...groupNames, ...topNames])].sort();
|
|
1660
|
+
}
|
|
1661
|
+
/**
|
|
1662
|
+
* Get a command by name: check builtins -> group cache -> group map -> top-level modules.
|
|
1663
|
+
*/
|
|
1664
|
+
getCommand(cmdName) {
|
|
1665
|
+
this.buildGroupMap();
|
|
1666
|
+
if (this.groupCache.has(cmdName)) {
|
|
1667
|
+
return this.groupCache.get(cmdName).command;
|
|
1668
|
+
}
|
|
1669
|
+
if (this.groupMap.has(cmdName)) {
|
|
1670
|
+
const lazyGrp = new LazyGroup(
|
|
1671
|
+
this.groupMap.get(cmdName),
|
|
1672
|
+
this.executor,
|
|
1673
|
+
cmdName,
|
|
1674
|
+
this.helpTextMaxLength
|
|
1675
|
+
);
|
|
1676
|
+
this.groupCache.set(cmdName, lazyGrp);
|
|
1677
|
+
return lazyGrp.command;
|
|
1678
|
+
}
|
|
1679
|
+
if (this.topLevelModules.has(cmdName)) {
|
|
1680
|
+
if (this.commandCache.has(cmdName)) {
|
|
1681
|
+
return this.commandCache.get(cmdName);
|
|
1682
|
+
}
|
|
1683
|
+
const [, descriptor] = this.topLevelModules.get(cmdName);
|
|
1684
|
+
const cmd = buildModuleCommand(
|
|
1685
|
+
descriptor,
|
|
1686
|
+
this.executor,
|
|
1687
|
+
this.helpTextMaxLength,
|
|
1688
|
+
cmdName
|
|
1689
|
+
);
|
|
1690
|
+
this.commandCache.set(cmdName, cmd);
|
|
1691
|
+
return cmd;
|
|
1692
|
+
}
|
|
1693
|
+
return null;
|
|
1694
|
+
}
|
|
1695
|
+
/** Expose groupMap for testing. */
|
|
1696
|
+
getGroupMap() {
|
|
1697
|
+
return this.groupMap;
|
|
1698
|
+
}
|
|
1699
|
+
/** Expose topLevelModules for testing. */
|
|
1700
|
+
getTopLevelModules() {
|
|
1701
|
+
return this.topLevelModules;
|
|
1702
|
+
}
|
|
1703
|
+
/** Expose groupMapBuilt for testing. */
|
|
1704
|
+
isGroupMapBuilt() {
|
|
1705
|
+
return this.groupMapBuilt;
|
|
1706
|
+
}
|
|
1707
|
+
};
|
|
1295
1708
|
|
|
1296
1709
|
// src/config.ts
|
|
1297
1710
|
init_esm_shims();
|
|
1298
|
-
import * as
|
|
1711
|
+
import * as fs4 from "fs";
|
|
1299
1712
|
import yaml from "js-yaml";
|
|
1300
1713
|
var DEFAULTS = {
|
|
1301
1714
|
"extensions.root": "./extensions",
|
|
1302
1715
|
"logging.level": "WARNING",
|
|
1303
1716
|
"sandbox.enabled": false,
|
|
1304
1717
|
"cli.stdin_buffer_limit": 10485760,
|
|
1305
|
-
"cli.auto_approve": false
|
|
1718
|
+
"cli.auto_approve": false,
|
|
1719
|
+
"cli.help_text_max_length": 1e3
|
|
1306
1720
|
};
|
|
1307
1721
|
var ConfigResolver = class {
|
|
1308
1722
|
cliFlags;
|
|
@@ -1355,7 +1769,7 @@ var ConfigResolver = class {
|
|
|
1355
1769
|
loadConfigFile() {
|
|
1356
1770
|
let content;
|
|
1357
1771
|
try {
|
|
1358
|
-
content =
|
|
1772
|
+
content = fs4.readFileSync(this.configPath, "utf-8");
|
|
1359
1773
|
} catch (err) {
|
|
1360
1774
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
1361
1775
|
return null;
|
|
@@ -1405,7 +1819,7 @@ var ConfigResolver = class {
|
|
|
1405
1819
|
// src/discovery.ts
|
|
1406
1820
|
init_esm_shims();
|
|
1407
1821
|
init_errors();
|
|
1408
|
-
import { Command as
|
|
1822
|
+
import { Command as Command3 } from "commander";
|
|
1409
1823
|
var TAG_PATTERN = /^[a-z][a-z0-9_-]*$/;
|
|
1410
1824
|
function validateTag(tag) {
|
|
1411
1825
|
if (!TAG_PATTERN.test(tag)) {
|
|
@@ -1420,7 +1834,7 @@ function collectTag(value, previous) {
|
|
|
1420
1834
|
return previous.concat([value]);
|
|
1421
1835
|
}
|
|
1422
1836
|
function registerDiscoveryCommands(cli, registry) {
|
|
1423
|
-
const listCmd = new
|
|
1837
|
+
const listCmd = new Command3("list").description("List available modules in the registry.").option("--tag <tag>", "Filter modules by tag (AND logic). Repeatable.", collectTag, []).option("--format <format>", "Output format.", void 0).action((opts) => {
|
|
1424
1838
|
for (const t of opts.tag) {
|
|
1425
1839
|
validateTag(t);
|
|
1426
1840
|
}
|
|
@@ -1440,7 +1854,7 @@ function registerDiscoveryCommands(cli, registry) {
|
|
|
1440
1854
|
formatModuleList(filtered, fmt, opts.tag.length > 0 ? opts.tag : void 0);
|
|
1441
1855
|
});
|
|
1442
1856
|
cli.addCommand(listCmd);
|
|
1443
|
-
const describeCmd = new
|
|
1857
|
+
const describeCmd = new Command3("describe").description("Show metadata, schema, and annotations for a module.").argument("<module-id>", "Module ID to describe").option("--format <format>", "Output format.", void 0).action((moduleId, opts) => {
|
|
1444
1858
|
validateModuleId(moduleId);
|
|
1445
1859
|
const moduleDef = registry.getModule(moduleId);
|
|
1446
1860
|
if (!moduleDef) {
|
|
@@ -1461,10 +1875,10 @@ init_esm_shims();
|
|
|
1461
1875
|
init_errors();
|
|
1462
1876
|
import { readFileSync as readFileSync3 } from "fs";
|
|
1463
1877
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
1464
|
-
import * as
|
|
1465
|
-
import { Command as
|
|
1466
|
-
var __dirname3 =
|
|
1467
|
-
var pkg2 = JSON.parse(readFileSync3(
|
|
1878
|
+
import * as path6 from "path";
|
|
1879
|
+
import { Command as Command4 } from "commander";
|
|
1880
|
+
var __dirname3 = path6.dirname(fileURLToPath3(import.meta.url));
|
|
1881
|
+
var pkg2 = JSON.parse(readFileSync3(path6.resolve(__dirname3, "../package.json"), "utf-8"));
|
|
1468
1882
|
var SHELL_VERSION = pkg2.version;
|
|
1469
1883
|
function makeFunctionName(progName) {
|
|
1470
1884
|
return "_" + progName.replace(/[^a-zA-Z0-9]/g, "_");
|
|
@@ -1476,6 +1890,18 @@ function generateBashCompletion(progName) {
|
|
|
1476
1890
|
const fn = makeFunctionName(progName);
|
|
1477
1891
|
const quoted = shellQuote(progName);
|
|
1478
1892
|
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1893
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1894
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1895
|
+
const ids=JSON.parse(m).map(x=>x.id);
|
|
1896
|
+
const g=new Set(),t=[];
|
|
1897
|
+
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1898
|
+
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1899
|
+
" 2>/dev/null`;
|
|
1900
|
+
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1901
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1902
|
+
const g=process.env._APCORE_GRP;
|
|
1903
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1904
|
+
" 2>/dev/null`;
|
|
1479
1905
|
return `${fn}() {
|
|
1480
1906
|
local cur prev opts
|
|
1481
1907
|
COMPREPLY=()
|
|
@@ -1483,14 +1909,21 @@ function generateBashCompletion(progName) {
|
|
|
1483
1909
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1484
1910
|
|
|
1485
1911
|
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
1486
|
-
opts="
|
|
1487
|
-
|
|
1912
|
+
opts="completion describe exec init list man"
|
|
1913
|
+
local groups_and_top=$(${groupsAndTopCmd})
|
|
1914
|
+
COMPREPLY=( $(compgen -W "\${opts} \${groups_and_top}" -- \${cur}) )
|
|
1488
1915
|
return 0
|
|
1489
1916
|
fi
|
|
1490
1917
|
|
|
1491
|
-
if [[
|
|
1492
|
-
|
|
1493
|
-
|
|
1918
|
+
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
1919
|
+
if [[ "\${COMP_WORDS[1]}" == "exec" ]]; then
|
|
1920
|
+
local modules=$(${moduleListCmd})
|
|
1921
|
+
COMPREPLY=( $(compgen -W "\${modules}" -- \${cur}) )
|
|
1922
|
+
return 0
|
|
1923
|
+
fi
|
|
1924
|
+
export _APCORE_GRP="\${COMP_WORDS[1]}"
|
|
1925
|
+
local group_cmds=$(${groupCmdsCmd})
|
|
1926
|
+
COMPREPLY=( $(compgen -W "\${group_cmds}" -- \${cur}) )
|
|
1494
1927
|
return 0
|
|
1495
1928
|
fi
|
|
1496
1929
|
}
|
|
@@ -1501,6 +1934,18 @@ function generateZshCompletion(progName) {
|
|
|
1501
1934
|
const fn = makeFunctionName(progName);
|
|
1502
1935
|
const quoted = shellQuote(progName);
|
|
1503
1936
|
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e "process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})" 2>/dev/null`;
|
|
1937
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1938
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1939
|
+
const ids=JSON.parse(m).map(x=>x.id);
|
|
1940
|
+
const g=new Set(),t=[];
|
|
1941
|
+
ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});
|
|
1942
|
+
console.log([...g].sort().concat(t.sort()).join(' '))
|
|
1943
|
+
" 2>/dev/null`;
|
|
1944
|
+
const groupCmdsCmd = `${quoted} list --format json 2>/dev/null | node -e "
|
|
1945
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
1946
|
+
const g=process.env._APCORE_GRP;
|
|
1947
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
1948
|
+
" 2>/dev/null`;
|
|
1504
1949
|
return `#compdef ${progName}
|
|
1505
1950
|
|
|
1506
1951
|
${fn}() {
|
|
@@ -1509,6 +1954,7 @@ ${fn}() {
|
|
|
1509
1954
|
'list:List available modules'
|
|
1510
1955
|
'describe:Show module metadata and schema'
|
|
1511
1956
|
'completion:Generate shell completion script'
|
|
1957
|
+
'init:Scaffolding commands'
|
|
1512
1958
|
'man:Generate man page'
|
|
1513
1959
|
)
|
|
1514
1960
|
|
|
@@ -1519,6 +1965,9 @@ ${fn}() {
|
|
|
1519
1965
|
case "$state" in
|
|
1520
1966
|
command)
|
|
1521
1967
|
_describe -t commands '${progName} commands' commands
|
|
1968
|
+
local -a groups_and_top
|
|
1969
|
+
groups_and_top=($(${groupsAndTopCmd}))
|
|
1970
|
+
compadd -a groups_and_top
|
|
1522
1971
|
;;
|
|
1523
1972
|
args)
|
|
1524
1973
|
case "\${words[1]}" in
|
|
@@ -1527,6 +1976,12 @@ ${fn}() {
|
|
|
1527
1976
|
modules=($(${moduleListCmd}))
|
|
1528
1977
|
compadd -a modules
|
|
1529
1978
|
;;
|
|
1979
|
+
*)
|
|
1980
|
+
export _APCORE_GRP="\${words[1]}"
|
|
1981
|
+
local -a group_cmds
|
|
1982
|
+
group_cmds=($(${groupCmdsCmd}))
|
|
1983
|
+
compadd -a group_cmds
|
|
1984
|
+
;;
|
|
1530
1985
|
esac
|
|
1531
1986
|
;;
|
|
1532
1987
|
esac
|
|
@@ -1538,13 +1993,29 @@ compdef ${fn} ${quoted}
|
|
|
1538
1993
|
function generateFishCompletion(progName) {
|
|
1539
1994
|
const quoted = shellQuote(progName);
|
|
1540
1995
|
const moduleListCmd = `${quoted} list --format json 2>/dev/null | node -e \\"process.stdin.on('data',d=>{JSON.parse(d).forEach(m=>console.log(m.id))})\\" 2>/dev/null`;
|
|
1996
|
+
const groupsAndTopCmd = `${quoted} list --format json 2>/dev/null | node -e \\"const m=require('fs').readFileSync('/dev/stdin','utf8');const ids=JSON.parse(m).map(x=>x.id);const g=new Set(),t=[];ids.forEach(i=>{if(i.includes('.'))g.add(i.split('.')[0]);else t.push(i)});console.log([...g].sort().concat(t.sort()).join('\\\\n'))\\" 2>/dev/null`;
|
|
1541
1997
|
return `# Fish completions for ${progName}
|
|
1542
1998
|
complete -c ${quoted} -n "__fish_use_subcommand" -a list -d "List available modules"
|
|
1543
1999
|
complete -c ${quoted} -n "__fish_use_subcommand" -a describe -d "Show module metadata and schema"
|
|
1544
2000
|
complete -c ${quoted} -n "__fish_use_subcommand" -a completion -d "Generate shell completion script"
|
|
2001
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a init -d "Scaffolding commands"
|
|
1545
2002
|
complete -c ${quoted} -n "__fish_use_subcommand" -a man -d "Generate man page"
|
|
2003
|
+
complete -c ${quoted} -n "__fish_use_subcommand" -a "(${groupsAndTopCmd})" -d "Module group"
|
|
1546
2004
|
|
|
1547
2005
|
complete -c ${quoted} -n "__fish_seen_subcommand_from exec" -a "(${moduleListCmd})"
|
|
2006
|
+
|
|
2007
|
+
function __apcore_group_cmds
|
|
2008
|
+
set -l grp (commandline -opc)[2]
|
|
2009
|
+
set -x _APCORE_GRP $grp
|
|
2010
|
+
${quoted} list --format json 2>/dev/null | node -e "
|
|
2011
|
+
const m=require('fs').readFileSync('/dev/stdin','utf8');
|
|
2012
|
+
const g=process.env._APCORE_GRP;
|
|
2013
|
+
JSON.parse(m).map(x=>x.id).filter(i=>i.includes('.')&&i.split('.')[0]===g).forEach(i=>console.log(i.split('.',2)[1]))
|
|
2014
|
+
" 2>/dev/null
|
|
2015
|
+
end
|
|
2016
|
+
|
|
2017
|
+
# Group subcommand completion \u2014 matches when position 1 is not a builtin
|
|
2018
|
+
complete -c ${quoted} -n "not __fish_use_subcommand; and not __fish_seen_subcommand_from list describe completion init man exec" -a "(__apcore_group_cmds)"
|
|
1548
2019
|
`;
|
|
1549
2020
|
}
|
|
1550
2021
|
function buildSynopsis(command, progName, commandName) {
|
|
@@ -1632,6 +2103,11 @@ function generateManPage(commandName, command, progName, version = SHELL_VERSION
|
|
|
1632
2103
|
sections.push(
|
|
1633
2104
|
"Global apcore logging verbosity. One of: DEBUG, INFO, WARNING, ERROR. Used as fallback when \\fBAPCORE_CLI_LOGGING_LEVEL\\fR is not set. Default: WARNING."
|
|
1634
2105
|
);
|
|
2106
|
+
sections.push(".TP");
|
|
2107
|
+
sections.push("\\fBAPCORE_AUTH_API_KEY\\fR");
|
|
2108
|
+
sections.push(
|
|
2109
|
+
"API key for authenticating with the apcore registry."
|
|
2110
|
+
);
|
|
1635
2111
|
sections.push(".SH EXIT CODES");
|
|
1636
2112
|
const exitCodes = [
|
|
1637
2113
|
["0", "Success."],
|
|
@@ -1668,7 +2144,7 @@ ${meaning}`);
|
|
|
1668
2144
|
return sections.join("\n");
|
|
1669
2145
|
}
|
|
1670
2146
|
function registerShellCommands(cli, progName = "apcore-cli") {
|
|
1671
|
-
const completionCmd = new
|
|
2147
|
+
const completionCmd = new Command4("completion").description(
|
|
1672
2148
|
"Generate a shell completion script and print it to stdout."
|
|
1673
2149
|
).argument("<shell>", "Shell type: bash, zsh, or fish").action((shell) => {
|
|
1674
2150
|
const validShells = ["bash", "zsh", "fish"];
|
|
@@ -1688,8 +2164,8 @@ function registerShellCommands(cli, progName = "apcore-cli") {
|
|
|
1688
2164
|
process.stdout.write(generators[shell]());
|
|
1689
2165
|
});
|
|
1690
2166
|
cli.addCommand(completionCmd);
|
|
1691
|
-
const manCmd = new
|
|
1692
|
-
const knownBuiltins = /* @__PURE__ */ new Set(["
|
|
2167
|
+
const manCmd = new Command4("man").description("Generate a roff man page for COMMAND and print it to stdout.").argument("<command>", "Command to generate man page for").action((commandName) => {
|
|
2168
|
+
const knownBuiltins = /* @__PURE__ */ new Set(["completion", "describe", "exec", "init", "list", "man"]);
|
|
1693
2169
|
const cmd = cli.commands.find((c) => c.name() === commandName) ?? null;
|
|
1694
2170
|
if (!cmd && !knownBuiltins.has(commandName)) {
|
|
1695
2171
|
process.stderr.write(
|
|
@@ -1714,16 +2190,20 @@ export {
|
|
|
1714
2190
|
AuditLogger,
|
|
1715
2191
|
AuthProvider,
|
|
1716
2192
|
AuthenticationError,
|
|
2193
|
+
BUILTIN_COMMANDS,
|
|
1717
2194
|
ConfigDecryptionError,
|
|
1718
2195
|
ConfigEncryptor,
|
|
1719
2196
|
ConfigResolver,
|
|
1720
2197
|
DEFAULTS,
|
|
1721
2198
|
EXIT_CODES,
|
|
2199
|
+
GroupedModuleGroup,
|
|
2200
|
+
LazyGroup,
|
|
1722
2201
|
LazyModuleGroup,
|
|
1723
2202
|
ModuleExecutionError,
|
|
1724
2203
|
ModuleNotFoundError,
|
|
1725
2204
|
Sandbox,
|
|
1726
2205
|
SchemaValidationError,
|
|
2206
|
+
applyToolkitIntegration,
|
|
1727
2207
|
buildModuleCommand,
|
|
1728
2208
|
checkApproval,
|
|
1729
2209
|
collectInput,
|
|
@@ -1736,12 +2216,15 @@ export {
|
|
|
1736
2216
|
formatModuleDetail,
|
|
1737
2217
|
formatModuleList,
|
|
1738
2218
|
getAuditLogger,
|
|
2219
|
+
getCliDisplayFields,
|
|
2220
|
+
getDisplay,
|
|
1739
2221
|
getLogLevel,
|
|
1740
2222
|
info,
|
|
1741
2223
|
main,
|
|
1742
2224
|
mapType,
|
|
1743
2225
|
reconvertEnumValues,
|
|
1744
2226
|
registerDiscoveryCommands,
|
|
2227
|
+
registerInitCommand,
|
|
1745
2228
|
registerShellCommands,
|
|
1746
2229
|
resolveFormat,
|
|
1747
2230
|
resolveRefs,
|