@iblai/data-layer 1.39.0 → 1.40.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/dist/index.js CHANGED
@@ -42242,7 +42242,10 @@ const CREDENTIALS_CUSTOM_ENDPOINTS = {
42242
42242
 
42243
42243
  const credentialsCustomApiSlice = react.createApi({
42244
42244
  reducerPath: CREDENTIALS_CUSTOM_REDUCER_PATH,
42245
- baseQuery: iblFetchBaseQuery,
42245
+ // The Secrets page hides a kind's rows on a 403 of its list and shows the
42246
+ // retry notice on any other failure, which needs the status: RTK Query
42247
+ // keeps only `{}` of a thrown `{ status, data }`.
42248
+ baseQuery: withReturnedErrors(iblFetchBaseQuery),
42246
42249
  tagTypes: [
42247
42250
  'IntegrationCredentialsSchema',
42248
42251
  'CredentialsSchema',
@@ -42262,6 +42265,23 @@ const credentialsCustomApiSlice = react.createApi({
42262
42265
  }),
42263
42266
  }),
42264
42267
  invalidatesTags: ['MaskedIntegrationCredentials'],
42268
+ // The Secrets screens read the admin list and the VM secrets, and the
42269
+ // backend deletes the secrets that read this credential with it. The
42270
+ // slices are imported lazily: api-slice.ts imports this file.
42271
+ async onQueryStarted(_args, { queryFulfilled, dispatch }) {
42272
+ try {
42273
+ await queryFulfilled;
42274
+ const [{ credentialsApiSlice }, { virtualMachineApiSlice }] = await Promise.all([
42275
+ Promise.resolve().then(function () { return apiSlice$1; }),
42276
+ Promise.resolve().then(function () { return apiSlice; }),
42277
+ ]);
42278
+ dispatch(credentialsApiSlice.util.invalidateTags(['IntegrationCredentials']));
42279
+ dispatch(virtualMachineApiSlice.util.invalidateTags(['VirtualMachineSecrets']));
42280
+ }
42281
+ catch (_a) {
42282
+ // Only a successful delete invalidates.
42283
+ }
42284
+ },
42265
42285
  }),
