@robinmordasiewicz/f5xc-xcsh 6.36.0 → 6.38.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/dist/index.js CHANGED
@@ -44530,9 +44530,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44530
44530
  ["observability", {
44531
44531
  name: "observability",
44532
44532
  displayName: "Observability",
44533
- description: "Deploy synthetic monitors to validate DNS resolution and HTTP service health from multiple geographic locations. Define monitoring schedules, response time thresholds, and alerting conditions for proactive issue detection. Access health summaries, historical trends, and detailed reports for certificate status and service availability. Integrate monitoring data with dashboards to visualize health patterns and identify performance degradation before user impact.",
44533
+ description: "Set up synthetic monitoring for DNS resolution and HTTP services across AWS regions. Generate health reports with historical trends and summary dashboards. Monitor certificate validity, track response times, and aggregate results by namespace for capacity planning.",
44534
44534
  descriptionShort: "Configure synthetic monitors and health checks",
44535
- descriptionMedium: "Set up DNS and HTTP monitoring with alerting thresholds. Track certificate expiration and service availability across regions.",
44535
+ descriptionMedium: "Define DNS and HTTP monitors with regional testing. Track certificate expiration and service availability across zones.",
44536
44536
  aliases: ["obs", "monitoring", "synth"],
44537
44537
  complexity: "advanced",
44538
44538
  isPreview: false,
@@ -44600,9 +44600,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44600
44600
  ["sites", {
44601
44601
  name: "sites",
44602
44602
  displayName: "Sites",
44603
- description: "Create virtual and physical edge deployments spanning multiple providers. Establish AWS Transit Gateway connections and secure tunnel configurations for hybrid connectivity. Integrate external container orchestration systems as customer edge nodes with managed control planes. Define virtual groupings using label selectors to apply consistent policies across distributed infrastructure. Enable secure mesh communication between edge nodes and cloud workloads with automated certificate management.",
44604
- descriptionShort: "Deploy and manage edge infrastructure",
44605
- descriptionMedium: "Configure cloud provider resources on AWS, Azure, and GCP. Set up VPC peering, transit gateways, and VPN tunnels for hybrid environments.",
44603
+ description: "Deploy edge nodes across AWS, Azure, and GCP with automated provisioning. Configure VPC peering, transit gateway attachments, and VPN tunnel settings. Define virtual groupings with label selectors for policy targeting. Manage Kubernetes cluster integrations and secure mesh deployments. Monitor node health, validate configurations, and set IP prefix allocations.",
44604
+ descriptionShort: "Deploy edge nodes across cloud providers",
44605
+ descriptionMedium: "Configure AWS, Azure, GCP deployments with VPC integration. Manage transit gateways and VPN tunnels.",
44606
44606
  aliases: ["site", "deployment"],
44607
44607
  complexity: "advanced",
44608
44608
  isPreview: false,
@@ -45354,8 +45354,8 @@ function getLogoModeFromEnv(envPrefix) {
45354
45354
  var CLI_NAME = "xcsh";
45355
45355
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
45356
45356
  function getVersion() {
45357
- if ("6.36.0") {
45358
- return "6.36.0";
45357
+ if ("6.38.0") {
45358
+ return "6.38.0";
45359
45359
  }
45360
45360
  if (process.env.XCSH_VERSION) {
45361
45361
  return process.env.XCSH_VERSION;
@@ -46024,6 +46024,728 @@ var APIClient = class {
46024
46024
  }
46025
46025
  };
46026
46026
 
46027
+ // src/output/formatter.ts
46028
+ var import_yaml2 = __toESM(require_dist(), 1);
46029
+
46030
+ // src/output/types.ts
46031
+ var DEFAULT_TABLE_STYLE = {
46032
+ unicode: true,
46033
+ coloredBorders: true,
46034
+ headerStyle: "bold"
46035
+ };
46036
+ var PLAIN_TABLE_STYLE = {
46037
+ unicode: false,
46038
+ coloredBorders: false,
46039
+ headerStyle: "normal"
46040
+ };
46041
+ function isValidOutputFormat(format) {
46042
+ return ["json", "yaml", "table", "text", "tsv", "none", "spec"].includes(
46043
+ format.toLowerCase()
46044
+ );
46045
+ }
46046
+
46047
+ // src/output/resolver.ts
46048
+ var OUTPUT_FORMAT_ENV_VAR = `${ENV_PREFIX}_OUTPUT_FORMAT`;
46049
+ function getOutputFormatFromEnv() {
46050
+ const envValue = process.env[OUTPUT_FORMAT_ENV_VAR]?.toLowerCase().trim();
46051
+ if (envValue && isValidOutputFormat(envValue)) {
46052
+ return envValue;
46053
+ }
46054
+ return void 0;
46055
+ }
46056
+ function shouldUseColors(isTTY = process.stdout.isTTY ?? false, noColorFlag = false) {
46057
+ if (noColorFlag) {
46058
+ return false;
46059
+ }
46060
+ if (process.env.NO_COLOR !== void 0) {
46061
+ return false;
46062
+ }
46063
+ if (process.env.FORCE_COLOR !== void 0) {
46064
+ return true;
46065
+ }
46066
+ return isTTY;
46067
+ }
46068
+
46069
+ // src/output/table.ts
46070
+ var UNICODE_BOX = {
46071
+ topLeft: "\u256D",
46072
+ // ╭
46073
+ topRight: "\u256E",
46074
+ // ╮
46075
+ bottomLeft: "\u2570",
46076
+ // ╰
46077
+ bottomRight: "\u256F",
46078
+ // ╯
46079
+ horizontal: "\u2500",
46080
+ // ─
46081
+ vertical: "\u2502",
46082
+ // │
46083
+ leftT: "\u251C",
46084
+ // ├
46085
+ rightT: "\u2524",
46086
+ // ┤
46087
+ topT: "\u252C",
46088
+ // ┬
46089
+ bottomT: "\u2534",
46090
+ // ┴
46091
+ cross: "\u253C"
46092
+ // ┼
46093
+ };
46094
+ var ASCII_BOX = {
46095
+ topLeft: "+",
46096
+ topRight: "+",
46097
+ bottomLeft: "+",
46098
+ bottomRight: "+",
46099
+ horizontal: "-",
46100
+ vertical: "|",
46101
+ leftT: "+",
46102
+ rightT: "+",
46103
+ topT: "+",
46104
+ bottomT: "+",
46105
+ cross: "+"
46106
+ };
46107
+ function getBoxCharacters(style) {
46108
+ return style.unicode ? UNICODE_BOX : ASCII_BOX;
46109
+ }
46110
+ function applyColor(text, color, useColors) {
46111
+ if (!useColors) {
46112
+ return text;
46113
+ }
46114
+ return `${color}${text}${colors.reset}`;
46115
+ }
46116
+ function wrapText2(text, maxWidth) {
46117
+ if (text.length <= maxWidth) {
46118
+ return [text];
46119
+ }
46120
+ const lines = [];
46121
+ let remaining = text;
46122
+ while (remaining.length > 0) {
46123
+ if (remaining.length <= maxWidth) {
46124
+ lines.push(remaining);
46125
+ break;
46126
+ }
46127
+ let breakPoint = maxWidth;
46128
+ for (let i = maxWidth - 1; i > 0; i--) {
46129
+ if (remaining[i] === " ") {
46130
+ breakPoint = i;
46131
+ break;
46132
+ }
46133
+ }
46134
+ lines.push(remaining.slice(0, breakPoint));
46135
+ remaining = remaining.slice(breakPoint).trimStart();
46136
+ }
46137
+ return lines;
46138
+ }
46139
+ function getValue(row, accessor) {
46140
+ if (typeof accessor === "function") {
46141
+ return accessor(row);
46142
+ }
46143
+ const value = row[accessor];
46144
+ if (value === null || value === void 0) {
46145
+ return "";
46146
+ }
46147
+ if (typeof value === "object") {
46148
+ if (accessor === "labels") {
46149
+ return formatLabelsValue(value);
46150
+ }
46151
+ return JSON.stringify(value);
46152
+ }
46153
+ return String(value);
46154
+ }
46155
+ function formatLabelsValue(labels) {
46156
+ const entries = Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`);
46157
+ if (entries.length === 0) {
46158
+ return "";
46159
+ }
46160
+ return `map[${entries.join(" ")}]`;
46161
+ }
46162
+ function calculateColumnWidths(columns, rows, maxTableWidth) {
46163
+ const widths = columns.map((col) => {
46164
+ let width = col.header.length;
46165
+ for (const row of rows) {
46166
+ const value = getValue(row, col.accessor);
46167
+ width = Math.max(width, value.length);
46168
+ }
46169
+ if (col.minWidth) {
46170
+ width = Math.max(width, col.minWidth);
46171
+ }
46172
+ if (col.maxWidth) {
46173
+ width = Math.min(width, col.maxWidth);
46174
+ }
46175
+ if (col.width) {
46176
+ width = col.width;
46177
+ }
46178
+ return width;
46179
+ });
46180
+ if (maxTableWidth) {
46181
+ const totalWidth = widths.reduce((a, b) => a + b, 0) + columns.length * 3 + 1;
46182
+ if (totalWidth > maxTableWidth) {
46183
+ const ratio = (maxTableWidth - columns.length * 3 - 1) / totalWidth;
46184
+ for (let i = 0; i < widths.length; i++) {
46185
+ widths[i] = Math.max(5, Math.floor(widths[i] * ratio));
46186
+ }
46187
+ }
46188
+ }
46189
+ return widths;
46190
+ }
46191
+ function formatBeautifulTable(data, config, noColor = false) {
46192
+ if (data.length === 0) {
46193
+ return "";
46194
+ }
46195
+ const useColors = shouldUseColors(void 0, noColor);
46196
+ const style = useColors ? config.style ?? DEFAULT_TABLE_STYLE : PLAIN_TABLE_STYLE;
46197
+ const box = getBoxCharacters(style);
46198
+ const borderColor = style.borderColor ?? colors.red;
46199
+ const widths = calculateColumnWidths(config.columns, data, config.maxWidth);
46200
+ const lines = [];
46201
+ const colorBorder = (text) => applyColor(text, borderColor, useColors && style.coloredBorders);
46202
+ const buildHorizontalLine = (left, mid, right, fill) => {
46203
+ const segments = widths.map((w) => fill.repeat(w + 2));
46204
+ return colorBorder(left + segments.join(mid) + right);
46205
+ };
46206
+ if (config.title) {
46207
+ const title = ` ${config.title} `;
46208
+ const totalWidth = widths.reduce((a, b) => a + b, 0) + widths.length * 3 - 1;
46209
+ const remainingWidth = totalWidth - title.length;
46210
+ const leftDashes = 1;
46211
+ const rightDashes = Math.max(0, remainingWidth - leftDashes);
46212
+ lines.push(
46213
+ colorBorder(box.topLeft + box.horizontal.repeat(leftDashes)) + title + colorBorder(box.horizontal.repeat(rightDashes) + box.topRight)
46214
+ );
46215
+ } else {
46216
+ lines.push(
46217
+ buildHorizontalLine(
46218
+ box.topLeft,
46219
+ box.topT,
46220
+ box.topRight,
46221
+ box.horizontal
46222
+ )
46223
+ );
46224
+ }
46225
+ const headerCells = config.columns.map((col, i) => {
46226
+ const padding = widths[i] - col.header.length;
46227
+ const leftPad = Math.floor(padding / 2);
46228
+ const rightPad = padding - leftPad;
46229
+ const content = " ".repeat(leftPad) + col.header + " ".repeat(rightPad);
46230
+ return ` ${content} `;
46231
+ });
46232
+ lines.push(
46233
+ colorBorder(box.vertical) + headerCells.join(colorBorder(box.vertical)) + colorBorder(box.vertical)
46234
+ );
46235
+ lines.push(
46236
+ buildHorizontalLine(box.leftT, box.cross, box.rightT, box.horizontal)
46237
+ );
46238
+ for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
46239
+ const row = data[rowIndex];
46240
+ const cellValues = config.columns.map((col, i) => {
46241
+ const value = getValue(row, col.accessor) || "<None>";
46242
+ return config.wrapText !== false ? wrapText2(value, widths[i]) : [value.slice(0, widths[i])];
46243
+ });
46244
+ const maxLines = Math.max(...cellValues.map((c) => c.length));
46245
+ for (let lineIndex = 0; lineIndex < maxLines; lineIndex++) {
46246
+ const cells = cellValues.map((cellLines, i) => {
46247
+ const text = cellLines[lineIndex] ?? "";
46248
+ const padding = widths[i] - text.length;
46249
+ const align = config.columns[i]?.align ?? "left";
46250
+ let content;
46251
+ if (align === "center") {
46252
+ const leftPad = Math.floor(padding / 2);
46253
+ const rightPad = padding - leftPad;
46254
+ content = " ".repeat(leftPad) + text + " ".repeat(rightPad);
46255
+ } else if (align === "right") {
46256
+ content = " ".repeat(padding) + text;
46257
+ } else {
46258
+ content = text + " ".repeat(padding);
46259
+ }
46260
+ return ` ${content} `;
46261
+ });
46262
+ lines.push(
46263
+ colorBorder(box.vertical) + cells.join(colorBorder(box.vertical)) + colorBorder(box.vertical)
46264
+ );
46265
+ }
46266
+ if (rowIndex < data.length - 1 && config.rowSeparators) {
46267
+ lines.push(
46268
+ buildHorizontalLine(
46269
+ box.leftT,
46270
+ box.cross,
46271
+ box.rightT,
46272
+ box.horizontal
46273
+ )
46274
+ );
46275
+ }
46276
+ }
46277
+ lines.push(
46278
+ buildHorizontalLine(
46279
+ box.bottomLeft,
46280
+ box.bottomT,
46281
+ box.bottomRight,
46282
+ box.horizontal
46283
+ )
46284
+ );
46285
+ return lines.join("\n");
46286
+ }
46287
+ var DEFAULT_RESOURCE_COLUMNS = [
46288
+ { header: "NAMESPACE", accessor: "namespace", minWidth: 9 },
46289
+ { header: "NAME", accessor: "name", minWidth: 10, maxWidth: 40 },
46290
+ { header: "LABELS", accessor: "labels", minWidth: 10, maxWidth: 35 }
46291
+ ];
46292
+ function formatResourceTable(data, noColor = false) {
46293
+ const items = extractItems(data);
46294
+ if (items.length === 0) {
46295
+ return "";
46296
+ }
46297
+ return formatBeautifulTable(
46298
+ items,
46299
+ {
46300
+ columns: DEFAULT_RESOURCE_COLUMNS,
46301
+ wrapText: true
46302
+ },
46303
+ noColor
46304
+ );
46305
+ }
46306
+ function extractItems(data) {
46307
+ if (data && typeof data === "object" && "items" in data) {
46308
+ const items = data.items;
46309
+ if (Array.isArray(items)) {
46310
+ return items.filter(
46311
+ (item) => item !== null && typeof item === "object"
46312
+ );
46313
+ }
46314
+ }
46315
+ if (Array.isArray(data)) {
46316
+ return data.filter(
46317
+ (item) => item !== null && typeof item === "object"
46318
+ );
46319
+ }
46320
+ if (data && typeof data === "object") {
46321
+ return [data];
46322
+ }
46323
+ return [];
46324
+ }
46325
+
46326
+ // src/output/formatter.ts
46327
+ function formatOutput(data, format = "table", noColor = false) {
46328
+ if (format === "none") {
46329
+ return "";
46330
+ }
46331
+ const useNoColor = noColor || !shouldUseColors();
46332
+ switch (format) {
46333
+ case "json":
46334
+ return formatJSON(data);
46335
+ case "yaml":
46336
+ return formatYAML(data);
46337
+ case "table":
46338
+ case "text":
46339
+ return formatTable(data, useNoColor);
46340
+ case "tsv":
46341
+ return formatTSV(data);
46342
+ case "spec":
46343
+ return formatJSON(data);
46344
+ default:
46345
+ return formatTable(data, useNoColor);
46346
+ }
46347
+ }
46348
+ function formatJSON(data) {
46349
+ return JSON.stringify(data, null, 2);
46350
+ }
46351
+ function formatYAML(data) {
46352
+ return import_yaml2.default.stringify(data, { indent: 2 });
46353
+ }
46354
+ function extractItems2(data) {
46355
+ if (data && typeof data === "object" && "items" in data) {
46356
+ const items = data.items;
46357
+ if (Array.isArray(items)) {
46358
+ return items.filter(
46359
+ (item) => item !== null && typeof item === "object"
46360
+ );
46361
+ }
46362
+ }
46363
+ if (Array.isArray(data)) {
46364
+ return data.filter(
46365
+ (item) => item !== null && typeof item === "object"
46366
+ );
46367
+ }
46368
+ if (data && typeof data === "object") {
46369
+ return [data];
46370
+ }
46371
+ return [];
46372
+ }
46373
+ function formatTable(data, noColor = false) {
46374
+ return formatResourceTable(data, noColor);
46375
+ }
46376
+ function formatTSV(data) {
46377
+ const items = extractItems2(data);
46378
+ if (items.length === 0) {
46379
+ return "";
46380
+ }
46381
+ const allKeys = /* @__PURE__ */ new Set();
46382
+ for (const item of items) {
46383
+ Object.keys(item).forEach((k) => allKeys.add(k));
46384
+ }
46385
+ const priority = ["name", "namespace", "status", "created", "modified"];
46386
+ const headers = [
46387
+ ...priority.filter((p) => allKeys.has(p)),
46388
+ ...[...allKeys].filter((k) => !priority.includes(k)).sort()
46389
+ ];
46390
+ const lines = [];
46391
+ for (const item of items) {
46392
+ const values = headers.map((h) => {
46393
+ const val = item[h];
46394
+ if (val === null || val === void 0) return "";
46395
+ if (typeof val === "object") return JSON.stringify(val);
46396
+ return String(val);
46397
+ });
46398
+ lines.push(values.join(" "));
46399
+ }
46400
+ return lines.join("\n");
46401
+ }
46402
+ function parseOutputFormat(format) {
46403
+ switch (format.toLowerCase()) {
46404
+ case "json":
46405
+ return "json";
46406
+ case "yaml":
46407
+ return "yaml";
46408
+ case "table":
46409
+ case "text":
46410
+ case "":
46411
+ return "table";
46412
+ case "tsv":
46413
+ return "tsv";
46414
+ case "none":
46415
+ return "none";
46416
+ default:
46417
+ return "table";
46418
+ }
46419
+ }
46420
+ function formatAPIError(statusCode, body, operation) {
46421
+ const lines = [];
46422
+ lines.push(`ERROR: ${operation} failed (HTTP ${statusCode})`);
46423
+ if (body && typeof body === "object") {
46424
+ const errResp = body;
46425
+ if (errResp.message) {
46426
+ lines.push(` Message: ${errResp.message}`);
46427
+ }
46428
+ if (errResp.code) {
46429
+ lines.push(` Code: ${errResp.code}`);
46430
+ }
46431
+ if (errResp.details) {
46432
+ lines.push(` Details: ${errResp.details}`);
46433
+ }
46434
+ }
46435
+ switch (statusCode) {
46436
+ case 401:
46437
+ lines.push(
46438
+ "\nHint: Authentication failed. Check your credentials with 'login profile show'"
46439
+ );
46440
+ break;
46441
+ case 403:
46442
+ lines.push(
46443
+ "\nHint: Permission denied. You may not have access to this resource."
46444
+ );
46445
+ break;
46446
+ case 404:
46447
+ lines.push(
46448
+ "\nHint: Resource not found. Verify the name and namespace are correct."
46449
+ );
46450
+ break;
46451
+ case 409:
46452
+ lines.push(
46453
+ "\nHint: Conflict - resource may already exist or be in a conflicting state."
46454
+ );
46455
+ break;
46456
+ case 429:
46457
+ lines.push("\nHint: Rate limited. Please wait and try again.");
46458
+ break;
46459
+ case 500:
46460
+ case 502:
46461
+ case 503:
46462
+ lines.push(
46463
+ "\nHint: Server error. Please try again later or contact support."
46464
+ );
46465
+ break;
46466
+ }
46467
+ return lines.join("\n");
46468
+ }
46469
+
46470
+ // src/output/spec.ts
46471
+ function buildCommandSpec(options) {
46472
+ const spec = {
46473
+ command: options.command,
46474
+ description: options.description,
46475
+ usage: options.usage ?? `xcsh ${options.command} [options]`,
46476
+ flags: options.flags ?? [],
46477
+ examples: options.examples ?? [],
46478
+ outputFormats: options.outputFormats ?? ["table", "json", "yaml"]
46479
+ };
46480
+ if (options.related !== void 0) {
46481
+ spec.related = options.related;
46482
+ }
46483
+ if (options.category !== void 0) {
46484
+ spec.category = options.category;
46485
+ }
46486
+ return spec;
46487
+ }
46488
+ function formatSpec(spec) {
46489
+ return JSON.stringify(spec, null, 2);
46490
+ }
46491
+ function buildCloudstatusSpecs() {
46492
+ return {
46493
+ status: buildCommandSpec({
46494
+ command: "cloudstatus status",
46495
+ description: "Get the overall health indicator for F5 Distributed Cloud services. Returns status level (operational, degraded, major outage) with description.",
46496
+ usage: "xcsh cloudstatus status [--quiet]",
46497
+ flags: [
46498
+ {
46499
+ name: "--quiet",
46500
+ alias: "-q",
46501
+ description: "Return exit code only (0=operational, 1=degraded, 2=outage)",
46502
+ type: "boolean"
46503
+ }
46504
+ ],
46505
+ examples: [
46506
+ {
46507
+ command: "xcsh cloudstatus status",
46508
+ description: "Check current F5 XC service status"
46509
+ },
46510
+ {
46511
+ command: "xcsh cloudstatus status --quiet && echo 'All systems operational'",
46512
+ description: "Use in scripts for health checks"
46513
+ },
46514
+ {
46515
+ command: "xcsh cloudstatus status --output json",
46516
+ description: "Get status as JSON for automation"
46517
+ }
46518
+ ],
46519
+ category: "cloudstatus",
46520
+ related: [
46521
+ "cloudstatus summary",
46522
+ "cloudstatus components",
46523
+ "cloudstatus incidents"
46524
+ ]
46525
+ }),
46526
+ summary: buildCommandSpec({
46527
+ command: "cloudstatus summary",
46528
+ description: "Get complete status summary including overall health, component status, and active incidents.",
46529
+ usage: "xcsh cloudstatus summary",
46530
+ examples: [
46531
+ {
46532
+ command: "xcsh cloudstatus summary",
46533
+ description: "View full infrastructure health overview"
46534
+ },
46535
+ {
46536
+ command: "xcsh cloudstatus summary --output json",
46537
+ description: "Get complete summary as JSON"
46538
+ }
46539
+ ],
46540
+ category: "cloudstatus",
46541
+ related: ["cloudstatus status", "cloudstatus components"]
46542
+ }),
46543
+ components: buildCommandSpec({
46544
+ command: "cloudstatus components",
46545
+ description: "List all infrastructure components and their current operational status.",
46546
+ usage: "xcsh cloudstatus components",
46547
+ examples: [
46548
+ {
46549
+ command: "xcsh cloudstatus components",
46550
+ description: "List all components with status"
46551
+ },
46552
+ {
46553
+ command: "xcsh cloudstatus components --output json",
46554
+ description: "Get components as JSON for monitoring integration"
46555
+ }
46556
+ ],
46557
+ category: "cloudstatus",
46558
+ related: ["cloudstatus status", "cloudstatus summary"]
46559
+ }),
46560
+ incidents: buildCommandSpec({
46561
+ command: "cloudstatus incidents",
46562
+ description: "List active and recent incidents affecting F5 Distributed Cloud services.",
46563
+ usage: "xcsh cloudstatus incidents",
46564
+ examples: [
46565
+ {
46566
+ command: "xcsh cloudstatus incidents",
46567
+ description: "View active incidents"
46568
+ },
46569
+ {
46570
+ command: "xcsh cloudstatus incidents --output json",
46571
+ description: "Get incidents as JSON for alerting systems"
46572
+ }
46573
+ ],
46574
+ category: "cloudstatus",
46575
+ related: ["cloudstatus status", "cloudstatus maintenance"]
46576
+ }),
46577
+ maintenance: buildCommandSpec({
46578
+ command: "cloudstatus maintenance",
46579
+ description: "List scheduled maintenance windows for F5 Distributed Cloud services.",
46580
+ usage: "xcsh cloudstatus maintenance",
46581
+ examples: [
46582
+ {
46583
+ command: "xcsh cloudstatus maintenance",
46584
+ description: "View upcoming maintenance windows"
46585
+ },
46586
+ {
46587
+ command: "xcsh cloudstatus maintenance --output json",
46588
+ description: "Get maintenance schedule as JSON"
46589
+ }
46590
+ ],
46591
+ category: "cloudstatus",
46592
+ related: ["cloudstatus status", "cloudstatus incidents"]
46593
+ })
46594
+ };
46595
+ }
46596
+ function buildLoginSpecs() {
46597
+ return {
46598
+ banner: buildCommandSpec({
46599
+ command: "login banner",
46600
+ description: "Display xcsh banner with logo and connection information.",
46601
+ usage: "xcsh login banner",
46602
+ examples: [
46603
+ {
46604
+ command: "xcsh login banner",
46605
+ description: "Show the xcsh welcome banner"
46606
+ }
46607
+ ],
46608
+ category: "login",
46609
+ related: ["login profile show"]
46610
+ }),
46611
+ "profile list": buildCommandSpec({
46612
+ command: "login profile list",
46613
+ description: "List all saved connection profiles.",
46614
+ usage: "xcsh login profile list",
46615
+ examples: [
46616
+ {
46617
+ command: "xcsh login profile list",
46618
+ description: "List saved profiles"
46619
+ },
46620
+ {
46621
+ command: "xcsh login profile list --output json",
46622
+ description: "Get profiles as JSON"
46623
+ }
46624
+ ],
46625
+ category: "login",
46626
+ related: [
46627
+ "login profile show",
46628
+ "login profile create",
46629
+ "login profile use"
46630
+ ]
46631
+ }),
46632
+ "profile show": buildCommandSpec({
46633
+ command: "login profile show",
46634
+ description: "Show current connection profile and authentication status.",
46635
+ usage: "xcsh login profile show [name]",
46636
+ flags: [
46637
+ {
46638
+ name: "name",
46639
+ description: "Profile name to show (optional, defaults to active)",
46640
+ type: "string"
46641
+ }
46642
+ ],
46643
+ examples: [
46644
+ {
46645
+ command: "xcsh login profile show",
46646
+ description: "Show active profile"
46647
+ },
46648
+ {
46649
+ command: "xcsh login profile show production",
46650
+ description: "Show specific profile"
46651
+ }
46652
+ ],
46653
+ category: "login",
46654
+ related: ["login profile list", "login profile use"]
46655
+ }),
46656
+ "profile create": buildCommandSpec({
46657
+ command: "login profile create",
46658
+ description: "Create a new connection profile with URL and credentials.",
46659
+ usage: "xcsh login profile create <name>",
46660
+ flags: [
46661
+ {
46662
+ name: "name",
46663
+ description: "Profile name",
46664
+ type: "string",
46665
+ required: true
46666
+ }
46667
+ ],
46668
+ examples: [
46669
+ {
46670
+ command: "xcsh login profile create production",
46671
+ description: "Create a new profile named 'production'"
46672
+ }
46673
+ ],
46674
+ category: "login",
46675
+ related: ["login profile list", "login profile use"]
46676
+ }),
46677
+ "profile use": buildCommandSpec({
46678
+ command: "login profile use",
46679
+ description: "Switch to a different connection profile.",
46680
+ usage: "xcsh login profile use <name>",
46681
+ flags: [
46682
+ {
46683
+ name: "name",
46684
+ description: "Profile name to activate",
46685
+ type: "string",
46686
+ required: true
46687
+ }
46688
+ ],
46689
+ examples: [
46690
+ {
46691
+ command: "xcsh login profile use staging",
46692
+ description: "Switch to staging profile"
46693
+ }
46694
+ ],
46695
+ category: "login",
46696
+ related: ["login profile list", "login profile show"]
46697
+ }),
46698
+ "context show": buildCommandSpec({
46699
+ command: "login context show",
46700
+ description: "Show the current default namespace context.",
46701
+ usage: "xcsh login context show",
46702
+ examples: [
46703
+ {
46704
+ command: "xcsh login context show",
46705
+ description: "Display current namespace"
46706
+ }
46707
+ ],
46708
+ category: "login",
46709
+ related: ["login context set", "login context list"]
46710
+ }),
46711
+ "context set": buildCommandSpec({
46712
+ command: "login context set",
46713
+ description: "Set the default namespace for subsequent operations.",
46714
+ usage: "xcsh login context set <namespace>",
46715
+ flags: [
46716
+ {
46717
+ name: "namespace",
46718
+ description: "Namespace to set as default",
46719
+ type: "string",
46720
+ required: true
46721
+ }
46722
+ ],
46723
+ examples: [
46724
+ {
46725
+ command: "xcsh login context set production",
46726
+ description: "Set production as default namespace"
46727
+ }
46728
+ ],
46729
+ category: "login",
46730
+ related: ["login context show", "login context list"]
46731
+ })
46732
+ };
46733
+ }
46734
+ function getCommandSpec(commandPath) {
46735
+ const cloudstatusSpecs = buildCloudstatusSpecs();
46736
+ const loginSpecs = buildLoginSpecs();
46737
+ const normalized = commandPath.toLowerCase().trim();
46738
+ if (normalized.startsWith("cloudstatus ")) {
46739
+ const subcommand = normalized.replace("cloudstatus ", "");
46740
+ return cloudstatusSpecs[subcommand];
46741
+ }
46742
+ if (normalized.startsWith("login ")) {
46743
+ const subcommand = normalized.replace("login ", "");
46744
+ return loginSpecs[subcommand];
46745
+ }
46746
+ return void 0;
46747
+ }
46748
+
46027
46749
  // src/repl/session.ts
46028
46750
  var NAMESPACE_CACHE_TTL = 5 * 60 * 1e3;
46029
46751
  var REPLSession = class {
@@ -46037,7 +46759,7 @@ var REPLSession = class {
46037
46759
  _serverUrl = "";
46038
46760
  _apiToken = "";
46039
46761
  _apiClient = null;
46040
- _outputFormat = "yaml";
46762
+ _outputFormat = "table";
46041
46763
  _debug = false;
46042
46764
  _profileManager;
46043
46765
  _activeProfile = null;
@@ -46051,7 +46773,7 @@ var REPLSession = class {
46051
46773
  this._profileManager = getProfileManager();
46052
46774
  this._serverUrl = config.serverUrl ?? process.env[`${ENV_PREFIX}_API_URL`] ?? "";
46053
46775
  this._apiToken = config.apiToken ?? process.env[`${ENV_PREFIX}_API_TOKEN`] ?? "";
46054
- this._outputFormat = config.outputFormat ?? "yaml";
46776
+ this._outputFormat = config.outputFormat ?? getOutputFormatFromEnv() ?? "table";
46055
46777
  this._debug = config.debug ?? process.env[`${ENV_PREFIX}_DEBUG`] === "true";
46056
46778
  if (this._serverUrl) {
46057
46779
  this._tenant = this.extractTenant(this._serverUrl);
@@ -46900,7 +47622,7 @@ function formatConfigSection() {
46900
47622
  }
46901
47623
 
46902
47624
  // src/repl/help.ts
46903
- function wrapText2(text, width, indent) {
47625
+ function wrapText3(text, width, indent) {
46904
47626
  const prefix = " ".repeat(indent);
46905
47627
  const words = text.split(/\s+/);
46906
47628
  const lines = [];
@@ -46924,7 +47646,7 @@ function formatRootHelp() {
46924
47646
  colorBoldWhite(`${CLI_NAME} - ${CLI_FULL_NAME} v${CLI_VERSION}`),
46925
47647
  "",
46926
47648
  "DESCRIPTION",
46927
- ...wrapText2(CLI_DESCRIPTION_LONG, 80, 2),
47649
+ ...wrapText3(CLI_DESCRIPTION_LONG, 80, 2),
46928
47650
  "",
46929
47651
  "USAGE",
46930
47652
  ` ${CLI_NAME} Enter interactive REPL mode`,
@@ -46936,7 +47658,6 @@ function formatRootHelp() {
46936
47658
  ` ${CLI_NAME} virtual get http_loadbalancer Get a specific load balancer`,
46937
47659
  ` ${CLI_NAME} dns list List DNS zones`,
46938
47660
  ` ${CLI_NAME} waf list -ns prod List WAF policies in prod`,
46939
- ` ${CLI_NAME} --interactive Force interactive REPL mode`,
46940
47661
  "",
46941
47662
  ...formatDomainsSection(),
46942
47663
  "",
@@ -46967,10 +47688,10 @@ function formatGlobalFlags() {
46967
47688
  "GLOBAL FLAGS",
46968
47689
  " -v, --version Show version number",
46969
47690
  " -h, --help Show this help",
46970
- " -i, --interactive Force interactive mode",
46971
47691
  " --no-color Disable color output",
46972
47692
  " -o, --output <fmt> Output format (json, yaml, table)",
46973
- " -ns, --namespace <ns> Target namespace"
47693
+ " -ns, --namespace <ns> Target namespace",
47694
+ " --spec Output command specification as JSON (for AI)"
46974
47695
  ];
46975
47696
  }
46976
47697
  function formatEnvironmentVariables() {
@@ -47235,7 +47956,7 @@ function formatDomainsSection() {
47235
47956
  function formatCustomDomainHelp(domain) {
47236
47957
  const output = ["", colorBoldWhite(domain.name), ""];
47237
47958
  output.push("DESCRIPTION");
47238
- output.push(...wrapText2(domain.description, 80, 2));
47959
+ output.push(...wrapText3(domain.description, 80, 2));
47239
47960
  output.push("");
47240
47961
  output.push("USAGE");
47241
47962
  output.push(` ${CLI_NAME} ${domain.name} <command> [options]`);
@@ -47265,7 +47986,7 @@ function formatSubcommandHelp(domainName, subcommand) {
47265
47986
  ""
47266
47987
  ];
47267
47988
  output.push("DESCRIPTION");
47268
- output.push(...wrapText2(subcommand.description, 80, 2));
47989
+ output.push(...wrapText3(subcommand.description, 80, 2));
47269
47990
  output.push("");
47270
47991
  output.push("USAGE");
47271
47992
  output.push(
@@ -47984,7 +48705,7 @@ var contextSubcommands = {
47984
48705
  };
47985
48706
 
47986
48707
  // src/config/settings.ts
47987
- var import_yaml2 = __toESM(require_dist(), 1);
48708
+ var import_yaml3 = __toESM(require_dist(), 1);
47988
48709
  import { homedir as homedir3 } from "os";
47989
48710
  import { join as join3 } from "path";
47990
48711
  var LOGO_MODES = [
@@ -48015,7 +48736,7 @@ function loadSettingsSync() {
48015
48736
  configPath,
48016
48737
  "utf-8"
48017
48738
  );
48018
- const parsed = import_yaml2.default.parse(content);
48739
+ const parsed = import_yaml3.default.parse(content);
48019
48740
  return {
48020
48741
  ...DEFAULT_SETTINGS,
48021
48742
  ...validateSettings(parsed)
@@ -48882,6 +49603,34 @@ function calculateRegionalStatus(components) {
48882
49603
  }
48883
49604
 
48884
49605
  // src/domains/cloudstatus/index.ts
49606
+ function parseOutputArgs(args, session) {
49607
+ let format;
49608
+ let spec = false;
49609
+ const filteredArgs = [];
49610
+ for (let i = 0; i < args.length; i++) {
49611
+ const arg = args[i] ?? "";
49612
+ const nextArg = args[i + 1];
49613
+ if (arg === "--output" || arg === "-o") {
49614
+ if (nextArg) {
49615
+ format = parseOutputFormat(nextArg);
49616
+ i++;
49617
+ }
49618
+ } else if (arg === "--spec") {
49619
+ spec = true;
49620
+ } else if (arg.startsWith("--output=")) {
49621
+ format = parseOutputFormat(arg.split("=")[1] ?? "table");
49622
+ } else if (arg.startsWith("-o=")) {
49623
+ format = parseOutputFormat(arg.split("=")[1] ?? "table");
49624
+ } else {
49625
+ filteredArgs.push(arg);
49626
+ }
49627
+ }
49628
+ return {
49629
+ format: format ?? session.getOutputFormat(),
49630
+ spec,
49631
+ filteredArgs
49632
+ };
49633
+ }
48885
49634
  var cloudstatusClient = null;
48886
49635
  function getClient() {
48887
49636
  if (!cloudstatusClient) {
@@ -48897,8 +49646,14 @@ var statusCommand = {
48897
49646
  usage: "[--quiet]",
48898
49647
  aliases: ["st"],
48899
49648
  async execute(args, session) {
48900
- const quiet = args.includes("--quiet") || args.includes("-q");
48901
- const format = session.getOutputFormat();
49649
+ const { format, spec, filteredArgs } = parseOutputArgs(args, session);
49650
+ const quiet = filteredArgs.includes("--quiet") || filteredArgs.includes("-q");
49651
+ if (spec) {
49652
+ const cmdSpec = getCommandSpec("cloudstatus status");
49653
+ if (cmdSpec) {
49654
+ return successResult([formatSpec(cmdSpec)]);
49655
+ }
49656
+ }
48902
49657
  try {
48903
49658
  const client = getClient();
48904
49659
  const response = await client.getStatus();
@@ -48948,8 +49703,14 @@ var summaryCommand = {
48948
49703
  usage: "[--brief]",
48949
49704
  aliases: ["sum"],
48950
49705
  async execute(args, session) {
48951
- const brief = args.includes("--brief") || args.includes("-b");
48952
- const format = session.getOutputFormat();
49706
+ const { format, spec, filteredArgs } = parseOutputArgs(args, session);
49707
+ const brief = filteredArgs.includes("--brief") || filteredArgs.includes("-b");
49708
+ if (spec) {
49709
+ const cmdSpec = getCommandSpec("cloudstatus summary");
49710
+ if (cmdSpec) {
49711
+ return successResult([formatSpec(cmdSpec)]);
49712
+ }
49713
+ }
48953
49714
  try {
48954
49715
  const client = getClient();
48955
49716
  const response = await client.getSummary();
@@ -48977,8 +49738,14 @@ var componentsCommand = {
48977
49738
  usage: "[--degraded-only]",
48978
49739
  aliases: ["comp"],
48979
49740
  async execute(args, session) {
48980
- const degradedOnly = args.includes("--degraded-only") || args.includes("-d");
48981
- const format = session.getOutputFormat();
49741
+ const { format, spec, filteredArgs } = parseOutputArgs(args, session);
49742
+ const degradedOnly = filteredArgs.includes("--degraded-only") || filteredArgs.includes("-d");
49743
+ if (spec) {
49744
+ const cmdSpec = getCommandSpec("cloudstatus components");
49745
+ if (cmdSpec) {
49746
+ return successResult([formatSpec(cmdSpec)]);
49747
+ }
49748
+ }
48982
49749
  try {
48983
49750
  const client = getClient();
48984
49751
  const response = await client.getComponents();
@@ -49020,8 +49787,14 @@ var incidentsCommand = {
49020
49787
  usage: "[--active-only]",
49021
49788
  aliases: ["inc"],
49022
49789
  async execute(args, session) {
49023
- const activeOnly = args.includes("--active-only") || args.includes("-a");
49024
- const format = session.getOutputFormat();
49790
+ const { format, spec, filteredArgs } = parseOutputArgs(args, session);
49791
+ const activeOnly = filteredArgs.includes("--active-only") || filteredArgs.includes("-a");
49792
+ if (spec) {
49793
+ const cmdSpec = getCommandSpec("cloudstatus incidents");
49794
+ if (cmdSpec) {
49795
+ return successResult([formatSpec(cmdSpec)]);
49796
+ }
49797
+ }
49025
49798
  try {
49026
49799
  const client = getClient();
49027
49800
  const response = activeOnly ? await client.getUnresolvedIncidents() : await client.getIncidents();
@@ -49061,8 +49834,14 @@ var maintenanceCommand = {
49061
49834
  usage: "[--upcoming]",
49062
49835
  aliases: ["maint"],
49063
49836
  async execute(args, session) {
49064
- const upcomingOnly = args.includes("--upcoming") || args.includes("-u");
49065
- const format = session.getOutputFormat();
49837
+ const { format, spec, filteredArgs } = parseOutputArgs(args, session);
49838
+ const upcomingOnly = filteredArgs.includes("--upcoming") || filteredArgs.includes("-u");
49839
+ if (spec) {
49840
+ const cmdSpec = getCommandSpec("cloudstatus maintenance");
49841
+ if (cmdSpec) {
49842
+ return successResult([formatSpec(cmdSpec)]);
49843
+ }
49844
+ }
49066
49845
  try {
49067
49846
  const client = getClient();
49068
49847
  const response = upcomingOnly ? await client.getUpcomingMaintenances() : await client.getMaintenances();
@@ -49534,7 +50313,7 @@ _xcsh_completions() {
49534
50313
  local commands="${domainNames} ${allAliases.join(" ")} help quit exit clear history"
49535
50314
  local actions="${actions}"
49536
50315
  local builtins="help quit exit clear history context ctx"
49537
- local global_flags="--help -h --version -v --interactive -i --no-color --output -o --namespace -ns"
50316
+ local global_flags="--help -h --version -v --no-color --output -o --namespace -ns --spec"
49538
50317
 
49539
50318
  # Handle completion based on position
49540
50319
  case \${cword} in
@@ -49565,7 +50344,7 @@ ${customDomainCompletions.join("\n")}
49565
50344
  *)
49566
50345
  # Third+ word: flags
49567
50346
  if [[ "\${cur}" == -* ]]; then
49568
- local action_flags="--name -n --namespace -ns --output -o --json --yaml --limit --label"
50347
+ local action_flags="--name -n --namespace -ns --output -o --json --yaml --limit --label --spec"
49569
50348
  COMPREPLY=($(compgen -W "\${action_flags}" -- "\${cur}"))
49570
50349
  fi
49571
50350
  return 0
@@ -49616,10 +50395,10 @@ _xcsh() {
49616
50395
  global_opts=(
49617
50396
  '(-h --help)'{-h,--help}'[Show help information]'
49618
50397
  '(-v --version)'{-v,--version}'[Show version number]'
49619
- '(-i --interactive)'{-i,--interactive}'[Force interactive mode]'
49620
50398
  '--no-color[Disable color output]'
49621
50399
  '(-o --output)'{-o,--output}'[Output format]:format:(json yaml table)'
49622
50400
  '(-ns --namespace)'{-ns,--namespace}'[Namespace]:namespace:_xcsh_namespaces'
50401
+ '--spec[Output command specification as JSON for AI assistants]'
49623
50402
  )
49624
50403
 
49625
50404
  _arguments -C \\
@@ -49670,6 +50449,7 @@ ${customDomainCompletions.join("\n")}
49670
50449
  '--limit[Maximum results]:limit:'
49671
50450
  '--label[Filter by label]:label:'
49672
50451
  '(-f --file)'{-f,--file}'[Configuration file]:file:_files'
50452
+ '--spec[Output command specification as JSON for AI assistants]'
49673
50453
  )
49674
50454
  _arguments "\${action_opts[@]}"
49675
50455
  ;;
@@ -49730,10 +50510,10 @@ complete -c xcsh -f
49730
50510
  # Global options
49731
50511
  complete -c xcsh -s h -l help -d 'Show help information'
49732
50512
  complete -c xcsh -s v -l version -d 'Show version number'
49733
- complete -c xcsh -s i -l interactive -d 'Force interactive mode'
49734
50513
  complete -c xcsh -l no-color -d 'Disable color output'
49735
50514
  complete -c xcsh -s o -l output -d 'Output format' -xa 'json yaml table'
49736
50515
  complete -c xcsh -l namespace -s ns -d 'Namespace' -xa 'default system shared'
50516
+ complete -c xcsh -l spec -d 'Output command specification as JSON for AI assistants'
49737
50517
 
49738
50518
  # Builtin commands
49739
50519
  complete -c xcsh -n "__fish_use_subcommand" -a "help" -d 'Show help information'
@@ -49763,6 +50543,7 @@ complete -c xcsh -l label -d 'Filter by label'
49763
50543
  complete -c xcsh -s f -l file -d 'Configuration file' -r
49764
50544
  complete -c xcsh -l force -d 'Force deletion'
49765
50545
  complete -c xcsh -l cascade -d 'Cascade delete'
50546
+ complete -c xcsh -l spec -d 'Output command specification as JSON for AI assistants'
49766
50547
  `;
49767
50548
  }
49768
50549
 
@@ -50929,219 +51710,6 @@ function useCompletion(options) {
50929
51710
  };
50930
51711
  }
50931
51712
 
50932
- // src/output/formatter.ts
50933
- var import_yaml3 = __toESM(require_dist(), 1);
50934
- function formatOutput(data, format = "yaml") {
50935
- if (format === "none") {
50936
- return "";
50937
- }
50938
- switch (format) {
50939
- case "json":
50940
- return formatJSON(data);
50941
- case "yaml":
50942
- return formatYAML(data);
50943
- case "table":
50944
- case "text":
50945
- return formatTable(data);
50946
- case "tsv":
50947
- return formatTSV(data);
50948
- default:
50949
- return formatYAML(data);
50950
- }
50951
- }
50952
- function formatJSON(data) {
50953
- return JSON.stringify(data, null, 2);
50954
- }
50955
- function formatYAML(data) {
50956
- return import_yaml3.default.stringify(data, { indent: 2 });
50957
- }
50958
- function extractItems(data) {
50959
- if (data && typeof data === "object" && "items" in data) {
50960
- const items = data.items;
50961
- if (Array.isArray(items)) {
50962
- return items.filter(
50963
- (item) => item !== null && typeof item === "object"
50964
- );
50965
- }
50966
- }
50967
- if (Array.isArray(data)) {
50968
- return data.filter(
50969
- (item) => item !== null && typeof item === "object"
50970
- );
50971
- }
50972
- if (data && typeof data === "object") {
50973
- return [data];
50974
- }
50975
- return [];
50976
- }
50977
- function getStringField(obj, key) {
50978
- const value = obj[key];
50979
- if (typeof value === "string") {
50980
- return value;
50981
- }
50982
- if (value !== null && value !== void 0) {
50983
- return String(value);
50984
- }
50985
- return "";
50986
- }
50987
- function formatLabels(obj) {
50988
- const labels = obj["labels"];
50989
- if (!labels || typeof labels !== "object") {
50990
- return "";
50991
- }
50992
- const labelMap = labels;
50993
- const entries = Object.entries(labelMap).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`);
50994
- if (entries.length === 0) {
50995
- return "";
50996
- }
50997
- return `map[${entries.join(" ")}]`;
50998
- }
50999
- function wrapText3(text, maxWidth) {
51000
- if (text.length <= maxWidth) {
51001
- return [text];
51002
- }
51003
- const lines = [];
51004
- let remaining = text;
51005
- while (remaining.length > 0) {
51006
- if (remaining.length <= maxWidth) {
51007
- lines.push(remaining);
51008
- break;
51009
- }
51010
- let breakPoint = maxWidth;
51011
- for (let i = maxWidth - 1; i > 0; i--) {
51012
- if (remaining[i] === " ") {
51013
- breakPoint = i;
51014
- break;
51015
- }
51016
- }
51017
- lines.push(remaining.slice(0, breakPoint));
51018
- remaining = remaining.slice(breakPoint).trimStart();
51019
- }
51020
- return lines;
51021
- }
51022
- function formatTable(data) {
51023
- const items = extractItems(data);
51024
- if (items.length === 0) {
51025
- return "";
51026
- }
51027
- const headers = ["NAMESPACE", "NAME", "LABELS"];
51028
- const widths = [9, 27, 30];
51029
- const rows = [];
51030
- for (const item of items) {
51031
- const row = [
51032
- getStringField(item, "namespace") || "<None>",
51033
- getStringField(item, "name") || "<None>",
51034
- formatLabels(item) || "<None>"
51035
- ];
51036
- const wrappedCells = row.map((cell, i) => wrapText3(cell, widths[i]));
51037
- const maxLines = Math.max(...wrappedCells.map((c) => c.length));
51038
- const wrappedRows = [];
51039
- for (let line = 0; line < maxLines; line++) {
51040
- wrappedRows.push(wrappedCells.map((c) => c[line] ?? ""));
51041
- }
51042
- rows.push(wrappedRows);
51043
- }
51044
- const lines = [];
51045
- const boxLine = "+" + widths.map((w) => "-".repeat(w + 2)).join("+") + "+";
51046
- lines.push(boxLine);
51047
- lines.push(
51048
- "|" + headers.map((h, i) => {
51049
- const padding = widths[i] - h.length;
51050
- const leftPad = Math.floor(padding / 2);
51051
- const rightPad = padding - leftPad;
51052
- return " " + " ".repeat(leftPad) + h + " ".repeat(rightPad) + " ";
51053
- }).join("|") + "|"
51054
- );
51055
- lines.push(boxLine);
51056
- for (const wrappedRows of rows) {
51057
- for (const row of wrappedRows) {
51058
- lines.push(
51059
- "|" + row.map((cell, i) => {
51060
- const padding = widths[i] - cell.length;
51061
- return " " + cell + " ".repeat(padding) + " ";
51062
- }).join("|") + "|"
51063
- );
51064
- }
51065
- lines.push(boxLine);
51066
- }
51067
- return lines.join("\n");
51068
- }
51069
- function formatTSV(data) {
51070
- const items = extractItems(data);
51071
- if (items.length === 0) {
51072
- return "";
51073
- }
51074
- const allKeys = /* @__PURE__ */ new Set();
51075
- for (const item of items) {
51076
- Object.keys(item).forEach((k) => allKeys.add(k));
51077
- }
51078
- const priority = ["name", "namespace", "status", "created", "modified"];
51079
- const headers = [
51080
- ...priority.filter((p) => allKeys.has(p)),
51081
- ...[...allKeys].filter((k) => !priority.includes(k)).sort()
51082
- ];
51083
- const lines = [];
51084
- for (const item of items) {
51085
- const values = headers.map((h) => {
51086
- const val = item[h];
51087
- if (val === null || val === void 0) return "";
51088
- if (typeof val === "object") return JSON.stringify(val);
51089
- return String(val);
51090
- });
51091
- lines.push(values.join(" "));
51092
- }
51093
- return lines.join("\n");
51094
- }
51095
- function formatAPIError(statusCode, body, operation) {
51096
- const lines = [];
51097
- lines.push(`ERROR: ${operation} failed (HTTP ${statusCode})`);
51098
- if (body && typeof body === "object") {
51099
- const errResp = body;
51100
- if (errResp.message) {
51101
- lines.push(` Message: ${errResp.message}`);
51102
- }
51103
- if (errResp.code) {
51104
- lines.push(` Code: ${errResp.code}`);
51105
- }
51106
- if (errResp.details) {
51107
- lines.push(` Details: ${errResp.details}`);
51108
- }
51109
- }
51110
- switch (statusCode) {
51111
- case 401:
51112
- lines.push(
51113
- "\nHint: Authentication failed. Check your credentials with 'login profile show'"
51114
- );
51115
- break;
51116
- case 403:
51117
- lines.push(
51118
- "\nHint: Permission denied. You may not have access to this resource."
51119
- );
51120
- break;
51121
- case 404:
51122
- lines.push(
51123
- "\nHint: Resource not found. Verify the name and namespace are correct."
51124
- );
51125
- break;
51126
- case 409:
51127
- lines.push(
51128
- "\nHint: Conflict - resource may already exist or be in a conflicting state."
51129
- );
51130
- break;
51131
- case 429:
51132
- lines.push("\nHint: Rate limited. Please wait and try again.");
51133
- break;
51134
- case 500:
51135
- case 502:
51136
- case 503:
51137
- lines.push(
51138
- "\nHint: Server error. Please try again later or contact support."
51139
- );
51140
- break;
51141
- }
51142
- return lines.join("\n");
51143
- }
51144
-
51145
51713
  // src/repl/executor.ts
51146
51714
  var BUILTIN_COMMANDS = /* @__PURE__ */ new Set([
51147
51715
  "help",
@@ -51691,34 +52259,66 @@ function domainToResourcePath(domain) {
51691
52259
  function parseCommandArgs(args) {
51692
52260
  let name;
51693
52261
  let namespace;
52262
+ let outputFormat;
52263
+ let spec = false;
52264
+ let noColor = false;
51694
52265
  for (let i = 0; i < args.length; i++) {
51695
52266
  const arg = args[i] ?? "";
51696
52267
  if (arg.startsWith("--")) {
51697
- const flagName = arg.slice(2);
52268
+ const flagName = arg.slice(2).toLowerCase();
51698
52269
  const nextArg = args[i + 1];
51699
- if (flagName === "namespace" || flagName === "ns") {
51700
- namespace = nextArg;
51701
- i++;
51702
- } else if (flagName === "name") {
51703
- name = nextArg;
51704
- i++;
51705
- } else if (nextArg && !nextArg.startsWith("--")) {
51706
- i++;
52270
+ switch (flagName) {
52271
+ case "namespace":
52272
+ case "ns":
52273
+ namespace = nextArg;
52274
+ i++;
52275
+ break;
52276
+ case "name":
52277
+ name = nextArg;
52278
+ i++;
52279
+ break;
52280
+ case "output":
52281
+ if (nextArg) {
52282
+ outputFormat = parseOutputFormat(nextArg);
52283
+ i++;
52284
+ }
52285
+ break;
52286
+ case "spec":
52287
+ spec = true;
52288
+ break;
52289
+ case "no-color":
52290
+ noColor = true;
52291
+ break;
52292
+ default:
52293
+ if (nextArg && !nextArg.startsWith("--")) {
52294
+ i++;
52295
+ }
51707
52296
  }
51708
52297
  } else if (arg.startsWith("-")) {
51709
52298
  const flagName = arg.slice(1);
51710
52299
  const nextArg = args[i + 1];
51711
- if (flagName === "n") {
51712
- namespace = nextArg;
51713
- i++;
51714
- } else if (nextArg && !nextArg.startsWith("-")) {
51715
- i++;
52300
+ switch (flagName) {
52301
+ case "n":
52302
+ case "ns":
52303
+ namespace = nextArg;
52304
+ i++;
52305
+ break;
52306
+ case "o":
52307
+ if (nextArg) {
52308
+ outputFormat = parseOutputFormat(nextArg);
52309
+ i++;
52310
+ }
52311
+ break;
52312
+ default:
52313
+ if (nextArg && !nextArg.startsWith("-")) {
52314
+ i++;
52315
+ }
51716
52316
  }
51717
52317
  } else if (!name) {
51718
52318
  name = arg;
51719
52319
  }
51720
52320
  }
51721
- return { name, namespace };
52321
+ return { name, namespace, outputFormat, spec, noColor };
51722
52322
  }
51723
52323
  async function executeAPICommand(session, ctx, cmd) {
51724
52324
  const client = session.getAPIClient();
@@ -51767,8 +52367,51 @@ async function executeAPICommand(session, ctx, cmd) {
51767
52367
  }
51768
52368
  }
51769
52369
  const canonicalDomain = resolveDomain(domain) ?? domain;
51770
- const { name, namespace } = parseCommandArgs(args);
52370
+ const { name, namespace, outputFormat, spec, noColor } = parseCommandArgs(args);
51771
52371
  const effectiveNamespace = namespace ?? session.getNamespace();
52372
+ if (spec) {
52373
+ const commandPath = `${canonicalDomain} ${action}`;
52374
+ const cmdSpec = getCommandSpec(commandPath);
52375
+ if (cmdSpec) {
52376
+ return {
52377
+ output: [formatSpec(cmdSpec)],
52378
+ shouldExit: false,
52379
+ shouldClear: false,
52380
+ contextChanged: false
52381
+ };
52382
+ }
52383
+ const basicSpec = {
52384
+ command: `${CLI_NAME} ${canonicalDomain} ${action}`,
52385
+ description: `Execute ${action} on ${canonicalDomain} resources`,
52386
+ usage: `${CLI_NAME} ${canonicalDomain} ${action} [name] [options]`,
52387
+ flags: [
52388
+ {
52389
+ name: "--namespace",
52390
+ alias: "-ns",
52391
+ type: "string",
52392
+ description: "Target namespace"
52393
+ },
52394
+ {
52395
+ name: "--output",
52396
+ alias: "-o",
52397
+ type: "string",
52398
+ description: "Output format (json, yaml, table)"
52399
+ },
52400
+ {
52401
+ name: "--name",
52402
+ type: "string",
52403
+ description: "Resource name"
52404
+ }
52405
+ ],
52406
+ outputFormats: ["table", "json", "yaml"]
52407
+ };
52408
+ return {
52409
+ output: [JSON.stringify(basicSpec, null, 2)],
52410
+ shouldExit: false,
52411
+ shouldClear: false,
52412
+ contextChanged: false
52413
+ };
52414
+ }
51772
52415
  const resourcePath = domainToResourcePath(canonicalDomain);
51773
52416
  let apiPath = `/api/config/namespaces/${effectiveNamespace}/${resourcePath}`;
51774
52417
  try {
@@ -51853,8 +52496,8 @@ async function executeAPICommand(session, ctx, cmd) {
51853
52496
  };
51854
52497
  }
51855
52498
  }
51856
- const outputFormat = session.getOutputFormat();
51857
- const formatted = formatOutput(result, outputFormat);
52499
+ const effectiveFormat = outputFormat ?? session.getOutputFormat();
52500
+ const formatted = formatOutput(result, effectiveFormat, noColor);
51858
52501
  return {
51859
52502
  output: formatted ? [formatted] : ["(no output)"],
51860
52503
  shouldExit: false,
@@ -52245,7 +52888,7 @@ var program2 = new Command();
52245
52888
  program2.configureHelp({
52246
52889
  formatHelp: () => formatRootHelp().join("\n")
52247
52890
  });
52248
- program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CLI for F5 XC").version(CLI_VERSION, "-v, --version", "Show version number").option("-i, --interactive", "Force interactive mode").option("--no-color", "Disable color output").option("--logo <mode>", "Logo display mode: image, ascii, none").option("-h, --help", "Show help").argument("[command...]", "Command to execute non-interactively").allowUnknownOption(true).helpOption(false).action(
52891
+ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CLI for F5 XC").version(CLI_VERSION, "-v, --version", "Show version number").option("--no-color", "Disable color output").option("--logo <mode>", "Logo display mode: image, ascii, none").option("-o, --output <format>", "Output format (json, yaml, table)").option("--spec", "Output command specification as JSON (for AI)").option("-h, --help", "Show help").argument("[command...]", "Command to execute non-interactively").allowUnknownOption(true).helpOption(false).action(
52249
52892
  async (commandArgs, options) => {
52250
52893
  if (options.help && commandArgs.length === 0) {
52251
52894
  formatRootHelp().forEach((line) => console.log(line));
@@ -52257,8 +52900,14 @@ program2.name(CLI_NAME).description("F5 Distributed Cloud Shell - Interactive CL
52257
52900
  if (options.logo && commandArgs.length > 0) {
52258
52901
  commandArgs.push("--logo", options.logo);
52259
52902
  }
52260
- if (commandArgs.length === 0 || options.interactive) {
52261
- if (!process.stdin.isTTY && !options.interactive) {
52903
+ if (options.output && commandArgs.length > 0) {
52904
+ commandArgs.push("--output", options.output);
52905
+ }
52906
+ if (options.spec && commandArgs.length > 0) {
52907
+ commandArgs.push("--spec");
52908
+ }
52909
+ if (commandArgs.length === 0) {
52910
+ if (!process.stdin.isTTY) {
52262
52911
  console.error(
52263
52912
  "Error: Interactive mode requires a terminal (TTY)."
52264
52913
  );