@iblai/iblai-js 2.29.1 → 2.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.
@@ -269000,6 +269000,43 @@ function llmProviderLabel(provider) {
269000
269000
  function llmModelLabel(model) {
269001
269001
  return model.display_name || model.llm_name;
269002
269002
  }
269003
+ function canSwitchProvider(providers, name) {
269004
+ const provider = providers.find((p) => p.name === name);
269005
+ return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
269006
+ }
269007
+ /**
269008
+ * Whether we hold a usable LLM key for this provider — its own credentials, or
269009
+ * the tenant's main keys when it is allowed to borrow them. An *absent*
269010
+ * `main_has_credentials` means the backend did not report on the main keys and
269011
+ * is treated as available (matching standalone's `@/lib/utils` and
269012
+ * `claw-sandbox`'s copy); only an explicit `false` rules the main keys out.
269013
+ */
269014
+ function canSwitchLLm(llm) {
269015
+ if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
269016
+ return true;
269017
+ }
269018
+ return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
269019
+ ((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
269020
+ }
269021
+ /**
269022
+ * Whether one chat model is selectable. A provider can hold a key that covers
269023
+ * only some of its models (e.g. an Azure deployment per model), so provider
269024
+ * flags alone over- or under-grant.
269025
+ */
269026
+ function isModelAvailable(model, provider) {
269027
+ var _a, _b;
269028
+ if (model === null || model === void 0 ? void 0 : model.availability)
269029
+ return !model.availability.requires_credential;
269030
+ return Boolean(canSwitchLLm({
269031
+ has_credentials: (_a = model === null || model === void 0 ? void 0 : model.has_credentials) !== null && _a !== void 0 ? _a : provider === null || provider === void 0 ? void 0 : provider.has_credentials,
269032
+ main_has_credentials: (_b = model === null || model === void 0 ? void 0 : model.main_has_credentials) !== null && _b !== void 0 ? _b : provider === null || provider === void 0 ? void 0 : provider.main_has_credentials,
269033
+ can_use_main_keys: provider === null || provider === void 0 ? void 0 : provider.can_use_main_keys,
269034
+ }));
269035
+ }
269036
+ function modelUnavailableReason(model) {
269037
+ var _a, _b;
269038
+ return (_b = (_a = model === null || model === void 0 ? void 0 : model.availability) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : undefined;
269039
+ }
269003
269040
 
269004
269041
  function OverlayModal({ open, onClose, title, description, className, children, }) {
269005
269042
  const t = useT();
@@ -269021,18 +269058,6 @@ function OverlayModal({ open, onClose, title, description, className, children,
269021
269058
  return (jsx(DialogPrimitive.Root, { open: open, onOpenChange: handleOpenChange, modal: true, children: jsxs(DialogPrimitive.Portal, { container: document.body, children: [jsx(DialogPrimitive.Overlay, { "data-iblai-dialog-interaction-layer": true, className: cn('pointer-events-auto fixed inset-0 z-[100] bg-black/60', 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0') }), jsx(DialogPrimitive.Content, { ref: setDialogPanel, "data-iblai-dialog-interaction-layer": true, ...(!description ? { 'aria-describedby': undefined } : {}), className: cn('pointer-events-auto fixed left-[50%] top-[50%] z-[100] grid w-full translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background shadow-lg duration-200', 'max-h-[min(90dvh,calc(100dvh-2rem))] max-w-[calc(100vw-2rem)] overflow-y-auto overscroll-contain rounded-lg p-6 sm:rounded-lg', 'pt-[max(1.5rem,env(safe-area-inset-top))] pb-[max(1.5rem,env(safe-area-inset-bottom))]', 'pl-[max(1.5rem,env(safe-area-inset-left))] pr-[max(1.5rem,env(safe-area-inset-right))]', 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%]', 'focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2', className), children: jsxs(FloatingPortalContainerContext.Provider, { value: dialogPanel, children: [jsx(DialogPrimitive.Close, { type: "button", className: "absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", "aria-label": t('clawSandboxOverlayModal.close'), children: jsx(X$1, { className: "h-4 w-4" }) }), jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "flex flex-col space-y-1.5 pr-8 text-center sm:text-left", children: [jsx(DialogPrimitive.Title, { className: "text-lg font-semibold leading-none tracking-tight", children: title }), description ? (jsx(DialogPrimitive.Description, { className: "text-sm text-muted-foreground", children: description })) : null] }), children] })] }) })] }) }));
269022
269059
  }
