@robinmordasiewicz/f5xc-xcsh 6.29.0 → 6.31.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/completions/_xcsh +37 -38
- package/completions/xcsh.bash +9 -9
- package/completions/xcsh.fish +393 -434
- package/dist/index.js +960 -1844
- package/package.json +1 -1
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: "
|
|
44112
|
-
descriptionShort: "
|
|
44113
|
-
descriptionMedium: "
|
|
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: "
|
|
44126
|
-
descriptionShort: "
|
|
44127
|
-
descriptionMedium: "
|
|
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: "
|
|
44204
|
-
descriptionShort: "
|
|
44205
|
-
descriptionMedium: "
|
|
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: "
|
|
44218
|
-
descriptionShort: "
|
|
44219
|
-
descriptionMedium: "
|
|
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: "
|
|
44232
|
-
descriptionShort: "
|
|
44233
|
-
descriptionMedium: "
|
|
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: "
|
|
44246
|
-
descriptionShort: "
|
|
44247
|
-
descriptionMedium: "
|
|
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
|
|
44260
|
-
descriptionShort: "Configure
|
|
44261
|
-
descriptionMedium: "
|
|
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: "
|
|
44274
|
-
descriptionShort: "Customer Edge
|
|
44275
|
-
descriptionMedium: "
|
|
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: "
|
|
44288
|
-
descriptionShort: "SSL/TLS certificate and trusted
|
|
44289
|
-
descriptionMedium: "
|
|
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
|
|
44302
|
-
descriptionShort: "
|
|
44303
|
-
descriptionMedium: "
|
|
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: "
|
|
44316
|
-
descriptionShort: "
|
|
44317
|
-
descriptionMedium: "
|
|
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: "
|
|
44330
|
-
descriptionShort: "
|
|
44331
|
-
descriptionMedium: "
|
|
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: "
|
|
44358
|
-
descriptionShort: "
|
|
44359
|
-
descriptionMedium: "
|
|
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: "
|
|
44372
|
-
descriptionShort: "Manage zones, records, and
|
|
44373
|
-
descriptionMedium: "Configure
|
|
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: "
|
|
44436
|
-
descriptionShort: "AI
|
|
44437
|
-
descriptionMedium: "
|
|
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: "
|
|
44450
|
-
descriptionShort: "Kubernetes RBAC
|
|
44451
|
-
descriptionMedium: "
|
|
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: "
|
|
44464
|
-
descriptionShort: "
|
|
44465
|
-
descriptionMedium: "
|
|
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: "
|
|
44478
|
-
descriptionShort: "BGP routing,
|
|
44479
|
-
descriptionMedium: "
|
|
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: "
|
|
44492
|
-
descriptionShort: "
|
|
44493
|
-
descriptionMedium: "
|
|
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: "
|
|
44506
|
-
descriptionShort: "NGINX
|
|
44507
|
-
descriptionMedium: "Manage
|
|
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: "
|
|
44520
|
-
descriptionShort: "
|
|
44521
|
-
descriptionMedium: "
|
|
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: "
|
|
44534
|
-
descriptionShort: "
|
|
44535
|
-
descriptionMedium: "
|
|
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: "
|
|
44548
|
-
descriptionShort: "
|
|
44549
|
-
descriptionMedium: "
|
|
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: "
|
|
44562
|
-
descriptionShort: "
|
|
44563
|
-
descriptionMedium: "
|
|
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: "
|
|
44576
|
-
descriptionShort: "
|
|
44577
|
-
descriptionMedium: "
|
|
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: "
|
|
44590
|
-
descriptionShort: "
|
|
44591
|
-
descriptionMedium: "
|
|
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: "
|
|
44604
|
-
descriptionShort: "
|
|
44605
|
-
descriptionMedium: "
|
|
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: "
|
|
44670
|
-
descriptionShort: "
|
|
44671
|
-
descriptionMedium: "
|
|
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: "
|
|
44684
|
-
descriptionShort: "
|
|
44685
|
-
descriptionMedium: "
|
|
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: "
|
|
44712
|
-
descriptionShort: "
|
|
44713
|
-
descriptionMedium: "
|
|
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: "
|
|
44740
|
-
descriptionShort: "
|
|
44741
|
-
descriptionMedium: "
|
|
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: "
|
|
44754
|
-
descriptionShort: "Configure
|
|
44755
|
-
descriptionMedium: "
|
|
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.
|
|
45313
|
-
return "6.
|
|
45312
|
+
if ("6.31.0") {
|
|
45313
|
+
return "6.31.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
|
-
*
|
|
45784
|
+
* Check if an error is retryable
|
|
45738
45785
|
*/
|
|
45739
|
-
|
|
45740
|
-
|
|
45741
|
-
|
|
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
|
-
|
|
45750
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
47744
|
-
if (!
|
|
47745
|
-
return errorResult(
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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/
|
|
49447
|
-
var
|
|
49448
|
-
|
|
49449
|
-
|
|
49450
|
-
|
|
49451
|
-
|
|
49452
|
-
|
|
49453
|
-
|
|
49454
|
-
|
|
49455
|
-
|
|
49456
|
-
|
|
49457
|
-
|
|
49458
|
-
|
|
49459
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
49487
|
-
|
|
49488
|
-
|
|
49489
|
-
|
|
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
|
|
49493
|
-
*
|
|
49494
|
-
* @param domain - Canonical domain name
|
|
49495
|
-
* @returns Extension if registered, undefined otherwise
|
|
48848
|
+
* Get the complete completion tree
|
|
49496
48849
|
*/
|
|
49497
|
-
|
|
49498
|
-
return this.
|
|
48850
|
+
getTree() {
|
|
48851
|
+
return this.tree;
|
|
49499
48852
|
}
|
|
49500
48853
|
/**
|
|
49501
|
-
*
|
|
48854
|
+
* Get all registered domains as array (sorted by name)
|
|
49502
48855
|
*/
|
|
49503
|
-
|
|
49504
|
-
return this.
|
|
48856
|
+
getDomains() {
|
|
48857
|
+
return Array.from(this.tree.values()).sort(
|
|
48858
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
48859
|
+
);
|
|
49505
48860
|
}
|
|
49506
48861
|
/**
|
|
49507
|
-
*
|
|
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
|
-
|
|
49518
|
-
|
|
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
|
-
*
|
|
48868
|
+
* Get a domain by name or alias
|
|
49534
48869
|
*/
|
|
49535
|
-
|
|
49536
|
-
const
|
|
49537
|
-
|
|
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
|
-
*
|
|
48875
|
+
* Check if a domain exists (by name or alias)
|
|
49565
48876
|
*/
|
|
49566
|
-
|
|
49567
|
-
|
|
48877
|
+
has(nameOrAlias) {
|
|
48878
|
+
const canonical = this.resolveAlias(nameOrAlias);
|
|
48879
|
+
return this.tree.has(canonical);
|
|
49568
48880
|
}
|
|
49569
48881
|
/**
|
|
49570
|
-
* Get all
|
|
48882
|
+
* Get all aliases
|
|
49571
48883
|
*/
|
|
49572
|
-
|
|
49573
|
-
return
|
|
48884
|
+
getAliases() {
|
|
48885
|
+
return new Map(this.aliases);
|
|
49574
48886
|
}
|
|
49575
48887
|
/**
|
|
49576
|
-
* Get
|
|
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
|
-
|
|
49584
|
-
const
|
|
49585
|
-
|
|
49586
|
-
|
|
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
|
-
|
|
49589
|
-
|
|
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
|
|
49592
|
-
|
|
49593
|
-
|
|
49594
|
-
if (
|
|
49595
|
-
|
|
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
|
|
48936
|
+
return suggestions.sort((a, b) => a.text.localeCompare(b.text));
|
|
49599
48937
|
}
|
|
49600
48938
|
/**
|
|
49601
|
-
* Get
|
|
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
|
-
|
|
49608
|
-
|
|
49609
|
-
if (!
|
|
49610
|
-
return
|
|
48941
|
+
getNestedChildSuggestions(domainName, path, prefix = "") {
|
|
48942
|
+
let node = this.get(domainName);
|
|
48943
|
+
if (!node) {
|
|
48944
|
+
return [];
|
|
49611
48945
|
}
|
|
49612
|
-
const
|
|
49613
|
-
|
|
49614
|
-
|
|
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
|
-
|
|
49617
|
-
|
|
49618
|
-
|
|
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
|
|
48971
|
+
return suggestions.sort((a, b) => a.text.localeCompare(b.text));
|
|
49622
48972
|
}
|
|
49623
48973
|
/**
|
|
49624
|
-
*
|
|
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
|
-
|
|
49631
|
-
|
|
49632
|
-
|
|
48976
|
+
clear() {
|
|
48977
|
+
this.tree.clear();
|
|
48978
|
+
this.aliases.clear();
|
|
49633
48979
|
}
|
|
49634
|
-
|
|
49635
|
-
|
|
49636
|
-
|
|
49637
|
-
|
|
49638
|
-
|
|
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
|
-
|
|
49699
|
-
|
|
49700
|
-
|
|
49701
|
-
|
|
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
|
-
|
|
49756
|
-
|
|
49757
|
-
|
|
49758
|
-
|
|
49759
|
-
|
|
49760
|
-
|
|
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
|
-
|
|
49855
|
-
|
|
49856
|
-
|
|
49857
|
-
|
|
49858
|
-
|
|
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
|
-
|
|
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
|
-
|
|
50024
|
-
|
|
50025
|
-
|
|
50026
|
-
|
|
50027
|
-
|
|
50028
|
-
|
|
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
|
-
|
|
50091
|
-
|
|
50092
|
-
|
|
50093
|
-
|
|
50094
|
-
|
|
50095
|
-
|
|
50096
|
-
|
|
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
|
-
|
|
50118
|
-
|
|
50119
|
-
|
|
50120
|
-
|
|
50121
|
-
|
|
50122
|
-
|
|
50123
|
-
|
|
50124
|
-
|
|
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
|
|
49058
|
+
return node;
|
|
50127
49059
|
}
|
|
50128
|
-
function
|
|
50129
|
-
|
|
50130
|
-
|
|
50131
|
-
|
|
50132
|
-
|
|
50133
|
-
|
|
50134
|
-
|
|
50135
|
-
|
|
50136
|
-
|
|
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 =
|
|
49074
|
+
const domains = completionRegistry.getDomains();
|
|
50142
49075
|
const domainNames = domains.map((d) => d.name).join(" ");
|
|
50143
|
-
const allAliases =
|
|
50144
|
-
const
|
|
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
|
|
50147
|
-
|
|
50148
|
-
|
|
50149
|
-
|
|
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 "${
|
|
49091
|
+
` COMPREPLY=($(compgen -W "${childNames.join(" ")}" -- "\${cur}"))`,
|
|
50156
49092
|
` return 0`,
|
|
50157
49093
|
` ;;`
|
|
50158
49094
|
);
|
|
50159
49095
|
}
|
|
50160
|
-
for (const [
|
|
50161
|
-
|
|
50162
|
-
|
|
50163
|
-
|
|
50164
|
-
|
|
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 =
|
|
49162
|
+
const domains = completionRegistry.getDomains();
|
|
50224
49163
|
const domainDescriptions = domains.map((d) => {
|
|
50225
|
-
const escaped = d.description
|
|
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
|
|
50230
|
-
|
|
50231
|
-
|
|
50232
|
-
|
|
50233
|
-
|
|
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
|
|
50243
|
-
|
|
50244
|
-
|
|
50245
|
-
|
|
50246
|
-
const
|
|
50247
|
-
|
|
50248
|
-
|
|
50249
|
-
|
|
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' ${
|
|
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
|
-
${
|
|
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 =
|
|
50340
|
-
const
|
|
49271
|
+
const domains = completionRegistry.getDomains();
|
|
49272
|
+
const actionDescriptions2 = getActionDescriptions();
|
|
50341
49273
|
const domainCompletions = domains.map((d) => {
|
|
50342
|
-
const escaped = d.description
|
|
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
|
|
50357
|
-
|
|
50358
|
-
for (const
|
|
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 "${
|
|
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
|
-
|
|
50364
|
-
|
|
50365
|
-
|
|
50366
|
-
|
|
50367
|
-
|
|
50368
|
-
|
|
50369
|
-
|
|
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
|
-
|
|
50375
|
-
|
|
50376
|
-
|
|
50377
|
-
|
|
50378
|
-
|
|
50379
|
-
|
|
50380
|
-
|
|
50381
|
-
|
|
50382
|
-
|
|
50383
|
-
|
|
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
|
-
|
|
50437
|
-
|
|
50438
|
-
|
|
50439
|
-
|
|
50440
|
-
|
|
50441
|
-
|
|
50442
|
-
|
|
50443
|
-
|
|
50444
|
-
|
|
50445
|
-
|
|
50446
|
-
|
|
50447
|
-
|
|
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
|
-
|
|
50453
|
-
|
|
50454
|
-
|
|
50455
|
-
|
|
50456
|
-
|
|
50457
|
-
|
|
50458
|
-
|
|
50459
|
-
|
|
50460
|
-
|
|
50461
|
-
|
|
50462
|
-
|
|
50463
|
-
|
|
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
|
|
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/
|
|
50482
|
-
|
|
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
|
-
|
|
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
|
|
50729
|
-
if (!
|
|
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
|
-
|
|
50735
|
-
|
|
50736
|
-
|
|
50737
|
-
|
|
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
|
|
50756
|
-
if (
|
|
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
|
|
50759
|
-
|
|
50760
|
-
|
|
50761
|
-
|
|
50762
|
-
|
|
50763
|
-
|
|
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
|
|
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
|
|
50792
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
51025
|
-
|
|
51026
|
-
{
|
|
51027
|
-
text:
|
|
51028
|
-
description
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
50892
|
+
const actionInfo = actionDescriptions2[action] ?? {
|
|
51777
50893
|
desc: `Execute ${action} operation`,
|
|
51778
50894
|
usage: `${CLI_NAME} ${domainName} ${action} [options]`
|
|
51779
50895
|
};
|