@robinmordasiewicz/f5xc-xcsh 6.34.0 → 6.36.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
@@ -44108,9 +44108,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44108
44108
  ["admin_console_and_ui", {
44109
44109
  name: "admin_console_and_ui",
44110
44110
  displayName: "Admin Console And Ui",
44111
- description: "Set up static resource definitions that control administrative dashboard appearance. Register named entries using object references, status tracking fields, and view settings. Fetch individual records through dedicated get operations or enumerate available entries with pagination and sorting. Handle request validation through typed error responses and support multiple output codes for success states. Define custom initialization blocks and namespace-scoped boundaries for organization.",
44112
- descriptionShort: "Manage static UI assets for admin console",
44113
- descriptionMedium: "Deploy and list dashboard widgets within namespaces. Create named visual resources with initialization parameters and structured configuration data.",
44111
+ description: "Create administrative dashboard building blocks with tailored setup data and view bindings. Organize presentational materials by namespace and fetch them by name or list all available items. Define display parameters, track system object relationships, and maintain consistent portal appearance through centralized resource management workflows.",
44112
+ descriptionShort: "Manage static UI components for admin console",
44113
+ descriptionMedium: "Deploy and retrieve graphical elements within namespaces. Configure custom startup parameters and view references for display composition.",
44114
44114
  aliases: ["console-ui", "ui-assets", "static-components"],
44115
44115
  complexity: "simple",
44116
44116
  isPreview: false,
@@ -44242,9 +44242,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44242
44242
  ["bot_and_threat_defense", {
44243
44243
  name: "bot_and_threat_defense",
44244
44244
  displayName: "Bot And Threat Defense",
44245
- description: "Deploy Shape bot defense instances with namespace-scoped configuration for automated threat detection. Create TPM categories to classify and organize threat types across your security infrastructure. Generate and manage provisioning keys for programmatic access to defense systems. Set up threat managers to coordinate detection rules, integrate with WAF policies, and enable real-time protection against malicious traffic patterns.",
44246
- descriptionShort: "Configure bot protection and threat categories",
44247
- descriptionMedium: "Manage bot defense instances and threat classification per namespace. Provision automated defense keys for security integration.",
44245
+ description: "Deploy namespace-scoped protection using behavioral analysis and machine learning. Provision dedicated keys for system automation and real-time intelligence feeds. Coordinate detection across protected applications through centralized managers. Configure pre-authentication checks to identify suspicious patterns before they reach backends. Enable adaptive blocking decisions based on risk scoring and historical activity profiles.",
44246
+ descriptionShort: "Detect and block automated attacks",
44247
+ descriptionMedium: "Create bot defense instances with Shape integration. Set up traffic classification rules and automated response policies for malicious actors.",
44248
44248
  aliases: ["threat-defense", "tpm", "shape-bot"],
44249
44249
  complexity: "moderate",
44250
44250
  isPreview: false,
@@ -44256,9 +44256,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44256
44256
  ["cdn", {
44257
44257
  name: "cdn",
44258
44258
  displayName: "Cdn",
44259
- description: "Create cache rules with cookie, header, and query parameter matching to control content eligibility. Configure load balancers with origin pool routing, access logging, and metrics collection. Manage cache purge operations for immediate content invalidation. Monitor service operation status and aggregate access logs for performance analysis. Define path matchers and expressions for granular cache behavior control across namespaces.",
44260
- descriptionShort: "Configure caching rules and load balancers",
44261
- descriptionMedium: "Define cache TTL policies and path matching rules. Set up load balancers with origin pools and purge controls for content delivery.",
44259
+ description: "Set up cache eligibility based on headers, cookies, and query parameters. Create expression-based rules with custom TTL settings and path matchers. Deploy load balancers that handle content distribution across origin pools. Monitor access logs and metrics, aggregate performance data, and execute cache purge operations when content updates require immediate invalidation.",
44260
+ descriptionShort: "Configure caching rules and load balancing",
44261
+ descriptionMedium: "Define cache rules, TTLs, and path matching. Manage load balancers with origin pools and purge operations.",
44262
44262
  aliases: ["cache", "content"],
44263
44263
  complexity: "advanced",
44264
44264
  isPreview: false,
@@ -45166,6 +45166,51 @@ var HistoryManager = class _HistoryManager {
45166
45166
  }
45167
45167
  };
45168
45168
 
45169
+ // src/domains/descriptions.generated.ts
45170
+ var generatedDescriptions = {
45171
+ version: "1.0.0",
45172
+ generatedAt: "2025-12-30T03:58:36.781Z",
45173
+ cli: {
45174
+ "xcsh": {
45175
+ short: "Navigate cloud services via interactive shell",
45176
+ medium: "Manage multi-tenant connections, execute domain operations across 100+ services, and output results in JSON, YAML, or table format.",
45177
+ long: "Interact with cloud services through an intelligent shell environment. Navigate over 100 domain operations using tab completion for commands, flags, and values. Organize multiple tenant connections with named profiles and switch contexts without re-authenticating. Execute commands directly from scripts or explore interactively with history and suggestions. Configure output format as JSON, YAML, or formatted tables. Set behavior through environment variables or persistent profile settings. Generate shell completions for bash, zsh, and fish terminals."
45178
+ }
45179
+ },
45180
+ domains: {
45181
+ "login": {
45182
+ short: "Configure session credentials and environment profiles",
45183
+ medium: "Set up authentication tokens, organize named profiles for multiple tenants, and switch between target environments for CLI operations.",
45184
+ long: "Control authentication and session state across multiple environments. Run 'show' to display current connection details and token status. Organize credentials with 'profile' to maintain separate configurations for development, staging, and production tenants. Switch active targets via 'context' without re-authenticating. Run 'banner' for visual confirmation of the active environment. Profiles persist locally with token-based and certificate authentication support.",
45185
+ subcommands: {
45186
+ "profile": {
45187
+ short: "Manage saved connection configurations for authentication",
45188
+ medium: "Store and switch between multiple tenant connection settings. Create, list, and activate named configurations to avoid repeated credential entry.",
45189
+ long: "Organize tenant connections as reusable named entries for rapid environment switching. Each entry persists URLs and authentication tokens, removing manual reconfiguration overhead. Available operations: 'list' enumerates saved entries, 'show' reveals configuration details, 'create' registers new connections, 'delete' purges obsolete ones, 'active' identifies the current selection, and 'use' changes context. Ideal for workflows spanning development, staging, and production tiers."
45190
+ },
45191
+ "context": {
45192
+ short: "Manage default namespace for scoping operations",
45193
+ medium: "Configure and display the active namespace used to scope commands. Set, view, or list available namespaces for your session.",
45194
+ long: "Control which namespace subsequent operations target by default. Namespaces partition resources and configurations, ensuring commands affect only the intended area. Use 'show' to display the current selection, 'set' to switch contexts, and 'list' to enumerate accessible options. Once configured, the choice persists across commands until explicitly changed, eliminating repeated namespace flag usage."
45195
+ }
45196
+ }
45197
+ },
45198
+ "cloudstatus": {
45199
+ short: "Check infrastructure health and active incidents",
45200
+ medium: "Query service availability, component health, ongoing incidents, and scheduled maintenance windows across infrastructure regions.",
45201
+ long: "Display real-time operational state for infrastructure components and services. Track ongoing incidents with severity levels and resolution timelines. View upcoming maintenance windows affecting particular regions or deployments. Filter results by component type, severity, time range, or operational condition. Retrieve incident history and outage notifications. Commands support monitoring degraded performance indicators and uptime metrics useful for operations teams."
45202
+ },
45203
+ "completion": {
45204
+ short: "Generate tab-assist scripts for supported shells",
45205
+ medium: "Create shell scripts enabling tab-triggered suggestions for commands, subcommands, flags, and option values in bash, zsh, and fish.",
45206
+ long: "Output autocomplete functionality for your terminal environment. Bash, zsh, and fish are fully supported with context-aware prompts covering command names, nested subcommands, available flags, and valid argument values. Installation requires sourcing the generated content in your shell's configuration file (.bashrc, .zshrc, or config.fish). Execute the appropriate subcommand to produce the script, then follow shell-specific setup instructions to activate intelligent tab behavior."
45207
+ }
45208
+ }
45209
+ };
45210
+ function getCliDescriptions(cliName = "xcsh") {
45211
+ return generatedDescriptions.cli?.[cliName];
45212
+ }
45213
+
45169
45214
  // src/branding/terminal.ts
45170
45215
  function detectTerminalCapabilities() {
45171
45216
  const termProgram = process.env.TERM_PROGRAM ?? "";
@@ -45309,8 +45354,8 @@ function getLogoModeFromEnv(envPrefix) {
45309
45354
  var CLI_NAME = "xcsh";
45310
45355
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
45311
45356
  function getVersion() {
45312
- if ("6.34.0") {
45313
- return "6.34.0";
45357
+ if ("6.36.0") {
45358
+ return "6.36.0";
45314
45359
  }
45315
45360
  if (process.env.XCSH_VERSION) {
45316
45361
  return process.env.XCSH_VERSION;
@@ -45318,6 +45363,10 @@ function getVersion() {
45318
45363
  return "dev";
45319
45364
  }
45320
45365
  var CLI_VERSION = getVersion();
45366
+ var cliDescs = getCliDescriptions();
45367
+ var CLI_DESCRIPTION_SHORT = cliDescs?.short ?? "Navigate cloud services via interactive shell";
45368
+ var CLI_DESCRIPTION_MEDIUM = cliDescs?.medium ?? CLI_DESCRIPTION_SHORT;
45369
+ var CLI_DESCRIPTION_LONG = cliDescs?.long ?? CLI_DESCRIPTION_MEDIUM;
45321
45370
  var CONFIG_FILE_NAME = ".xcshconfig";
45322
45371
  var ENV_PREFIX = "F5XC";
45323
45372
  var F5_LOGO = ` ________
@@ -46794,96 +46843,542 @@ function useHistory(options) {
46794
46843
  // src/repl/hooks/useCompletion.ts
46795
46844
  var import_react27 = __toESM(require_react(), 1);
46796
46845
 
46797
- // src/domains/registry.ts
46798
- var DomainRegistry = class {
46799
- domains = /* @__PURE__ */ new Map();
46800
- /**
46801
- * Register a custom domain
46802
- */
46803
- register(domain) {
46804
- this.domains.set(domain.name, domain);
46846
+ // src/config/envvars.ts
46847
+ var EnvVarRegistry = [
46848
+ {
46849
+ name: `${ENV_PREFIX}_API_URL`,
46850
+ description: "API endpoint URL",
46851
+ relatedFlag: "",
46852
+ required: true
46853
+ },
46854
+ {
46855
+ name: `${ENV_PREFIX}_API_TOKEN`,
46856
+ description: "API authentication token",
46857
+ relatedFlag: "",
46858
+ required: true
46859
+ },
46860
+ {
46861
+ name: `${ENV_PREFIX}_NAMESPACE`,
46862
+ description: "Default namespace",
46863
+ relatedFlag: "-ns"
46864
+ },
46865
+ {
46866
+ name: `${ENV_PREFIX}_OUTPUT_FORMAT`,
46867
+ description: "Output format (json, yaml, table)",
46868
+ relatedFlag: "-o"
46869
+ },
46870
+ {
46871
+ name: `${ENV_PREFIX}_LOGO`,
46872
+ description: "Logo display mode (auto, image, ascii, both, none)",
46873
+ relatedFlag: "--logo"
46874
+ },
46875
+ {
46876
+ name: "NO_COLOR",
46877
+ description: "Disable color output",
46878
+ relatedFlag: "--no-color"
46805
46879
  }
46806
- /**
46807
- * Check if a domain is registered
46808
- */
46809
- has(name) {
46810
- return this.domains.has(name);
46880
+ ];
46881
+ function formatEnvVarsSection() {
46882
+ const maxLen = Math.max(...EnvVarRegistry.map((e) => e.name.length));
46883
+ const lines = ["ENVIRONMENT VARIABLES"];
46884
+ for (const env3 of EnvVarRegistry) {
46885
+ const padding = " ".repeat(maxLen - env3.name.length + 3);
46886
+ const flagNote = env3.relatedFlag ? ` [${env3.relatedFlag}]` : "";
46887
+ lines.push(` ${env3.name}${padding}${env3.description}${flagNote}`);
46811
46888
  }
46812
- /**
46813
- * Get a domain by name
46814
- */
46815
- get(name) {
46816
- return this.domains.get(name);
46889
+ return lines;
46890
+ }
46891
+ function formatConfigSection() {
46892
+ return [
46893
+ "CONFIGURATION",
46894
+ ` Config file: ~/${CONFIG_FILE_NAME}`,
46895
+ " Priority: CLI flags > environment variables > config file > defaults",
46896
+ "",
46897
+ "DOCUMENTATION",
46898
+ ` ${DOCS_URL}`
46899
+ ];
46900
+ }
46901
+
46902
+ // src/repl/help.ts
46903
+ function wrapText2(text, width, indent) {
46904
+ const prefix = " ".repeat(indent);
46905
+ const words = text.split(/\s+/);
46906
+ const lines = [];
46907
+ let currentLine = prefix;
46908
+ for (const word of words) {
46909
+ if (currentLine.length + word.length + 1 > width && currentLine !== prefix) {
46910
+ lines.push(currentLine);
46911
+ currentLine = prefix + word;
46912
+ } else {
46913
+ currentLine += (currentLine === prefix ? "" : " ") + word;
46914
+ }
46817
46915
  }
46818
- /**
46819
- * Get all registered domain names
46820
- */
46821
- list() {
46822
- return Array.from(this.domains.keys());
46916
+ if (currentLine.trim()) {
46917
+ lines.push(currentLine);
46823
46918
  }
46824
- /**
46825
- * Get all domains
46826
- */
46827
- all() {
46828
- return Array.from(this.domains.values());
46919
+ return lines;
46920
+ }
46921
+ function formatRootHelp() {
46922
+ return [
46923
+ "",
46924
+ colorBoldWhite(`${CLI_NAME} - ${CLI_FULL_NAME} v${CLI_VERSION}`),
46925
+ "",
46926
+ "DESCRIPTION",
46927
+ ...wrapText2(CLI_DESCRIPTION_LONG, 80, 2),
46928
+ "",
46929
+ "USAGE",
46930
+ ` ${CLI_NAME} Enter interactive REPL mode`,
46931
+ ` ${CLI_NAME} <domain> <action> Execute command non-interactively`,
46932
+ ` ${CLI_NAME} help [topic] Show help for a topic`,
46933
+ "",
46934
+ "EXAMPLES",
46935
+ ` ${CLI_NAME} tenant_and_identity list namespace List all namespaces`,
46936
+ ` ${CLI_NAME} virtual get http_loadbalancer Get a specific load balancer`,
46937
+ ` ${CLI_NAME} dns list List DNS zones`,
46938
+ ` ${CLI_NAME} waf list -ns prod List WAF policies in prod`,
46939
+ ` ${CLI_NAME} --interactive Force interactive REPL mode`,
46940
+ "",
46941
+ ...formatDomainsSection(),
46942
+ "",
46943
+ ...formatGlobalFlags(),
46944
+ "",
46945
+ ...formatEnvVarsSection(),
46946
+ "",
46947
+ ...formatConfigSection(),
46948
+ "",
46949
+ "NAVIGATION (Interactive Mode)",
46950
+ " <domain> Navigate into a domain (e.g., 'dns', 'lb')",
46951
+ " /domain Navigate directly to domain from anywhere",
46952
+ " .. Go up one level",
46953
+ " / Return to root",
46954
+ " context Show current navigation context",
46955
+ "",
46956
+ "BUILTINS",
46957
+ " help Show this help",
46958
+ " domains List all available domains",
46959
+ " clear Clear the screen",
46960
+ " history Show command history",
46961
+ " quit, exit Exit the shell",
46962
+ ""
46963
+ ];
46964
+ }
46965
+ function formatGlobalFlags() {
46966
+ return [
46967
+ "GLOBAL FLAGS",
46968
+ " -v, --version Show version number",
46969
+ " -h, --help Show this help",
46970
+ " -i, --interactive Force interactive mode",
46971
+ " --no-color Disable color output",
46972
+ " -o, --output <fmt> Output format (json, yaml, table)",
46973
+ " -ns, --namespace <ns> Target namespace"
46974
+ ];
46975
+ }
46976
+ function formatEnvironmentVariables() {
46977
+ return formatEnvVarsSection();
46978
+ }
46979
+ function formatDomainHelp(domain) {
46980
+ const output = ["", colorBoldWhite(`${domain.displayName}`), ""];
46981
+ output.push(` ${domain.description}`);
46982
+ output.push("");
46983
+ if (domain.category || domain.complexity) {
46984
+ const meta = [];
46985
+ if (domain.category) meta.push(`Category: ${domain.category}`);
46986
+ if (domain.complexity) meta.push(`Complexity: ${domain.complexity}`);
46987
+ output.push(colorDim(` ${meta.join(" | ")}`));
46988
+ output.push("");
46829
46989
  }
46830
- /**
46831
- * Execute a command within a domain
46832
- * Handles routing through subcommand groups
46833
- *
46834
- * @param domainName - Name of the domain (e.g., "login")
46835
- * @param args - Command arguments (e.g., ["profile", "list"])
46836
- * @param session - REPL session
46837
- */
46838
- async execute(domainName, args, session) {
46839
- const domain = this.domains.get(domainName);
46840
- if (!domain) {
46841
- return {
46842
- output: [`Unknown domain: ${domainName}`],
46843
- shouldExit: false,
46844
- shouldClear: false,
46845
- contextChanged: false,
46846
- error: "Unknown domain"
46847
- };
46848
- }
46849
- if (args.length === 0) {
46850
- if (domain.defaultCommand) {
46851
- return domain.defaultCommand.execute([], session);
46852
- }
46853
- return this.showDomainHelp(domain);
46854
- }
46855
- const firstArg = args[0]?.toLowerCase() ?? "";
46856
- const restArgs = args.slice(1);
46857
- if (firstArg === "--help" || firstArg === "-h" || firstArg === "help") {
46858
- return this.showDomainHelp(domain);
46990
+ output.push("USAGE");
46991
+ output.push(` ${CLI_NAME} ${domain.name} <action> [options]`);
46992
+ output.push("");
46993
+ output.push("ACTIONS");
46994
+ const actionDescriptions2 = {
46995
+ list: "List resources",
46996
+ get: "Get a specific resource by name",
46997
+ create: "Create a new resource",
46998
+ delete: "Delete a resource",
46999
+ replace: "Replace a resource configuration",
47000
+ apply: "Apply configuration from file",
47001
+ status: "Get resource status",
47002
+ patch: "Patch a resource",
47003
+ "add-labels": "Add labels to a resource",
47004
+ "remove-labels": "Remove labels from a resource"
47005
+ };
47006
+ for (const action of validActions) {
47007
+ const desc = actionDescriptions2[action] ?? action;
47008
+ output.push(` ${action.padEnd(16)} ${desc}`);
47009
+ }
47010
+ output.push("");
47011
+ output.push("EXAMPLES");
47012
+ output.push(` ${CLI_NAME} ${domain.name} list`);
47013
+ output.push(` ${CLI_NAME} ${domain.name} get my-resource`);
47014
+ output.push(
47015
+ ` ${CLI_NAME} ${domain.name} create my-resource -f config.yaml`
47016
+ );
47017
+ output.push(` ${CLI_NAME} ${domain.name} delete my-resource`);
47018
+ output.push("");
47019
+ if (domain.useCases && domain.useCases.length > 0) {
47020
+ output.push("USE CASES");
47021
+ for (const useCase of domain.useCases.slice(0, 5)) {
47022
+ output.push(` - ${useCase}`);
46859
47023
  }
46860
- const subgroup = domain.subcommands.get(firstArg);
46861
- if (subgroup) {
46862
- if (restArgs.length === 0) {
46863
- if (subgroup.defaultCommand) {
46864
- return subgroup.defaultCommand.execute([], session);
46865
- }
46866
- return this.showSubcommandHelp(domain, subgroup);
46867
- }
46868
- const cmdName = restArgs[0]?.toLowerCase() ?? "";
46869
- if (cmdName === "--help" || cmdName === "-h" || cmdName === "help") {
46870
- return this.showSubcommandHelp(domain, subgroup);
46871
- }
46872
- const cmdArgs = restArgs.slice(1);
46873
- const cmd2 = subgroup.commands.get(cmdName);
46874
- if (cmd2) {
46875
- return cmd2.execute(cmdArgs, session);
46876
- }
46877
- for (const [, command] of subgroup.commands) {
46878
- if (command.aliases?.includes(cmdName)) {
46879
- return command.execute(cmdArgs, session);
46880
- }
46881
- }
46882
- return {
46883
- output: [
46884
- `Unknown command: ${domainName} ${firstArg} ${cmdName}`,
46885
- ``,
46886
- `Run '${domainName} ${firstArg}' for available commands.`
47024
+ output.push("");
47025
+ }
47026
+ if (domain.relatedDomains && domain.relatedDomains.length > 0) {
47027
+ output.push("RELATED DOMAINS");
47028
+ output.push(` ${domain.relatedDomains.join(", ")}`);
47029
+ output.push("");
47030
+ }
47031
+ if (domain.aliases && domain.aliases.length > 0) {
47032
+ output.push("ALIASES");
47033
+ output.push(` ${domain.aliases.join(", ")}`);
47034
+ output.push("");
47035
+ }
47036
+ output.push(colorDim(`For global options, run: ${CLI_NAME} --help`));
47037
+ output.push("");
47038
+ return output;
47039
+ }
47040
+ function formatActionHelp(domainName, action) {
47041
+ const domain = getDomainInfo(domainName);
47042
+ const displayDomain = domain?.displayName ?? domainName;
47043
+ const actionDescriptions2 = {
47044
+ list: {
47045
+ desc: "List all resources in the namespace",
47046
+ usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
47047
+ },
47048
+ get: {
47049
+ desc: "Get a specific resource by name",
47050
+ usage: `${CLI_NAME} ${domainName} get <name> [-o json|yaml|table]`
47051
+ },
47052
+ create: {
47053
+ desc: "Create a new resource",
47054
+ usage: `${CLI_NAME} ${domainName} create <name> -f <file.yaml>`
47055
+ },
47056
+ delete: {
47057
+ desc: "Delete a resource by name",
47058
+ usage: `${CLI_NAME} ${domainName} delete <name>`
47059
+ },
47060
+ replace: {
47061
+ desc: "Replace an existing resource configuration",
47062
+ usage: `${CLI_NAME} ${domainName} replace <name> -f <file.yaml>`
47063
+ },
47064
+ apply: {
47065
+ desc: "Apply configuration from a file (create or update)",
47066
+ usage: `${CLI_NAME} ${domainName} apply -f <file.yaml>`
47067
+ },
47068
+ status: {
47069
+ desc: "Get the current status of a resource",
47070
+ usage: `${CLI_NAME} ${domainName} status <name>`
47071
+ },
47072
+ patch: {
47073
+ desc: "Patch specific fields of a resource",
47074
+ usage: `${CLI_NAME} ${domainName} patch <name> -f <patch.yaml>`
47075
+ },
47076
+ "add-labels": {
47077
+ desc: "Add labels to a resource",
47078
+ usage: `${CLI_NAME} ${domainName} add-labels <name> key=value`
47079
+ },
47080
+ "remove-labels": {
47081
+ desc: "Remove labels from a resource",
47082
+ usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
47083
+ }
47084
+ };
47085
+ const actionInfo = actionDescriptions2[action] ?? {
47086
+ desc: `Execute ${action} operation`,
47087
+ usage: `${CLI_NAME} ${domainName} ${action} [options]`
47088
+ };
47089
+ return [
47090
+ "",
47091
+ colorBoldWhite(`${displayDomain} - ${action}`),
47092
+ "",
47093
+ ` ${actionInfo.desc}`,
47094
+ "",
47095
+ "USAGE",
47096
+ ` ${actionInfo.usage}`,
47097
+ "",
47098
+ "OPTIONS",
47099
+ " -n, --name <name> Resource name",
47100
+ " -ns, --namespace <ns> Target namespace",
47101
+ " -o, --output <fmt> Output format (json, yaml, table)",
47102
+ " -f, --file <path> Configuration file",
47103
+ "",
47104
+ colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`),
47105
+ ""
47106
+ ];
47107
+ }
47108
+ function formatTopicHelp(topic) {
47109
+ const lowerTopic = topic.toLowerCase();
47110
+ const domainInfo = getDomainInfo(lowerTopic);
47111
+ if (domainInfo) {
47112
+ return formatDomainHelp(domainInfo);
47113
+ }
47114
+ switch (lowerTopic) {
47115
+ case "domains":
47116
+ return formatDomainsHelp();
47117
+ case "actions":
47118
+ return formatActionsHelp();
47119
+ case "navigation":
47120
+ case "nav":
47121
+ return formatNavigationHelp();
47122
+ case "env":
47123
+ case "environment":
47124
+ return ["", ...formatEnvironmentVariables(), ""];
47125
+ case "flags":
47126
+ return ["", ...formatGlobalFlags(), ""];
47127
+ default:
47128
+ return [
47129
+ "",
47130
+ `Unknown help topic: ${topic}`,
47131
+ "",
47132
+ "Available topics:",
47133
+ " domains List all available domains",
47134
+ " actions List available actions",
47135
+ " navigation Navigation commands",
47136
+ " env Environment variables",
47137
+ " flags Global flags",
47138
+ " <domain> Help for a specific domain (e.g., 'help dns')",
47139
+ ""
47140
+ ];
47141
+ }
47142
+ }
47143
+ function formatDomainsHelp() {
47144
+ const output = ["", colorBoldWhite("Available Domains"), ""];
47145
+ const categories = /* @__PURE__ */ new Map();
47146
+ for (const domain of domainRegistry.values()) {
47147
+ const category = domain.category ?? "Other";
47148
+ if (!categories.has(category)) {
47149
+ categories.set(category, []);
47150
+ }
47151
+ categories.get(category)?.push(domain);
47152
+ }
47153
+ const sortedCategories = Array.from(categories.keys()).sort();
47154
+ for (const category of sortedCategories) {
47155
+ const domains = categories.get(category) ?? [];
47156
+ output.push(colorBoldWhite(` ${category}`));
47157
+ for (const domain of domains.sort(
47158
+ (a, b) => a.name.localeCompare(b.name)
47159
+ )) {
47160
+ const aliases = domain.aliases.length > 0 ? colorDim(` (${domain.aliases.join(", ")})`) : "";
47161
+ output.push(
47162
+ ` ${domain.name.padEnd(24)} ${domain.descriptionShort}`
47163
+ );
47164
+ if (aliases) {
47165
+ output.push(` ${"".padEnd(24)} Aliases:${aliases}`);
47166
+ }
47167
+ }
47168
+ output.push("");
47169
+ }
47170
+ return output;
47171
+ }
47172
+ function formatActionsHelp() {
47173
+ return [
47174
+ "",
47175
+ colorBoldWhite("Available Actions"),
47176
+ "",
47177
+ " list List all resources in the namespace",
47178
+ " get Get a specific resource by name",
47179
+ " create Create a new resource from a file",
47180
+ " delete Delete a resource by name",
47181
+ " replace Replace a resource configuration",
47182
+ " apply Apply configuration (create or update)",
47183
+ " status Get resource status",
47184
+ " patch Patch specific fields of a resource",
47185
+ " add-labels Add labels to a resource",
47186
+ " remove-labels Remove labels from a resource",
47187
+ "",
47188
+ "USAGE",
47189
+ ` ${CLI_NAME} <domain> <action> [options]`,
47190
+ "",
47191
+ "EXAMPLES",
47192
+ ` ${CLI_NAME} dns list`,
47193
+ ` ${CLI_NAME} lb get my-loadbalancer`,
47194
+ ` ${CLI_NAME} waf create my-policy -f policy.yaml`,
47195
+ ""
47196
+ ];
47197
+ }
47198
+ function formatNavigationHelp() {
47199
+ return [
47200
+ "",
47201
+ colorBoldWhite("Navigation Commands"),
47202
+ "",
47203
+ " <domain> Navigate into a domain context",
47204
+ " /<domain> Navigate directly to domain from anywhere",
47205
+ " .. Go up one level in context",
47206
+ " / Return to root context",
47207
+ " back Go up one level (same as ..)",
47208
+ " root Return to root (same as /)",
47209
+ "",
47210
+ "CONTEXT DISPLAY",
47211
+ " context Show current navigation context",
47212
+ " ctx Alias for context",
47213
+ "",
47214
+ "EXAMPLES",
47215
+ " xcsh> dns # Enter dns domain",
47216
+ " dns> list # Execute list in dns context",
47217
+ " dns> .. # Return to root",
47218
+ " xcsh> /waf # Jump directly to waf",
47219
+ " waf> /dns # Jump from waf to dns",
47220
+ ""
47221
+ ];
47222
+ }
47223
+ function formatDomainsSection() {
47224
+ const output = ["DOMAINS"];
47225
+ const domains = Array.from(domainRegistry.values()).sort(
47226
+ (a, b) => a.name.localeCompare(b.name)
47227
+ );
47228
+ const maxNameLen = Math.max(...domains.map((d) => d.name.length));
47229
+ for (const domain of domains) {
47230
+ const padding = " ".repeat(maxNameLen - domain.name.length + 2);
47231
+ output.push(` ${domain.name}${padding}${domain.descriptionShort}`);
47232
+ }
47233
+ return output;
47234
+ }
47235
+ function formatCustomDomainHelp(domain) {
47236
+ const output = ["", colorBoldWhite(domain.name), ""];
47237
+ output.push("DESCRIPTION");
47238
+ output.push(...wrapText2(domain.description, 80, 2));
47239
+ output.push("");
47240
+ output.push("USAGE");
47241
+ output.push(` ${CLI_NAME} ${domain.name} <command> [options]`);
47242
+ output.push("");
47243
+ if (domain.subcommands.size > 0) {
47244
+ output.push("SUBCOMMANDS");
47245
+ for (const [name, group] of domain.subcommands) {
47246
+ output.push(` ${name.padEnd(16)} ${group.descriptionShort}`);
47247
+ }
47248
+ output.push("");
47249
+ }
47250
+ if (domain.commands.size > 0) {
47251
+ output.push("COMMANDS");
47252
+ for (const [name, cmd] of domain.commands) {
47253
+ output.push(` ${name.padEnd(16)} ${cmd.descriptionShort}`);
47254
+ }
47255
+ output.push("");
47256
+ }
47257
+ output.push(colorDim(`For global options, run: ${CLI_NAME} --help`));
47258
+ output.push("");
47259
+ return output;
47260
+ }
47261
+ function formatSubcommandHelp(domainName, subcommand) {
47262
+ const output = [
47263
+ "",
47264
+ colorBoldWhite(`${domainName} ${subcommand.name}`),
47265
+ ""
47266
+ ];
47267
+ output.push("DESCRIPTION");
47268
+ output.push(...wrapText2(subcommand.description, 80, 2));
47269
+ output.push("");
47270
+ output.push("USAGE");
47271
+ output.push(
47272
+ ` ${CLI_NAME} ${domainName} ${subcommand.name} <command> [options]`
47273
+ );
47274
+ output.push("");
47275
+ if (subcommand.commands.size > 0) {
47276
+ output.push("COMMANDS");
47277
+ for (const [name, cmd] of subcommand.commands) {
47278
+ const usage = cmd.usage ? ` ${cmd.usage}` : "";
47279
+ output.push(
47280
+ ` ${name}${usage.padEnd(16 - name.length)} ${cmd.descriptionShort}`
47281
+ );
47282
+ }
47283
+ output.push("");
47284
+ }
47285
+ output.push(
47286
+ colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`)
47287
+ );
47288
+ output.push("");
47289
+ return output;
47290
+ }
47291
+
47292
+ // src/domains/registry.ts
47293
+ var DomainRegistry = class {
47294
+ domains = /* @__PURE__ */ new Map();
47295
+ /**
47296
+ * Register a custom domain
47297
+ */
47298
+ register(domain) {
47299
+ this.domains.set(domain.name, domain);
47300
+ }
47301
+ /**
47302
+ * Check if a domain is registered
47303
+ */
47304
+ has(name) {
47305
+ return this.domains.has(name);
47306
+ }
47307
+ /**
47308
+ * Get a domain by name
47309
+ */
47310
+ get(name) {
47311
+ return this.domains.get(name);
47312
+ }
47313
+ /**
47314
+ * Get all registered domain names
47315
+ */
47316
+ list() {
47317
+ return Array.from(this.domains.keys());
47318
+ }
47319
+ /**
47320
+ * Get all domains
47321
+ */
47322
+ all() {
47323
+ return Array.from(this.domains.values());
47324
+ }
47325
+ /**
47326
+ * Execute a command within a domain
47327
+ * Handles routing through subcommand groups
47328
+ *
47329
+ * @param domainName - Name of the domain (e.g., "login")
47330
+ * @param args - Command arguments (e.g., ["profile", "list"])
47331
+ * @param session - REPL session
47332
+ */
47333
+ async execute(domainName, args, session) {
47334
+ const domain = this.domains.get(domainName);
47335
+ if (!domain) {
47336
+ return {
47337
+ output: [`Unknown domain: ${domainName}`],
47338
+ shouldExit: false,
47339
+ shouldClear: false,
47340
+ contextChanged: false,
47341
+ error: "Unknown domain"
47342
+ };
47343
+ }
47344
+ if (args.length === 0) {
47345
+ if (domain.defaultCommand) {
47346
+ return domain.defaultCommand.execute([], session);
47347
+ }
47348
+ return this.showDomainHelp(domain);
47349
+ }
47350
+ const firstArg = args[0]?.toLowerCase() ?? "";
47351
+ const restArgs = args.slice(1);
47352
+ if (firstArg === "--help" || firstArg === "-h" || firstArg === "help") {
47353
+ return this.showDomainHelp(domain);
47354
+ }
47355
+ const subgroup = domain.subcommands.get(firstArg);
47356
+ if (subgroup) {
47357
+ if (restArgs.length === 0) {
47358
+ if (subgroup.defaultCommand) {
47359
+ return subgroup.defaultCommand.execute([], session);
47360
+ }
47361
+ return this.showSubcommandHelp(domain, subgroup);
47362
+ }
47363
+ const cmdName = restArgs[0]?.toLowerCase() ?? "";
47364
+ if (cmdName === "--help" || cmdName === "-h" || cmdName === "help") {
47365
+ return this.showSubcommandHelp(domain, subgroup);
47366
+ }
47367
+ const cmdArgs = restArgs.slice(1);
47368
+ const cmd2 = subgroup.commands.get(cmdName);
47369
+ if (cmd2) {
47370
+ return cmd2.execute(cmdArgs, session);
47371
+ }
47372
+ for (const [, command] of subgroup.commands) {
47373
+ if (command.aliases?.includes(cmdName)) {
47374
+ return command.execute(cmdArgs, session);
47375
+ }
47376
+ }
47377
+ return {
47378
+ output: [
47379
+ `Unknown command: ${domainName} ${firstArg} ${cmdName}`,
47380
+ ``,
47381
+ `Run '${domainName} ${firstArg}' for available commands.`
46887
47382
  ],
46888
47383
  shouldExit: false,
46889
47384
  shouldClear: false,
@@ -46975,45 +47470,24 @@ var DomainRegistry = class {
46975
47470
  return suggestions;
46976
47471
  }
46977
47472
  /**
46978
- * Show help for a domain
47473
+ * Show help for a domain using the unified help formatter.
47474
+ * This ensures consistent professional formatting across all domains.
46979
47475
  */
46980
47476
  showDomainHelp(domain) {
46981
- const output = [`${domain.name} - ${domain.description}`, ``];
46982
- if (domain.subcommands.size > 0) {
46983
- output.push(`Subcommands:`);
46984
- for (const [name, group] of domain.subcommands) {
46985
- output.push(` ${name} ${group.description}`);
46986
- }
46987
- output.push(``);
46988
- }
46989
- if (domain.commands.size > 0) {
46990
- output.push(`Commands:`);
46991
- for (const [name, cmd] of domain.commands) {
46992
- output.push(` ${name} ${cmd.description}`);
46993
- }
46994
- }
46995
47477
  return {
46996
- output,
47478
+ output: formatCustomDomainHelp(domain),
46997
47479
  shouldExit: false,
46998
47480
  shouldClear: false,
46999
47481
  contextChanged: false
47000
47482
  };
47001
47483
  }
47002
47484
  /**
47003
- * Show help for a subcommand group
47485
+ * Show help for a subcommand group using the unified help formatter.
47486
+ * This ensures consistent professional formatting across all subcommands.
47004
47487
  */
47005
47488
  showSubcommandHelp(domain, subgroup) {
47006
- const output = [
47007
- `${domain.name} ${subgroup.name} - ${subgroup.description}`,
47008
- ``,
47009
- `Commands:`
47010
- ];
47011
- for (const [name, cmd] of subgroup.commands) {
47012
- const usage = cmd.usage ? ` ${cmd.usage}` : "";
47013
- output.push(` ${name}${usage} ${cmd.description}`);
47014
- }
47015
47489
  return {
47016
- output,
47490
+ output: formatSubcommandHelp(domain.name, subgroup),
47017
47491
  shouldExit: false,
47018
47492
  shouldClear: false,
47019
47493
  contextChanged: false
@@ -47495,75 +47969,19 @@ var listCommand2 = {
47495
47969
  const message = error instanceof Error ? error.message : String(error);
47496
47970
  return errorResult(`Failed to list namespaces: ${message}`);
47497
47971
  }
47498
- }
47499
- };
47500
- var contextSubcommands = {
47501
- name: "context",
47502
- description: "Manage default namespace context for scoping operations. Set, display, and list namespaces to control which namespace is used when no explicit namespace is specified in commands.",
47503
- descriptionShort: "Manage default namespace context",
47504
- descriptionMedium: "Set, display, and list namespaces for scoping operations without explicit namespace flags.",
47505
- commands: /* @__PURE__ */ new Map([
47506
- ["show", showCommand2],
47507
- ["set", setCommand],
47508
- ["list", listCommand2]
47509
- ])
47510
- };
47511
-
47512
- // src/config/envvars.ts
47513
- var EnvVarRegistry = [
47514
- {
47515
- name: `${ENV_PREFIX}_API_URL`,
47516
- description: "API endpoint URL",
47517
- relatedFlag: "",
47518
- required: true
47519
- },
47520
- {
47521
- name: `${ENV_PREFIX}_API_TOKEN`,
47522
- description: "API authentication token",
47523
- relatedFlag: "",
47524
- required: true
47525
- },
47526
- {
47527
- name: `${ENV_PREFIX}_NAMESPACE`,
47528
- description: "Default namespace",
47529
- relatedFlag: "-ns"
47530
- },
47531
- {
47532
- name: `${ENV_PREFIX}_OUTPUT_FORMAT`,
47533
- description: "Output format (json, yaml, table)",
47534
- relatedFlag: "-o"
47535
- },
47536
- {
47537
- name: `${ENV_PREFIX}_LOGO`,
47538
- description: "Logo display mode (auto, image, ascii, both, none)",
47539
- relatedFlag: "--logo"
47540
- },
47541
- {
47542
- name: "NO_COLOR",
47543
- description: "Disable color output",
47544
- relatedFlag: "--no-color"
47545
- }
47546
- ];
47547
- function formatEnvVarsSection() {
47548
- const maxLen = Math.max(...EnvVarRegistry.map((e) => e.name.length));
47549
- const lines = ["ENVIRONMENT VARIABLES"];
47550
- for (const env3 of EnvVarRegistry) {
47551
- const padding = " ".repeat(maxLen - env3.name.length + 3);
47552
- const flagNote = env3.relatedFlag ? ` [${env3.relatedFlag}]` : "";
47553
- lines.push(` ${env3.name}${padding}${env3.description}${flagNote}`);
47554
- }
47555
- return lines;
47556
- }
47557
- function formatConfigSection() {
47558
- return [
47559
- "CONFIGURATION",
47560
- ` Config file: ~/${CONFIG_FILE_NAME}`,
47561
- " Priority: CLI flags > environment variables > config file > defaults",
47562
- "",
47563
- "DOCUMENTATION",
47564
- ` ${DOCS_URL}`
47565
- ];
47566
- }
47972
+ }
47973
+ };
47974
+ var contextSubcommands = {
47975
+ name: "context",
47976
+ description: "Manage default namespace context for scoping operations. Set, display, and list namespaces to control which namespace is used when no explicit namespace is specified in commands.",
47977
+ descriptionShort: "Manage default namespace context",
47978
+ descriptionMedium: "Set, display, and list namespaces for scoping operations without explicit namespace flags.",
47979
+ commands: /* @__PURE__ */ new Map([
47980
+ ["show", showCommand2],
47981
+ ["set", setCommand],
47982
+ ["list", listCommand2]
47983
+ ])
47984
+ };
47567
47985
 
47568
47986
  // src/config/settings.ts
47569
47987
  var import_yaml2 = __toESM(require_dist(), 1);
@@ -50477,567 +50895,251 @@ function useCompletion(options) {
50477
50895
  setSuggestions(newSuggestions);
50478
50896
  setSelectedIndex(0);
50479
50897
  }
50480
- },
50481
- [completer, isShowing, hide]
50482
- );
50483
- const navigateUp = (0, import_react27.useCallback)(() => {
50484
- if (!isShowing || suggestions.length === 0) return;
50485
- setSelectedIndex(
50486
- (current) => current > 0 ? current - 1 : suggestions.length - 1
50487
- );
50488
- }, [isShowing, suggestions.length]);
50489
- const navigateDown = (0, import_react27.useCallback)(() => {
50490
- if (!isShowing || suggestions.length === 0) return;
50491
- setSelectedIndex(
50492
- (current) => current < suggestions.length - 1 ? current + 1 : 0
50493
- );
50494
- }, [isShowing, suggestions.length]);
50495
- const selectCurrent = (0, import_react27.useCallback)(() => {
50496
- if (!isShowing || suggestions.length === 0) return null;
50497
- const selected = suggestions.at(selectedIndex);
50498
- hide();
50499
- return selected ?? null;
50500
- }, [isShowing, suggestions, selectedIndex, hide]);
50501
- return {
50502
- suggestions,
50503
- selectedIndex,
50504
- isShowing,
50505
- triggerCompletion,
50506
- navigateUp,
50507
- navigateDown,
50508
- selectCurrent,
50509
- hide,
50510
- filterSuggestions
50511
- };
50512
- }
50513
-
50514
- // src/output/formatter.ts
50515
- var import_yaml3 = __toESM(require_dist(), 1);
50516
- function formatOutput(data, format = "yaml") {
50517
- if (format === "none") {
50518
- return "";
50519
- }
50520
- switch (format) {
50521
- case "json":
50522
- return formatJSON(data);
50523
- case "yaml":
50524
- return formatYAML(data);
50525
- case "table":
50526
- case "text":
50527
- return formatTable(data);
50528
- case "tsv":
50529
- return formatTSV(data);
50530
- default:
50531
- return formatYAML(data);
50532
- }
50533
- }
50534
- function formatJSON(data) {
50535
- return JSON.stringify(data, null, 2);
50536
- }
50537
- function formatYAML(data) {
50538
- return import_yaml3.default.stringify(data, { indent: 2 });
50539
- }
50540
- function extractItems(data) {
50541
- if (data && typeof data === "object" && "items" in data) {
50542
- const items = data.items;
50543
- if (Array.isArray(items)) {
50544
- return items.filter(
50545
- (item) => item !== null && typeof item === "object"
50546
- );
50547
- }
50548
- }
50549
- if (Array.isArray(data)) {
50550
- return data.filter(
50551
- (item) => item !== null && typeof item === "object"
50552
- );
50553
- }
50554
- if (data && typeof data === "object") {
50555
- return [data];
50556
- }
50557
- return [];
50558
- }
50559
- function getStringField(obj, key) {
50560
- const value = obj[key];
50561
- if (typeof value === "string") {
50562
- return value;
50563
- }
50564
- if (value !== null && value !== void 0) {
50565
- return String(value);
50566
- }
50567
- return "";
50568
- }
50569
- function formatLabels(obj) {
50570
- const labels = obj["labels"];
50571
- if (!labels || typeof labels !== "object") {
50572
- return "";
50573
- }
50574
- const labelMap = labels;
50575
- const entries = Object.entries(labelMap).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}:${v}`);
50576
- if (entries.length === 0) {
50577
- return "";
50578
- }
50579
- return `map[${entries.join(" ")}]`;
50580
- }
50581
- function wrapText2(text, maxWidth) {
50582
- if (text.length <= maxWidth) {
50583
- return [text];
50584
- }
50585
- const lines = [];
50586
- let remaining = text;
50587
- while (remaining.length > 0) {
50588
- if (remaining.length <= maxWidth) {
50589
- lines.push(remaining);
50590
- break;
50591
- }
50592
- let breakPoint = maxWidth;
50593
- for (let i = maxWidth - 1; i > 0; i--) {
50594
- if (remaining[i] === " ") {
50595
- breakPoint = i;
50596
- break;
50597
- }
50598
- }
50599
- lines.push(remaining.slice(0, breakPoint));
50600
- remaining = remaining.slice(breakPoint).trimStart();
50601
- }
50602
- return lines;
50603
- }
50604
- function formatTable(data) {
50605
- const items = extractItems(data);
50606
- if (items.length === 0) {
50607
- return "";
50608
- }
50609
- const headers = ["NAMESPACE", "NAME", "LABELS"];
50610
- const widths = [9, 27, 30];
50611
- const rows = [];
50612
- for (const item of items) {
50613
- const row = [
50614
- getStringField(item, "namespace") || "<None>",
50615
- getStringField(item, "name") || "<None>",
50616
- formatLabels(item) || "<None>"
50617
- ];
50618
- const wrappedCells = row.map((cell, i) => wrapText2(cell, widths[i]));
50619
- const maxLines = Math.max(...wrappedCells.map((c) => c.length));
50620
- const wrappedRows = [];
50621
- for (let line = 0; line < maxLines; line++) {
50622
- wrappedRows.push(wrappedCells.map((c) => c[line] ?? ""));
50623
- }
50624
- rows.push(wrappedRows);
50625
- }
50626
- const lines = [];
50627
- const boxLine = "+" + widths.map((w) => "-".repeat(w + 2)).join("+") + "+";
50628
- lines.push(boxLine);
50629
- lines.push(
50630
- "|" + headers.map((h, i) => {
50631
- const padding = widths[i] - h.length;
50632
- const leftPad = Math.floor(padding / 2);
50633
- const rightPad = padding - leftPad;
50634
- return " " + " ".repeat(leftPad) + h + " ".repeat(rightPad) + " ";
50635
- }).join("|") + "|"
50636
- );
50637
- lines.push(boxLine);
50638
- for (const wrappedRows of rows) {
50639
- for (const row of wrappedRows) {
50640
- lines.push(
50641
- "|" + row.map((cell, i) => {
50642
- const padding = widths[i] - cell.length;
50643
- return " " + cell + " ".repeat(padding) + " ";
50644
- }).join("|") + "|"
50645
- );
50646
- }
50647
- lines.push(boxLine);
50648
- }
50649
- return lines.join("\n");
50650
- }
50651
- function formatTSV(data) {
50652
- const items = extractItems(data);
50653
- if (items.length === 0) {
50654
- return "";
50655
- }
50656
- const allKeys = /* @__PURE__ */ new Set();
50657
- for (const item of items) {
50658
- Object.keys(item).forEach((k) => allKeys.add(k));
50659
- }
50660
- const priority = ["name", "namespace", "status", "created", "modified"];
50661
- const headers = [
50662
- ...priority.filter((p) => allKeys.has(p)),
50663
- ...[...allKeys].filter((k) => !priority.includes(k)).sort()
50664
- ];
50665
- const lines = [];
50666
- for (const item of items) {
50667
- const values = headers.map((h) => {
50668
- const val = item[h];
50669
- if (val === null || val === void 0) return "";
50670
- if (typeof val === "object") return JSON.stringify(val);
50671
- return String(val);
50672
- });
50673
- lines.push(values.join(" "));
50674
- }
50675
- return lines.join("\n");
50676
- }
50677
- function formatAPIError(statusCode, body, operation) {
50678
- const lines = [];
50679
- lines.push(`ERROR: ${operation} failed (HTTP ${statusCode})`);
50680
- if (body && typeof body === "object") {
50681
- const errResp = body;
50682
- if (errResp.message) {
50683
- lines.push(` Message: ${errResp.message}`);
50684
- }
50685
- if (errResp.code) {
50686
- lines.push(` Code: ${errResp.code}`);
50687
- }
50688
- if (errResp.details) {
50689
- lines.push(` Details: ${errResp.details}`);
50690
- }
50691
- }
50692
- switch (statusCode) {
50693
- case 401:
50694
- lines.push(
50695
- "\nHint: Authentication failed. Check your credentials with 'login profile show'"
50696
- );
50697
- break;
50698
- case 403:
50699
- lines.push(
50700
- "\nHint: Permission denied. You may not have access to this resource."
50701
- );
50702
- break;
50703
- case 404:
50704
- lines.push(
50705
- "\nHint: Resource not found. Verify the name and namespace are correct."
50706
- );
50707
- break;
50708
- case 409:
50709
- lines.push(
50710
- "\nHint: Conflict - resource may already exist or be in a conflicting state."
50711
- );
50712
- break;
50713
- case 429:
50714
- lines.push("\nHint: Rate limited. Please wait and try again.");
50715
- break;
50716
- case 500:
50717
- case 502:
50718
- case 503:
50719
- lines.push(
50720
- "\nHint: Server error. Please try again later or contact support."
50721
- );
50722
- break;
50723
- }
50724
- return lines.join("\n");
50898
+ },
50899
+ [completer, isShowing, hide]
50900
+ );
50901
+ const navigateUp = (0, import_react27.useCallback)(() => {
50902
+ if (!isShowing || suggestions.length === 0) return;
50903
+ setSelectedIndex(
50904
+ (current) => current > 0 ? current - 1 : suggestions.length - 1
50905
+ );
50906
+ }, [isShowing, suggestions.length]);
50907
+ const navigateDown = (0, import_react27.useCallback)(() => {
50908
+ if (!isShowing || suggestions.length === 0) return;
50909
+ setSelectedIndex(
50910
+ (current) => current < suggestions.length - 1 ? current + 1 : 0
50911
+ );
50912
+ }, [isShowing, suggestions.length]);
50913
+ const selectCurrent = (0, import_react27.useCallback)(() => {
50914
+ if (!isShowing || suggestions.length === 0) return null;
50915
+ const selected = suggestions.at(selectedIndex);
50916
+ hide();
50917
+ return selected ?? null;
50918
+ }, [isShowing, suggestions, selectedIndex, hide]);
50919
+ return {
50920
+ suggestions,
50921
+ selectedIndex,
50922
+ isShowing,
50923
+ triggerCompletion,
50924
+ navigateUp,
50925
+ navigateDown,
50926
+ selectCurrent,
50927
+ hide,
50928
+ filterSuggestions
50929
+ };
50725
50930
  }
50726
50931
 
50727
- // src/repl/help.ts
50728
- function formatRootHelp() {
50729
- return [
50730
- "",
50731
- colorBoldWhite(`${CLI_NAME} - ${CLI_FULL_NAME} v${CLI_VERSION}`),
50732
- "",
50733
- "DESCRIPTION",
50734
- ` Interactive CLI for F5 Distributed Cloud services.`,
50735
- "",
50736
- "USAGE",
50737
- ` ${CLI_NAME} Enter interactive REPL mode`,
50738
- ` ${CLI_NAME} <domain> <action> Execute command non-interactively`,
50739
- ` ${CLI_NAME} help [topic] Show help for a topic`,
50740
- "",
50741
- "EXAMPLES",
50742
- ` ${CLI_NAME} tenant_and_identity list namespace List all namespaces`,
50743
- ` ${CLI_NAME} virtual get http_loadbalancer Get a specific load balancer`,
50744
- ` ${CLI_NAME} dns list List DNS zones`,
50745
- ` ${CLI_NAME} waf list -ns prod List WAF policies in prod`,
50746
- ` ${CLI_NAME} --interactive Force interactive REPL mode`,
50747
- "",
50748
- ...formatDomainsSection(),
50749
- "",
50750
- ...formatGlobalFlags(),
50751
- "",
50752
- ...formatEnvVarsSection(),
50753
- "",
50754
- ...formatConfigSection(),
50755
- "",
50756
- "NAVIGATION (Interactive Mode)",
50757
- " <domain> Navigate into a domain (e.g., 'dns', 'lb')",
50758
- " /domain Navigate directly to domain from anywhere",
50759
- " .. Go up one level",
50760
- " / Return to root",
50761
- " context Show current navigation context",
50762
- "",
50763
- "BUILTINS",
50764
- " help Show this help",
50765
- " domains List all available domains",
50766
- " clear Clear the screen",
50767
- " history Show command history",
50768
- " quit, exit Exit the shell",
50769
- ""
50770
- ];
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
+ }
50771
50951
  }
50772
- function formatGlobalFlags() {
50773
- return [
50774
- "GLOBAL FLAGS",
50775
- " -v, --version Show version number",
50776
- " -h, --help Show this help",
50777
- " -i, --interactive Force interactive mode",
50778
- " --no-color Disable color output",
50779
- " -o, --output <fmt> Output format (json, yaml, table)",
50780
- " -ns, --namespace <ns> Target namespace"
50781
- ];
50952
+ function formatJSON(data) {
50953
+ return JSON.stringify(data, null, 2);
50782
50954
  }
50783
- function formatEnvironmentVariables() {
50784
- return formatEnvVarsSection();
50955
+ function formatYAML(data) {
50956
+ return import_yaml3.default.stringify(data, { indent: 2 });
50785
50957
  }
50786
- function formatDomainHelp(domain) {
50787
- const output = ["", colorBoldWhite(`${domain.displayName}`), ""];
50788
- output.push(` ${domain.description}`);
50789
- output.push("");
50790
- if (domain.category || domain.complexity) {
50791
- const meta = [];
50792
- if (domain.category) meta.push(`Category: ${domain.category}`);
50793
- if (domain.complexity) meta.push(`Complexity: ${domain.complexity}`);
50794
- output.push(colorDim(` ${meta.join(" | ")}`));
50795
- output.push("");
50796
- }
50797
- output.push("USAGE");
50798
- output.push(` ${CLI_NAME} ${domain.name} <action> [options]`);
50799
- output.push("");
50800
- output.push("ACTIONS");
50801
- const actionDescriptions2 = {
50802
- list: "List resources",
50803
- get: "Get a specific resource by name",
50804
- create: "Create a new resource",
50805
- delete: "Delete a resource",
50806
- replace: "Replace a resource configuration",
50807
- apply: "Apply configuration from file",
50808
- status: "Get resource status",
50809
- patch: "Patch a resource",
50810
- "add-labels": "Add labels to a resource",
50811
- "remove-labels": "Remove labels from a resource"
50812
- };
50813
- for (const action of validActions) {
50814
- const desc = actionDescriptions2[action] ?? action;
50815
- output.push(` ${action.padEnd(16)} ${desc}`);
50816
- }
50817
- output.push("");
50818
- output.push("EXAMPLES");
50819
- output.push(` ${CLI_NAME} ${domain.name} list`);
50820
- output.push(` ${CLI_NAME} ${domain.name} get my-resource`);
50821
- output.push(
50822
- ` ${CLI_NAME} ${domain.name} create my-resource -f config.yaml`
50823
- );
50824
- output.push(` ${CLI_NAME} ${domain.name} delete my-resource`);
50825
- output.push("");
50826
- if (domain.useCases && domain.useCases.length > 0) {
50827
- output.push("USE CASES");
50828
- for (const useCase of domain.useCases.slice(0, 5)) {
50829
- output.push(` - ${useCase}`);
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
+ );
50830
50965
  }
50831
- output.push("");
50832
50966
  }
50833
- if (domain.relatedDomains && domain.relatedDomains.length > 0) {
50834
- output.push("RELATED DOMAINS");
50835
- output.push(` ${domain.relatedDomains.join(", ")}`);
50836
- output.push("");
50967
+ if (Array.isArray(data)) {
50968
+ return data.filter(
50969
+ (item) => item !== null && typeof item === "object"
50970
+ );
50837
50971
  }
50838
- if (domain.aliases && domain.aliases.length > 0) {
50839
- output.push("ALIASES");
50840
- output.push(` ${domain.aliases.join(", ")}`);
50841
- output.push("");
50972
+ if (data && typeof data === "object") {
50973
+ return [data];
50842
50974
  }
50843
- output.push(colorDim(`For global options, run: ${CLI_NAME} --help`));
50844
- output.push("");
50845
- return output;
50975
+ return [];
50846
50976
  }
50847
- function formatActionHelp(domainName, action) {
50848
- const domain = getDomainInfo(domainName);
50849
- const displayDomain = domain?.displayName ?? domainName;
50850
- const actionDescriptions2 = {
50851
- list: {
50852
- desc: "List all resources in the namespace",
50853
- usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
50854
- },
50855
- get: {
50856
- desc: "Get a specific resource by name",
50857
- usage: `${CLI_NAME} ${domainName} get <name> [-o json|yaml|table]`
50858
- },
50859
- create: {
50860
- desc: "Create a new resource",
50861
- usage: `${CLI_NAME} ${domainName} create <name> -f <file.yaml>`
50862
- },
50863
- delete: {
50864
- desc: "Delete a resource by name",
50865
- usage: `${CLI_NAME} ${domainName} delete <name>`
50866
- },
50867
- replace: {
50868
- desc: "Replace an existing resource configuration",
50869
- usage: `${CLI_NAME} ${domainName} replace <name> -f <file.yaml>`
50870
- },
50871
- apply: {
50872
- desc: "Apply configuration from a file (create or update)",
50873
- usage: `${CLI_NAME} ${domainName} apply -f <file.yaml>`
50874
- },
50875
- status: {
50876
- desc: "Get the current status of a resource",
50877
- usage: `${CLI_NAME} ${domainName} status <name>`
50878
- },
50879
- patch: {
50880
- desc: "Patch specific fields of a resource",
50881
- usage: `${CLI_NAME} ${domainName} patch <name> -f <patch.yaml>`
50882
- },
50883
- "add-labels": {
50884
- desc: "Add labels to a resource",
50885
- usage: `${CLI_NAME} ${domainName} add-labels <name> key=value`
50886
- },
50887
- "remove-labels": {
50888
- desc: "Remove labels from a resource",
50889
- usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
50890
- }
50891
- };
50892
- const actionInfo = actionDescriptions2[action] ?? {
50893
- desc: `Execute ${action} operation`,
50894
- usage: `${CLI_NAME} ${domainName} ${action} [options]`
50895
- };
50896
- return [
50897
- "",
50898
- colorBoldWhite(`${displayDomain} - ${action}`),
50899
- "",
50900
- ` ${actionInfo.desc}`,
50901
- "",
50902
- "USAGE",
50903
- ` ${actionInfo.usage}`,
50904
- "",
50905
- "OPTIONS",
50906
- " -n, --name <name> Resource name",
50907
- " -ns, --namespace <ns> Target namespace",
50908
- " -o, --output <fmt> Output format (json, yaml, table)",
50909
- " -f, --file <path> Configuration file",
50910
- "",
50911
- colorDim(`For domain help, run: ${CLI_NAME} ${domainName} --help`),
50912
- ""
50913
- ];
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 "";
50914
50986
  }
50915
- function formatTopicHelp(topic) {
50916
- const lowerTopic = topic.toLowerCase();
50917
- const domainInfo = getDomainInfo(lowerTopic);
50918
- if (domainInfo) {
50919
- return formatDomainHelp(domainInfo);
50987
+ function formatLabels(obj) {
50988
+ const labels = obj["labels"];
50989
+ if (!labels || typeof labels !== "object") {
50990
+ return "";
50920
50991
  }
50921
- switch (lowerTopic) {
50922
- case "domains":
50923
- return formatDomainsHelp();
50924
- case "actions":
50925
- return formatActionsHelp();
50926
- case "navigation":
50927
- case "nav":
50928
- return formatNavigationHelp();
50929
- case "env":
50930
- case "environment":
50931
- return ["", ...formatEnvironmentVariables(), ""];
50932
- case "flags":
50933
- return ["", ...formatGlobalFlags(), ""];
50934
- default:
50935
- return [
50936
- "",
50937
- `Unknown help topic: ${topic}`,
50938
- "",
50939
- "Available topics:",
50940
- " domains List all available domains",
50941
- " actions List available actions",
50942
- " navigation Navigation commands",
50943
- " env Environment variables",
50944
- " flags Global flags",
50945
- " <domain> Help for a specific domain (e.g., 'help dns')",
50946
- ""
50947
- ];
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 "";
50948
50996
  }
50997
+ return `map[${entries.join(" ")}]`;
50949
50998
  }
50950
- function formatDomainsHelp() {
50951
- const output = ["", colorBoldWhite("Available Domains"), ""];
50952
- const categories = /* @__PURE__ */ new Map();
50953
- for (const domain of domainRegistry.values()) {
50954
- const category = domain.category ?? "Other";
50955
- if (!categories.has(category)) {
50956
- categories.set(category, []);
50957
- }
50958
- categories.get(category)?.push(domain);
50999
+ function wrapText3(text, maxWidth) {
51000
+ if (text.length <= maxWidth) {
51001
+ return [text];
50959
51002
  }
50960
- const sortedCategories = Array.from(categories.keys()).sort();
50961
- for (const category of sortedCategories) {
50962
- const domains = categories.get(category) ?? [];
50963
- output.push(colorBoldWhite(` ${category}`));
50964
- for (const domain of domains.sort(
50965
- (a, b) => a.name.localeCompare(b.name)
50966
- )) {
50967
- const aliases = domain.aliases.length > 0 ? colorDim(` (${domain.aliases.join(", ")})`) : "";
50968
- output.push(
50969
- ` ${domain.name.padEnd(24)} ${domain.descriptionShort}`
50970
- );
50971
- if (aliases) {
50972
- output.push(` ${"".padEnd(24)} Aliases:${aliases}`);
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;
50973
51015
  }
50974
51016
  }
50975
- output.push("");
51017
+ lines.push(remaining.slice(0, breakPoint));
51018
+ remaining = remaining.slice(breakPoint).trimStart();
50976
51019
  }
50977
- return output;
51020
+ return lines;
50978
51021
  }
50979
- function formatActionsHelp() {
50980
- return [
50981
- "",
50982
- colorBoldWhite("Available Actions"),
50983
- "",
50984
- " list List all resources in the namespace",
50985
- " get Get a specific resource by name",
50986
- " create Create a new resource from a file",
50987
- " delete Delete a resource by name",
50988
- " replace Replace a resource configuration",
50989
- " apply Apply configuration (create or update)",
50990
- " status Get resource status",
50991
- " patch Patch specific fields of a resource",
50992
- " add-labels Add labels to a resource",
50993
- " remove-labels Remove labels from a resource",
50994
- "",
50995
- "USAGE",
50996
- ` ${CLI_NAME} <domain> <action> [options]`,
50997
- "",
50998
- "EXAMPLES",
50999
- ` ${CLI_NAME} dns list`,
51000
- ` ${CLI_NAME} lb get my-loadbalancer`,
51001
- ` ${CLI_NAME} waf create my-policy -f policy.yaml`,
51002
- ""
51003
- ];
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");
51004
51068
  }
51005
- function formatNavigationHelp() {
51006
- return [
51007
- "",
51008
- colorBoldWhite("Navigation Commands"),
51009
- "",
51010
- " <domain> Navigate into a domain context",
51011
- " /<domain> Navigate directly to domain from anywhere",
51012
- " .. Go up one level in context",
51013
- " / Return to root context",
51014
- " back Go up one level (same as ..)",
51015
- " root Return to root (same as /)",
51016
- "",
51017
- "CONTEXT DISPLAY",
51018
- " context Show current navigation context",
51019
- " ctx Alias for context",
51020
- "",
51021
- "EXAMPLES",
51022
- " xcsh> dns # Enter dns domain",
51023
- " dns> list # Execute list in dns context",
51024
- " dns> .. # Return to root",
51025
- " xcsh> /waf # Jump directly to waf",
51026
- " waf> /dns # Jump from waf to dns",
51027
- ""
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()
51028
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");
51029
51094
  }
51030
- function formatDomainsSection() {
51031
- const output = ["DOMAINS"];
51032
- const domains = Array.from(domainRegistry.values()).sort(
51033
- (a, b) => a.name.localeCompare(b.name)
51034
- );
51035
- const maxNameLen = Math.max(...domains.map((d) => d.name.length));
51036
- for (const domain of domains) {
51037
- const padding = " ".repeat(maxNameLen - domain.name.length + 2);
51038
- output.push(` ${domain.name}${padding}${domain.descriptionShort}`);
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
+ }
51039
51109
  }
51040
- return output;
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");
51041
51143
  }
51042
51144
 
51043
51145
  // src/repl/executor.ts