269023
269060
 
269024
- // ── Helpers ──────────────────────────────────────────────────────────────────
269025
- const canSwitchLLm = (llm) => {
269026
- if (llm === null || llm === void 0 ? void 0 : llm.has_credentials) {
269027
- return true;
269028
- }
269029
- return ((llm === null || llm === void 0 ? void 0 : llm.can_use_main_keys) &&
269030
- ((llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === true || (llm === null || llm === void 0 ? void 0 : llm.main_has_credentials) === undefined));
269031
- };
269032
- function canSwitchProvider(providers, name) {
269033
- const provider = providers.find((provider) => provider.name === name);
269034
- return !!(provider === null || provider === void 0 ? void 0 : provider.chat_models) && provider.chat_models.length > 0;
269035
- }
269036
269061
  function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting, mentorSettings, llms, }) {
269037
269062
  const t = useT();
269038
269063
  const [searchQuery, setSearchQuery] = useState('');
@@ -269047,13 +269072,15 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
269047
269072
  ((_a = llm.display_name) !== null && _a !== void 0 ? _a : '').toLowerCase().includes(query);
269048
269073
  });
269049
269074
  }, [searchQuery, llmProvider]);
269050
- const switchLLMAllowed = canSwitchLLm(llmProvider);
269051
269075
  const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
