@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.
- package/dist/index.js +639 -25
- 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-
|
|
41233
|
-
return "v2.0.41-
|
|
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.
|
|
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
|
-
|
|
173284
|
-
|
|
173285
|
-
|
|
173286
|
-
|
|
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:
|
|
173290
|
-
bold: suggestion.
|
|
173831
|
+
color: quotaColor ?? "#666666",
|
|
173832
|
+
bold: suggestion.quotaLevel === "error",
|
|
173291
173833
|
children: [
|
|
173292
|
-
"
|
|
173293
|
-
suggestion.
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
177434
|
-
|
|
177435
|
-
|
|
177436
|
-
|
|
177437
|
-
|
|
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,
|