@openwop/cli 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +33 -70
- package/dist/openwop.js +33 -70
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -578,7 +578,7 @@ function errText(err) {
|
|
|
578
578
|
// package.json
|
|
579
579
|
var package_default = {
|
|
580
580
|
name: "@openwop/cli",
|
|
581
|
-
version: "1.2.
|
|
581
|
+
version: "1.2.3",
|
|
582
582
|
description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
|
|
583
583
|
license: "Apache-2.0",
|
|
584
584
|
type: "module",
|
|
@@ -1265,9 +1265,9 @@ function streamBaseFrom(extensions, baseUrl) {
|
|
|
1265
1265
|
continue;
|
|
1266
1266
|
}
|
|
1267
1267
|
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") continue;
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if (
|
|
1268
|
+
if (isLoopback(base16)) {
|
|
1269
|
+
if (!isLoopback(url)) continue;
|
|
1270
|
+
} else if (url.protocol !== "https:" || base16.protocol !== "https:") continue;
|
|
1271
1271
|
return raw.replace(/\/+$/, "");
|
|
1272
1272
|
}
|
|
1273
1273
|
return void 0;
|
|
@@ -2004,7 +2004,7 @@ function formatCheckTable(checks) {
|
|
|
2004
2004
|
|
|
2005
2005
|
// src/cli/routeKit.ts
|
|
2006
2006
|
var errText2 = (err) => err instanceof Error ? err.message : String(err);
|
|
2007
|
-
var
|
|
2007
|
+
var MSG = {
|
|
2008
2008
|
required: (flag, usage6) => `${usage6}
|
|
2009
2009
|
${flag} is required.`,
|
|
2010
2010
|
refusal: (words, positionals) => `Refusing to ${words.join(" ")} ${positionals.join(" ")} without --yes.`.replace(/\s+/g, " ").trim(),
|
|
@@ -2083,14 +2083,14 @@ function setPath(target, key, value) {
|
|
|
2083
2083
|
}
|
|
2084
2084
|
node[parts[parts.length - 1]] = value;
|
|
2085
2085
|
}
|
|
2086
|
-
function coerce(f, raw
|
|
2086
|
+
function coerce(f, raw) {
|
|
2087
2087
|
const t = f.type ?? "string";
|
|
2088
2088
|
if (t === "boolean") return raw;
|
|
2089
2089
|
if (t === "bool") {
|
|
2090
2090
|
const s2 = String(raw);
|
|
2091
2091
|
if (s2 === "true" || s2 === "yes" || s2 === "1") return true;
|
|
2092
2092
|
if (s2 === "false" || s2 === "no" || s2 === "0") return false;
|
|
2093
|
-
throw new CliError(
|
|
2093
|
+
throw new CliError(MSG.invalidBool(f.flag, s2), 2);
|
|
2094
2094
|
}
|
|
2095
2095
|
if (t === "list") return Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
2096
2096
|
if (t === "map") {
|
|
@@ -2107,7 +2107,7 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2107
2107
|
const s = String(raw);
|
|
2108
2108
|
if (t === "number") {
|
|
2109
2109
|
const n = Number(s);
|
|
2110
|
-
if (
|
|
2110
|
+
if (s.trim() === "" || !Number.isFinite(n)) throw new CliError(MSG.invalidNumber(f.flag, s), 2);
|
|
2111
2111
|
return n;
|
|
2112
2112
|
}
|
|
2113
2113
|
if (t === "json-file") {
|
|
@@ -2115,7 +2115,7 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2115
2115
|
try {
|
|
2116
2116
|
text = readFileSync4(s, "utf8");
|
|
2117
2117
|
} catch (err) {
|
|
2118
|
-
throw new CliError(
|
|
2118
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2119
2119
|
}
|
|
2120
2120
|
try {
|
|
2121
2121
|
return JSON.parse(text);
|
|
@@ -2127,14 +2127,14 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2127
2127
|
try {
|
|
2128
2128
|
return readFileSync4(s).toString("base64");
|
|
2129
2129
|
} catch (err) {
|
|
2130
|
-
throw new CliError(
|
|
2130
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2131
2131
|
}
|
|
2132
2132
|
}
|
|
2133
2133
|
if (t === "file") {
|
|
2134
2134
|
try {
|
|
2135
2135
|
return readFileSync4(s, "utf8").replace(/\r?\n$/, "");
|
|
2136
2136
|
} catch (err) {
|
|
2137
|
-
throw new CliError(
|
|
2137
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2138
2138
|
}
|
|
2139
2139
|
}
|
|
2140
2140
|
if (t === "csv") return s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
@@ -2142,19 +2142,19 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2142
2142
|
try {
|
|
2143
2143
|
return JSON.parse(s);
|
|
2144
2144
|
} catch {
|
|
2145
|
-
throw new CliError(
|
|
2145
|
+
throw new CliError(MSG.invalidJson(f.flag), 2);
|
|
2146
2146
|
}
|
|
2147
2147
|
}
|
|
2148
2148
|
return s;
|
|
2149
2149
|
}
|
|
2150
|
-
function readBodyOptions(options
|
|
2150
|
+
function readBodyOptions(options) {
|
|
2151
2151
|
let raw;
|
|
2152
2152
|
if (options.bodyFile !== void 0) {
|
|
2153
2153
|
if (String(options.bodyFile).trim() === "") throw new CliError("--body-file needs a path (got an empty value).", 2);
|
|
2154
2154
|
try {
|
|
2155
2155
|
raw = readFileSync4(String(options.bodyFile), "utf8");
|
|
2156
2156
|
} catch (err) {
|
|
2157
|
-
throw new CliError(
|
|
2157
|
+
throw new CliError(MSG.unreadableBody(String(options.bodyFile), err), 2);
|
|
2158
2158
|
}
|
|
2159
2159
|
} else if (options.body !== void 0) {
|
|
2160
2160
|
raw = String(options.body);
|
|
@@ -2164,9 +2164,9 @@ function readBodyOptions(options, msg) {
|
|
|
2164
2164
|
try {
|
|
2165
2165
|
parsed = JSON.parse(raw);
|
|
2166
2166
|
} catch {
|
|
2167
|
-
throw new CliError(
|
|
2167
|
+
throw new CliError(MSG.invalidBodyJson, 2);
|
|
2168
2168
|
}
|
|
2169
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(
|
|
2169
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(MSG.bodyNotObject, 2);
|
|
2170
2170
|
return parsed;
|
|
2171
2171
|
}
|
|
2172
2172
|
function hostError(err) {
|
|
@@ -2177,7 +2177,6 @@ function hostError(err) {
|
|
|
2177
2177
|
${hint}` : line, code);
|
|
2178
2178
|
}
|
|
2179
2179
|
async function runRoute(ctx, group, cmd, argv) {
|
|
2180
|
-
const msg = { ...DEFAULT_MESSAGES, ...cmd.messages ?? {} };
|
|
2181
2180
|
const rest = argv.slice(cmd.words.length);
|
|
2182
2181
|
const fields = [...cmd.query ?? [], ...cmd.body ?? []];
|
|
2183
2182
|
const bool = [...COMMON_BOOL, ...fields.filter((f) => f.type === "boolean").flatMap((f) => [f.flag, f.flag.replace(/^--/, "--no-")])];
|
|
@@ -2190,8 +2189,7 @@ async function runRoute(ctx, group, cmd, argv) {
|
|
|
2190
2189
|
const multi = fields.filter(isMulti).map((f) => f.flag);
|
|
2191
2190
|
const { options, positionals } = parseOptions(rest, { bool, value, multi });
|
|
2192
2191
|
if (options.help) {
|
|
2193
|
-
|
|
2194
|
-
else writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
2192
|
+
writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
2195
2193
|
|
|
2196
2194
|
${cmd.summary}
|
|
2197
2195
|
Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
@@ -2199,12 +2197,12 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2199
2197
|
}
|
|
2200
2198
|
const template = templates(cmd).find((p) => positionalNames(cmd, p).length === positionals.length);
|
|
2201
2199
|
if (!template) {
|
|
2202
|
-
write(ctx.io.stderr,
|
|
2200
|
+
write(ctx.io.stderr, `Usage: ${usageLine(group, cmd)}
|
|
2203
2201
|
`);
|
|
2204
2202
|
return 2;
|
|
2205
2203
|
}
|
|
2206
2204
|
const path = fillPath(cmd, template, positionals, options.org);
|
|
2207
|
-
const requiredError = (f) => new CliError(
|
|
2205
|
+
const requiredError = (f) => new CliError(MSG.required(f.flag, usageLine(group, cmd)), 2);
|
|
2208
2206
|
const optionOf = (f) => {
|
|
2209
2207
|
const name = f.flag.replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2210
2208
|
if (f.type === "boolean") {
|
|
@@ -2222,7 +2220,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2222
2220
|
if (f.required) throw requiredError(f);
|
|
2223
2221
|
continue;
|
|
2224
2222
|
}
|
|
2225
|
-
const c = coerce(f, v
|
|
2223
|
+
const c = coerce(f, v);
|
|
2226
2224
|
if (f.type === "csv") qs2.set(f.key, c.join(","));
|
|
2227
2225
|
else if (Array.isArray(c)) c.forEach((x) => qs2.append(f.key, String(x)));
|
|
2228
2226
|
else qs2.set(f.key, c !== null && typeof c === "object" ? JSON.stringify(c) : String(c));
|
|
@@ -2230,7 +2228,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2230
2228
|
const url = qs2.size ? `${path}${path.includes("?") ? "&" : "?"}${qs2.toString()}` : path;
|
|
2231
2229
|
const confirm2 = cmd.confirm ?? cmd.method === "DELETE";
|
|
2232
2230
|
const gate = () => {
|
|
2233
|
-
if (confirm2 && !options.yes) throw new CliError(
|
|
2231
|
+
if (confirm2 && !options.yes) throw new CliError(MSG.refusal(cmd.words, positionals), 2);
|
|
2234
2232
|
};
|
|
2235
2233
|
if (!cmd.validateFirst) gate();
|
|
2236
2234
|
const isWrite = cmd.method !== "GET" && cmd.method !== "DELETE";
|
|
@@ -2238,10 +2236,10 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2238
2236
|
let explicit = {};
|
|
2239
2237
|
const overlay = {};
|
|
2240
2238
|
if (takesBody) {
|
|
2241
|
-
explicit = readBodyOptions(options
|
|
2239
|
+
explicit = readBodyOptions(options);
|
|
2242
2240
|
for (const f of cmd.body ?? []) {
|
|
2243
2241
|
const v = optionOf(f);
|
|
2244
|
-
if (v !== void 0) setPath(overlay, f.key, coerce(f, v
|
|
2242
|
+
if (v !== void 0) setPath(overlay, f.key, coerce(f, v));
|
|
2245
2243
|
else if (cmd.validateFirst && f.required && getPath(explicit, f.key) === void 0) throw requiredError(f);
|
|
2246
2244
|
}
|
|
2247
2245
|
}
|
|
@@ -2278,7 +2276,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2278
2276
|
render(ctx, { ...cmd, method: "GET" }, positionals, err.status, err.body);
|
|
2279
2277
|
return 1;
|
|
2280
2278
|
}
|
|
2281
|
-
if (err instanceof HttpError
|
|
2279
|
+
if (err instanceof HttpError) throw hostError(err);
|
|
2282
2280
|
throw err;
|
|
2283
2281
|
}
|
|
2284
2282
|
}
|
|
@@ -2289,7 +2287,7 @@ async function send(ctx, cmd, url, init) {
|
|
|
2289
2287
|
try {
|
|
2290
2288
|
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2291
2289
|
} catch (err) {
|
|
2292
|
-
if (err instanceof HttpError
|
|
2290
|
+
if (err instanceof HttpError) throw hostError(err);
|
|
2293
2291
|
throw err;
|
|
2294
2292
|
}
|
|
2295
2293
|
}
|
|
@@ -2339,7 +2337,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2339
2337
|
return;
|
|
2340
2338
|
}
|
|
2341
2339
|
}
|
|
2342
|
-
if (cmd.method === "GET"
|
|
2340
|
+
if (cmd.method === "GET") {
|
|
2343
2341
|
writeJson(ctx.io.stdout, body);
|
|
2344
2342
|
return;
|
|
2345
2343
|
}
|
|
@@ -6311,25 +6309,8 @@ function parseField(spec) {
|
|
|
6311
6309
|
const flag = `--${flagOverride ?? key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace(/_/g, "-")}`;
|
|
6312
6310
|
return { key, flag, type, required };
|
|
6313
6311
|
}
|
|
6314
|
-
function hasOrg(route) {
|
|
6315
|
-
return /:org(?![A-Za-z0-9_])/.test(route);
|
|
6316
|
-
}
|
|
6317
|
-
function paramsOf(route) {
|
|
6318
|
-
return (route.match(/:[A-Za-z][A-Za-z0-9_]*/g) ?? []).map((p) => p.slice(1)).filter((p) => p !== "org");
|
|
6319
|
-
}
|
|
6320
6312
|
function usageLine2(group, spec) {
|
|
6321
|
-
|
|
6322
|
-
if (hasOrg(spec.route)) parts.push("--org <orgId>");
|
|
6323
|
-
const fmt = (f) => {
|
|
6324
|
-
const v = f.type === "bool" ? "true|false" : f.type === "json" ? "json" : f.type === "list" ? "a,b" : f.type === "number" ? "n" : f.type === "file" ? "path" : "v";
|
|
6325
|
-
return f.required ? `${f.flag} <${v}>` : `[${f.flag} <${v}>]`;
|
|
6326
|
-
};
|
|
6327
|
-
for (const f of (spec.query ?? []).map(parseField)) parts.push(fmt(f));
|
|
6328
|
-
for (const f of (spec.body ?? []).map(parseField)) parts.push(fmt(f));
|
|
6329
|
-
if (spec.body || spec.rawBody) parts.push("[--body <json> | --body-file <path>]");
|
|
6330
|
-
if (spec.confirm) parts.push("--yes");
|
|
6331
|
-
parts.push("[--json]");
|
|
6332
|
-
return parts.join(" ");
|
|
6313
|
+
return usageLine(group, toRouteCmd(spec));
|
|
6333
6314
|
}
|
|
6334
6315
|
function buildGroupHelp(group, intro, specs, footer = "") {
|
|
6335
6316
|
const lines = ["Usage:"];
|
|
@@ -6358,10 +6339,7 @@ function toField(spec) {
|
|
|
6358
6339
|
const f = parseField(spec);
|
|
6359
6340
|
return { flag: f.flag, key: f.key, type: TYPES[f.type] ?? "string", required: f.required };
|
|
6360
6341
|
}
|
|
6361
|
-
function toRouteCmd(
|
|
6362
|
-
const usage6 = `Usage: ${usageLine2(group, spec)}
|
|
6363
|
-
${spec.method} ${spec.route} \u2014 ${spec.summary}
|
|
6364
|
-
`;
|
|
6342
|
+
function toRouteCmd(spec) {
|
|
6365
6343
|
const rmwKey = spec.rmwKey;
|
|
6366
6344
|
return {
|
|
6367
6345
|
words: spec.cmd,
|
|
@@ -6381,30 +6359,14 @@ function toRouteCmd(group, spec) {
|
|
|
6381
6359
|
rawText: true,
|
|
6382
6360
|
// every spec, as before — `text` only documents the route
|
|
6383
6361
|
noContent: spec.method === "DELETE" ? "Deleted." : "Done (HTTP {status}).",
|
|
6384
|
-
|
|
6385
|
-
hostErrors: false,
|
|
6386
|
-
validateFirst: true,
|
|
6387
|
-
strictNumbers: true,
|
|
6388
|
-
usageText: usage6,
|
|
6389
|
-
messages: {
|
|
6390
|
-
required: (flag, usageText) => `${flag} is required.
|
|
6391
|
-
${usageText}`,
|
|
6392
|
-
refusal: () => `Refusing to ${spec.summary.charAt(0).toLowerCase()}${spec.summary.slice(1).replace(/\.$/, "")} without --yes.`,
|
|
6393
|
-
invalidNumber: (flag, raw) => `${flag} must be a number, got: ${raw}`,
|
|
6394
|
-
invalidBool: (flag, raw) => `${flag} must be true or false, got: ${raw}`,
|
|
6395
|
-
invalidJson: (flag) => `${flag} must be valid JSON`,
|
|
6396
|
-
unreadable: (flag, path) => `Cannot read ${flag} ${path}`,
|
|
6397
|
-
unreadableBody: (path) => `Cannot read --body-file ${path}`,
|
|
6398
|
-
invalidBodyJson: "--body/--body-file must be valid JSON",
|
|
6399
|
-
bodyNotObject: "--body/--body-file must be a JSON object"
|
|
6400
|
-
}
|
|
6362
|
+
validateFirst: true
|
|
6401
6363
|
};
|
|
6402
6364
|
}
|
|
6403
6365
|
function matchSpec(specs, argv) {
|
|
6404
6366
|
return matchRoute(specs.map((spec) => ({ words: spec.cmd, spec })), argv)?.spec;
|
|
6405
6367
|
}
|
|
6406
6368
|
async function runSpec(ctx, group, spec, argv) {
|
|
6407
|
-
return runRoute(ctx, group, toRouteCmd(
|
|
6369
|
+
return runRoute(ctx, group, toRouteCmd(spec), [...spec.cmd, ...argv]);
|
|
6408
6370
|
}
|
|
6409
6371
|
async function dispatchSpecs(ctx, group, specs, argv) {
|
|
6410
6372
|
const spec = matchSpec(specs, argv);
|
|
@@ -29521,8 +29483,9 @@ async function runCapabilityFirewall(ctx, argv) {
|
|
|
29521
29483
|
try {
|
|
29522
29484
|
return await runResourceGroup(ctx, "capability-firewall", CAPABILITY_FIREWALL_HELP, CAPABILITY_FIREWALL_SPECS, argv);
|
|
29523
29485
|
} catch (err) {
|
|
29524
|
-
if (argv[0] === "platform" && err instanceof
|
|
29525
|
-
throw new CliError(gateAdvice("superadmin", "The capability-firewall platform baseline")
|
|
29486
|
+
if (argv[0] === "platform" && err instanceof CliError && err.code === 4) {
|
|
29487
|
+
throw new CliError(`${gateAdvice("superadmin", "The capability-firewall platform baseline")}
|
|
29488
|
+
${err.message}`, 4);
|
|
29526
29489
|
}
|
|
29527
29490
|
throw err;
|
|
29528
29491
|
}
|
package/dist/openwop.js
CHANGED
|
@@ -579,7 +579,7 @@ function errText(err) {
|
|
|
579
579
|
// package.json
|
|
580
580
|
var package_default = {
|
|
581
581
|
name: "@openwop/cli",
|
|
582
|
-
version: "1.2.
|
|
582
|
+
version: "1.2.3",
|
|
583
583
|
description: "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
|
|
584
584
|
license: "Apache-2.0",
|
|
585
585
|
type: "module",
|
|
@@ -1265,9 +1265,9 @@ function streamBaseFrom(extensions, baseUrl) {
|
|
|
1265
1265
|
continue;
|
|
1266
1266
|
}
|
|
1267
1267
|
if (url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "") continue;
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
if (
|
|
1268
|
+
if (isLoopback(base16)) {
|
|
1269
|
+
if (!isLoopback(url)) continue;
|
|
1270
|
+
} else if (url.protocol !== "https:" || base16.protocol !== "https:") continue;
|
|
1271
1271
|
return raw.replace(/\/+$/, "");
|
|
1272
1272
|
}
|
|
1273
1273
|
return void 0;
|
|
@@ -2004,7 +2004,7 @@ function formatCheckTable(checks) {
|
|
|
2004
2004
|
|
|
2005
2005
|
// src/cli/routeKit.ts
|
|
2006
2006
|
var errText2 = (err) => err instanceof Error ? err.message : String(err);
|
|
2007
|
-
var
|
|
2007
|
+
var MSG = {
|
|
2008
2008
|
required: (flag, usage6) => `${usage6}
|
|
2009
2009
|
${flag} is required.`,
|
|
2010
2010
|
refusal: (words, positionals) => `Refusing to ${words.join(" ")} ${positionals.join(" ")} without --yes.`.replace(/\s+/g, " ").trim(),
|
|
@@ -2083,14 +2083,14 @@ function setPath(target, key, value) {
|
|
|
2083
2083
|
}
|
|
2084
2084
|
node[parts[parts.length - 1]] = value;
|
|
2085
2085
|
}
|
|
2086
|
-
function coerce(f, raw
|
|
2086
|
+
function coerce(f, raw) {
|
|
2087
2087
|
const t = f.type ?? "string";
|
|
2088
2088
|
if (t === "boolean") return raw;
|
|
2089
2089
|
if (t === "bool") {
|
|
2090
2090
|
const s2 = String(raw);
|
|
2091
2091
|
if (s2 === "true" || s2 === "yes" || s2 === "1") return true;
|
|
2092
2092
|
if (s2 === "false" || s2 === "no" || s2 === "0") return false;
|
|
2093
|
-
throw new CliError(
|
|
2093
|
+
throw new CliError(MSG.invalidBool(f.flag, s2), 2);
|
|
2094
2094
|
}
|
|
2095
2095
|
if (t === "list") return Array.isArray(raw) ? raw.map(String) : [String(raw)];
|
|
2096
2096
|
if (t === "map") {
|
|
@@ -2107,7 +2107,7 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2107
2107
|
const s = String(raw);
|
|
2108
2108
|
if (t === "number") {
|
|
2109
2109
|
const n = Number(s);
|
|
2110
|
-
if (
|
|
2110
|
+
if (s.trim() === "" || !Number.isFinite(n)) throw new CliError(MSG.invalidNumber(f.flag, s), 2);
|
|
2111
2111
|
return n;
|
|
2112
2112
|
}
|
|
2113
2113
|
if (t === "json-file") {
|
|
@@ -2115,7 +2115,7 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2115
2115
|
try {
|
|
2116
2116
|
text = readFileSync4(s, "utf8");
|
|
2117
2117
|
} catch (err) {
|
|
2118
|
-
throw new CliError(
|
|
2118
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2119
2119
|
}
|
|
2120
2120
|
try {
|
|
2121
2121
|
return JSON.parse(text);
|
|
@@ -2127,14 +2127,14 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2127
2127
|
try {
|
|
2128
2128
|
return readFileSync4(s).toString("base64");
|
|
2129
2129
|
} catch (err) {
|
|
2130
|
-
throw new CliError(
|
|
2130
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2131
2131
|
}
|
|
2132
2132
|
}
|
|
2133
2133
|
if (t === "file") {
|
|
2134
2134
|
try {
|
|
2135
2135
|
return readFileSync4(s, "utf8").replace(/\r?\n$/, "");
|
|
2136
2136
|
} catch (err) {
|
|
2137
|
-
throw new CliError(
|
|
2137
|
+
throw new CliError(MSG.unreadable(f.flag, s, err), 2);
|
|
2138
2138
|
}
|
|
2139
2139
|
}
|
|
2140
2140
|
if (t === "csv") return s.split(",").map((x) => x.trim()).filter(Boolean);
|
|
@@ -2142,19 +2142,19 @@ function coerce(f, raw, cmd, msg) {
|
|
|
2142
2142
|
try {
|
|
2143
2143
|
return JSON.parse(s);
|
|
2144
2144
|
} catch {
|
|
2145
|
-
throw new CliError(
|
|
2145
|
+
throw new CliError(MSG.invalidJson(f.flag), 2);
|
|
2146
2146
|
}
|
|
2147
2147
|
}
|
|
2148
2148
|
return s;
|
|
2149
2149
|
}
|
|
2150
|
-
function readBodyOptions(options
|
|
2150
|
+
function readBodyOptions(options) {
|
|
2151
2151
|
let raw;
|
|
2152
2152
|
if (options.bodyFile !== void 0) {
|
|
2153
2153
|
if (String(options.bodyFile).trim() === "") throw new CliError("--body-file needs a path (got an empty value).", 2);
|
|
2154
2154
|
try {
|
|
2155
2155
|
raw = readFileSync4(String(options.bodyFile), "utf8");
|
|
2156
2156
|
} catch (err) {
|
|
2157
|
-
throw new CliError(
|
|
2157
|
+
throw new CliError(MSG.unreadableBody(String(options.bodyFile), err), 2);
|
|
2158
2158
|
}
|
|
2159
2159
|
} else if (options.body !== void 0) {
|
|
2160
2160
|
raw = String(options.body);
|
|
@@ -2164,9 +2164,9 @@ function readBodyOptions(options, msg) {
|
|
|
2164
2164
|
try {
|
|
2165
2165
|
parsed = JSON.parse(raw);
|
|
2166
2166
|
} catch {
|
|
2167
|
-
throw new CliError(
|
|
2167
|
+
throw new CliError(MSG.invalidBodyJson, 2);
|
|
2168
2168
|
}
|
|
2169
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(
|
|
2169
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(MSG.bodyNotObject, 2);
|
|
2170
2170
|
return parsed;
|
|
2171
2171
|
}
|
|
2172
2172
|
function hostError(err) {
|
|
@@ -2177,7 +2177,6 @@ function hostError(err) {
|
|
|
2177
2177
|
${hint}` : line, code2);
|
|
2178
2178
|
}
|
|
2179
2179
|
async function runRoute(ctx, group, cmd, argv) {
|
|
2180
|
-
const msg = { ...DEFAULT_MESSAGES, ...cmd.messages ?? {} };
|
|
2181
2180
|
const rest = argv.slice(cmd.words.length);
|
|
2182
2181
|
const fields = [...cmd.query ?? [], ...cmd.body ?? []];
|
|
2183
2182
|
const bool = [...COMMON_BOOL, ...fields.filter((f) => f.type === "boolean").flatMap((f) => [f.flag, f.flag.replace(/^--/, "--no-")])];
|
|
@@ -2190,8 +2189,7 @@ async function runRoute(ctx, group, cmd, argv) {
|
|
|
2190
2189
|
const multi = fields.filter(isMulti).map((f) => f.flag);
|
|
2191
2190
|
const { options, positionals } = parseOptions(rest, { bool, value, multi });
|
|
2192
2191
|
if (options.help) {
|
|
2193
|
-
|
|
2194
|
-
else writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
2192
|
+
writeLine(ctx.io.stdout, `Usage: ${usageLine(group, cmd)}
|
|
2195
2193
|
|
|
2196
2194
|
${cmd.summary}
|
|
2197
2195
|
Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
@@ -2199,12 +2197,12 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2199
2197
|
}
|
|
2200
2198
|
const template = templates(cmd).find((p) => positionalNames(cmd, p).length === positionals.length);
|
|
2201
2199
|
if (!template) {
|
|
2202
|
-
write(ctx.io.stderr,
|
|
2200
|
+
write(ctx.io.stderr, `Usage: ${usageLine(group, cmd)}
|
|
2203
2201
|
`);
|
|
2204
2202
|
return 2;
|
|
2205
2203
|
}
|
|
2206
2204
|
const path = fillPath(cmd, template, positionals, options.org);
|
|
2207
|
-
const requiredError = (f) => new CliError(
|
|
2205
|
+
const requiredError = (f) => new CliError(MSG.required(f.flag, usageLine(group, cmd)), 2);
|
|
2208
2206
|
const optionOf = (f) => {
|
|
2209
2207
|
const name = f.flag.replace(/^--/, "").replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
2210
2208
|
if (f.type === "boolean") {
|
|
@@ -2222,7 +2220,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2222
2220
|
if (f.required) throw requiredError(f);
|
|
2223
2221
|
continue;
|
|
2224
2222
|
}
|
|
2225
|
-
const c = coerce(f, v
|
|
2223
|
+
const c = coerce(f, v);
|
|
2226
2224
|
if (f.type === "csv") qs2.set(f.key, c.join(","));
|
|
2227
2225
|
else if (Array.isArray(c)) c.forEach((x) => qs2.append(f.key, String(x)));
|
|
2228
2226
|
else qs2.set(f.key, c !== null && typeof c === "object" ? JSON.stringify(c) : String(c));
|
|
@@ -2230,7 +2228,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2230
2228
|
const url = qs2.size ? `${path}${path.includes("?") ? "&" : "?"}${qs2.toString()}` : path;
|
|
2231
2229
|
const confirm2 = cmd.confirm ?? cmd.method === "DELETE";
|
|
2232
2230
|
const gate = () => {
|
|
2233
|
-
if (confirm2 && !options.yes) throw new CliError(
|
|
2231
|
+
if (confirm2 && !options.yes) throw new CliError(MSG.refusal(cmd.words, positionals), 2);
|
|
2234
2232
|
};
|
|
2235
2233
|
if (!cmd.validateFirst) gate();
|
|
2236
2234
|
const isWrite = cmd.method !== "GET" && cmd.method !== "DELETE";
|
|
@@ -2238,10 +2236,10 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2238
2236
|
let explicit = {};
|
|
2239
2237
|
const overlay = {};
|
|
2240
2238
|
if (takesBody) {
|
|
2241
|
-
explicit = readBodyOptions(options
|
|
2239
|
+
explicit = readBodyOptions(options);
|
|
2242
2240
|
for (const f of cmd.body ?? []) {
|
|
2243
2241
|
const v = optionOf(f);
|
|
2244
|
-
if (v !== void 0) setPath(overlay, f.key, coerce(f, v
|
|
2242
|
+
if (v !== void 0) setPath(overlay, f.key, coerce(f, v));
|
|
2245
2243
|
else if (cmd.validateFirst && f.required && getPath(explicit, f.key) === void 0) throw requiredError(f);
|
|
2246
2244
|
}
|
|
2247
2245
|
}
|
|
@@ -2278,7 +2276,7 @@ Endpoint: ${cmd.method} ${templates(cmd).join(" | ")}`);
|
|
|
2278
2276
|
render(ctx, { ...cmd, method: "GET" }, positionals, err.status, err.body);
|
|
2279
2277
|
return 1;
|
|
2280
2278
|
}
|
|
2281
|
-
if (err instanceof HttpError
|
|
2279
|
+
if (err instanceof HttpError) throw hostError(err);
|
|
2282
2280
|
throw err;
|
|
2283
2281
|
}
|
|
2284
2282
|
}
|
|
@@ -2289,7 +2287,7 @@ async function send(ctx, cmd, url, init) {
|
|
|
2289
2287
|
try {
|
|
2290
2288
|
return await requestJson(ctx, url, { method: init.method, ...init.body !== void 0 ? { body: init.body } : {}, ...cmd.anonymous ? { auth: false } : {} });
|
|
2291
2289
|
} catch (err) {
|
|
2292
|
-
if (err instanceof HttpError
|
|
2290
|
+
if (err instanceof HttpError) throw hostError(err);
|
|
2293
2291
|
throw err;
|
|
2294
2292
|
}
|
|
2295
2293
|
}
|
|
@@ -2339,7 +2337,7 @@ function render(ctx, cmd, positionals, status, rawBody) {
|
|
|
2339
2337
|
return;
|
|
2340
2338
|
}
|
|
2341
2339
|
}
|
|
2342
|
-
if (cmd.method === "GET"
|
|
2340
|
+
if (cmd.method === "GET") {
|
|
2343
2341
|
writeJson(ctx.io.stdout, body);
|
|
2344
2342
|
return;
|
|
2345
2343
|
}
|
|
@@ -6311,25 +6309,8 @@ function parseField(spec) {
|
|
|
6311
6309
|
const flag = `--${flagOverride ?? key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`).replace(/_/g, "-")}`;
|
|
6312
6310
|
return { key, flag, type, required };
|
|
6313
6311
|
}
|
|
6314
|
-
function hasOrg(route) {
|
|
6315
|
-
return /:org(?![A-Za-z0-9_])/.test(route);
|
|
6316
|
-
}
|
|
6317
|
-
function paramsOf(route) {
|
|
6318
|
-
return (route.match(/:[A-Za-z][A-Za-z0-9_]*/g) ?? []).map((p) => p.slice(1)).filter((p) => p !== "org");
|
|
6319
|
-
}
|
|
6320
6312
|
function usageLine2(group, spec) {
|
|
6321
|
-
|
|
6322
|
-
if (hasOrg(spec.route)) parts.push("--org <orgId>");
|
|
6323
|
-
const fmt = (f) => {
|
|
6324
|
-
const v = f.type === "bool" ? "true|false" : f.type === "json" ? "json" : f.type === "list" ? "a,b" : f.type === "number" ? "n" : f.type === "file" ? "path" : "v";
|
|
6325
|
-
return f.required ? `${f.flag} <${v}>` : `[${f.flag} <${v}>]`;
|
|
6326
|
-
};
|
|
6327
|
-
for (const f of (spec.query ?? []).map(parseField)) parts.push(fmt(f));
|
|
6328
|
-
for (const f of (spec.body ?? []).map(parseField)) parts.push(fmt(f));
|
|
6329
|
-
if (spec.body || spec.rawBody) parts.push("[--body <json> | --body-file <path>]");
|
|
6330
|
-
if (spec.confirm) parts.push("--yes");
|
|
6331
|
-
parts.push("[--json]");
|
|
6332
|
-
return parts.join(" ");
|
|
6313
|
+
return usageLine(group, toRouteCmd(spec));
|
|
6333
6314
|
}
|
|
6334
6315
|
function buildGroupHelp(group, intro, specs, footer = "") {
|
|
6335
6316
|
const lines = ["Usage:"];
|
|
@@ -6358,10 +6339,7 @@ function toField(spec) {
|
|
|
6358
6339
|
const f = parseField(spec);
|
|
6359
6340
|
return { flag: f.flag, key: f.key, type: TYPES[f.type] ?? "string", required: f.required };
|
|
6360
6341
|
}
|
|
6361
|
-
function toRouteCmd(
|
|
6362
|
-
const usage6 = `Usage: ${usageLine2(group, spec)}
|
|
6363
|
-
${spec.method} ${spec.route} \u2014 ${spec.summary}
|
|
6364
|
-
`;
|
|
6342
|
+
function toRouteCmd(spec) {
|
|
6365
6343
|
const rmwKey = spec.rmwKey;
|
|
6366
6344
|
return {
|
|
6367
6345
|
words: spec.cmd,
|
|
@@ -6381,30 +6359,14 @@ function toRouteCmd(group, spec) {
|
|
|
6381
6359
|
rawText: true,
|
|
6382
6360
|
// every spec, as before — `text` only documents the route
|
|
6383
6361
|
noContent: spec.method === "DELETE" ? "Deleted." : "Done (HTTP {status}).",
|
|
6384
|
-
|
|
6385
|
-
hostErrors: false,
|
|
6386
|
-
validateFirst: true,
|
|
6387
|
-
strictNumbers: true,
|
|
6388
|
-
usageText: usage6,
|
|
6389
|
-
messages: {
|
|
6390
|
-
required: (flag, usageText) => `${flag} is required.
|
|
6391
|
-
${usageText}`,
|
|
6392
|
-
refusal: () => `Refusing to ${spec.summary.charAt(0).toLowerCase()}${spec.summary.slice(1).replace(/\.$/, "")} without --yes.`,
|
|
6393
|
-
invalidNumber: (flag, raw) => `${flag} must be a number, got: ${raw}`,
|
|
6394
|
-
invalidBool: (flag, raw) => `${flag} must be true or false, got: ${raw}`,
|
|
6395
|
-
invalidJson: (flag) => `${flag} must be valid JSON`,
|
|
6396
|
-
unreadable: (flag, path) => `Cannot read ${flag} ${path}`,
|
|
6397
|
-
unreadableBody: (path) => `Cannot read --body-file ${path}`,
|
|
6398
|
-
invalidBodyJson: "--body/--body-file must be valid JSON",
|
|
6399
|
-
bodyNotObject: "--body/--body-file must be a JSON object"
|
|
6400
|
-
}
|
|
6362
|
+
validateFirst: true
|
|
6401
6363
|
};
|
|
6402
6364
|
}
|
|
6403
6365
|
function matchSpec(specs, argv) {
|
|
6404
6366
|
return matchRoute(specs.map((spec) => ({ words: spec.cmd, spec })), argv)?.spec;
|
|
6405
6367
|
}
|
|
6406
6368
|
async function runSpec(ctx, group, spec, argv) {
|
|
6407
|
-
return runRoute(ctx, group, toRouteCmd(
|
|
6369
|
+
return runRoute(ctx, group, toRouteCmd(spec), [...spec.cmd, ...argv]);
|
|
6408
6370
|
}
|
|
6409
6371
|
async function dispatchSpecs(ctx, group, specs, argv) {
|
|
6410
6372
|
const spec = matchSpec(specs, argv);
|
|
@@ -29521,8 +29483,9 @@ async function runCapabilityFirewall(ctx, argv) {
|
|
|
29521
29483
|
try {
|
|
29522
29484
|
return await runResourceGroup(ctx, "capability-firewall", CAPABILITY_FIREWALL_HELP, CAPABILITY_FIREWALL_SPECS, argv);
|
|
29523
29485
|
} catch (err) {
|
|
29524
|
-
if (argv[0] === "platform" && err instanceof
|
|
29525
|
-
throw new CliError(gateAdvice("superadmin", "The capability-firewall platform baseline")
|
|
29486
|
+
if (argv[0] === "platform" && err instanceof CliError && err.code === 4) {
|
|
29487
|
+
throw new CliError(`${gateAdvice("superadmin", "The capability-firewall platform baseline")}
|
|
29488
|
+
${err.message}`, 4);
|
|
29526
29489
|
}
|
|
29527
29490
|
throw err;
|
|
29528
29491
|
}
|