@robinmordasiewicz/f5xc-xcsh 6.29.0 → 6.30.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: "Provides management capabilities for static components used in the F5 XC admin console and user interface. Enables operations to deploy, retrieve, update, and list static UI assets within namespace boundaries. Supports configuration of console interface elements, component metadata management, and asset lifecycle operations. Use this domain to manage custom UI components, static resources, and interface configurations that extend or customize the admin console experience.",
44112
- descriptionShort: "Static UI component and console asset management",
44113
- descriptionMedium: "Manage static components for the admin console interface. Deploy, retrieve, and list UI assets and configuration elements within namespaces.",
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.",
44114
44114
  aliases: ["console-ui", "ui-assets", "static-components"],
44115
44115
  complexity: "simple",
44116
44116
  isPreview: false,
@@ -44122,9 +44122,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44122
44122
  ["api", {
44123
44123
  name: "api",
44124
44124
  displayName: "Api",
44125
- description: "Comprehensive API lifecycle management including automatic discovery and cataloging of APIs across your infrastructure, security testing to identify vulnerabilities and validate behavior, credential management for secure API access, and policy-driven API grouping. Define testing policies to continuously validate API security posture, organize APIs into logical groups for governance, and integrate with WAF and network security controls. Supports marking endpoints as non-API traffic and...",
44126
- descriptionShort: "API discovery, security testing, and credential management",
44127
- descriptionMedium: "Discover and catalog APIs, test security behavior, manage credentials, and define API groups with testing policies for comprehensive API lifecycle...",
44125
+ description: "Catalog services automatically to maintain an inventory of operations and their characteristics. Organize related resources by function or ownership through logical groupings. Establish verification procedures that confirm authentication requirements and expected response structures. Link definitions with load balancers for traffic routing decisions. Flag non-standard paths for exclusion from automated scanning. Monitor resource status and metadata throughout deployment zones.",
44126
+ descriptionShort: "Discover, catalog, and test service interfaces",
44127
+ descriptionMedium: "Define interface groups and discovery policies. Set up verification rules to check security posture and expected patterns across environments.",
44128
44128
  aliases: ["apisec", "api-discovery"],
44129
44129
  complexity: "advanced",
44130
44130
  isPreview: false,
@@ -44200,9 +44200,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44200
44200
  ["bigip", {
44201
44201
  name: "bigip",
44202
44202
  displayName: "Bigip",
44203
- description: "Configure and manage BigIP F5 appliance integration with Distributed Cloud infrastructure. Create and deploy iRule scripts for advanced traffic manipulation, manage data groups for dynamic configuration, configure Access Policy Manager (APM) settings for authentication and access control, and define BigIP virtual servers. Provides metrics collection for APM performance monitoring and enables seamless hybrid deployments combining traditional BigIP infrastructure with cloud-native services...",
44204
- descriptionShort: "BigIP appliance management, iRules, and data groups",
44205
- descriptionMedium: "Manage BigIP F5 appliances including iRule script configuration, data groups, APM policies, and virtual server integration with Distributed Cloud.",
44203
+ description: "Define custom rule-based policies governing routing decisions and request handling. Build organized collections for network ranges, string patterns, and key-value entries. Map cloud services to physical appliances through connector setups. Link identity workflows using access modules. Track performance metrics and coordinate synchronization between components.",
44204
+ descriptionShort: "Manage iRules, data groups, and virtual servers",
44205
+ descriptionMedium: "Configure traffic logic scripts and structured list entries. Establish appliance bindings and access module integrations.",
44206
44206
  aliases: ["f5-bigip", "irule", "ltm"],
44207
44207
  complexity: "moderate",
44208
44208
  isPreview: false,
@@ -44214,9 +44214,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44214
44214
  ["billing_and_usage", {
44215
44215
  name: "billing_and_usage",
44216
44216
  displayName: "Billing And Usage",
44217
- description: "Comprehensive billing and usage management for F5 XC tenants. Handle subscription plan transitions between tiers, configure primary and secondary payment methods, and download invoice PDFs. Monitor resource quota limits and current usage across namespaces. Supports custom invoice listing, quota configuration per namespace, and contact management for billing communications. Essential for financial operations, capacity planning, and subscription lifecycle management.",
44218
- descriptionShort: "Subscription billing, payment methods, and usage tracking",
44219
- descriptionMedium: "Manage subscription plans, payment methods, invoices, and resource quotas. Track usage limits and billing transitions across namespaces.",
44217
+ description: "Set up payment methods with primary and secondary designations for redundancy. Initiate plan transitions between subscription tiers with state tracking. Download invoice PDFs and query custom invoice lists by date range or status. Define quota limits per namespace and monitor current usage against allocated capacity. Swap payment method roles without service interruption.",
44218
+ descriptionShort: "Manage subscription plans and payment methods",
44219
+ descriptionMedium: "Configure billing transitions and payment processing. Track invoices and monitor resource quota consumption across namespaces.",
44220
44220
  aliases: ["billing-usage", "quotas", "usage-tracking"],
44221
44221
  complexity: "moderate",
44222
44222
  isPreview: false,
@@ -44228,9 +44228,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44228
44228
  ["blindfold", {
44229
44229
  name: "blindfold",
44230
44230
  displayName: "Blindfold",
44231
- description: "Configure and manage cryptographic secret protection with policy-based access controls. Create secret policies and policy rules that govern how sensitive data is encrypted, shared, and accessed across namespaces. Retrieve public keys for encryption operations, process policy information for secret sharing workflows, and decrypt secrets with proper authorization. Monitor secret access through comprehensive audit logs with aggregation and scrolling capabilities. Enforce data protection...",
44232
- descriptionShort: "Secret encryption and policy-based data protection",
44233
- descriptionMedium: "Manage encryption keys, secret policies, and sensitive data protection. Configure policy rules for secure secret sharing with audit logging.",
44231
+ description: "Define policy rules with label matching and combining algorithms. Set up transformers and matchers to control data safeguarding. Track access patterns through timestamped records with scroll queries and date groupings. Retrieve public keys for cryptographic operations and process policy information for decryption workflows.",
44232
+ descriptionShort: "Manage secret encryption and policy rules",
44233
+ descriptionMedium: "Configure protection policies and access controls for sensitive data. Monitor usage through detailed logs and date-based rollups.",
44234
44234
  aliases: ["bf", "encrypt", "secrets"],
44235
44235
  complexity: "moderate",
44236
44236
  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: "Manage comprehensive bot and threat defense capabilities including Shape bot defense instance configuration, threat protection manager (TPM) categories for threat classification, and API key provisioning for automated defense systems. Create and manage TPM categories to organize threats by type, configure bot defense instances per namespace, and handle TPM manager lifecycle operations. Supports preauthorization and provisioning workflows for integrating threat intelligence services with...",
44246
- descriptionShort: "Bot detection, threat categorization, and defense management",
44247
- descriptionMedium: "Configure bot defense instances, manage threat categories, and provision TPM API keys for automated threat detection and mitigation.",
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.",
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 using path matchers, cookie matching, header conditions, and query parameter filters. Configure TTL settings and cache eligibility options for fine-grained control over cached content. Deploy load balancers with access logging, metrics collection, and cache purge capabilities. Manage subscription services and monitor service operation status for active deployments.",
44260
- descriptionShort: "Configure content delivery and caching rules",
44261
- descriptionMedium: "Set up cache rules with path matching and TTL controls. Define load balancers for optimized content distribution across edge locations.",
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.",
44262
44262
  aliases: ["cache", "content"],
44263
44263
  complexity: "advanced",
44264
44264
  isPreview: false,
@@ -44270,9 +44270,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44270
44270
  ["ce_management", {
44271
44271
  name: "ce_management",
44272
44272
  displayName: "Ce Management",
44273
- description: "Configure and manage Customer Edge (CE) site infrastructure across distributed deployments. Define network interfaces with DHCP, IPv6, and dedicated management settings. Organize sites into fleets for coordinated management. Handle site registration workflows including token-based registration, image downloads, and suggested configuration values. Monitor and execute site upgrades with pre-upgrade checks and status tracking. Supports both dedicated and Ethernet interface types with...",
44274
- descriptionShort: "Customer Edge site lifecycle and network configuration",
44275
- descriptionMedium: "Manage Customer Edge sites including network interfaces, fleet configurations, site upgrades, and registration workflows for distributed deployments.",
44273
+ description: "Define network connectivity parameters including address allocation ranges, dual-stack protocol support, and isolated administrative ports for out-of-band access. Group physical locations under common policy templates for streamlined oversight. Onboard new deployments through secure credential workflows with expiration policies. Execute controlled software transitions featuring pre-flight validation, rollback capabilities, and progress tracking to maintain service continuity.",
44274
+ descriptionShort: "Manage Customer Edge sites and network interfaces",
44275
+ descriptionMedium: "Configure DHCP pools, IPv6 addressing, and dedicated management ports. Handle site tokens with lifecycle controls and software version transitions.",
44276
44276
  aliases: ["ce-mgmt", "edge-management", "ce-lifecycle"],
44277
44277
  complexity: "advanced",
44278
44278
  isPreview: false,
@@ -44284,9 +44284,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44284
44284
  ["certificates", {
44285
44285
  name: "certificates",
44286
44286
  displayName: "Certificates",
44287
- description: "Comprehensive certificate lifecycle management for securing application communications. Configure SSL/TLS certificates and certificate chains for endpoints, manage trusted Certificate Authority (CA) lists for client verification, and maintain Certificate Revocation Lists (CRLs) to invalidate compromised certificates. Supports certificate manifests for organized deployment across namespaces, enabling mTLS authentication, HTTPS termination, and secure service-to-service communication patterns.",
44288
- descriptionShort: "SSL/TLS certificate and trusted CA management",
44289
- descriptionMedium: "Manage SSL/TLS certificates, certificate chains, trusted CA lists, and certificate revocation lists for secure communications.",
44287
+ description: "Create PKI artifacts organizing cryptographic identity materials by namespace for multi-tenant isolation. Deploy keypair bundles with issuer hierarchies for TLS termination. Establish verification anchor collections governing which external parties can authenticate. Maintain deny-lists blocking compromised identities from initiating sessions. Organize resources within independent security boundaries supporting granular access control.",
44288
+ descriptionShort: "Manage SSL/TLS certificate chains and trusted CAs",
44289
+ descriptionMedium: "Configure certificate manifests linking keys to credential bundles. Define trust anchors for validating client authenticity during mutual TLS.",
44290
44290
  aliases: ["cert", "certs", "ssl", "tls"],
44291
44291
  complexity: "moderate",
44292
44292
  isPreview: false,
@@ -44298,9 +44298,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44298
44298
  ["cloud_infrastructure", {
44299
44299
  name: "cloud_infrastructure",
44300
44300
  displayName: "Cloud Infrastructure",
44301
- description: "Establish and manage connectivity to major cloud providers including AWS, Azure, and GCP. Configure cloud credentials and authentication for secure provider access. Create and manage VPC attachments, transit gateways, and route tables for cross-cloud networking. Support elastic provisioning with automatic resource discovery and reapplication workflows. Monitor cloud connection metrics and segment performance. Integrate with Customer Edge sites for hybrid cloud deployments across multiple...",
44302
- descriptionShort: "Multi-cloud provider connectivity and credential management",
44303
- descriptionMedium: "Connect to AWS, Azure, and GCP cloud providers. Manage cloud credentials, VPC attachments, transit gateways, and cross-cloud networking with...",
44301
+ description: "Establish connections to AWS, Azure, and GCP environments with secure authentication and network discovery. Define gateway links, edge site peering, and elastic provisioning workflows. Monitor segment performance and connection health across geographic regions. Create automated VPC attachment policies with intelligent path selection between customer locations and cloud workloads.",
44302
+ descriptionShort: "Connect and manage multi-cloud providers",
44303
+ descriptionMedium: "Configure cloud provider credentials and VPC attachments. Manage AWS transit gateways, Azure route tables, and cross-cloud connectivity.",
44304
44304
  aliases: ["cloud", "infra", "provider"],
44305
44305
  complexity: "moderate",
44306
44306
  isPreview: false,
@@ -44312,9 +44312,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44312
44312
  ["container_services", {
44313
44313
  name: "container_services",
44314
44314
  displayName: "Container Services",
44315
- description: "Container Services (XCCS) enables deployment and management of containerized applications across distributed edge sites without requiring full Kubernetes complexity. Create virtual Kubernetes clusters for isolated multi-tenant environments, define workload flavors for resource allocation, and deploy container workloads with simplified orchestration. Monitor workload usage and PVC metrics, manage namespace isolation, and integrate with site infrastructure for edge-native container...",
44316
- descriptionShort: "Edge container workloads and virtual Kubernetes management",
44317
- descriptionMedium: "Deploy and manage containerized workloads at the edge with simplified orchestration. Configure virtual Kubernetes clusters, workload flavors, and...",
44315
+ description: "Create definitions for applications running on distributed infrastructure. Establish standardized templates controlling resource consumption and disk limits. Set up partitioned execution contexts supporting namespace separation and multi-tenant isolation. Track persistent volume claims and usage metrics. Connect with mesh networking for traffic routing.",
44316
+ descriptionShort: "Deploy containerized workloads across sites",
44317
+ descriptionMedium: "Run services with simplified orchestration. Define blueprints governing processor and storage allocation.",
44318
44318
  aliases: ["vk8s", "containers", "workloads"],
44319
44319
  complexity: "moderate",
44320
44320
  isPreview: false,
@@ -44326,9 +44326,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44326
44326
  ["data_and_privacy_security", {
44327
44327
  name: "data_and_privacy_security",
44328
44328
  displayName: "Data And Privacy Security",
44329
- description: "Manage comprehensive data privacy and security controls including sensitive data detection policies, custom data type definitions, and log management analytics (LMA) region configurations. Define patterns for identifying PII, financial data, and other sensitive information with configurable actions for masking, alerting, or blocking. Configure LMA regions with Elasticsearch, Kafka, or ClickHouse backends for centralized security logging and compliance auditing. Integrate geo-configurations...",
44330
- descriptionShort: "Sensitive data detection, classification, and privacy...",
44331
- descriptionMedium: "Configure data types, sensitive data policies, and LMA regions for detecting, classifying, and protecting personally identifiable information and...",
44329
+ description: "Set up sensitive data policies that identify and protect personally identifiable information across traffic flows. Create custom data type definitions matching organizational privacy standards and industry regulations. Configure LMA region parameters including Clickhouse, Elastic, and Kafka integrations. Deploy geo-configurations enforcing data residency rules and regional compliance mandates. Monitor detection status through condition tracking and secret management with blindfold encryption.",
44330
+ descriptionShort: "Configure sensitive data detection and privacy policies",
44331
+ descriptionMedium: "Define custom data types for PII classification. Manage LMA regions and geo-configurations to meet regulatory compliance requirements.",
44332
44332
  aliases: ["data-privacy", "pii", "sensitive-data", "lma"],
44333
44333
  complexity: "simple",
44334
44334
  isPreview: false,
@@ -44354,9 +44354,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44354
44354
  ["ddos", {
44355
44355
  name: "ddos",
44356
44356
  displayName: "Ddos",
44357
- description: "Comprehensive DDoS protection and infrastructure security management. Configure deny list rules to block malicious traffic sources, create firewall rule groups for granular traffic filtering, and manage protection tunnels for secure infrastructure connectivity. The infraprotect APIs enable proactive threat mitigation through customizable security policies, real-time tunnel status monitoring, and namespace-scoped rule management. Integrates with network security and virtual load balancing for...",
44358
- descriptionShort: "DDoS protection and infrastructure security policies",
44359
- descriptionMedium: "Configure DDoS protection policies, deny lists, and firewall rules. Monitor infrastructure threats and manage protection tunnels for network security.",
44357
+ description: "Deploy definitions that block IP addresses and network segments from accessing protected resources. Organize by threat type or source classification. Manage secure channels routing suspicious packets for analysis before reaching origin servers. Update status for real-time visibility into active defenses. Add items during attacks and monitor health metrics.",
44358
+ descriptionShort: "Configure blocking policies and tunnel protection",
44359
+ descriptionMedium: "Set up firewall configurations with deny list rules. Filter malicious traffic through inspection points.",
44360
44360
  aliases: ["dos", "ddos-protect"],
44361
44361
  complexity: "advanced",
44362
44362
  isPreview: false,
@@ -44368,9 +44368,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44368
44368
  ["dns", {
44369
44369
  name: "dns",
44370
44370
  displayName: "Dns",
44371
- description: "Create and manage authoritative zones with support for standard record types including A, AAAA, CNAME, CAA, CERT, and AFSDB. Import existing configurations through BIND file uploads or zone transfers. Define health check policies that monitor backend availability and automatically adjust record responses. Export zone files for backup or migration. Access request logs and performance metrics to analyze query patterns and troubleshoot resolution issues across namespaces.",
44372
- descriptionShort: "Manage zones, records, and resolution policies",
44373
- descriptionMedium: "Configure zone imports from BIND files or AXFR transfers. Set up health checks for load-balanced records and monitor query metrics.",
44371
+ description: "Set up primary and secondary zones with support for A, AAAA, CNAME, CAA, CERT, and AFSDB record types. Define health checks to monitor target availability and enable automatic failover between record destinations. Clone existing domains, import zone configurations from external servers, or export zone files for backup. Track query metrics and request logs to analyze resolution patterns across namespaces.",
44372
+ descriptionShort: "Manage zones, records, and load balancing",
44373
+ descriptionMedium: "Configure authoritative name services with record sets and health checks. Import zones from BIND files or transfer via AXFR protocol.",
44374
44374
  aliases: ["dns-zone", "zones"],
44375
44375
  complexity: "advanced",
44376
44376
  isPreview: false,
@@ -44432,9 +44432,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44432
44432
  ["generative_ai", {
44433
44433
  name: "generative_ai",
44434
44434
  displayName: "Generative Ai",
44435
- description: "Generative AI services providing intelligent automation and analysis capabilities. Configure AI assistant policies and submit queries with feedback tracking for continuous improvement. Enable flow anomaly detection powered by machine learning. Manage AI data collection through the BFDP subsystem including feature enablement, token management, and subscription controls. Supports IP allocation for GIA services. Integrates dashboard visualization with customizable displays, filters, and link...",
44436
- descriptionShort: "AI-powered features, assistants, and data collection",
44437
- descriptionMedium: "Access generative AI capabilities including AI assistant queries, flow anomaly detection, and AI data collection with feedback mechanisms.",
44435
+ description: "Set up query evaluation and response handling for intelligent assistant workflows. Manage rating collection with positive and negative outcome tracking. Subscribe to data streams for traffic pattern detection and behavioral analysis. Allocate and deallocate IP resources for ML infrastructure. Control feature enablement and token management for telemetry collection paths.",
44436
+ descriptionShort: "Access AI assistant queries and feedback",
44437
+ descriptionMedium: "Configure machine learning interactions and collect response ratings. Enable flow pattern monitoring through data subscription channels.",
44438
44438
  aliases: ["ai", "genai", "assistant"],
44439
44439
  complexity: "simple",
44440
44440
  isPreview: true,
@@ -44446,9 +44446,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44446
44446
  ["managed_kubernetes", {
44447
44447
  name: "managed_kubernetes",
44448
44448
  displayName: "Managed Kubernetes",
44449
- description: "Configure and manage Managed Kubernetes (XCKS) security and access controls. Define cluster roles with fine-grained permissions for API resources and non-resource URLs. Create role bindings to associate users and groups with cluster-wide permissions. Enforce pod security standards through admission controllers with configurable enforcement levels. Manage private container registries for secure image distribution. Integrates with external Kubernetes clusters including EKS, AKS, and GKE for...",
44450
- descriptionShort: "Kubernetes RBAC, pod security, and container registries",
44451
- descriptionMedium: "Manage Kubernetes cluster roles, RBAC bindings, pod security admission policies, and container registries for enterprise deployments.",
44449
+ description: "Create granular access controls for namespace resources and non-resource URLs. Map permissions to users, groups, or service accounts through binding configurations. Deploy security admission enforcement using baseline, restricted, or privileged profiles. Register private image sources with credential management for secure pulls. Integrate with external managed solutions including EKS, AKS, and GKE infrastructure.",
44450
+ descriptionShort: "Configure Kubernetes RBAC and pod security policies",
44451
+ descriptionMedium: "Define permission boundaries for workload access. Set up private image repositories with authentication for enterprise deployments.",
44452
44452
  aliases: ["mk8s", "appstack", "k8s-mgmt"],
44453
44453
  complexity: "moderate",
44454
44454
  isPreview: false,
@@ -44460,9 +44460,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44460
44460
  ["marketplace", {
44461
44461
  name: "marketplace",
44462
44462
  displayName: "Marketplace",
44463
- description: "Access and manage the marketplace ecosystem including third-party integrations, add-on services, and external connectors. Configure connection types for direct, GRE tunnel, and IPSec connectivity with customizable IKE parameters and DPD keepalive settings. Manage navigation tiles for custom UI extensions, activate and monitor add-on service status across namespaces, and integrate with external platforms like Terraform. Supports TPM policy management and configuration management instances for...",
44464
- descriptionShort: "Third-party integrations, add-ons, and extensions",
44465
- descriptionMedium: "Manage marketplace extensions, external connectors, and third-party add-on services. Configure Terraform integrations and TPM policies.",
44463
+ description: "Set up secure tunnel connections using IKEv1/IKEv2 parameters, GRE encapsulation with source and destination addressing, or dedicated link types. Manage DPD keep-alive timers and tunnel termination points for reliable connectivity. Activate purchasable services with namespace-scoped status tracking. Create custom portal widgets for interface integration and configure Cloud Manager instances for Terraform and infrastructure automation workflows.",
44464
+ descriptionShort: "Manage third-party integrations and add-ons",
44465
+ descriptionMedium: "Configure connector tunnels with IPSec, GRE, or direct links. Deploy purchasable services and portal customizations across namespaces.",
44466
44466
  aliases: ["market", "addons", "extensions"],
44467
44467
  complexity: "moderate",
44468
44468
  isPreview: false,
@@ -44474,9 +44474,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44474
44474
  ["network", {
44475
44475
  name: "network",
44476
44476
  displayName: "Network",
44477
- description: "Comprehensive network infrastructure management including BGP routing with ASN configuration and peering policies, IPsec tunnel establishment with full IKE phase 1 and phase 2 parameter control, and network connector configuration for hybrid cloud connectivity. Supports SRv6 segment routing, subnet management, DC cluster groups for data center integration, static and dynamic route definitions, and IP prefix set policies. Enables secure site-to-site VPN connections, multi-cloud network...",
44478
- descriptionShort: "BGP routing, IPsec tunnels, and network connectivity",
44479
- descriptionMedium: "Configure BGP routing policies, IPsec tunnels with IKE phases, network connectors, SRv6, and IP prefix sets for secure site-to-site connectivity.",
44477
+ description: "Deploy secure site connectivity using IPsec tunnels with customizable IKE phase settings, encryption algorithms, and DH groups. Configure BGP routing with peer state monitoring, ASN management, and traffic policies. Set up SRv6 segments, IP prefix sets, and subnet definitions. Manage DC cluster groups for data center integration and define routes for traffic steering across distributed infrastructure.",
44478
+ descriptionShort: "Configure BGP routing, tunnels, and connectivity",
44479
+ descriptionMedium: "Manage IPsec tunnels and IKE configurations. Define BGP peers, ASN assignments, and routing policies for site-to-site connections.",
44480
44480
  aliases: ["net", "routing", "bgp"],
44481
44481
  complexity: "advanced",
44482
44482
  isPreview: false,
@@ -44488,9 +44488,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44488
44488
  ["network_security", {
44489
44489
  name: "network_security",
44490
44490
  displayName: "Network Security",
44491
- description: "Network security controls for protecting traffic at the network layer. Configure network firewalls with stateful inspection and ACL rules. Define NAT policies for address translation, port forwarding, and dynamic pool management. Create network policy sets for segmentation and micro-segmentation between workloads. Implement policy-based routing to direct traffic based on source, destination, or application criteria. Manage segment connections for multi-site network isolation. Configure...",
44492
- descriptionShort: "Network firewall, NAT, ACL, and policy-based routing",
44493
- descriptionMedium: "Configure network firewalls, NAT policies, ACLs, and policy-based routing. Manage network segmentation, port forwarding, and forward proxy policies.",
44491
+ description: "Manage firewall configurations with match criteria and action rules. Create NAT policies using dynamic pools and port configurations for address translation. Define segment connections to isolate traffic between network zones. Configure policy-based routing to direct packets based on source, destination, or protocol attributes. Set up forward proxy policies and access control lists to govern outbound connections.",
44492
+ descriptionShort: "Configure firewalls, NAT, and routing policies",
44493
+ descriptionMedium: "Define network firewall rules and NAT policies. Set up policy-based routing with segment connections for traffic control.",
44494
44494
  aliases: ["netsec", "nfw"],
44495
44495
  complexity: "advanced",
44496
44496
  isPreview: false,
@@ -44502,9 +44502,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44502
44502
  ["nginx_one", {
44503
44503
  name: "nginx_one",
44504
44504
  displayName: "Nginx One",
44505
- description: "Integrate and manage NGINX One platform capabilities including subscription lifecycle management, NGINX Plus instance provisioning, and server configuration. Configure dataplane servers, manage nginx instances with WAF and API discovery specifications, and enable service discovery integrations. Supports NGINX Configuration Sync Gateway (CSG) configurations for centralized management workflows. Typical operations include subscribing to NGINX One services, retrieving server status and...",
44506
- descriptionShort: "NGINX One platform integration and instance management",
44507
- descriptionMedium: "Manage NGINX One platform subscriptions, configure NGINX Plus instances and servers, and integrate service discovery with centralized...",
44505
+ description: "Set up load balancing configurations with backend server definitions and routing logic. Create monitoring schedules for availability tracking across distributed nodes. Build request handling pipelines with rate controls and authentication layers. Track instance performance metrics and traffic patterns. Coordinate failover mechanisms using weighted distribution and priority-based selection.",
44506
+ descriptionShort: "Configure NGINX proxy instances and deployments",
44507
+ descriptionMedium: "Manage upstream server pools and health monitors. Define SSL termination rules and connection parameters for gateway endpoints.",
44508
44508
  aliases: ["nginx", "nms", "nginx-plus"],
44509
44509
  complexity: "simple",
44510
44510
  isPreview: false,
@@ -44516,9 +44516,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44516
44516
  ["object_storage", {
44517
44517
  name: "object_storage",
44518
44518
  displayName: "Object Storage",
44519
- description: "Manage versioned object storage for mobile application components and platform integrations. Upload and retrieve mobile app shield configurations, SDK integrations, and custom artifacts organized by namespace and object type. Support for multiple versions of each object enables rollback and version-specific deployments. Presigned URLs provide secure, time-limited access for direct object downloads. Object types include mobile-app-shield for application protection, mobile-integrator for...",
44520
- descriptionShort: "Object storage for mobile SDK artifacts and integrations",
44521
- descriptionMedium: "Store and retrieve versioned objects including mobile app shields, SDK integrations, and custom artifacts with presigned URL access.",
44519
+ description: "Deploy binary artifacts and configuration bundles with automatic version tracking and lifecycle policies. Organize content by category including protection signatures, SDK packages, and third-party connector files. Enable time-limited download links for secure distribution without credential exposure. Track revision history for audit trails and support rollback to previous artifact versions when needed.",
44520
+ descriptionShort: "Manage stored objects and bucket versioning",
44521
+ descriptionMedium: "Create versioned content within tenant buckets. Generate secure access URLs for SDK distributions and application protection resources.",
44522
44522
  aliases: ["storage", "s3", "buckets"],
44523
44523
  complexity: "simple",
44524
44524
  isPreview: false,
@@ -44530,9 +44530,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44530
44530
  ["observability", {
44531
44531
  name: "observability",
44532
44532
  displayName: "Observability",
44533
- description: "Comprehensive synthetic monitoring and observability capabilities for proactive infrastructure health assessment. Configure DNS monitors to validate resolution across AWS regions, set up HTTP monitors for endpoint availability testing, and track SSL/TLS certificate expiration status. Access real-time health summaries at global and namespace levels, review historical monitoring data, and generate detailed reports for DNS and HTTP monitors. Integrate with dashboards to visualize monitoring...",
44534
- descriptionShort: "Synthetic monitoring, health checks, and observability...",
44535
- descriptionMedium: "Configure synthetic monitoring with DNS and HTTP health checks. Track certificate status, monitor global health summaries, and analyze monitoring...",
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.",
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.",
44536
44536
  aliases: ["obs", "monitoring", "synth"],
44537
44537
  complexity: "advanced",
44538
44538
  isPreview: false,
@@ -44544,9 +44544,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44544
44544
  ["rate_limiting", {
44545
44545
  name: "rate_limiting",
44546
44546
  displayName: "Rate Limiting",
44547
- description: "Manage rate limiting policies to protect applications from traffic surges and abuse. Configure rate limiters with customizable thresholds, time periods, and enforcement actions including blocking or throttling. Implement policers using leaky bucket algorithms for smooth traffic shaping. Define protocol-specific policers for granular control over different traffic types. Integrate with virtual hosts and load balancers to enforce rate limits at the edge, preventing resource exhaustion and...",
44548
- descriptionShort: "Traffic rate limiting, policers, and throttling controls",
44549
- descriptionMedium: "Configure rate limiters and policers to control traffic flow. Define request thresholds, leaky bucket algorithms, and enforcement actions for API...",
44547
+ description: "Create rate limiter policies with configurable time periods using seconds, minutes, or hours granularity. Deploy policers and protocol policers to enforce bandwidth constraints across namespaces. Define limit values, burst allowances, and blocking behaviors when thresholds trigger. Integrate with load balancers and security policies for layered traffic management and abuse prevention.",
44548
+ descriptionShort: "Configure traffic throttling and policer rules",
44549
+ descriptionMedium: "Define request limits and burst thresholds for traffic control. Set up leaky bucket algorithms and block actions for exceeded quotas.",
44550
44550
  aliases: ["ratelimit", "throttle", "policer"],
44551
44551
  complexity: "simple",
44552
44552
  isPreview: false,
@@ -44558,9 +44558,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44558
44558
  ["secops_and_incident_response", {
44559
44559
  name: "secops_and_incident_response",
44560
44560
  displayName: "Secops And Incident Response",
44561
- description: "Security operations and incident response capabilities for detecting and mitigating malicious user activity. Create mitigation policies that define automated responses based on user threat levels, including blocking, challenging, or rate limiting suspicious users. Configure rules that match specific malicious user types and threat severity levels to appropriate mitigation actions. Supports namespace-scoped configurations for managing security policies across different application...",
44562
- descriptionShort: "Malicious user detection and automated threat mitigation",
44563
- descriptionMedium: "Configure automated responses to malicious user behavior. Define mitigation rules based on threat levels and apply actions like blocking or rate...",
44561
+ description: "Manage incident response workflows that detect and mitigate malicious users automatically. Create rules matching threat levels to actions like blocking, rate limiting, or alerting. Set up mitigation policies per namespace to isolate security responses. Define thresholds for user behavior analysis and configure graduated responses based on severity. Integrate with bot defense and WAF systems for coordinated protection across application layers.",
44562
+ descriptionShort: "Configure automated threat mitigation rules",
44563
+ descriptionMedium: "Define malicious user detection policies and response actions. Apply blocking or rate limiting based on threat levels.",
44564
44564
  aliases: ["secops", "incident-response", "mitigation"],
44565
44565
  complexity: "simple",
44566
44566
  isPreview: false,
@@ -44572,9 +44572,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44572
44572
  ["service_mesh", {
44573
44573
  name: "service_mesh",
44574
44574
  displayName: "Service Mesh",
44575
- description: "Manage service mesh infrastructure including endpoint discovery and intelligent routing between distributed services. Define application types with learned API schemas, security risk classifications, and authentication configurations. Configure NFV (Network Function Virtualization) services with lifecycle management including force-delete operations. Leverage machine learning capabilities for automatic API endpoint detection, schema learning, and traffic pattern analysis. Integrate with...",
44576
- descriptionShort: "Service mesh connectivity, discovery, and NFV management",
44577
- descriptionMedium: "Configure service mesh networking with endpoint discovery, application type definitions, API endpoint learning, and NFV service lifecycle management.",
44575
+ description: "Create classifications to organize services and support automatic identification of interconnected components. Set up analysis pipelines to understand patterns and build intelligent routing rules. Define network function virtualization for regional architectures. Configure authentication settings including location, state, and type recognition.",
44576
+ descriptionShort: "Configure application types and discovery",
44577
+ descriptionMedium: "Manage NFV integrations and workload categories. Enable traffic learning across distributed deployments.",
44578
44578
  aliases: ["mesh", "svc-mesh"],
44579
44579
  complexity: "advanced",
44580
44580
  isPreview: false,
@@ -44586,9 +44586,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44586
44586
  ["shape", {
44587
44587
  name: "shape",
44588
44588
  displayName: "Shape",
44589
- description: "Shape Security integration for advanced bot defense and threat prevention capabilities. Configure bot infrastructure deployments with policy management, deployment history tracking, and status monitoring. Manage mobile SDK attributes for application shielding and integrator configurations. Subscribe to bot defense add-ons and client-side defense services. Includes SafeAP policy configuration, threat recognition rules, and automated bot mitigation across namespaces with comprehensive...",
44590
- descriptionShort: "Bot defense and threat prevention with Shape Security",
44591
- descriptionMedium: "Configure Shape Security policies for bot defense, threat recognition, and mobile SDK protection. Manage bot infrastructure deployments and SafeAP...",
44589
+ description: "Set up bot defense infrastructure across namespaces with deployment tracking and status monitoring. Integrate mobile SDK attributes for app shielding and device recognition. Subscribe to threat intelligence services for real-time protection updates. Define cluster states and location-based policies for distributed bot mitigation. Track deployment history and manage policy configurations through centralized infrastructure objects.",
44590
+ descriptionShort: "Configure bot defense and threat prevention",
44591
+ descriptionMedium: "Deploy bot infrastructure with mobile SDK integration. Manage subscription services and policy enforcement for automated threat protection.",
44592
44592
  aliases: ["shape-sec", "safeap"],
44593
44593
  complexity: "advanced",
44594
44594
  isPreview: false,
@@ -44600,9 +44600,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44600
44600
  ["sites", {
44601
44601
  name: "sites",
44602
44602
  displayName: "Sites",
44603
- description: "Comprehensive site infrastructure management for deploying F5 XC across multiple cloud providers and edge locations. Configure AWS Transit Gateway sites with VPN tunnels, VPC IP prefixes, and security settings. Manage virtual sites for logical grouping and policy application. Deploy Secure Mesh sites for networking-focused edge deployments, integrate external Kubernetes clusters as Customer Edge nodes, and configure cloud-specific resources including AWS VPC, Azure VNet, and GCP VPC sites....",
44604
- descriptionShort: "Multi-cloud site deployment and edge infrastructure",
44605
- descriptionMedium: "Deploy and manage F5 XC sites across AWS, Azure, and GCP. Configure AWS TGW sites, virtual sites, managed Kubernetes, and Customer Edge integrations.",
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.",
44606
44606
  aliases: ["site", "deployment"],
44607
44607
  complexity: "advanced",
44608
44608
  isPreview: false,
@@ -44666,9 +44666,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44666
44666
  ["statistics", {
44667
44667
  name: "statistics",
44668
44668
  displayName: "Statistics",
44669
- description: "Comprehensive operational analytics and monitoring capabilities for distributed cloud infrastructure. Configure alert policies with custom matchers and grouping rules to detect anomalies across namespaces. Manage alert receivers with confirmation, testing, and verification workflows for reliable notification delivery. Access flow statistics, view historical alerts, generate reports and graphs for capacity planning, track service topology and discovery patterns, and monitor real-time status...",
44670
- descriptionShort: "Flow statistics, alerts, logs, and operational analytics",
44671
- descriptionMedium: "Access flow statistics and analytics, configure alert policies and receivers, view logs, generate reports and graphs, and monitor site status.",
44669
+ description: "Set up alert policies with custom matchers, label filters, and group-by rules for targeted notifications. Define routing channels via email, webhook, or integration receivers with confirmation and verification workflows. Access flow analytics, historical alert data, and namespace-scoped metrics. Build capacity planning graphs and operational summaries. Observe deployment health and service discovery mapping across distributed environments.",
44670
+ descriptionShort: "Monitor alerts, logs, and flow analytics",
44671
+ descriptionMedium: "Configure alerting policies and notification receivers. Track service topology, build dashboards, and view site health summaries.",
44672
44672
  aliases: ["stats", "metrics", "logs"],
44673
44673
  complexity: "advanced",
44674
44674
  isPreview: false,
@@ -44680,9 +44680,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44680
44680
  ["support", {
44681
44681
  name: "support",
44682
44682
  displayName: "Support",
44683
- description: "Manage the complete customer support ticket lifecycle including creation, commenting, priority adjustment, escalation, and closure. Submit specialized requests such as tax exemption verification. Access site-level diagnostic capabilities including TCP dump capture, listing, and management for network troubleshooting. Integrates with operational workflows to enable support teams to gather diagnostic data directly from distributed sites while maintaining ticket-based tracking of all customer...",
44684
- descriptionShort: "Customer support ticket lifecycle and site diagnostics",
44685
- descriptionMedium: "Create, track, and manage support tickets with escalation workflows. Includes site diagnostic tools for packet capture and troubleshooting.",
44683
+ description: "Open new cases and assign severity ratings based on business impact. Append notes throughout resolution workflows. Mark items as closed or reinstate them if symptoms recur. Execute diagnostic packet captures on deployed sites for network troubleshooting. Handle tax exemption verification through certificate submission.",
44684
+ descriptionShort: "Create and track customer tickets",
44685
+ descriptionMedium: "Submit requests with file uploads and priority levels. Add comments and escalate critical incidents to engineering teams.",
44686
44686
  aliases: ["tickets", "help-desk"],
44687
44687
  complexity: "moderate",
44688
44688
  isPreview: false,
@@ -44708,9 +44708,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44708
44708
  ["tenant_and_identity", {
44709
44709
  name: "tenant_and_identity",
44710
44710
  displayName: "Tenant And Identity",
44711
- description: "Comprehensive user and tenant identity management for F5 Distributed Cloud. Configure user settings including profile images, notification preferences (admin and combined), and view preferences. Manage user sessions with listing and control capabilities. Handle OTP (one-time password) administration including admin resets. Support identity management (IDM) enable/disable operations. Process initial access requests for new users. Manage customer support ticket attachments and interactions for...",
44712
- descriptionShort: "User settings, notifications, sessions, and identity...",
44713
- descriptionMedium: "Manage user profiles, notification preferences, session controls, OTP settings, and customer support interactions. Configure identity management...",
44711
+ description: "Set up granular alert routing for administrative and combined channels with personalized delivery options. Control active login sessions and enforce one-time password resets for security compliance. Define display layouts and avatar images for customized user experiences. Process onboarding access submissions and toggle account management features. Coordinate support ticket attachments and client relationship interactions across managed tenant hierarchies.",
44712
+ descriptionShort: "Manage user profiles and session controls",
44713
+ descriptionMedium: "Configure OTP resets and admin alert channels. Handle view settings and profile customization for platform participants.",
44714
44714
  aliases: ["tenant-identity", "idm", "user-settings"],
44715
44715
  complexity: "advanced",
44716
44716
  isPreview: false,
@@ -44736,9 +44736,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44736
44736
  ["users", {
44737
44737
  name: "users",
44738
44738
  displayName: "Users",
44739
- description: "Comprehensive user and identity management for the F5 XC platform. Create and manage registration tokens for site and node onboarding, including cloud-init configuration retrieval. Define known label keys and values to establish consistent resource tagging taxonomies across namespaces. Configure implicit labels for automatic resource classification. Supports full lifecycle management of user-related configuration objects with metadata tracking, state management, and condition monitoring for...",
44740
- descriptionShort: "User accounts, tokens, and label management",
44741
- descriptionMedium: "Manage user accounts, registration tokens, and label systems. Configure known and implicit labels for resource organization and user identification.",
44739
+ description: "Deploy namespace-scoped access credentials with lifecycle state tracking for secure machine enrollment. Build hierarchical tagging frameworks that enable systematic organization of infrastructure elements. Retrieve automated provisioning payloads for streamlined node initialization. Enable system-level automatic tagging that applies predefined metadata to newly created objects without operator action.",
44740
+ descriptionShort: "Manage account tokens and label settings",
44741
+ descriptionMedium: "Configure credential issuance and cloud-init provisioning. Establish key-value taxonomies for consistent resource categorization across deployments.",
44742
44742
  aliases: ["user", "accounts", "iam"],
44743
44743
  complexity: "simple",
44744
44744
  isPreview: false,
@@ -44750,9 +44750,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
44750
44750
  ["virtual", {
44751
44751
  name: "virtual",
44752
44752
  displayName: "Virtual",
44753
- description: "Set up load balancing for HTTP, TCP, and UDP protocols with origin pool configuration and weighted routing. Create service policies to control access patterns and enforce rate limits on incoming requests. Deploy geo-location routing for region-aware traffic steering. Configure health checks to monitor backend availability and trigger automatic failover. Manage proxy forwarding rules and threat protection including malware scanning and campaign blocking.",
44754
- descriptionShort: "Configure HTTP and TCP load balancers",
44755
- descriptionMedium: "Manage origin pools and routing rules. Define rate limiting, service policies, and health checks for traffic distribution.",
44753
+ description: "Deploy load balancers across protocols with origin pool management and service discovery. Set up geo-location routing to direct traffic based on client location. Define rate limiter policies to control request volume and protect services from abuse. Configure health checks for origin monitoring and automatic failover. Manage service policies for access control and traffic filtering. Enable malware protection and threat campaign blocking for security enforcement.",
44754
+ descriptionShort: "Configure load balancers and origin pools",
44755
+ descriptionMedium: "Create HTTP, TCP, and UDP load balancers with origin pools. Define routing rules, health checks, and rate limiting policies.",
44756
44756
  aliases: ["lb", "loadbalancer", "vhost"],
44757
44757
  complexity: "advanced",
44758
44758
  isPreview: false,
@@ -45309,8 +45309,8 @@ function getLogoModeFromEnv(envPrefix) {
45309
45309
  var CLI_NAME = "xcsh";
45310
45310
  var CLI_FULL_NAME = "F5 Distributed Cloud Shell";
45311
45311
  function getVersion() {
45312
- if ("6.29.0") {
45313
- return "6.29.0";
45312
+ if ("6.30.0") {
45313
+ return "6.30.0";
45314
45314
  }
45315
45315
  if (process.env.XCSH_VERSION) {
45316
45316
  return process.env.XCSH_VERSION;
@@ -45579,6 +45579,15 @@ var ProfileManager = class {
45579
45579
  return null;
45580
45580
  }
45581
45581
  }
45582
+ /**
45583
+ * Clear the active profile (used for force delete)
45584
+ */
45585
+ async clearActive() {
45586
+ try {
45587
+ await fs2.unlink(this.config.activeProfileFile);
45588
+ } catch {
45589
+ }
45590
+ }
45582
45591
  /**
45583
45592
  * Set the active profile
45584
45593
  */
@@ -45694,16 +45703,54 @@ var APIError = class extends Error {
45694
45703
  };
45695
45704
 
45696
45705
  // src/api/client.ts
45706
+ var DEFAULT_RETRY_CONFIG = {
45707
+ maxRetries: 3,
45708
+ initialDelayMs: 1e3,
45709
+ maxDelayMs: 1e4,
45710
+ backoffMultiplier: 2,
45711
+ jitter: true
45712
+ };
45713
+ var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([
45714
+ 408,
45715
+ // Request Timeout
45716
+ 429,
45717
+ // Too Many Requests
45718
+ 500,
45719
+ // Internal Server Error
45720
+ 502,
45721
+ // Bad Gateway
45722
+ 503,
45723
+ // Service Unavailable
45724
+ 504
45725
+ // Gateway Timeout
45726
+ ]);
45727
+ function calculateBackoffDelay(attempt, config) {
45728
+ const exponentialDelay = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
45729
+ const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
45730
+ if (config.jitter) {
45731
+ const jitterFactor = 1 + Math.random() * 0.25;
45732
+ return Math.floor(cappedDelay * jitterFactor);
45733
+ }
45734
+ return cappedDelay;
45735
+ }
45736
+ function sleep(ms) {
45737
+ return new Promise((resolve) => setTimeout(resolve, ms));
45738
+ }
45697
45739
  var APIClient = class {
45698
45740
  serverUrl;
45699
45741
  apiToken;
45700
45742
  timeout;
45701
45743
  debug;
45744
+ retryConfig;
45702
45745
  constructor(config) {
45703
45746
  this.serverUrl = config.serverUrl.replace(/\/+$/, "");
45704
45747
  this.apiToken = config.apiToken ?? "";
45705
45748
  this.timeout = config.timeout ?? 3e4;
45706
45749
  this.debug = config.debug ?? false;
45750
+ this.retryConfig = {
45751
+ ...DEFAULT_RETRY_CONFIG,
45752
+ ...config.retry
45753
+ };
45707
45754
  }
45708
45755
  /**
45709
45756
  * Check if client has authentication configured
@@ -45734,25 +45781,21 @@ var APIClient = class {
45734
45781
  return url;
45735
45782
  }
45736
45783
  /**
45737
- * Execute an HTTP request
45784
+ * Check if an error is retryable
45738
45785
  */
45739
- async request(options) {
45740
- const url = this.buildUrl(options.path, options.query);
45741
- const headers = {
45742
- "Content-Type": "application/json",
45743
- Accept: "application/json",
45744
- ...options.headers
45745
- };
45746
- if (this.apiToken) {
45747
- headers["Authorization"] = `APIToken ${this.apiToken}`;
45786
+ isRetryableError(error) {
45787
+ if (error instanceof APIError) {
45788
+ return RETRYABLE_STATUS_CODES.has(error.statusCode);
45748
45789
  }
45749
- const body = options.body ? JSON.stringify(options.body) : null;
45750
- if (this.debug) {
45751
- console.error(`DEBUG: ${options.method} ${url}`);
45752
- if (body) {
45753
- console.error(`DEBUG: Request body: ${body}`);
45754
- }
45790
+ if (error instanceof Error) {
45791
+ return error.name === "AbortError" || error.message.includes("fetch failed") || error.message.includes("network") || error.message.includes("ECONNREFUSED") || error.message.includes("ENOTFOUND") || error.message.includes("ETIMEDOUT");
45755
45792
  }
45793
+ return false;
45794
+ }
45795
+ /**
45796
+ * Execute a single HTTP request attempt
45797
+ */
45798
+ async executeRequest(options, url, headers, body) {
45756
45799
  const controller = new AbortController();
45757
45800
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
45758
45801
  try {
@@ -45801,7 +45844,7 @@ var APIClient = class {
45801
45844
  if (error instanceof Error && error.name === "AbortError") {
45802
45845
  throw new APIError(
45803
45846
  `Request timed out after ${this.timeout}ms`,
45804
- 0,
45847
+ 408,
45805
45848
  void 0,
45806
45849
  `${options.method} ${options.path}`
45807
45850
  );
@@ -45817,6 +45860,58 @@ var APIClient = class {
45817
45860
  throw error;
45818
45861
  }
45819
45862
  }
45863
+ /**
45864
+ * Execute an HTTP request with retry logic
45865
+ */
45866
+ async request(options) {
45867
+ const url = this.buildUrl(options.path, options.query);
45868
+ const headers = {
45869
+ "Content-Type": "application/json",
45870
+ Accept: "application/json",
45871
+ ...options.headers
45872
+ };
45873
+ if (this.apiToken) {
45874
+ headers["Authorization"] = `APIToken ${this.apiToken}`;
45875
+ }
45876
+ const body = options.body ? JSON.stringify(options.body) : null;
45877
+ if (this.debug) {
45878
+ console.error(`DEBUG: ${options.method} ${url}`);
45879
+ if (body) {
45880
+ console.error(`DEBUG: Request body: ${body}`);
45881
+ }
45882
+ }
45883
+ let lastError;
45884
+ for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
45885
+ try {
45886
+ return await this.executeRequest(
45887
+ options,
45888
+ url,
45889
+ headers,
45890
+ body
45891
+ );
45892
+ } catch (error) {
45893
+ lastError = error instanceof Error ? error : new Error(String(error));
45894
+ const isRetryable = this.isRetryableError(error);
45895
+ const hasRetriesLeft = attempt < this.retryConfig.maxRetries;
45896
+ if (isRetryable && hasRetriesLeft) {
45897
+ const delay = calculateBackoffDelay(
45898
+ attempt,
45899
+ this.retryConfig
45900
+ );
45901
+ if (this.debug) {
45902
+ const statusInfo = error instanceof APIError ? ` (${error.statusCode})` : "";
45903
+ console.error(
45904
+ `DEBUG: Request failed${statusInfo}, retrying in ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`
45905
+ );
45906
+ }
45907
+ await sleep(delay);
45908
+ continue;
45909
+ }
45910
+ throw error;
45911
+ }
45912
+ }
45913
+ throw lastError ?? new Error("Request failed after all retries");
45914
+ }
45820
45915
  /**
45821
45916
  * GET request
45822
45917
  */
@@ -45880,621 +45975,8 @@ var APIClient = class {
45880
45975
  }
45881
45976
  };
45882
45977
 
45883
- // src/subscription/types.ts
45884
- var Tier = {
45885
- NoTier: "NO_TIER",
45886
- Basic: "BASIC",
45887
- // Discontinued - maps to Standard
45888
- Standard: "STANDARD",
45889
- // Active tier
45890
- Advanced: "ADVANCED",
45891
- // Active tier
45892
- Premium: "PREMIUM"
45893
- // Discontinued - maps to Advanced
45894
- };
45895
- var AddonState = {
45896
- None: "AS_NONE",
45897
- Pending: "AS_PENDING",
45898
- Subscribed: "AS_SUBSCRIBED",
45899
- Error: "AS_ERROR"
45900
- };
45901
- var AccessStatus = {
45902
- Allowed: "AS_AC_ALLOWED",
45903
- Denied: "AS_AC_PBAC_DENY",
45904
- UpgradeRequired: "AS_AC_PBAC_DENY_UPGRADE_PLAN",
45905
- ContactSales: "AS_AC_PBAC_DENY_CONTACT_SALES",
45906
- InternalService: "AS_AC_PBAC_DENY_INTERNAL_SVC",
45907
- Unknown: "AS_AC_UNKNOWN",
45908
- EOL: "AS_AC_EOL"
45909
- };
45910
- var ActivationType = {
45911
- Self: "self",
45912
- PartiallyManaged: "partially_managed",
45913
- Managed: "managed"
45914
- };
45915
- var SubscriptionState = {
45916
- Pending: "SUBSCRIPTION_PENDING",
45917
- Enabled: "SUBSCRIPTION_ENABLED",
45918
- DisablePending: "SUBSCRIPTION_DISABLE_PENDING",
45919
- Disabled: "SUBSCRIPTION_DISABLED"
45920
- };
45921
- var ValidationStatus = {
45922
- Pass: "PASS",
45923
- Fail: "FAIL",
45924
- Warning: "WARNING"
45925
- };
45926
- var QuotaStatus = {
45927
- OK: "OK",
45928
- Warning: "WARNING",
45929
- Exceeded: "EXCEEDED"
45930
- };
45931
- function isAddonActive(addon) {
45932
- return addon.state === AddonState.Subscribed;
45933
- }
45934
- function isAddonAvailable(addon) {
45935
- return addon.accessStatus === AccessStatus.Allowed && addon.state !== AddonState.Subscribed;
45936
- }
45937
- function isAddonDenied(addon) {
45938
- return addon.accessStatus === AccessStatus.Denied || addon.accessStatus === AccessStatus.UpgradeRequired || addon.accessStatus === AccessStatus.ContactSales || addon.accessStatus === AccessStatus.InternalService;
45939
- }
45940
- function isQuotaExceeded(quota) {
45941
- return quota.usage >= quota.limit;
45942
- }
45943
- function isQuotaAtRisk(quota) {
45944
- return quota.percentage >= 80 && quota.percentage < 100;
45945
- }
45946
- function getQuotaRemainingCapacity(quota) {
45947
- const remaining = quota.limit - quota.usage;
45948
- return remaining < 0 ? 0 : remaining;
45949
- }
45950
- function getTierDescription(tier) {
45951
- switch (tier) {
45952
- case Tier.NoTier:
45953
- return "No Tier";
45954
- case Tier.Basic:
45955
- return "Standard";
45956
- // Basic discontinued, maps to Standard
45957
- case Tier.Standard:
45958
- return "Standard";
45959
- case Tier.Advanced:
45960
- return "Advanced";
45961
- case Tier.Premium:
45962
- return "Advanced";
45963
- // Premium discontinued, maps to Advanced
45964
- default:
45965
- return "Unknown";
45966
- }
45967
- }
45968
- function getStateDescription(state) {
45969
- switch (state) {
45970
- case AddonState.None:
45971
- return "Not Subscribed";
45972
- case AddonState.Pending:
45973
- return "Pending";
45974
- case AddonState.Subscribed:
45975
- return "Subscribed";
45976
- case AddonState.Error:
45977
- return "Error";
45978
- default:
45979
- return "Unknown";
45980
- }
45981
- }
45982
- function getAccessStatusDescription(status) {
45983
- switch (status) {
45984
- case AccessStatus.Allowed:
45985
- return "Allowed";
45986
- case AccessStatus.Denied:
45987
- return "Denied";
45988
- case AccessStatus.UpgradeRequired:
45989
- return "Upgrade Required";
45990
- case AccessStatus.ContactSales:
45991
- return "Contact Sales";
45992
- case AccessStatus.InternalService:
45993
- return "Internal Service";
45994
- case AccessStatus.Unknown:
45995
- return "Unknown";
45996
- case AccessStatus.EOL:
45997
- return "End of Life";
45998
- default:
45999
- return "Unknown";
46000
- }
46001
- }
46002
- function getQuotaStatusFromPercentage(percentage) {
46003
- if (percentage >= 100) {
46004
- return QuotaStatus.Exceeded;
46005
- }
46006
- if (percentage >= 80) {
46007
- return QuotaStatus.Warning;
46008
- }
46009
- return QuotaStatus.OK;
46010
- }
46011
-
46012
- // src/subscription/client.ts
46013
- var SubscriptionClient = class {
46014
- apiClient;
46015
- constructor(apiClient) {
46016
- this.apiClient = apiClient;
46017
- }
46018
- /**
46019
- * Format display name from service name
46020
- * e.g., "client-side-defense" -> "Client Side Defense"
46021
- */
46022
- formatDisplayName(name) {
46023
- return name.split("-").map(
46024
- (word) => word.length > 0 ? word.charAt(0).toUpperCase() + word.slice(1).toLowerCase() : ""
46025
- ).join(" ");
46026
- }
46027
- /**
46028
- * Normalize tier value
46029
- */
46030
- normalizeTier(tier) {
46031
- const normalized = tier.toUpperCase().trim();
46032
- switch (normalized) {
46033
- case "NO_TIER":
46034
- case "NOTIER":
46035
- case "":
46036
- return "NO_TIER";
46037
- case "BASIC":
46038
- return "BASIC";
46039
- case "STANDARD":
46040
- return "STANDARD";
46041
- case "ADVANCED":
46042
- return "ADVANCED";
46043
- case "PREMIUM":
46044
- return "PREMIUM";
46045
- default:
46046
- return tier;
46047
- }
46048
- }
46049
- /**
46050
- * Normalize state value
46051
- */
46052
- normalizeState(state) {
46053
- const normalized = state.toUpperCase().trim();
46054
- switch (normalized) {
46055
- case "AS_NONE":
46056
- case "NONE":
46057
- case "":
46058
- return AddonState.None;
46059
- case "AS_PENDING":
46060
- case "PENDING":
46061
- return AddonState.Pending;
46062
- case "AS_SUBSCRIBED":
46063
- case "SUBSCRIBED":
46064
- return AddonState.Subscribed;
46065
- case "AS_ERROR":
46066
- case "ERROR":
46067
- return AddonState.Error;
46068
- default:
46069
- return state;
46070
- }
46071
- }
46072
- /**
46073
- * Normalize access status value
46074
- */
46075
- normalizeAccessStatus(status) {
46076
- const normalized = status.toUpperCase().trim();
46077
- switch (normalized) {
46078
- case "AS_AC_ALLOWED":
46079
- case "ALLOWED":
46080
- case "":
46081
- return AccessStatus.Allowed;
46082
- case "AS_AC_PBAC_DENY":
46083
- case "DENIED":
46084
- case "DENY":
46085
- return AccessStatus.Denied;
46086
- case "AS_AC_PBAC_DENY_UPGRADE_PLAN":
46087
- case "UPGRADE_REQUIRED":
46088
- case "UPGRADE_PLAN":
46089
- return AccessStatus.UpgradeRequired;
46090
- case "AS_AC_PBAC_DENY_CONTACT_SALES":
46091
- case "CONTACT_SALES":
46092
- return AccessStatus.ContactSales;
46093
- case "AS_AC_PBAC_DENY_INTERNAL_SVC":
46094
- case "INTERNAL_SERVICE":
46095
- return AccessStatus.InternalService;
46096
- default:
46097
- return status;
46098
- }
46099
- }
46100
- /**
46101
- * Determine tier from tenant type
46102
- */
46103
- determineTierFromTenantType(tenantType) {
46104
- switch (tenantType.toUpperCase()) {
46105
- case "ENTERPRISE":
46106
- return "Advanced";
46107
- case "FREEMIUM":
46108
- return "Standard";
46109
- default:
46110
- return "Standard";
46111
- }
46112
- }
46113
- /**
46114
- * Get subscription plans
46115
- */
46116
- async getPlans(namespace = "system") {
46117
- try {
46118
- const path = `/api/web/namespaces/${namespace}/plans`;
46119
- const response = await this.apiClient.get(path);
46120
- const plans = [];
46121
- for (const item of response.data?.items ?? []) {
46122
- if (!item?.metadata?.name) continue;
46123
- const plan = {
46124
- name: item.metadata.name,
46125
- displayName: item.spec?.display_name ?? item.metadata.name,
46126
- includedServices: item.spec?.included_services?.map(
46127
- (s) => s.name ?? ""
46128
- ) ?? [],
46129
- allowedServices: item.spec?.allowed_services?.map((s) => s.name ?? "") ?? []
46130
- };
46131
- if (item.spec?.description) {
46132
- plan.description = item.spec.description;
46133
- }
46134
- plans.push(plan);
46135
- }
46136
- return plans;
46137
- } catch {
46138
- return [];
46139
- }
46140
- }
46141
- /**
46142
- * Get addon service activation status
46143
- */
46144
- async getAddonServiceActivationStatus(addonName) {
46145
- try {
46146
- const path = `/api/web/namespaces/system/addon_services/${addonName}/activation-status`;
46147
- const response = await this.apiClient.get(path);
46148
- return {
46149
- name: addonName,
46150
- displayName: this.formatDisplayName(addonName),
46151
- tier: this.normalizeTier(response.data.tier ?? ""),
46152
- state: this.normalizeState(response.data.state ?? ""),
46153
- accessStatus: this.normalizeAccessStatus(
46154
- response.data.access_status ?? ""
46155
- )
46156
- };
46157
- } catch {
46158
- return null;
46159
- }
46160
- }
46161
- /**
46162
- * Get addon services
46163
- */
46164
- async getAddonServices(namespace = "system") {
46165
- const path = `/api/web/namespaces/${namespace}/addon_services`;
46166
- const response = await this.apiClient.get(path);
46167
- const addons = [];
46168
- for (const item of response.data.items ?? []) {
46169
- let addon = {
46170
- name: item.name,
46171
- displayName: this.formatDisplayName(item.name),
46172
- tier: "",
46173
- state: AddonState.None,
46174
- accessStatus: AccessStatus.Allowed
46175
- };
46176
- if (item.description) {
46177
- addon.description = item.description;
46178
- }
46179
- if (item.namespace) {
46180
- addon.namespace = item.namespace;
46181
- }
46182
- if (item.disabled) {
46183
- addon.state = AddonState.None;
46184
- addon.accessStatus = AccessStatus.Denied;
46185
- }
46186
- const activationStatus = await this.getAddonServiceActivationStatus(
46187
- item.name
46188
- );
46189
- if (activationStatus) {
46190
- addon = {
46191
- ...addon,
46192
- state: activationStatus.state,
46193
- accessStatus: activationStatus.accessStatus,
46194
- tier: activationStatus.tier
46195
- };
46196
- }
46197
- addons.push(addon);
46198
- }
46199
- return addons;
46200
- }
46201
- /**
46202
- * Get quota usage information (tenant-level)
46203
- */
46204
- async getQuotaInfo() {
46205
- const path = "/api/web/namespaces/system/quota/usage";
46206
- const response = await this.apiClient.get(path);
46207
- const objects = [];
46208
- const quotaMap = response.data.objects ?? response.data.quota_usage ?? {};
46209
- for (const [name, entry] of Object.entries(quotaMap)) {
46210
- const limit = entry.limit?.maximum ?? 0;
46211
- const usage = entry.usage?.current ?? 0;
46212
- if (limit < 0 || usage < 0) {
46213
- continue;
46214
- }
46215
- const percentage = limit > 0 ? usage / limit * 100 : 0;
46216
- const quotaItem = {
46217
- name,
46218
- displayName: entry.display_name ?? name,
46219
- objectType: name,
46220
- limit,
46221
- usage,
46222
- percentage,
46223
- status: getQuotaStatusFromPercentage(percentage)
46224
- };
46225
- if (entry.description) {
46226
- quotaItem.description = entry.description;
46227
- }
46228
- objects.push(quotaItem);
46229
- }
46230
- return {
46231
- namespace: "tenant",
46232
- // Quotas are tenant-level
46233
- objects
46234
- };
46235
- }
46236
- /**
46237
- * Get current usage plan
46238
- */
46239
- async getCurrentUsagePlan() {
46240
- const path = "/api/web/namespaces/system/usage_plans/current";
46241
- const response = await this.apiClient.get(path);
46242
- for (const plan of response.data.plans ?? []) {
46243
- if (plan.current) {
46244
- return plan;
46245
- }
46246
- }
46247
- if (response.data.plans && response.data.plans.length > 0) {
46248
- return response.data.plans[0] ?? null;
46249
- }
46250
- return null;
46251
- }
46252
- /**
46253
- * Get subscription tier from current plan
46254
- */
46255
- async getTierFromCurrentPlan() {
46256
- const plan = await this.getCurrentUsagePlan();
46257
- if (!plan) {
46258
- return "Standard";
46259
- }
46260
- return this.determineTierFromTenantType(plan.tenant_type);
46261
- }
46262
- /**
46263
- * Get complete subscription information
46264
- */
46265
- async getSubscriptionInfo() {
46266
- const plans = await this.getPlans("system");
46267
- const addons = await this.getAddonServices("system");
46268
- const quotaInfo = await this.getQuotaInfo();
46269
- let tier;
46270
- try {
46271
- tier = await this.getTierFromCurrentPlan();
46272
- } catch {
46273
- tier = this.determineTierFromPlansAndAddons(plans, addons);
46274
- }
46275
- const activeAddons = addons.filter(isAddonActive);
46276
- const availableAddons = addons.filter(isAddonAvailable);
46277
- let atRisk = 0;
46278
- let exceeded = 0;
46279
- for (const q of quotaInfo.objects) {
46280
- if (isQuotaExceeded(q)) {
46281
- exceeded++;
46282
- } else if (isQuotaAtRisk(q)) {
46283
- atRisk++;
46284
- }
46285
- }
46286
- const quotaSummary = {
46287
- totalLimits: quotaInfo.objects.length,
46288
- limitsAtRisk: atRisk,
46289
- limitsExceeded: exceeded,
46290
- objects: quotaInfo.objects
46291
- };
46292
- const info = {
46293
- tier,
46294
- activeAddons,
46295
- availableAddons,
46296
- quotaSummary,
46297
- plan: plans[0] ?? {
46298
- name: "unknown",
46299
- displayName: "Unknown"
46300
- }
46301
- };
46302
- return info;
46303
- }
46304
- /**
46305
- * Determine tier from plans and addons (legacy fallback)
46306
- */
46307
- determineTierFromPlansAndAddons(plans, addons) {
46308
- for (const plan of plans) {
46309
- const nameLower = plan.name.toLowerCase();
46310
- const displayLower = plan.displayName.toLowerCase();
46311
- if (nameLower.includes("advanced") || displayLower.includes("advanced")) {
46312
- return "Advanced";
46313
- }
46314
- if (nameLower.includes("enterprise") || displayLower.includes("enterprise")) {
46315
- return "Advanced";
46316
- }
46317
- }
46318
- for (const addon of addons) {
46319
- if (isAddonActive(addon) && (addon.tier === "ADVANCED" || addon.tier === "PREMIUM")) {
46320
- return "Advanced";
46321
- }
46322
- }
46323
- return "Standard";
46324
- }
46325
- /**
46326
- * Validate resource deployment
46327
- */
46328
- async validateResource(req) {
46329
- const result = {
46330
- valid: true,
46331
- checks: [],
46332
- warnings: [],
46333
- errors: []
46334
- };
46335
- if (req.resourceType && req.count && req.count > 0) {
46336
- const quotaInfo = await this.getQuotaInfo();
46337
- let found = false;
46338
- for (const q of quotaInfo.objects) {
46339
- if (q.objectType?.toLowerCase() === req.resourceType.toLowerCase() || q.name.toLowerCase() === req.resourceType.toLowerCase()) {
46340
- found = true;
46341
- const remaining = getQuotaRemainingCapacity(q);
46342
- const check = {
46343
- type: "quota",
46344
- resource: req.resourceType,
46345
- current: q.usage,
46346
- requested: req.count,
46347
- limit: q.limit,
46348
- result: ValidationStatus.Pass
46349
- };
46350
- if (req.count > remaining) {
46351
- check.result = ValidationStatus.Fail;
46352
- check.message = `Quota exceeded: ${req.resourceType} would have ${Math.floor(q.usage + req.count)}/${q.limit} (requesting ${req.count}, only ${Math.floor(remaining)} available)`;
46353
- result.valid = false;
46354
- result.errors?.push(check.message);
46355
- } else if ((q.usage + req.count) / q.limit >= 0.8) {
46356
- check.result = ValidationStatus.Warning;
46357
- check.message = `Quota warning: ${req.resourceType} will be at ${Math.round((q.usage + req.count) / q.limit * 100)}% after deployment`;
46358
- result.warnings?.push(check.message);
46359
- } else {
46360
- check.message = `Quota OK: ${req.resourceType} has sufficient capacity (${Math.floor(remaining)} available)`;
46361
- }
46362
- result.checks.push(check);
46363
- break;
46364
- }
46365
- }
46366
- if (!found) {
46367
- result.checks.push({
46368
- type: "quota",
46369
- resource: req.resourceType,
46370
- requested: req.count,
46371
- result: ValidationStatus.Warning,
46372
- message: `No quota limit found for resource type: ${req.resourceType}`
46373
- });
46374
- }
46375
- }
46376
- if (req.feature) {
46377
- const addons = await this.getAddonServices("system");
46378
- let found = false;
46379
- for (const addon of addons) {
46380
- if (addon.name.toLowerCase() === req.feature.toLowerCase()) {
46381
- found = true;
46382
- const check = {
46383
- type: "feature",
46384
- feature: req.feature,
46385
- currentTier: addon.tier,
46386
- status: getStateDescription(addon.state),
46387
- result: ValidationStatus.Pass
46388
- };
46389
- if (isAddonActive(addon)) {
46390
- check.message = `Feature '${req.feature}' is active (tier: ${addon.tier})`;
46391
- } else if (addon.accessStatus === AccessStatus.UpgradeRequired) {
46392
- check.result = ValidationStatus.Fail;
46393
- check.message = `Feature '${req.feature}' requires a plan upgrade`;
46394
- result.valid = false;
46395
- result.errors?.push(check.message);
46396
- } else if (addon.accessStatus === AccessStatus.ContactSales) {
46397
- check.result = ValidationStatus.Fail;
46398
- check.message = `Feature '${req.feature}' requires contacting F5 sales`;
46399
- result.valid = false;
46400
- result.errors?.push(check.message);
46401
- } else if (isAddonAvailable(addon)) {
46402
- check.result = ValidationStatus.Warning;
46403
- check.message = `Feature '${req.feature}' is available but not subscribed`;
46404
- result.warnings?.push(check.message);
46405
- } else {
46406
- check.result = ValidationStatus.Fail;
46407
- check.message = `Feature '${req.feature}' is not available (access: ${getAccessStatusDescription(addon.accessStatus)})`;
46408
- result.valid = false;
46409
- result.errors?.push(check.message);
46410
- }
46411
- result.checks.push(check);
46412
- break;
46413
- }
46414
- }
46415
- if (!found) {
46416
- result.checks.push({
46417
- type: "feature",
46418
- feature: req.feature,
46419
- result: ValidationStatus.Warning,
46420
- message: `Feature '${req.feature}' not found in addon services`
46421
- });
46422
- }
46423
- }
46424
- return result;
46425
- }
46426
- /**
46427
- * Filter addons by criteria
46428
- */
46429
- filterAddons(addons, filter) {
46430
- if (!filter) {
46431
- return addons;
46432
- }
46433
- switch (filter.toLowerCase()) {
46434
- case "active":
46435
- return addons.filter(isAddonActive);
46436
- case "available":
46437
- return addons.filter(isAddonAvailable);
46438
- case "denied":
46439
- return addons.filter(
46440
- (a) => a.accessStatus === AccessStatus.Denied || a.accessStatus === AccessStatus.UpgradeRequired || a.accessStatus === AccessStatus.ContactSales || a.accessStatus === AccessStatus.InternalService
46441
- );
46442
- default:
46443
- return addons;
46444
- }
46445
- }
46446
- /**
46447
- * Get pending activation requests
46448
- */
46449
- async getPendingActivations(namespace = "system") {
46450
- const addons = await this.getAddonServices(namespace);
46451
- const result = {
46452
- pendingActivations: [],
46453
- activeAddons: [],
46454
- totalPending: 0
46455
- };
46456
- for (const addon of addons) {
46457
- if (addon.state === AddonState.Pending) {
46458
- const pending = {
46459
- addonService: addon.name,
46460
- subscriptionState: SubscriptionState.Pending,
46461
- message: this.getActivationMessage(
46462
- addon.activationType ?? ""
46463
- )
46464
- };
46465
- if (addon.namespace) {
46466
- pending.namespace = addon.namespace;
46467
- }
46468
- if (addon.activationType) {
46469
- pending.activationType = addon.activationType;
46470
- }
46471
- result.pendingActivations.push(pending);
46472
- }
46473
- if (isAddonActive(addon)) {
46474
- result.activeAddons.push(addon.name);
46475
- }
46476
- }
46477
- result.totalPending = result.pendingActivations.length;
46478
- return result;
46479
- }
46480
- /**
46481
- * Get activation message based on type
46482
- */
46483
- getActivationMessage(activationType) {
46484
- switch (activationType) {
46485
- case ActivationType.Self:
46486
- return "Self-activation in progress";
46487
- case ActivationType.PartiallyManaged:
46488
- return "Awaiting partial backend processing";
46489
- case ActivationType.Managed:
46490
- return "Awaiting SRE approval";
46491
- default:
46492
- return "Activation pending";
46493
- }
46494
- }
46495
- };
46496
-
46497
45978
  // src/repl/session.ts
45979
+ var NAMESPACE_CACHE_TTL = 5 * 60 * 1e3;
46498
45980
  var REPLSession = class {
46499
45981
  _history = null;
46500
45982
  _namespace;
@@ -46511,7 +45993,8 @@ var REPLSession = class {
46511
45993
  _profileManager;
46512
45994
  _activeProfile = null;
46513
45995
  _activeProfileName = null;
46514
- _tier = "";
45996
+ _namespaceCache = [];
45997
+ _namespaceCacheTime = 0;
46515
45998
  constructor(config = {}) {
46516
45999
  this._namespace = config.namespace ?? this.getDefaultNamespace();
46517
46000
  this._contextPath = new ContextPath();
@@ -46548,18 +46031,6 @@ var REPLSession = class {
46548
46031
  await this.loadActiveProfile();
46549
46032
  if (this._apiClient?.isAuthenticated()) {
46550
46033
  await this.fetchUserInfo();
46551
- await this.fetchTier();
46552
- }
46553
- }
46554
- /**
46555
- * Fetch subscription tier from the API
46556
- */
46557
- async fetchTier() {
46558
- if (!this._apiClient) return;
46559
- try {
46560
- const subscriptionClient = new SubscriptionClient(this._apiClient);
46561
- this._tier = await subscriptionClient.getTierFromCurrentPlan();
46562
- } catch {
46563
46034
  }
46564
46035
  }
46565
46036
  /**
@@ -46577,6 +46048,7 @@ var REPLSession = class {
46577
46048
  }
46578
46049
  /**
46579
46050
  * Load the active profile from profile manager
46051
+ * Note: Environment variables take priority over profile settings
46580
46052
  */
46581
46053
  async loadActiveProfile() {
46582
46054
  try {
@@ -46586,14 +46058,17 @@ var REPLSession = class {
46586
46058
  if (profile) {
46587
46059
  this._activeProfileName = activeName;
46588
46060
  this._activeProfile = profile;
46589
- if (profile.apiUrl) {
46061
+ const envUrl = process.env[`${ENV_PREFIX}_API_URL`];
46062
+ const envToken = process.env[`${ENV_PREFIX}_API_TOKEN`];
46063
+ const envNamespace = process.env[`${ENV_PREFIX}_NAMESPACE`];
46064
+ if (!envUrl && profile.apiUrl) {
46590
46065
  this._serverUrl = profile.apiUrl;
46591
46066
  this._tenant = this.extractTenant(profile.apiUrl);
46592
46067
  }
46593
- if (profile.apiToken) {
46068
+ if (!envToken && profile.apiToken) {
46594
46069
  this._apiToken = profile.apiToken;
46595
46070
  }
46596
- if (profile.defaultNamespace) {
46071
+ if (!envNamespace && profile.defaultNamespace) {
46597
46072
  this._namespace = profile.defaultNamespace;
46598
46073
  }
46599
46074
  if (this._serverUrl) {
@@ -46678,12 +46153,6 @@ var REPLSession = class {
46678
46153
  setUsername(username) {
46679
46154
  this._username = username;
46680
46155
  }
46681
- /**
46682
- * Get the subscription tier (Standard/Advanced)
46683
- */
46684
- getTier() {
46685
- return this._tier;
46686
- }
46687
46156
  /**
46688
46157
  * Get the context validator
46689
46158
  */
@@ -46768,6 +46237,7 @@ var REPLSession = class {
46768
46237
  if (!result.success) {
46769
46238
  return false;
46770
46239
  }
46240
+ this.clearNamespaceCache();
46771
46241
  this._activeProfileName = profileName;
46772
46242
  this._activeProfile = profile;
46773
46243
  if (profile.apiUrl) {
@@ -46798,6 +46268,30 @@ var REPLSession = class {
46798
46268
  this._activeProfileName = null;
46799
46269
  this._activeProfile = null;
46800
46270
  }
46271
+ /**
46272
+ * Get cached namespaces (returns empty array if cache is stale/empty)
46273
+ */
46274
+ getNamespaceCache() {
46275
+ const now = Date.now();
46276
+ if (this._namespaceCache.length > 0 && now - this._namespaceCacheTime < NAMESPACE_CACHE_TTL) {
46277
+ return this._namespaceCache;
46278
+ }
46279
+ return [];
46280
+ }
46281
+ /**
46282
+ * Set namespace cache
46283
+ */
46284
+ setNamespaceCache(namespaces) {
46285
+ this._namespaceCache = namespaces;
46286
+ this._namespaceCacheTime = Date.now();
46287
+ }
46288
+ /**
46289
+ * Clear namespace cache (called when switching profiles)
46290
+ */
46291
+ clearNamespaceCache() {
46292
+ this._namespaceCache = [];
46293
+ this._namespaceCacheTime = 0;
46294
+ }
46801
46295
  /**
46802
46296
  * Add a command to history
46803
46297
  */
@@ -47705,10 +47199,11 @@ var createCommand2 = {
47705
47199
  if (!apiToken) {
47706
47200
  return errorResult("Missing required --token option");
47707
47201
  }
47202
+ const sanitizedToken = apiToken.replace(/^["']|["']$/g, "");
47708
47203
  const profile = {
47709
47204
  name,
47710
47205
  apiUrl,
47711
- apiToken
47206
+ apiToken: sanitizedToken
47712
47207
  };
47713
47208
  if (defaultNamespace) {
47714
47209
  profile.defaultNamespace = defaultNamespace;
@@ -47734,22 +47229,16 @@ var useCommand = {
47734
47229
  usage: "<name>",
47735
47230
  aliases: ["switch", "activate"],
47736
47231
  async execute(args, session) {
47737
- const manager = getProfileManager();
47738
47232
  const name = args[0];
47739
47233
  if (!name) {
47740
47234
  return errorResult("Usage: login profile use <name>");
47741
47235
  }
47742
47236
  try {
47743
- const result = await manager.setActive(name);
47744
- if (!result.success) {
47745
- return errorResult(result.message);
47746
- }
47747
- const profile = await manager.get(name);
47748
- if (profile) {
47749
- if (profile.defaultNamespace) {
47750
- session.setNamespace(profile.defaultNamespace);
47751
- }
47237
+ const success = await session.switchProfile(name);
47238
+ if (!success) {
47239
+ return errorResult(`Profile '${name}' not found.`);
47752
47240
  }
47241
+ const profile = session.getActiveProfile();
47753
47242
  return successResult(
47754
47243
  [
47755
47244
  `Switched to profile '${name}'.`,
@@ -47807,6 +47296,7 @@ var deleteCommand = {
47807
47296
  ].join("\n")
47808
47297
  );
47809
47298
  }
47299
+ await manager.clearActive();
47810
47300
  }
47811
47301
  const result = await manager.delete(name);
47812
47302
  if (!result.success) {
@@ -47939,12 +47429,19 @@ var setCommand = {
47939
47429
  return successResult(lines, true);
47940
47430
  },
47941
47431
  async completion(partial, _args, session) {
47432
+ const cached = session.getNamespaceCache();
47433
+ if (cached.length > 0) {
47434
+ return cached.filter(
47435
+ (ns) => ns.toLowerCase().startsWith(partial.toLowerCase())
47436
+ );
47437
+ }
47942
47438
  const client = session.getAPIClient();
47943
47439
  if (client?.isAuthenticated()) {
47944
47440
  try {
47945
47441
  const response = await client.get("/api/web/namespaces");
47946
47442
  if (response.ok && response.data?.items) {
47947
- const namespaces = response.data.items.map((item) => item.name).filter((name) => !!name);
47443
+ const namespaces = response.data.items.map((item) => item.name).filter((name) => !!name).sort();
47444
+ session.setNamespaceCache(namespaces);
47948
47445
  return namespaces.filter(
47949
47446
  (ns) => ns.toLowerCase().startsWith(partial.toLowerCase())
47950
47447
  );
@@ -47982,6 +47479,7 @@ var listCommand2 = {
47982
47479
  return errorResult("Failed to fetch namespaces from API.");
47983
47480
  }
47984
47481
  const namespaces = response.data.items.map((item) => item.name).filter((name) => !!name).sort();
47482
+ session.setNamespaceCache(namespaces);
47985
47483
  const lines = ["Available namespaces:", ""];
47986
47484
  for (const ns of namespaces) {
47987
47485
  if (ns === currentNamespace) {
@@ -48035,11 +47533,6 @@ var EnvVarRegistry = [
48035
47533
  description: "Output format (json, yaml, table)",
48036
47534
  relatedFlag: "-o"
48037
47535
  },
48038
- {
48039
- name: `${ENV_PREFIX}_SUBSCRIPTION_TIER`,
48040
- description: "Subscription tier for feature validation",
48041
- relatedFlag: ""
48042
- },
48043
47536
  {
48044
47537
  name: `${ENV_PREFIX}_LOGO`,
48045
47538
  description: "Logo display mode (auto, image, ascii, both, none)",
@@ -48488,25 +47981,8 @@ var bannerCommand = {
48488
47981
  }
48489
47982
  };
48490
47983
 
48491
- // src/domains/login/whoami/types.ts
48492
- function toDisplayTier(tier) {
48493
- const normalized = tier.toUpperCase();
48494
- switch (normalized) {
48495
- case "STANDARD":
48496
- case "BASIC":
48497
- return "Standard";
48498
- case "ADVANCED":
48499
- case "PREMIUM":
48500
- // Legacy mapping
48501
- case "ENTERPRISE":
48502
- return "Advanced";
48503
- default:
48504
- return void 0;
48505
- }
48506
- }
48507
-
48508
47984
  // src/domains/login/whoami/service.ts
48509
- async function getWhoamiInfo(session, options = {}) {
47985
+ async function getWhoamiInfo(session, _options = {}) {
48510
47986
  const info = {
48511
47987
  serverUrl: session.getServerUrl(),
48512
47988
  namespace: session.getNamespace(),
@@ -48527,32 +48003,6 @@ async function getWhoamiInfo(session, options = {}) {
48527
48003
  info.username = username;
48528
48004
  }
48529
48005
  }
48530
- const apiClient = session.getAPIClient();
48531
- if (apiClient) {
48532
- const subscriptionClient = new SubscriptionClient(apiClient);
48533
- try {
48534
- const tierValue = await subscriptionClient.getTierFromCurrentPlan();
48535
- const displayTier = toDisplayTier(tierValue);
48536
- if (displayTier) {
48537
- info.tier = displayTier;
48538
- }
48539
- } catch {
48540
- }
48541
- if (options.includeQuotas || options.verbose) {
48542
- try {
48543
- const subscriptionInfo = await subscriptionClient.getSubscriptionInfo();
48544
- info.quotas = subscriptionInfo.quotaSummary;
48545
- } catch {
48546
- }
48547
- }
48548
- if (options.includeAddons || options.verbose) {
48549
- try {
48550
- const addons = await subscriptionClient.getAddonServices();
48551
- info.addons = addons.filter(isAddonActive);
48552
- } catch {
48553
- }
48554
- }
48555
- }
48556
48006
  return info;
48557
48007
  }
48558
48008
 
@@ -48564,41 +48014,19 @@ function formatWhoami(info, options = {}) {
48564
48014
  if (options.json) {
48565
48015
  return formatWhoamiJson(info);
48566
48016
  }
48567
- return formatWhoamiBox(info, options);
48017
+ return formatWhoamiBox(info);
48568
48018
  }
48569
48019
  function formatWhoamiJson(info) {
48570
48020
  const output = {};
48571
48021
  if (info.tenant) output.tenant = info.tenant;
48572
48022
  if (info.username) output.username = info.username;
48573
48023
  if (info.email) output.email = info.email;
48574
- if (info.tier) output.tier = info.tier;
48575
48024
  output.namespace = info.namespace;
48576
48025
  output.serverUrl = info.serverUrl;
48577
48026
  output.isAuthenticated = info.isAuthenticated;
48578
- if (info.quotas) {
48579
- output.quotas = {
48580
- totalLimits: info.quotas.totalLimits,
48581
- limitsAtRisk: info.quotas.limitsAtRisk,
48582
- limitsExceeded: info.quotas.limitsExceeded,
48583
- objects: info.quotas.objects?.map((q) => ({
48584
- name: q.name,
48585
- displayName: q.displayName,
48586
- usage: q.usage,
48587
- limit: q.limit,
48588
- percentage: Math.round(q.percentage)
48589
- }))
48590
- };
48591
- }
48592
- if (info.addons && info.addons.length > 0) {
48593
- output.addons = info.addons.map((a) => ({
48594
- name: a.name,
48595
- displayName: a.displayName,
48596
- state: a.state
48597
- }));
48598
- }
48599
48027
  return [JSON.stringify(output, null, 2)];
48600
48028
  }
48601
- function formatWhoamiBox(info, options) {
48029
+ function formatWhoamiBox(info) {
48602
48030
  const lines = [];
48603
48031
  const red = colors.red;
48604
48032
  const reset = colors.reset;
@@ -48608,9 +48036,7 @@ function formatWhoamiBox(info, options) {
48608
48036
  bottomLeft: "\u2570",
48609
48037
  bottomRight: "\u256F",
48610
48038
  horizontal: "\u2500",
48611
- vertical: "\u2502",
48612
- leftT: "\u251C",
48613
- rightT: "\u2524"
48039
+ vertical: "\u2502"
48614
48040
  };
48615
48041
  const contentLines = [];
48616
48042
  if (info.tenant) {
@@ -48621,46 +48047,19 @@ function formatWhoamiBox(info, options) {
48621
48047
  } else if (info.username) {
48622
48048
  contentLines.push({ label: "User", value: info.username });
48623
48049
  }
48624
- if (info.tier) {
48625
- contentLines.push({ label: "Tier", value: info.tier });
48626
- }
48627
48050
  contentLines.push({ label: "Namespace", value: info.namespace });
48628
48051
  contentLines.push({ label: "Server", value: info.serverUrl });
48629
48052
  contentLines.push({
48630
48053
  label: "Auth",
48631
48054
  value: info.isAuthenticated ? "\u2713 Authenticated" : "Not authenticated"
48632
48055
  });
48633
- const quotaLines = [];
48634
- if (info.quotas && info.quotas.objects && (options.includeQuotas || options.verbose)) {
48635
- for (const quota of info.quotas.objects.slice(0, 10)) {
48636
- const pct = Math.round(quota.percentage);
48637
- quotaLines.push(
48638
- `${quota.displayName}: ${quota.usage}/${quota.limit} (${pct}%)`
48639
- );
48640
- }
48641
- if (info.quotas.objects.length > 10) {
48642
- const remaining = info.quotas.objects.length - 10;
48643
- quotaLines.push(`... and ${remaining} more`);
48644
- }
48645
- }
48646
- const addonLines = [];
48647
- if (info.addons && info.addons.length > 0 && (options.includeAddons || options.verbose)) {
48648
- for (const addon of info.addons) {
48649
- addonLines.push(`\u2713 ${addon.displayName}`);
48650
- }
48651
- }
48652
48056
  const maxLabelWidth = Math.max(...contentLines.map((c) => c.label.length));
48653
48057
  const formattedContent = contentLines.map((c) => {
48654
48058
  const paddedLabel = c.label.padEnd(maxLabelWidth);
48655
48059
  return `${paddedLabel}: ${c.value}`;
48656
48060
  });
48657
- const headerTitles = ["Connection Info", "Quota Usage", "Active Addons"];
48658
- const allTextLines = [
48659
- ...formattedContent,
48660
- ...quotaLines,
48661
- ...addonLines,
48662
- ...headerTitles
48663
- ];
48061
+ const headerTitle = "Connection Info";
48062
+ const allTextLines = [...formattedContent, headerTitle];
48664
48063
  const maxContentWidth = Math.max(...allTextLines.map((l) => l.length));
48665
48064
  const innerWidth = maxContentWidth + 2;
48666
48065
  const title = " Connection Info ";
@@ -48676,36 +48075,6 @@ function formatWhoamiBox(info, options) {
48676
48075
  `${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
48677
48076
  );
48678
48077
  }
48679
- if (quotaLines.length > 0) {
48680
- const quotaTitle = " Quota Usage ";
48681
- const quotaRemaining = innerWidth - quotaTitle.length;
48682
- const quotaLeft = 1;
48683
- const quotaRight = Math.max(0, quotaRemaining - quotaLeft);
48684
- lines.push(
48685
- `${red}${BOX.leftT}${BOX.horizontal.repeat(quotaLeft)}${reset}${quotaTitle}${red}${BOX.horizontal.repeat(quotaRight)}${BOX.rightT}${reset}`
48686
- );
48687
- for (const text of quotaLines) {
48688
- const padding = innerWidth - text.length;
48689
- lines.push(
48690
- `${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
48691
- );
48692
- }
48693
- }
48694
- if (addonLines.length > 0) {
48695
- const addonTitle = " Active Addons ";
48696
- const addonRemaining = innerWidth - addonTitle.length;
48697
- const addonLeft = 1;
48698
- const addonRight = Math.max(0, addonRemaining - addonLeft);
48699
- lines.push(
48700
- `${red}${BOX.leftT}${BOX.horizontal.repeat(addonLeft)}${reset}${addonTitle}${red}${BOX.horizontal.repeat(addonRight)}${BOX.rightT}${reset}`
48701
- );
48702
- for (const text of addonLines) {
48703
- const padding = innerWidth - text.length;
48704
- lines.push(
48705
- `${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
48706
- );
48707
- }
48708
- }
48709
48078
  lines.push(
48710
48079
  `${red}${BOX.bottomLeft}${BOX.horizontal.repeat(innerWidth)}${BOX.bottomRight}${reset}`
48711
48080
  );
@@ -49443,727 +48812,297 @@ var cloudstatusDomain = {
49443
48812
  };
49444
48813
  var cloudstatusAliases = ["cs", "status"];
49445
48814
 
49446
- // src/extensions/types.ts
49447
- var RESERVED_API_ACTIONS = /* @__PURE__ */ new Set([
49448
- "list",
49449
- "get",
49450
- "create",
49451
- "delete",
49452
- "replace",
49453
- "apply",
49454
- "status",
49455
- "patch",
49456
- "add-labels",
49457
- "remove-labels"
49458
- ]);
49459
- function isReservedAction(name) {
49460
- return RESERVED_API_ACTIONS.has(name.toLowerCase());
49461
- }
49462
- function validateExtension(extension) {
49463
- const conflicts = [];
49464
- for (const [name] of extension.commands) {
49465
- if (isReservedAction(name)) {
49466
- conflicts.push(name);
48815
+ // src/completion/registry.ts
48816
+ var CompletionRegistry = class {
48817
+ tree = /* @__PURE__ */ new Map();
48818
+ aliases = /* @__PURE__ */ new Map();
48819
+ /**
48820
+ * Register a domain with its completion subtree
48821
+ * Later registrations for same name take precedence (custom > api)
48822
+ */
48823
+ registerDomain(node) {
48824
+ this.tree.set(node.name, node);
48825
+ if (node.aliases) {
48826
+ for (const alias of node.aliases) {
48827
+ this.aliases.set(alias, node.name);
48828
+ }
49467
48829
  }
49468
48830
  }
49469
- if (conflicts.length > 0) {
49470
- throw new Error(
49471
- `Extension "${extension.targetDomain}" has commands that conflict with API actions: ${conflicts.join(", ")}. Use unique names or submit a feature request to upstream.`
49472
- );
49473
- }
49474
- }
49475
-
49476
- // src/extensions/registry.ts
49477
- var ExtensionRegistry = class {
49478
- extensions = /* @__PURE__ */ new Map();
49479
- mergedCache = /* @__PURE__ */ new Map();
49480
48831
  /**
49481
- * Register a domain extension
49482
- *
49483
- * @param extension - Extension definition to register
49484
- * @throws Error if extension has command name conflicts with API actions
48832
+ * Add or merge children into an existing domain
48833
+ * Used for extensions that augment API domains
49485
48834
  */
49486
- register(extension) {
49487
- validateExtension(extension);
49488
- this.extensions.set(extension.targetDomain, extension);
49489
- this.mergedCache.delete(extension.targetDomain);
48835
+ mergeChildren(domainName, children) {
48836
+ const existing = this.tree.get(domainName);
48837
+ if (!existing) {
48838
+ return;
48839
+ }
48840
+ if (!existing.children) {
48841
+ existing.children = /* @__PURE__ */ new Map();
48842
+ }
48843
+ for (const [name, node] of children) {
48844
+ existing.children.set(name, node);
48845
+ }
49490
48846
  }
49491
48847
  /**
49492
- * Get extension for a domain
49493
- *
49494
- * @param domain - Canonical domain name
49495
- * @returns Extension if registered, undefined otherwise
48848
+ * Get the complete completion tree
49496
48849
  */
49497
- getExtension(domain) {
49498
- return this.extensions.get(domain);
48850
+ getTree() {
48851
+ return this.tree;
49499
48852
  }
49500
48853
  /**
49501
- * Check if a domain has an extension
48854
+ * Get all registered domains as array (sorted by name)
49502
48855
  */
49503
- hasExtension(domain) {
49504
- return this.extensions.has(domain);
48856
+ getDomains() {
48857
+ return Array.from(this.tree.values()).sort(
48858
+ (a, b) => a.name.localeCompare(b.name)
48859
+ );
49505
48860
  }
49506
48861
  /**
49507
- * Get merged domain view combining API domain + extension
49508
- *
49509
- * Resolution priority:
49510
- * 1. Check if canonical domain exists in generated domains
49511
- * 2. Check if extension exists for this domain
49512
- * 3. Merge if both exist, or return standalone if only one
49513
- *
49514
- * @param domain - Canonical domain name or alias
49515
- * @returns Merged domain view or undefined if neither exists
48862
+ * Resolve alias to canonical name
49516
48863
  */
49517
- getMergedDomain(domain) {
49518
- const canonical = aliasRegistry.get(domain) ?? domain;
49519
- const cached = this.mergedCache.get(canonical);
49520
- if (cached) {
49521
- return cached;
49522
- }
49523
- const domainInfo = domainRegistry.get(canonical);
49524
- const extension = this.extensions.get(canonical);
49525
- if (!domainInfo && !extension) {
49526
- return void 0;
49527
- }
49528
- const merged = this.buildMergedDomain(canonical, domainInfo, extension);
49529
- this.mergedCache.set(canonical, merged);
49530
- return merged;
48864
+ resolveAlias(nameOrAlias) {
48865
+ return this.aliases.get(nameOrAlias) ?? nameOrAlias;
49531
48866
  }
49532
48867
  /**
49533
- * Build a merged domain from API domain info and/or extension
48868
+ * Get a domain by name or alias
49534
48869
  */
49535
- buildMergedDomain(name, domainInfo, extension) {
49536
- const hasGeneratedDomain = !!domainInfo;
49537
- const hasExtension = !!extension;
49538
- let source;
49539
- if (hasGeneratedDomain && hasExtension) {
49540
- source = "merged";
49541
- } else if (hasGeneratedDomain) {
49542
- source = "generated";
49543
- } else {
49544
- source = "extension";
49545
- }
49546
- const displayName = domainInfo?.displayName ?? this.toDisplayName(extension?.targetDomain ?? name);
49547
- const description = domainInfo?.description ?? extension?.description ?? `Commands for ${displayName}`;
49548
- const merged = {
49549
- name,
49550
- displayName,
49551
- description,
49552
- source,
49553
- hasGeneratedDomain,
49554
- hasExtension,
49555
- extensionCommands: extension?.commands ?? /* @__PURE__ */ new Map(),
49556
- extensionSubcommands: extension?.subcommands ?? /* @__PURE__ */ new Map()
49557
- };
49558
- if (domainInfo) {
49559
- merged.metadata = domainInfo;
49560
- }
49561
- return merged;
48870
+ get(nameOrAlias) {
48871
+ const canonical = this.resolveAlias(nameOrAlias);
48872
+ return this.tree.get(canonical);
49562
48873
  }
49563
48874
  /**
49564
- * Convert snake_case domain name to display name
48875
+ * Check if a domain exists (by name or alias)
49565
48876
  */
49566
- toDisplayName(name) {
49567
- return name.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
48877
+ has(nameOrAlias) {
48878
+ const canonical = this.resolveAlias(nameOrAlias);
48879
+ return this.tree.has(canonical);
49568
48880
  }
49569
48881
  /**
49570
- * Get all domains with extensions (for iteration)
48882
+ * Get all aliases
49571
48883
  */
49572
- getExtendedDomains() {
49573
- return Array.from(this.extensions.keys());
48884
+ getAliases() {
48885
+ return new Map(this.aliases);
49574
48886
  }
49575
48887
  /**
49576
- * Get all merged domains (generated + extended)
49577
- *
49578
- * Returns domains that either:
49579
- * - Have generated API commands (from upstream)
49580
- * - Have extension commands (xcsh-specific)
49581
- * - Have both (merged)
48888
+ * Get domain suggestions for completion
49582
48889
  */
49583
- getAllMergedDomains() {
49584
- const domains = /* @__PURE__ */ new Set();
49585
- for (const name of domainRegistry.keys()) {
49586
- domains.add(name);
48890
+ getDomainSuggestions(prefix = "") {
48891
+ const suggestions = [];
48892
+ const lowerPrefix = prefix.toLowerCase();
48893
+ for (const node of this.tree.values()) {
48894
+ if (node.hidden) continue;
48895
+ if (!prefix || node.name.toLowerCase().startsWith(lowerPrefix)) {
48896
+ suggestions.push({
48897
+ text: node.name,
48898
+ description: node.description,
48899
+ category: "domain"
48900
+ });
48901
+ }
48902
+ if (node.aliases) {
48903
+ for (const alias of node.aliases) {
48904
+ if (!prefix || alias.toLowerCase().startsWith(lowerPrefix)) {
48905
+ suggestions.push({
48906
+ text: alias,
48907
+ description: `Alias for ${node.name}`,
48908
+ category: "domain"
48909
+ });
48910
+ }
48911
+ }
48912
+ }
49587
48913
  }
49588
- for (const name of this.extensions.keys()) {
49589
- domains.add(name);
48914
+ return suggestions.sort((a, b) => a.text.localeCompare(b.text));
48915
+ }
48916
+ /**
48917
+ * Get child suggestions for a domain
48918
+ */
48919
+ getChildSuggestions(domainName, prefix = "") {
48920
+ const node = this.get(domainName);
48921
+ if (!node?.children) {
48922
+ return [];
49590
48923
  }
49591
- const merged = [];
49592
- for (const name of domains) {
49593
- const domain = this.getMergedDomain(name);
49594
- if (domain) {
49595
- merged.push(domain);
48924
+ const suggestions = [];
48925
+ const lowerPrefix = prefix.toLowerCase();
48926
+ for (const child of node.children.values()) {
48927
+ if (child.hidden) continue;
48928
+ if (!prefix || child.name.toLowerCase().startsWith(lowerPrefix)) {
48929
+ suggestions.push({
48930
+ text: child.name,
48931
+ description: child.description,
48932
+ category: child.children ? "subcommand" : "action"
48933
+ });
49596
48934
  }
49597
48935
  }
49598
- return merged.sort((a, b) => a.name.localeCompare(b.name));
48936
+ return suggestions.sort((a, b) => a.text.localeCompare(b.text));
49599
48937
  }
49600
48938
  /**
49601
- * Get extension command by name
49602
- *
49603
- * @param domain - Canonical domain name
49604
- * @param command - Command name
49605
- * @returns Command definition if found
48939
+ * Get nested child suggestions (e.g., login profile <TAB>)
49606
48940
  */
49607
- getExtensionCommand(domain, command) {
49608
- const extension = this.getExtension(domain);
49609
- if (!extension) {
49610
- return void 0;
48941
+ getNestedChildSuggestions(domainName, path, prefix = "") {
48942
+ let node = this.get(domainName);
48943
+ if (!node) {
48944
+ return [];
49611
48945
  }
49612
- const cmd = extension.commands.get(command);
49613
- if (cmd) {
49614
- return cmd;
48946
+ for (const segment of path) {
48947
+ if (!node.children) {
48948
+ return [];
48949
+ }
48950
+ const child = node.children.get(segment);
48951
+ if (!child) {
48952
+ return [];
48953
+ }
48954
+ node = child;
49615
48955
  }
49616
- for (const [, cmdDef] of extension.commands) {
49617
- if (cmdDef.aliases?.includes(command)) {
49618
- return cmdDef;
48956
+ if (!node.children) {
48957
+ return [];
48958
+ }
48959
+ const suggestions = [];
48960
+ const lowerPrefix = prefix.toLowerCase();
48961
+ for (const child of node.children.values()) {
48962
+ if (child.hidden) continue;
48963
+ if (!prefix || child.name.toLowerCase().startsWith(lowerPrefix)) {
48964
+ suggestions.push({
48965
+ text: child.name,
48966
+ description: child.description,
48967
+ category: "command"
48968
+ });
49619
48969
  }
49620
48970
  }
49621
- return void 0;
48971
+ return suggestions.sort((a, b) => a.text.localeCompare(b.text));
49622
48972
  }
49623
48973
  /**
49624
- * Get extension subcommand group
49625
- *
49626
- * @param domain - Canonical domain name
49627
- * @param subcommand - Subcommand group name
49628
- * @returns Subcommand group if found
48974
+ * Clear the registry (useful for testing)
49629
48975
  */
49630
- getExtensionSubcommand(domain, subcommand) {
49631
- const extension = this.getExtension(domain);
49632
- return extension?.subcommands?.get(subcommand);
48976
+ clear() {
48977
+ this.tree.clear();
48978
+ this.aliases.clear();
49633
48979
  }
49634
- /**
49635
- * Check if a command exists in extension
49636
- */
49637
- hasExtensionCommand(domain, command) {
49638
- return this.getExtensionCommand(domain, command) !== void 0;
48980
+ };
48981
+ var completionRegistry = new CompletionRegistry();
48982
+
48983
+ // src/completion/adapters.ts
48984
+ var actionDescriptions = {
48985
+ list: "List resources",
48986
+ get: "Get a specific resource",
48987
+ create: "Create a new resource",
48988
+ delete: "Delete a resource",
48989
+ replace: "Replace a resource",
48990
+ apply: "Apply configuration from file",
48991
+ status: "Get resource status",
48992
+ patch: "Patch a resource",
48993
+ "add-labels": "Add labels to a resource",
48994
+ "remove-labels": "Remove labels from a resource"
48995
+ };
48996
+ function fromCommand(cmd) {
48997
+ const node = {
48998
+ name: cmd.name,
48999
+ description: cmd.descriptionShort,
49000
+ source: "custom"
49001
+ };
49002
+ if (cmd.aliases && cmd.aliases.length > 0) {
49003
+ node.aliases = cmd.aliases;
49639
49004
  }
49640
- /**
49641
- * Get all extension command names for a domain
49642
- * Used for tab completion
49643
- */
49644
- getExtensionCommandNames(domain) {
49645
- const extension = this.getExtension(domain);
49646
- if (!extension) {
49647
- return [];
49648
- }
49649
- const names = [];
49650
- for (const [name, cmd] of extension.commands) {
49651
- names.push(name);
49652
- if (cmd.aliases) {
49653
- names.push(...cmd.aliases);
49654
- }
49655
- }
49656
- return names;
49657
- }
49658
- /**
49659
- * Clear the merged domain cache
49660
- * Call this if generated domains change
49661
- */
49662
- clearCache() {
49663
- this.mergedCache.clear();
49664
- }
49665
- /**
49666
- * Get registry statistics
49667
- */
49668
- getStats() {
49669
- let standaloneCount = 0;
49670
- let mergedCount = 0;
49671
- for (const [domain] of this.extensions) {
49672
- if (domainRegistry.has(domain)) {
49673
- mergedCount++;
49674
- } else {
49675
- standaloneCount++;
49676
- }
49677
- }
49678
- return {
49679
- extensionCount: this.extensions.size,
49680
- standaloneCount,
49681
- mergedCount
49682
- };
49683
- }
49684
- };
49685
- var extensionRegistry = new ExtensionRegistry();
49686
-
49687
- // src/extensions/subscription/index.ts
49688
- var clientCache = /* @__PURE__ */ new Map();
49689
- function getClient2(apiClient) {
49690
- const key = apiClient.getServerUrl();
49691
- let client = clientCache.get(key);
49692
- if (!client) {
49693
- client = new SubscriptionClient(apiClient);
49694
- clientCache.set(key, client);
49695
- }
49696
- return client;
49005
+ return node;
49697
49006
  }
49698
- var overviewCommand = {
49699
- name: "overview",
49700
- description: "Display a comprehensive overview of your tenant subscription including current tier level, activated addon services, and quota usage summary. Provides at-a-glance subscription health assessment.",
49701
- descriptionShort: "Display subscription tier and summary",
49702
- descriptionMedium: "Show subscription tier, active addons, and quota usage summary for tenant health assessment.",
49703
- usage: "[--json]",
49704
- aliases: ["show", "info"],
49705
- async execute(args, session) {
49706
- const jsonOutput = args.includes("--json");
49707
- const format = session.getOutputFormat();
49708
- const apiClient = session.getAPIClient();
49709
- if (!apiClient) {
49710
- return errorResult("Not authenticated. Use 'login' command first.");
49711
- }
49712
- try {
49713
- const client = getClient2(apiClient);
49714
- const info = await client.getSubscriptionInfo();
49715
- if (jsonOutput || format === "json") {
49716
- return successResult([JSON.stringify(info, null, 2)]);
49717
- }
49718
- const lines = [];
49719
- lines.push("=== Subscription Overview ===");
49720
- lines.push("");
49721
- lines.push(`Tier: ${info.tier}`);
49722
- if (info.plan) {
49723
- lines.push(`Plan: ${info.plan.displayName}`);
49724
- if (info.plan.description) {
49725
- lines.push(`Description: ${info.plan.description}`);
49726
- }
49727
- }
49728
- lines.push("");
49729
- lines.push("--- Active Addons ---");
49730
- if (info.activeAddons.length === 0) {
49731
- lines.push(" No active addons");
49732
- } else {
49733
- for (const addon of info.activeAddons) {
49734
- lines.push(
49735
- ` - ${addon.displayName} (${getTierDescription(addon.tier)})`
49736
- );
49737
- }
49738
- }
49739
- lines.push("");
49740
- lines.push("--- Quota Summary ---");
49741
- lines.push(` Total Limits: ${info.quotaSummary.totalLimits}`);
49742
- lines.push(` At Risk (>80%): ${info.quotaSummary.limitsAtRisk}`);
49743
- lines.push(` Exceeded: ${info.quotaSummary.limitsExceeded}`);
49744
- if (info.quotaSummary.limitsExceeded > 0) {
49745
- lines.push("");
49746
- lines.push(" Warning: Some quotas are exceeded!");
49747
- }
49748
- return successResult(lines);
49749
- } catch (err) {
49750
- const message = err instanceof Error ? err.message : String(err);
49751
- return errorResult(`Failed to get subscription info: ${message}`);
49752
- }
49007
+ function fromSubcommandGroup(group) {
49008
+ const children = /* @__PURE__ */ new Map();
49009
+ for (const [name, cmd] of group.commands) {
49010
+ children.set(name, fromCommand(cmd));
49753
49011
  }
49754
- };
49755
- var addonsCommand = {
49756
- name: "addons",
49757
- description: "List all addon services with their activation state and access status. Filter by active, available, or denied status. Shows tier requirements and enables identification of upgradable features.",
49758
- descriptionShort: "List addon services and status",
49759
- descriptionMedium: "Display addon services with activation state. Filter by active, available, or denied status.",
49760
- usage: "[--filter active|available|denied] [--all] [--json]",
49761
- aliases: ["services"],
49762
- async execute(args, session) {
49763
- const jsonOutput = args.includes("--json");
49764
- const showAll = args.includes("--all");
49765
- const format = session.getOutputFormat();
49766
- let filter = "";
49767
- const filterIdx = args.indexOf("--filter");
49768
- if (filterIdx !== -1) {
49769
- const filterArg = args[filterIdx + 1];
49770
- if (filterArg) {
49771
- filter = filterArg;
49772
- }
49773
- }
49774
- const apiClient = session.getAPIClient();
49775
- if (!apiClient) {
49776
- return errorResult("Not authenticated. Use 'login' command first.");
49777
- }
49778
- try {
49779
- const client = getClient2(apiClient);
49780
- let addons = await client.getAddonServices("system");
49781
- if (filter) {
49782
- addons = client.filterAddons(addons, filter);
49783
- } else if (!showAll) {
49784
- addons = addons.filter(
49785
- (a) => isAddonActive(a) || isAddonAvailable(a)
49786
- );
49787
- }
49788
- if (jsonOutput || format === "json") {
49789
- return successResult([JSON.stringify(addons, null, 2)]);
49790
- }
49791
- if (addons.length === 0) {
49792
- return successResult([
49793
- `No addon services found${filter ? ` (filter: ${filter})` : ""}`
49794
- ]);
49795
- }
49796
- const lines = [];
49797
- lines.push("=== Addon Services ===");
49798
- lines.push("");
49799
- const active = addons.filter(isAddonActive);
49800
- const available = addons.filter(isAddonAvailable);
49801
- const denied = addons.filter(isAddonDenied);
49802
- const other = addons.filter(
49803
- (a) => !isAddonActive(a) && !isAddonAvailable(a) && !isAddonDenied(a)
49804
- );
49805
- if (active.length > 0) {
49806
- lines.push("--- Active ---");
49807
- for (const addon of active) {
49808
- lines.push(` [OK] ${addon.displayName}`);
49809
- lines.push(
49810
- ` Tier: ${getTierDescription(addon.tier)}`
49811
- );
49812
- }
49813
- lines.push("");
49814
- }
49815
- if (available.length > 0) {
49816
- lines.push("--- Available ---");
49817
- for (const addon of available) {
49818
- lines.push(` [ ] ${addon.displayName}`);
49819
- lines.push(
49820
- ` Status: ${getStateDescription(addon.state)}`
49821
- );
49822
- }
49823
- lines.push("");
49824
- }
49825
- if (denied.length > 0 && (showAll || filter === "denied")) {
49826
- lines.push("--- Access Denied ---");
49827
- for (const addon of denied) {
49828
- lines.push(` [X] ${addon.displayName}`);
49829
- lines.push(
49830
- ` Reason: ${getAccessStatusDescription(addon.accessStatus)}`
49831
- );
49832
- }
49833
- lines.push("");
49834
- }
49835
- if (other.length > 0 && showAll) {
49836
- lines.push("--- Other ---");
49837
- for (const addon of other) {
49838
- lines.push(` [-] ${addon.displayName}`);
49839
- lines.push(
49840
- ` State: ${getStateDescription(addon.state)}`
49841
- );
49842
- lines.push(
49843
- ` Access: ${getAccessStatusDescription(addon.accessStatus)}`
49844
- );
49845
- }
49846
- }
49847
- return successResult(lines);
49848
- } catch (err) {
49849
- const message = err instanceof Error ? err.message : String(err);
49850
- return errorResult(`Failed to get addon services: ${message}`);
49851
- }
49012
+ const node = {
49013
+ name: group.name,
49014
+ description: group.descriptionShort,
49015
+ source: "custom"
49016
+ };
49017
+ if (children.size > 0) {
49018
+ node.children = children;
49852
49019
  }
49853
- };
49854
- var quotaCommand = {
49855
- name: "quota",
49856
- description: "Display tenant-level quota limits and current usage for all resource types. Identify resources approaching limits with warning thresholds. Use --warnings to filter for at-risk quotas only.",
49857
- descriptionShort: "Display quota limits and usage",
49858
- descriptionMedium: "Show tenant-level quota limits with usage percentages. Filter for at-risk resources.",
49859
- usage: "[--warnings] [--json]",
49860
- aliases: ["quotas", "limits"],
49861
- async execute(args, session) {
49862
- const jsonOutput = args.includes("--json");
49863
- const showWarnings = args.includes("--warnings");
49864
- const format = session.getOutputFormat();
49865
- const apiClient = session.getAPIClient();
49866
- if (!apiClient) {
49867
- return errorResult("Not authenticated. Use 'login' command first.");
49868
- }
49869
- try {
49870
- const client = getClient2(apiClient);
49871
- const quotaInfo = await client.getQuotaInfo();
49872
- let objects = quotaInfo.objects;
49873
- if (showWarnings) {
49874
- objects = objects.filter(
49875
- (q) => q.status === QuotaStatus.Warning || q.status === QuotaStatus.Exceeded
49876
- );
49877
- }
49878
- if (jsonOutput || format === "json") {
49879
- return successResult([
49880
- JSON.stringify({ ...quotaInfo, objects }, null, 2)
49881
- ]);
49882
- }
49883
- if (objects.length === 0) {
49884
- return successResult([
49885
- showWarnings ? "No quota warnings or exceeded limits" : "No quota information available"
49886
- ]);
49887
- }
49888
- const lines = [];
49889
- lines.push("=== Quota Usage (Tenant-Level) ===");
49890
- lines.push("");
49891
- const exceeded = objects.filter(
49892
- (q) => q.status === QuotaStatus.Exceeded
49893
- );
49894
- const warning = objects.filter(
49895
- (q) => q.status === QuotaStatus.Warning
49896
- );
49897
- const ok = objects.filter((q) => q.status === QuotaStatus.OK);
49898
- if (exceeded.length > 0) {
49899
- lines.push("--- Exceeded ---");
49900
- for (const q of exceeded) {
49901
- lines.push(` [!!] ${q.displayName}`);
49902
- lines.push(
49903
- ` Usage: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
49904
- );
49905
- }
49906
- lines.push("");
49907
- }
49908
- if (warning.length > 0) {
49909
- lines.push("--- Warning (>80%) ---");
49910
- for (const q of warning) {
49911
- lines.push(` [!] ${q.displayName}`);
49912
- lines.push(
49913
- ` Usage: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
49914
- );
49915
- }
49916
- lines.push("");
49917
- }
49918
- if (ok.length > 0 && !showWarnings) {
49919
- lines.push("--- OK ---");
49920
- for (const q of ok) {
49921
- lines.push(
49922
- ` [OK] ${q.displayName}: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
49923
- );
49924
- }
49925
- }
49926
- return successResult(lines);
49927
- } catch (err) {
49928
- const message = err instanceof Error ? err.message : String(err);
49929
- return errorResult(`Failed to get quota info: ${message}`);
49930
- }
49020
+ return node;
49021
+ }
49022
+ function fromCustomDomain(domain) {
49023
+ const children = /* @__PURE__ */ new Map();
49024
+ for (const [name, cmd] of domain.commands) {
49025
+ children.set(name, fromCommand(cmd));
49931
49026
  }
49932
- };
49933
- var validateCommand = {
49934
- name: "validate",
49935
- description: "Run pre-deployment validation checks against quota limits and feature availability. Verify resource creation will succeed before deployment. Check if specific features are enabled for your subscription tier.",
49936
- descriptionShort: "Validate quotas and feature access",
49937
- descriptionMedium: "Pre-deployment validation for quota capacity and feature availability checks.",
49938
- usage: "[--resource-type <type> --count <n>] [--feature <name>] [--json]",
49939
- async execute(args, session) {
49940
- const jsonOutput = args.includes("--json");
49941
- const format = session.getOutputFormat();
49942
- let resourceType;
49943
- const resourceIdx = args.indexOf("--resource-type");
49944
- if (resourceIdx !== -1 && args[resourceIdx + 1]) {
49945
- resourceType = args[resourceIdx + 1];
49946
- }
49947
- let count;
49948
- const countIdx = args.indexOf("--count");
49949
- if (countIdx !== -1) {
49950
- const countArg = args[countIdx + 1];
49951
- if (countArg) {
49952
- count = parseInt(countArg, 10);
49953
- }
49954
- }
49955
- let feature;
49956
- const featureIdx = args.indexOf("--feature");
49957
- if (featureIdx !== -1 && args[featureIdx + 1]) {
49958
- feature = args[featureIdx + 1];
49959
- }
49960
- if (!resourceType && !feature) {
49961
- return errorResult(
49962
- "Please specify --resource-type and --count, or --feature to validate"
49963
- );
49964
- }
49965
- const apiClient = session.getAPIClient();
49966
- if (!apiClient) {
49967
- return errorResult("Not authenticated. Use 'login' command first.");
49968
- }
49969
- try {
49970
- const client = getClient2(apiClient);
49971
- const result = await client.validateResource({
49972
- resourceType: resourceType ?? "",
49973
- count: count ?? 0,
49974
- feature: feature ?? ""
49975
- });
49976
- if (jsonOutput || format === "json") {
49977
- return successResult([JSON.stringify(result, null, 2)]);
49978
- }
49979
- const lines = [];
49980
- lines.push("=== Validation Result ===");
49981
- lines.push("");
49982
- lines.push(`Status: ${result.valid ? "[PASS]" : "[FAIL]"}`);
49983
- lines.push("");
49984
- if (result.checks.length > 0) {
49985
- lines.push("--- Checks ---");
49986
- for (const check of result.checks) {
49987
- const icon = check.result === "PASS" ? "[OK]" : check.result === "WARNING" ? "[!]" : "[X]";
49988
- lines.push(
49989
- ` ${icon} [${check.type}] ${check.message ?? ""}`
49990
- );
49991
- }
49992
- }
49993
- if (result.errors && result.errors.length > 0) {
49994
- lines.push("");
49995
- lines.push("--- Errors ---");
49996
- for (const error of result.errors) {
49997
- lines.push(` [X] ${error}`);
49998
- }
49999
- }
50000
- if (result.warnings && result.warnings.length > 0) {
50001
- lines.push("");
50002
- lines.push("--- Warnings ---");
50003
- for (const warning of result.warnings) {
50004
- lines.push(` [!] ${warning}`);
50005
- }
50006
- }
50007
- const commandResult = {
50008
- output: lines,
50009
- shouldExit: false,
50010
- shouldClear: false,
50011
- contextChanged: false
50012
- };
50013
- if (!result.valid) {
50014
- commandResult.error = "Validation failed";
50015
- }
50016
- return commandResult;
50017
- } catch (err) {
50018
- const message = err instanceof Error ? err.message : String(err);
50019
- return errorResult(`Validation failed: ${message}`);
50020
- }
49027
+ for (const [name, group] of domain.subcommands) {
49028
+ children.set(name, fromSubcommandGroup(group));
50021
49029
  }
50022
- };
50023
- var activationStatusCommand = {
50024
- name: "activation-status",
50025
- description: "Monitor the status of pending addon service activation requests. Track which addons are awaiting approval or provisioning. View currently active addons alongside pending requests.",
50026
- descriptionShort: "Check pending addon activations",
50027
- descriptionMedium: "Monitor pending addon activation requests and track provisioning status.",
50028
- usage: "[--json]",
50029
- async execute(args, session) {
50030
- const jsonOutput = args.includes("--json");
50031
- const format = session.getOutputFormat();
50032
- const apiClient = session.getAPIClient();
50033
- if (!apiClient) {
50034
- return errorResult("Not authenticated. Use 'login' command first.");
50035
- }
50036
- try {
50037
- const client = getClient2(apiClient);
50038
- const result = await client.getPendingActivations("system");
50039
- if (jsonOutput || format === "json") {
50040
- return successResult([JSON.stringify(result, null, 2)]);
50041
- }
50042
- const lines = [];
50043
- lines.push("=== Activation Status ===");
50044
- lines.push("");
50045
- if (result.pendingActivations.length === 0) {
50046
- lines.push("No pending activation requests.");
50047
- } else {
50048
- lines.push(`Pending Activations: ${result.totalPending}`);
50049
- lines.push("");
50050
- for (const pending of result.pendingActivations) {
50051
- lines.push(` [...] ${pending.addonService}`);
50052
- if (pending.message) {
50053
- lines.push(` Status: ${pending.message}`);
50054
- }
50055
- }
50056
- }
50057
- lines.push("");
50058
- lines.push(`Active Addons: ${result.activeAddons.length}`);
50059
- if (result.activeAddons.length > 0) {
50060
- for (const addon of result.activeAddons) {
50061
- lines.push(` [OK] ${addon}`);
50062
- }
50063
- }
50064
- return successResult(lines);
50065
- } catch (err) {
50066
- const message = err instanceof Error ? err.message : String(err);
50067
- return errorResult(`Failed to get activation status: ${message}`);
50068
- }
49030
+ const node = {
49031
+ name: domain.name,
49032
+ description: domain.descriptionShort,
49033
+ source: "custom"
49034
+ };
49035
+ if (children.size > 0) {
49036
+ node.children = children;
50069
49037
  }
50070
- };
50071
- var subscriptionExtension = {
50072
- targetDomain: "subscription",
50073
- description: "xcsh-specific subscription management commands (overview, quota analysis, validation)",
50074
- standalone: true,
50075
- // Works even before upstream adds subscription domain
50076
- commands: /* @__PURE__ */ new Map([
50077
- ["overview", overviewCommand],
50078
- ["addons", addonsCommand],
50079
- ["quota", quotaCommand],
50080
- ["validate", validateCommand],
50081
- ["activation-status", activationStatusCommand]
50082
- ]),
50083
- subcommands: /* @__PURE__ */ new Map()
50084
- };
50085
-
50086
- // src/extensions/index.ts
50087
- function initializeExtensions() {
50088
- extensionRegistry.register(subscriptionExtension);
49038
+ return node;
50089
49039
  }
50090
- initializeExtensions();
50091
-
50092
- // src/domains/completion/generators.ts
50093
- function getAllDomains() {
50094
- const domains = [];
50095
- const seen = /* @__PURE__ */ new Set();
50096
- for (const domain of customDomains.all()) {
50097
- domains.push({
50098
- name: domain.name,
50099
- description: domain.descriptionShort,
50100
- aliases: []
49040
+ function fromApiDomain(info) {
49041
+ const children = /* @__PURE__ */ new Map();
49042
+ for (const action of validActions) {
49043
+ children.set(action, {
49044
+ name: action,
49045
+ description: actionDescriptions[action] ?? action,
49046
+ source: "api"
50101
49047
  });
50102
- seen.add(domain.name);
50103
- }
50104
- for (const extDomain of extensionRegistry.getExtendedDomains()) {
50105
- if (seen.has(extDomain)) continue;
50106
- if (domainRegistry.has(extDomain)) continue;
50107
- const merged = extensionRegistry.getMergedDomain(extDomain);
50108
- if (merged && merged.source === "extension") {
50109
- domains.push({
50110
- name: extDomain,
50111
- description: merged.description,
50112
- aliases: []
50113
- });
50114
- seen.add(extDomain);
50115
- }
50116
49048
  }
50117
- for (const [name, info] of domainRegistry) {
50118
- if (seen.has(name)) continue;
50119
- domains.push({
50120
- name,
50121
- description: info.descriptionShort,
50122
- aliases: info.aliases
50123
- });
50124
- seen.add(name);
49049
+ const node = {
49050
+ name: info.name,
49051
+ description: info.descriptionShort,
49052
+ children,
49053
+ source: "api"
49054
+ };
49055
+ if (info.aliases.length > 0) {
49056
+ node.aliases = info.aliases;
50125
49057
  }
50126
- return domains.sort((a, b) => a.name.localeCompare(b.name));
49058
+ return node;
50127
49059
  }
50128
- function getCustomDomainCommands(domainName) {
50129
- const domain = customDomains.get(domainName);
50130
- if (!domain) {
50131
- return { commands: [], subcommands: /* @__PURE__ */ new Map() };
50132
- }
50133
- const commands = Array.from(domain.commands.keys());
50134
- const subcommands = /* @__PURE__ */ new Map();
50135
- for (const [groupName, group] of domain.subcommands) {
50136
- subcommands.set(groupName, Array.from(group.commands.keys()));
50137
- }
50138
- return { commands, subcommands };
49060
+ function getActionDescriptions() {
49061
+ return { ...actionDescriptions };
49062
+ }
49063
+
49064
+ // src/completion/generators/common.ts
49065
+ function escapeForZsh(str) {
49066
+ return str.replace(/'/g, "'\\''").replace(/:/g, "\\:");
49067
+ }
49068
+ function escapeForFish(str) {
49069
+ return str.replace(/'/g, "\\'");
50139
49070
  }
49071
+
49072
+ // src/domains/completion/generators.ts
50140
49073
  function generateBashCompletion() {
50141
- const domains = getAllDomains();
49074
+ const domains = completionRegistry.getDomains();
50142
49075
  const domainNames = domains.map((d) => d.name).join(" ");
50143
- const allAliases = domains.flatMap((d) => d.aliases).join(" ");
50144
- const actions = Array.from(validActions).join(" ");
49076
+ const allAliases = [];
49077
+ for (const domain of domains) {
49078
+ if (domain.aliases) {
49079
+ allAliases.push(...domain.aliases);
49080
+ }
49081
+ }
49082
+ const actionDescriptions2 = getActionDescriptions();
49083
+ const actions = Object.keys(actionDescriptions2).join(" ");
50145
49084
  const customDomainCompletions = [];
50146
- for (const domain of customDomains.all()) {
50147
- const { commands, subcommands } = getCustomDomainCommands(domain.name);
50148
- if (commands.length > 0 || subcommands.size > 0) {
50149
- const allCommands = [
50150
- ...commands,
50151
- ...Array.from(subcommands.keys())
50152
- ];
49085
+ for (const domain of domains) {
49086
+ if (domain.source !== "custom" || !domain.children) continue;
49087
+ const childNames = Array.from(domain.children.keys());
49088
+ if (childNames.length > 0) {
50153
49089
  customDomainCompletions.push(
50154
49090
  ` ${domain.name})`,
50155
- ` COMPREPLY=($(compgen -W "${allCommands.join(" ")}" -- "\${cur}"))`,
49091
+ ` COMPREPLY=($(compgen -W "${childNames.join(" ")}" -- "\${cur}"))`,
50156
49092
  ` return 0`,
50157
49093
  ` ;;`
50158
49094
  );
50159
49095
  }
50160
- for (const [groupName, groupCommands] of subcommands) {
50161
- customDomainCompletions.push(
50162
- ` ${domain.name}/${groupName})`,
50163
- ` COMPREPLY=($(compgen -W "${groupCommands.join(" ")}" -- "\${cur}"))`,
50164
- ` return 0`,
50165
- ` ;;`
50166
- );
49096
+ for (const [childName, child] of domain.children) {
49097
+ if (child.children && child.children.size > 0) {
49098
+ const nestedNames = Array.from(child.children.keys());
49099
+ customDomainCompletions.push(
49100
+ ` ${domain.name}/${childName})`,
49101
+ ` COMPREPLY=($(compgen -W "${nestedNames.join(" ")}" -- "\${cur}"))`,
49102
+ ` return 0`,
49103
+ ` ;;`
49104
+ );
49105
+ }
50167
49106
  }
50168
49107
  }
50169
49108
  return `# bash completion for xcsh
@@ -50174,7 +49113,7 @@ _xcsh_completions() {
50174
49113
  local cur prev words cword
50175
49114
  _init_completion || return
50176
49115
 
50177
- local commands="${domainNames} ${allAliases} help quit exit clear history"
49116
+ local commands="${domainNames} ${allAliases.join(" ")} help quit exit clear history"
50178
49117
  local actions="${actions}"
50179
49118
  local builtins="help quit exit clear history context ctx"
50180
49119
  local global_flags="--help -h --version -v --interactive -i --no-color --output -o --namespace -ns"
@@ -50220,36 +49159,29 @@ complete -F _xcsh_completions xcsh
50220
49159
  `;
50221
49160
  }
50222
49161
  function generateZshCompletion() {
50223
- const domains = getAllDomains();
49162
+ const domains = completionRegistry.getDomains();
50224
49163
  const domainDescriptions = domains.map((d) => {
50225
- const escaped = d.description.replace(/'/g, "'\\''").replace(/:/g, "\\:");
49164
+ const escaped = escapeForZsh(d.description);
50226
49165
  return `'${d.name}:${escaped}'`;
50227
49166
  }).join("\n ");
50228
- const aliasDescriptions = domains.flatMap((d) => d.aliases.map((a) => `'${a}:Alias for ${d.name}'`)).join("\n ");
50229
- const actionDescriptions = [
50230
- "'list:List resources'",
50231
- "'get:Get a specific resource'",
50232
- "'create:Create a new resource'",
50233
- "'delete:Delete a resource'",
50234
- "'replace:Replace a resource'",
50235
- "'apply:Apply configuration from file'",
50236
- "'status:Get resource status'",
50237
- "'patch:Patch a resource'",
50238
- "'add-labels:Add labels to a resource'",
50239
- "'remove-labels:Remove labels from a resource'"
50240
- ].join("\n ");
49167
+ const aliasDescriptions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap((d) => d.aliases.map((a) => `'${a}:Alias for ${d.name}'`)).join("\n ");
49168
+ const actionDescriptions2 = getActionDescriptions();
49169
+ const actionDescArray = Object.entries(actionDescriptions2).map(([action, desc]) => {
49170
+ const escaped = escapeForZsh(desc);
49171
+ return `'${action}:${escaped}'`;
49172
+ }).join("\n ");
50241
49173
  const customDomainCompletions = [];
50242
- for (const domain of customDomains.all()) {
50243
- const { commands, subcommands } = getCustomDomainCommands(domain.name);
50244
- if (commands.length > 0 || subcommands.size > 0) {
50245
- const cmdDescriptions = commands.map((c) => `'${c}:Command'`);
50246
- const subDescriptions = Array.from(subcommands.keys()).map(
50247
- (s) => `'${s}:Subcommand group'`
50248
- );
50249
- const allDescriptions = [...cmdDescriptions, ...subDescriptions].filter((d) => d.length > 0).join(" ");
49174
+ for (const domain of domains) {
49175
+ if (domain.source !== "custom" || !domain.children) continue;
49176
+ const childDescriptions = [];
49177
+ for (const child of domain.children.values()) {
49178
+ const escaped = escapeForZsh(child.description);
49179
+ childDescriptions.push(`'${child.name}:${escaped}'`);
49180
+ }
49181
+ if (childDescriptions.length > 0) {
50250
49182
  customDomainCompletions.push(
50251
49183
  ` (${domain.name})`,
50252
- ` _values 'command' ${allDescriptions}`,
49184
+ ` _values 'command' ${childDescriptions.join(" ")}`,
50253
49185
  ` ;;`
50254
49186
  );
50255
49187
  }
@@ -50305,7 +49237,7 @@ ${customDomainCompletions.join("\n")}
50305
49237
  (*)
50306
49238
  local -a actions
50307
49239
  actions=(
50308
- ${actionDescriptions}
49240
+ ${actionDescArray}
50309
49241
  )
50310
49242
  _describe -t actions 'action' actions
50311
49243
  ;;
@@ -50336,163 +49268,417 @@ _xcsh "$@"
50336
49268
  `;
50337
49269
  }
50338
49270
  function generateFishCompletion() {
50339
- const domains = getAllDomains();
50340
- const actions = Array.from(validActions);
49271
+ const domains = completionRegistry.getDomains();
49272
+ const actionDescriptions2 = getActionDescriptions();
50341
49273
  const domainCompletions = domains.map((d) => {
50342
- const escaped = d.description.replace(/'/g, "\\'");
49274
+ const escaped = escapeForFish(d.description);
50343
49275
  return `complete -c xcsh -n "__fish_use_subcommand" -a "${d.name}" -d '${escaped}'`;
50344
49276
  }).join("\n");
50345
- const aliasCompletions = domains.flatMap(
49277
+ const aliasCompletions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap(
50346
49278
  (d) => d.aliases.map(
50347
49279
  (a) => `complete -c xcsh -n "__fish_use_subcommand" -a "${a}" -d 'Alias for ${d.name}'`
50348
49280
  )
50349
49281
  ).join("\n");
50350
- const actionCompletions = domains.map(
50351
- (d) => actions.map(
50352
- (a) => `complete -c xcsh -n "__fish_seen_subcommand_from ${d.name}" -a "${a}" -d '${a.charAt(0).toUpperCase() + a.slice(1)} resources'`
50353
- ).join("\n")
50354
- ).join("\n");
50355
49282
  const customDomainCompletions = [];
50356
- for (const domain of customDomains.all()) {
50357
- const { commands, subcommands } = getCustomDomainCommands(domain.name);
50358
- for (const cmd of commands) {
49283
+ for (const domain of domains) {
49284
+ if (domain.source !== "custom" || !domain.children) continue;
49285
+ for (const child of domain.children.values()) {
49286
+ const escaped = escapeForFish(child.description);
50359
49287
  customDomainCompletions.push(
50360
- `complete -c xcsh -n "__fish_seen_subcommand_from ${domain.name}" -a "${cmd}" -d 'Command'`
49288
+ `complete -c xcsh -n "__fish_seen_subcommand_from ${domain.name}" -a "${child.name}" -d '${escaped}'`
50361
49289
  );
49290
+ if (child.children) {
49291
+ for (const nested of child.children.values()) {
49292
+ const nestedEscaped = escapeForFish(nested.description);
49293
+ customDomainCompletions.push(
49294
+ `complete -c xcsh -n "__fish_seen_subcommand_from ${domain.name}; and __fish_seen_subcommand_from ${child.name}" -a "${nested.name}" -d '${nestedEscaped}'`
49295
+ );
49296
+ }
49297
+ }
50362
49298
  }
50363
- for (const [groupName, groupCommands] of subcommands) {
50364
- customDomainCompletions.push(
50365
- `complete -c xcsh -n "__fish_seen_subcommand_from ${domain.name}" -a "${groupName}" -d 'Subcommand group'`
50366
- );
50367
- for (const cmd of groupCommands) {
50368
- customDomainCompletions.push(
50369
- `complete -c xcsh -n "__fish_seen_subcommand_from ${domain.name}; and __fish_seen_subcommand_from ${groupName}" -a "${cmd}" -d 'Command'`
50370
- );
49299
+ }
49300
+ const apiDomains = domains.filter((d) => d.source === "api");
49301
+ const actionCompletions = apiDomains.map(
49302
+ (d) => Object.entries(actionDescriptions2).map(
49303
+ ([action, desc]) => `complete -c xcsh -n "__fish_seen_subcommand_from ${d.name}" -a "${action}" -d '${escapeForFish(desc)}'`
49304
+ ).join("\n")
49305
+ ).join("\n");
49306
+ return `# fish completion for xcsh
49307
+ # Generated by xcsh completion fish
49308
+
49309
+ # Disable file completions for xcsh
49310
+ complete -c xcsh -f
49311
+
49312
+ # Global options
49313
+ complete -c xcsh -s h -l help -d 'Show help information'
49314
+ complete -c xcsh -s v -l version -d 'Show version number'
49315
+ complete -c xcsh -s i -l interactive -d 'Force interactive mode'
49316
+ complete -c xcsh -l no-color -d 'Disable color output'
49317
+ complete -c xcsh -s o -l output -d 'Output format' -xa 'json yaml table'
49318
+ complete -c xcsh -l namespace -s ns -d 'Namespace' -xa 'default system shared'
49319
+
49320
+ # Builtin commands
49321
+ complete -c xcsh -n "__fish_use_subcommand" -a "help" -d 'Show help information'
49322
+ complete -c xcsh -n "__fish_use_subcommand" -a "quit" -d 'Exit the shell'
49323
+ complete -c xcsh -n "__fish_use_subcommand" -a "exit" -d 'Exit the shell'
49324
+ complete -c xcsh -n "__fish_use_subcommand" -a "clear" -d 'Clear the screen'
49325
+ complete -c xcsh -n "__fish_use_subcommand" -a "history" -d 'Show command history'
49326
+ complete -c xcsh -n "__fish_use_subcommand" -a "context" -d 'Show current navigation context'
49327
+ complete -c xcsh -n "__fish_use_subcommand" -a "ctx" -d 'Show current navigation context'
49328
+
49329
+ # Domain completions
49330
+ ${domainCompletions}
49331
+
49332
+ # Alias completions
49333
+ ${aliasCompletions}
49334
+
49335
+ # Custom domain subcommands
49336
+ ${customDomainCompletions.join("\n")}
49337
+
49338
+ # Action completions for API domains
49339
+ ${actionCompletions}
49340
+
49341
+ # Action-specific flags
49342
+ complete -c xcsh -l name -s n -d 'Resource name'
49343
+ complete -c xcsh -l limit -d 'Maximum results'
49344
+ complete -c xcsh -l label -d 'Filter by label'
49345
+ complete -c xcsh -s f -l file -d 'Configuration file' -r
49346
+ complete -c xcsh -l force -d 'Force deletion'
49347
+ complete -c xcsh -l cascade -d 'Cascade delete'
49348
+ `;
49349
+ }
49350
+
49351
+ // src/domains/completion/index.ts
49352
+ var bashCommand = {
49353
+ name: "bash",
49354
+ description: "Generate a bash shell completion script for xcsh. Output the script to stdout for manual installation or pipe to a file. Enables tab-completion for commands, domains, actions, and option flags.",
49355
+ descriptionShort: "Generate bash completion script",
49356
+ descriptionMedium: "Output bash completion script for tab-completion of commands, domains, and flags.",
49357
+ async execute() {
49358
+ try {
49359
+ const script = generateBashCompletion();
49360
+ return successResult([script]);
49361
+ } catch (error) {
49362
+ return errorResult(
49363
+ `Failed to generate bash completion: ${error instanceof Error ? error.message : "Unknown error"}`
49364
+ );
49365
+ }
49366
+ }
49367
+ };
49368
+ var zshCommand = {
49369
+ name: "zsh",
49370
+ description: "Generate a zsh shell completion script for xcsh. Output the script to stdout for manual installation or add to your fpath. Provides rich tab-completion with descriptions for commands, domains, and options.",
49371
+ descriptionShort: "Generate zsh completion script",
49372
+ descriptionMedium: "Output zsh completion script with rich descriptions for commands and options.",
49373
+ async execute() {
49374
+ try {
49375
+ const script = generateZshCompletion();
49376
+ return successResult([script]);
49377
+ } catch (error) {
49378
+ return errorResult(
49379
+ `Failed to generate zsh completion: ${error instanceof Error ? error.message : "Unknown error"}`
49380
+ );
49381
+ }
49382
+ }
49383
+ };
49384
+ var fishCommand = {
49385
+ name: "fish",
49386
+ description: "Generate a fish shell completion script for xcsh. Output the script to stdout for manual installation or save to your fish completions directory. Enables intelligent tab-completion with inline descriptions.",
49387
+ descriptionShort: "Generate fish completion script",
49388
+ descriptionMedium: "Output fish completion script with inline descriptions for commands and options.",
49389
+ async execute() {
49390
+ try {
49391
+ const script = generateFishCompletion();
49392
+ return successResult([script]);
49393
+ } catch (error) {
49394
+ return errorResult(
49395
+ `Failed to generate fish completion: ${error instanceof Error ? error.message : "Unknown error"}`
49396
+ );
49397
+ }
49398
+ }
49399
+ };
49400
+ var completionDomain = {
49401
+ name: "completion",
49402
+ description: "Generate shell completion scripts for bash, zsh, and fish shells. Enables tab-completion for xcsh commands, domains, actions, and flags in your preferred shell environment.",
49403
+ descriptionShort: "Shell completion script generation",
49404
+ descriptionMedium: "Generate tab-completion scripts for bash, zsh, and fish shells to enhance the xcsh command-line experience.",
49405
+ commands: /* @__PURE__ */ new Map([
49406
+ ["bash", bashCommand],
49407
+ ["zsh", zshCommand],
49408
+ ["fish", fishCommand]
49409
+ ]),
49410
+ subcommands: /* @__PURE__ */ new Map()
49411
+ };
49412
+
49413
+ // src/domains/index.ts
49414
+ customDomains.register(loginDomain);
49415
+ customDomains.register(cloudstatusDomain);
49416
+ customDomains.register(completionDomain);
49417
+ for (const domain of customDomains.all()) {
49418
+ completionRegistry.registerDomain(fromCustomDomain(domain));
49419
+ }
49420
+ for (const [, info] of domainRegistry) {
49421
+ if (!completionRegistry.has(info.name)) {
49422
+ completionRegistry.registerDomain(fromApiDomain(info));
49423
+ }
49424
+ }
49425
+ var domainAliases = /* @__PURE__ */ new Map();
49426
+ for (const alias of cloudstatusAliases) {
49427
+ domainAliases.set(alias, "cloudstatus");
49428
+ }
49429
+ function resolveDomainAlias(name) {
49430
+ return domainAliases.get(name) ?? name;
49431
+ }
49432
+ function isCustomDomain(name) {
49433
+ const canonical = resolveDomainAlias(name);
49434
+ return customDomains.has(canonical);
49435
+ }
49436
+
49437
+ // src/extensions/types.ts
49438
+ var RESERVED_API_ACTIONS = /* @__PURE__ */ new Set([
49439
+ "list",
49440
+ "get",
49441
+ "create",
49442
+ "delete",
49443
+ "replace",
49444
+ "apply",
49445
+ "status",
49446
+ "patch",
49447
+ "add-labels",
49448
+ "remove-labels"
49449
+ ]);
49450
+ function isReservedAction(name) {
49451
+ return RESERVED_API_ACTIONS.has(name.toLowerCase());
49452
+ }
49453
+ function validateExtension(extension) {
49454
+ const conflicts = [];
49455
+ for (const [name] of extension.commands) {
49456
+ if (isReservedAction(name)) {
49457
+ conflicts.push(name);
49458
+ }
49459
+ }
49460
+ if (conflicts.length > 0) {
49461
+ throw new Error(
49462
+ `Extension "${extension.targetDomain}" has commands that conflict with API actions: ${conflicts.join(", ")}. Use unique names or submit a feature request to upstream.`
49463
+ );
49464
+ }
49465
+ }
49466
+
49467
+ // src/extensions/registry.ts
49468
+ var ExtensionRegistry = class {
49469
+ extensions = /* @__PURE__ */ new Map();
49470
+ mergedCache = /* @__PURE__ */ new Map();
49471
+ /**
49472
+ * Register a domain extension
49473
+ *
49474
+ * @param extension - Extension definition to register
49475
+ * @throws Error if extension has command name conflicts with API actions
49476
+ */
49477
+ register(extension) {
49478
+ validateExtension(extension);
49479
+ this.extensions.set(extension.targetDomain, extension);
49480
+ this.mergedCache.delete(extension.targetDomain);
49481
+ }
49482
+ /**
49483
+ * Get extension for a domain
49484
+ *
49485
+ * @param domain - Canonical domain name
49486
+ * @returns Extension if registered, undefined otherwise
49487
+ */
49488
+ getExtension(domain) {
49489
+ return this.extensions.get(domain);
49490
+ }
49491
+ /**
49492
+ * Check if a domain has an extension
49493
+ */
49494
+ hasExtension(domain) {
49495
+ return this.extensions.has(domain);
49496
+ }
49497
+ /**
49498
+ * Get merged domain view combining API domain + extension
49499
+ *
49500
+ * Resolution priority:
49501
+ * 1. Check if canonical domain exists in generated domains
49502
+ * 2. Check if extension exists for this domain
49503
+ * 3. Merge if both exist, or return standalone if only one
49504
+ *
49505
+ * @param domain - Canonical domain name or alias
49506
+ * @returns Merged domain view or undefined if neither exists
49507
+ */
49508
+ getMergedDomain(domain) {
49509
+ const canonical = aliasRegistry.get(domain) ?? domain;
49510
+ const cached = this.mergedCache.get(canonical);
49511
+ if (cached) {
49512
+ return cached;
49513
+ }
49514
+ const domainInfo = domainRegistry.get(canonical);
49515
+ const extension = this.extensions.get(canonical);
49516
+ if (!domainInfo && !extension) {
49517
+ return void 0;
49518
+ }
49519
+ const merged = this.buildMergedDomain(canonical, domainInfo, extension);
49520
+ this.mergedCache.set(canonical, merged);
49521
+ return merged;
49522
+ }
49523
+ /**
49524
+ * Build a merged domain from API domain info and/or extension
49525
+ */
49526
+ buildMergedDomain(name, domainInfo, extension) {
49527
+ const hasGeneratedDomain = !!domainInfo;
49528
+ const hasExtension = !!extension;
49529
+ let source;
49530
+ if (hasGeneratedDomain && hasExtension) {
49531
+ source = "merged";
49532
+ } else if (hasGeneratedDomain) {
49533
+ source = "generated";
49534
+ } else {
49535
+ source = "extension";
49536
+ }
49537
+ const displayName = domainInfo?.displayName ?? this.toDisplayName(extension?.targetDomain ?? name);
49538
+ const description = domainInfo?.description ?? extension?.description ?? `Commands for ${displayName}`;
49539
+ const merged = {
49540
+ name,
49541
+ displayName,
49542
+ description,
49543
+ source,
49544
+ hasGeneratedDomain,
49545
+ hasExtension,
49546
+ extensionCommands: extension?.commands ?? /* @__PURE__ */ new Map(),
49547
+ extensionSubcommands: extension?.subcommands ?? /* @__PURE__ */ new Map()
49548
+ };
49549
+ if (domainInfo) {
49550
+ merged.metadata = domainInfo;
49551
+ }
49552
+ return merged;
49553
+ }
49554
+ /**
49555
+ * Convert snake_case domain name to display name
49556
+ */
49557
+ toDisplayName(name) {
49558
+ return name.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
49559
+ }
49560
+ /**
49561
+ * Get all domains with extensions (for iteration)
49562
+ */
49563
+ getExtendedDomains() {
49564
+ return Array.from(this.extensions.keys());
49565
+ }
49566
+ /**
49567
+ * Get all merged domains (generated + extended)
49568
+ *
49569
+ * Returns domains that either:
49570
+ * - Have generated API commands (from upstream)
49571
+ * - Have extension commands (xcsh-specific)
49572
+ * - Have both (merged)
49573
+ */
49574
+ getAllMergedDomains() {
49575
+ const domains = /* @__PURE__ */ new Set();
49576
+ for (const name of domainRegistry.keys()) {
49577
+ domains.add(name);
49578
+ }
49579
+ for (const name of this.extensions.keys()) {
49580
+ domains.add(name);
49581
+ }
49582
+ const merged = [];
49583
+ for (const name of domains) {
49584
+ const domain = this.getMergedDomain(name);
49585
+ if (domain) {
49586
+ merged.push(domain);
49587
+ }
49588
+ }
49589
+ return merged.sort((a, b) => a.name.localeCompare(b.name));
49590
+ }
49591
+ /**
49592
+ * Get extension command by name
49593
+ *
49594
+ * @param domain - Canonical domain name
49595
+ * @param command - Command name
49596
+ * @returns Command definition if found
49597
+ */
49598
+ getExtensionCommand(domain, command) {
49599
+ const extension = this.getExtension(domain);
49600
+ if (!extension) {
49601
+ return void 0;
49602
+ }
49603
+ const cmd = extension.commands.get(command);
49604
+ if (cmd) {
49605
+ return cmd;
49606
+ }
49607
+ for (const [, cmdDef] of extension.commands) {
49608
+ if (cmdDef.aliases?.includes(command)) {
49609
+ return cmdDef;
50371
49610
  }
50372
49611
  }
49612
+ return void 0;
50373
49613
  }
50374
- return `# fish completion for xcsh
50375
- # Generated by xcsh completion fish
50376
-
50377
- # Disable file completions for xcsh
50378
- complete -c xcsh -f
50379
-
50380
- # Global options
50381
- complete -c xcsh -s h -l help -d 'Show help information'
50382
- complete -c xcsh -s v -l version -d 'Show version number'
50383
- complete -c xcsh -s i -l interactive -d 'Force interactive mode'
50384
- complete -c xcsh -l no-color -d 'Disable color output'
50385
- complete -c xcsh -s o -l output -d 'Output format' -xa 'json yaml table'
50386
- complete -c xcsh -l namespace -s ns -d 'Namespace' -xa 'default system shared'
50387
-
50388
- # Builtin commands
50389
- complete -c xcsh -n "__fish_use_subcommand" -a "help" -d 'Show help information'
50390
- complete -c xcsh -n "__fish_use_subcommand" -a "quit" -d 'Exit the shell'
50391
- complete -c xcsh -n "__fish_use_subcommand" -a "exit" -d 'Exit the shell'
50392
- complete -c xcsh -n "__fish_use_subcommand" -a "clear" -d 'Clear the screen'
50393
- complete -c xcsh -n "__fish_use_subcommand" -a "history" -d 'Show command history'
50394
- complete -c xcsh -n "__fish_use_subcommand" -a "context" -d 'Show current navigation context'
50395
- complete -c xcsh -n "__fish_use_subcommand" -a "ctx" -d 'Show current navigation context'
50396
-
50397
- # Domain completions
50398
- ${domainCompletions}
50399
-
50400
- # Alias completions
50401
- ${aliasCompletions}
50402
-
50403
- # Custom domain subcommands
50404
- ${customDomainCompletions.join("\n")}
50405
-
50406
- # Action completions for API domains
50407
- ${actionCompletions}
50408
-
50409
- # Action-specific flags
50410
- complete -c xcsh -l name -s n -d 'Resource name'
50411
- complete -c xcsh -l limit -d 'Maximum results'
50412
- complete -c xcsh -l label -d 'Filter by label'
50413
- complete -c xcsh -s f -l file -d 'Configuration file' -r
50414
- complete -c xcsh -l force -d 'Force deletion'
50415
- complete -c xcsh -l cascade -d 'Cascade delete'
50416
- `;
50417
- }
50418
-
50419
- // src/domains/completion/index.ts
50420
- var bashCommand = {
50421
- name: "bash",
50422
- description: "Generate a bash shell completion script for xcsh. Output the script to stdout for manual installation or pipe to a file. Enables tab-completion for commands, domains, actions, and option flags.",
50423
- descriptionShort: "Generate bash completion script",
50424
- descriptionMedium: "Output bash completion script for tab-completion of commands, domains, and flags.",
50425
- async execute() {
50426
- try {
50427
- const script = generateBashCompletion();
50428
- return successResult([script]);
50429
- } catch (error) {
50430
- return errorResult(
50431
- `Failed to generate bash completion: ${error instanceof Error ? error.message : "Unknown error"}`
50432
- );
50433
- }
49614
+ /**
49615
+ * Get extension subcommand group
49616
+ *
49617
+ * @param domain - Canonical domain name
49618
+ * @param subcommand - Subcommand group name
49619
+ * @returns Subcommand group if found
49620
+ */
49621
+ getExtensionSubcommand(domain, subcommand) {
49622
+ const extension = this.getExtension(domain);
49623
+ return extension?.subcommands?.get(subcommand);
50434
49624
  }
50435
- };
50436
- var zshCommand = {
50437
- name: "zsh",
50438
- description: "Generate a zsh shell completion script for xcsh. Output the script to stdout for manual installation or add to your fpath. Provides rich tab-completion with descriptions for commands, domains, and options.",
50439
- descriptionShort: "Generate zsh completion script",
50440
- descriptionMedium: "Output zsh completion script with rich descriptions for commands and options.",
50441
- async execute() {
50442
- try {
50443
- const script = generateZshCompletion();
50444
- return successResult([script]);
50445
- } catch (error) {
50446
- return errorResult(
50447
- `Failed to generate zsh completion: ${error instanceof Error ? error.message : "Unknown error"}`
50448
- );
49625
+ /**
49626
+ * Check if a command exists in extension
49627
+ */
49628
+ hasExtensionCommand(domain, command) {
49629
+ return this.getExtensionCommand(domain, command) !== void 0;
49630
+ }
49631
+ /**
49632
+ * Get all extension command names for a domain
49633
+ * Used for tab completion
49634
+ */
49635
+ getExtensionCommandNames(domain) {
49636
+ const extension = this.getExtension(domain);
49637
+ if (!extension) {
49638
+ return [];
49639
+ }
49640
+ const names = [];
49641
+ for (const [name, cmd] of extension.commands) {
49642
+ names.push(name);
49643
+ if (cmd.aliases) {
49644
+ names.push(...cmd.aliases);
49645
+ }
50449
49646
  }
49647
+ return names;
50450
49648
  }
50451
- };
50452
- var fishCommand = {
50453
- name: "fish",
50454
- description: "Generate a fish shell completion script for xcsh. Output the script to stdout for manual installation or save to your fish completions directory. Enables intelligent tab-completion with inline descriptions.",
50455
- descriptionShort: "Generate fish completion script",
50456
- descriptionMedium: "Output fish completion script with inline descriptions for commands and options.",
50457
- async execute() {
50458
- try {
50459
- const script = generateFishCompletion();
50460
- return successResult([script]);
50461
- } catch (error) {
50462
- return errorResult(
50463
- `Failed to generate fish completion: ${error instanceof Error ? error.message : "Unknown error"}`
50464
- );
49649
+ /**
49650
+ * Clear the merged domain cache
49651
+ * Call this if generated domains change
49652
+ */
49653
+ clearCache() {
49654
+ this.mergedCache.clear();
49655
+ }
49656
+ /**
49657
+ * Get registry statistics
49658
+ */
49659
+ getStats() {
49660
+ let standaloneCount = 0;
49661
+ let mergedCount = 0;
49662
+ for (const [domain] of this.extensions) {
49663
+ if (domainRegistry.has(domain)) {
49664
+ mergedCount++;
49665
+ } else {
49666
+ standaloneCount++;
49667
+ }
50465
49668
  }
49669
+ return {
49670
+ extensionCount: this.extensions.size,
49671
+ standaloneCount,
49672
+ mergedCount
49673
+ };
50466
49674
  }
50467
49675
  };
50468
- var completionDomain = {
50469
- name: "completion",
50470
- description: "Generate shell completion scripts for bash, zsh, and fish shells. Enables tab-completion for xcsh commands, domains, actions, and flags in your preferred shell environment.",
50471
- descriptionShort: "Shell completion script generation",
50472
- descriptionMedium: "Generate tab-completion scripts for bash, zsh, and fish shells to enhance the xcsh command-line experience.",
50473
- commands: /* @__PURE__ */ new Map([
50474
- ["bash", bashCommand],
50475
- ["zsh", zshCommand],
50476
- ["fish", fishCommand]
50477
- ]),
50478
- subcommands: /* @__PURE__ */ new Map()
50479
- };
49676
+ var extensionRegistry = new ExtensionRegistry();
50480
49677
 
50481
- // src/domains/index.ts
50482
- customDomains.register(loginDomain);
50483
- customDomains.register(cloudstatusDomain);
50484
- customDomains.register(completionDomain);
50485
- var domainAliases = /* @__PURE__ */ new Map();
50486
- for (const alias of cloudstatusAliases) {
50487
- domainAliases.set(alias, "cloudstatus");
50488
- }
50489
- function resolveDomainAlias(name) {
50490
- return domainAliases.get(name) ?? name;
50491
- }
50492
- function isCustomDomain(name) {
50493
- const canonical = resolveDomainAlias(name);
50494
- return customDomains.has(canonical);
49678
+ // src/extensions/index.ts
49679
+ function initializeExtensions() {
50495
49680
  }
49681
+ initializeExtensions();
50496
49682
 
50497
49683
  // src/repl/completion/cache.ts
50498
49684
  var DEFAULT_TTL_MS = 5 * 60 * 1e3;
@@ -50712,7 +49898,25 @@ var Completer = class {
50712
49898
  }
50713
49899
  let suggestions;
50714
49900
  if (parsed.isEscapedToRoot) {
50715
- suggestions = this.getRootContextSuggestions();
49901
+ const firstArg2 = parsed.args[0];
49902
+ if (parsed.args.length > 0 && firstArg2) {
49903
+ const targetDomain = firstArg2.toLowerCase();
49904
+ if (completionRegistry.has(targetDomain)) {
49905
+ const domainNode = completionRegistry.get(targetDomain);
49906
+ if (domainNode?.source === "api") {
49907
+ suggestions = this.getActionSuggestions();
49908
+ } else {
49909
+ suggestions = completionRegistry.getChildSuggestions(
49910
+ targetDomain,
49911
+ parsed.currentWord
49912
+ );
49913
+ }
49914
+ } else {
49915
+ suggestions = this.getRootContextSuggestions();
49916
+ }
49917
+ } else {
49918
+ suggestions = this.getRootContextSuggestions();
49919
+ }
50716
49920
  } else {
50717
49921
  suggestions = await this.getContextualSuggestions();
50718
49922
  }
@@ -50723,53 +49927,36 @@ var Completer = class {
50723
49927
  }
50724
49928
  /**
50725
49929
  * Get completions for custom domain commands
49930
+ * Uses unified completion registry for structure navigation,
49931
+ * falls back to domain handlers for argument completions
50726
49932
  */
50727
49933
  async getCustomDomainCompletions(domainName, args, currentWord) {
50728
- const domain = customDomains.get(domainName);
50729
- if (!domain) {
49934
+ const domainNode = completionRegistry.get(domainName);
49935
+ if (!domainNode) {
50730
49936
  return [];
50731
49937
  }
50732
- const suggestions = [];
50733
49938
  if (args.length === 0 || args.length === 1 && currentWord === args[0]) {
50734
- for (const [name, group] of domain.subcommands) {
50735
- if (!currentWord || name.toLowerCase().startsWith(currentWord.toLowerCase())) {
50736
- suggestions.push({
50737
- text: name,
50738
- description: group.description,
50739
- category: "subcommand"
50740
- });
50741
- }
50742
- }
50743
- for (const [name, cmd] of domain.commands) {
50744
- if (!currentWord || name.toLowerCase().startsWith(currentWord.toLowerCase())) {
50745
- suggestions.push({
50746
- text: name,
50747
- description: cmd.description,
50748
- category: "command"
50749
- });
50750
- }
50751
- }
50752
- return suggestions;
49939
+ return completionRegistry.getChildSuggestions(
49940
+ domainName,
49941
+ currentWord
49942
+ );
50753
49943
  }
50754
49944
  const subgroupName = args[0]?.toLowerCase() ?? "";
50755
- const subgroup = domain.subcommands.get(subgroupName);
50756
- if (subgroup) {
49945
+ const subgroupNode = domainNode.children?.get(subgroupName);
49946
+ if (subgroupNode?.children) {
50757
49947
  if (args.length === 1 || args.length === 2 && currentWord === args[1]) {
50758
- const cmdPrefix = args.length === 2 ? currentWord : "";
50759
- for (const [name, cmd] of subgroup.commands) {
50760
- if (!cmdPrefix || name.toLowerCase().startsWith(cmdPrefix.toLowerCase())) {
50761
- suggestions.push({
50762
- text: name,
50763
- description: cmd.description,
50764
- category: "command"
50765
- });
50766
- }
50767
- }
50768
- return suggestions;
49948
+ const prefix = args.length === 2 ? currentWord : "";
49949
+ return completionRegistry.getNestedChildSuggestions(
49950
+ domainName,
49951
+ [subgroupName],
49952
+ prefix
49953
+ );
50769
49954
  }
50770
49955
  if (args.length >= 2 && this.session) {
50771
49956
  const cmdName = args[1]?.toLowerCase() ?? "";
50772
- const cmd = subgroup.commands.get(cmdName);
49957
+ const domain2 = customDomains.get(domainName);
49958
+ const subgroup = domain2?.subcommands.get(subgroupName);
49959
+ const cmd = subgroup?.commands.get(cmdName);
50773
49960
  if (cmd?.completion) {
50774
49961
  try {
50775
49962
  const completions = await cmd.completion(
@@ -50788,8 +49975,9 @@ var Completer = class {
50788
49975
  }
50789
49976
  }
50790
49977
  const directCmdName = args[0]?.toLowerCase() ?? "";
50791
- const directCmd = domain.commands.get(directCmdName);
50792
- if (directCmd?.completion) {
49978
+ const domain = customDomains.get(domainName);
49979
+ const directCmd = domain?.commands.get(directCmdName);
49980
+ if (directCmd?.completion && this.session) {
50793
49981
  try {
50794
49982
  const completions = await directCmd.completion(
50795
49983
  currentWord,
@@ -50804,7 +49992,7 @@ var Completer = class {
50804
49992
  } catch {
50805
49993
  }
50806
49994
  }
50807
- return suggestions;
49995
+ return [];
50808
49996
  }
50809
49997
  /**
50810
49998
  * Get suggestions based on current navigation context
@@ -50980,95 +50168,23 @@ var Completer = class {
50980
50168
  return suggestions;
50981
50169
  }
50982
50170
  /**
50983
- * Get domain suggestions from registry
50171
+ * Get domain suggestions from unified registry
50984
50172
  */
50985
50173
  getDomainSuggestions() {
50986
- const suggestions = [];
50987
- const addedDomains = /* @__PURE__ */ new Set();
50988
- for (const domain of customDomains.all()) {
50989
- suggestions.push({
50990
- text: domain.name,
50991
- description: domain.description,
50992
- category: "domain"
50993
- });
50994
- addedDomains.add(domain.name);
50995
- }
50996
- for (const extDomain of extensionRegistry.getExtendedDomains()) {
50997
- if (addedDomains.has(extDomain)) continue;
50998
- if (domainRegistry.has(extDomain)) continue;
50999
- const merged = extensionRegistry.getMergedDomain(extDomain);
51000
- if (merged && merged.source === "extension") {
51001
- suggestions.push({
51002
- text: extDomain,
51003
- description: merged.description,
51004
- category: "domain"
51005
- });
51006
- addedDomains.add(extDomain);
51007
- }
51008
- }
51009
- for (const [domain, meta] of domainRegistry) {
51010
- if (addedDomains.has(domain)) continue;
51011
- suggestions.push({
51012
- text: domain,
51013
- description: meta.descriptionShort,
51014
- category: "domain"
51015
- });
51016
- addedDomains.add(domain);
51017
- }
51018
- return suggestions;
50174
+ return completionRegistry.getDomainSuggestions();
51019
50175
  }
51020
50176
  /**
51021
- * Get action suggestions
50177
+ * Get action suggestions from unified registry
51022
50178
  */
51023
50179
  getActionSuggestions() {
51024
- return [
51025
- { text: "list", description: "List resources", category: "action" },
51026
- {
51027
- text: "get",
51028
- description: "Get a specific resource",
51029
- category: "action"
51030
- },
51031
- {
51032
- text: "create",
51033
- description: "Create a new resource",
51034
- category: "action"
51035
- },
51036
- {
51037
- text: "delete",
51038
- description: "Delete a resource",
51039
- category: "action"
51040
- },
51041
- {
51042
- text: "replace",
51043
- description: "Replace a resource",
51044
- category: "action"
51045
- },
51046
- {
51047
- text: "apply",
51048
- description: "Apply configuration from file",
50180
+ const actionDescriptions2 = getActionDescriptions();
50181
+ return Object.entries(actionDescriptions2).map(
50182
+ ([action, description]) => ({
50183
+ text: action,
50184
+ description,
51049
50185
  category: "action"
51050
- },
51051
- {
51052
- text: "status",
51053
- description: "Get resource status",
51054
- category: "action"
51055
- },
51056
- {
51057
- text: "patch",
51058
- description: "Patch a resource",
51059
- category: "action"
51060
- },
51061
- {
51062
- text: "add-labels",
51063
- description: "Add labels to a resource",
51064
- category: "action"
51065
- },
51066
- {
51067
- text: "remove-labels",
51068
- description: "Remove labels from a resource",
51069
- category: "action"
51070
- }
51071
- ];
50186
+ })
50187
+ );
51072
50188
  }
51073
50189
  /**
51074
50190
  * Get flag suggestions for current action
@@ -51682,7 +50798,7 @@ function formatDomainHelp(domain) {
51682
50798
  output.push(` ${CLI_NAME} ${domain.name} <action> [options]`);
51683
50799
  output.push("");
51684
50800
  output.push("ACTIONS");
51685
- const actionDescriptions = {
50801
+ const actionDescriptions2 = {
51686
50802
  list: "List resources",
51687
50803
  get: "Get a specific resource by name",
51688
50804
  create: "Create a new resource",
@@ -51695,7 +50811,7 @@ function formatDomainHelp(domain) {
51695
50811
  "remove-labels": "Remove labels from a resource"
51696
50812
  };
51697
50813
  for (const action of validActions) {
51698
- const desc = actionDescriptions[action] ?? action;
50814
+ const desc = actionDescriptions2[action] ?? action;
51699
50815
  output.push(` ${action.padEnd(16)} ${desc}`);
51700
50816
  }
51701
50817
  output.push("");
@@ -51731,7 +50847,7 @@ function formatDomainHelp(domain) {
51731
50847
  function formatActionHelp(domainName, action) {
51732
50848
  const domain = getDomainInfo(domainName);
51733
50849
  const displayDomain = domain?.displayName ?? domainName;
51734
- const actionDescriptions = {
50850
+ const actionDescriptions2 = {
51735
50851
  list: {
51736
50852
  desc: "List all resources in the namespace",
51737
50853
  usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
@@ -51773,7 +50889,7 @@ function formatActionHelp(domainName, action) {
51773
50889
  usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
51774
50890
  }
51775
50891
  };
51776
- const actionInfo = actionDescriptions[action] ?? {
50892
+ const actionInfo = actionDescriptions2[action] ?? {
51777
50893
  desc: `Execute ${action} operation`,
51778
50894
  usage: `${CLI_NAME} ${domainName} ${action} [options]`
51779
50895
  };