@robinmordasiewicz/f5xc-xcsh 6.28.0 → 6.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/completions/_xcsh +60 -39
- package/completions/xcsh.bash +9 -9
- package/completions/xcsh.fish +416 -435
- package/dist/index.js +1119 -1895
- package/package.json +4 -2
package/dist/index.js
CHANGED
|
@@ -44108,23 +44108,23 @@ 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: "
|
|
44114
|
-
aliases: [],
|
|
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
|
+
aliases: ["console-ui", "ui-assets", "static-components"],
|
|
44115
44115
|
complexity: "simple",
|
|
44116
44116
|
isPreview: false,
|
|
44117
44117
|
requiresTier: "Standard",
|
|
44118
|
-
category: "
|
|
44119
|
-
useCases: [],
|
|
44120
|
-
relatedDomains: []
|
|
44118
|
+
category: "Platform",
|
|
44119
|
+
useCases: ["Manage static UI components for admin console", "Deploy and retrieve UI assets within namespaces", "Configure console interface elements", "Manage custom UI component metadata"],
|
|
44120
|
+
relatedDomains: ["admin", "system"]
|
|
44121
44121
|
}],
|
|
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,23 +44214,23 @@ 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: "
|
|
44220
|
-
aliases: [],
|
|
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
|
+
aliases: ["billing-usage", "quotas", "usage-tracking"],
|
|
44221
44221
|
complexity: "moderate",
|
|
44222
44222
|
isPreview: false,
|
|
44223
44223
|
requiresTier: "Standard",
|
|
44224
|
-
category: "
|
|
44225
|
-
useCases: [],
|
|
44226
|
-
relatedDomains: []
|
|
44224
|
+
category: "Platform",
|
|
44225
|
+
useCases: ["Manage subscription plans and billing transitions", "Configure payment methods and invoices", "Track resource quota usage across namespaces", "Monitor usage limits and capacity"],
|
|
44226
|
+
relatedDomains: ["system", "users"]
|
|
44227
44227
|
}],
|
|
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,23 +44242,23 @@ 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: "
|
|
44248
|
-
aliases: [],
|
|
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
|
+
aliases: ["threat-defense", "tpm", "shape-bot"],
|
|
44249
44249
|
complexity: "moderate",
|
|
44250
44250
|
isPreview: false,
|
|
44251
|
-
requiresTier: "
|
|
44252
|
-
category: "
|
|
44253
|
-
useCases: [],
|
|
44254
|
-
relatedDomains: []
|
|
44251
|
+
requiresTier: "Advanced",
|
|
44252
|
+
category: "Security",
|
|
44253
|
+
useCases: ["Configure bot defense instances per namespace", "Manage TPM threat categories for classification", "Provision API keys for automated defense systems", "Integrate threat intelligence services"],
|
|
44254
|
+
relatedDomains: ["bot_defense", "shape", "waf"]
|
|
44255
44255
|
}],
|
|
44256
44256
|
["cdn", {
|
|
44257
44257
|
name: "cdn",
|
|
44258
44258
|
displayName: "Cdn",
|
|
44259
|
-
description: "Create cache rules with
|
|
44260
|
-
descriptionShort: "Configure
|
|
44261
|
-
descriptionMedium: "Define cache
|
|
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,23 +44270,23 @@ 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: "
|
|
44276
|
-
aliases: [],
|
|
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
|
+
aliases: ["ce-mgmt", "edge-management", "ce-lifecycle"],
|
|
44277
44277
|
complexity: "advanced",
|
|
44278
44278
|
isPreview: false,
|
|
44279
44279
|
requiresTier: "Standard",
|
|
44280
|
-
category: "
|
|
44281
|
-
useCases: [],
|
|
44282
|
-
relatedDomains: []
|
|
44280
|
+
category: "Infrastructure",
|
|
44281
|
+
useCases: ["Manage Customer Edge site lifecycle", "Configure network interfaces and fleet settings", "Handle site registration and token workflows", "Execute site upgrades with pre-upgrade checks"],
|
|
44282
|
+
relatedDomains: ["customer_edge", "sites"]
|
|
44283
44283
|
}],
|
|
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,16 +44326,16 @@ 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: "
|
|
44332
|
-
aliases: [],
|
|
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
|
+
aliases: ["data-privacy", "pii", "sensitive-data", "lma"],
|
|
44333
44333
|
complexity: "simple",
|
|
44334
44334
|
isPreview: false,
|
|
44335
|
-
requiresTier: "
|
|
44336
|
-
category: "
|
|
44337
|
-
useCases: [],
|
|
44338
|
-
relatedDomains: []
|
|
44335
|
+
requiresTier: "Advanced",
|
|
44336
|
+
category: "Security",
|
|
44337
|
+
useCases: ["Configure sensitive data detection policies", "Define custom data types for PII classification", "Manage LMA region configurations", "Integrate geo-configurations for compliance"],
|
|
44338
|
+
relatedDomains: ["blindfold", "client_side_defense"]
|
|
44339
44339
|
}],
|
|
44340
44340
|
["data_intelligence", {
|
|
44341
44341
|
name: "data_intelligence",
|
|
@@ -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,23 +44558,23 @@ 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: "
|
|
44564
|
-
aliases: [],
|
|
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
|
+
aliases: ["secops", "incident-response", "mitigation"],
|
|
44565
44565
|
complexity: "simple",
|
|
44566
44566
|
isPreview: false,
|
|
44567
|
-
requiresTier: "
|
|
44568
|
-
category: "
|
|
44569
|
-
useCases: [],
|
|
44570
|
-
relatedDomains: []
|
|
44567
|
+
requiresTier: "Advanced",
|
|
44568
|
+
category: "Security",
|
|
44569
|
+
useCases: ["Configure automated threat mitigation policies", "Define rules for malicious user detection", "Manage incident response workflows", "Apply blocking or rate limiting to threats"],
|
|
44570
|
+
relatedDomains: ["bot_defense", "waf", "network_security"]
|
|
44571
44571
|
}],
|
|
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,16 +44708,16 @@ 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: "
|
|
44714
|
-
aliases: [],
|
|
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
|
+
aliases: ["tenant-identity", "idm", "user-settings"],
|
|
44715
44715
|
complexity: "advanced",
|
|
44716
44716
|
isPreview: false,
|
|
44717
44717
|
requiresTier: "Standard",
|
|
44718
|
-
category: "
|
|
44719
|
-
useCases: [],
|
|
44720
|
-
relatedDomains: []
|
|
44718
|
+
category: "Platform",
|
|
44719
|
+
useCases: ["Manage user profiles and notification preferences", "Configure session controls and OTP settings", "Handle identity management operations", "Process initial user access requests"],
|
|
44720
|
+
relatedDomains: ["users", "authentication", "system"]
|
|
44721
44721
|
}],
|
|
44722
44722
|
["threat_campaign", {
|
|
44723
44723
|
name: "threat_campaign",
|
|
@@ -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,
|
|
@@ -44830,9 +44830,9 @@ var generatedDomains = /* @__PURE__ */ new Map([
|
|
|
44830
44830
|
["waf", {
|
|
44831
44831
|
name: "waf",
|
|
44832
44832
|
displayName: "Waf",
|
|
44833
|
-
description: "
|
|
44834
|
-
descriptionShort: "Configure
|
|
44835
|
-
descriptionMedium: "
|
|
44833
|
+
description: "Set up firewall configurations with attack type settings and violation detection. Create exclusion policies to tune false positives and customize blocking responses. Deploy staged signatures before production release and monitor rule hits through security event metrics. Integrate with virtual hosts for layered protection using AI-based risk blocking and anonymization settings for sensitive data handling.",
|
|
44834
|
+
descriptionShort: "Configure application firewall rules and bot protection",
|
|
44835
|
+
descriptionMedium: "Define security policies for web applications. Manage attack signatures, exclusion rules, and threat detection settings.",
|
|
44836
44836
|
aliases: ["firewall", "appfw"],
|
|
44837
44837
|
complexity: "advanced",
|
|
44838
44838
|
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.30.0") {
|
|
45313
|
+
return "6.30.0";
|
|
45314
45314
|
}
|
|
45315
45315
|
if (process.env.XCSH_VERSION) {
|
|
45316
45316
|
return process.env.XCSH_VERSION;
|
|
@@ -45579,6 +45579,15 @@ var ProfileManager = class {
|
|
|
45579
45579
|
return null;
|
|
45580
45580
|
}
|
|
45581
45581
|
}
|
|
45582
|
+
/**
|
|
45583
|
+
* Clear the active profile (used for force delete)
|
|
45584
|
+
*/
|
|
45585
|
+
async clearActive() {
|
|
45586
|
+
try {
|
|
45587
|
+
await fs2.unlink(this.config.activeProfileFile);
|
|
45588
|
+
} catch {
|
|
45589
|
+
}
|
|
45590
|
+
}
|
|
45582
45591
|
/**
|
|
45583
45592
|
* Set the active profile
|
|
45584
45593
|
*/
|
|
@@ -45694,16 +45703,54 @@ var APIError = class extends Error {
|
|
|
45694
45703
|
};
|
|
45695
45704
|
|
|
45696
45705
|
// src/api/client.ts
|
|
45706
|
+
var DEFAULT_RETRY_CONFIG = {
|
|
45707
|
+
maxRetries: 3,
|
|
45708
|
+
initialDelayMs: 1e3,
|
|
45709
|
+
maxDelayMs: 1e4,
|
|
45710
|
+
backoffMultiplier: 2,
|
|
45711
|
+
jitter: true
|
|
45712
|
+
};
|
|
45713
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([
|
|
45714
|
+
408,
|
|
45715
|
+
// Request Timeout
|
|
45716
|
+
429,
|
|
45717
|
+
// Too Many Requests
|
|
45718
|
+
500,
|
|
45719
|
+
// Internal Server Error
|
|
45720
|
+
502,
|
|
45721
|
+
// Bad Gateway
|
|
45722
|
+
503,
|
|
45723
|
+
// Service Unavailable
|
|
45724
|
+
504
|
|
45725
|
+
// Gateway Timeout
|
|
45726
|
+
]);
|
|
45727
|
+
function calculateBackoffDelay(attempt, config) {
|
|
45728
|
+
const exponentialDelay = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
45729
|
+
const cappedDelay = Math.min(exponentialDelay, config.maxDelayMs);
|
|
45730
|
+
if (config.jitter) {
|
|
45731
|
+
const jitterFactor = 1 + Math.random() * 0.25;
|
|
45732
|
+
return Math.floor(cappedDelay * jitterFactor);
|
|
45733
|
+
}
|
|
45734
|
+
return cappedDelay;
|
|
45735
|
+
}
|
|
45736
|
+
function sleep(ms) {
|
|
45737
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
45738
|
+
}
|
|
45697
45739
|
var APIClient = class {
|
|
45698
45740
|
serverUrl;
|
|
45699
45741
|
apiToken;
|
|
45700
45742
|
timeout;
|
|
45701
45743
|
debug;
|
|
45744
|
+
retryConfig;
|
|
45702
45745
|
constructor(config) {
|
|
45703
45746
|
this.serverUrl = config.serverUrl.replace(/\/+$/, "");
|
|
45704
45747
|
this.apiToken = config.apiToken ?? "";
|
|
45705
45748
|
this.timeout = config.timeout ?? 3e4;
|
|
45706
45749
|
this.debug = config.debug ?? false;
|
|
45750
|
+
this.retryConfig = {
|
|
45751
|
+
...DEFAULT_RETRY_CONFIG,
|
|
45752
|
+
...config.retry
|
|
45753
|
+
};
|
|
45707
45754
|
}
|
|
45708
45755
|
/**
|
|
45709
45756
|
* Check if client has authentication configured
|
|
@@ -45734,25 +45781,21 @@ var APIClient = class {
|
|
|
45734
45781
|
return url;
|
|
45735
45782
|
}
|
|
45736
45783
|
/**
|
|
45737
|
-
*
|
|
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,24 +46031,12 @@ var REPLSession = class {
|
|
|
46548
46031
|
await this.loadActiveProfile();
|
|
46549
46032
|
if (this._apiClient?.isAuthenticated()) {
|
|
46550
46033
|
await this.fetchUserInfo();
|
|
46551
|
-
await this.fetchTier();
|
|
46552
46034
|
}
|
|
46553
46035
|
}
|
|
46554
46036
|
/**
|
|
46555
|
-
* Fetch
|
|
46037
|
+
* Fetch user info from the API
|
|
46556
46038
|
*/
|
|
46557
|
-
async
|
|
46558
|
-
if (!this._apiClient) return;
|
|
46559
|
-
try {
|
|
46560
|
-
const subscriptionClient = new SubscriptionClient(this._apiClient);
|
|
46561
|
-
this._tier = await subscriptionClient.getTierFromCurrentPlan();
|
|
46562
|
-
} catch {
|
|
46563
|
-
}
|
|
46564
|
-
}
|
|
46565
|
-
/**
|
|
46566
|
-
* Fetch user info from the API
|
|
46567
|
-
*/
|
|
46568
|
-
async fetchUserInfo() {
|
|
46039
|
+
async fetchUserInfo() {
|
|
46569
46040
|
if (!this._apiClient) return;
|
|
46570
46041
|
try {
|
|
46571
46042
|
const response = await this._apiClient.get("/api/web/custom/user/info");
|
|
@@ -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
|
*/
|
|
@@ -47366,6 +46860,9 @@ var DomainRegistry = class {
|
|
|
47366
46860
|
const subgroup = domain.subcommands.get(firstArg);
|
|
47367
46861
|
if (subgroup) {
|
|
47368
46862
|
if (restArgs.length === 0) {
|
|
46863
|
+
if (subgroup.defaultCommand) {
|
|
46864
|
+
return subgroup.defaultCommand.execute([], session);
|
|
46865
|
+
}
|
|
47369
46866
|
return this.showSubcommandHelp(domain, subgroup);
|
|
47370
46867
|
}
|
|
47371
46868
|
const cmdName = restArgs[0]?.toLowerCase() ?? "";
|
|
@@ -47429,7 +46926,7 @@ var DomainRegistry = class {
|
|
|
47429
46926
|
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47430
46927
|
suggestions.push({
|
|
47431
46928
|
text: name,
|
|
47432
|
-
description: group.
|
|
46929
|
+
description: group.descriptionShort,
|
|
47433
46930
|
category: "subcommand"
|
|
47434
46931
|
});
|
|
47435
46932
|
}
|
|
@@ -47438,7 +46935,7 @@ var DomainRegistry = class {
|
|
|
47438
46935
|
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47439
46936
|
suggestions.push({
|
|
47440
46937
|
text: name,
|
|
47441
|
-
description: cmd.
|
|
46938
|
+
description: cmd.descriptionShort,
|
|
47442
46939
|
category: "command"
|
|
47443
46940
|
});
|
|
47444
46941
|
}
|
|
@@ -47452,7 +46949,7 @@ var DomainRegistry = class {
|
|
|
47452
46949
|
if (name.toLowerCase().startsWith(partial.toLowerCase())) {
|
|
47453
46950
|
suggestions.push({
|
|
47454
46951
|
text: name,
|
|
47455
|
-
description: cmd.
|
|
46952
|
+
description: cmd.descriptionShort,
|
|
47456
46953
|
category: "command"
|
|
47457
46954
|
});
|
|
47458
46955
|
}
|
|
@@ -47555,7 +47052,9 @@ function rawStdoutResult(content) {
|
|
|
47555
47052
|
// src/domains/login/profile/list.ts
|
|
47556
47053
|
var listCommand = {
|
|
47557
47054
|
name: "list",
|
|
47558
|
-
description: "
|
|
47055
|
+
description: "Display all saved connection profiles with their tenant URLs and authentication types. Highlights the currently active profile for easy identification when managing multiple tenants.",
|
|
47056
|
+
descriptionShort: "List all saved profiles",
|
|
47057
|
+
descriptionMedium: "Show all profiles with tenant URLs, auth types, and active status indicator.",
|
|
47559
47058
|
aliases: ["ls"],
|
|
47560
47059
|
async execute(_args, _session) {
|
|
47561
47060
|
const manager = getProfileManager();
|
|
@@ -47590,7 +47089,9 @@ var listCommand = {
|
|
|
47590
47089
|
// src/domains/login/profile/show.ts
|
|
47591
47090
|
var showCommand = {
|
|
47592
47091
|
name: "show",
|
|
47593
|
-
description: "
|
|
47092
|
+
description: "Display detailed configuration for a specific profile. Shows tenant URL, authentication method, and namespace settings with sensitive credentials securely masked for safe viewing.",
|
|
47093
|
+
descriptionShort: "Show profile details (masked credentials)",
|
|
47094
|
+
descriptionMedium: "Display profile configuration with tenant URL, auth type, and masked credentials.",
|
|
47594
47095
|
usage: "<name>",
|
|
47595
47096
|
aliases: ["get", "view"],
|
|
47596
47097
|
async execute(args, _session) {
|
|
@@ -47646,7 +47147,9 @@ var showCommand = {
|
|
|
47646
47147
|
// src/domains/login/profile/create.ts
|
|
47647
47148
|
var createCommand2 = {
|
|
47648
47149
|
name: "create",
|
|
47649
|
-
description: "Create a new profile",
|
|
47150
|
+
description: "Create a new connection profile with tenant URL and authentication credentials. Profiles store all settings needed to connect to a tenant, including optional default namespace for scoped operations.",
|
|
47151
|
+
descriptionShort: "Create a new connection profile",
|
|
47152
|
+
descriptionMedium: "Create a profile with tenant URL, authentication token, and optional default namespace.",
|
|
47650
47153
|
usage: "<name> --url <api-url> --token <api-token> [--namespace <ns>]",
|
|
47651
47154
|
aliases: ["add", "new"],
|
|
47652
47155
|
async execute(args, _session) {
|
|
@@ -47696,10 +47199,11 @@ var createCommand2 = {
|
|
|
47696
47199
|
if (!apiToken) {
|
|
47697
47200
|
return errorResult("Missing required --token option");
|
|
47698
47201
|
}
|
|
47202
|
+
const sanitizedToken = apiToken.replace(/^["']|["']$/g, "");
|
|
47699
47203
|
const profile = {
|
|
47700
47204
|
name,
|
|
47701
47205
|
apiUrl,
|
|
47702
|
-
apiToken
|
|
47206
|
+
apiToken: sanitizedToken
|
|
47703
47207
|
};
|
|
47704
47208
|
if (defaultNamespace) {
|
|
47705
47209
|
profile.defaultNamespace = defaultNamespace;
|
|
@@ -47719,26 +47223,22 @@ var createCommand2 = {
|
|
|
47719
47223
|
// src/domains/login/profile/use.ts
|
|
47720
47224
|
var useCommand = {
|
|
47721
47225
|
name: "use",
|
|
47722
|
-
description: "Switch to a different profile",
|
|
47226
|
+
description: "Switch the active connection to a different saved profile. Updates the session context to use the new profile's tenant URL, credentials, and default namespace for all subsequent operations.",
|
|
47227
|
+
descriptionShort: "Switch to a different profile",
|
|
47228
|
+
descriptionMedium: "Activate a profile and update session context with its tenant and namespace settings.",
|
|
47723
47229
|
usage: "<name>",
|
|
47724
47230
|
aliases: ["switch", "activate"],
|
|
47725
47231
|
async execute(args, session) {
|
|
47726
|
-
const manager = getProfileManager();
|
|
47727
47232
|
const name = args[0];
|
|
47728
47233
|
if (!name) {
|
|
47729
47234
|
return errorResult("Usage: login profile use <name>");
|
|
47730
47235
|
}
|
|
47731
47236
|
try {
|
|
47732
|
-
const
|
|
47733
|
-
if (!
|
|
47734
|
-
return errorResult(
|
|
47735
|
-
}
|
|
47736
|
-
const profile = await manager.get(name);
|
|
47737
|
-
if (profile) {
|
|
47738
|
-
if (profile.defaultNamespace) {
|
|
47739
|
-
session.setNamespace(profile.defaultNamespace);
|
|
47740
|
-
}
|
|
47237
|
+
const success = await session.switchProfile(name);
|
|
47238
|
+
if (!success) {
|
|
47239
|
+
return errorResult(`Profile '${name}' not found.`);
|
|
47741
47240
|
}
|
|
47241
|
+
const profile = session.getActiveProfile();
|
|
47742
47242
|
return successResult(
|
|
47743
47243
|
[
|
|
47744
47244
|
`Switched to profile '${name}'.`,
|
|
@@ -47766,7 +47266,9 @@ var useCommand = {
|
|
|
47766
47266
|
// src/domains/login/profile/delete.ts
|
|
47767
47267
|
var deleteCommand = {
|
|
47768
47268
|
name: "delete",
|
|
47769
|
-
description: "Delete a saved profile",
|
|
47269
|
+
description: "Delete a saved connection profile permanently. Requires --force flag to delete the currently active profile to prevent accidental disconnection from active tenant.",
|
|
47270
|
+
descriptionShort: "Delete a saved profile",
|
|
47271
|
+
descriptionMedium: "Remove a saved profile permanently. Use --force to delete active profile.",
|
|
47770
47272
|
usage: "<name> [--force]",
|
|
47771
47273
|
aliases: ["rm", "remove"],
|
|
47772
47274
|
async execute(args, _session) {
|
|
@@ -47794,6 +47296,7 @@ var deleteCommand = {
|
|
|
47794
47296
|
].join("\n")
|
|
47795
47297
|
);
|
|
47796
47298
|
}
|
|
47299
|
+
await manager.clearActive();
|
|
47797
47300
|
}
|
|
47798
47301
|
const result = await manager.delete(name);
|
|
47799
47302
|
if (!result.success) {
|
|
@@ -47815,10 +47318,66 @@ var deleteCommand = {
|
|
|
47815
47318
|
}
|
|
47816
47319
|
};
|
|
47817
47320
|
|
|
47321
|
+
// src/domains/login/profile/active.ts
|
|
47322
|
+
var activeCommand = {
|
|
47323
|
+
name: "active",
|
|
47324
|
+
description: "Display the currently active profile configuration including tenant URL, authentication method, and default namespace. Shows masked credentials for security while confirming connection settings.",
|
|
47325
|
+
descriptionShort: "Display active profile configuration",
|
|
47326
|
+
descriptionMedium: "Show current active profile details including tenant URL, auth type, and namespace settings.",
|
|
47327
|
+
async execute(_args, _session) {
|
|
47328
|
+
const manager = getProfileManager();
|
|
47329
|
+
try {
|
|
47330
|
+
const activeName = await manager.getActive();
|
|
47331
|
+
if (!activeName) {
|
|
47332
|
+
return successResult([
|
|
47333
|
+
"No active profile set.",
|
|
47334
|
+
"",
|
|
47335
|
+
"Run 'login profile list' to see available profiles.",
|
|
47336
|
+
"Run 'login profile use <name>' to activate a profile."
|
|
47337
|
+
]);
|
|
47338
|
+
}
|
|
47339
|
+
const profile = await manager.get(activeName);
|
|
47340
|
+
if (!profile) {
|
|
47341
|
+
return errorResult(
|
|
47342
|
+
`Active profile '${activeName}' not found. Run 'login profile list' to see available profiles.`
|
|
47343
|
+
);
|
|
47344
|
+
}
|
|
47345
|
+
const masked = manager.maskProfile(profile);
|
|
47346
|
+
const output = [
|
|
47347
|
+
`Active Profile: ${profile.name}`,
|
|
47348
|
+
``,
|
|
47349
|
+
` API URL: ${masked.apiUrl}`
|
|
47350
|
+
];
|
|
47351
|
+
if (masked.apiToken) {
|
|
47352
|
+
output.push(` API Token: ${masked.apiToken}`);
|
|
47353
|
+
}
|
|
47354
|
+
if (masked.p12Bundle) {
|
|
47355
|
+
output.push(` P12 Bundle: ${masked.p12Bundle}`);
|
|
47356
|
+
}
|
|
47357
|
+
if (masked.cert) {
|
|
47358
|
+
output.push(` Certificate: ${masked.cert}`);
|
|
47359
|
+
}
|
|
47360
|
+
if (masked.key) {
|
|
47361
|
+
output.push(` Private Key: ${masked.key}`);
|
|
47362
|
+
}
|
|
47363
|
+
if (masked.defaultNamespace) {
|
|
47364
|
+
output.push(` Namespace: ${masked.defaultNamespace}`);
|
|
47365
|
+
}
|
|
47366
|
+
return successResult(output);
|
|
47367
|
+
} catch (error) {
|
|
47368
|
+
return errorResult(
|
|
47369
|
+
`Failed to get active profile: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
47370
|
+
);
|
|
47371
|
+
}
|
|
47372
|
+
}
|
|
47373
|
+
};
|
|
47374
|
+
|
|
47818
47375
|
// src/domains/login/context/index.ts
|
|
47819
47376
|
var showCommand2 = {
|
|
47820
47377
|
name: "show",
|
|
47821
|
-
description: "
|
|
47378
|
+
description: "Display the currently active namespace context used for scoping operations. Shows both the namespace value and its source (environment variable, profile default, or session configuration).",
|
|
47379
|
+
descriptionShort: "Show current default namespace",
|
|
47380
|
+
descriptionMedium: "Display active namespace context and its configuration source.",
|
|
47822
47381
|
usage: "",
|
|
47823
47382
|
aliases: ["current", "get"],
|
|
47824
47383
|
async execute(_args, session) {
|
|
@@ -47843,7 +47402,9 @@ function determineNamespaceSource(namespace) {
|
|
|
47843
47402
|
}
|
|
47844
47403
|
var setCommand = {
|
|
47845
47404
|
name: "set",
|
|
47846
|
-
description: "
|
|
47405
|
+
description: "Change the default namespace context for all subsequent operations. Updates the session scope so operations target the specified namespace unless explicitly overridden.",
|
|
47406
|
+
descriptionShort: "Set default namespace context",
|
|
47407
|
+
descriptionMedium: "Switch namespace context for scoped operations without specifying namespace each time.",
|
|
47847
47408
|
usage: "<namespace>",
|
|
47848
47409
|
aliases: ["use", "switch"],
|
|
47849
47410
|
async execute(args, session) {
|
|
@@ -47868,12 +47429,19 @@ var setCommand = {
|
|
|
47868
47429
|
return successResult(lines, true);
|
|
47869
47430
|
},
|
|
47870
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
|
+
}
|
|
47871
47438
|
const client = session.getAPIClient();
|
|
47872
47439
|
if (client?.isAuthenticated()) {
|
|
47873
47440
|
try {
|
|
47874
47441
|
const response = await client.get("/api/web/namespaces");
|
|
47875
47442
|
if (response.ok && response.data?.items) {
|
|
47876
|
-
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);
|
|
47877
47445
|
return namespaces.filter(
|
|
47878
47446
|
(ns) => ns.toLowerCase().startsWith(partial.toLowerCase())
|
|
47879
47447
|
);
|
|
@@ -47887,7 +47455,9 @@ var setCommand = {
|
|
|
47887
47455
|
};
|
|
47888
47456
|
var listCommand2 = {
|
|
47889
47457
|
name: "list",
|
|
47890
|
-
description: "
|
|
47458
|
+
description: "Fetch and display all available namespaces from the tenant. Requires authenticated connection. Shows current namespace indicator and provides switch command guidance.",
|
|
47459
|
+
descriptionShort: "List available namespaces",
|
|
47460
|
+
descriptionMedium: "Query tenant for available namespaces with current context indicator.",
|
|
47891
47461
|
usage: "",
|
|
47892
47462
|
aliases: ["ls"],
|
|
47893
47463
|
async execute(_args, session) {
|
|
@@ -47909,6 +47479,7 @@ var listCommand2 = {
|
|
|
47909
47479
|
return errorResult("Failed to fetch namespaces from API.");
|
|
47910
47480
|
}
|
|
47911
47481
|
const namespaces = response.data.items.map((item) => item.name).filter((name) => !!name).sort();
|
|
47482
|
+
session.setNamespaceCache(namespaces);
|
|
47912
47483
|
const lines = ["Available namespaces:", ""];
|
|
47913
47484
|
for (const ns of namespaces) {
|
|
47914
47485
|
if (ns === currentNamespace) {
|
|
@@ -47928,7 +47499,9 @@ var listCommand2 = {
|
|
|
47928
47499
|
};
|
|
47929
47500
|
var contextSubcommands = {
|
|
47930
47501
|
name: "context",
|
|
47931
|
-
description: "Manage default namespace context for
|
|
47502
|
+
description: "Manage default namespace context for scoping operations. Set, display, and list namespaces to control which namespace is used when no explicit namespace is specified in commands.",
|
|
47503
|
+
descriptionShort: "Manage default namespace context",
|
|
47504
|
+
descriptionMedium: "Set, display, and list namespaces for scoping operations without explicit namespace flags.",
|
|
47932
47505
|
commands: /* @__PURE__ */ new Map([
|
|
47933
47506
|
["show", showCommand2],
|
|
47934
47507
|
["set", setCommand],
|
|
@@ -47960,11 +47533,6 @@ var EnvVarRegistry = [
|
|
|
47960
47533
|
description: "Output format (json, yaml, table)",
|
|
47961
47534
|
relatedFlag: "-o"
|
|
47962
47535
|
},
|
|
47963
|
-
{
|
|
47964
|
-
name: `${ENV_PREFIX}_SUBSCRIPTION_TIER`,
|
|
47965
|
-
description: "Subscription tier for feature validation",
|
|
47966
|
-
relatedFlag: ""
|
|
47967
|
-
},
|
|
47968
47536
|
{
|
|
47969
47537
|
name: `${ENV_PREFIX}_LOGO`,
|
|
47970
47538
|
description: "Logo display mode (auto, image, ascii, both, none)",
|
|
@@ -48338,7 +47906,9 @@ function parseLogoArg(args) {
|
|
|
48338
47906
|
}
|
|
48339
47907
|
var bannerCommand = {
|
|
48340
47908
|
name: "banner",
|
|
48341
|
-
description: "
|
|
47909
|
+
description: "Render the xcsh startup banner with customizable logo display. Supports multiple render modes including image, ASCII, Unicode, and sixel graphics based on terminal capabilities.",
|
|
47910
|
+
descriptionShort: "Display xcsh banner with logo",
|
|
47911
|
+
descriptionMedium: "Show startup banner with configurable logo mode (image, ASCII, sixel) based on terminal.",
|
|
48342
47912
|
usage: "[--logo <mode>]",
|
|
48343
47913
|
async execute(args, _session) {
|
|
48344
47914
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -48411,25 +47981,8 @@ var bannerCommand = {
|
|
|
48411
47981
|
}
|
|
48412
47982
|
};
|
|
48413
47983
|
|
|
48414
|
-
// src/domains/login/whoami/types.ts
|
|
48415
|
-
function toDisplayTier(tier) {
|
|
48416
|
-
const normalized = tier.toUpperCase();
|
|
48417
|
-
switch (normalized) {
|
|
48418
|
-
case "STANDARD":
|
|
48419
|
-
case "BASIC":
|
|
48420
|
-
return "Standard";
|
|
48421
|
-
case "ADVANCED":
|
|
48422
|
-
case "PREMIUM":
|
|
48423
|
-
// Legacy mapping
|
|
48424
|
-
case "ENTERPRISE":
|
|
48425
|
-
return "Advanced";
|
|
48426
|
-
default:
|
|
48427
|
-
return void 0;
|
|
48428
|
-
}
|
|
48429
|
-
}
|
|
48430
|
-
|
|
48431
47984
|
// src/domains/login/whoami/service.ts
|
|
48432
|
-
async function getWhoamiInfo(session,
|
|
47985
|
+
async function getWhoamiInfo(session, _options = {}) {
|
|
48433
47986
|
const info = {
|
|
48434
47987
|
serverUrl: session.getServerUrl(),
|
|
48435
47988
|
namespace: session.getNamespace(),
|
|
@@ -48450,32 +48003,6 @@ async function getWhoamiInfo(session, options = {}) {
|
|
|
48450
48003
|
info.username = username;
|
|
48451
48004
|
}
|
|
48452
48005
|
}
|
|
48453
|
-
const apiClient = session.getAPIClient();
|
|
48454
|
-
if (apiClient) {
|
|
48455
|
-
const subscriptionClient = new SubscriptionClient(apiClient);
|
|
48456
|
-
try {
|
|
48457
|
-
const tierValue = await subscriptionClient.getTierFromCurrentPlan();
|
|
48458
|
-
const displayTier = toDisplayTier(tierValue);
|
|
48459
|
-
if (displayTier) {
|
|
48460
|
-
info.tier = displayTier;
|
|
48461
|
-
}
|
|
48462
|
-
} catch {
|
|
48463
|
-
}
|
|
48464
|
-
if (options.includeQuotas || options.verbose) {
|
|
48465
|
-
try {
|
|
48466
|
-
const subscriptionInfo = await subscriptionClient.getSubscriptionInfo();
|
|
48467
|
-
info.quotas = subscriptionInfo.quotaSummary;
|
|
48468
|
-
} catch {
|
|
48469
|
-
}
|
|
48470
|
-
}
|
|
48471
|
-
if (options.includeAddons || options.verbose) {
|
|
48472
|
-
try {
|
|
48473
|
-
const addons = await subscriptionClient.getAddonServices();
|
|
48474
|
-
info.addons = addons.filter(isAddonActive);
|
|
48475
|
-
} catch {
|
|
48476
|
-
}
|
|
48477
|
-
}
|
|
48478
|
-
}
|
|
48479
48006
|
return info;
|
|
48480
48007
|
}
|
|
48481
48008
|
|
|
@@ -48487,41 +48014,19 @@ function formatWhoami(info, options = {}) {
|
|
|
48487
48014
|
if (options.json) {
|
|
48488
48015
|
return formatWhoamiJson(info);
|
|
48489
48016
|
}
|
|
48490
|
-
return formatWhoamiBox(info
|
|
48017
|
+
return formatWhoamiBox(info);
|
|
48491
48018
|
}
|
|
48492
48019
|
function formatWhoamiJson(info) {
|
|
48493
48020
|
const output = {};
|
|
48494
48021
|
if (info.tenant) output.tenant = info.tenant;
|
|
48495
48022
|
if (info.username) output.username = info.username;
|
|
48496
48023
|
if (info.email) output.email = info.email;
|
|
48497
|
-
if (info.tier) output.tier = info.tier;
|
|
48498
48024
|
output.namespace = info.namespace;
|
|
48499
48025
|
output.serverUrl = info.serverUrl;
|
|
48500
48026
|
output.isAuthenticated = info.isAuthenticated;
|
|
48501
|
-
if (info.quotas) {
|
|
48502
|
-
output.quotas = {
|
|
48503
|
-
totalLimits: info.quotas.totalLimits,
|
|
48504
|
-
limitsAtRisk: info.quotas.limitsAtRisk,
|
|
48505
|
-
limitsExceeded: info.quotas.limitsExceeded,
|
|
48506
|
-
objects: info.quotas.objects?.map((q) => ({
|
|
48507
|
-
name: q.name,
|
|
48508
|
-
displayName: q.displayName,
|
|
48509
|
-
usage: q.usage,
|
|
48510
|
-
limit: q.limit,
|
|
48511
|
-
percentage: Math.round(q.percentage)
|
|
48512
|
-
}))
|
|
48513
|
-
};
|
|
48514
|
-
}
|
|
48515
|
-
if (info.addons && info.addons.length > 0) {
|
|
48516
|
-
output.addons = info.addons.map((a) => ({
|
|
48517
|
-
name: a.name,
|
|
48518
|
-
displayName: a.displayName,
|
|
48519
|
-
state: a.state
|
|
48520
|
-
}));
|
|
48521
|
-
}
|
|
48522
48027
|
return [JSON.stringify(output, null, 2)];
|
|
48523
48028
|
}
|
|
48524
|
-
function formatWhoamiBox(info
|
|
48029
|
+
function formatWhoamiBox(info) {
|
|
48525
48030
|
const lines = [];
|
|
48526
48031
|
const red = colors.red;
|
|
48527
48032
|
const reset = colors.reset;
|
|
@@ -48531,9 +48036,7 @@ function formatWhoamiBox(info, options) {
|
|
|
48531
48036
|
bottomLeft: "\u2570",
|
|
48532
48037
|
bottomRight: "\u256F",
|
|
48533
48038
|
horizontal: "\u2500",
|
|
48534
|
-
vertical: "\u2502"
|
|
48535
|
-
leftT: "\u251C",
|
|
48536
|
-
rightT: "\u2524"
|
|
48039
|
+
vertical: "\u2502"
|
|
48537
48040
|
};
|
|
48538
48041
|
const contentLines = [];
|
|
48539
48042
|
if (info.tenant) {
|
|
@@ -48544,46 +48047,19 @@ function formatWhoamiBox(info, options) {
|
|
|
48544
48047
|
} else if (info.username) {
|
|
48545
48048
|
contentLines.push({ label: "User", value: info.username });
|
|
48546
48049
|
}
|
|
48547
|
-
if (info.tier) {
|
|
48548
|
-
contentLines.push({ label: "Tier", value: info.tier });
|
|
48549
|
-
}
|
|
48550
48050
|
contentLines.push({ label: "Namespace", value: info.namespace });
|
|
48551
48051
|
contentLines.push({ label: "Server", value: info.serverUrl });
|
|
48552
48052
|
contentLines.push({
|
|
48553
48053
|
label: "Auth",
|
|
48554
48054
|
value: info.isAuthenticated ? "\u2713 Authenticated" : "Not authenticated"
|
|
48555
48055
|
});
|
|
48556
|
-
const quotaLines = [];
|
|
48557
|
-
if (info.quotas && info.quotas.objects && (options.includeQuotas || options.verbose)) {
|
|
48558
|
-
for (const quota of info.quotas.objects.slice(0, 10)) {
|
|
48559
|
-
const pct = Math.round(quota.percentage);
|
|
48560
|
-
quotaLines.push(
|
|
48561
|
-
`${quota.displayName}: ${quota.usage}/${quota.limit} (${pct}%)`
|
|
48562
|
-
);
|
|
48563
|
-
}
|
|
48564
|
-
if (info.quotas.objects.length > 10) {
|
|
48565
|
-
const remaining = info.quotas.objects.length - 10;
|
|
48566
|
-
quotaLines.push(`... and ${remaining} more`);
|
|
48567
|
-
}
|
|
48568
|
-
}
|
|
48569
|
-
const addonLines = [];
|
|
48570
|
-
if (info.addons && info.addons.length > 0 && (options.includeAddons || options.verbose)) {
|
|
48571
|
-
for (const addon of info.addons) {
|
|
48572
|
-
addonLines.push(`\u2713 ${addon.displayName}`);
|
|
48573
|
-
}
|
|
48574
|
-
}
|
|
48575
48056
|
const maxLabelWidth = Math.max(...contentLines.map((c) => c.label.length));
|
|
48576
48057
|
const formattedContent = contentLines.map((c) => {
|
|
48577
48058
|
const paddedLabel = c.label.padEnd(maxLabelWidth);
|
|
48578
48059
|
return `${paddedLabel}: ${c.value}`;
|
|
48579
48060
|
});
|
|
48580
|
-
const
|
|
48581
|
-
const allTextLines = [
|
|
48582
|
-
...formattedContent,
|
|
48583
|
-
...quotaLines,
|
|
48584
|
-
...addonLines,
|
|
48585
|
-
...headerTitles
|
|
48586
|
-
];
|
|
48061
|
+
const headerTitle = "Connection Info";
|
|
48062
|
+
const allTextLines = [...formattedContent, headerTitle];
|
|
48587
48063
|
const maxContentWidth = Math.max(...allTextLines.map((l) => l.length));
|
|
48588
48064
|
const innerWidth = maxContentWidth + 2;
|
|
48589
48065
|
const title = " Connection Info ";
|
|
@@ -48599,36 +48075,6 @@ function formatWhoamiBox(info, options) {
|
|
|
48599
48075
|
`${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
|
|
48600
48076
|
);
|
|
48601
48077
|
}
|
|
48602
|
-
if (quotaLines.length > 0) {
|
|
48603
|
-
const quotaTitle = " Quota Usage ";
|
|
48604
|
-
const quotaRemaining = innerWidth - quotaTitle.length;
|
|
48605
|
-
const quotaLeft = 1;
|
|
48606
|
-
const quotaRight = Math.max(0, quotaRemaining - quotaLeft);
|
|
48607
|
-
lines.push(
|
|
48608
|
-
`${red}${BOX.leftT}${BOX.horizontal.repeat(quotaLeft)}${reset}${quotaTitle}${red}${BOX.horizontal.repeat(quotaRight)}${BOX.rightT}${reset}`
|
|
48609
|
-
);
|
|
48610
|
-
for (const text of quotaLines) {
|
|
48611
|
-
const padding = innerWidth - text.length;
|
|
48612
|
-
lines.push(
|
|
48613
|
-
`${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
|
|
48614
|
-
);
|
|
48615
|
-
}
|
|
48616
|
-
}
|
|
48617
|
-
if (addonLines.length > 0) {
|
|
48618
|
-
const addonTitle = " Active Addons ";
|
|
48619
|
-
const addonRemaining = innerWidth - addonTitle.length;
|
|
48620
|
-
const addonLeft = 1;
|
|
48621
|
-
const addonRight = Math.max(0, addonRemaining - addonLeft);
|
|
48622
|
-
lines.push(
|
|
48623
|
-
`${red}${BOX.leftT}${BOX.horizontal.repeat(addonLeft)}${reset}${addonTitle}${red}${BOX.horizontal.repeat(addonRight)}${BOX.rightT}${reset}`
|
|
48624
|
-
);
|
|
48625
|
-
for (const text of addonLines) {
|
|
48626
|
-
const padding = innerWidth - text.length;
|
|
48627
|
-
lines.push(
|
|
48628
|
-
`${red}${BOX.vertical}${reset} ${text}${" ".repeat(Math.max(0, padding - 1))}${red}${BOX.vertical}${reset}`
|
|
48629
|
-
);
|
|
48630
|
-
}
|
|
48631
|
-
}
|
|
48632
48078
|
lines.push(
|
|
48633
48079
|
`${red}${BOX.bottomLeft}${BOX.horizontal.repeat(innerWidth)}${BOX.bottomRight}${reset}`
|
|
48634
48080
|
);
|
|
@@ -48638,7 +48084,9 @@ function formatWhoamiBox(info, options) {
|
|
|
48638
48084
|
// src/domains/login/whoami/index.ts
|
|
48639
48085
|
var whoamiCommand = {
|
|
48640
48086
|
name: "show",
|
|
48641
|
-
description: "
|
|
48087
|
+
description: "Display current connection status and authenticated identity information. Shows active profile, tenant URL, username, tenant details, and session context including namespace targeting.",
|
|
48088
|
+
descriptionShort: "Show connection and identity info",
|
|
48089
|
+
descriptionMedium: "Display active profile, tenant URL, user identity, and current namespace context.",
|
|
48642
48090
|
async execute(_args, session) {
|
|
48643
48091
|
try {
|
|
48644
48092
|
const info = await getWhoamiInfo(session);
|
|
@@ -48655,14 +48103,17 @@ var whoamiCommand = {
|
|
|
48655
48103
|
// src/domains/login/index.ts
|
|
48656
48104
|
var profileSubcommands = {
|
|
48657
48105
|
name: "profile",
|
|
48658
|
-
description: "Manage saved connection profiles",
|
|
48106
|
+
description: "Manage saved connection profiles for tenant authentication. Create, list, activate, and delete profiles that store tenant URL, credentials, and default namespace settings for seamless tenant switching.",
|
|
48107
|
+
descriptionShort: "Manage saved connection profiles",
|
|
48108
|
+
descriptionMedium: "Create, list, switch, and delete saved authentication profiles for multi-tenant management.",
|
|
48659
48109
|
commands: /* @__PURE__ */ new Map([
|
|
48660
48110
|
["list", listCommand],
|
|
48661
48111
|
["show", showCommand],
|
|
48662
48112
|
["create", createCommand2],
|
|
48663
48113
|
["use", useCommand],
|
|
48664
48114
|
["delete", deleteCommand]
|
|
48665
|
-
])
|
|
48115
|
+
]),
|
|
48116
|
+
defaultCommand: activeCommand
|
|
48666
48117
|
};
|
|
48667
48118
|
var loginDomain = {
|
|
48668
48119
|
name: "login",
|
|
@@ -49022,7 +48473,9 @@ function getClient() {
|
|
|
49022
48473
|
}
|
|
49023
48474
|
var statusCommand = {
|
|
49024
48475
|
name: "status",
|
|
49025
|
-
description: "
|
|
48476
|
+
description: "Retrieve the current overall health indicator for F5 Distributed Cloud services. Returns status level (operational, degraded, major outage) with description. Use --quiet for script-friendly exit code output.",
|
|
48477
|
+
descriptionShort: "Get overall cloud status indicator",
|
|
48478
|
+
descriptionMedium: "Check overall service health status. Use --quiet for exit code suitable for scripts.",
|
|
49026
48479
|
usage: "[--quiet]",
|
|
49027
48480
|
aliases: ["st"],
|
|
49028
48481
|
async execute(args, session) {
|
|
@@ -49071,7 +48524,9 @@ var statusCommand = {
|
|
|
49071
48524
|
};
|
|
49072
48525
|
var summaryCommand = {
|
|
49073
48526
|
name: "summary",
|
|
49074
|
-
description: "
|
|
48527
|
+
description: "Display comprehensive status overview combining overall health, component statuses, active incidents, and scheduled maintenance in a single report. Use --brief for condensed statistics output.",
|
|
48528
|
+
descriptionShort: "Get complete status summary",
|
|
48529
|
+
descriptionMedium: "Show combined overview of health, components, incidents, and maintenance windows.",
|
|
49075
48530
|
usage: "[--brief]",
|
|
49076
48531
|
aliases: ["sum"],
|
|
49077
48532
|
async execute(args, session) {
|
|
@@ -49098,7 +48553,9 @@ var summaryCommand = {
|
|
|
49098
48553
|
};
|
|
49099
48554
|
var componentsCommand = {
|
|
49100
48555
|
name: "components",
|
|
49101
|
-
description: "List all components
|
|
48556
|
+
description: "List all service components with their current operational status. Shows each component's health level. Use --degraded-only to filter for components experiencing issues.",
|
|
48557
|
+
descriptionShort: "List all components and status",
|
|
48558
|
+
descriptionMedium: "Display service component health. Use --degraded-only to show only affected components.",
|
|
49102
48559
|
usage: "[--degraded-only]",
|
|
49103
48560
|
aliases: ["comp"],
|
|
49104
48561
|
async execute(args, session) {
|
|
@@ -49139,7 +48596,9 @@ var componentsCommand = {
|
|
|
49139
48596
|
};
|
|
49140
48597
|
var incidentsCommand = {
|
|
49141
48598
|
name: "incidents",
|
|
49142
|
-
description: "
|
|
48599
|
+
description: "Track service incidents with their impact levels, status, and latest updates. Shows both active and recently resolved incidents. Use --active-only to filter for ongoing issues requiring attention.",
|
|
48600
|
+
descriptionShort: "List active and recent incidents",
|
|
48601
|
+
descriptionMedium: "Display incidents with impact levels and updates. Use --active-only for ongoing issues.",
|
|
49143
48602
|
usage: "[--active-only]",
|
|
49144
48603
|
aliases: ["inc"],
|
|
49145
48604
|
async execute(args, session) {
|
|
@@ -49178,7 +48637,9 @@ var incidentsCommand = {
|
|
|
49178
48637
|
};
|
|
49179
48638
|
var maintenanceCommand = {
|
|
49180
48639
|
name: "maintenance",
|
|
49181
|
-
description: "
|
|
48640
|
+
description: "View scheduled and in-progress maintenance windows with their timing and affected services. Plan around downtime windows. Use --upcoming to filter for future maintenance only.",
|
|
48641
|
+
descriptionShort: "List scheduled maintenance windows",
|
|
48642
|
+
descriptionMedium: "Show maintenance schedules and timing. Use --upcoming for future windows only.",
|
|
49182
48643
|
usage: "[--upcoming]",
|
|
49183
48644
|
aliases: ["maint"],
|
|
49184
48645
|
async execute(args, session) {
|
|
@@ -49351,717 +48812,297 @@ var cloudstatusDomain = {
|
|
|
49351
48812
|
};
|
|
49352
48813
|
var cloudstatusAliases = ["cs", "status"];
|
|
49353
48814
|
|
|
49354
|
-
// src/
|
|
49355
|
-
var
|
|
49356
|
-
|
|
49357
|
-
|
|
49358
|
-
|
|
49359
|
-
|
|
49360
|
-
|
|
49361
|
-
|
|
49362
|
-
|
|
49363
|
-
|
|
49364
|
-
|
|
49365
|
-
|
|
49366
|
-
|
|
49367
|
-
|
|
49368
|
-
return RESERVED_API_ACTIONS.has(name.toLowerCase());
|
|
49369
|
-
}
|
|
49370
|
-
function validateExtension(extension) {
|
|
49371
|
-
const conflicts = [];
|
|
49372
|
-
for (const [name] of extension.commands) {
|
|
49373
|
-
if (isReservedAction(name)) {
|
|
49374
|
-
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
|
+
}
|
|
49375
48829
|
}
|
|
49376
48830
|
}
|
|
49377
|
-
if (conflicts.length > 0) {
|
|
49378
|
-
throw new Error(
|
|
49379
|
-
`Extension "${extension.targetDomain}" has commands that conflict with API actions: ${conflicts.join(", ")}. Use unique names or submit a feature request to upstream.`
|
|
49380
|
-
);
|
|
49381
|
-
}
|
|
49382
|
-
}
|
|
49383
|
-
|
|
49384
|
-
// src/extensions/registry.ts
|
|
49385
|
-
var ExtensionRegistry = class {
|
|
49386
|
-
extensions = /* @__PURE__ */ new Map();
|
|
49387
|
-
mergedCache = /* @__PURE__ */ new Map();
|
|
49388
48831
|
/**
|
|
49389
|
-
*
|
|
49390
|
-
*
|
|
49391
|
-
* @param extension - Extension definition to register
|
|
49392
|
-
* @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
|
|
49393
48834
|
*/
|
|
49394
|
-
|
|
49395
|
-
|
|
49396
|
-
|
|
49397
|
-
|
|
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
|
+
}
|
|
49398
48846
|
}
|
|
49399
48847
|
/**
|
|
49400
|
-
* Get
|
|
49401
|
-
*
|
|
49402
|
-
* @param domain - Canonical domain name
|
|
49403
|
-
* @returns Extension if registered, undefined otherwise
|
|
48848
|
+
* Get the complete completion tree
|
|
49404
48849
|
*/
|
|
49405
|
-
|
|
49406
|
-
return this.
|
|
48850
|
+
getTree() {
|
|
48851
|
+
return this.tree;
|
|
49407
48852
|
}
|
|
49408
48853
|
/**
|
|
49409
|
-
*
|
|
48854
|
+
* Get all registered domains as array (sorted by name)
|
|
49410
48855
|
*/
|
|
49411
|
-
|
|
49412
|
-
return this.
|
|
48856
|
+
getDomains() {
|
|
48857
|
+
return Array.from(this.tree.values()).sort(
|
|
48858
|
+
(a, b) => a.name.localeCompare(b.name)
|
|
48859
|
+
);
|
|
49413
48860
|
}
|
|
49414
48861
|
/**
|
|
49415
|
-
*
|
|
49416
|
-
*
|
|
49417
|
-
* Resolution priority:
|
|
49418
|
-
* 1. Check if canonical domain exists in generated domains
|
|
49419
|
-
* 2. Check if extension exists for this domain
|
|
49420
|
-
* 3. Merge if both exist, or return standalone if only one
|
|
49421
|
-
*
|
|
49422
|
-
* @param domain - Canonical domain name or alias
|
|
49423
|
-
* @returns Merged domain view or undefined if neither exists
|
|
48862
|
+
* Resolve alias to canonical name
|
|
49424
48863
|
*/
|
|
49425
|
-
|
|
49426
|
-
|
|
49427
|
-
const cached = this.mergedCache.get(canonical);
|
|
49428
|
-
if (cached) {
|
|
49429
|
-
return cached;
|
|
49430
|
-
}
|
|
49431
|
-
const domainInfo = domainRegistry.get(canonical);
|
|
49432
|
-
const extension = this.extensions.get(canonical);
|
|
49433
|
-
if (!domainInfo && !extension) {
|
|
49434
|
-
return void 0;
|
|
49435
|
-
}
|
|
49436
|
-
const merged = this.buildMergedDomain(canonical, domainInfo, extension);
|
|
49437
|
-
this.mergedCache.set(canonical, merged);
|
|
49438
|
-
return merged;
|
|
48864
|
+
resolveAlias(nameOrAlias) {
|
|
48865
|
+
return this.aliases.get(nameOrAlias) ?? nameOrAlias;
|
|
49439
48866
|
}
|
|
49440
48867
|
/**
|
|
49441
|
-
*
|
|
48868
|
+
* Get a domain by name or alias
|
|
49442
48869
|
*/
|
|
49443
|
-
|
|
49444
|
-
const
|
|
49445
|
-
|
|
49446
|
-
let source;
|
|
49447
|
-
if (hasGeneratedDomain && hasExtension) {
|
|
49448
|
-
source = "merged";
|
|
49449
|
-
} else if (hasGeneratedDomain) {
|
|
49450
|
-
source = "generated";
|
|
49451
|
-
} else {
|
|
49452
|
-
source = "extension";
|
|
49453
|
-
}
|
|
49454
|
-
const displayName = domainInfo?.displayName ?? this.toDisplayName(extension?.targetDomain ?? name);
|
|
49455
|
-
const description = domainInfo?.description ?? extension?.description ?? `Commands for ${displayName}`;
|
|
49456
|
-
const merged = {
|
|
49457
|
-
name,
|
|
49458
|
-
displayName,
|
|
49459
|
-
description,
|
|
49460
|
-
source,
|
|
49461
|
-
hasGeneratedDomain,
|
|
49462
|
-
hasExtension,
|
|
49463
|
-
extensionCommands: extension?.commands ?? /* @__PURE__ */ new Map(),
|
|
49464
|
-
extensionSubcommands: extension?.subcommands ?? /* @__PURE__ */ new Map()
|
|
49465
|
-
};
|
|
49466
|
-
if (domainInfo) {
|
|
49467
|
-
merged.metadata = domainInfo;
|
|
49468
|
-
}
|
|
49469
|
-
return merged;
|
|
48870
|
+
get(nameOrAlias) {
|
|
48871
|
+
const canonical = this.resolveAlias(nameOrAlias);
|
|
48872
|
+
return this.tree.get(canonical);
|
|
49470
48873
|
}
|
|
49471
48874
|
/**
|
|
49472
|
-
*
|
|
48875
|
+
* Check if a domain exists (by name or alias)
|
|
49473
48876
|
*/
|
|
49474
|
-
|
|
49475
|
-
|
|
48877
|
+
has(nameOrAlias) {
|
|
48878
|
+
const canonical = this.resolveAlias(nameOrAlias);
|
|
48879
|
+
return this.tree.has(canonical);
|
|
49476
48880
|
}
|
|
49477
48881
|
/**
|
|
49478
|
-
* Get all
|
|
48882
|
+
* Get all aliases
|
|
49479
48883
|
*/
|
|
49480
|
-
|
|
49481
|
-
return
|
|
48884
|
+
getAliases() {
|
|
48885
|
+
return new Map(this.aliases);
|
|
49482
48886
|
}
|
|
49483
48887
|
/**
|
|
49484
|
-
* Get
|
|
49485
|
-
*
|
|
49486
|
-
* Returns domains that either:
|
|
49487
|
-
* - Have generated API commands (from upstream)
|
|
49488
|
-
* - Have extension commands (xcsh-specific)
|
|
49489
|
-
* - Have both (merged)
|
|
48888
|
+
* Get domain suggestions for completion
|
|
49490
48889
|
*/
|
|
49491
|
-
|
|
49492
|
-
const
|
|
49493
|
-
|
|
49494
|
-
|
|
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
|
+
}
|
|
49495
48913
|
}
|
|
49496
|
-
|
|
49497
|
-
domains.add(name);
|
|
49498
|
-
}
|
|
49499
|
-
const merged = [];
|
|
49500
|
-
for (const name of domains) {
|
|
49501
|
-
const domain = this.getMergedDomain(name);
|
|
49502
|
-
if (domain) {
|
|
49503
|
-
merged.push(domain);
|
|
49504
|
-
}
|
|
49505
|
-
}
|
|
49506
|
-
return merged.sort((a, b) => a.name.localeCompare(b.name));
|
|
48914
|
+
return suggestions.sort((a, b) => a.text.localeCompare(b.text));
|
|
49507
48915
|
}
|
|
49508
48916
|
/**
|
|
49509
|
-
* Get
|
|
49510
|
-
*
|
|
49511
|
-
* @param domain - Canonical domain name
|
|
49512
|
-
* @param command - Command name
|
|
49513
|
-
* @returns Command definition if found
|
|
48917
|
+
* Get child suggestions for a domain
|
|
49514
48918
|
*/
|
|
49515
|
-
|
|
49516
|
-
const
|
|
49517
|
-
if (!
|
|
49518
|
-
return
|
|
49519
|
-
}
|
|
49520
|
-
const cmd = extension.commands.get(command);
|
|
49521
|
-
if (cmd) {
|
|
49522
|
-
return cmd;
|
|
48919
|
+
getChildSuggestions(domainName, prefix = "") {
|
|
48920
|
+
const node = this.get(domainName);
|
|
48921
|
+
if (!node?.children) {
|
|
48922
|
+
return [];
|
|
49523
48923
|
}
|
|
49524
|
-
|
|
49525
|
-
|
|
49526
|
-
|
|
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
|
+
});
|
|
49527
48934
|
}
|
|
49528
48935
|
}
|
|
49529
|
-
return
|
|
49530
|
-
}
|
|
49531
|
-
/**
|
|
49532
|
-
* Get extension subcommand group
|
|
49533
|
-
*
|
|
49534
|
-
* @param domain - Canonical domain name
|
|
49535
|
-
* @param subcommand - Subcommand group name
|
|
49536
|
-
* @returns Subcommand group if found
|
|
49537
|
-
*/
|
|
49538
|
-
getExtensionSubcommand(domain, subcommand) {
|
|
49539
|
-
const extension = this.getExtension(domain);
|
|
49540
|
-
return extension?.subcommands?.get(subcommand);
|
|
49541
|
-
}
|
|
49542
|
-
/**
|
|
49543
|
-
* Check if a command exists in extension
|
|
49544
|
-
*/
|
|
49545
|
-
hasExtensionCommand(domain, command) {
|
|
49546
|
-
return this.getExtensionCommand(domain, command) !== void 0;
|
|
48936
|
+
return suggestions.sort((a, b) => a.text.localeCompare(b.text));
|
|
49547
48937
|
}
|
|
49548
48938
|
/**
|
|
49549
|
-
* Get
|
|
49550
|
-
* Used for tab completion
|
|
48939
|
+
* Get nested child suggestions (e.g., login profile <TAB>)
|
|
49551
48940
|
*/
|
|
49552
|
-
|
|
49553
|
-
|
|
49554
|
-
if (!
|
|
48941
|
+
getNestedChildSuggestions(domainName, path, prefix = "") {
|
|
48942
|
+
let node = this.get(domainName);
|
|
48943
|
+
if (!node) {
|
|
49555
48944
|
return [];
|
|
49556
48945
|
}
|
|
49557
|
-
const
|
|
49558
|
-
|
|
49559
|
-
|
|
49560
|
-
|
|
49561
|
-
|
|
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 [];
|
|
49562
48953
|
}
|
|
48954
|
+
node = child;
|
|
49563
48955
|
}
|
|
49564
|
-
|
|
49565
|
-
|
|
49566
|
-
|
|
49567
|
-
|
|
49568
|
-
|
|
49569
|
-
|
|
49570
|
-
|
|
49571
|
-
|
|
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
|
+
});
|
|
48969
|
+
}
|
|
48970
|
+
}
|
|
48971
|
+
return suggestions.sort((a, b) => a.text.localeCompare(b.text));
|
|
49572
48972
|
}
|
|
49573
48973
|
/**
|
|
49574
|
-
*
|
|
48974
|
+
* Clear the registry (useful for testing)
|
|
49575
48975
|
*/
|
|
49576
|
-
|
|
49577
|
-
|
|
49578
|
-
|
|
49579
|
-
for (const [domain] of this.extensions) {
|
|
49580
|
-
if (domainRegistry.has(domain)) {
|
|
49581
|
-
mergedCount++;
|
|
49582
|
-
} else {
|
|
49583
|
-
standaloneCount++;
|
|
49584
|
-
}
|
|
49585
|
-
}
|
|
49586
|
-
return {
|
|
49587
|
-
extensionCount: this.extensions.size,
|
|
49588
|
-
standaloneCount,
|
|
49589
|
-
mergedCount
|
|
49590
|
-
};
|
|
48976
|
+
clear() {
|
|
48977
|
+
this.tree.clear();
|
|
48978
|
+
this.aliases.clear();
|
|
49591
48979
|
}
|
|
49592
48980
|
};
|
|
49593
|
-
var
|
|
49594
|
-
|
|
49595
|
-
// src/
|
|
49596
|
-
var
|
|
49597
|
-
|
|
49598
|
-
|
|
49599
|
-
|
|
49600
|
-
|
|
49601
|
-
|
|
49602
|
-
|
|
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;
|
|
49603
49004
|
}
|
|
49604
|
-
return
|
|
49005
|
+
return node;
|
|
49605
49006
|
}
|
|
49606
|
-
|
|
49607
|
-
|
|
49608
|
-
|
|
49609
|
-
|
|
49610
|
-
aliases: ["show", "info"],
|
|
49611
|
-
async execute(args, session) {
|
|
49612
|
-
const jsonOutput = args.includes("--json");
|
|
49613
|
-
const format = session.getOutputFormat();
|
|
49614
|
-
const apiClient = session.getAPIClient();
|
|
49615
|
-
if (!apiClient) {
|
|
49616
|
-
return errorResult("Not authenticated. Use 'login' command first.");
|
|
49617
|
-
}
|
|
49618
|
-
try {
|
|
49619
|
-
const client = getClient2(apiClient);
|
|
49620
|
-
const info = await client.getSubscriptionInfo();
|
|
49621
|
-
if (jsonOutput || format === "json") {
|
|
49622
|
-
return successResult([JSON.stringify(info, null, 2)]);
|
|
49623
|
-
}
|
|
49624
|
-
const lines = [];
|
|
49625
|
-
lines.push("=== Subscription Overview ===");
|
|
49626
|
-
lines.push("");
|
|
49627
|
-
lines.push(`Tier: ${info.tier}`);
|
|
49628
|
-
if (info.plan) {
|
|
49629
|
-
lines.push(`Plan: ${info.plan.displayName}`);
|
|
49630
|
-
if (info.plan.description) {
|
|
49631
|
-
lines.push(`Description: ${info.plan.description}`);
|
|
49632
|
-
}
|
|
49633
|
-
}
|
|
49634
|
-
lines.push("");
|
|
49635
|
-
lines.push("--- Active Addons ---");
|
|
49636
|
-
if (info.activeAddons.length === 0) {
|
|
49637
|
-
lines.push(" No active addons");
|
|
49638
|
-
} else {
|
|
49639
|
-
for (const addon of info.activeAddons) {
|
|
49640
|
-
lines.push(
|
|
49641
|
-
` - ${addon.displayName} (${getTierDescription(addon.tier)})`
|
|
49642
|
-
);
|
|
49643
|
-
}
|
|
49644
|
-
}
|
|
49645
|
-
lines.push("");
|
|
49646
|
-
lines.push("--- Quota Summary ---");
|
|
49647
|
-
lines.push(` Total Limits: ${info.quotaSummary.totalLimits}`);
|
|
49648
|
-
lines.push(` At Risk (>80%): ${info.quotaSummary.limitsAtRisk}`);
|
|
49649
|
-
lines.push(` Exceeded: ${info.quotaSummary.limitsExceeded}`);
|
|
49650
|
-
if (info.quotaSummary.limitsExceeded > 0) {
|
|
49651
|
-
lines.push("");
|
|
49652
|
-
lines.push(" Warning: Some quotas are exceeded!");
|
|
49653
|
-
}
|
|
49654
|
-
return successResult(lines);
|
|
49655
|
-
} catch (err) {
|
|
49656
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49657
|
-
return errorResult(`Failed to get subscription info: ${message}`);
|
|
49658
|
-
}
|
|
49007
|
+
function fromSubcommandGroup(group) {
|
|
49008
|
+
const children = /* @__PURE__ */ new Map();
|
|
49009
|
+
for (const [name, cmd] of group.commands) {
|
|
49010
|
+
children.set(name, fromCommand(cmd));
|
|
49659
49011
|
}
|
|
49660
|
-
|
|
49661
|
-
|
|
49662
|
-
|
|
49663
|
-
|
|
49664
|
-
|
|
49665
|
-
|
|
49666
|
-
|
|
49667
|
-
|
|
49668
|
-
|
|
49669
|
-
|
|
49670
|
-
|
|
49671
|
-
|
|
49672
|
-
|
|
49673
|
-
|
|
49674
|
-
|
|
49675
|
-
|
|
49676
|
-
|
|
49677
|
-
|
|
49678
|
-
|
|
49679
|
-
|
|
49680
|
-
|
|
49681
|
-
|
|
49682
|
-
|
|
49683
|
-
|
|
49684
|
-
|
|
49685
|
-
|
|
49686
|
-
|
|
49687
|
-
|
|
49688
|
-
|
|
49689
|
-
|
|
49690
|
-
|
|
49691
|
-
|
|
49692
|
-
|
|
49693
|
-
|
|
49694
|
-
|
|
49695
|
-
|
|
49696
|
-
|
|
49697
|
-
|
|
49698
|
-
|
|
49699
|
-
|
|
49700
|
-
|
|
49701
|
-
|
|
49702
|
-
|
|
49703
|
-
|
|
49704
|
-
|
|
49705
|
-
|
|
49706
|
-
|
|
49707
|
-
|
|
49708
|
-
|
|
49709
|
-
|
|
49710
|
-
|
|
49711
|
-
|
|
49712
|
-
|
|
49713
|
-
|
|
49714
|
-
|
|
49715
|
-
|
|
49716
|
-
|
|
49717
|
-
|
|
49718
|
-
|
|
49719
|
-
|
|
49720
|
-
|
|
49721
|
-
|
|
49722
|
-
|
|
49723
|
-
|
|
49724
|
-
|
|
49725
|
-
|
|
49726
|
-
|
|
49727
|
-
|
|
49728
|
-
}
|
|
49729
|
-
if (denied.length > 0 && (showAll || filter === "denied")) {
|
|
49730
|
-
lines.push("--- Access Denied ---");
|
|
49731
|
-
for (const addon of denied) {
|
|
49732
|
-
lines.push(` [X] ${addon.displayName}`);
|
|
49733
|
-
lines.push(
|
|
49734
|
-
` Reason: ${getAccessStatusDescription(addon.accessStatus)}`
|
|
49735
|
-
);
|
|
49736
|
-
}
|
|
49737
|
-
lines.push("");
|
|
49738
|
-
}
|
|
49739
|
-
if (other.length > 0 && showAll) {
|
|
49740
|
-
lines.push("--- Other ---");
|
|
49741
|
-
for (const addon of other) {
|
|
49742
|
-
lines.push(` [-] ${addon.displayName}`);
|
|
49743
|
-
lines.push(
|
|
49744
|
-
` State: ${getStateDescription(addon.state)}`
|
|
49745
|
-
);
|
|
49746
|
-
lines.push(
|
|
49747
|
-
` Access: ${getAccessStatusDescription(addon.accessStatus)}`
|
|
49748
|
-
);
|
|
49749
|
-
}
|
|
49750
|
-
}
|
|
49751
|
-
return successResult(lines);
|
|
49752
|
-
} catch (err) {
|
|
49753
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49754
|
-
return errorResult(`Failed to get addon services: ${message}`);
|
|
49012
|
+
const node = {
|
|
49013
|
+
name: group.name,
|
|
49014
|
+
description: group.descriptionShort,
|
|
49015
|
+
source: "custom"
|
|
49016
|
+
};
|
|
49017
|
+
if (children.size > 0) {
|
|
49018
|
+
node.children = children;
|
|
49019
|
+
}
|
|
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));
|
|
49026
|
+
}
|
|
49027
|
+
for (const [name, group] of domain.subcommands) {
|
|
49028
|
+
children.set(name, fromSubcommandGroup(group));
|
|
49029
|
+
}
|
|
49030
|
+
const node = {
|
|
49031
|
+
name: domain.name,
|
|
49032
|
+
description: domain.descriptionShort,
|
|
49033
|
+
source: "custom"
|
|
49034
|
+
};
|
|
49035
|
+
if (children.size > 0) {
|
|
49036
|
+
node.children = children;
|
|
49037
|
+
}
|
|
49038
|
+
return node;
|
|
49039
|
+
}
|
|
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"
|
|
49047
|
+
});
|
|
49048
|
+
}
|
|
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;
|
|
49057
|
+
}
|
|
49058
|
+
return node;
|
|
49059
|
+
}
|
|
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, "\\'");
|
|
49070
|
+
}
|
|
49071
|
+
|
|
49072
|
+
// src/domains/completion/generators.ts
|
|
49073
|
+
function generateBashCompletion() {
|
|
49074
|
+
const domains = completionRegistry.getDomains();
|
|
49075
|
+
const domainNames = domains.map((d) => d.name).join(" ");
|
|
49076
|
+
const allAliases = [];
|
|
49077
|
+
for (const domain of domains) {
|
|
49078
|
+
if (domain.aliases) {
|
|
49079
|
+
allAliases.push(...domain.aliases);
|
|
49755
49080
|
}
|
|
49756
49081
|
}
|
|
49757
|
-
|
|
49758
|
-
|
|
49759
|
-
|
|
49760
|
-
|
|
49761
|
-
|
|
49762
|
-
|
|
49763
|
-
|
|
49764
|
-
|
|
49765
|
-
|
|
49766
|
-
|
|
49767
|
-
|
|
49768
|
-
|
|
49769
|
-
|
|
49082
|
+
const actionDescriptions2 = getActionDescriptions();
|
|
49083
|
+
const actions = Object.keys(actionDescriptions2).join(" ");
|
|
49084
|
+
const customDomainCompletions = [];
|
|
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) {
|
|
49089
|
+
customDomainCompletions.push(
|
|
49090
|
+
` ${domain.name})`,
|
|
49091
|
+
` COMPREPLY=($(compgen -W "${childNames.join(" ")}" -- "\${cur}"))`,
|
|
49092
|
+
` return 0`,
|
|
49093
|
+
` ;;`
|
|
49094
|
+
);
|
|
49770
49095
|
}
|
|
49771
|
-
|
|
49772
|
-
|
|
49773
|
-
|
|
49774
|
-
|
|
49775
|
-
|
|
49776
|
-
|
|
49777
|
-
|
|
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
|
+
` ;;`
|
|
49778
49104
|
);
|
|
49779
49105
|
}
|
|
49780
|
-
if (jsonOutput || format === "json") {
|
|
49781
|
-
return successResult([
|
|
49782
|
-
JSON.stringify({ ...quotaInfo, objects }, null, 2)
|
|
49783
|
-
]);
|
|
49784
|
-
}
|
|
49785
|
-
if (objects.length === 0) {
|
|
49786
|
-
return successResult([
|
|
49787
|
-
showWarnings ? "No quota warnings or exceeded limits" : "No quota information available"
|
|
49788
|
-
]);
|
|
49789
|
-
}
|
|
49790
|
-
const lines = [];
|
|
49791
|
-
lines.push("=== Quota Usage (Tenant-Level) ===");
|
|
49792
|
-
lines.push("");
|
|
49793
|
-
const exceeded = objects.filter(
|
|
49794
|
-
(q) => q.status === QuotaStatus.Exceeded
|
|
49795
|
-
);
|
|
49796
|
-
const warning = objects.filter(
|
|
49797
|
-
(q) => q.status === QuotaStatus.Warning
|
|
49798
|
-
);
|
|
49799
|
-
const ok = objects.filter((q) => q.status === QuotaStatus.OK);
|
|
49800
|
-
if (exceeded.length > 0) {
|
|
49801
|
-
lines.push("--- Exceeded ---");
|
|
49802
|
-
for (const q of exceeded) {
|
|
49803
|
-
lines.push(` [!!] ${q.displayName}`);
|
|
49804
|
-
lines.push(
|
|
49805
|
-
` Usage: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
|
|
49806
|
-
);
|
|
49807
|
-
}
|
|
49808
|
-
lines.push("");
|
|
49809
|
-
}
|
|
49810
|
-
if (warning.length > 0) {
|
|
49811
|
-
lines.push("--- Warning (>80%) ---");
|
|
49812
|
-
for (const q of warning) {
|
|
49813
|
-
lines.push(` [!] ${q.displayName}`);
|
|
49814
|
-
lines.push(
|
|
49815
|
-
` Usage: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
|
|
49816
|
-
);
|
|
49817
|
-
}
|
|
49818
|
-
lines.push("");
|
|
49819
|
-
}
|
|
49820
|
-
if (ok.length > 0 && !showWarnings) {
|
|
49821
|
-
lines.push("--- OK ---");
|
|
49822
|
-
for (const q of ok) {
|
|
49823
|
-
lines.push(
|
|
49824
|
-
` [OK] ${q.displayName}: ${q.usage} / ${q.limit} (${Math.round(q.percentage)}%)`
|
|
49825
|
-
);
|
|
49826
|
-
}
|
|
49827
|
-
}
|
|
49828
|
-
return successResult(lines);
|
|
49829
|
-
} catch (err) {
|
|
49830
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49831
|
-
return errorResult(`Failed to get quota info: ${message}`);
|
|
49832
|
-
}
|
|
49833
|
-
}
|
|
49834
|
-
};
|
|
49835
|
-
var validateCommand = {
|
|
49836
|
-
name: "validate",
|
|
49837
|
-
description: "Pre-deployment validation for quotas and features",
|
|
49838
|
-
usage: "[--resource-type <type> --count <n>] [--feature <name>] [--json]",
|
|
49839
|
-
async execute(args, session) {
|
|
49840
|
-
const jsonOutput = args.includes("--json");
|
|
49841
|
-
const format = session.getOutputFormat();
|
|
49842
|
-
let resourceType;
|
|
49843
|
-
const resourceIdx = args.indexOf("--resource-type");
|
|
49844
|
-
if (resourceIdx !== -1 && args[resourceIdx + 1]) {
|
|
49845
|
-
resourceType = args[resourceIdx + 1];
|
|
49846
|
-
}
|
|
49847
|
-
let count;
|
|
49848
|
-
const countIdx = args.indexOf("--count");
|
|
49849
|
-
if (countIdx !== -1) {
|
|
49850
|
-
const countArg = args[countIdx + 1];
|
|
49851
|
-
if (countArg) {
|
|
49852
|
-
count = parseInt(countArg, 10);
|
|
49853
|
-
}
|
|
49854
|
-
}
|
|
49855
|
-
let feature;
|
|
49856
|
-
const featureIdx = args.indexOf("--feature");
|
|
49857
|
-
if (featureIdx !== -1 && args[featureIdx + 1]) {
|
|
49858
|
-
feature = args[featureIdx + 1];
|
|
49859
|
-
}
|
|
49860
|
-
if (!resourceType && !feature) {
|
|
49861
|
-
return errorResult(
|
|
49862
|
-
"Please specify --resource-type and --count, or --feature to validate"
|
|
49863
|
-
);
|
|
49864
|
-
}
|
|
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 result = await client.validateResource({
|
|
49872
|
-
resourceType: resourceType ?? "",
|
|
49873
|
-
count: count ?? 0,
|
|
49874
|
-
feature: feature ?? ""
|
|
49875
|
-
});
|
|
49876
|
-
if (jsonOutput || format === "json") {
|
|
49877
|
-
return successResult([JSON.stringify(result, null, 2)]);
|
|
49878
|
-
}
|
|
49879
|
-
const lines = [];
|
|
49880
|
-
lines.push("=== Validation Result ===");
|
|
49881
|
-
lines.push("");
|
|
49882
|
-
lines.push(`Status: ${result.valid ? "[PASS]" : "[FAIL]"}`);
|
|
49883
|
-
lines.push("");
|
|
49884
|
-
if (result.checks.length > 0) {
|
|
49885
|
-
lines.push("--- Checks ---");
|
|
49886
|
-
for (const check of result.checks) {
|
|
49887
|
-
const icon = check.result === "PASS" ? "[OK]" : check.result === "WARNING" ? "[!]" : "[X]";
|
|
49888
|
-
lines.push(
|
|
49889
|
-
` ${icon} [${check.type}] ${check.message ?? ""}`
|
|
49890
|
-
);
|
|
49891
|
-
}
|
|
49892
|
-
}
|
|
49893
|
-
if (result.errors && result.errors.length > 0) {
|
|
49894
|
-
lines.push("");
|
|
49895
|
-
lines.push("--- Errors ---");
|
|
49896
|
-
for (const error of result.errors) {
|
|
49897
|
-
lines.push(` [X] ${error}`);
|
|
49898
|
-
}
|
|
49899
|
-
}
|
|
49900
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
49901
|
-
lines.push("");
|
|
49902
|
-
lines.push("--- Warnings ---");
|
|
49903
|
-
for (const warning of result.warnings) {
|
|
49904
|
-
lines.push(` [!] ${warning}`);
|
|
49905
|
-
}
|
|
49906
|
-
}
|
|
49907
|
-
const commandResult = {
|
|
49908
|
-
output: lines,
|
|
49909
|
-
shouldExit: false,
|
|
49910
|
-
shouldClear: false,
|
|
49911
|
-
contextChanged: false
|
|
49912
|
-
};
|
|
49913
|
-
if (!result.valid) {
|
|
49914
|
-
commandResult.error = "Validation failed";
|
|
49915
|
-
}
|
|
49916
|
-
return commandResult;
|
|
49917
|
-
} catch (err) {
|
|
49918
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49919
|
-
return errorResult(`Validation failed: ${message}`);
|
|
49920
|
-
}
|
|
49921
|
-
}
|
|
49922
|
-
};
|
|
49923
|
-
var activationStatusCommand = {
|
|
49924
|
-
name: "activation-status",
|
|
49925
|
-
description: "Check pending addon activation requests",
|
|
49926
|
-
usage: "[--json]",
|
|
49927
|
-
async execute(args, session) {
|
|
49928
|
-
const jsonOutput = args.includes("--json");
|
|
49929
|
-
const format = session.getOutputFormat();
|
|
49930
|
-
const apiClient = session.getAPIClient();
|
|
49931
|
-
if (!apiClient) {
|
|
49932
|
-
return errorResult("Not authenticated. Use 'login' command first.");
|
|
49933
|
-
}
|
|
49934
|
-
try {
|
|
49935
|
-
const client = getClient2(apiClient);
|
|
49936
|
-
const result = await client.getPendingActivations("system");
|
|
49937
|
-
if (jsonOutput || format === "json") {
|
|
49938
|
-
return successResult([JSON.stringify(result, null, 2)]);
|
|
49939
|
-
}
|
|
49940
|
-
const lines = [];
|
|
49941
|
-
lines.push("=== Activation Status ===");
|
|
49942
|
-
lines.push("");
|
|
49943
|
-
if (result.pendingActivations.length === 0) {
|
|
49944
|
-
lines.push("No pending activation requests.");
|
|
49945
|
-
} else {
|
|
49946
|
-
lines.push(`Pending Activations: ${result.totalPending}`);
|
|
49947
|
-
lines.push("");
|
|
49948
|
-
for (const pending of result.pendingActivations) {
|
|
49949
|
-
lines.push(` [...] ${pending.addonService}`);
|
|
49950
|
-
if (pending.message) {
|
|
49951
|
-
lines.push(` Status: ${pending.message}`);
|
|
49952
|
-
}
|
|
49953
|
-
}
|
|
49954
|
-
}
|
|
49955
|
-
lines.push("");
|
|
49956
|
-
lines.push(`Active Addons: ${result.activeAddons.length}`);
|
|
49957
|
-
if (result.activeAddons.length > 0) {
|
|
49958
|
-
for (const addon of result.activeAddons) {
|
|
49959
|
-
lines.push(` [OK] ${addon}`);
|
|
49960
|
-
}
|
|
49961
|
-
}
|
|
49962
|
-
return successResult(lines);
|
|
49963
|
-
} catch (err) {
|
|
49964
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
49965
|
-
return errorResult(`Failed to get activation status: ${message}`);
|
|
49966
|
-
}
|
|
49967
|
-
}
|
|
49968
|
-
};
|
|
49969
|
-
var subscriptionExtension = {
|
|
49970
|
-
targetDomain: "subscription",
|
|
49971
|
-
description: "xcsh-specific subscription management commands (overview, quota analysis, validation)",
|
|
49972
|
-
standalone: true,
|
|
49973
|
-
// Works even before upstream adds subscription domain
|
|
49974
|
-
commands: /* @__PURE__ */ new Map([
|
|
49975
|
-
["overview", overviewCommand],
|
|
49976
|
-
["addons", addonsCommand],
|
|
49977
|
-
["quota", quotaCommand],
|
|
49978
|
-
["validate", validateCommand],
|
|
49979
|
-
["activation-status", activationStatusCommand]
|
|
49980
|
-
]),
|
|
49981
|
-
subcommands: /* @__PURE__ */ new Map()
|
|
49982
|
-
};
|
|
49983
|
-
|
|
49984
|
-
// src/extensions/index.ts
|
|
49985
|
-
function initializeExtensions() {
|
|
49986
|
-
extensionRegistry.register(subscriptionExtension);
|
|
49987
|
-
}
|
|
49988
|
-
initializeExtensions();
|
|
49989
|
-
|
|
49990
|
-
// src/domains/completion/generators.ts
|
|
49991
|
-
function getAllDomains() {
|
|
49992
|
-
const domains = [];
|
|
49993
|
-
const seen = /* @__PURE__ */ new Set();
|
|
49994
|
-
for (const domain of customDomains.all()) {
|
|
49995
|
-
domains.push({
|
|
49996
|
-
name: domain.name,
|
|
49997
|
-
description: domain.descriptionShort,
|
|
49998
|
-
aliases: []
|
|
49999
|
-
});
|
|
50000
|
-
seen.add(domain.name);
|
|
50001
|
-
}
|
|
50002
|
-
for (const extDomain of extensionRegistry.getExtendedDomains()) {
|
|
50003
|
-
if (seen.has(extDomain)) continue;
|
|
50004
|
-
if (domainRegistry.has(extDomain)) continue;
|
|
50005
|
-
const merged = extensionRegistry.getMergedDomain(extDomain);
|
|
50006
|
-
if (merged && merged.source === "extension") {
|
|
50007
|
-
domains.push({
|
|
50008
|
-
name: extDomain,
|
|
50009
|
-
description: merged.description,
|
|
50010
|
-
aliases: []
|
|
50011
|
-
});
|
|
50012
|
-
seen.add(extDomain);
|
|
50013
|
-
}
|
|
50014
|
-
}
|
|
50015
|
-
for (const [name, info] of domainRegistry) {
|
|
50016
|
-
if (seen.has(name)) continue;
|
|
50017
|
-
domains.push({
|
|
50018
|
-
name,
|
|
50019
|
-
description: info.descriptionShort,
|
|
50020
|
-
aliases: info.aliases
|
|
50021
|
-
});
|
|
50022
|
-
seen.add(name);
|
|
50023
|
-
}
|
|
50024
|
-
return domains.sort((a, b) => a.name.localeCompare(b.name));
|
|
50025
|
-
}
|
|
50026
|
-
function getCustomDomainCommands(domainName) {
|
|
50027
|
-
const domain = customDomains.get(domainName);
|
|
50028
|
-
if (!domain) {
|
|
50029
|
-
return { commands: [], subcommands: /* @__PURE__ */ new Map() };
|
|
50030
|
-
}
|
|
50031
|
-
const commands = Array.from(domain.commands.keys());
|
|
50032
|
-
const subcommands = /* @__PURE__ */ new Map();
|
|
50033
|
-
for (const [groupName, group] of domain.subcommands) {
|
|
50034
|
-
subcommands.set(groupName, Array.from(group.commands.keys()));
|
|
50035
|
-
}
|
|
50036
|
-
return { commands, subcommands };
|
|
50037
|
-
}
|
|
50038
|
-
function generateBashCompletion() {
|
|
50039
|
-
const domains = getAllDomains();
|
|
50040
|
-
const domainNames = domains.map((d) => d.name).join(" ");
|
|
50041
|
-
const allAliases = domains.flatMap((d) => d.aliases).join(" ");
|
|
50042
|
-
const actions = Array.from(validActions).join(" ");
|
|
50043
|
-
const customDomainCompletions = [];
|
|
50044
|
-
for (const domain of customDomains.all()) {
|
|
50045
|
-
const { commands, subcommands } = getCustomDomainCommands(domain.name);
|
|
50046
|
-
if (commands.length > 0 || subcommands.size > 0) {
|
|
50047
|
-
const allCommands = [
|
|
50048
|
-
...commands,
|
|
50049
|
-
...Array.from(subcommands.keys())
|
|
50050
|
-
];
|
|
50051
|
-
customDomainCompletions.push(
|
|
50052
|
-
` ${domain.name})`,
|
|
50053
|
-
` COMPREPLY=($(compgen -W "${allCommands.join(" ")}" -- "\${cur}"))`,
|
|
50054
|
-
` return 0`,
|
|
50055
|
-
` ;;`
|
|
50056
|
-
);
|
|
50057
|
-
}
|
|
50058
|
-
for (const [groupName, groupCommands] of subcommands) {
|
|
50059
|
-
customDomainCompletions.push(
|
|
50060
|
-
` ${domain.name}/${groupName})`,
|
|
50061
|
-
` COMPREPLY=($(compgen -W "${groupCommands.join(" ")}" -- "\${cur}"))`,
|
|
50062
|
-
` return 0`,
|
|
50063
|
-
` ;;`
|
|
50064
|
-
);
|
|
50065
49106
|
}
|
|
50066
49107
|
}
|
|
50067
49108
|
return `# bash completion for xcsh
|
|
@@ -50072,7 +49113,7 @@ _xcsh_completions() {
|
|
|
50072
49113
|
local cur prev words cword
|
|
50073
49114
|
_init_completion || return
|
|
50074
49115
|
|
|
50075
|
-
local commands="${domainNames} ${allAliases} help quit exit clear history"
|
|
49116
|
+
local commands="${domainNames} ${allAliases.join(" ")} help quit exit clear history"
|
|
50076
49117
|
local actions="${actions}"
|
|
50077
49118
|
local builtins="help quit exit clear history context ctx"
|
|
50078
49119
|
local global_flags="--help -h --version -v --interactive -i --no-color --output -o --namespace -ns"
|
|
@@ -50118,36 +49159,29 @@ complete -F _xcsh_completions xcsh
|
|
|
50118
49159
|
`;
|
|
50119
49160
|
}
|
|
50120
49161
|
function generateZshCompletion() {
|
|
50121
|
-
const domains =
|
|
49162
|
+
const domains = completionRegistry.getDomains();
|
|
50122
49163
|
const domainDescriptions = domains.map((d) => {
|
|
50123
|
-
const escaped = d.description
|
|
49164
|
+
const escaped = escapeForZsh(d.description);
|
|
50124
49165
|
return `'${d.name}:${escaped}'`;
|
|
50125
49166
|
}).join("\n ");
|
|
50126
|
-
const aliasDescriptions = domains.flatMap((d) => d.aliases.map((a) => `'${a}:Alias for ${d.name}'`)).join("\n ");
|
|
50127
|
-
const
|
|
50128
|
-
|
|
50129
|
-
|
|
50130
|
-
|
|
50131
|
-
|
|
50132
|
-
"'replace:Replace a resource'",
|
|
50133
|
-
"'apply:Apply configuration from file'",
|
|
50134
|
-
"'status:Get resource status'",
|
|
50135
|
-
"'patch:Patch a resource'",
|
|
50136
|
-
"'add-labels:Add labels to a resource'",
|
|
50137
|
-
"'remove-labels:Remove labels from a resource'"
|
|
50138
|
-
].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 ");
|
|
50139
49173
|
const customDomainCompletions = [];
|
|
50140
|
-
for (const domain of
|
|
50141
|
-
|
|
50142
|
-
|
|
50143
|
-
|
|
50144
|
-
const
|
|
50145
|
-
|
|
50146
|
-
|
|
50147
|
-
|
|
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) {
|
|
50148
49182
|
customDomainCompletions.push(
|
|
50149
49183
|
` (${domain.name})`,
|
|
50150
|
-
` _values 'command' ${
|
|
49184
|
+
` _values 'command' ${childDescriptions.join(" ")}`,
|
|
50151
49185
|
` ;;`
|
|
50152
49186
|
);
|
|
50153
49187
|
}
|
|
@@ -50203,7 +49237,7 @@ ${customDomainCompletions.join("\n")}
|
|
|
50203
49237
|
(*)
|
|
50204
49238
|
local -a actions
|
|
50205
49239
|
actions=(
|
|
50206
|
-
${
|
|
49240
|
+
${actionDescArray}
|
|
50207
49241
|
)
|
|
50208
49242
|
_describe -t actions 'action' actions
|
|
50209
49243
|
;;
|
|
@@ -50234,157 +49268,417 @@ _xcsh "$@"
|
|
|
50234
49268
|
`;
|
|
50235
49269
|
}
|
|
50236
49270
|
function generateFishCompletion() {
|
|
50237
|
-
const domains =
|
|
50238
|
-
const
|
|
49271
|
+
const domains = completionRegistry.getDomains();
|
|
49272
|
+
const actionDescriptions2 = getActionDescriptions();
|
|
50239
49273
|
const domainCompletions = domains.map((d) => {
|
|
50240
|
-
const escaped = d.description
|
|
49274
|
+
const escaped = escapeForFish(d.description);
|
|
50241
49275
|
return `complete -c xcsh -n "__fish_use_subcommand" -a "${d.name}" -d '${escaped}'`;
|
|
50242
49276
|
}).join("\n");
|
|
50243
|
-
const aliasCompletions = domains.flatMap(
|
|
49277
|
+
const aliasCompletions = domains.filter((d) => d.aliases && d.aliases.length > 0).flatMap(
|
|
50244
49278
|
(d) => d.aliases.map(
|
|
50245
49279
|
(a) => `complete -c xcsh -n "__fish_use_subcommand" -a "${a}" -d 'Alias for ${d.name}'`
|
|
50246
49280
|
)
|
|
50247
49281
|
).join("\n");
|
|
50248
|
-
const actionCompletions = domains.map(
|
|
50249
|
-
(d) => actions.map(
|
|
50250
|
-
(a) => `complete -c xcsh -n "__fish_seen_subcommand_from ${d.name}" -a "${a}" -d '${a.charAt(0).toUpperCase() + a.slice(1)} resources'`
|
|
50251
|
-
).join("\n")
|
|
50252
|
-
).join("\n");
|
|
50253
49282
|
const customDomainCompletions = [];
|
|
50254
|
-
for (const domain of
|
|
50255
|
-
|
|
50256
|
-
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);
|
|
50257
49287
|
customDomainCompletions.push(
|
|
50258
|
-
`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}'`
|
|
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
|
+
}
|
|
49298
|
+
}
|
|
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"}`
|
|
50259
49364
|
);
|
|
50260
49365
|
}
|
|
50261
|
-
|
|
50262
|
-
|
|
50263
|
-
|
|
50264
|
-
|
|
50265
|
-
|
|
50266
|
-
|
|
50267
|
-
|
|
50268
|
-
|
|
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;
|
|
50269
49610
|
}
|
|
50270
49611
|
}
|
|
49612
|
+
return void 0;
|
|
50271
49613
|
}
|
|
50272
|
-
|
|
50273
|
-
|
|
50274
|
-
|
|
50275
|
-
|
|
50276
|
-
|
|
50277
|
-
|
|
50278
|
-
|
|
50279
|
-
|
|
50280
|
-
|
|
50281
|
-
|
|
50282
|
-
complete -c xcsh -l no-color -d 'Disable color output'
|
|
50283
|
-
complete -c xcsh -s o -l output -d 'Output format' -xa 'json yaml table'
|
|
50284
|
-
complete -c xcsh -l namespace -s ns -d 'Namespace' -xa 'default system shared'
|
|
50285
|
-
|
|
50286
|
-
# Builtin commands
|
|
50287
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "help" -d 'Show help information'
|
|
50288
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "quit" -d 'Exit the shell'
|
|
50289
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "exit" -d 'Exit the shell'
|
|
50290
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "clear" -d 'Clear the screen'
|
|
50291
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "history" -d 'Show command history'
|
|
50292
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "context" -d 'Show current navigation context'
|
|
50293
|
-
complete -c xcsh -n "__fish_use_subcommand" -a "ctx" -d 'Show current navigation context'
|
|
50294
|
-
|
|
50295
|
-
# Domain completions
|
|
50296
|
-
${domainCompletions}
|
|
50297
|
-
|
|
50298
|
-
# Alias completions
|
|
50299
|
-
${aliasCompletions}
|
|
50300
|
-
|
|
50301
|
-
# Custom domain subcommands
|
|
50302
|
-
${customDomainCompletions.join("\n")}
|
|
50303
|
-
|
|
50304
|
-
# Action completions for API domains
|
|
50305
|
-
${actionCompletions}
|
|
50306
|
-
|
|
50307
|
-
# Action-specific flags
|
|
50308
|
-
complete -c xcsh -l name -s n -d 'Resource name'
|
|
50309
|
-
complete -c xcsh -l limit -d 'Maximum results'
|
|
50310
|
-
complete -c xcsh -l label -d 'Filter by label'
|
|
50311
|
-
complete -c xcsh -s f -l file -d 'Configuration file' -r
|
|
50312
|
-
complete -c xcsh -l force -d 'Force deletion'
|
|
50313
|
-
complete -c xcsh -l cascade -d 'Cascade delete'
|
|
50314
|
-
`;
|
|
50315
|
-
}
|
|
50316
|
-
|
|
50317
|
-
// src/domains/completion/index.ts
|
|
50318
|
-
var bashCommand = {
|
|
50319
|
-
name: "bash",
|
|
50320
|
-
description: "Generate bash completion script",
|
|
50321
|
-
async execute() {
|
|
50322
|
-
try {
|
|
50323
|
-
const script = generateBashCompletion();
|
|
50324
|
-
return successResult([script]);
|
|
50325
|
-
} catch (error) {
|
|
50326
|
-
return errorResult(
|
|
50327
|
-
`Failed to generate bash completion: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
50328
|
-
);
|
|
50329
|
-
}
|
|
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);
|
|
50330
49624
|
}
|
|
50331
|
-
|
|
50332
|
-
|
|
50333
|
-
|
|
50334
|
-
|
|
50335
|
-
|
|
50336
|
-
|
|
50337
|
-
|
|
50338
|
-
|
|
50339
|
-
|
|
50340
|
-
|
|
50341
|
-
|
|
50342
|
-
|
|
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
|
+
}
|
|
50343
49646
|
}
|
|
49647
|
+
return names;
|
|
50344
49648
|
}
|
|
50345
|
-
|
|
50346
|
-
|
|
50347
|
-
|
|
50348
|
-
|
|
50349
|
-
|
|
50350
|
-
|
|
50351
|
-
|
|
50352
|
-
|
|
50353
|
-
|
|
50354
|
-
|
|
50355
|
-
|
|
50356
|
-
|
|
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
|
+
}
|
|
50357
49668
|
}
|
|
49669
|
+
return {
|
|
49670
|
+
extensionCount: this.extensions.size,
|
|
49671
|
+
standaloneCount,
|
|
49672
|
+
mergedCount
|
|
49673
|
+
};
|
|
50358
49674
|
}
|
|
50359
49675
|
};
|
|
50360
|
-
var
|
|
50361
|
-
name: "completion",
|
|
50362
|
-
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.",
|
|
50363
|
-
descriptionShort: "Shell completion script generation",
|
|
50364
|
-
descriptionMedium: "Generate tab-completion scripts for bash, zsh, and fish shells to enhance the xcsh command-line experience.",
|
|
50365
|
-
commands: /* @__PURE__ */ new Map([
|
|
50366
|
-
["bash", bashCommand],
|
|
50367
|
-
["zsh", zshCommand],
|
|
50368
|
-
["fish", fishCommand]
|
|
50369
|
-
]),
|
|
50370
|
-
subcommands: /* @__PURE__ */ new Map()
|
|
50371
|
-
};
|
|
49676
|
+
var extensionRegistry = new ExtensionRegistry();
|
|
50372
49677
|
|
|
50373
|
-
// src/
|
|
50374
|
-
|
|
50375
|
-
customDomains.register(cloudstatusDomain);
|
|
50376
|
-
customDomains.register(completionDomain);
|
|
50377
|
-
var domainAliases = /* @__PURE__ */ new Map();
|
|
50378
|
-
for (const alias of cloudstatusAliases) {
|
|
50379
|
-
domainAliases.set(alias, "cloudstatus");
|
|
50380
|
-
}
|
|
50381
|
-
function resolveDomainAlias(name) {
|
|
50382
|
-
return domainAliases.get(name) ?? name;
|
|
50383
|
-
}
|
|
50384
|
-
function isCustomDomain(name) {
|
|
50385
|
-
const canonical = resolveDomainAlias(name);
|
|
50386
|
-
return customDomains.has(canonical);
|
|
49678
|
+
// src/extensions/index.ts
|
|
49679
|
+
function initializeExtensions() {
|
|
50387
49680
|
}
|
|
49681
|
+
initializeExtensions();
|
|
50388
49682
|
|
|
50389
49683
|
// src/repl/completion/cache.ts
|
|
50390
49684
|
var DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
@@ -50604,7 +49898,25 @@ var Completer = class {
|
|
|
50604
49898
|
}
|
|
50605
49899
|
let suggestions;
|
|
50606
49900
|
if (parsed.isEscapedToRoot) {
|
|
50607
|
-
|
|
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
|
+
}
|
|
50608
49920
|
} else {
|
|
50609
49921
|
suggestions = await this.getContextualSuggestions();
|
|
50610
49922
|
}
|
|
@@ -50615,53 +49927,36 @@ var Completer = class {
|
|
|
50615
49927
|
}
|
|
50616
49928
|
/**
|
|
50617
49929
|
* Get completions for custom domain commands
|
|
49930
|
+
* Uses unified completion registry for structure navigation,
|
|
49931
|
+
* falls back to domain handlers for argument completions
|
|
50618
49932
|
*/
|
|
50619
49933
|
async getCustomDomainCompletions(domainName, args, currentWord) {
|
|
50620
|
-
const
|
|
50621
|
-
if (!
|
|
49934
|
+
const domainNode = completionRegistry.get(domainName);
|
|
49935
|
+
if (!domainNode) {
|
|
50622
49936
|
return [];
|
|
50623
49937
|
}
|
|
50624
|
-
const suggestions = [];
|
|
50625
49938
|
if (args.length === 0 || args.length === 1 && currentWord === args[0]) {
|
|
50626
|
-
|
|
50627
|
-
|
|
50628
|
-
|
|
50629
|
-
|
|
50630
|
-
description: group.description,
|
|
50631
|
-
category: "subcommand"
|
|
50632
|
-
});
|
|
50633
|
-
}
|
|
50634
|
-
}
|
|
50635
|
-
for (const [name, cmd] of domain.commands) {
|
|
50636
|
-
if (!currentWord || name.toLowerCase().startsWith(currentWord.toLowerCase())) {
|
|
50637
|
-
suggestions.push({
|
|
50638
|
-
text: name,
|
|
50639
|
-
description: cmd.description,
|
|
50640
|
-
category: "command"
|
|
50641
|
-
});
|
|
50642
|
-
}
|
|
50643
|
-
}
|
|
50644
|
-
return suggestions;
|
|
49939
|
+
return completionRegistry.getChildSuggestions(
|
|
49940
|
+
domainName,
|
|
49941
|
+
currentWord
|
|
49942
|
+
);
|
|
50645
49943
|
}
|
|
50646
49944
|
const subgroupName = args[0]?.toLowerCase() ?? "";
|
|
50647
|
-
const
|
|
50648
|
-
if (
|
|
49945
|
+
const subgroupNode = domainNode.children?.get(subgroupName);
|
|
49946
|
+
if (subgroupNode?.children) {
|
|
50649
49947
|
if (args.length === 1 || args.length === 2 && currentWord === args[1]) {
|
|
50650
|
-
const
|
|
50651
|
-
|
|
50652
|
-
|
|
50653
|
-
|
|
50654
|
-
|
|
50655
|
-
|
|
50656
|
-
category: "command"
|
|
50657
|
-
});
|
|
50658
|
-
}
|
|
50659
|
-
}
|
|
50660
|
-
return suggestions;
|
|
49948
|
+
const prefix = args.length === 2 ? currentWord : "";
|
|
49949
|
+
return completionRegistry.getNestedChildSuggestions(
|
|
49950
|
+
domainName,
|
|
49951
|
+
[subgroupName],
|
|
49952
|
+
prefix
|
|
49953
|
+
);
|
|
50661
49954
|
}
|
|
50662
49955
|
if (args.length >= 2 && this.session) {
|
|
50663
49956
|
const cmdName = args[1]?.toLowerCase() ?? "";
|
|
50664
|
-
const
|
|
49957
|
+
const domain2 = customDomains.get(domainName);
|
|
49958
|
+
const subgroup = domain2?.subcommands.get(subgroupName);
|
|
49959
|
+
const cmd = subgroup?.commands.get(cmdName);
|
|
50665
49960
|
if (cmd?.completion) {
|
|
50666
49961
|
try {
|
|
50667
49962
|
const completions = await cmd.completion(
|
|
@@ -50680,8 +49975,9 @@ var Completer = class {
|
|
|
50680
49975
|
}
|
|
50681
49976
|
}
|
|
50682
49977
|
const directCmdName = args[0]?.toLowerCase() ?? "";
|
|
50683
|
-
const
|
|
50684
|
-
|
|
49978
|
+
const domain = customDomains.get(domainName);
|
|
49979
|
+
const directCmd = domain?.commands.get(directCmdName);
|
|
49980
|
+
if (directCmd?.completion && this.session) {
|
|
50685
49981
|
try {
|
|
50686
49982
|
const completions = await directCmd.completion(
|
|
50687
49983
|
currentWord,
|
|
@@ -50696,7 +49992,7 @@ var Completer = class {
|
|
|
50696
49992
|
} catch {
|
|
50697
49993
|
}
|
|
50698
49994
|
}
|
|
50699
|
-
return
|
|
49995
|
+
return [];
|
|
50700
49996
|
}
|
|
50701
49997
|
/**
|
|
50702
49998
|
* Get suggestions based on current navigation context
|
|
@@ -50872,95 +50168,23 @@ var Completer = class {
|
|
|
50872
50168
|
return suggestions;
|
|
50873
50169
|
}
|
|
50874
50170
|
/**
|
|
50875
|
-
* Get domain suggestions from registry
|
|
50171
|
+
* Get domain suggestions from unified registry
|
|
50876
50172
|
*/
|
|
50877
50173
|
getDomainSuggestions() {
|
|
50878
|
-
|
|
50879
|
-
const addedDomains = /* @__PURE__ */ new Set();
|
|
50880
|
-
for (const domain of customDomains.all()) {
|
|
50881
|
-
suggestions.push({
|
|
50882
|
-
text: domain.name,
|
|
50883
|
-
description: domain.description,
|
|
50884
|
-
category: "domain"
|
|
50885
|
-
});
|
|
50886
|
-
addedDomains.add(domain.name);
|
|
50887
|
-
}
|
|
50888
|
-
for (const extDomain of extensionRegistry.getExtendedDomains()) {
|
|
50889
|
-
if (addedDomains.has(extDomain)) continue;
|
|
50890
|
-
if (domainRegistry.has(extDomain)) continue;
|
|
50891
|
-
const merged = extensionRegistry.getMergedDomain(extDomain);
|
|
50892
|
-
if (merged && merged.source === "extension") {
|
|
50893
|
-
suggestions.push({
|
|
50894
|
-
text: extDomain,
|
|
50895
|
-
description: merged.description,
|
|
50896
|
-
category: "domain"
|
|
50897
|
-
});
|
|
50898
|
-
addedDomains.add(extDomain);
|
|
50899
|
-
}
|
|
50900
|
-
}
|
|
50901
|
-
for (const [domain, meta] of domainRegistry) {
|
|
50902
|
-
if (addedDomains.has(domain)) continue;
|
|
50903
|
-
suggestions.push({
|
|
50904
|
-
text: domain,
|
|
50905
|
-
description: meta.descriptionShort,
|
|
50906
|
-
category: "domain"
|
|
50907
|
-
});
|
|
50908
|
-
addedDomains.add(domain);
|
|
50909
|
-
}
|
|
50910
|
-
return suggestions;
|
|
50174
|
+
return completionRegistry.getDomainSuggestions();
|
|
50911
50175
|
}
|
|
50912
50176
|
/**
|
|
50913
|
-
* Get action suggestions
|
|
50177
|
+
* Get action suggestions from unified registry
|
|
50914
50178
|
*/
|
|
50915
50179
|
getActionSuggestions() {
|
|
50916
|
-
|
|
50917
|
-
|
|
50918
|
-
{
|
|
50919
|
-
text:
|
|
50920
|
-
description
|
|
50921
|
-
category: "action"
|
|
50922
|
-
},
|
|
50923
|
-
{
|
|
50924
|
-
text: "create",
|
|
50925
|
-
description: "Create a new resource",
|
|
50926
|
-
category: "action"
|
|
50927
|
-
},
|
|
50928
|
-
{
|
|
50929
|
-
text: "delete",
|
|
50930
|
-
description: "Delete a resource",
|
|
50931
|
-
category: "action"
|
|
50932
|
-
},
|
|
50933
|
-
{
|
|
50934
|
-
text: "replace",
|
|
50935
|
-
description: "Replace a resource",
|
|
50936
|
-
category: "action"
|
|
50937
|
-
},
|
|
50938
|
-
{
|
|
50939
|
-
text: "apply",
|
|
50940
|
-
description: "Apply configuration from file",
|
|
50180
|
+
const actionDescriptions2 = getActionDescriptions();
|
|
50181
|
+
return Object.entries(actionDescriptions2).map(
|
|
50182
|
+
([action, description]) => ({
|
|
50183
|
+
text: action,
|
|
50184
|
+
description,
|
|
50941
50185
|
category: "action"
|
|
50942
|
-
}
|
|
50943
|
-
|
|
50944
|
-
text: "status",
|
|
50945
|
-
description: "Get resource status",
|
|
50946
|
-
category: "action"
|
|
50947
|
-
},
|
|
50948
|
-
{
|
|
50949
|
-
text: "patch",
|
|
50950
|
-
description: "Patch a resource",
|
|
50951
|
-
category: "action"
|
|
50952
|
-
},
|
|
50953
|
-
{
|
|
50954
|
-
text: "add-labels",
|
|
50955
|
-
description: "Add labels to a resource",
|
|
50956
|
-
category: "action"
|
|
50957
|
-
},
|
|
50958
|
-
{
|
|
50959
|
-
text: "remove-labels",
|
|
50960
|
-
description: "Remove labels from a resource",
|
|
50961
|
-
category: "action"
|
|
50962
|
-
}
|
|
50963
|
-
];
|
|
50186
|
+
})
|
|
50187
|
+
);
|
|
50964
50188
|
}
|
|
50965
50189
|
/**
|
|
50966
50190
|
* Get flag suggestions for current action
|
|
@@ -51574,7 +50798,7 @@ function formatDomainHelp(domain) {
|
|
|
51574
50798
|
output.push(` ${CLI_NAME} ${domain.name} <action> [options]`);
|
|
51575
50799
|
output.push("");
|
|
51576
50800
|
output.push("ACTIONS");
|
|
51577
|
-
const
|
|
50801
|
+
const actionDescriptions2 = {
|
|
51578
50802
|
list: "List resources",
|
|
51579
50803
|
get: "Get a specific resource by name",
|
|
51580
50804
|
create: "Create a new resource",
|
|
@@ -51587,7 +50811,7 @@ function formatDomainHelp(domain) {
|
|
|
51587
50811
|
"remove-labels": "Remove labels from a resource"
|
|
51588
50812
|
};
|
|
51589
50813
|
for (const action of validActions) {
|
|
51590
|
-
const desc =
|
|
50814
|
+
const desc = actionDescriptions2[action] ?? action;
|
|
51591
50815
|
output.push(` ${action.padEnd(16)} ${desc}`);
|
|
51592
50816
|
}
|
|
51593
50817
|
output.push("");
|
|
@@ -51623,7 +50847,7 @@ function formatDomainHelp(domain) {
|
|
|
51623
50847
|
function formatActionHelp(domainName, action) {
|
|
51624
50848
|
const domain = getDomainInfo(domainName);
|
|
51625
50849
|
const displayDomain = domain?.displayName ?? domainName;
|
|
51626
|
-
const
|
|
50850
|
+
const actionDescriptions2 = {
|
|
51627
50851
|
list: {
|
|
51628
50852
|
desc: "List all resources in the namespace",
|
|
51629
50853
|
usage: `${CLI_NAME} ${domainName} list [--limit N] [--label key=value]`
|
|
@@ -51665,7 +50889,7 @@ function formatActionHelp(domainName, action) {
|
|
|
51665
50889
|
usage: `${CLI_NAME} ${domainName} remove-labels <name> key`
|
|
51666
50890
|
}
|
|
51667
50891
|
};
|
|
51668
|
-
const actionInfo =
|
|
50892
|
+
const actionInfo = actionDescriptions2[action] ?? {
|
|
51669
50893
|
desc: `Execute ${action} operation`,
|
|
51670
50894
|
usage: `${CLI_NAME} ${domainName} ${action} [options]`
|
|
51671
50895
|
};
|