269052
269076
  return (jsxs(OverlayModal, { open: isOpen, onClose: onClose, title: t('clawSandboxLlmProviderModal.title'), description: t('clawSandboxLlmProviderModal.description', { providerName: llmProvider.name }), className: "max-w-4xl", children: [jsx("p", { className: "text-sm text-gray-600", children: t('clawSandboxLlmProviderModal.chooseLlmHint') }), jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { placeholder: t('clawSandboxLlmProviderModal.searchPlaceholder'), className: "py-6 pl-9", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), autoComplete: "off", disabled: isSelecting })] }), jsx("div", { className: "grid max-h-[60vh] grid-cols-1 gap-4 overflow-y-auto pr-2 sm:grid-cols-2 lg:grid-cols-3", children: filteredLLMs.map((llm) => {
269077
+ var _a;
269053
269078
  const isActive = (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_name) === llm.llm_name &&
269054
269079
  (mentorSettings === null || mentorSettings === void 0 ? void 0 : mentorSettings.llm_provider) === llmProvider.name;
269055
- const isDisabled = !switchLLMAllowed || isSelecting || isActive || !switchProviderAllowed;
269056
- return (jsxs("button", { disabled: isDisabled, onClick: () => {
269080
+ const modelAvailable = isModelAvailable(llm, llmProvider);
269081
+ const unavailableReason = modelAvailable ? undefined : modelUnavailableReason(llm);
269082
+ const isDisabled = !modelAvailable || isSelecting || isActive || !switchProviderAllowed;
269083
+ return (jsxs("button", { "data-model": llm.llm_name, "data-unavailable": modelAvailable ? 'false' : 'true', "data-availability-status": (_a = llm.availability) === null || _a === void 0 ? void 0 : _a.status, title: unavailableReason, disabled: isDisabled, onClick: () => {
269057
269084
  onSelect(llmProvider.name, llm.llm_name);
269058
269085
  }, className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 p-4 transition-colors', {
269059
269086
  'cursor-not-allowed border-gray-200 bg-white': isDisabled,
@@ -290436,22 +290436,34 @@ function compareLLMProvidersByDisplayName(a, b) {
290436
290436
  });
290437
290437
  }
290438
290438
  /**
290439
- * Whether the user can reach *any* model of this provider.
290440
- *
290441
- * This is exactly the provider-level half of the per-model `isDisabled` rule in
290442
- * `llm-tab/llm-provider-modal.tsx`: a model row there is disabled when
290443
- * `!canSwitchLLm(provider) || !canSwitchProvider(llms, provider.name)`, and both
290444
- * of those depend only on the provider — so when either fails, *every* model row
290445
- * of that provider is disabled. `canSwitchProvider` is just "has at least one
290446
- * `chat_models` entry", which is checked inline here to keep the helper pure and
290447
- * free of the surrounding provider list.
290448
- *
290449
- * `canSwitchLLm` can return `undefined` (its `can_use_main_keys` branch), hence
290450
- * the explicit `Boolean(...)`.
290439
+ * Whether one chat model is selectable. A provider can hold a key that covers
290440
+ * only some of its models (e.g. an Azure deployment per model), so provider
290441
+ * flags alone over- or under-grant.
290451
290442
  */
290452
- function canAccessProvider(provider) {
290443
+ function isModelAvailable(model, provider) {
290453
290444
  var _a, _b;
290454
- return Boolean(canSwitchLLm(provider !== null && provider !== void 0 ? provider : {})) && ((_b = (_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
290445
+ if (model === null || model === void 0 ? void 0 : model.availability)
290446
+ return !model.availability.requires_credential;
290447
+ return Boolean(canSwitchLLm({
290448
+ has_credentials: (_a = model === null || model === void 0 ? void 0 : model.has_credentials) !== null && _a !== void 0 ? _a : provider === null || provider === void 0 ? void 0 : provider.has_credentials,
290449
+ main_has_credentials: (_b = model === null || model === void 0 ? void 0 : model.main_has_credentials) !== null && _b !== void 0 ? _b : provider === null || provider === void 0 ? void 0 : provider.main_has_credentials,
290450
+ can_use_main_keys: provider === null || provider === void 0 ? void 0 : provider.can_use_main_keys,
290451
+ }));
290452
+ }
290453
+ function modelUnavailableReason(model) {
290454
+ var _a, _b;
290455
+ return (_b = (_a = model === null || model === void 0 ? void 0 : model.availability) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : undefined;
290456
+ }
290457
+ /**
290458
+ * Whether the user can reach *any* model of this provider — the grid grays a
290459
+ * card only when every one of its models is unavailable (see
290460
+ * {@link isModelAvailable}, the same per-model rule the picker disables rows
290461
+ * with). Models without per-model fields inherit the provider flags, so older
290462
+ * responses gray all-or-nothing exactly as before.
290463
+ */
290464
+ function canAccessProvider(provider) {
290465
+ var _a;
290466
+ return ((_a = provider === null || provider === void 0 ? void 0 : provider.chat_models) !== null && _a !== void 0 ? _a : []).some((model) => isModelAvailable(model, provider));
290455
290467
  }
290456
290468
  /**
290457
290469
  * Orders LLM provider cards as two alphabetical groups: providers the user can
@@ -302311,7 +302323,6 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
302311
302323
  return localModelsForThisDevice().filter((m) => getProviderName(m.provider) === key &&
302312
302324
  m.name.toLowerCase().includes(searchQuery.toLowerCase()));
302313
302325
  }, [cloudOnly, isAvailable, llmProvider.name, searchQuery]);
302314
- const switchLLMAllowed = canSwitchLLm(llmProvider);
302315
302326
  const switchProviderAllowed = canSwitchProvider(llms, llmProvider.name);
302316
302327
  // Only an actual pull counts as "busy". A status refresh ('checking') must NOT
302317
302328
  // — otherwise a stale `activeModel` makes a row flash a progress bar every time
@@ -302339,18 +302350,21 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
302339
302350
  (cloudOnly || !localSel.enabled);
302340
302351
  // Order the WHOLE list — cloud + local — available (usable now) first,
302341
302352
  // unavailable last, with the in-use model leading. "Available" for a cloud
302342
- // model means the provider is switchable (has credentials + models); for a
302343
- // local model it's the install state. So an installed on-device model outranks
302344
- // cloud models whose provider has no credentials. Ranked by state, not the
302353
+ // model means it is usable (see isModelAvailable) and its provider ships
302354
+ // models; for a local model it's the install state. So an installed on-device
302355
+ // model outranks cloud models that need a key. Ranked by state, not the
302345
302356
  // transient download status, so rows don't jump mid-pull; a stable sort keeps
302346
302357
  // the original (catalog) order within each rank.
302347
- const cloudRank = (llm) => isCloudActive(llm) ? 0 : switchLLMAllowed && switchProviderAllowed ? 1 : 2;
302358
+ const cloudRank = (llm, available) => isCloudActive(llm) ? 0 : available && switchProviderAllowed ? 1 : 2;
302348
302359
  const localRank = (m) => {
302349
302360
  const s = statusFor(m);
302350
302361
  return s === 'selected' ? 0 : s === 'installed' ? 1 : 2;
302351
302362
  };
302352
302363
  const sortedModels = [
302353
- ...filteredLLMs.map((llm) => ({ kind: 'cloud', llm, rank: cloudRank(llm) })),
302364
+ ...filteredLLMs.map((llm) => {
302365
+ const available = isModelAvailable(llm, llmProvider);
302366
+ return { kind: 'cloud', llm, available, rank: cloudRank(llm, available) };
302367
+ }),
302354
302368
  ...localModels.map((model) => ({ kind: 'local', model, rank: localRank(model) })),
302355
302369
  ].sort((a, b) => a.rank - b.rank);
302356
302370
  // Preserve the "model may be too large" prompt before a download starts. Opens
@@ -302427,11 +302441,13 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
302427
302441
  ? labels.announceStarted(activeLocal.name)
302428
302442
  : '';
302429
302443
  return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { className: "max-w-4xl space-y-6 p-4", children: [jsx(DialogDescription, { className: "sr-only", children: labels.description(llmProvider.name) }), jsx(DialogHeader, { children: jsx(DialogTitle, { className: "ibl-dialog-title", children: labels.title }) }), jsx("p", { className: "text-sm text-gray-600", children: labels.helpText }), jsxs("div", { className: "relative", children: [jsx(Search, { className: "absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-gray-400" }), jsx(Input, { placeholder: labels.searchPlaceholder, className: "py-6 pl-9", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), autoComplete: "off", disabled: isSelecting })] }), jsx("div", { className: "sr-only", "aria-live": "polite", role: "status", children: liveMessage }), jsx("div", { className: "grid max-h-[60vh] grid-cols-1 gap-4 overflow-y-auto pr-2 sm:grid-cols-2 lg:grid-cols-3", children: sortedModels.map((item) => {
302444
+ var _a;
302430
302445
  if (item.kind === 'cloud') {
302431
- const llm = item.llm;
302446
+ const { llm, available: modelAvailable } = item;
302432
302447
  const isActive = isCloudActive(llm);
302433
- const isDisabled = !switchLLMAllowed || isSelecting || isActive || !switchProviderAllowed;
302434
- return (jsxs("button", { "data-model": llm.llm_name, disabled: isDisabled, onClick: () => {
302448
+ const unavailableReason = modelAvailable ? undefined : modelUnavailableReason(llm);
302449
+ const isDisabled = !modelAvailable || isSelecting || isActive || !switchProviderAllowed;
302450
+ return (jsxs("button", { "data-model": llm.llm_name, "data-unavailable": modelAvailable ? 'false' : 'true', "data-availability-status": (_a = llm.availability) === null || _a === void 0 ? void 0 : _a.status, title: unavailableReason, disabled: isDisabled, onClick: () => {
302435
302451
  selectCloud(llm.llm_name);
302436
302452
  }, className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 p-4 transition-colors', {
302437
302453
  // Matches the provider grid's inactive treatment: tinted
@@ -302477,7 +302493,7 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
302477
302493
  // @ts-ignore
302478
302494
  userId: username !== null && username !== void 0 ? username : '',
302479
302495
  }, { skip: !username || !mentorId || !tenantKey });
302480
- const { data: llmProviders, isLoading: isLoadingLLMProviders } = useGetLlmsQuery({
302496
+ const { data: llmProvidersData, isLoading: isLoadingLLMProviders } = useGetLlmsQuery({
302481
302497
  org: tenantKey,
302482
302498
  // @ts-ignore
302483
302499
  userId: username !== null && username !== void 0 ? username : '',
@@ -302485,6 +302501,8 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
302485
302501
  }, {
302486
302502
  skip: !tenantKey || !username,
302487
302503
  });
302504
+ // The generated LLMResponse type omits the credential flags the backend sends.
302505
+ const llmProviders = llmProvidersData;
302488
302506
  // Providers that only offer on-device (local) models — surfaced as grid cards
302489
302507
  // (Tauri only) so their downloadable models are reachable even when the
302490
302508
  // backend LLM list has no matching cloud provider. Built from THIS device's
@@ -302578,9 +302596,6 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
302578
302596
  .map((model) => {
302579
302597
  const providerLabel = llmProviderLabel(model);
302580
302598
  const isActive = !!activeProviderKey && getProviderName(model.name) === activeProviderKey;
302581
- // The generated LLM response type doesn't declare the
302582
- // credential flags the backend actually sends, so read them
302583
- // through the access-shaped view the helper expects.
302584
302599
  const isGrayed = !canAccessProvider(model) && !isActive;
302585
302600
  return (jsxs("div", { "data-testid": "llm-provider-card", "data-provider": model.name, "data-disabled": isGrayed ? 'true' : 'false', className: cn('flex cursor-pointer items-center gap-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md', {
302586
302601
  'border-blue-500': isActive,
@@ -302597,11 +302612,8 @@ function AgentLLMTab({ showConfigurationHeader = true, mentorId: mentorIdOverrid
302597
302612
  logo: model.logo,
302598
302613
  description: model.description,
302599
302614
  chat_models: model.chat_models,
302600
- // @ts-ignore - has_credentials property does not exist on LLMResponse type
302601
302615
  has_credentials: model === null || model === void 0 ? void 0 : model.has_credentials,
302602
- // @ts-ignore - main_has_credentials property does not exist on LLMResponse type
302603
302616
  main_has_credentials: model === null || model === void 0 ? void 0 : model.main_has_credentials,
302604
- // @ts-ignore - can_use_main_keys property does not exist on LLMResponse type
302605
302617
  can_use_main_keys: model === null || model === void 0 ? void 0 : model.can_use_main_keys,
302606
302618
  });
302607
302619
  }, children: [jsx("div", { className: "h-8 w-8 flex-shrink-0", children: jsx(LLMProviderLogo, { logo: model.logo, label: providerLabel, alt: labels.providerLogoAlt(providerLabel), className: cn({
@@ -312549,12 +312561,14 @@ function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, pla
312549
312561
  const [showProviders, setShowProviders] = React__default.useState(false);
312550
312562
  const [selectedProvider, setSelectedProvider] = React__default.useState(null);
312551
312563
  const [providerSearch, setProviderSearch] = React__default.useState('');
312552
- const { data: llmProviders, isLoading } = useGetLlmsQuery({
312564
+ const { data, isLoading } = useGetLlmsQuery({
312553
312565
  org: tenantKey,
312554
312566
  // @ts-ignore - userId not on generated signature
312555
312567
  userId: username,
312556
312568
  mentorId,
312557
312569
  }, { skip: !tenantKey || !username || !mentorId });
312570
+ // The generated LLMResponse type omits the credential flags the backend sends.
312571
+ const llmProviders = data;
312558
312572
  const filteredProviders = React__default.useMemo(() => (llmProviders !== null && llmProviders !== void 0 ? llmProviders : []).filter((model) => model.name.toLowerCase().includes(providerSearch.toLowerCase())), [llmProviders, providerSearch]);
312559
312573
  // The selected provider's backend row, for its logo and label; `undefined`
312560
312574
  // until the catalogue loads or when the stored key no longer matches one.
@@ -312571,11 +312585,8 @@ function EvaluateLlmPicker({ tenantKey, username, mentorId, value, onChange, pla
312571
312585
  logo: model.logo,
312572
312586
  description: model.description,
312573
312587
  chat_models: model.chat_models,
312574
- // @ts-ignore - has_credentials not on LLMResponse type
312575
312588
  has_credentials: model === null || model === void 0 ? void 0 : model.has_credentials,
312576
- // @ts-ignore - main_has_credentials not on LLMResponse type
312577
312589
  main_has_credentials: model === null || model === void 0 ? void 0 : model.main_has_credentials,
312578
- // @ts-ignore - can_use_main_keys not on LLMResponse type
312579
312590
  can_use_main_keys: model === null || model === void 0 ? void 0 : model.can_use_main_keys,
312580
312591
  });
312581
312592
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iblai/iblai-js",
3
- "version": "2.29.1",
3
+ "version": "2.30.0",
4
4
  "description": "Unified JavaScript SDK for IBL.ai — re-exports data-layer, web-containers, and web-utils under a single package",
5
5
  "type": "module",
6
6
  "engines": {
@@ -76,9 +76,9 @@
76
76
  "axios": "1.13.6",
77
77
  "dotenv": "16.6.1",
78
78
  "winston": "3.19.0",
79
- "@iblai/data-layer": "1.26.0",
80
- "@iblai/mcp": "1.24.1",
81
- "@iblai/web-containers": "1.35.0",
79
+ "@iblai/data-layer": "1.27.0",
80
+ "@iblai/mcp": "1.25.0",
81
+ "@iblai/web-containers": "1.35.1",
82
82
  "@iblai/web-utils": "2.6.1"
83
83
  },
84
84
  "peerDependencies": {