42266
42286
  deleteCredential: builder.mutation({
42267
42287
  query: ({ org, name, platform_key, model_name }) => ({
@@ -42331,7 +42351,7 @@ const { useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, use
42331
42351
  const credentialsApiSlice = react.createApi({
42332
42352
  reducerPath: 'credentialsApiSlice',
42333
42353
  baseQuery: iblFakeBaseQuery,
42334
- tagTypes: ['Credentials', 'ProviderConfig', 'ExternalMapping'],
42354
+ tagTypes: ['Credentials', 'ProviderConfig', 'ExternalMapping', 'IntegrationCredentials'],
42335
42355
  endpoints: (builder) => ({
42336
42356
  getCredentials: builder.query({
42337
42357
  ...buildEndpointFromService(exports.SERVICES.DM, async (args) => {
@@ -42431,11 +42451,15 @@ const credentialsApiSlice = react.createApi({
42431
42451
  createCallCredentials: builder.mutation({
42432
42452
  ...buildEndpointFromAxdService(AiMentorService.aiMentorOrgsUsersMentorsCreateCallCredentialsCreate),
42433
42453
  }),
42454
+ // The admin list (ids + service_info), which the Secrets screens join
42455
+ // with the VM secrets that read a credential.
42434
42456
  getIntegrationCredentials: builder.query({
42435
42457
  ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsIntegrationCredentialRetrieve),
42458
+ providesTags: ['IntegrationCredentials'],
42436
42459
  }),
42437
42460
  createIntegrationCredential: builder.mutation({
42438
42461
  ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsIntegrationCredentialCreate),
42462
+ invalidatesTags: ['IntegrationCredentials'],
42439
42463
  async onQueryStarted(_args, { queryFulfilled, dispatch }) {
42440
42464
  try {
42441
42465
  await queryFulfilled;
@@ -42448,9 +42472,42 @@ const credentialsApiSlice = react.createApi({
42448
42472
  }),
42449
42473
  createLLMCredential: builder.mutation({
42450
42474
  ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsCredentialCreate),
42475
+ async onQueryStarted(_args, { queryFulfilled, dispatch }) {
42476
+ try {
42477
+ await queryFulfilled;
42478
+ dispatch(credentialsCustomApiSlice.util.invalidateTags(['MaskedLLMCredentials']));
42479
+ }
42480
+ catch (_a) {
42481
+ // Swallow — invalidation should only happen on success.
42482
+ }
42483
+ },
42484
+ }),
42485
+ // PATCH `/credential/` replaces the whole value for every provider but
42486
+ // Azure OpenAI, whose per-model values the backend merges.
42487
+ updateLLMCredential: builder.mutation({
42488
+ ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsCredentialPartialUpdate),
42489
+ async onQueryStarted(_args, { queryFulfilled, dispatch }) {
42490
+ try {
42491
+ await queryFulfilled;
42492
+ dispatch(credentialsCustomApiSlice.util.invalidateTags(['MaskedLLMCredentials']));
42493
+ }
42494
+ catch (_a) {
42495
+ // Swallow — invalidation should only happen on success.
42496
+ }
42497
+ },
42451
42498
  }),
42452
42499
  updateIntegrationCredential: builder.mutation({
42453
42500
  ...buildEndpointFromDmService(AiAccountService.aiAccountOrgsIntegrationCredentialPartialUpdate),
42501
+ invalidatesTags: ['IntegrationCredentials'],
42502
+ async onQueryStarted(_args, { queryFulfilled, dispatch }) {
42503
+ try {
42504
+ await queryFulfilled;
42505
+ dispatch(credentialsCustomApiSlice.util.invalidateTags(['MaskedIntegrationCredentials']));
42506
+ }
42507
+ catch (_a) {
42508
+ // Swallow — invalidation should only happen on success.
42509
+ }
42510
+ },
42454
42511
  }),
42455
42512
  getConnectedServiceAuthUrl: builder.query({
42456
42513
  queryFn: async (queryParams, api, extraOptions) => {
@@ -42561,7 +42618,45 @@ const credentialsApiSlice = react.createApi({
42561
42618
  }),
42562
42619
  }),
42563
42620
  });
42564
- const { useGetCredentialsQuery, useLazyGetCredentialsQuery, useGetUserCredentialsQuery, useLazyGetUserCredentialsQuery, useCreateCallCredentialsMutation, useGetIntegrationCredentialsQuery, useLazyGetIntegrationCredentialsQuery, useCreateIntegrationCredentialMutation, useUpdateIntegrationCredentialMutation, useGetLLMCredentialsQuery, useLazyGetLLMCredentialsQuery, useCreateLLMCredentialMutation, useGetConnectedServiceAuthUrlQuery, useLazyGetConnectedServiceAuthUrlQuery, useConnectedServicesCallbackQuery, useLazyConnectedServicesCallbackQuery, useGetUsersAsAssertionsQuery, useLazyGetUsersAsAssertionsQuery, useCreateCredentialAssertionMutation, useGetCredentialsListQuery, useLazyGetCredentialsListQuery, useCreateCredentialMutation, useUpdateCredentialMutation, useDeleteCourseCredentialMutation, useGetProviderConfigQuery, useLazyGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useLazyGetExternalMappingQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, } = credentialsApiSlice;
42621
+ const { useGetCredentialsQuery, useLazyGetCredentialsQuery, useGetUserCredentialsQuery, useLazyGetUserCredentialsQuery, useCreateCallCredentialsMutation, useGetIntegrationCredentialsQuery, useLazyGetIntegrationCredentialsQuery, useCreateIntegrationCredentialMutation, useUpdateIntegrationCredentialMutation, useGetLLMCredentialsQuery, useLazyGetLLMCredentialsQuery, useCreateLLMCredentialMutation, useUpdateLLMCredentialMutation, useGetConnectedServiceAuthUrlQuery, useLazyGetConnectedServiceAuthUrlQuery, useConnectedServicesCallbackQuery, useLazyConnectedServicesCallbackQuery, useGetUsersAsAssertionsQuery, useLazyGetUsersAsAssertionsQuery, useCreateCredentialAssertionMutation, useGetCredentialsListQuery, useLazyGetCredentialsListQuery, useCreateCredentialMutation, useUpdateCredentialMutation, useDeleteCourseCredentialMutation, useGetProviderConfigQuery, useLazyGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useLazyGetExternalMappingQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, } = credentialsApiSlice;
42622
+
42623
+ var apiSlice$1 = /*#__PURE__*/Object.freeze({
42624
+ __proto__: null,
42625
+ credentialsApiSlice: credentialsApiSlice,
42626
+ useConnectedServicesCallbackQuery: useConnectedServicesCallbackQuery,
42627
+ useCreateCallCredentialsMutation: useCreateCallCredentialsMutation,
42628
+ useCreateCredentialAssertionMutation: useCreateCredentialAssertionMutation,
42629
+ useCreateCredentialMutation: useCreateCredentialMutation,
42630
+ useCreateExternalMappingMutation: useCreateExternalMappingMutation,
42631
+ useCreateIntegrationCredentialMutation: useCreateIntegrationCredentialMutation,
42632
+ useCreateLLMCredentialMutation: useCreateLLMCredentialMutation,
42633
+ useCreateProviderConfigMutation: useCreateProviderConfigMutation,
42634
+ useDeleteCourseCredentialMutation: useDeleteCourseCredentialMutation,
42635
+ useDeleteExternalMappingMutation: useDeleteExternalMappingMutation,
42636
+ useDeleteProviderConfigMutation: useDeleteProviderConfigMutation,
42637
+ useGetConnectedServiceAuthUrlQuery: useGetConnectedServiceAuthUrlQuery,
42638
+ useGetCredentialsListQuery: useGetCredentialsListQuery,
42639
+ useGetCredentialsQuery: useGetCredentialsQuery,
42640
+ useGetExternalMappingQuery: useGetExternalMappingQuery,
42641
+ useGetIntegrationCredentialsQuery: useGetIntegrationCredentialsQuery,
42642
+ useGetLLMCredentialsQuery: useGetLLMCredentialsQuery,
42643
+ useGetProviderConfigQuery: useGetProviderConfigQuery,
42644
+ useGetUserCredentialsQuery: useGetUserCredentialsQuery,
42645
+ useGetUsersAsAssertionsQuery: useGetUsersAsAssertionsQuery,
42646
+ useLazyConnectedServicesCallbackQuery: useLazyConnectedServicesCallbackQuery,
42647
+ useLazyGetConnectedServiceAuthUrlQuery: useLazyGetConnectedServiceAuthUrlQuery,
42648
+ useLazyGetCredentialsListQuery: useLazyGetCredentialsListQuery,
42649
+ useLazyGetCredentialsQuery: useLazyGetCredentialsQuery,
42650
+ useLazyGetExternalMappingQuery: useLazyGetExternalMappingQuery,
42651
+ useLazyGetIntegrationCredentialsQuery: useLazyGetIntegrationCredentialsQuery,
42652
+ useLazyGetLLMCredentialsQuery: useLazyGetLLMCredentialsQuery,
42653
+ useLazyGetProviderConfigQuery: useLazyGetProviderConfigQuery,
42654
+ useLazyGetUserCredentialsQuery: useLazyGetUserCredentialsQuery,
42655
+ useLazyGetUsersAsAssertionsQuery: useLazyGetUsersAsAssertionsQuery,
42656
+ useUpdateCredentialMutation: useUpdateCredentialMutation,
42657
+ useUpdateIntegrationCredentialMutation: useUpdateIntegrationCredentialMutation,
42658
+ useUpdateLLMCredentialMutation: useUpdateLLMCredentialMutation
42659
+ });
42565
42660
 
42566
42661
  const chatPrivacyApiSlice = react.createApi({
42567
42662
  reducerPath: 'chatPrivacyApiSlice',
@@ -49992,11 +50087,51 @@ const VIRTUAL_MACHINE_REJECTED_HOST_SUFFIXES = [
49992
50087
  ];
49993
50088
  /** `env_var` of a VM secret: a POSIX-style environment variable name. */
49994
50089
  const VIRTUAL_MACHINE_ENV_VAR_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
50090
+ /** The backend caps `env_var` at 128 characters. */
50091
+ const VIRTUAL_MACHINE_ENV_VAR_MAX_LENGTH = 128;
50092
+ /**
50093
+ * Variable names the UI refuses for a secret. The backend only checks the
50094
+ * POSIX shape, but the sandbox sets `env_var` in the VM's environment, so a
50095
+ * secret named PATH or LD_PRELOAD would break every command run there.
50096
+ */
50097
+ const VIRTUAL_MACHINE_RESERVED_ENV_VARS = [
50098
+ 'PATH',
50099
+ 'HOME',
50100
+ 'USER',
50101
+ 'LOGNAME',
50102
+ 'SHELL',
50103
+ 'PWD',
50104
+ 'OLDPWD',
50105
+ 'TMPDIR',
50106
+ 'TERM',
50107
+ 'LANG',
50108
+ 'LANGUAGE',
50109
+ 'LC_ALL',
50110
+ 'LD_PRELOAD',
50111
+ 'LD_LIBRARY_PATH',
50112
+ 'PYTHONPATH',
50113
+ 'PYTHONHOME',
50114
+ 'NODE_OPTIONS',
50115
+ 'HTTP_PROXY',
50116
+ 'HTTPS_PROXY',
50117
+ 'NO_PROXY',
50118
+ ];
50119
+ /** Prefixes the sandbox and ibl.ai keep for their own variables. */
50120
+ const VIRTUAL_MACHINE_RESERVED_ENV_VAR_PREFIXES = ['MSB_', 'IBLAI_'];
50121
+ /**
50122
+ * The port a bare hostname gets in an allowlist. Secrets are only ever
50123
+ * swapped into HTTPS requests, so `api.acme.com` means `api.acme.com:443`.
50124
+ */
50125
+ const VIRTUAL_MACHINE_DEFAULT_HOST_PORT = 443;
49995
50126
  /** A network policy holds between 1 and 100 hosts. */
49996
50127
  const VIRTUAL_MACHINE_MAX_POLICY_HOSTS = 100;
49997
50128
  /** An agent may bind at most 20 VM secrets. */
49998
50129
  const VIRTUAL_MACHINE_MAX_SECRETS_PER_MENTOR = 20;
49999
- /** A pasted secret value must be at least this long. */
50130
+ /**
50131
+ * The backend's floor for a pasted secret value (its output scrubber blanks
50132
+ * every occurrence of a bound value, so a very short one would blank too
50133
+ * much). The UI no longer checks it; a refusal is shown under the Key field.
50134
+ */
50000
50135
  const VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH = 8;
50001
50136
  /**
50002
50137
  * Default VM runtime billing: charged to the chatting user's credits, prorated
@@ -50016,12 +50151,14 @@ const VIRTUAL_MACHINE_DEFAULT_BILLING = {
50016
50151
  * Hand-written: these endpoints are newer than the pinned `@iblai/iblai-api`
50017
50152
  * release. Records are keyed in the body (PATCH) or the query string (DELETE),
50018
50153
  * never in the URL. A 403 on either list means the caller lacks the matching
50019
- * `Ibl.Mentor/VirtualMachine…/list` permission — hosts hide the pages then.
50154
+ * `Ibl.Mentor/VirtualMachine…/list` permission — hosts hide the pages then,
50155
+ * which needs the status: the base query returns failures rather than throwing
50156
+ * them, since RTK Query keeps only `{}` of a thrown `{ status, data }`.
50020
50157
  */
50021
50158
  const virtualMachineApiSlice = react.createApi({
50022
50159
  reducerPath: VIRTUAL_MACHINE_REDUCER_PATH,
50023
50160
  tagTypes: [VIRTUAL_MACHINE_QUERY_KEYS.NETWORK_POLICIES, VIRTUAL_MACHINE_QUERY_KEYS.SECRETS],
50024
- baseQuery: iblFetchBaseQuery,
50161
+ baseQuery: withReturnedErrors(iblFetchBaseQuery),
50025
50162
  endpoints: (builder) => ({
50026
50163
  // ── Network policies ────────────────────────────────────────────────
50027
50164
  listVirtualMachineNetworkPolicies: builder.query({
@@ -50102,6 +50239,21 @@ const virtualMachineApiSlice = react.createApi({
50102
50239
  });
50103
50240
  const { useListVirtualMachineNetworkPoliciesQuery, useLazyListVirtualMachineNetworkPoliciesQuery, useCreateVirtualMachineNetworkPolicyMutation, useUpdateVirtualMachineNetworkPolicyMutation, useDeleteVirtualMachineNetworkPolicyMutation, useListVirtualMachineSecretsQuery, useLazyListVirtualMachineSecretsQuery, useCreateVirtualMachineSecretMutation, useUpdateVirtualMachineSecretMutation, useDeleteVirtualMachineSecretMutation, } = virtualMachineApiSlice;
50104
50241
 
50242
+ var apiSlice = /*#__PURE__*/Object.freeze({
50243
+ __proto__: null,
50244
+ useCreateVirtualMachineNetworkPolicyMutation: useCreateVirtualMachineNetworkPolicyMutation,
50245
+ useCreateVirtualMachineSecretMutation: useCreateVirtualMachineSecretMutation,
50246
+ useDeleteVirtualMachineNetworkPolicyMutation: useDeleteVirtualMachineNetworkPolicyMutation,
50247
+ useDeleteVirtualMachineSecretMutation: useDeleteVirtualMachineSecretMutation,
50248
+ useLazyListVirtualMachineNetworkPoliciesQuery: useLazyListVirtualMachineNetworkPoliciesQuery,
50249
+ useLazyListVirtualMachineSecretsQuery: useLazyListVirtualMachineSecretsQuery,
50250
+ useListVirtualMachineNetworkPoliciesQuery: useListVirtualMachineNetworkPoliciesQuery,
50251
+ useListVirtualMachineSecretsQuery: useListVirtualMachineSecretsQuery,
50252
+ useUpdateVirtualMachineNetworkPolicyMutation: useUpdateVirtualMachineNetworkPolicyMutation,
50253
+ useUpdateVirtualMachineSecretMutation: useUpdateVirtualMachineSecretMutation,
50254
+ virtualMachineApiSlice: virtualMachineApiSlice
50255
+ });
50256
+
50105
50257
  /**
50106
50258
  * Normalise a `host:port` entry the way the backend stores it: trimmed and
50107
50259
  * lowercased.
@@ -50109,13 +50261,78 @@ const { useListVirtualMachineNetworkPoliciesQuery, useLazyListVirtualMachineNetw
50109
50261
  function normalizeHostPort(value) {
50110
50262
  return value.trim().toLowerCase();
50111
50263
  }
50264
+ const SCHEME_PORTS = { 'https:': 443, 'http:': 80 };
50265
+ // A loop rather than `/x+$/`, which takes quadratic time on a long run of x
50266
+ // that does not reach the end (CodeQL js/polynomial-redos).
50267
+ function trimChar(value, char, { start = false } = {}) {
50268
+ let from = 0;
50269
+ let to = value.length;
50270
+ if (start)
50271
+ while (from < to && value[from] === char)
50272
+ from += 1;
50273
+ while (to > from && value[to - 1] === char)
50274
+ to -= 1;
50275
+ return value.slice(from, to);
50276
+ }
50277
+ /**
50278
+ * Turn what a person typed or pasted into the `host:port` the backend stores.
50279
+ * People paste the address from a service's instructions, so a whole URL
50280
+ * (`https://api.acme.com/v1/keys`) is reduced to its host, with the scheme's
50281
+ * port unless the URL names one; `api.acme.com/v1` loses its path; a trailing
50282
+ * dot goes. Anything unparseable comes back trimmed and lowercased so
50283
+ * `validateHostPort` can explain it.
50284
+ */
50285
+ function normalizeHostInput(value, options = {}) {
50286
+ let entry = normalizeHostPort(value);
50287
+ if (entry.length === 0)
50288
+ return entry;
50289
+ let port = null;
50290
+ if (entry.includes('://')) {
50291
+ try {
50292
+ const url = new URL(entry);
50293
+ if (!url.hostname)
50294
+ return entry;
50295
+ port = url.port || (SCHEME_PORTS[url.protocol] ? String(SCHEME_PORTS[url.protocol]) : null);
50296
+ entry = url.hostname;
50297
+ }
50298
+ catch (_a) {
50299
+ return entry;
50300
+ }
50301
+ }
50302
+ else {
50303
+ const pathStart = entry.search(/[/?#]/);
50304
+ if (pathStart !== -1)
50305
+ entry = entry.slice(0, pathStart);
50306
+ }
50307
+ if (port === null) {
50308
+ const separator = entry.lastIndexOf(':');
50309
+ if (separator !== -1) {
50310
+ port = entry.slice(separator + 1);
50311
+ entry = entry.slice(0, separator);
50312
+ }
50313
+ }
50314
+ entry = trimChar(entry, '.');
50315
+ if (entry.length === 0)
50316
+ return entry;
50317
+ if (port)
50318
+ return `${entry}:${port}`;
50319
+ return options.defaultPort !== undefined ? `${entry}:${options.defaultPort}` : entry;
50320
+ }
50112
50321
  /** Split a pasted or typed string into candidate `host:port` entries. */
50113
- function splitHostPortInput(value) {
50322
+ function splitHostPortInput(value, options = {}) {
50114
50323
  return value
50115
50324
  .split(/[\s,;]+/)
50116
- .map(normalizeHostPort)
50325
+ .map((entry) => normalizeHostInput(entry, options))
50117
50326
  .filter((entry) => entry.length > 0);
50118
50327
  }
50328
+ /**
50329
+ * The `host:port` a person reads: the standard HTTPS port is implied, so
50330
+ * `api.acme.com:443` shows as `api.acme.com`; any other port stays visible.
50331
+ * The stored and compared form is never changed by this.
50332
+ */
50333
+ function formatHostPort(entry) {
50334
+ return entry.endsWith(':443') ? entry.slice(0, -4) : entry;
50335
+ }
50119
50336
  const IPV4_PATTERN = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/;
50120
50337
  /** Whether the host part is an IPv4 literal (the `host:port` grammar admits no IPv6). */
50121
50338
  function isIpAddressHost(host) {
@@ -50271,6 +50488,75 @@ function findUncoveredSecretHosts(secrets, allowedHosts) {
50271
50488
  }
50272
50489
  return uncovered;
50273
50490
  }
50491
+ /** Whether a variable name is one the VM itself relies on (see the reserved list). */
50492
+ function isReservedEnvVar(name) {
50493
+ const upper = name.toUpperCase();
50494
+ if (VIRTUAL_MACHINE_RESERVED_ENV_VARS.includes(upper))
50495
+ return true;
50496
+ return VIRTUAL_MACHINE_RESERVED_ENV_VAR_PREFIXES.some((prefix) => upper.startsWith(prefix));
50497
+ }
50498
+ const ENV_VAR_WORDS = ['KEY', 'TOKEN', 'SECRET', 'PASSWORD'];
50499
+ const ENV_VAR_FALLBACK = 'SECRET';
50500
+ /** Leaves room for a `_NN` suffix under the backend's 128-character cap. */
50501
+ const ENV_VAR_DERIVED_MAX_LENGTH = VIRTUAL_MACHINE_ENV_VAR_MAX_LENGTH - 8;
50502
+ /**
50503
+ * The variable name a secret gets from its display name, so a person never
50504
+ * has to invent one: "Acme API key" → `ACME_API_KEY`, "Clé Stripe" →
50505
+ * `CLE_STRIPE_KEY`. Accents are folded, every other run of characters becomes
50506
+ * an underscore, a leading digit gets `KEY_` in front, a name without
50507
+ * KEY/TOKEN/SECRET/PASSWORD gets `_KEY` (which also keeps "Path" from
50508
+ * becoming the VM's PATH), nothing derivable falls back to `SECRET`, and a
50509
+ * clash with `taken` gets `_2`, `_3`, …
50510
+ */
50511
+ function deriveSecretEnvVar(name, options = {}) {
50512
+ var _a;
50513
+ let base = trimChar(name
50514
+ .normalize('NFKD')
50515
+ .replace(/[\u0300-\u036f]/g, '')
50516
+ .toUpperCase()
50517
+ .replace(/[^A-Z0-9]+/g, '_'), '_', { start: true }).replace(/_{2,}/g, '_');
50518
+ if (base.length === 0)
50519
+ base = ENV_VAR_FALLBACK;
50520
+ if (/^[0-9]/.test(base))
50521
+ base = `KEY_${base}`;
50522
+ if (!ENV_VAR_WORDS.some((word) => base.split('_').includes(word)))
50523
+ base = `${base}_KEY`;
50524
+ if (isReservedEnvVar(base))
50525
+ base = `KEY_${base}`;
50526
+ base = trimChar(base.slice(0, ENV_VAR_DERIVED_MAX_LENGTH), '_');
50527
+ const taken = new Set(((_a = options.taken) !== null && _a !== void 0 ? _a : []).map((entry) => entry.toUpperCase()));
50528
+ if (!taken.has(base))
50529
+ return base;
50530
+ for (let suffix = 2;; suffix += 1) {
50531
+ const candidate = `${base}_${suffix}`;
50532
+ if (!taken.has(candidate))
50533
+ return candidate;
50534
+ }
50535
+ }
50536
+ function validateSecretEnvVar(value, options = {}) {
50537
+ var _a;
50538
+ const entry = value.trim();
50539
+ if (entry.length === 0)
50540
+ return 'required';
50541
+ if (!VIRTUAL_MACHINE_ENV_VAR_PATTERN.test(entry))
50542
+ return 'format';
50543
+ if (entry.length > VIRTUAL_MACHINE_ENV_VAR_MAX_LENGTH)
50544
+ return 'too_long';
50545
+ if (isReservedEnvVar(entry))
50546
+ return 'reserved';
50547
+ const upper = entry.toUpperCase();
50548
+ if (((_a = options.taken) !== null && _a !== void 0 ? _a : []).some((taken) => taken.toUpperCase() === upper))
50549
+ return 'taken';
50550
+ return null;
50551
+ }
50552
+ /**
50553
+ * The backend refuses a duplicate variable with a plain-string 400
50554
+ * (`"ACME_KEY already exists for this organization."`), not a field error,
50555
+ * so the form has to recognise it to put the message under the right field.
50556
+ */
50557
+ function isSecretEnvVarTakenMessage(message) {
50558
+ return /already exists/i.test(message !== null && message !== void 0 ? message : '');
50559
+ }
50274
50560
 
50275
50561
  const mentorReducer = {
50276
50562
  [mentorApiSlice.reducerPath]: mentorApiSlice.reducer,
@@ -50637,8 +50923,10 @@ exports.USER_PROGRESS_DATA_POINTS = USER_PROGRESS_DATA_POINTS;
50637
50923
  exports.USER_PROGRESS_SUMMARY_TYPE = USER_PROGRESS_SUMMARY_TYPE;
50638
50924
  exports.VERCEL_SPA = VERCEL_SPA;
50639
50925
  exports.VIRTUAL_MACHINE_DEFAULT_BILLING = VIRTUAL_MACHINE_DEFAULT_BILLING;
50926
+ exports.VIRTUAL_MACHINE_DEFAULT_HOST_PORT = VIRTUAL_MACHINE_DEFAULT_HOST_PORT;
50640
50927
  exports.VIRTUAL_MACHINE_EGRESS_PROFILES = VIRTUAL_MACHINE_EGRESS_PROFILES;
50641
50928
  exports.VIRTUAL_MACHINE_ENDPOINTS = VIRTUAL_MACHINE_ENDPOINTS;
50929
+ exports.VIRTUAL_MACHINE_ENV_VAR_MAX_LENGTH = VIRTUAL_MACHINE_ENV_VAR_MAX_LENGTH;
50642
50930
  exports.VIRTUAL_MACHINE_ENV_VAR_PATTERN = VIRTUAL_MACHINE_ENV_VAR_PATTERN;
50643
50931
  exports.VIRTUAL_MACHINE_HOST_PORT_PATTERN = VIRTUAL_MACHINE_HOST_PORT_PATTERN;
50644
50932
  exports.VIRTUAL_MACHINE_MAX_POLICY_HOSTS = VIRTUAL_MACHINE_MAX_POLICY_HOSTS;
@@ -50648,6 +50936,8 @@ exports.VIRTUAL_MACHINE_QUERY_KEYS = VIRTUAL_MACHINE_QUERY_KEYS;
50648
50936
  exports.VIRTUAL_MACHINE_REDUCER_PATH = VIRTUAL_MACHINE_REDUCER_PATH;
50649
50937
  exports.VIRTUAL_MACHINE_REJECTED_HOSTS = VIRTUAL_MACHINE_REJECTED_HOSTS;
50650
50938
  exports.VIRTUAL_MACHINE_REJECTED_HOST_SUFFIXES = VIRTUAL_MACHINE_REJECTED_HOST_SUFFIXES;
50939
+ exports.VIRTUAL_MACHINE_RESERVED_ENV_VARS = VIRTUAL_MACHINE_RESERVED_ENV_VARS;
50940
+ exports.VIRTUAL_MACHINE_RESERVED_ENV_VAR_PREFIXES = VIRTUAL_MACHINE_RESERVED_ENV_VAR_PREFIXES;
50651
50941
  exports.VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH = VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH;
50652
50942
  exports.WATCHED_GROUPS_ENDPOINTS = WATCHED_GROUPS_ENDPOINTS;
50653
50943
  exports.WATCHED_GROUPS_QUERY_KEYS = WATCHED_GROUPS_QUERY_KEYS;
@@ -50697,6 +50987,7 @@ exports.credentialsCustomApiSlice = credentialsCustomApiSlice;
50697
50987
  exports.customAiSearchApiSlice = customAiSearchApiSlice;
50698
50988
  exports.customDomainApiSlice = customDomainApiSlice;
50699
50989
  exports.datasetsApiSlice = datasetsApiSlice;
50990
+ exports.deriveSecretEnvVar = deriveSecretEnvVar;
50700
50991
  exports.disclaimersApiSlice = disclaimersApiSlice;
50701
50992
  exports.edxProctoringApiSlice = edxProctoringApiSlice;
50702
50993
  exports.edxSsoApiSlice = edxSsoApiSlice;
@@ -50715,6 +51006,7 @@ exports.filterSlashSkills = filterSlashSkills;
50715
51006
  exports.findResumeBlock = findResumeBlock;
50716
51007
  exports.findUncoveredSecretHosts = findUncoveredSecretHosts;
50717
51008
  exports.flattenOutline = flattenOutline;
51009
+ exports.formatHostPort = formatHostPort;
50718
51010
  exports.getNextRecentMessagesPageParam = getNextRecentMessagesPageParam;
50719
51011
  exports.getSections = getSections;
50720
51012
  exports.getSpendCapExceededError = getSpendCapExceededError;
@@ -50731,6 +51023,8 @@ exports.isPoliciesResponse = isPoliciesResponse;
50731
51023
  exports.isPrivacyBlockedError = isPrivacyBlockedError;
50732
51024
  exports.isRejectedHostname = isRejectedHostname;
50733
51025
  exports.isRejectedIpAddress = isRejectedIpAddress;
51026
+ exports.isReservedEnvVar = isReservedEnvVar;
51027
+ exports.isSecretEnvVarTakenMessage = isSecretEnvVarTakenMessage;
50734
51028
  exports.isSpendCapExceededErrorBody = isSpendCapExceededErrorBody;
50735
51029
  exports.isUserLlmUsageEmpty = isUserLlmUsageEmpty;
50736
51030
  exports.isValidHostPort = isValidHostPort;
@@ -50748,6 +51042,7 @@ exports.mentorReducer = mentorReducer;
50748
51042
  exports.moderationLogsApiReducer = moderationLogsApiReducer;
50749
51043
  exports.moderationLogsApiSlice = moderationLogsApiSlice;
50750
51044
  exports.monetizationApiSlice = monetizationApiSlice;
51045
+ exports.normalizeHostInput = normalizeHostInput;
50751
51046
  exports.normalizeHostPort = normalizeHostPort;
50752
51047
  exports.normalizeLearnerCompletionError = normalizeLearnerCompletionError;
50753
51048
  exports.normalizeOutline = normalizeOutline;
@@ -51595,6 +51890,7 @@ exports.useUpdateGlobalMemoryMutation = useUpdateGlobalMemoryMutation;
51595
51890
  exports.useUpdateGraderCriterionMutation = useUpdateGraderCriterionMutation;
51596
51891
  exports.useUpdateHostingDomainMutation = useUpdateHostingDomainMutation;
51597
51892
  exports.useUpdateIntegrationCredentialMutation = useUpdateIntegrationCredentialMutation;
51893
+ exports.useUpdateLLMCredentialMutation = useUpdateLLMCredentialMutation;
51598
51894
  exports.useUpdateLtiKeyMutation = useUpdateLtiKeyMutation;
51599
51895
  exports.useUpdateLtiMentorMutation = useUpdateLtiMentorMutation;
51600
51896
  exports.useUpdateLtiToolMutation = useUpdateLtiToolMutation;
@@ -51681,6 +51977,7 @@ exports.userInvitationsApiSlice = userInvitationsApiSlice;
51681
51977
  exports.userLlmUsageApiSlice = userLlmUsageApiSlice;
51682
51978
  exports.validateHostPort = validateHostPort;
51683
51979
  exports.validateLearnerCompletionArgs = validateLearnerCompletionArgs;
51980
+ exports.validateSecretEnvVar = validateSecretEnvVar;
51684
51981
  exports.validateUserLlmUsageParams = validateUserLlmUsageParams;
51685
51982
  exports.virtualMachineApiSlice = virtualMachineApiSlice;
51686
51983
  exports.watchedGroupsApiSlice = watchedGroupsApiSlice;