@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 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.1",
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
- const secure = url.protocol === "https:" && (base16.protocol === "https:" || isLoopback(base16));
1269
- const localDev = url.protocol === "http:" && isLoopback(url) && isLoopback(base16);
1270
- if (!secure && !localDev) continue;
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 DEFAULT_MESSAGES = {
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, cmd, msg) {
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(msg.invalidBool(f.flag, s2), 2);
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 (!Number.isFinite(n) || cmd.strictNumbers && s.trim() === "") throw new CliError(msg.invalidNumber(f.flag, s), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.invalidJson(f.flag, s), 2);
2145
+ throw new CliError(MSG.invalidJson(f.flag), 2);
2146
2146
  }
2147
2147
  }
2148
2148
  return s;
2149
2149
  }
2150
- function readBodyOptions(options, msg) {
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(msg.unreadableBody(String(options.bodyFile), err), 2);
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(msg.invalidBodyJson, 2);
2167
+ throw new CliError(MSG.invalidBodyJson, 2);
2168
2168
  }
2169
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(msg.bodyNotObject, 2);
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
- if (cmd.usageText !== void 0) write(ctx.io.stdout, cmd.usageText);
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, cmd.usageText ?? `Usage: ${usageLine(group, cmd)}
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(msg.required(f.flag, cmd.usageText ?? usageLine(group, cmd)), 2);
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, cmd, msg);
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(msg.refusal(cmd.words, positionals), 2);
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, msg);
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, cmd, msg));
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 && cmd.hostErrors !== false) throw hostError(err);
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 && cmd.hostErrors !== false) throw hostError(err);
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" || cmd.writeOutput === "json") {
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
- const parts = [`openwop ${group}`, ...spec.cmd, ...paramsOf(spec.route).map((p) => `<${p}>`)];
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(group, spec) {
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
- writeOutput: "json",
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(group, spec), [...spec.cmd, ...argv]);
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 HttpError && (err.status === 401 || err.status === 403)) {
29525
- throw new CliError(gateAdvice("superadmin", "The capability-firewall platform baseline"), 4);
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.1",
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
- const secure = url.protocol === "https:" && (base16.protocol === "https:" || isLoopback(base16));
1269
- const localDev = url.protocol === "http:" && isLoopback(url) && isLoopback(base16);
1270
- if (!secure && !localDev) continue;
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 DEFAULT_MESSAGES = {
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, cmd, msg) {
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(msg.invalidBool(f.flag, s2), 2);
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 (!Number.isFinite(n) || cmd.strictNumbers && s.trim() === "") throw new CliError(msg.invalidNumber(f.flag, s), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.unreadable(f.flag, s, err), 2);
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(msg.invalidJson(f.flag, s), 2);
2145
+ throw new CliError(MSG.invalidJson(f.flag), 2);
2146
2146
  }
2147
2147
  }
2148
2148
  return s;
2149
2149
  }
2150
- function readBodyOptions(options, msg) {
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(msg.unreadableBody(String(options.bodyFile), err), 2);
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(msg.invalidBodyJson, 2);
2167
+ throw new CliError(MSG.invalidBodyJson, 2);
2168
2168
  }
2169
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new CliError(msg.bodyNotObject, 2);
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
- if (cmd.usageText !== void 0) write(ctx.io.stdout, cmd.usageText);
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, cmd.usageText ?? `Usage: ${usageLine(group, cmd)}
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(msg.required(f.flag, cmd.usageText ?? usageLine(group, cmd)), 2);
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, cmd, msg);
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(msg.refusal(cmd.words, positionals), 2);
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, msg);
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, cmd, msg));
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 && cmd.hostErrors !== false) throw hostError(err);
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 && cmd.hostErrors !== false) throw hostError(err);
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" || cmd.writeOutput === "json") {
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
- const parts = [`openwop ${group}`, ...spec.cmd, ...paramsOf(spec.route).map((p) => `<${p}>`)];
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(group, spec) {
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
- writeOutput: "json",
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(group, spec), [...spec.cmd, ...argv]);
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 HttpError && (err.status === 401 || err.status === 403)) {
29525
- throw new CliError(gateAdvice("superadmin", "The capability-firewall platform baseline"), 4);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openwop/cli",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "description": "OpenWOP CLI for operating the workflow-engine demo app and OpenWOP-compatible hosts.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",