@robinmordasiewicz/f5xc-xcsh 2.0.41-2601192054 → 2.0.41-2601192346

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.
Files changed (2) hide show
  1. package/dist/index.js +639 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -41229,8 +41229,8 @@ var init_logo_renderer = __esm({
41229
41229
 
41230
41230
  // src/branding/index.ts
41231
41231
  function getVersion() {
41232
- if ("v2.0.41-2601192054") {
41233
- return "v2.0.41-2601192054";
41232
+ if ("v2.0.41-2601192346") {
41233
+ return "v2.0.41-2601192346";
41234
41234
  }
41235
41235
  if (process.env.XCSH_VERSION) {
41236
41236
  return process.env.XCSH_VERSION;
@@ -152862,12 +152862,21 @@ var init_ai_services = __esm({
152862
152862
  });
152863
152863
 
152864
152864
  // src/domains/subscription/client.ts
152865
+ var client_exports = {};
152866
+ __export(client_exports, {
152867
+ SubscriptionClient: () => SubscriptionClient,
152868
+ getSubscriptionClient: () => getSubscriptionClient,
152869
+ resetSubscriptionClient: () => resetSubscriptionClient
152870
+ });
152865
152871
  function getSubscriptionClient(apiClient) {
152866
152872
  if (!cachedClient2) {
152867
152873
  cachedClient2 = new SubscriptionClient(apiClient);
152868
152874
  }
152869
152875
  return cachedClient2;
152870
152876
  }
152877
+ function resetSubscriptionClient() {
152878
+ cachedClient2 = null;
152879
+ }
152871
152880
  var SubscriptionClient, cachedClient2;
152872
152881
  var init_client3 = __esm({
152873
152882
  "src/domains/subscription/client.ts"() {
@@ -163290,6 +163299,294 @@ var init_creation_flags = __esm({
163290
163299
  }
163291
163300
  });
163292
163301
 
163302
+ // src/quota/cache.ts
163303
+ function getQuotaCache() {
163304
+ if (!globalCache) {
163305
+ globalCache = new QuotaCache();
163306
+ }
163307
+ return globalCache;
163308
+ }
163309
+ function resetQuotaCache() {
163310
+ if (globalCache) {
163311
+ globalCache.invalidate();
163312
+ }
163313
+ globalCache = null;
163314
+ }
163315
+ var CACHE_TTL_MS, QuotaCache, globalCache;
163316
+ var init_cache2 = __esm({
163317
+ "src/quota/cache.ts"() {
163318
+ "use strict";
163319
+ CACHE_TTL_MS = 6e4;
163320
+ QuotaCache = class {
163321
+ cache = /* @__PURE__ */ new Map();
163322
+ /**
163323
+ * Get quota usage, using cache if available and fresh
163324
+ *
163325
+ * @param client - Subscription client for API calls
163326
+ * @param namespace - Namespace to get quota for
163327
+ * @returns Array of quota usage items
163328
+ */
163329
+ async getQuotaUsage(client, namespace) {
163330
+ const cacheKey = this.getCacheKey(namespace);
163331
+ const cached = this.cache.get(cacheKey);
163332
+ if (cached && !this.isExpired(cached.fetchedAt)) {
163333
+ return cached.usage;
163334
+ }
163335
+ const response = await client.getQuotaUsage(namespace);
163336
+ const usage = [];
163337
+ if (response.quota_usage) {
163338
+ for (const [name, item] of Object.entries(response.quota_usage)) {
163339
+ const current = item.usage?.current ?? 0;
163340
+ const limit = item.limit?.maximum ?? 0;
163341
+ const percentage = limit > 0 ? Math.round(current / limit * 100) : 0;
163342
+ usage.push({
163343
+ name,
163344
+ display_name: item.display_name || name,
163345
+ current,
163346
+ limit,
163347
+ percentage
163348
+ });
163349
+ }
163350
+ }
163351
+ this.cache.set(cacheKey, {
163352
+ usage,
163353
+ fetchedAt: Date.now(),
163354
+ namespace
163355
+ });
163356
+ return usage;
163357
+ }
163358
+ /**
163359
+ * Find quota usage for a specific quota name
163360
+ *
163361
+ * @param client - Subscription client for API calls
163362
+ * @param namespace - Namespace to get quota for
163363
+ * @param quotaName - API quota name to find
163364
+ * @returns Quota usage item or undefined if not found
163365
+ */
163366
+ async findQuotaUsage(client, namespace, quotaName) {
163367
+ const usage = await this.getQuotaUsage(client, namespace);
163368
+ return usage.find((q) => q.name === quotaName);
163369
+ }
163370
+ /**
163371
+ * Invalidate cache for a namespace or all namespaces
163372
+ *
163373
+ * @param namespace - Optional namespace to invalidate; if omitted, clears all
163374
+ */
163375
+ invalidate(namespace) {
163376
+ if (namespace) {
163377
+ this.cache.delete(this.getCacheKey(namespace));
163378
+ } else {
163379
+ this.cache.clear();
163380
+ }
163381
+ }
163382
+ /**
163383
+ * Generate cache key for a namespace
163384
+ */
163385
+ getCacheKey(namespace) {
163386
+ return `quota:${namespace}`;
163387
+ }
163388
+ /**
163389
+ * Check if cached data is expired
163390
+ */
163391
+ isExpired(fetchedAt) {
163392
+ return Date.now() - fetchedAt > CACHE_TTL_MS;
163393
+ }
163394
+ };
163395
+ globalCache = null;
163396
+ }
163397
+ });
163398
+
163399
+ // src/quota/mapping.ts
163400
+ var mapping_exports = {};
163401
+ __export(mapping_exports, {
163402
+ getAllQuotaMappings: () => getAllQuotaMappings,
163403
+ getQuotaMapping: () => getQuotaMapping,
163404
+ getQuotaMappingByName: () => getQuotaMappingByName,
163405
+ hasQuotaMapping: () => hasQuotaMapping
163406
+ });
163407
+ function getQuotaMapping(resourceType) {
163408
+ return MAPPING_BY_RESOURCE_TYPE.get(resourceType);
163409
+ }
163410
+ function getQuotaMappingByName(quotaName) {
163411
+ return MAPPING_BY_QUOTA_NAME.get(quotaName);
163412
+ }
163413
+ function getAllQuotaMappings() {
163414
+ return QUOTA_MAPPINGS;
163415
+ }
163416
+ function hasQuotaMapping(resourceType) {
163417
+ return MAPPING_BY_RESOURCE_TYPE.has(resourceType);
163418
+ }
163419
+ var QUOTA_MAPPINGS, MAPPING_BY_RESOURCE_TYPE, MAPPING_BY_QUOTA_NAME;
163420
+ var init_mapping = __esm({
163421
+ "src/quota/mapping.ts"() {
163422
+ "use strict";
163423
+ QUOTA_MAPPINGS = [
163424
+ // Load Balancing
163425
+ // API returns: "HTTP Load Balancer", "TCP Load Balancer"
163426
+ {
163427
+ resourceType: "http_loadbalancer",
163428
+ quotaName: "HTTP Load Balancer",
163429
+ displayName: "HTTP Load Balancers"
163430
+ },
163431
+ {
163432
+ resourceType: "tcp_loadbalancer",
163433
+ quotaName: "TCP Load Balancer",
163434
+ displayName: "TCP Load Balancers"
163435
+ },
163436
+ // Origin & Health
163437
+ // API returns: "origin_pool" (snake_case), "Healthcheck" (title case)
163438
+ {
163439
+ resourceType: "origin_pool",
163440
+ quotaName: "origin_pool",
163441
+ displayName: "Origin Pools"
163442
+ },
163443
+ {
163444
+ resourceType: "healthcheck",
163445
+ quotaName: "Healthcheck",
163446
+ displayName: "Health Checks"
163447
+ },
163448
+ // Sites
163449
+ // API returns: "AWS VPC Site", "Azure VNET Site", "GCP VPC Site"
163450
+ {
163451
+ resourceType: "aws_vpc_site",
163452
+ quotaName: "AWS VPC Site",
163453
+ displayName: "AWS VPC Sites"
163454
+ },
163455
+ {
163456
+ resourceType: "azure_vnet_site",
163457
+ quotaName: "Azure VNET Site",
163458
+ displayName: "Azure VNET Sites"
163459
+ },
163460
+ {
163461
+ resourceType: "gcp_vpc_site",
163462
+ quotaName: "GCP VPC Site",
163463
+ displayName: "GCP VPC Sites"
163464
+ },
163465
+ // Networking
163466
+ // API returns: "Virtual Network", "Network Connector"
163467
+ {
163468
+ resourceType: "virtual_network",
163469
+ quotaName: "Virtual Network",
163470
+ displayName: "Virtual Networks"
163471
+ },
163472
+ {
163473
+ resourceType: "network_connector",
163474
+ quotaName: "Network Connector",
163475
+ displayName: "Network Connectors"
163476
+ },
163477
+ // Security
163478
+ // API returns: "Application Firewall", "Service Policy", "Rate Limiter"
163479
+ {
163480
+ resourceType: "app_firewall",
163481
+ quotaName: "Application Firewall",
163482
+ displayName: "Application Firewalls"
163483
+ },
163484
+ {
163485
+ resourceType: "service_policy",
163486
+ quotaName: "Service Policy",
163487
+ displayName: "Service Policies"
163488
+ },
163489
+ {
163490
+ resourceType: "rate_limiter",
163491
+ quotaName: "Rate Limiter",
163492
+ displayName: "Rate Limiters"
163493
+ },
163494
+ // DNS
163495
+ // API returns: "DNS Zone", "DNS Domain"
163496
+ {
163497
+ resourceType: "dns_zone",
163498
+ quotaName: "DNS Zone",
163499
+ displayName: "DNS Zones"
163500
+ },
163501
+ {
163502
+ resourceType: "dns_domain",
163503
+ quotaName: "DNS Domain",
163504
+ displayName: "DNS Domains"
163505
+ },
163506
+ // API Security
163507
+ // API returns: "API Definition"
163508
+ {
163509
+ resourceType: "api_definition",
163510
+ quotaName: "API Definition",
163511
+ displayName: "API Definitions"
163512
+ },
163513
+ // Namespaces
163514
+ // API returns: "Namespace"
163515
+ {
163516
+ resourceType: "namespace",
163517
+ quotaName: "Namespace",
163518
+ displayName: "Namespaces"
163519
+ }
163520
+ ];
163521
+ MAPPING_BY_RESOURCE_TYPE = new Map(
163522
+ QUOTA_MAPPINGS.map((m) => [m.resourceType, m])
163523
+ );
163524
+ MAPPING_BY_QUOTA_NAME = new Map(
163525
+ QUOTA_MAPPINGS.map((m) => [m.quotaName, m])
163526
+ );
163527
+ }
163528
+ });
163529
+
163530
+ // src/repl/completion/quota-helper.ts
163531
+ var quota_helper_exports = {};
163532
+ __export(quota_helper_exports, {
163533
+ getQuotaForResourceType: () => getQuotaForResourceType,
163534
+ getQuotasForResourceTypes: () => getQuotasForResourceTypes
163535
+ });
163536
+ async function getQuotaForResourceType(apiClient, namespace, resourceType) {
163537
+ const mapping = getQuotaMapping(resourceType);
163538
+ if (!mapping) {
163539
+ return null;
163540
+ }
163541
+ try {
163542
+ const subscriptionClient = new SubscriptionClient(apiClient);
163543
+ const cache3 = getQuotaCache();
163544
+ const usage = await cache3.findQuotaUsage(
163545
+ subscriptionClient,
163546
+ namespace,
163547
+ mapping.quotaName
163548
+ );
163549
+ if (!usage || usage.limit <= 0) {
163550
+ return null;
163551
+ }
163552
+ const percentage = Math.round(usage.current / usage.limit * 100);
163553
+ const level = percentage >= 100 ? "error" : percentage >= 90 ? "warning" : "none";
163554
+ return {
163555
+ current: usage.current,
163556
+ limit: usage.limit,
163557
+ percentage,
163558
+ level,
163559
+ display: `${usage.current}/${usage.limit}`
163560
+ };
163561
+ } catch {
163562
+ return null;
163563
+ }
163564
+ }
163565
+ async function getQuotasForResourceTypes(apiClient, namespace, resourceTypes) {
163566
+ const results = /* @__PURE__ */ new Map();
163567
+ await Promise.all(
163568
+ resourceTypes.map(async (rt) => {
163569
+ const info = await getQuotaForResourceType(
163570
+ apiClient,
163571
+ namespace,
163572
+ rt
163573
+ );
163574
+ if (info) {
163575
+ results.set(rt, info);
163576
+ }
163577
+ })
163578
+ );
163579
+ return results;
163580
+ }
163581
+ var init_quota_helper = __esm({
163582
+ "src/repl/completion/quota-helper.ts"() {
163583
+ "use strict";
163584
+ init_cache2();
163585
+ init_mapping();
163586
+ init_client3();
163587
+ }
163588
+ });
163589
+
163293
163590
  // src/repl/completion/completer.ts
163294
163591
  function isResourceCompletionAppropriate(resource) {
163295
163592
  if (resource.isPrimary) {
@@ -163554,8 +163851,10 @@ var init_completer = __esm({
163554
163851
  }
163555
163852
  return suggestions2;
163556
163853
  }
163557
- const resourceTypes = this.getResourceTypeSuggestions(
163558
- resourceCtx.domain
163854
+ const resourceTypes = await this.getResourceTypeSuggestionsWithQuota(
163855
+ resourceCtx.domain,
163856
+ this.session?.getNamespace() ?? null,
163857
+ this.session?.getAPIClient() ?? null
163559
163858
  );
163560
163859
  if (resourceTypes.length > 0) {
163561
163860
  let suggestions2 = resourceTypes;
@@ -163624,6 +163923,50 @@ var init_completer = __esm({
163624
163923
  }
163625
163924
  return suggestions;
163626
163925
  }
163926
+ /**
163927
+ * Get suggestions with metadata (including resource quota info)
163928
+ * Use this when you need quota info for the status line
163929
+ */
163930
+ async completeWithMeta(text) {
163931
+ const trimmed = text.trimStart();
163932
+ const parsed = parseInput(trimmed);
163933
+ const resourceCtx = this.parseResourceContext(parsed);
163934
+ const suggestions = await this.complete(text);
163935
+ let resourceQuotaInfo;
163936
+ if (resourceCtx.resourceType && resourceCtx.action && CREATION_ACTIONS.has(resourceCtx.action) && hasCreationFlags(resourceCtx.resourceType) && this.session) {
163937
+ try {
163938
+ const { getQuotaForResourceType: getQuotaForResourceType2 } = await Promise.resolve().then(() => (init_quota_helper(), quota_helper_exports));
163939
+ const { getQuotaMapping: getQuotaMapping2 } = await Promise.resolve().then(() => (init_mapping(), mapping_exports));
163940
+ const namespace = this.session.getNamespace();
163941
+ const client = this.session.getAPIClient();
163942
+ const mapping = getQuotaMapping2(resourceCtx.resourceType);
163943
+ if (namespace && client && mapping) {
163944
+ const quotaInfo = await getQuotaForResourceType2(
163945
+ client,
163946
+ namespace,
163947
+ resourceCtx.resourceType
163948
+ );
163949
+ if (quotaInfo) {
163950
+ resourceQuotaInfo = {
163951
+ resourceType: resourceCtx.resourceType,
163952
+ displayName: mapping.displayName,
163953
+ current: quotaInfo.current,
163954
+ limit: quotaInfo.limit,
163955
+ level: quotaInfo.level
163956
+ };
163957
+ }
163958
+ }
163959
+ } catch {
163960
+ }
163961
+ }
163962
+ const result = {
163963
+ suggestions
163964
+ };
163965
+ if (resourceQuotaInfo) {
163966
+ result.resourceQuotaInfo = resourceQuotaInfo;
163967
+ }
163968
+ return result;
163969
+ }
163627
163970
  /**
163628
163971
  * Get completions for custom domain commands
163629
163972
  * Uses unified completion registry for structure navigation,
@@ -163929,6 +164272,47 @@ var init_completer = __esm({
163929
164272
  };
163930
164273
  });
163931
164274
  }
164275
+ /**
164276
+ * Get resource type suggestions with quota information
164277
+ *
164278
+ * Enhanced version of getResourceTypeSuggestions that includes quota usage
164279
+ * data for resources that have quota mappings. Quota info is displayed
164280
+ * right-justified with color coding:
164281
+ * - White: Normal usage (< 90%)
164282
+ * - Yellow: Warning (90-99%)
164283
+ * - Red: At quota limit (100%)
164284
+ *
164285
+ * @param domain - The domain to get resource types for
164286
+ * @param namespace - The namespace to get quota for
164287
+ * @param client - The API client for quota lookups
164288
+ * @returns Promise of completion suggestions with quota info
164289
+ */
164290
+ async getResourceTypeSuggestionsWithQuota(domain, namespace, client) {
164291
+ const baseSuggestions = this.getResourceTypeSuggestions(domain);
164292
+ if (!client || !namespace) {
164293
+ return baseSuggestions;
164294
+ }
164295
+ try {
164296
+ const { getQuotasForResourceTypes: getQuotasForResourceTypes2 } = await Promise.resolve().then(() => (init_quota_helper(), quota_helper_exports));
164297
+ const resourceTypes = baseSuggestions.map((s) => s.text);
164298
+ const quotas = await getQuotasForResourceTypes2(
164299
+ client,
164300
+ namespace,
164301
+ resourceTypes
164302
+ );
164303
+ return baseSuggestions.map((suggestion) => {
164304
+ const quota = quotas.get(suggestion.text);
164305
+ if (!quota) return suggestion;
164306
+ return {
164307
+ ...suggestion,
164308
+ quotaLevel: quota.level,
164309
+ quotaInfo: quota.display
164310
+ };
164311
+ });
164312
+ } catch {
164313
+ return baseSuggestions;
164314
+ }
164315
+ }
163932
164316
  /**
163933
164317
  * Get resource name suggestions from live API
163934
164318
  * Fetches actual resource names for a given resource type
@@ -165202,6 +165586,131 @@ var init_verb_handler = __esm({
165202
165586
  }
165203
165587
  });
165204
165588
 
165589
+ // src/quota/pre-check.ts
165590
+ async function checkQuotaBeforeCreate(client, options) {
165591
+ const { resourceType, namespace, force = false } = options;
165592
+ const mapping = getQuotaMapping(resourceType);
165593
+ if (!mapping) {
165594
+ return { proceed: true, level: "none" };
165595
+ }
165596
+ try {
165597
+ const cache3 = getQuotaCache();
165598
+ const quotaUsage = await cache3.findQuotaUsage(
165599
+ client,
165600
+ namespace,
165601
+ mapping.quotaName
165602
+ );
165603
+ if (!quotaUsage) {
165604
+ return { proceed: true, level: "none" };
165605
+ }
165606
+ const percentage = quotaUsage.percentage ?? (quotaUsage.limit > 0 ? Math.round(quotaUsage.current / quotaUsage.limit * 100) : 0);
165607
+ const quotaInfo = {
165608
+ name: quotaUsage.name,
165609
+ displayName: mapping.displayName,
165610
+ current: quotaUsage.current,
165611
+ limit: quotaUsage.limit,
165612
+ percentage
165613
+ };
165614
+ if (percentage >= ERROR_THRESHOLD) {
165615
+ return {
165616
+ proceed: force,
165617
+ level: "error",
165618
+ message: force ? `Quota limit reached for ${mapping.displayName}, proceeding due to --force` : `Quota limit reached for ${mapping.displayName}`,
165619
+ quota: quotaInfo
165620
+ };
165621
+ }
165622
+ if (percentage >= WARNING_THRESHOLD) {
165623
+ return {
165624
+ proceed: true,
165625
+ level: "warning",
165626
+ message: `Approaching quota limit for ${mapping.displayName}`,
165627
+ quota: quotaInfo
165628
+ };
165629
+ }
165630
+ return { proceed: true, level: "none" };
165631
+ } catch (error) {
165632
+ const message = error instanceof Error ? error.message : "Unknown error";
165633
+ return {
165634
+ proceed: true,
165635
+ level: "none",
165636
+ message: `Could not check quota: ${message}`
165637
+ };
165638
+ }
165639
+ }
165640
+ function formatQuotaWarning(result) {
165641
+ if (result.level === "none" || !result.quota) {
165642
+ return [];
165643
+ }
165644
+ const { quota, level } = result;
165645
+ const lines = [];
165646
+ const icon = level === "error" ? "\u274C" : "\u26A0\uFE0F";
165647
+ const title = level === "error" ? "Quota Exceeded" : "Quota Warning";
165648
+ lines.push(`${icon} ${title}: ${quota.displayName}`);
165649
+ lines.push(
165650
+ ` Usage: ${quota.current}/${quota.limit} (${quota.percentage}%)`
165651
+ );
165652
+ const barWidth = 20;
165653
+ const filledWidth = Math.round(quota.percentage / 100 * barWidth);
165654
+ const emptyWidth = barWidth - filledWidth;
165655
+ const filledChar = "\u2588";
165656
+ const emptyChar = "\u2591";
165657
+ const progressBar2 = filledChar.repeat(filledWidth) + emptyChar.repeat(emptyWidth);
165658
+ lines.push(` ${progressBar2}`);
165659
+ lines.push("");
165660
+ if (level === "error") {
165661
+ lines.push(" Cannot create - quota limit reached.");
165662
+ lines.push(
165663
+ " Use --force to attempt anyway, or delete existing resources."
165664
+ );
165665
+ } else {
165666
+ lines.push(
165667
+ " Creating this resource will approach your quota limit."
165668
+ );
165669
+ }
165670
+ return lines;
165671
+ }
165672
+ function formatQuotaStatusLine(result) {
165673
+ if (result.level === "none" || !result.quota) {
165674
+ return "";
165675
+ }
165676
+ const { quota, level } = result;
165677
+ const icon = level === "error" ? "\u274C" : "\u26A0\uFE0F";
165678
+ return `${icon} ${quota.displayName}: ${quota.current}/${quota.limit} (${quota.percentage}%)`;
165679
+ }
165680
+ var WARNING_THRESHOLD, ERROR_THRESHOLD;
165681
+ var init_pre_check = __esm({
165682
+ "src/quota/pre-check.ts"() {
165683
+ "use strict";
165684
+ init_mapping();
165685
+ init_cache2();
165686
+ WARNING_THRESHOLD = 90;
165687
+ ERROR_THRESHOLD = 100;
165688
+ }
165689
+ });
165690
+
165691
+ // src/quota/index.ts
165692
+ var quota_exports = {};
165693
+ __export(quota_exports, {
165694
+ QuotaCache: () => QuotaCache,
165695
+ checkQuotaBeforeCreate: () => checkQuotaBeforeCreate,
165696
+ formatQuotaStatusLine: () => formatQuotaStatusLine,
165697
+ formatQuotaWarning: () => formatQuotaWarning,
165698
+ getAllQuotaMappings: () => getAllQuotaMappings,
165699
+ getQuotaCache: () => getQuotaCache,
165700
+ getQuotaMapping: () => getQuotaMapping,
165701
+ getQuotaMappingByName: () => getQuotaMappingByName,
165702
+ hasQuotaMapping: () => hasQuotaMapping,
165703
+ resetQuotaCache: () => resetQuotaCache
165704
+ });
165705
+ var init_quota = __esm({
165706
+ "src/quota/index.ts"() {
165707
+ "use strict";
165708
+ init_pre_check();
165709
+ init_mapping();
165710
+ init_cache2();
165711
+ }
165712
+ });
165713
+
165205
165714
  // src/profiling/profiler.ts
165206
165715
  var DEFAULT_THRESHOLDS = {
165207
165716
  slowPhaseMs: 100,
@@ -173273,6 +173782,16 @@ function getCategoryColor(category) {
173273
173782
  return "#ffffff";
173274
173783
  }
173275
173784
  }
173785
+ function getQuotaColor(quotaLevel) {
173786
+ if (quotaLevel === "error") return "#CA260A";
173787
+ if (quotaLevel === "warning") return "#ffc107";
173788
+ return null;
173789
+ }
173790
+ function getQuotaIndicator(quotaLevel) {
173791
+ if (quotaLevel === "error") return " \u274C";
173792
+ if (quotaLevel === "warning") return " \u26A0\uFE0F";
173793
+ return "";
173794
+ }
173276
173795
  function SuggestionItem({
173277
173796
  suggestion,
173278
173797
  isSelected,
@@ -173280,20 +173799,45 @@ function SuggestionItem({
173280
173799
  isContextOnly
173281
173800
  }) {
173282
173801
  const categoryColor = getCategoryColor(suggestion.category);
173283
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
173284
- isContextOnly ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: isSelected ? "#CA260A" : "#333333", children: isSelected ? "\u25B6 " : " " }),
173285
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: categoryColor, bold: isSelected, inverse: isSelected, children: suggestion.label.padEnd(maxLabelWidth) }),
173286
- suggestion.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
173802
+ const quotaColor = getQuotaColor(suggestion.quotaLevel);
173803
+ const quotaIndicator = getQuotaIndicator(suggestion.quotaLevel);
173804
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { justifyContent: "space-between", width: "100%", children: [
173805
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
173806
+ isContextOnly ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: isSelected ? "#CA260A" : "#333333", children: isSelected ? "\u25B6 " : " " }),
173807
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
173808
+ Text,
173809
+ {
173810
+ color: categoryColor,
173811
+ bold: isSelected,
173812
+ inverse: isSelected,
173813
+ children: suggestion.label.padEnd(maxLabelWidth)
173814
+ }
173815
+ ),
173816
+ suggestion.description && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
173817
+ Text,
173818
+ {
173819
+ color: suggestion.isPrimary ? "#ffffff" : "#666666",
173820
+ bold: suggestion.isPrimary || false,
173821
+ children: [
173822
+ " - ",
173823
+ suggestion.description
173824
+ ]
173825
+ }
173826
+ )
173827
+ ] }),
173828
+ suggestion.quotaInfo && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
173287
173829
  Text,
173288
173830
  {
173289
- color: suggestion.isPrimary ? "#ffffff" : "#666666",
173290
- bold: suggestion.isPrimary || false,
173831
+ color: quotaColor ?? "#666666",
173832
+ bold: suggestion.quotaLevel === "error",
173291
173833
  children: [
173292
- " - ",
173293
- suggestion.description
173834
+ "(",
173835
+ suggestion.quotaInfo,
173836
+ quotaIndicator,
173837
+ ")"
173294
173838
  ]
173295
173839
  }
173296
- )
173840
+ ) })
173297
173841
  ] });
173298
173842
  }
173299
173843
  function Suggestions({
@@ -173303,7 +173847,8 @@ function Suggestions({
173303
173847
  onNavigate,
173304
173848
  onCancel,
173305
173849
  maxVisible = 20,
173306
- isActive = true
173850
+ isActive = true,
173851
+ resourceQuotaInfo
173307
173852
  }) {
173308
173853
  const isContextOnlyMode = suggestions.every(
173309
173854
  (s) => s.category === "context"
@@ -173387,7 +173932,25 @@ function Suggestions({
173387
173932
  totalCount - startIndex - maxVisible,
173388
173933
  " more below)"
173389
173934
  ] }),
173390
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "#666666", dimColor: true, children: isContextOnlyMode ? "\u2191\u2193: scroll | Esc: close" : "Tab/\u2192: select | Enter: execute | \u2191\u2193: navigate | Esc: cancel" }) })
173935
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { marginTop: 1, justifyContent: "space-between", width: "100%", children: [
173936
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: "#666666", dimColor: true, children: isContextOnlyMode ? "\u2191\u2193: scroll | Esc: close" : "Tab/\u2192: select | Enter: execute | \u2191\u2193: navigate | Esc: cancel" }),
173937
+ resourceQuotaInfo && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
173938
+ Text,
173939
+ {
173940
+ color: getQuotaColor(resourceQuotaInfo.level) ?? "#666666",
173941
+ bold: resourceQuotaInfo.level === "error",
173942
+ children: [
173943
+ resourceQuotaInfo.displayName,
173944
+ ":",
173945
+ " ",
173946
+ resourceQuotaInfo.current,
173947
+ "/",
173948
+ resourceQuotaInfo.limit,
173949
+ getQuotaIndicator(resourceQuotaInfo.level)
173950
+ ]
173951
+ }
173952
+ )
173953
+ ] })
173391
173954
  ]
173392
173955
  }
173393
173956
  );
@@ -174183,6 +174746,7 @@ function useCompletion(options) {
174183
174746
  const [suggestions, setSuggestions] = (0, import_react32.useState)([]);
174184
174747
  const [selectedIndex, setSelectedIndex] = (0, import_react32.useState)(0);
174185
174748
  const [isShowing, setIsShowing] = (0, import_react32.useState)(false);
174749
+ const [resourceQuotaInfo, setResourceQuotaInfo] = (0, import_react32.useState)(null);
174186
174750
  (0, import_react32.useEffect)(() => {
174187
174751
  if (session) {
174188
174752
  completer.setSession(session);
@@ -174192,10 +174756,13 @@ function useCompletion(options) {
174192
174756
  setIsShowing(false);
174193
174757
  setSuggestions([]);
174194
174758
  setSelectedIndex(0);
174759
+ setResourceQuotaInfo(null);
174195
174760
  }, []);
174196
174761
  const triggerCompletion = (0, import_react32.useCallback)(
174197
174762
  async (input) => {
174198
- const newSuggestions = await completer.complete(input);
174763
+ const result = await completer.completeWithMeta(input);
174764
+ const newSuggestions = result.suggestions;
174765
+ setResourceQuotaInfo(result.resourceQuotaInfo ?? null);
174199
174766
  if (newSuggestions.length === 1) {
174200
174767
  const singleSuggestion = newSuggestions[0];
174201
174768
  if (singleSuggestion?.category === "context") {
@@ -174221,7 +174788,9 @@ function useCompletion(options) {
174221
174788
  const filterSuggestions = (0, import_react32.useCallback)(
174222
174789
  async (input) => {
174223
174790
  if (!isShowing) return;
174224
- const newSuggestions = await completer.complete(input);
174791
+ const result = await completer.completeWithMeta(input);
174792
+ const newSuggestions = result.suggestions;
174793
+ setResourceQuotaInfo(result.resourceQuotaInfo ?? null);
174225
174794
  if (newSuggestions.length === 0) {
174226
174795
  hide();
174227
174796
  } else {
@@ -174256,6 +174825,7 @@ function useCompletion(options) {
174256
174825
  suggestions,
174257
174826
  selectedIndex,
174258
174827
  isShowing,
174828
+ resourceQuotaInfo,
174259
174829
  triggerCompletion,
174260
174830
  navigateUp,
174261
174831
  navigateDown,
@@ -177078,6 +177648,35 @@ async function executeAPICommand(session, ctx, cmd) {
177078
177648
  contextChanged: false
177079
177649
  };
177080
177650
  }
177651
+ if (action === "create" && effectiveResourceType) {
177652
+ const { SubscriptionClient: SubscriptionClient2 } = await Promise.resolve().then(() => (init_client3(), client_exports));
177653
+ const { checkQuotaBeforeCreate: checkQuotaBeforeCreate2, formatQuotaWarning: formatQuotaWarning2 } = await Promise.resolve().then(() => (init_quota(), quota_exports));
177654
+ const subscriptionClient = new SubscriptionClient2(client);
177655
+ const forceFlag = args.some(
177656
+ (a) => a === "--force" || a === "-f"
177657
+ );
177658
+ const quotaResult = await checkQuotaBeforeCreate2(
177659
+ subscriptionClient,
177660
+ {
177661
+ resourceType: effectiveResourceType,
177662
+ namespace: effectiveNamespace,
177663
+ force: forceFlag
177664
+ }
177665
+ );
177666
+ if (quotaResult.level !== "none") {
177667
+ const quotaWarningLines = formatQuotaWarning2(quotaResult);
177668
+ if (!quotaResult.proceed) {
177669
+ return {
177670
+ output: quotaWarningLines,
177671
+ shouldExit: false,
177672
+ shouldClear: false,
177673
+ contextChanged: false,
177674
+ error: "Quota exceeded"
177675
+ };
177676
+ }
177677
+ warningOutput.push(...quotaWarningLines, "");
177678
+ }
177679
+ }
177081
177680
  if (action === "create") {
177082
177681
  const response = await client.post(apiPath, requestBody);
177083
177682
  result = response.data ?? {
@@ -177429,13 +178028,25 @@ function isValidDomain2(word) {
177429
178028
  return false;
177430
178029
  }
177431
178030
  function toUISuggestions(suggestions) {
177432
- return suggestions.map((s) => ({
177433
- label: s.text,
177434
- value: s.text,
177435
- description: s.description,
177436
- category: s.category ?? "builtin"
177437
- // Provide default to avoid undefined
177438
- }));
178031
+ return suggestions.map((s) => {
178032
+ const suggestion = {
178033
+ label: s.text,
178034
+ value: s.text,
178035
+ description: s.description,
178036
+ category: s.category ?? "builtin"
178037
+ // Provide default to avoid undefined
178038
+ };
178039
+ if (s.isPrimary !== void 0) {
178040
+ suggestion.isPrimary = s.isPrimary;
178041
+ }
178042
+ if (s.quotaLevel !== void 0) {
178043
+ suggestion.quotaLevel = s.quotaLevel;
178044
+ }
178045
+ if (s.quotaInfo !== void 0) {
178046
+ suggestion.quotaInfo = s.quotaInfo;
178047
+ }
178048
+ return suggestion;
178049
+ });
177439
178050
  }
177440
178051
  function App2({ initialSession } = {}) {
177441
178052
  const { exit } = use_app_default();
@@ -177910,7 +178521,10 @@ function App2({ initialSession } = {}) {
177910
178521
  onNavigate: handleSuggestionNavigate,
177911
178522
  onCancel: completion.hide,
177912
178523
  maxVisible: 20,
177913
- isActive: false
178524
+ isActive: false,
178525
+ ...completion.resourceQuotaInfo ? {
178526
+ resourceQuotaInfo: completion.resourceQuotaInfo
178527
+ } : {}
177914
178528
  }
177915
178529
  ) : /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
177916
178530
  StatusBar,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@robinmordasiewicz/f5xc-xcsh",
3
- "version": "2.0.41-2601192054",
3
+ "version": "2.0.41-2601192346",
4
4
  "description": "F5 Distributed Cloud Shell - Interactive CLI for F5 XC",
5
5
  "type": "module",
6
6
  "bin": {