@iblai/iblai-js 2.22.0 → 2.23.1

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.
@@ -1,7 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import React__default, { useRef, useEffect, useMemo, useCallback, useState, createContext, useContext, forwardRef, createElement as createElement$1, useLayoutEffect, useReducer, useImperativeHandle, isValidElement, Children, PureComponent, cloneElement, Component, Fragment as Fragment$2, useId as useId$2, useDebugValue, lazy, Suspense } from 'react';
3
3
  import * as dataLayer from '@iblai/data-layer';
4
- import { useGetLlmsQuery, useCreateSessionIdMutation, useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerCourseOutlineQuery, getSections, findResumeBlock, flattenOutline, normalizeLearnerCompletionError, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useUserLlmUsageSummary, useUserLlmSpendOverTime, useUserLlmCostByModel, useUserLlmCostByService, useUserLlmRecentActivity, USER_LLM_RECENT_ACTIVITY_PAGE_SIZE, useUserLlmTraceObservations, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetVectorDocumentsQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetCredentialsSchemaQuery, useGetMaskedLLMCredentialsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, VERCEL_SPA, useGetCustomDomainsQuery, useGetHostingProjectsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useCreateHostingProjectDomainMutation, useUpdateHostingDomainMutation, useDeleteHostingDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, USER_PROGRESS_DATA_POINTS, useResetTemplateMutation, useSendTestTemplateEmailMutation, useUpdateTemplateMutation, useGetTemplateDetailsQuery, USER_PROGRESS_SUMMARY_TYPE, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetLlmUsageTracesQuery, useGetLlmUsageObservationsQuery, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetAverageRatingQuery, useGetModelCostsQuery, useGetPerUserCostsQuery, useGetTenantCostQuery, useGetAgentCostQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportDetailQuery, useGetMentorPublicSettingsQuery, useGetReportsQuery, useCreateReportMutation, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery, useGetRevenueQuery, useListSubscribersQuery, useGetAuditLogsQuery, useGetPlatformAuditLogsQuery, PLATFORM_AUDIT_EVENT_TYPES, PLATFORM_AUDIT_ACTION_CODES, useGetMentorMemoriesQuery, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetUserEnrolledCoursesQuery, useLazyGetUserAssignedCoursesQuery, useLazyGetUserCredentialsQuery, useLazyGetOverTimeActivityQuery, useLazyGetCatalogSearchQuery, useCreateOrUpdateUserReportedSkillMutation, useCreateOrUpdateUserDesiredSkillMutation, useLazyGetPathwayListQuery, useLazyGetUserAssignedPathwaysQuery, useLazyGetUserEnrolledPathwaysQuery, useLazyGetPathwayCompletionQuery, useLazyGetProgramListQuery, useLazyGetProgramCompletionQuery, useLazyGetUserEnrolledProgramsQuery, useLazyGetAssignedProgramsQuery, useLazyGetUserSkillsPointsQuery, useLazyGetUserReportedSkillsQuery, useLazyGetUserDesiredSkillsQuery, useLazyGetUserCatalogPathwaysQuery, useLazyGetPerLearnerInfoQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useLazyGetPersonnalizedSearchQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetMentorSettingsQuery, useEditMentorMutation, useGetClawMentorConfigQuery, useGetClawInstancesQuery, useGetAgentConfigQuery, useUpdateClawMentorConfigMutation, useCreateClawMentorConfigMutation, useDeleteClawMentorConfigMutation, usePushClawConfigMutation, useCreateClawInstanceMutation, useDeleteClawInstanceMutation, useUpdateAgentConfigMutation, useHealthCheckClawInstanceMutation, useTestConnectivityClawInstanceMutation, useUpdateClawInstanceMutation, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useCreateCheckoutMutation, useGetUserProjectsQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorCategoriesQuery, useCreateMentorMutation, useUpdateUserPlatformMetadataMutation, useGetApplicationStatusQuery, useListOwnApplicationsQuery, useGetApplicationFormQuery, useWithdrawApplicationMutation, useRenewApplicationMutation, useCreateApplicationDraftMutation, useSubmitApplicationMutation, useUpdateApplicationMutation, useUploadApplicationAttachmentMutation, useDeleteApplicationAttachmentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useGetPromptCategoriesQuery, useCreatePromptMutation, useDeletePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation } from '@iblai/data-layer';
4
+ import { useGetCredentialsSchemaQuery, useGetLlmsQuery, useCreateSessionIdMutation, useTimeTrackingMutation, useGetUserMetadataQuery, useGetUserMetadataEdxQuery, useGetUserProfileSummaryQuery, useUpdateUserMetadataMutation, useUpdateUserMetadataEdxMutation, useUploadProfileImageMutation, useResetPasswordMutation, useSelfRetireMutation, useCreateUserInstitutionMutation, useGetUserInstitutionsQuery, useCreateUserEducationMutation, useUpdateUserEducationMutation, useDeleteUserEducationMutation, useGetUserEducationQuery, useCreateUserCompanyMutation, useGetUserCompaniesQuery, useCreateUserExperienceMutation, useUpdateUserExperienceMutation, useDeleteUserExperienceMutation, useGetUserExperienceQuery, useGetUserResumeQuery, useCreateUserResumeMutation, useGetMySubscriptionsQuery, useGetItemSubscriptionQuery, useCancelSubscriptionMutation, useGetLearnerCourseOutlineQuery, getSections, findResumeBlock, flattenOutline, normalizeLearnerCompletionError, useGetLearnerDetailsQuery, useGetUserEnrolledCoursesQuery, useGetUserEnrolledProgramsQuery, useGetUserCatalogPathwaysQuery, useGetUsersAsAssertionsQuery, useGetUserEarnedSkillsQuery, useGetUserReportedSkillsQuery, useGetUserDesiredSkillsQuery, useCreateGlobalMemoryMutation, useUpdateGlobalMemoryMutation, useUpdateMentorMemoryMutation, useGetMemsearchStatusQuery, useGetUserMemorySettingsQuery, useUpdateUserMemorySettingsMutation, useGetGlobalMemoriesQuery, useGetAllMentorMemoriesQuery, useDeleteGlobalMemoryMutation, useDeleteMentorMemoryMutation, useGetAiSearchMentorsQuery, useGetUserChatHistoryReportsQuery, USER_CHAT_HISTORY_REPORT_ACTIVE_STATES, useCreateUserChatHistoryReportMutation, useLazyGetDownloadReportFromURLQuery, useGetUserChatHistoryReportStatusQuery, userChatHistoryApiSlice, USER_CHAT_HISTORY_QUERY_KEYS, useGetUserChatHistoryQuery, useGetUserChatHistoryFilterQuery, useGetMentorsQuery, useGetUserChatHistorySessionQuery, useUserLlmUsageSummary, useUserLlmSpendOverTime, useUserLlmCostByModel, useUserLlmCostByService, useUserLlmRecentActivity, USER_LLM_RECENT_ACTIVITY_PAGE_SIZE, useUserLlmTraceObservations, useInviteUserMutation, usePlatformInvitationsQuery, useCreateCatalogInvitationCourseBulkMutation, useGetCatalogInvitationsCourseQuery, useLazyPlatformUsersQuery, useLazyPlatformUserGroupsQuery, useGetPersonnalizedSearchQuery, useCreateCatalogInvitationProgramBulkMutation, useGetCatalogInvitationsProgramQuery, useGetVectorDocumentsQuery, useUpdateUserRoleMutation, useUpdateUserStatusMutation, useUpdatePlatformUserRoleWithPoliciesMutation, usePlatformUsersQuery, useGetAccessibleUsersQuery, isPoliciesResponse, platformApiSlice, featureTags, useLazyGetRbacTeamsAccessListQuery, useCreateRbacTeamsAccessMutation, useGetRbacGroupsQuery, usePlatformUserGroupsQuery, useCreateRbacGroupMutation, useUpdateRbacGroupMutation, useDeleteRbacGroupMutation, useCreatePlatformUserGroupMutation, useUpdatePlatformUserGroupMutation, useDeletePlatformUserGroupMutation, useGetRbacGroupDetailsQuery, useGetPlatformUserGroupDetailsQuery, useGetRbacPermissionsMutation, useGetRbacRolesQuery, useCreateRbacRoleMutation, useUpdateRbacRoleMutation, useDeleteRbacRoleMutation, useGetRbacRoleDetailsQuery, useGetRbacActionsDefinitionQuery, useGetRbacResourceDiscoveryQuery, useGetRbacResourceListQuery, useGetRbacPoliciesQuery, useCreateRbacPolicyMutation, useUpdateRbacPolicyMutation, useDeleteRbacPolicyMutation, useGetRbacPolicyDetailsQuery, useGetWatchedGroupsQuery, useCreateWatchedGroupMutation, useUpdateWatchedGroupMutation, useDeleteWatchedGroupMutation, useAddWatchedUserMutation, useRemoveWatchedUserMutation, useAddWatcherMutation, useUpdateWatcherMutation, useDeleteWatcherMutation, useGetWatchedUsersQuery, useGetWatchersQuery, WATCHER_NOTIFICATION_EVENTS, WATCHER_NOTIFICATION_EVENT_LABELS, useGetApplicationFormsQuery, useGetApplicationFormDetailQuery, useCreateApplicationFormMutation, useUpdateApplicationFormMutation, useGetApplicationAdminDetailQuery, useInviteApplicationCandidateMutation, useLazyGetApplicationAttachmentFileQuery, useCreateApplicationWaiverMutation, useDeleteApplicationWaiverMutation, useSubmitApplicationOverrideMutation, useUpdateApplicationPlacementMutation, useDeleteApplicationCourseAssignmentMutation, useCreateApplicationNoteMutation, useUpdateApplicationNoteMutation, useDeleteApplicationNoteMutation, useTransitionApplicationMutation, useLinkApplicationCandidateMutation, useCreateApplicationPlacementsMutation, useGetCustomMentorsQuery, useCreateApplicationCourseAssignmentsMutation, useGetApplicationBlocksQuery, useDeleteApplicationBlockMutation, useGetApplicationsPipelineStatsQuery, useGetApplicationsPipelineQuery, useGetTenantMetadataQuery, useUpdateTenantMetadataMutation, useLazyGetTenantMetadataQuery, useDeleteApiKeyMutation, useGetApiKeyQuery, useGetApiKeyFieldPermissionsQuery, useUpdateApiKeyMutation, useGetApiKeysQuery, unwrapApiTokenList, useCreateApiKeyMutation, useCreateLLMCredentialMutation, useGetMaskedLLMCredentialsQuery, useDeleteIntegrationCredentialMutation, useDeleteCredentialMutation, useCreateIntegrationCredentialMutation, useGetIntegrationCredentialsSchemaQuery, useGetMaskedIntegrationCredentialsQuery, useGetAccountBillingInfoQuery, useUpdateAutoRechargeInfoMutation, useTriggerAutoRechargeMutation, useGetAgentSpendCapQuery, useGetTenantSpendCapQuery, useListAgentUserSpendCapsQuery, useUpsertAgentSpendCapMutation, useDeleteAgentSpendCapMutation, useUpsertTenantSpendCapMutation, useDeleteTenantSpendCapMutation, useUpsertUserSpendCapMutation, useDeleteUserSpendCapMutation, useGetLlmUsageMetricsQuery, useListAgentSpendCapsQuery, useGetCreditTransactionsQuery, useCreateStripeCustomerPortalMutation, useSetPlatformConfigurationsMutation, useGetPlatformConfigurationsQuery, useUpdatePlatformMembershipMutation, useGetPlatformMembershipQuery, VERCEL_SPA, useGetCustomDomainsQuery, useGetHostingProjectsQuery, useCreateCustomDomainMutation, useDeleteCustomDomainMutation, useCreateHostingProjectDomainMutation, useUpdateHostingDomainMutation, useDeleteHostingDomainMutation, useGetStudentMentorCreationStatusQuery, useSetStudentMentorCreationStatusMutation, coreApiSlice, recommendationPromptTypeEnum, useGetRecommendedPromptsListQuery, useCreateRecommendedPromptMutation, useUpdateRecommendedPromptMutation, useDeleteRecommendedPromptMutation, useLazyGetPublicPlatformImageAssetFileUrlQuery, useCreatePlatformImageAssetMutation, useGetProviderConfigQuery, useCreateProviderConfigMutation, useDeleteProviderConfigMutation, useGetExternalMappingQuery, useGetCredentialsListQuery, useCreateExternalMappingMutation, useDeleteExternalMappingMutation, useGetMemsearchConfigQuery, useUpdateMemsearchConfigMutation, useGetStripeConnectStatusQuery, useStartStripeConnectOnboardingMutation, useLazyGetStripeConnectDashboardQuery, useListPaywallsQuery, useListPricesQuery, useCreatePriceMutation, useUpdatePriceMutation, useDeletePriceMutation, useGetPaywallConfigQuery, useEnablePaywallMutation, useUpdatePaywallMutation, useGetMentorMemoriesListQuery, useGetMemoryCategoriesAdminQuery, useCreateMentorMemoryMutation, splitHostPortInput, validateHostPort, useCreateVirtualMachineNetworkPolicyMutation, useUpdateVirtualMachineNetworkPolicyMutation, VIRTUAL_MACHINE_MAX_POLICY_HOSTS, getVirtualMachineApiError, useDeleteVirtualMachineNetworkPolicyMutation, extractBoundMentorIds, useCreateVirtualMachineSecretMutation, useUpdateVirtualMachineSecretMutation, VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH, VIRTUAL_MACHINE_ENV_VAR_PATTERN, useDeleteVirtualMachineSecretMutation, useGetIntegrationCredentialsQuery, useListVirtualMachineNetworkPoliciesQuery, useListVirtualMachineSecretsQuery, getVirtualMachineErrorStatus, useGetNotificationsCountQuery, useLazyGetNotificationsQuery, useMarkAllAsReadMutation, useCreateNotificationPreviewMutation, useSendNotificationMutation, USER_PROGRESS_DATA_POINTS, useResetTemplateMutation, useSendTestTemplateEmailMutation, useUpdateTemplateMutation, useGetTemplateDetailsQuery, USER_PROGRESS_SUMMARY_TYPE, useGetTemplatesQuery, useLazyGetTemplateDetailsQuery, useToggleTemplateMutation, useGetLlmUsageTracesQuery, useGetLlmUsageObservationsQuery, useGetTopicsStatsQuery, useGetUsersStatsQuery, useGetSessionStatsQuery, useGetTopicsDetailsStatsQuery, useGetTranscriptsConversationHeadlineQuery, useGetAccessTimeHeatmapQuery, useGetUserDetailsStatsQuery, useGetAverageRatingQuery, useGetModelCostsQuery, useGetPerUserCostsQuery, useGetTenantCostQuery, useGetAgentCostQuery, useGetTranscriptsMessagesDetailsQuery, useGetTranscriptsMessagesQuery, useGetReportDetailQuery, useGetMentorPublicSettingsQuery, useGetReportsQuery, useCreateReportMutation, useGetContentAnalyticsQuery, useGetContentAnalyticsDetailsQuery, useGetRevenueQuery, useListSubscribersQuery, useGetAuditLogsQuery, useGetPlatformAuditLogsQuery, PLATFORM_AUDIT_EVENT_TYPES, PLATFORM_AUDIT_ACTION_CODES, useGetMentorMemoriesQuery, useLazyGetCourseMetaDataQuery, useLazyGetCourseCompletionOutlinesQuery, useLazyGetCourseEligibilityQuery, useLazyGetUserEnrolledCoursesQuery, useLazyGetUserAssignedCoursesQuery, useLazyGetUserCredentialsQuery, useLazyGetOverTimeActivityQuery, useLazyGetCatalogSearchQuery, useCreateOrUpdateUserReportedSkillMutation, useCreateOrUpdateUserDesiredSkillMutation, useLazyGetPathwayListQuery, useLazyGetUserAssignedPathwaysQuery, useLazyGetUserEnrolledPathwaysQuery, useLazyGetPathwayCompletionQuery, useLazyGetProgramListQuery, useLazyGetProgramCompletionQuery, useLazyGetUserEnrolledProgramsQuery, useLazyGetAssignedProgramsQuery, useLazyGetUserSkillsPointsQuery, useLazyGetUserReportedSkillsQuery, useLazyGetUserDesiredSkillsQuery, useLazyGetUserCatalogPathwaysQuery, useLazyGetPerLearnerInfoQuery, useLazyGetEdxSSOTokenQuery, useCreateCourseEnrollmentMutation, useCreateStripeCheckoutSessionMutation, useLazyGetCourseProgressQuery, useLazyGetCourseCompletionQuery, useLazyGetPersonnalizedSearchQuery, useUpdateExamAttemptMutation, useStartExamMutation, useLazyGetExamInfoQuery, useGetMentorSettingsQuery, useEditMentorMutation, isVirtualMachineEgressNetworked, findUncoveredSecretHosts, VIRTUAL_MACHINE_MAX_SECRETS_PER_MENTOR, VIRTUAL_MACHINE_DEFAULT_BILLING, VIRTUAL_MACHINE_EGRESS_PROFILES, useGetClawMentorConfigQuery, useGetClawInstancesQuery, useGetAgentConfigQuery, useUpdateClawMentorConfigMutation, useCreateClawMentorConfigMutation, useDeleteClawMentorConfigMutation, usePushClawConfigMutation, useCreateClawInstanceMutation, useDeleteClawInstanceMutation, useUpdateAgentConfigMutation, useHealthCheckClawInstanceMutation, useTestConnectivityClawInstanceMutation, useUpdateClawInstanceMutation, useGetAgentSkillResourcesQuery, useCreateAgentSkillResourceMutation, useUpdateAgentSkillResourceMutation, useUploadAgentSkillResourceAssetMutation, useDeleteAgentSkillResourceMutation, useGetMentorSkillAssignmentsQuery, useGetAgentSkillsQuery, useCreateMentorSkillAssignmentMutation, useUpdateMentorSkillAssignmentMutation, useDeleteMentorSkillAssignmentMutation, useCreateAgentSkillMutation, useUpdateAgentSkillMutation, useDeleteAgentSkillMutation, filterSlashSkills, useGetMentorSkillAssignmentsInfiniteQuery, MENTOR_SKILL_ASSIGNMENTS_PAGE_SIZE, useCreateCheckoutMutation, useGetUserProjectsQuery, useUpdateUserProjectMutation, useGetTrainingDocumentsQuery, useLazyGetConnectedServiceAuthUrlQuery, useCreateUserProjectMutation, useDeleteUserProjectMutation, useGetUserProjectDetailsQuery, useEditTrainingDocumentMutation, useAddTrainingDocumentMutation, useLazyGetCredentialsQuery, useGetMentorCategoriesQuery, useCreateMentorMutation, useUpdateUserPlatformMetadataMutation, useGetApplicationStatusQuery, useListOwnApplicationsQuery, useGetApplicationFormQuery, useWithdrawApplicationMutation, useRenewApplicationMutation, useCreateApplicationDraftMutation, useSubmitApplicationMutation, useUpdateApplicationMutation, useUploadApplicationAttachmentMutation, useDeleteApplicationAttachmentMutation, useCreateMemoryCategoryMutation, useUpdateMemoryCategoryMutation, useDeleteMemoryCategoryMutation, useGetPromptCategoriesQuery, useCreatePromptMutation, useDeletePromptMutation, useGetPromptsSearchQuery, useUpdatePromptMutation, useDeleteTrainingDocumentMutation, useGetTrainingDocumentRetrainScheduleQuery, useCreateTrainingDocumentRetrainScheduleMutation } from '@iblai/data-layer';
5
5
  import { useDispatch, useSelector } from 'react-redux';
6
6
  import { selectSessionId, selectActiveChatMessages, useCachedSessionId, chatActions, LOCAL_STORAGE_KEYS, TimeTracker, selectRbacPermissions, useCurrentTenant, canViewOtherUsersChatHistory, isPureWatcher, getInitials, useTenantMetadata, useUsername, selectStreaming, isLoggedIn, TOOL_NAME_MAP, WRITE_TODOS_TOOL, WithPermissions, canWriteField, useStripeUpgrade, openExternalUrl, CHAT_AREA_SIZE, isAlphaNumeric32, checkRbacPermission, getTimeAgo, REQUIRED_ACTIONS_FOR_GROUPS, getVisibleAnalyticsTabs, canViewMonetizationAnalytics, getUserName as getUserName$2, formatRelativeTime as formatRelativeTime$2, ANONYMOUS_USERNAME, combineCSVData, WATCHER_HIDDEN_REPORTS, canViewPlatformAuditLogs, redirectToAuthSpaJoinTenant, redirectToAuthSpa, getAuthSpaJoinUrl, useOS, selectNumberOfActiveChatMessages, TOOLS, hasRemoteAiConfig, isSafariBrowser, useShowAttachment, useShowVoiceCall, useShowVoiceRecorder, useMentorSettings, selectShowingSharedChat, useShowFreeTrialDialog, useEmbedMode, chatInputSliceSelectors, useResponsive, useAccessingPublicRoute, useVisitingTenant, useModelFileUploadCapabilities, useChatFileUpload, useVoiceChat, selectAttachedFiles, MENTOR_CHAT_DOCUMENTS_EXTENSIONS, chatInputSliceActions, removeFile, WithFormPermissions, MENTOR_VISIBILITY } from '@iblai/web-utils';
7
7
  import { toast } from 'sonner';
@@ -266,6 +266,34 @@ function useLlmDisplayName({ llmName, llmConfig, org, userId, mentorId, }) {
266
266
  }, [skip, providers, llmName]);
267
267
  return (_a = fromConfig !== null && fromConfig !== void 0 ? fromConfig : fromCatalogue) !== null && _a !== void 0 ? _a : getLLMModelDisplayName(llmName);
268
268
  }
269
+ /**
270
+ * A resolver from an LLM provider's display name (what the mentor-search
271
+ * `llm_providers` facet carries, e.g. `OpenAI`, `Microsoft`) to the logo the
272
+ * tenant's credentials schema publishes for it, matched on
273
+ * `service_info.display_name`. Resolves to `null` when disabled, still
274
+ * loading, or when no schema row carries that name.
275
+ *
276
+ * Moved here from the mentorai app's `use-llm-provider-details` so every host
277
+ * shares one copy; the app keeps its own until its next SDK bump.
278
+ */
279
+ function useCredentialsSchemaLogos({ org, enabled }) {
280
+ const { data: schemas } = useGetCredentialsSchemaQuery({ org: org !== null && org !== void 0 ? org : '' }, { skip: !org || !enabled });
281
+ // First row wins on a duplicate display name, as `Array.find` would.
282
+ const logosByDisplayName = useMemo(() => {
283
+ var _a, _b;
284
+ const byName = new Map();
285
+ if (!Array.isArray(schemas))
286
+ return byName;
287
+ for (const schema of schemas) {
288
+ const displayName = (_a = schema === null || schema === void 0 ? void 0 : schema.service_info) === null || _a === void 0 ? void 0 : _a.display_name;
289
+ const logo = (_b = schema === null || schema === void 0 ? void 0 : schema.service_info) === null || _b === void 0 ? void 0 : _b.logo;
290
+ if (displayName && logo && !byName.has(displayName))
291
+ byName.set(displayName, logo);
292
+ }
293
+ return byName;
294
+ }, [schemas]);
295
+ return useCallback((displayName) => (displayName && logosByDisplayName.get(displayName)) || null, [logosByDisplayName]);
296
+ }
269
297
 
270
298
  var _a;
271
299
  const dl$1 = dataLayer;
@@ -3836,7 +3864,9 @@ var datasetsTabAddResourceModal$3 = {
3836
3864
  title: "Add Resources",
3837
3865
  description: "Add knowledge to help your agent provide more relevant insights. Others with edit access can reuse these sources for more topics.",
3838
3866
  googleDrivePickerError: "Google Drive Picker Error",
3839
- forceClosePicker: "Force Close Picker"
3867
+ forceClosePicker: "Force Close Picker",
3868
+ notConfigured: "Not configured for this tenant",
3869
+ scrollRegionAriaLabel: "Resource types"
3840
3870
  };
3841
3871
  var datasetsTabDatasetItem$3 = {
3842
3872
  scheduleRetrain: "Schedule retrain",
@@ -5213,7 +5243,9 @@ var profileAccount$3 = {
5213
5243
  groupSystem: "System",
5214
5244
  ariaSettingsNav: "Organization settings",
5215
5245
  ariaSettingsNavMobile: "Organization settings (mobile)",
5216
- groupFinance: "Finance"
5246
+ groupFinance: "Finance",
5247
+ tabVirtualMachine: "Virtual Machine",
5248
+ descVirtualMachine: "Manage the network policies and secrets available to agents' virtual machines."
5217
5249
  };
5218
5250
  var profileAddCreditsModal$3 = {
5219
5251
  title: "Add Credits",
@@ -5754,7 +5786,11 @@ var resourceModalGithubFileUploadModal$3 = {
5754
5786
  errorAddingDocument: "Error adding training document",
5755
5787
  githubRepoUrlPlaceholder: "Github Repo URL",
5756
5788
  selectBranchPlaceholder: "Select Branch",
5757
- submit: "Submit"
5789
+ submit: "Submit",
5790
+ description: "Add a GitHub repository and pick a branch to train your agent on.",
5791
+ scrollRegionAriaLabel: "Repository details",
5792
+ repoUrlLabel: "Repository URL",
5793
+ branchLabel: "Branch"
5758
5794
  };
5759
5795
  var resourceModalLocalFileUploadModal$3 = {
5760
5796
  fileNotFound: "File not found",
@@ -5768,7 +5804,9 @@ var resourceModalLocalFileUploadModal$3 = {
5768
5804
  descriptionOptional: "Description (optional)",
5769
5805
  descriptionPlaceholder: "Add a description for this image...",
5770
5806
  submitting: "Submitting",
5771
- submit: "Submit"
5807
+ submit: "Submit",
5808
+ description: "Upload a file to train your agent on its contents.",
5809
+ scrollRegionAriaLabel: "File upload"
5772
5810
  };
5773
5811
  var resourceModalUrlUploadModal$3 = {
5774
5812
  urlPlaceholder: "URL",
@@ -5777,11 +5815,15 @@ var resourceModalUrlUploadModal$3 = {
5777
5815
  invalidYoutubeUrl: "Invalid YouTube URL",
5778
5816
  invalidBlackboardUrl: "Invalid Blackboard URL",
5779
5817
  invalidUrl: "Invalid URL",
5780
- submit: "Submit"
5818
+ submit: "Submit",
5819
+ description: "Add a link to train your agent on its content.",
5820
+ scrollRegionAriaLabel: "Link details",
5821
+ urlLabel: "URL"
5781
5822
  };
5782
5823
  var resourceModalWebsiteCrawlModal$3 = {
5783
- title: "Web Crawler Configuration",
5784
5824
  description: "Configure your web crawler settings and URL patterns",
5825
+ infoBox: "Crawl a website and train your agent on its pages. Set how deep to follow links, how many pages to fetch, and which URLs to include.",
5826
+ scrollRegionAriaLabel: "Web crawler settings",
5785
5827
  formAriaLabel: "Web crawler configuration form",
5786
5828
  urlRequired: "URL is required",
5787
5829
  invalidUrl: "Invalid URL",
@@ -5796,6 +5838,9 @@ var resourceModalWebsiteCrawlModal$3 = {
5796
5838
  selectPatternTypeAriaLabel: "Select pattern type",
5797
5839
  globPattern: "Glob Pattern",
5798
5840
  regularExpression: "Regular Expression",
5841
+ userAgentLabel: "User-Agent (optional)",
5842
+ userAgentPlaceholder: "Iblai-bot/1.0 (+https://ibl.ai/bot)",
5843
+ userAgentHelpText: "Optional. Overrides the default crawler User-Agent for every request. Leave blank to use the default.",
5799
5844
  crawlerMatchPatternsLabel: "Crawler Match Patterns",
5800
5845
  globPlaceholder: "https://example.com/",
5801
5846
  regexPlaceholder: "(http|https)://www.example.com/(.*)",
@@ -7000,8 +7045,8 @@ var uiSidebar$3 = {
7000
7045
  var userProfileDropdownIndex$3 = {
7001
7046
  profile: "Profile",
7002
7047
  account: "Account",
7003
- learner: "Learner",
7004
- instructor: "Instructor",
7048
+ learner: "User",
7049
+ instructor: "Admin",
7005
7050
  help: "Help",
7006
7051
  logOut: "Log Out",
7007
7052
  moreOptions: "More options"
@@ -8538,6 +8583,177 @@ var profileLlmUsage$3 = {
8538
8583
  tryAgain: "Try again",
8539
8584
  scopeUser: "Usage for {username}"
8540
8585
  };
8586
+ var virtualMachineHostInput$3 = {
8587
+ listLabel: "Allowed Hosts",
8588
+ removeHost: "Remove {host}",
8589
+ placeholder: "api.example.com:443",
8590
+ hint: "Type host:port and press Enter, or paste a list.",
8591
+ errorFormat: "{entry} must be host:port, for example api.example.com:443 — no scheme, path or wildcard.",
8592
+ errorPort: "{entry}: the port must be between 1 and 65535.",
8593
+ errorRejectedHost: "{entry}: this host is reserved and cannot be allowed.",
8594
+ errorIpNotAllowed: "{entry}: use a hostname here, not an IP address.",
8595
+ errorDuplicate: "{entry} is already in the list.",
8596
+ errorMax: "You can add at most {max} hosts."
8597
+ };
8598
+ var virtualMachineAdminTab$3 = {
8599
+ infoBox: "Agents with Virtual Machine Shell run code in an isolated Linux VM that has no network access by default. Network policies are reusable lists of the host:port destinations a VM may reach, and VM secrets are keys the VM can use but never read. Pick them for an agent from its Sandbox settings.",
8600
+ tabsAriaLabel: "Virtual machine sections",
8601
+ tabNetworkPolicies: "Network Policies",
8602
+ tabSecrets: "Secrets",
8603
+ forbidden: "You do not have permission to manage virtual machine network policies or secrets."
8604
+ };
8605
+ var virtualMachineNetworkPolicies$3 = {
8606
+ newPolicy: "New Policy",
8607
+ loadFailed: "Network policies could not be loaded. Try again in a moment.",
8608
+ colName: "Name",
8609
+ colHosts: "Allowed Hosts",
8610
+ colDescription: "Description",
8611
+ colUpdated: "Updated",
8612
+ colActions: "Actions",
8613
+ hostsListAriaLabel: "Hosts allowed by {name}",
8614
+ moreHosts: "+{count} more",
8615
+ editAriaLabel: "Edit policy {name}",
8616
+ deleteAriaLabel: "Delete policy {name}",
8617
+ empty: "No network policies yet. Create one to let an agent's VM reach specific hosts.",
8618
+ createTitle: "New Network Policy",
8619
+ editTitle: "Edit Network Policy",
8620
+ dialogDescription: "The host:port destinations a VM may reach under this policy. HTTPS is port 443.",
8621
+ fieldName: "Name",
8622
+ namePlaceholder: "e.g. Acme APIs",
8623
+ fieldHosts: "Allowed Hosts",
8624
+ hostsHint: "Type host:port and press Enter, or paste a list. Up to {max} entries; IP addresses are allowed.",
8625
+ removedHostsWarning: "You are removing {hosts}. If an agent uses this policy and has a secret that is sent to one of these hosts, that secret will stop working.",
8626
+ fieldDescription: "Description (Optional)",
8627
+ descriptionPlaceholder: "What this policy is for",
8628
+ cancel: "Cancel",
8629
+ saveChanges: "Save Changes",
8630
+ createPolicy: "Create Policy",
8631
+ errorNameRequired: "Give the policy a name.",
8632
+ errorHostsRequired: "Add at least one host.",
8633
+ created: "Network policy created",
8634
+ updated: "Network policy updated",
8635
+ deleted: "Network policy deleted",
8636
+ saveFailed: "The policy could not be saved. Try again.",
8637
+ forbiddenWrite: "You do not have permission to change network policies.",
8638
+ forbiddenDelete: "You do not have permission to delete network policies.",
8639
+ deleteTitle: "Delete Network Policy",
8640
+ deleteConfirm: "Delete {name}? Agents set to the Custom profile can only use policies that still exist.",
8641
+ deleteBlockedInUse: "This policy is still used by the following agents. Move them to another policy or profile first.",
8642
+ deleteFailed: "The policy could not be deleted. Try again.",
8643
+ "delete": "Delete"
8644
+ };
8645
+ var virtualMachineSecrets$3 = {
8646
+ newSecret: "New Secret",
8647
+ loadFailed: "VM secrets could not be loaded. Try again in a moment.",
8648
+ colName: "Name",
8649
+ colEnvVar: "Variable",
8650
+ colHosts: "Allowed Hosts",
8651
+ colSource: "Source",
8652
+ colUpdated: "Updated",
8653
+ colActions: "Actions",
8654
+ hostsListAriaLabel: "Hosts {name} is sent to",
8655
+ moreHosts: "+{count} more",
8656
+ sourceStored: "Stored Value",
8657
+ sourceFromCredential: "Credential {credential} · {field}",
8658
+ editAriaLabel: "Edit secret {name}",
8659
+ deleteAriaLabel: "Delete secret {name}",
8660
+ empty: "No VM secrets yet. Create one to let an agent call an API with your organization's key without ever seeing it.",
8661
+ createTitle: "New VM Secret",
8662
+ editTitle: "Edit VM Secret",
8663
+ dialogDescription: "The value is never shown again after you save it.",
8664
+ fieldName: "Name",
8665
+ namePlaceholder: "e.g. Acme API key",
8666
+ fieldEnvVar: "Environment Variable",
8667
+ envVarPlaceholder: "e.g. ACME_KEY",
8668
+ envVarHelp: "Letters, digits and underscores; cannot start with a digit.",
8669
+ envVarReadOnlyHelp: "Cannot be renamed. Delete this secret and create a new one instead.",
8670
+ fieldHosts: "Allowed Hosts",
8671
+ hostsHint: "Only these hosts receive the real key, so keep it to the narrowest host that needs it. Type host:port and press Enter.",
8672
+ fieldSource: "Value Source",
8673
+ sourceValue: "Enter a Value",
8674
+ sourceValueDescription: "Stored encrypted and never shown again.",
8675
+ sourceCredential: "Use an Integration Credential",
8676
+ sourceCredentialDescription: "Reads one field of a saved credential and follows rotations automatically.",
8677
+ fieldValue: "Value",
8678
+ valuePlaceholder: "Paste the key",
8679
+ valueKeepPlaceholder: "Leave blank to keep the current value",
8680
+ valueHelp: "At least {min} characters.",
8681
+ valueKeepHelp: "Leave blank to keep the current value.",
8682
+ fieldCredential: "Integration Credential",
8683
+ credentialPlaceholder: "Choose a credential",
8684
+ noCredentials: "Your organization has no integration credentials yet. Add one under Integrations, or enter a value instead.",
8685
+ fieldCredentialField: "Credential Field",
8686
+ credentialFieldPlaceholder: "Choose a field",
8687
+ credentialNoFields: "This credential has no fields to read from.",
8688
+ cancel: "Cancel",
8689
+ saveChanges: "Save Changes",
8690
+ createSecret: "Create Secret",
8691
+ errorNameRequired: "Give the secret a name.",
8692
+ errorEnvVarRequired: "Enter the environment variable name.",
8693
+ errorEnvVarFormat: "Use letters, digits and underscores only, and do not start with a digit.",
8694
+ errorHostsRequired: "Add at least one host.",
8695
+ errorValueRequired: "Enter the value.",
8696
+ errorValueTooShort: "The value must be at least {min} characters.",
8697
+ errorValueMasked: "This looks like a masked value. Paste the real key.",
8698
+ errorCredentialRequired: "Choose a credential.",
8699
+ errorFieldRequired: "Choose the credential field.",
8700
+ created: "VM secret created",
8701
+ updated: "VM secret updated",
8702
+ deleted: "VM secret deleted",
8703
+ saveFailed: "The secret could not be saved. Try again.",
8704
+ forbiddenWrite: "You do not have permission to change VM secrets.",
8705
+ forbiddenDelete: "You do not have permission to delete VM secrets.",
8706
+ deleteTitle: "Delete VM Secret",
8707
+ deleteConfirm: "Delete {envVar}? It will also be removed from every agent that uses it.",
8708
+ deleteFailed: "The secret could not be deleted. Try again.",
8709
+ "delete": "Delete"
8710
+ };
8711
+ var virtualMachineNetworkSection$3 = {
8712
+ title: "Network Access",
8713
+ description: "Decide what this agent's virtual machine can reach and which credentials it can use.",
8714
+ billingNotice: "Virtual machine time is billed to the chatting user's credits: ${usd} per {minutes} minutes by default, prorated per second. Your organization's rate may differ.",
8715
+ egressLabel: "Egress Profile",
8716
+ egressHelp: "How much of the network the VM gets.",
8717
+ egressNoneLabel: "No Network",
8718
+ egressNoneDescription: "The VM cannot reach anything.",
8719
+ egressRegistriesLabel: "Package Registries",
8720
+ egressRegistriesDescription: "PyPI, npm, apt and apk only, for installing packages.",
8721
+ egressPublicLabel: "Public Internet",
8722
+ egressPublicDescription: "Any public host. Private networks, localhost and cloud metadata stay blocked.",
8723
+ egressCustomLabel: "Custom Allowlist",
8724
+ egressCustomDescription: "Only the hosts in the network policy you pick below.",
8725
+ policyLabel: "Network Policy",
8726
+ policiesForbidden: "You do not have permission to view network policies, so this profile cannot be configured from here.",
8727
+ noPolicies: "No network policies yet",
8728
+ policyPlaceholder: "Choose a policy",
8729
+ createPolicy: "Create Policy",
8730
+ editPolicy: "Edit Policy",
8731
+ managePolicies: "Manage Policies",
8732
+ manageSecrets: "Manage Secrets",
8733
+ policyRequired: "The Custom profile needs a network policy.",
8734
+ policyHosts: "Hosts this policy allows:",
8735
+ secretsLabel: "Secrets",
8736
+ secretsHelp: "Keys the VM can use but never read. Up to {max} per agent.",
8737
+ noSecrets: "Your organization has no VM secrets yet. Use Manage Secrets to create one.",
8738
+ secretsCapReached: "This agent has the maximum of {max} secrets.",
8739
+ uncoveredTitle: "Some secrets are sent to hosts this policy does not allow:",
8740
+ uncoveredItem: "{envVar} needs {hosts}, which {policy} doesn't allow.",
8741
+ addHostsAndSave: "Add Hosts to {policy} and Save",
8742
+ saved: "Network settings saved",
8743
+ saveFailed: "The network settings could not be saved. Try again.",
8744
+ forbiddenSave: "You do not have permission to change these settings.",
8745
+ policyHostsAdded: "Hosts added to {policy}",
8746
+ policyUpdateFailed: "The policy could not be updated, so the settings were not saved.",
8747
+ policyUpdateForbidden: "You do not have permission to change network policies, so the settings were not saved.",
8748
+ discard: "Discard",
8749
+ save: "Save Changes",
8750
+ narrowTitle: "Unbind Secrets?",
8751
+ narrowDescription: "{profile} gives the VM no access to the hosts these secrets need, so saving will unbind {count, plural, one {# secret} other {# secrets}} from this agent.",
8752
+ cancel: "Cancel",
8753
+ narrowConfirm: "Unbind and Save",
8754
+ manageTitle: "Virtual Machine Settings",
8755
+ manageDescription: "Network policies and VM secrets are shared by every agent in your organization."
8756
+ };
8541
8757
  var en = {
8542
8758
  accessTabAddAccess: accessTabAddAccess$3,
8543
8759
  accessTabLabels: accessTabLabels$3,
@@ -8823,7 +9039,12 @@ var en = {
8823
9039
  analyticsCostTabs: analyticsCostTabs$3,
8824
9040
  analyticsLlmUsage: analyticsLlmUsage$3,
8825
9041
  analyticsTraces: analyticsTraces$3,
8826
- profileLlmUsage: profileLlmUsage$3
9042
+ profileLlmUsage: profileLlmUsage$3,
9043
+ virtualMachineHostInput: virtualMachineHostInput$3,
9044
+ virtualMachineAdminTab: virtualMachineAdminTab$3,
9045
+ virtualMachineNetworkPolicies: virtualMachineNetworkPolicies$3,
9046
+ virtualMachineSecrets: virtualMachineSecrets$3,
9047
+ virtualMachineNetworkSection: virtualMachineNetworkSection$3
8827
9048
  };
8828
9049
 
8829
9050
  var accessTabAddAccess$2 = {
@@ -11325,7 +11546,9 @@ var datasetsTabAddResourceModal$2 = {
11325
11546
  title: "Ajouter des ressources",
11326
11547
  description: "Ajoutez des connaissances pour aider votre agent à fournir des informations plus pertinentes. Les autres utilisateurs avec accès en modification peuvent réutiliser ces sources pour d'autres sujets.",
11327
11548
  googleDrivePickerError: "Erreur du sélecteur Google Drive",
11328
- forceClosePicker: "Forcer la fermeture du sélecteur"
11549
+ forceClosePicker: "Forcer la fermeture du sélecteur",
11550
+ notConfigured: "Non configuré pour ce locataire",
11551
+ scrollRegionAriaLabel: "Types de ressources"
11329
11552
  };
11330
11553
  var datasetsTabDatasetItem$2 = {
11331
11554
  scheduleRetrain: "Planifier le réentraînement",
@@ -12702,7 +12925,9 @@ var profileAccount$2 = {
12702
12925
  groupSystem: "Système",
12703
12926
  ariaSettingsNav: "Paramètres de l'organisation",
12704
12927
  ariaSettingsNavMobile: "Paramètres de l'organisation (mobile)",
12705
- groupFinance: "Finance"
12928
+ groupFinance: "Finance",
12929
+ tabVirtualMachine: "Machine virtuelle",
12930
+ descVirtualMachine: "Gérez les politiques réseau et les secrets disponibles pour les machines virtuelles des agents."
12706
12931
  };
12707
12932
  var profileAddCreditsModal$2 = {
12708
12933
  title: "Ajouter des crédits",
@@ -13243,7 +13468,11 @@ var resourceModalGithubFileUploadModal$2 = {
13243
13468
  errorAddingDocument: "Erreur lors de l'ajout du document d'entraînement",
13244
13469
  githubRepoUrlPlaceholder: "URL du dépôt GitHub",
13245
13470
  selectBranchPlaceholder: "Sélectionner une branche",
13246
- submit: "Soumettre"
13471
+ submit: "Soumettre",
13472
+ description: "Ajoutez un dépôt GitHub et choisissez une branche pour entraîner votre agent.",
13473
+ scrollRegionAriaLabel: "Détails du dépôt",
13474
+ repoUrlLabel: "URL du dépôt",
13475
+ branchLabel: "Branche"
13247
13476
  };
13248
13477
  var resourceModalLocalFileUploadModal$2 = {
13249
13478
  fileNotFound: "Fichier introuvable",
@@ -13257,7 +13486,9 @@ var resourceModalLocalFileUploadModal$2 = {
13257
13486
  descriptionOptional: "Description (facultative)",
13258
13487
  descriptionPlaceholder: "Ajoutez une description pour cette image...",
13259
13488
  submitting: "Envoi en cours",
13260
- submit: "Soumettre"
13489
+ submit: "Soumettre",
13490
+ description: "Importez un fichier pour entraîner votre agent sur son contenu.",
13491
+ scrollRegionAriaLabel: "Import de fichier"
13261
13492
  };
13262
13493
  var resourceModalUrlUploadModal$2 = {
13263
13494
  urlPlaceholder: "URL",
@@ -13266,11 +13497,15 @@ var resourceModalUrlUploadModal$2 = {
13266
13497
  invalidYoutubeUrl: "URL YouTube invalide",
13267
13498
  invalidBlackboardUrl: "URL Blackboard invalide",
13268
13499
  invalidUrl: "URL invalide",
13269
- submit: "Soumettre"
13500
+ submit: "Soumettre",
13501
+ description: "Ajoutez un lien pour entraîner votre agent sur son contenu.",
13502
+ scrollRegionAriaLabel: "Détails du lien",
13503
+ urlLabel: "URL"
13270
13504
  };
13271
13505
  var resourceModalWebsiteCrawlModal$2 = {
13272
- title: "Configuration du robot d'exploration web",
13273
13506
  description: "Configurez les paramètres et les modèles d'URL de votre robot d'exploration web",
13507
+ infoBox: "Explorez un site web et entraînez votre agent sur ses pages. Définissez la profondeur des liens, le nombre de pages à récupérer et les URL à inclure.",
13508
+ scrollRegionAriaLabel: "Paramètres du robot d'exploration",
13274
13509
  formAriaLabel: "Formulaire de configuration du robot d'exploration web",
13275
13510
  urlRequired: "L'URL est requise",
13276
13511
  invalidUrl: "URL invalide",
@@ -13285,6 +13520,9 @@ var resourceModalWebsiteCrawlModal$2 = {
13285
13520
  selectPatternTypeAriaLabel: "Sélectionner le type de modèle",
13286
13521
  globPattern: "Modèle Glob",
13287
13522
  regularExpression: "Expression régulière",
13523
+ userAgentLabel: "User-Agent (facultatif)",
13524
+ userAgentPlaceholder: "Iblai-bot/1.0 (+https://ibl.ai/bot)",
13525
+ userAgentHelpText: "Facultatif. Remplace le User-Agent par défaut du robot d'exploration pour chaque requête. Laissez vide pour utiliser celui par défaut.",
13288
13526
  crawlerMatchPatternsLabel: "Modèles de correspondance du robot",
13289
13527
  globPlaceholder: "https://example.com/",
13290
13528
  regexPlaceholder: "(http|https)://www.example.com/(.*)",
@@ -14489,8 +14727,8 @@ var uiSidebar$2 = {
14489
14727
  var userProfileDropdownIndex$2 = {
14490
14728
  profile: "Profil",
14491
14729
  account: "Compte",
14492
- learner: "Apprenant",
14493
- instructor: "Instructeur",
14730
+ learner: "Utilisateur",
14731
+ instructor: "Administrateur",
14494
14732
  help: "Aide",
14495
14733
  logOut: "Se déconnecter",
14496
14734
  moreOptions: "Plus d'options"
@@ -15861,6 +16099,177 @@ var profileLlmUsage$2 = {
15861
16099
  tryAgain: "Réessayer",
15862
16100
  scopeUser: "Utilisation de {username}"
15863
16101
  };
16102
+ var virtualMachineHostInput$2 = {
16103
+ listLabel: "Hôtes autorisés",
16104
+ removeHost: "Retirer {host}",
16105
+ placeholder: "api.exemple.com:443",
16106
+ hint: "Saisissez hôte:port et appuyez sur Entrée, ou collez une liste.",
16107
+ errorFormat: "{entry} doit être au format hôte:port, par exemple api.exemple.com:443, sans schéma, chemin ni joker.",
16108
+ errorPort: "{entry} : le port doit être compris entre 1 et 65535.",
16109
+ errorRejectedHost: "{entry} : cet hôte est réservé et ne peut pas être autorisé.",
16110
+ errorIpNotAllowed: "{entry} : utilisez ici un nom d'hôte, pas une adresse IP.",
16111
+ errorDuplicate: "{entry} figure déjà dans la liste.",
16112
+ errorMax: "Vous pouvez ajouter au plus {max} hôtes."
16113
+ };
16114
+ var virtualMachineAdminTab$2 = {
16115
+ infoBox: "Les agents dotés du Shell de machine virtuelle exécutent du code dans une VM Linux isolée, sans accès réseau par défaut. Les politiques réseau sont des listes réutilisables de destinations hôte:port qu'une VM peut joindre, et les secrets de VM sont des clés que la VM peut utiliser sans jamais les lire. Sélectionnez-les pour un agent depuis ses paramètres Sandbox.",
16116
+ tabsAriaLabel: "Sections de la machine virtuelle",
16117
+ tabNetworkPolicies: "Politiques réseau",
16118
+ tabSecrets: "Secrets",
16119
+ forbidden: "Vous n'avez pas l'autorisation de gérer les politiques réseau ni les secrets des machines virtuelles."
16120
+ };
16121
+ var virtualMachineNetworkPolicies$2 = {
16122
+ newPolicy: "Nouvelle politique",
16123
+ loadFailed: "Impossible de charger les politiques réseau. Réessayez dans un instant.",
16124
+ colName: "Nom",
16125
+ colHosts: "Hôtes autorisés",
16126
+ colDescription: "Description",
16127
+ colUpdated: "Mise à jour",
16128
+ colActions: "Actions",
16129
+ hostsListAriaLabel: "Hôtes autorisés par {name}",
16130
+ moreHosts: "+{count} de plus",
16131
+ editAriaLabel: "Modifier la politique {name}",
16132
+ deleteAriaLabel: "Supprimer la politique {name}",
16133
+ empty: "Aucune politique réseau pour l'instant. Créez-en une pour que la VM d'un agent puisse joindre des hôtes précis.",
16134
+ createTitle: "Nouvelle politique réseau",
16135
+ editTitle: "Modifier la politique réseau",
16136
+ dialogDescription: "Les destinations hôte:port qu'une VM peut joindre avec cette politique. HTTPS correspond au port 443.",
16137
+ fieldName: "Nom",
16138
+ namePlaceholder: "p. ex. API Acme",
16139
+ fieldHosts: "Hôtes autorisés",
16140
+ hostsHint: "Saisissez hôte:port et appuyez sur Entrée, ou collez une liste. Jusqu'à {max} entrées ; les adresses IP sont acceptées.",
16141
+ removedHostsWarning: "Vous retirez {hosts}. Si un agent utilise cette politique et possède un secret envoyé à l'un de ces hôtes, ce secret cessera de fonctionner.",
16142
+ fieldDescription: "Description (facultatif)",
16143
+ descriptionPlaceholder: "À quoi sert cette politique",
16144
+ cancel: "Annuler",
16145
+ saveChanges: "Enregistrer les modifications",
16146
+ createPolicy: "Créer la politique",
16147
+ errorNameRequired: "Donnez un nom à la politique.",
16148
+ errorHostsRequired: "Ajoutez au moins un hôte.",
16149
+ created: "Politique réseau créée",
16150
+ updated: "Politique réseau mise à jour",
16151
+ deleted: "Politique réseau supprimée",
16152
+ saveFailed: "La politique n'a pas pu être enregistrée. Réessayez.",
16153
+ forbiddenWrite: "Vous n'avez pas l'autorisation de modifier les politiques réseau.",
16154
+ forbiddenDelete: "Vous n'avez pas l'autorisation de supprimer les politiques réseau.",
16155
+ deleteTitle: "Supprimer la politique réseau",
16156
+ deleteConfirm: "Supprimer {name} ? Les agents en profil Personnalisé ne peuvent utiliser que des politiques existantes.",
16157
+ deleteBlockedInUse: "Cette politique est encore utilisée par les agents suivants. Passez-les d'abord sur une autre politique ou un autre profil.",
16158
+ deleteFailed: "La politique n'a pas pu être supprimée. Réessayez.",
16159
+ "delete": "Supprimer"
16160
+ };
16161
+ var virtualMachineSecrets$2 = {
16162
+ newSecret: "Nouveau secret",
16163
+ loadFailed: "Impossible de charger les secrets de VM. Réessayez dans un instant.",
16164
+ colName: "Nom",
16165
+ colEnvVar: "Variable",
16166
+ colHosts: "Hôtes autorisés",
16167
+ colSource: "Source",
16168
+ colUpdated: "Mis à jour",
16169
+ colActions: "Actions",
16170
+ hostsListAriaLabel: "Hôtes auxquels {name} est envoyé",
16171
+ moreHosts: "+{count} de plus",
16172
+ sourceStored: "Valeur enregistrée",
16173
+ sourceFromCredential: "Identifiant {credential} · {field}",
16174
+ editAriaLabel: "Modifier le secret {name}",
16175
+ deleteAriaLabel: "Supprimer le secret {name}",
16176
+ empty: "Aucun secret de VM pour l'instant. Créez-en un pour qu'un agent appelle une API avec la clé de votre organisation sans jamais la voir.",
16177
+ createTitle: "Nouveau secret de VM",
16178
+ editTitle: "Modifier le secret de VM",
16179
+ dialogDescription: "La valeur n'est plus jamais affichée après l'enregistrement.",
16180
+ fieldName: "Nom",
16181
+ namePlaceholder: "p. ex. Clé API Acme",
16182
+ fieldEnvVar: "Variable d'environnement",
16183
+ envVarPlaceholder: "p. ex. ACME_KEY",
16184
+ envVarHelp: "Lettres, chiffres et tirets bas ; ne peut pas commencer par un chiffre.",
16185
+ envVarReadOnlyHelp: "Ne peut pas être renommée. Supprimez ce secret et créez-en un nouveau.",
16186
+ fieldHosts: "Hôtes autorisés",
16187
+ hostsHint: "Seuls ces hôtes reçoivent la vraie clé ; limitez la liste à l'hôte le plus précis qui en a besoin. Saisissez hôte:port et appuyez sur Entrée.",
16188
+ fieldSource: "Source de la valeur",
16189
+ sourceValue: "Saisir une valeur",
16190
+ sourceValueDescription: "Stockée chiffrée et jamais réaffichée.",
16191
+ sourceCredential: "Utiliser un identifiant d'intégration",
16192
+ sourceCredentialDescription: "Lit un champ d'un identifiant enregistré et suit automatiquement les rotations.",
16193
+ fieldValue: "Valeur",
16194
+ valuePlaceholder: "Collez la clé",
16195
+ valueKeepPlaceholder: "Laissez vide pour conserver la valeur actuelle",
16196
+ valueHelp: "Au moins {min} caractères.",
16197
+ valueKeepHelp: "Laissez vide pour conserver la valeur actuelle.",
16198
+ fieldCredential: "Identifiant d'intégration",
16199
+ credentialPlaceholder: "Choisissez un identifiant",
16200
+ noCredentials: "Votre organisation n'a pas encore d'identifiants d'intégration. Ajoutez-en un dans Intégrations, ou saisissez une valeur.",
16201
+ fieldCredentialField: "Champ de l'identifiant",
16202
+ credentialFieldPlaceholder: "Choisissez un champ",
16203
+ credentialNoFields: "Cet identifiant n'a aucun champ à lire.",
16204
+ cancel: "Annuler",
16205
+ saveChanges: "Enregistrer les modifications",
16206
+ createSecret: "Créer le secret",
16207
+ errorNameRequired: "Donnez un nom au secret.",
16208
+ errorEnvVarRequired: "Saisissez le nom de la variable d'environnement.",
16209
+ errorEnvVarFormat: "Utilisez uniquement des lettres, des chiffres et des tirets bas, sans commencer par un chiffre.",
16210
+ errorHostsRequired: "Ajoutez au moins un hôte.",
16211
+ errorValueRequired: "Saisissez la valeur.",
16212
+ errorValueTooShort: "La valeur doit contenir au moins {min} caractères.",
16213
+ errorValueMasked: "Cela ressemble à une valeur masquée. Collez la vraie clé.",
16214
+ errorCredentialRequired: "Choisissez un identifiant.",
16215
+ errorFieldRequired: "Choisissez le champ de l'identifiant.",
16216
+ created: "Secret de VM créé",
16217
+ updated: "Secret de VM mis à jour",
16218
+ deleted: "Secret de VM supprimé",
16219
+ saveFailed: "Le secret n'a pas pu être enregistré. Réessayez.",
16220
+ forbiddenWrite: "Vous n'avez pas l'autorisation de modifier les secrets de VM.",
16221
+ forbiddenDelete: "Vous n'avez pas l'autorisation de supprimer les secrets de VM.",
16222
+ deleteTitle: "Supprimer le secret de VM",
16223
+ deleteConfirm: "Supprimer {envVar} ? Il sera aussi retiré de tous les agents qui l'utilisent.",
16224
+ deleteFailed: "Le secret n'a pas pu être supprimé. Réessayez.",
16225
+ "delete": "Supprimer"
16226
+ };
16227
+ var virtualMachineNetworkSection$2 = {
16228
+ title: "Accès réseau",
16229
+ description: "Décidez ce que la machine virtuelle de cet agent peut joindre et quels identifiants elle peut utiliser.",
16230
+ billingNotice: "Le temps de machine virtuelle est facturé sur les crédits de l'utilisateur qui discute : ${usd} par {minutes} minutes par défaut, au prorata de la seconde. Le tarif de votre organisation peut différer.",
16231
+ egressLabel: "Profil de sortie",
16232
+ egressHelp: "Quelle part du réseau la VM obtient.",
16233
+ egressNoneLabel: "Aucun réseau",
16234
+ egressNoneDescription: "La VM ne peut rien joindre.",
16235
+ egressRegistriesLabel: "Registres de paquets",
16236
+ egressRegistriesDescription: "PyPI, npm, apt et apk uniquement, pour installer des paquets.",
16237
+ egressPublicLabel: "Internet public",
16238
+ egressPublicDescription: "Tout hôte public. Les réseaux privés, localhost et les métadonnées cloud restent bloqués.",
16239
+ egressCustomLabel: "Liste personnalisée",
16240
+ egressCustomDescription: "Uniquement les hôtes de la politique réseau choisie ci-dessous.",
16241
+ policyLabel: "Politique réseau",
16242
+ policiesForbidden: "Vous n'avez pas l'autorisation de voir les politiques réseau, ce profil ne peut donc pas être configuré d'ici.",
16243
+ noPolicies: "Aucune politique réseau pour l'instant",
16244
+ policyPlaceholder: "Choisissez une politique",
16245
+ createPolicy: "Créer une politique",
16246
+ editPolicy: "Modifier la politique",
16247
+ managePolicies: "Gérer les politiques",
16248
+ manageSecrets: "Gérer les secrets",
16249
+ policyRequired: "Le profil Personnalisé nécessite une politique réseau.",
16250
+ policyHosts: "Hôtes autorisés par cette politique :",
16251
+ secretsLabel: "Secrets",
16252
+ secretsHelp: "Clés que la VM peut utiliser sans jamais les lire. Jusqu'à {max} par agent.",
16253
+ noSecrets: "Votre organisation n'a pas encore de secrets de VM. Utilisez Gérer les secrets pour en créer un.",
16254
+ secretsCapReached: "Cet agent a atteint le maximum de {max} secrets.",
16255
+ uncoveredTitle: "Certains secrets sont envoyés à des hôtes que cette politique n'autorise pas :",
16256
+ uncoveredItem: "{envVar} a besoin de {hosts}, que {policy} n'autorise pas.",
16257
+ addHostsAndSave: "Ajouter les hôtes à {policy} et enregistrer",
16258
+ saved: "Paramètres réseau enregistrés",
16259
+ saveFailed: "Les paramètres réseau n'ont pas pu être enregistrés. Réessayez.",
16260
+ forbiddenSave: "Vous n'avez pas l'autorisation de modifier ces paramètres.",
16261
+ policyHostsAdded: "Hôtes ajoutés à {policy}",
16262
+ policyUpdateFailed: "La politique n'a pas pu être mise à jour, les paramètres n'ont donc pas été enregistrés.",
16263
+ policyUpdateForbidden: "Vous n'avez pas l'autorisation de modifier les politiques réseau, les paramètres n'ont donc pas été enregistrés.",
16264
+ discard: "Annuler les modifications",
16265
+ save: "Enregistrer les modifications",
16266
+ narrowTitle: "Détacher les secrets ?",
16267
+ narrowDescription: "{profile} ne donne à la VM aucun accès aux hôtes dont ces secrets ont besoin ; l'enregistrement détachera donc {count, plural, one {# secret} other {# secrets}} de cet agent.",
16268
+ cancel: "Annuler",
16269
+ narrowConfirm: "Détacher et enregistrer",
16270
+ manageTitle: "Paramètres de la machine virtuelle",
16271
+ manageDescription: "Les politiques réseau et les secrets de VM sont partagés par tous les agents de votre organisation."
16272
+ };
15864
16273
  var fr = {
15865
16274
  accessTabAddAccess: accessTabAddAccess$2,
15866
16275
  accessTabLabels: accessTabLabels$2,
@@ -16146,7 +16555,12 @@ var fr = {
16146
16555
  analyticsCostTabs: analyticsCostTabs$2,
16147
16556
  analyticsLlmUsage: analyticsLlmUsage$2,
16148
16557
  analyticsTraces: analyticsTraces$2,
16149
- profileLlmUsage: profileLlmUsage$2
16558
+ profileLlmUsage: profileLlmUsage$2,
16559
+ virtualMachineHostInput: virtualMachineHostInput$2,
16560
+ virtualMachineAdminTab: virtualMachineAdminTab$2,
16561
+ virtualMachineNetworkPolicies: virtualMachineNetworkPolicies$2,
16562
+ virtualMachineSecrets: virtualMachineSecrets$2,
16563
+ virtualMachineNetworkSection: virtualMachineNetworkSection$2
16150
16564
  };
16151
16565
 
16152
16566
  var accessTabAddAccess$1 = {
@@ -18648,7 +19062,9 @@ var datasetsTabAddResourceModal$1 = {
18648
19062
  title: "Agregar recursos",
18649
19063
  description: "Agregue conocimiento para ayudar a su agente a proporcionar información más relevante. Otros usuarios con acceso de edición pueden reutilizar estas fuentes para más temas.",
18650
19064
  googleDrivePickerError: "Error del selector de Google Drive",
18651
- forceClosePicker: "Forzar cierre del selector"
19065
+ forceClosePicker: "Forzar cierre del selector",
19066
+ notConfigured: "No configurado para este inquilino",
19067
+ scrollRegionAriaLabel: "Tipos de recursos"
18652
19068
  };
18653
19069
  var datasetsTabDatasetItem$1 = {
18654
19070
  scheduleRetrain: "Programar reentrenamiento",
@@ -20025,7 +20441,9 @@ var profileAccount$1 = {
20025
20441
  groupSystem: "Sistema",
20026
20442
  ariaSettingsNav: "Configuración de la organización",
20027
20443
  ariaSettingsNavMobile: "Configuración de la organización (móvil)",
20028
- groupFinance: "Finanzas"
20444
+ groupFinance: "Finanzas",
20445
+ tabVirtualMachine: "Máquina virtual",
20446
+ descVirtualMachine: "Gestiona las políticas de red y los secretos disponibles para las máquinas virtuales de los agentes."
20029
20447
  };
20030
20448
  var profileAddCreditsModal$1 = {
20031
20449
  title: "Agregar créditos",
@@ -20566,7 +20984,11 @@ var resourceModalGithubFileUploadModal$1 = {
20566
20984
  errorAddingDocument: "Error al agregar el documento de entrenamiento",
20567
20985
  githubRepoUrlPlaceholder: "URL del repositorio de GitHub",
20568
20986
  selectBranchPlaceholder: "Seleccionar rama",
20569
- submit: "Enviar"
20987
+ submit: "Enviar",
20988
+ description: "Agrega un repositorio de GitHub y elige una rama para entrenar a tu agente.",
20989
+ scrollRegionAriaLabel: "Detalles del repositorio",
20990
+ repoUrlLabel: "URL del repositorio",
20991
+ branchLabel: "Rama"
20570
20992
  };
20571
20993
  var resourceModalLocalFileUploadModal$1 = {
20572
20994
  fileNotFound: "Archivo no encontrado",
@@ -20580,7 +21002,9 @@ var resourceModalLocalFileUploadModal$1 = {
20580
21002
  descriptionOptional: "Descripción (opcional)",
20581
21003
  descriptionPlaceholder: "Añade una descripción para esta imagen...",
20582
21004
  submitting: "Enviando",
20583
- submit: "Enviar"
21005
+ submit: "Enviar",
21006
+ description: "Sube un archivo para entrenar a tu agente con su contenido.",
21007
+ scrollRegionAriaLabel: "Carga de archivo"
20584
21008
  };
20585
21009
  var resourceModalUrlUploadModal$1 = {
20586
21010
  urlPlaceholder: "URL",
@@ -20589,11 +21013,15 @@ var resourceModalUrlUploadModal$1 = {
20589
21013
  invalidYoutubeUrl: "URL de YouTube no válida",
20590
21014
  invalidBlackboardUrl: "URL de Blackboard no válida",
20591
21015
  invalidUrl: "URL no válida",
20592
- submit: "Enviar"
21016
+ submit: "Enviar",
21017
+ description: "Agrega un enlace para entrenar a tu agente con su contenido.",
21018
+ scrollRegionAriaLabel: "Detalles del enlace",
21019
+ urlLabel: "URL"
20593
21020
  };
20594
21021
  var resourceModalWebsiteCrawlModal$1 = {
20595
- title: "Configuración del rastreador web",
20596
21022
  description: "Configure los ajustes de su rastreador web y los patrones de URL",
21023
+ infoBox: "Rastrea un sitio web y entrena a tu agente con sus páginas. Define la profundidad de los enlaces, cuántas páginas obtener y qué URL incluir.",
21024
+ scrollRegionAriaLabel: "Configuración del rastreador web",
20597
21025
  formAriaLabel: "Formulario de configuración del rastreador web",
20598
21026
  urlRequired: "La URL es obligatoria",
20599
21027
  invalidUrl: "URL no válida",
@@ -20608,6 +21036,9 @@ var resourceModalWebsiteCrawlModal$1 = {
20608
21036
  selectPatternTypeAriaLabel: "Seleccionar tipo de patrón",
20609
21037
  globPattern: "Patrón Glob",
20610
21038
  regularExpression: "Expresión regular",
21039
+ userAgentLabel: "User-Agent (opcional)",
21040
+ userAgentPlaceholder: "Iblai-bot/1.0 (+https://ibl.ai/bot)",
21041
+ userAgentHelpText: "Opcional. Sustituye el User-Agent predeterminado del rastreador en cada solicitud. Déjalo en blanco para usar el predeterminado.",
20611
21042
  crawlerMatchPatternsLabel: "Patrones de coincidencia del rastreador",
20612
21043
  globPlaceholder: "https://example.com/",
20613
21044
  regexPlaceholder: "(http|https)://www.example.com/(.*)",
@@ -21812,8 +22243,8 @@ var uiSidebar$1 = {
21812
22243
  var userProfileDropdownIndex$1 = {
21813
22244
  profile: "Perfil",
21814
22245
  account: "Cuenta",
21815
- learner: "Estudiante",
21816
- instructor: "Instructor",
22246
+ learner: "Usuario",
22247
+ instructor: "Administrador",
21817
22248
  help: "Ayuda",
21818
22249
  logOut: "Cerrar sesión",
21819
22250
  moreOptions: "Más opciones"
@@ -23184,6 +23615,177 @@ var profileLlmUsage$1 = {
23184
23615
  tryAgain: "Intentar de nuevo",
23185
23616
  scopeUser: "Uso de {username}"
23186
23617
  };
23618
+ var virtualMachineHostInput$1 = {
23619
+ listLabel: "Hosts permitidos",
23620
+ removeHost: "Quitar {host}",
23621
+ placeholder: "api.ejemplo.com:443",
23622
+ hint: "Escribe host:puerto y pulsa Intro, o pega una lista.",
23623
+ errorFormat: "{entry} debe tener el formato host:puerto, por ejemplo api.ejemplo.com:443, sin esquema, ruta ni comodín.",
23624
+ errorPort: "{entry}: el puerto debe estar entre 1 y 65535.",
23625
+ errorRejectedHost: "{entry}: este host está reservado y no se puede permitir.",
23626
+ errorIpNotAllowed: "{entry}: usa aquí un nombre de host, no una dirección IP.",
23627
+ errorDuplicate: "{entry} ya está en la lista.",
23628
+ errorMax: "Puedes añadir como máximo {max} hosts."
23629
+ };
23630
+ var virtualMachineAdminTab$1 = {
23631
+ infoBox: "Los agentes con Shell de máquina virtual ejecutan código en una VM Linux aislada que, por defecto, no tiene acceso a la red. Las políticas de red son listas reutilizables de destinos host:puerto a los que una VM puede llegar, y los secretos de VM son claves que la VM puede usar pero nunca leer. Elígelos para un agente desde su configuración de Sandbox.",
23632
+ tabsAriaLabel: "Secciones de máquina virtual",
23633
+ tabNetworkPolicies: "Políticas de red",
23634
+ tabSecrets: "Secretos",
23635
+ forbidden: "No tienes permiso para gestionar las políticas de red ni los secretos de las máquinas virtuales."
23636
+ };
23637
+ var virtualMachineNetworkPolicies$1 = {
23638
+ newPolicy: "Nueva política",
23639
+ loadFailed: "No se pudieron cargar las políticas de red. Inténtalo de nuevo en un momento.",
23640
+ colName: "Nombre",
23641
+ colHosts: "Hosts permitidos",
23642
+ colDescription: "Descripción",
23643
+ colUpdated: "Actualizada",
23644
+ colActions: "Acciones",
23645
+ hostsListAriaLabel: "Hosts permitidos por {name}",
23646
+ moreHosts: "+{count} más",
23647
+ editAriaLabel: "Editar la política {name}",
23648
+ deleteAriaLabel: "Eliminar la política {name}",
23649
+ empty: "Todavía no hay políticas de red. Crea una para que la VM de un agente pueda llegar a hosts concretos.",
23650
+ createTitle: "Nueva política de red",
23651
+ editTitle: "Editar política de red",
23652
+ dialogDescription: "Los destinos host:puerto a los que una VM puede llegar con esta política. HTTPS es el puerto 443.",
23653
+ fieldName: "Nombre",
23654
+ namePlaceholder: "p. ej. APIs de Acme",
23655
+ fieldHosts: "Hosts permitidos",
23656
+ hostsHint: "Escribe host:puerto y pulsa Intro, o pega una lista. Hasta {max} entradas; se admiten direcciones IP.",
23657
+ removedHostsWarning: "Vas a quitar {hosts}. Si un agente usa esta política y tiene un secreto que se envía a uno de estos hosts, ese secreto dejará de funcionar.",
23658
+ fieldDescription: "Descripción (opcional)",
23659
+ descriptionPlaceholder: "Para qué sirve esta política",
23660
+ cancel: "Cancelar",
23661
+ saveChanges: "Guardar cambios",
23662
+ createPolicy: "Crear política",
23663
+ errorNameRequired: "Dale un nombre a la política.",
23664
+ errorHostsRequired: "Añade al menos un host.",
23665
+ created: "Política de red creada",
23666
+ updated: "Política de red actualizada",
23667
+ deleted: "Política de red eliminada",
23668
+ saveFailed: "No se pudo guardar la política. Inténtalo de nuevo.",
23669
+ forbiddenWrite: "No tienes permiso para modificar políticas de red.",
23670
+ forbiddenDelete: "No tienes permiso para eliminar políticas de red.",
23671
+ deleteTitle: "Eliminar política de red",
23672
+ deleteConfirm: "¿Eliminar {name}? Los agentes con el perfil Personalizado solo pueden usar políticas que sigan existiendo.",
23673
+ deleteBlockedInUse: "Esta política todavía la usan los siguientes agentes. Cámbialos antes a otra política o perfil.",
23674
+ deleteFailed: "No se pudo eliminar la política. Inténtalo de nuevo.",
23675
+ "delete": "Eliminar"
23676
+ };
23677
+ var virtualMachineSecrets$1 = {
23678
+ newSecret: "Nuevo secreto",
23679
+ loadFailed: "No se pudieron cargar los secretos de VM. Inténtalo de nuevo en un momento.",
23680
+ colName: "Nombre",
23681
+ colEnvVar: "Variable",
23682
+ colHosts: "Hosts permitidos",
23683
+ colSource: "Origen",
23684
+ colUpdated: "Actualizado",
23685
+ colActions: "Acciones",
23686
+ hostsListAriaLabel: "Hosts a los que se envía {name}",
23687
+ moreHosts: "+{count} más",
23688
+ sourceStored: "Valor guardado",
23689
+ sourceFromCredential: "Credencial {credential} · {field}",
23690
+ editAriaLabel: "Editar el secreto {name}",
23691
+ deleteAriaLabel: "Eliminar el secreto {name}",
23692
+ empty: "Todavía no hay secretos de VM. Crea uno para que un agente llame a una API con la clave de tu organización sin verla nunca.",
23693
+ createTitle: "Nuevo secreto de VM",
23694
+ editTitle: "Editar secreto de VM",
23695
+ dialogDescription: "El valor no vuelve a mostrarse después de guardarlo.",
23696
+ fieldName: "Nombre",
23697
+ namePlaceholder: "p. ej. Clave de la API de Acme",
23698
+ fieldEnvVar: "Variable de entorno",
23699
+ envVarPlaceholder: "p. ej. ACME_KEY",
23700
+ envVarHelp: "Letras, dígitos y guiones bajos; no puede empezar por un dígito.",
23701
+ envVarReadOnlyHelp: "No se puede renombrar. Elimina este secreto y crea uno nuevo.",
23702
+ fieldHosts: "Hosts permitidos",
23703
+ hostsHint: "Solo estos hosts reciben la clave real; limita la lista al host más concreto que la necesite. Escribe host:puerto y pulsa Intro.",
23704
+ fieldSource: "Origen del valor",
23705
+ sourceValue: "Introducir un valor",
23706
+ sourceValueDescription: "Se guarda cifrada y no vuelve a mostrarse.",
23707
+ sourceCredential: "Usar una credencial de integración",
23708
+ sourceCredentialDescription: "Lee un campo de una credencial guardada y sigue las rotaciones automáticamente.",
23709
+ fieldValue: "Valor",
23710
+ valuePlaceholder: "Pega la clave",
23711
+ valueKeepPlaceholder: "Déjalo en blanco para conservar el valor actual",
23712
+ valueHelp: "Al menos {min} caracteres.",
23713
+ valueKeepHelp: "Déjalo en blanco para conservar el valor actual.",
23714
+ fieldCredential: "Credencial de integración",
23715
+ credentialPlaceholder: "Elige una credencial",
23716
+ noCredentials: "Tu organización todavía no tiene credenciales de integración. Añade una en Integraciones o introduce un valor.",
23717
+ fieldCredentialField: "Campo de la credencial",
23718
+ credentialFieldPlaceholder: "Elige un campo",
23719
+ credentialNoFields: "Esta credencial no tiene campos de los que leer.",
23720
+ cancel: "Cancelar",
23721
+ saveChanges: "Guardar cambios",
23722
+ createSecret: "Crear secreto",
23723
+ errorNameRequired: "Dale un nombre al secreto.",
23724
+ errorEnvVarRequired: "Introduce el nombre de la variable de entorno.",
23725
+ errorEnvVarFormat: "Usa solo letras, dígitos y guiones bajos, y no empieces por un dígito.",
23726
+ errorHostsRequired: "Añade al menos un host.",
23727
+ errorValueRequired: "Introduce el valor.",
23728
+ errorValueTooShort: "El valor debe tener al menos {min} caracteres.",
23729
+ errorValueMasked: "Parece un valor enmascarado. Pega la clave real.",
23730
+ errorCredentialRequired: "Elige una credencial.",
23731
+ errorFieldRequired: "Elige el campo de la credencial.",
23732
+ created: "Secreto de VM creado",
23733
+ updated: "Secreto de VM actualizado",
23734
+ deleted: "Secreto de VM eliminado",
23735
+ saveFailed: "No se pudo guardar el secreto. Inténtalo de nuevo.",
23736
+ forbiddenWrite: "No tienes permiso para modificar secretos de VM.",
23737
+ forbiddenDelete: "No tienes permiso para eliminar secretos de VM.",
23738
+ deleteTitle: "Eliminar secreto de VM",
23739
+ deleteConfirm: "¿Eliminar {envVar}? También se quitará de todos los agentes que lo usan.",
23740
+ deleteFailed: "No se pudo eliminar el secreto. Inténtalo de nuevo.",
23741
+ "delete": "Eliminar"
23742
+ };
23743
+ var virtualMachineNetworkSection$1 = {
23744
+ title: "Acceso a la red",
23745
+ description: "Decide a qué puede llegar la máquina virtual de este agente y qué credenciales puede usar.",
23746
+ billingNotice: "El tiempo de máquina virtual se cobra de los créditos del usuario que chatea: ${usd} por cada {minutes} minutos por defecto, prorrateado por segundo. La tarifa de tu organización puede ser distinta.",
23747
+ egressLabel: "Perfil de salida",
23748
+ egressHelp: "Cuánta red recibe la VM.",
23749
+ egressNoneLabel: "Sin red",
23750
+ egressNoneDescription: "La VM no puede llegar a nada.",
23751
+ egressRegistriesLabel: "Registros de paquetes",
23752
+ egressRegistriesDescription: "Solo PyPI, npm, apt y apk, para instalar paquetes.",
23753
+ egressPublicLabel: "Internet público",
23754
+ egressPublicDescription: "Cualquier host público. Las redes privadas, localhost y los metadatos de la nube siguen bloqueados.",
23755
+ egressCustomLabel: "Lista personalizada",
23756
+ egressCustomDescription: "Solo los hosts de la política de red que elijas abajo.",
23757
+ policyLabel: "Política de red",
23758
+ policiesForbidden: "No tienes permiso para ver las políticas de red, así que este perfil no se puede configurar desde aquí.",
23759
+ noPolicies: "Todavía no hay políticas de red",
23760
+ policyPlaceholder: "Elige una política",
23761
+ createPolicy: "Crear política",
23762
+ editPolicy: "Editar política",
23763
+ managePolicies: "Gestionar políticas",
23764
+ manageSecrets: "Gestionar secretos",
23765
+ policyRequired: "El perfil Personalizado necesita una política de red.",
23766
+ policyHosts: "Hosts que permite esta política:",
23767
+ secretsLabel: "Secretos",
23768
+ secretsHelp: "Claves que la VM puede usar pero nunca leer. Hasta {max} por agente.",
23769
+ noSecrets: "Tu organización todavía no tiene secretos de VM. Usa Gestionar secretos para crear uno.",
23770
+ secretsCapReached: "Este agente tiene el máximo de {max} secretos.",
23771
+ uncoveredTitle: "Algunos secretos se envían a hosts que esta política no permite:",
23772
+ uncoveredItem: "{envVar} necesita {hosts}, y {policy} no lo permite.",
23773
+ addHostsAndSave: "Añadir hosts a {policy} y guardar",
23774
+ saved: "Configuración de red guardada",
23775
+ saveFailed: "No se pudo guardar la configuración de red. Inténtalo de nuevo.",
23776
+ forbiddenSave: "No tienes permiso para cambiar esta configuración.",
23777
+ policyHostsAdded: "Hosts añadidos a {policy}",
23778
+ policyUpdateFailed: "No se pudo actualizar la política, así que la configuración no se guardó.",
23779
+ policyUpdateForbidden: "No tienes permiso para modificar políticas de red, así que la configuración no se guardó.",
23780
+ discard: "Descartar",
23781
+ save: "Guardar cambios",
23782
+ narrowTitle: "¿Desvincular los secretos?",
23783
+ narrowDescription: "{profile} no da a la VM acceso a los hosts que necesitan estos secretos, así que al guardar se {count, plural, one {desvinculará # secreto} other {desvincularán # secretos}} de este agente.",
23784
+ cancel: "Cancelar",
23785
+ narrowConfirm: "Desvincular y guardar",
23786
+ manageTitle: "Configuración de la máquina virtual",
23787
+ manageDescription: "Las políticas de red y los secretos de VM son compartidos por todos los agentes de tu organización."
23788
+ };
23187
23789
  var es = {
23188
23790
  accessTabAddAccess: accessTabAddAccess$1,
23189
23791
  accessTabLabels: accessTabLabels$1,
@@ -23469,7 +24071,12 @@ var es = {
23469
24071
  analyticsCostTabs: analyticsCostTabs$1,
23470
24072
  analyticsLlmUsage: analyticsLlmUsage$1,
23471
24073
  analyticsTraces: analyticsTraces$1,
23472
- profileLlmUsage: profileLlmUsage$1
24074
+ profileLlmUsage: profileLlmUsage$1,
24075
+ virtualMachineHostInput: virtualMachineHostInput$1,
24076
+ virtualMachineAdminTab: virtualMachineAdminTab$1,
24077
+ virtualMachineNetworkPolicies: virtualMachineNetworkPolicies$1,
24078
+ virtualMachineSecrets: virtualMachineSecrets$1,
24079
+ virtualMachineNetworkSection: virtualMachineNetworkSection$1
23473
24080
  };
23474
24081
 
23475
24082
  var accessTabAddAccess = {
@@ -25971,7 +26578,9 @@ var datasetsTabAddResourceModal = {
25971
26578
  title: "添加资源",
25972
26579
  description: "添加知识以帮助您的代理提供更相关的见解。其他具有编辑权限的用户可以为更多主题重复使用这些来源。",
25973
26580
  googleDrivePickerError: "Google Drive 选择器错误",
25974
- forceClosePicker: "强制关闭选择器"
26581
+ forceClosePicker: "强制关闭选择器",
26582
+ notConfigured: "未为此租户配置",
26583
+ scrollRegionAriaLabel: "资源类型"
25975
26584
  };
25976
26585
  var datasetsTabDatasetItem = {
25977
26586
  scheduleRetrain: "安排重新训练",
@@ -27348,7 +27957,9 @@ var profileAccount = {
27348
27957
  groupSystem: "系统",
27349
27958
  ariaSettingsNav: "组织设置",
27350
27959
  ariaSettingsNavMobile: "组织设置(移动端)",
27351
- groupFinance: "财务"
27960
+ groupFinance: "财务",
27961
+ tabVirtualMachine: "虚拟机",
27962
+ descVirtualMachine: "管理可供智能体虚拟机使用的网络策略和密钥。"
27352
27963
  };
27353
27964
  var profileAddCreditsModal = {
27354
27965
  title: "添加积分",
@@ -27889,7 +28500,11 @@ var resourceModalGithubFileUploadModal = {
27889
28500
  errorAddingDocument: "添加训练文档时出错",
27890
28501
  githubRepoUrlPlaceholder: "GitHub 仓库 URL",
27891
28502
  selectBranchPlaceholder: "选择分支",
27892
- submit: "提交"
28503
+ submit: "提交",
28504
+ description: "添加 GitHub 仓库并选择分支来训练您的智能体。",
28505
+ scrollRegionAriaLabel: "仓库详情",
28506
+ repoUrlLabel: "仓库 URL",
28507
+ branchLabel: "分支"
27893
28508
  };
27894
28509
  var resourceModalLocalFileUploadModal = {
27895
28510
  fileNotFound: "未找到文件",
@@ -27903,7 +28518,9 @@ var resourceModalLocalFileUploadModal = {
27903
28518
  descriptionOptional: "描述(可选)",
27904
28519
  descriptionPlaceholder: "为此图片添加描述...",
27905
28520
  submitting: "提交中",
27906
- submit: "提交"
28521
+ submit: "提交",
28522
+ description: "上传文件,用其内容训练您的智能体。",
28523
+ scrollRegionAriaLabel: "文件上传"
27907
28524
  };
27908
28525
  var resourceModalUrlUploadModal = {
27909
28526
  urlPlaceholder: "URL",
@@ -27912,11 +28529,15 @@ var resourceModalUrlUploadModal = {
27912
28529
  invalidYoutubeUrl: "无效的 YouTube URL",
27913
28530
  invalidBlackboardUrl: "无效的 Blackboard URL",
27914
28531
  invalidUrl: "无效的 URL",
27915
- submit: "提交"
28532
+ submit: "提交",
28533
+ description: "添加链接,用其内容训练您的智能体。",
28534
+ scrollRegionAriaLabel: "链接详情",
28535
+ urlLabel: "URL"
27916
28536
  };
27917
28537
  var resourceModalWebsiteCrawlModal = {
27918
- title: "网络爬虫配置",
27919
28538
  description: "配置您的网络爬虫设置和 URL 模式",
28539
+ infoBox: "抓取网站并用其页面训练您的智能体。设置链接跟踪深度、抓取页面数量以及要包含的网址。",
28540
+ scrollRegionAriaLabel: "网络爬虫设置",
27920
28541
  formAriaLabel: "网络爬虫配置表单",
27921
28542
  urlRequired: "URL 为必填项",
27922
28543
  invalidUrl: "无效的 URL",
@@ -27931,6 +28552,9 @@ var resourceModalWebsiteCrawlModal = {
27931
28552
  selectPatternTypeAriaLabel: "选择模式类型",
27932
28553
  globPattern: "Glob 模式",
27933
28554
  regularExpression: "正则表达式",
28555
+ userAgentLabel: "User-Agent(可选)",
28556
+ userAgentPlaceholder: "Iblai-bot/1.0 (+https://ibl.ai/bot)",
28557
+ userAgentHelpText: "可选。为每个请求覆盖默认的抓取工具 User-Agent。留空则使用默认值。",
27934
28558
  crawlerMatchPatternsLabel: "爬虫匹配模式",
27935
28559
  globPlaceholder: "https://example.com/",
27936
28560
  regexPlaceholder: "(http|https)://www.example.com/(.*)",
@@ -29135,8 +29759,8 @@ var uiSidebar = {
29135
29759
  var userProfileDropdownIndex = {
29136
29760
  profile: "个人资料",
29137
29761
  account: "账户",
29138
- learner: "学习者",
29139
- instructor: "讲师",
29762
+ learner: "用户",
29763
+ instructor: "管理员",
29140
29764
  help: "帮助",
29141
29765
  logOut: "退出登录",
29142
29766
  moreOptions: "更多选项"
@@ -30507,6 +31131,177 @@ var profileLlmUsage = {
30507
31131
  tryAgain: "重试",
30508
31132
  scopeUser: "{username} 的用量"
30509
31133
  };
31134
+ var virtualMachineHostInput = {
31135
+ listLabel: "允许的主机",
31136
+ removeHost: "移除 {host}",
31137
+ placeholder: "api.example.com:443",
31138
+ hint: "输入 host:port 并按 Enter,或粘贴列表。",
31139
+ errorFormat: "{entry} 必须为 host:port 格式,例如 api.example.com:443,不能包含协议、路径或通配符。",
31140
+ errorPort: "{entry}:端口必须在 1 到 65535 之间。",
31141
+ errorRejectedHost: "{entry}:此主机为保留主机,不能加入允许列表。",
31142
+ errorIpNotAllowed: "{entry}:此处请使用主机名,而不是 IP 地址。",
31143
+ errorDuplicate: "{entry} 已在列表中。",
31144
+ errorMax: "最多只能添加 {max} 个主机。"
31145
+ };
31146
+ var virtualMachineAdminTab = {
31147
+ infoBox: "启用了虚拟机 Shell 的智能体会在隔离的 Linux 虚拟机中运行代码,默认没有网络访问权限。网络策略是可复用的 host:port 目标列表,规定虚拟机可以访问的地址;虚拟机密钥是虚拟机可以使用但永远无法读取的密钥。请在智能体的沙箱设置中为其选择。",
31148
+ tabsAriaLabel: "虚拟机设置分区",
31149
+ tabNetworkPolicies: "网络策略",
31150
+ tabSecrets: "密钥",
31151
+ forbidden: "您没有权限管理虚拟机的网络策略或密钥。"
31152
+ };
31153
+ var virtualMachineNetworkPolicies = {
31154
+ newPolicy: "新建策略",
31155
+ loadFailed: "无法加载网络策略,请稍后重试。",
31156
+ colName: "名称",
31157
+ colHosts: "允许的主机",
31158
+ colDescription: "描述",
31159
+ colUpdated: "更新时间",
31160
+ colActions: "操作",
31161
+ hostsListAriaLabel: "{name} 允许的主机",
31162
+ moreHosts: "还有 {count} 个",
31163
+ editAriaLabel: "编辑策略 {name}",
31164
+ deleteAriaLabel: "删除策略 {name}",
31165
+ empty: "还没有网络策略。创建一个策略,让智能体的虚拟机可以访问指定主机。",
31166
+ createTitle: "新建网络策略",
31167
+ editTitle: "编辑网络策略",
31168
+ dialogDescription: "此策略下虚拟机可以访问的 host:port 目标。HTTPS 使用 443 端口。",
31169
+ fieldName: "名称",
31170
+ namePlaceholder: "例如:Acme API",
31171
+ fieldHosts: "允许的主机",
31172
+ hostsHint: "输入 host:port 并按 Enter,或粘贴列表。最多 {max} 条;允许使用 IP 地址。",
31173
+ removedHostsWarning: "您正在移除 {hosts}。如果某个智能体使用此策略,并且有密钥会发送到这些主机之一,该密钥将无法再使用。",
31174
+ fieldDescription: "描述(可选)",
31175
+ descriptionPlaceholder: "此策略的用途",
31176
+ cancel: "取消",
31177
+ saveChanges: "保存更改",
31178
+ createPolicy: "创建策略",
31179
+ errorNameRequired: "请为策略输入名称。",
31180
+ errorHostsRequired: "请至少添加一个主机。",
31181
+ created: "网络策略已创建",
31182
+ updated: "网络策略已更新",
31183
+ deleted: "网络策略已删除",
31184
+ saveFailed: "无法保存策略,请重试。",
31185
+ forbiddenWrite: "您没有权限修改网络策略。",
31186
+ forbiddenDelete: "您没有权限删除网络策略。",
31187
+ deleteTitle: "删除网络策略",
31188
+ deleteConfirm: "确定删除 {name} 吗?设为“自定义”配置的智能体只能使用仍然存在的策略。",
31189
+ deleteBlockedInUse: "以下智能体仍在使用此策略。请先将它们改为其他策略或配置。",
31190
+ deleteFailed: "无法删除策略,请重试。",
31191
+ "delete": "删除"
31192
+ };
31193
+ var virtualMachineSecrets = {
31194
+ newSecret: "新建密钥",
31195
+ loadFailed: "无法加载虚拟机密钥,请稍后重试。",
31196
+ colName: "名称",
31197
+ colEnvVar: "变量",
31198
+ colHosts: "允许的主机",
31199
+ colSource: "来源",
31200
+ colUpdated: "更新时间",
31201
+ colActions: "操作",
31202
+ hostsListAriaLabel: "{name} 发送至的主机",
31203
+ moreHosts: "还有 {count} 个",
31204
+ sourceStored: "已存储的值",
31205
+ sourceFromCredential: "凭据 {credential} · {field}",
31206
+ editAriaLabel: "编辑密钥 {name}",
31207
+ deleteAriaLabel: "删除密钥 {name}",
31208
+ empty: "还没有虚拟机密钥。创建一个密钥,让智能体无需看到组织的密钥即可调用 API。",
31209
+ createTitle: "新建虚拟机密钥",
31210
+ editTitle: "编辑虚拟机密钥",
31211
+ dialogDescription: "保存后,该值不会再显示。",
31212
+ fieldName: "名称",
31213
+ namePlaceholder: "例如:Acme API 密钥",
31214
+ fieldEnvVar: "环境变量",
31215
+ envVarPlaceholder: "例如:ACME_KEY",
31216
+ envVarHelp: "只能包含字母、数字和下划线,且不能以数字开头。",
31217
+ envVarReadOnlyHelp: "无法重命名。请删除此密钥并新建一个。",
31218
+ fieldHosts: "允许的主机",
31219
+ hostsHint: "只有这些主机会收到真实密钥,请仅填写需要它的最小范围主机。输入 host:port 并按 Enter。",
31220
+ fieldSource: "值的来源",
31221
+ sourceValue: "输入一个值",
31222
+ sourceValueDescription: "加密存储,之后不再显示。",
31223
+ sourceCredential: "使用集成凭据",
31224
+ sourceCredentialDescription: "读取已保存凭据中的一个字段,并自动跟随凭据轮换。",
31225
+ fieldValue: "值",
31226
+ valuePlaceholder: "粘贴密钥",
31227
+ valueKeepPlaceholder: "留空以保留当前值",
31228
+ valueHelp: "至少 {min} 个字符。",
31229
+ valueKeepHelp: "留空以保留当前值。",
31230
+ fieldCredential: "集成凭据",
31231
+ credentialPlaceholder: "选择一个凭据",
31232
+ noCredentials: "您的组织还没有集成凭据。请在“集成”中添加,或改为输入一个值。",
31233
+ fieldCredentialField: "凭据字段",
31234
+ credentialFieldPlaceholder: "选择一个字段",
31235
+ credentialNoFields: "此凭据没有可读取的字段。",
31236
+ cancel: "取消",
31237
+ saveChanges: "保存更改",
31238
+ createSecret: "创建密钥",
31239
+ errorNameRequired: "请为密钥输入名称。",
31240
+ errorEnvVarRequired: "请输入环境变量名。",
31241
+ errorEnvVarFormat: "只能使用字母、数字和下划线,且不能以数字开头。",
31242
+ errorHostsRequired: "请至少添加一个主机。",
31243
+ errorValueRequired: "请输入值。",
31244
+ errorValueTooShort: "值至少需要 {min} 个字符。",
31245
+ errorValueMasked: "这看起来是打码后的值,请粘贴真实密钥。",
31246
+ errorCredentialRequired: "请选择一个凭据。",
31247
+ errorFieldRequired: "请选择凭据字段。",
31248
+ created: "虚拟机密钥已创建",
31249
+ updated: "虚拟机密钥已更新",
31250
+ deleted: "虚拟机密钥已删除",
31251
+ saveFailed: "无法保存密钥,请重试。",
31252
+ forbiddenWrite: "您没有权限修改虚拟机密钥。",
31253
+ forbiddenDelete: "您没有权限删除虚拟机密钥。",
31254
+ deleteTitle: "删除虚拟机密钥",
31255
+ deleteConfirm: "确定删除 {envVar} 吗?它也会从所有使用它的智能体中移除。",
31256
+ deleteFailed: "无法删除密钥,请重试。",
31257
+ "delete": "删除"
31258
+ };
31259
+ var virtualMachineNetworkSection = {
31260
+ title: "网络访问",
31261
+ description: "决定此智能体的虚拟机可以访问哪些地址,以及可以使用哪些凭据。",
31262
+ billingNotice: "虚拟机使用时长按聊天用户的积分计费:默认每 {minutes} 分钟 ${usd},按秒计算。您所在组织的费率可能不同。",
31263
+ egressLabel: "出站配置",
31264
+ egressHelp: "虚拟机可获得的网络访问范围。",
31265
+ egressNoneLabel: "无网络",
31266
+ egressNoneDescription: "虚拟机无法访问任何地址。",
31267
+ egressRegistriesLabel: "软件包仓库",
31268
+ egressRegistriesDescription: "仅限 PyPI、npm、apt 和 apk,用于安装软件包。",
31269
+ egressPublicLabel: "公共互联网",
31270
+ egressPublicDescription: "任何公共主机。私有网络、localhost 和云元数据仍被阻止。",
31271
+ egressCustomLabel: "自定义允许列表",
31272
+ egressCustomDescription: "仅限下方所选网络策略中的主机。",
31273
+ policyLabel: "网络策略",
31274
+ policiesForbidden: "您没有权限查看网络策略,因此无法在此处配置该选项。",
31275
+ noPolicies: "还没有网络策略",
31276
+ policyPlaceholder: "选择一个策略",
31277
+ createPolicy: "创建策略",
31278
+ editPolicy: "编辑策略",
31279
+ managePolicies: "管理策略",
31280
+ manageSecrets: "管理密钥",
31281
+ policyRequired: "“自定义”配置需要一个网络策略。",
31282
+ policyHosts: "此策略允许的主机:",
31283
+ secretsLabel: "密钥",
31284
+ secretsHelp: "虚拟机可以使用但永远无法读取的密钥。每个智能体最多 {max} 个。",
31285
+ noSecrets: "您的组织还没有虚拟机密钥。请使用“管理密钥”创建一个。",
31286
+ secretsCapReached: "此智能体已达到 {max} 个密钥的上限。",
31287
+ uncoveredTitle: "部分密钥会发送到此策略不允许的主机:",
31288
+ uncoveredItem: "{envVar} 需要 {hosts},但 {policy} 不允许。",
31289
+ addHostsAndSave: "将主机添加到 {policy} 并保存",
31290
+ saved: "网络设置已保存",
31291
+ saveFailed: "无法保存网络设置,请重试。",
31292
+ forbiddenSave: "您没有权限修改这些设置。",
31293
+ policyHostsAdded: "主机已添加到 {policy}",
31294
+ policyUpdateFailed: "无法更新策略,因此设置未保存。",
31295
+ policyUpdateForbidden: "您没有权限修改网络策略,因此设置未保存。",
31296
+ discard: "放弃更改",
31297
+ save: "保存更改",
31298
+ narrowTitle: "解除密钥绑定?",
31299
+ narrowDescription: "{profile} 不允许虚拟机访问这些密钥所需的主机,因此保存后将从此智能体解除 {count, plural, one {# 个密钥} other {# 个密钥}} 的绑定。",
31300
+ cancel: "取消",
31301
+ narrowConfirm: "解除绑定并保存",
31302
+ manageTitle: "虚拟机设置",
31303
+ manageDescription: "网络策略和虚拟机密钥由您组织中的所有智能体共享。"
31304
+ };
30510
31305
  var zh = {
30511
31306
  accessTabAddAccess: accessTabAddAccess,
30512
31307
  accessTabLabels: accessTabLabels,
@@ -30792,7 +31587,12 @@ var zh = {
30792
31587
  analyticsCostTabs: analyticsCostTabs,
30793
31588
  analyticsLlmUsage: analyticsLlmUsage,
30794
31589
  analyticsTraces: analyticsTraces,
30795
- profileLlmUsage: profileLlmUsage
31590
+ profileLlmUsage: profileLlmUsage,
31591
+ virtualMachineHostInput: virtualMachineHostInput,
31592
+ virtualMachineAdminTab: virtualMachineAdminTab,
31593
+ virtualMachineNetworkPolicies: virtualMachineNetworkPolicies,
31594
+ virtualMachineSecrets: virtualMachineSecrets,
31595
+ virtualMachineNetworkSection: virtualMachineNetworkSection
30796
31596
  };
30797
31597
 
30798
31598
  /**
@@ -200298,6 +201098,686 @@ function MemoryAdminTab({ tenantKey, username }) {
200298
201098
  return (jsxs(Tabs, { defaultValue: "global-memories", "aria-label": t('memoryAdminTab.tabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('memoryAdminTab.tabsAriaLabel'), className: "max-w-full overflow-x-auto justify-start px-1", children: [jsx(TabsTrigger, { "data-testid": "memory-admin-sub-tab-global", value: "global-memories", children: t('memoryAdminTab.tabGlobalMemories') }), jsx(TabsTrigger, { "data-testid": "memory-admin-sub-tab-agent", value: "agent-memories", children: t('memoryAdminTab.tabAgentMemories') })] }), jsx(TabsContent, { className: "mt-4", value: "global-memories", children: jsx(GlobalMemoriesSection, { tenantKey: tenantKey, username: username }) }), jsx(TabsContent, { className: "mt-4", value: "agent-memories", children: jsx(AgentMemoriesSection, { tenantKey: tenantKey, username: username }) })] }));
200299
201099
  }
200300
201100
 
201101
+ const ISSUE_KEYS = {
201102
+ format: 'virtualMachineHostInput.errorFormat',
201103
+ port: 'virtualMachineHostInput.errorPort',
201104
+ rejected_host: 'virtualMachineHostInput.errorRejectedHost',
201105
+ ip_not_allowed: 'virtualMachineHostInput.errorIpNotAllowed',
201106
+ };
201107
+ /**
201108
+ * A chip (tag) input for `host:port` allowlists. Entries are added with Enter,
201109
+ * comma or space, or by pasting a list; each is checked client-side
201110
+ * (`validateHostPort`) before it becomes a chip, and invalid ones stay in the
201111
+ * box with the reason announced through a polite live region. Backspace on an
201112
+ * empty box removes the last chip; every chip has a labelled remove button so
201113
+ * the whole control works from the keyboard.
201114
+ */
201115
+ function HostPortChipInput({ id, value, onChange, allowIpAddresses = false, maxEntries, disabled = false, placeholder, describedBy, error, listLabel, hint, testId, }) {
201116
+ var _a;
201117
+ const t = useT();
201118
+ const generatedId = useId$2();
201119
+ const hintId = `${generatedId}-hint`;
201120
+ const errorId = `${generatedId}-error`;
201121
+ const [draft, setDraft] = useState('');
201122
+ const [localError, setLocalError] = useState(null);
201123
+ const message = (_a = localError !== null && localError !== void 0 ? localError : error) !== null && _a !== void 0 ? _a : null;
201124
+ /**
201125
+ * Turn the typed / pasted text into chips. Valid entries are appended;
201126
+ * whatever failed stays in the box, with the first problem explained.
201127
+ */
201128
+ const commit = (text) => {
201129
+ const entries = splitHostPortInput(text);
201130
+ if (entries.length === 0) {
201131
+ setDraft('');
201132
+ return;
201133
+ }
201134
+ const next = [...value];
201135
+ const leftover = [];
201136
+ let problem = null;
201137
+ for (const entry of entries) {
201138
+ if (next.includes(entry)) {
201139
+ problem !== null && problem !== void 0 ? problem : (problem = t('virtualMachineHostInput.errorDuplicate', { entry }));
201140
+ continue;
201141
+ }
201142
+ const issue = validateHostPort(entry, { allowIpAddresses });
201143
+ if (issue) {
201144
+ problem !== null && problem !== void 0 ? problem : (problem = t(ISSUE_KEYS[issue], { entry }));
201145
+ leftover.push(entry);
201146
+ continue;
201147
+ }
201148
+ if (maxEntries !== undefined && next.length >= maxEntries) {
201149
+ problem !== null && problem !== void 0 ? problem : (problem = t('virtualMachineHostInput.errorMax', { max: maxEntries }));
201150
+ leftover.push(entry);
201151
+ continue;
201152
+ }
201153
+ next.push(entry);
201154
+ }
201155
+ if (next.length !== value.length)
201156
+ onChange(next);
201157
+ setDraft(leftover.join(' '));
201158
+ setLocalError(problem);
201159
+ };
201160
+ const remove = (host) => {
201161
+ onChange(value.filter((entry) => entry !== host));
201162
+ };
201163
+ const handleKeyDown = (event) => {
201164
+ if (event.key === 'Enter' || event.key === ',' || event.key === ' ') {
201165
+ event.preventDefault();
201166
+ commit(draft);
201167
+ }
201168
+ else if (event.key === 'Backspace' && draft === '' && value.length > 0) {
201169
+ event.preventDefault();
201170
+ remove(value[value.length - 1]);
201171
+ }
201172
+ else if (event.key === 'Escape' && (draft !== '' || localError)) {
201173
+ event.preventDefault();
201174
+ setDraft('');
201175
+ setLocalError(null);
201176
+ }
201177
+ };
201178
+ const handlePaste = (event) => {
201179
+ const pasted = event.clipboardData.getData('text');
201180
+ if (!pasted)
201181
+ return;
201182
+ event.preventDefault();
201183
+ commit(`${draft} ${pasted}`);
201184
+ };
201185
+ return (jsxs("div", { className: "space-y-2", "data-testid": testId, children: [value.length > 0 && (jsx("ul", { role: "list", "aria-label": listLabel !== null && listLabel !== void 0 ? listLabel : t('virtualMachineHostInput.listLabel'), className: "flex flex-wrap gap-2", children: value.map((host) => (jsxs("li", { className: "inline-flex items-center gap-1 rounded-md border border-gray-200 bg-gray-50 py-0.5 pr-1 pl-2 font-mono text-xs text-gray-700", children: [jsx("span", { children: host }), !disabled && (jsx("button", { type: "button", onClick: () => remove(host), "aria-label": t('virtualMachineHostInput.removeHost', { host }), className: "cursor-pointer rounded p-0.5 text-gray-400 hover:text-gray-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500", children: jsx(X$1, { className: "h-3 w-3", "aria-hidden": "true" }) }))] }, host))) })), jsx(Input, { id: id, type: "text", value: draft, disabled: disabled, placeholder: placeholder !== null && placeholder !== void 0 ? placeholder : t('virtualMachineHostInput.placeholder'), autoComplete: "off", autoCapitalize: "off", spellCheck: false, onChange: (event) => {
201186
+ setDraft(event.target.value);
201187
+ if (localError)
201188
+ setLocalError(null);
201189
+ }, onKeyDown: handleKeyDown, onPaste: handlePaste, onBlur: () => {
201190
+ if (draft.trim())
201191
+ commit(draft);
201192
+ }, "aria-invalid": message ? true : undefined, "aria-describedby": [describedBy, hintId, errorId].filter(Boolean).join(' ') }), jsx("p", { id: hintId, className: "text-xs text-gray-500", children: hint !== null && hint !== void 0 ? hint : t('virtualMachineHostInput.hint') }), jsx("p", { id: errorId, "aria-live": "polite", className: cn(message ? 'text-xs text-red-600' : 'sr-only'), children: message })] }));
201193
+ }
201194
+
201195
+ const NAME_MAX_LENGTH$1 = 255;
201196
+ /**
201197
+ * Create / edit form for a virtual-machine network policy: a name (unique in
201198
+ * the org), 1–100 `host:port` entries and an optional description. Backend
201199
+ * 400s land next to the matching field or, for a plain-string `error`, as a
201200
+ * form-level message. On edit, only the fields that changed are sent
201201
+ * (`allowed_hosts` always replaces the whole list), and removing a host warns
201202
+ * that a bound secret which needs it would stop working — the backend does
201203
+ * not block that edit.
201204
+ */
201205
+ function NetworkPolicyDialog({ open, onOpenChange, tenantKey, policy = null, onSaved, }) {
201206
+ const t = useT();
201207
+ const baseId = useId$2();
201208
+ const nameId = `${baseId}-name`;
201209
+ const hostsId = `${baseId}-hosts`;
201210
+ const descriptionId = `${baseId}-description`;
201211
+ const [createPolicy, { isLoading: isCreating }] = useCreateVirtualMachineNetworkPolicyMutation();
201212
+ const [updatePolicy, { isLoading: isUpdating }] = useUpdateVirtualMachineNetworkPolicyMutation();
201213
+ const isSaving = isCreating || isUpdating;
201214
+ const [name, setName] = useState('');
201215
+ const [hosts, setHosts] = useState([]);
201216
+ const [description, setDescription] = useState('');
201217
+ const [fieldErrors, setFieldErrors] = useState({});
201218
+ const [formError, setFormError] = useState(null);
201219
+ // Start from the policy being edited (or blank) every time the dialog opens.
201220
+ useEffect(() => {
201221
+ var _a, _b, _c;
201222
+ if (!open)
201223
+ return;
201224
+ setName((_a = policy === null || policy === void 0 ? void 0 : policy.name) !== null && _a !== void 0 ? _a : '');
201225
+ setHosts((_b = policy === null || policy === void 0 ? void 0 : policy.allowed_hosts) !== null && _b !== void 0 ? _b : []);
201226
+ setDescription((_c = policy === null || policy === void 0 ? void 0 : policy.description) !== null && _c !== void 0 ? _c : '');
201227
+ setFieldErrors({});
201228
+ setFormError(null);
201229
+ }, [open, policy]);
201230
+ const isEdit = policy !== null;
201231
+ const removedHosts = isEdit ? policy.allowed_hosts.filter((host) => !hosts.includes(host)) : [];
201232
+ const handleSubmit = async (event) => {
201233
+ var _a;
201234
+ event.preventDefault();
201235
+ const trimmedName = name.trim();
201236
+ const trimmedDescription = description.trim();
201237
+ const clientErrors = {};
201238
+ if (!trimmedName)
201239
+ clientErrors.name = t('virtualMachineNetworkPolicies.errorNameRequired');
201240
+ if (hosts.length === 0) {
201241
+ clientErrors.allowed_hosts = t('virtualMachineNetworkPolicies.errorHostsRequired');
201242
+ }
201243
+ setFieldErrors(clientErrors);
201244
+ setFormError(null);
201245
+ if (Object.keys(clientErrors).length > 0)
201246
+ return;
201247
+ try {
201248
+ let saved;
201249
+ if (isEdit) {
201250
+ const body = { id: policy.id };
201251
+ if (trimmedName !== policy.name)
201252
+ body.name = trimmedName;
201253
+ if (hosts.length !== policy.allowed_hosts.length ||
201254
+ hosts.some((host, index) => host !== policy.allowed_hosts[index])) {
201255
+ body.allowed_hosts = hosts;
201256
+ }
201257
+ if (trimmedDescription !== ((_a = policy.description) !== null && _a !== void 0 ? _a : ''))
201258
+ body.description = trimmedDescription;
201259
+ if (Object.keys(body).length === 1) {
201260
+ onOpenChange(false);
201261
+ return;
201262
+ }
201263
+ saved = await updatePolicy({ org: tenantKey, body }).unwrap();
201264
+ toast.success(t('virtualMachineNetworkPolicies.updated'));
201265
+ }
201266
+ else {
201267
+ saved = await createPolicy({
201268
+ org: tenantKey,
201269
+ body: { name: trimmedName, allowed_hosts: hosts, description: trimmedDescription },
201270
+ }).unwrap();
201271
+ toast.success(t('virtualMachineNetworkPolicies.created'));
201272
+ }
201273
+ onSaved === null || onSaved === void 0 ? void 0 : onSaved(saved);
201274
+ onOpenChange(false);
201275
+ }
201276
+ catch (error) {
201277
+ const parsed = getVirtualMachineApiError(error);
201278
+ if (parsed.status === 403) {
201279
+ setFormError(t('virtualMachineNetworkPolicies.forbiddenWrite'));
201280
+ return;
201281
+ }
201282
+ const nextErrors = {};
201283
+ let unmatched = [];
201284
+ for (const [field, messages] of Object.entries(parsed.fieldErrors)) {
201285
+ if (field === 'name' || field === 'allowed_hosts' || field === 'description') {
201286
+ nextErrors[field] = messages.join(' ');
201287
+ }
201288
+ else {
201289
+ unmatched = unmatched.concat(messages);
201290
+ }
201291
+ }
201292
+ setFieldErrors(nextErrors);
201293
+ const message = [parsed.message, ...unmatched].filter(Boolean).join(' ');
201294
+ if (message) {
201295
+ setFormError(message);
201296
+ }
201297
+ else if (Object.keys(nextErrors).length === 0) {
201298
+ setFormError(t('virtualMachineNetworkPolicies.saveFailed'));
201299
+ }
201300
+ }
201301
+ };
201302
+ return (jsx(Dialog, { open: open, onOpenChange: (next) => !isSaving && onOpenChange(next), children: jsxs(DialogContent, { className: "max-h-[90vh] w-full max-w-lg overflow-y-auto", "data-testid": "network-policy-dialog", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: isEdit
201303
+ ? t('virtualMachineNetworkPolicies.editTitle')
201304
+ : t('virtualMachineNetworkPolicies.createTitle') }), jsx(DialogDescription, { children: t('virtualMachineNetworkPolicies.dialogDescription') })] }), jsxs("form", { onSubmit: handleSubmit, noValidate: true, className: "mt-1 grid gap-5", children: [formError && (jsx("div", { role: "alert", "data-testid": "network-policy-form-error", className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: formError })), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: nameId, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineNetworkPolicies.fieldName') }), jsx(Input, { id: nameId, value: name, maxLength: NAME_MAX_LENGTH$1, onChange: (event) => setName(event.target.value), placeholder: t('virtualMachineNetworkPolicies.namePlaceholder'), "aria-invalid": fieldErrors.name ? true : undefined, "aria-describedby": fieldErrors.name ? `${nameId}-error` : undefined, required: true }), fieldErrors.name && (jsx("p", { id: `${nameId}-error`, role: "alert", className: "text-xs text-red-600", children: fieldErrors.name }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: hostsId, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineNetworkPolicies.fieldHosts') }), jsx(HostPortChipInput, { id: hostsId, value: hosts, onChange: setHosts, allowIpAddresses: true, maxEntries: VIRTUAL_MACHINE_MAX_POLICY_HOSTS, hint: t('virtualMachineNetworkPolicies.hostsHint', {
201305
+ max: VIRTUAL_MACHINE_MAX_POLICY_HOSTS,
201306
+ }), error: fieldErrors.allowed_hosts, testId: "network-policy-hosts" }), removedHosts.length > 0 && (jsxs("div", { role: "status", "data-testid": "network-policy-removed-hosts-warning", className: "flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2 text-xs text-amber-800", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0", "aria-hidden": "true" }), jsx("span", { children: t('virtualMachineNetworkPolicies.removedHostsWarning', {
201307
+ hosts: removedHosts.join(', '),
201308
+ }) })] }))] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: descriptionId, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineNetworkPolicies.fieldDescription') }), jsx(Textarea, { id: descriptionId, value: description, rows: 2, onChange: (event) => setDescription(event.target.value), placeholder: t('virtualMachineNetworkPolicies.descriptionPlaceholder'), "aria-invalid": fieldErrors.description ? true : undefined, "aria-describedby": fieldErrors.description ? `${descriptionId}-error` : undefined }), fieldErrors.description && (jsx("p", { id: `${descriptionId}-error`, role: "alert", className: "text-xs text-red-600", children: fieldErrors.description }))] }), jsxs(DialogFooter, { children: [jsx(Button$1, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSaving, children: t('virtualMachineNetworkPolicies.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSaving, "data-testid": "network-policy-save", className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isSaving && jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }), isEdit
201309
+ ? t('virtualMachineNetworkPolicies.saveChanges')
201310
+ : t('virtualMachineNetworkPolicies.createPolicy')] })] })] })] }) }));
201311
+ }
201312
+
201313
+ const HOST_PREVIEW_COUNT$1 = 3;
201314
+ /** Placeholder swapped for the inline record in the delete sentence. */
201315
+ const RECORD_SLOT$1 = '__record__';
201316
+ const MENTOR_LOOKUP_LIMIT = 100;
201317
+ /** "3 days ago" for a backend timestamp, or a dash when it is unusable. */
201318
+ function relativeTime$1(value) {
201319
+ const date = new Date(value);
201320
+ return Number.isNaN(date.getTime()) ? '—' : formatDistanceToNow(date, { addSuffix: true });
201321
+ }
201322
+ /**
201323
+ * Network policies table with create / edit / delete. Deleting a policy an
201324
+ * agent still uses is refused by the backend; the refusal names the agents,
201325
+ * which are listed (by name where the tenant agent list resolves them, else
201326
+ * by id) so the admin knows what to move off it first.
201327
+ */
201328
+ function NetworkPoliciesSection({ tenantKey, username, policies, isLoading, isError, }) {
201329
+ var _a;
201330
+ const t = useT();
201331
+ const [dialogOpen, setDialogOpen] = useState(false);
201332
+ const [editingPolicy, setEditingPolicy] = useState(null);
201333
+ const [deletingPolicy, setDeletingPolicy] = useState(null);
201334
+ const [deleteFailure, setDeleteFailure] = useState(null);
201335
+ const [deletePolicy, { isLoading: isDeleting }] = useDeleteVirtualMachineNetworkPolicyMutation();
201336
+ const boundMentorIds = (_a = deleteFailure === null || deleteFailure === void 0 ? void 0 : deleteFailure.boundMentorIds) !== null && _a !== void 0 ? _a : [];
201337
+ // Best-effort names for the agents named in a refused delete.
201338
+ const { data: mentorsData } = useGetMentorsQuery({ org: tenantKey, username: username !== null && username !== void 0 ? username : '', limit: MENTOR_LOOKUP_LIMIT }, { skip: !username || boundMentorIds.length === 0 });
201339
+ const mentorNames = useMemo(() => {
201340
+ const names = new Map();
201341
+ const results = mentorsData === null || mentorsData === void 0 ? void 0 : mentorsData.results;
201342
+ for (const mentor of results !== null && results !== void 0 ? results : []) {
201343
+ if (mentor.unique_id && mentor.name)
201344
+ names.set(mentor.unique_id, mentor.name);
201345
+ }
201346
+ return names;
201347
+ }, [mentorsData]);
201348
+ // The translated sentence with the policy name rendered inline, whatever the word order.
201349
+ const [deleteConfirmBefore, deleteConfirmAfter] = t('virtualMachineNetworkPolicies.deleteConfirm', {
201350
+ name: RECORD_SLOT$1,
201351
+ }).split(RECORD_SLOT$1);
201352
+ const openCreate = () => {
201353
+ setEditingPolicy(null);
201354
+ setDialogOpen(true);
201355
+ };
201356
+ const openEdit = (policy) => {
201357
+ setEditingPolicy(policy);
201358
+ setDialogOpen(true);
201359
+ };
201360
+ const openDelete = (policy) => {
201361
+ setDeleteFailure(null);
201362
+ setDeletingPolicy(policy);
201363
+ };
201364
+ const handleDelete = async (policy) => {
201365
+ var _a;
201366
+ try {
201367
+ await deletePolicy({ org: tenantKey, id: policy.id }).unwrap();
201368
+ toast.success(t('virtualMachineNetworkPolicies.deleted'));
201369
+ setDeletingPolicy(null);
201370
+ }
201371
+ catch (error) {
201372
+ const parsed = getVirtualMachineApiError(error);
201373
+ const boundIds = extractBoundMentorIds(parsed.message);
201374
+ setDeleteFailure({
201375
+ message: parsed.status === 403
201376
+ ? t('virtualMachineNetworkPolicies.forbiddenDelete')
201377
+ : boundIds.length > 0
201378
+ ? t('virtualMachineNetworkPolicies.deleteBlockedInUse')
201379
+ : ((_a = parsed.message) !== null && _a !== void 0 ? _a : t('virtualMachineNetworkPolicies.deleteFailed')),
201380
+ boundMentorIds: boundIds,
201381
+ });
201382
+ }
201383
+ };
201384
+ return (jsxs("div", { "data-testid": "vm-network-policies-section", className: "space-y-4", children: [jsx("div", { className: "flex justify-end", children: jsxs(Button$1, { size: "sm", onClick: openCreate, "data-testid": "vm-network-policy-new", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineNetworkPolicies.newPolicy')] }) }), isLoading ? (jsx("div", { className: "flex h-40 items-center justify-center", children: jsx(Spinner, {}) })) : isError ? (jsx("div", { role: "alert", className: "rounded-lg border border-red-200 bg-red-50 px-6 py-6 text-center text-sm text-red-700", children: t('virtualMachineNetworkPolicies.loadFailed') })) : (jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "border-b border-gray-200 bg-gray-50 dark:border-gray-600 dark:bg-gray-700", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.colName') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.colHosts') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.colDescription') }), jsx("th", { className: "px-6 py-3 text-left font-medium whitespace-nowrap text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.colUpdated') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.colActions') })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [policies.map((policy) => {
201385
+ const preview = policy.allowed_hosts.slice(0, HOST_PREVIEW_COUNT$1);
201386
+ const remaining = policy.allowed_hosts.length - preview.length;
201387
+ return (jsxs("tr", { "data-testid": `vm-network-policy-row-${policy.id}`, children: [jsx("td", { className: "px-6 py-4 font-medium text-gray-900 dark:text-gray-100", children: policy.name }), jsx("td", { className: "px-6 py-4", children: jsxs("ul", { role: "list", "aria-label": t('virtualMachineNetworkPolicies.hostsListAriaLabel', {
201388
+ name: policy.name,
201389
+ }), className: "flex flex-wrap gap-1", children: [preview.map((host) => (jsx("li", { className: "rounded-md border border-gray-200 bg-gray-50 px-2 py-0.5 font-mono text-xs text-gray-700", children: host }, host))), remaining > 0 && (jsx("li", { className: "px-1 py-0.5 text-xs text-gray-500", children: t('virtualMachineNetworkPolicies.moreHosts', { count: remaining }) }))] }) }), jsx("td", { className: "max-w-0 px-6 py-4 text-gray-600 dark:text-gray-300", children: jsx("div", { className: "truncate", children: policy.description || '—' }) }), jsx("td", { className: "px-6 py-4 whitespace-nowrap text-gray-600 dark:text-gray-300", children: relativeTime$1(policy.updated_at) }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex justify-end gap-1", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(policy), "aria-label": t('virtualMachineNetworkPolicies.editAriaLabel', {
201390
+ name: policy.name,
201391
+ }), "data-testid": `vm-network-policy-edit-${policy.id}`, children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openDelete(policy), "aria-label": t('virtualMachineNetworkPolicies.deleteAriaLabel', {
201392
+ name: policy.name,
201393
+ }), "data-testid": `vm-network-policy-delete-${policy.id}`, className: "text-red-600 hover:text-red-700", children: jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }) })] }) })] }, policy.id));
201394
+ }), policies.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 5, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('virtualMachineNetworkPolicies.empty') }) }))] })] }) })), jsx(NetworkPolicyDialog, { open: dialogOpen, onOpenChange: setDialogOpen, tenantKey: tenantKey, policy: editingPolicy }), jsx(Dialog, { open: deletingPolicy !== null, onOpenChange: (open) => {
201395
+ if (!open && !isDeleting) {
201396
+ setDeletingPolicy(null);
201397
+ setDeleteFailure(null);
201398
+ }
201399
+ }, children: jsxs(DialogContent, { className: "max-w-md", "data-testid": "vm-network-policy-delete-dialog", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('virtualMachineNetworkPolicies.deleteTitle') }) }), jsxs(DialogDescription, { className: "text-gray-600", children: [deleteConfirmBefore, jsx("span", { className: "font-medium text-gray-900", children: deletingPolicy === null || deletingPolicy === void 0 ? void 0 : deletingPolicy.name }), deleteConfirmAfter] }), deleteFailure && (jsxs("div", { role: "alert", "data-testid": "vm-network-policy-delete-error", className: "space-y-2 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: [jsx("p", { children: deleteFailure.message }), boundMentorIds.length > 0 && (jsx("ul", { role: "list", className: "list-disc space-y-0.5 pl-5", children: boundMentorIds.map((id) => {
201400
+ var _a;
201401
+ return (jsx("li", { children: (_a = mentorNames.get(id)) !== null && _a !== void 0 ? _a : id }, id));
201402
+ }) }))] })), jsxs(DialogFooter, { className: "pt-2", children: [jsx(Button$1, { type: "button", variant: "outline", disabled: isDeleting, onClick: () => {
201403
+ setDeletingPolicy(null);
201404
+ setDeleteFailure(null);
201405
+ }, children: t('virtualMachineNetworkPolicies.cancel') }), boundMentorIds.length === 0 && deletingPolicy && (jsxs(Button$1, { type: "button", onClick: () => void handleDelete(deletingPolicy), disabled: isDeleting, "data-testid": "vm-network-policy-delete-confirm", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isDeleting && jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }), t('virtualMachineNetworkPolicies.delete')] }))] })] }) })] }));
201406
+ }
201407
+
201408
+ function fieldKeys(value) {
201409
+ if (value && typeof value === 'object' && !Array.isArray(value)) {
201410
+ return Object.keys(value);
201411
+ }
201412
+ // Some serialisers hand the masked value back as a JSON string.
201413
+ if (typeof value === 'string') {
201414
+ try {
201415
+ return fieldKeys(JSON.parse(value));
201416
+ }
201417
+ catch (_a) {
201418
+ return [];
201419
+ }
201420
+ }
201421
+ return [];
201422
+ }
201423
+ /**
201424
+ * Normalise the integration-credential list response into picker options.
201425
+ * Tolerates a bare array or a `{ results }` / `{ data }` wrapper, and drops
201426
+ * rows without a numeric id.
201427
+ */
201428
+ function toIntegrationCredentialOptions(data) {
201429
+ var _a, _b;
201430
+ const rows = Array.isArray(data)
201431
+ ? data
201432
+ : data && typeof data === 'object'
201433
+ ? ((_b = (_a = data.results) !== null && _a !== void 0 ? _a : data.data) !== null && _b !== void 0 ? _b : [])
201434
+ : [];
201435
+ const options = [];
201436
+ for (const row of rows) {
201437
+ if (!row || typeof row !== 'object')
201438
+ continue;
201439
+ const { id, name, value } = row;
201440
+ if (typeof id !== 'number')
201441
+ continue;
201442
+ options.push({
201443
+ id,
201444
+ name: typeof name === 'string' && name ? name : String(id),
201445
+ fields: fieldKeys(value),
201446
+ });
201447
+ }
201448
+ return options.sort((a, b) => a.name.localeCompare(b.name));
201449
+ }
201450
+
201451
+ /**
201452
+ * Radio items in the virtual-machine forms use the same blue as the sandbox
201453
+ * switches (`data-[state=checked]:bg-blue-500`) so the selected option reads
201454
+ * as "on" like the toggles around it. The indicator is an inner svg, hence the
201455
+ * descendant variants.
201456
+ */
201457
+ const BLUE_RADIO_ITEM_CLASS = 'mt-0.5 border-gray-300 text-blue-500 data-[state=checked]:border-blue-500 [&_svg]:fill-blue-500 [&_svg]:text-blue-500';
201458
+ /** Checkboxes beside those radios: the same grey border while unchecked, the same blue when checked. */
201459
+ const BLUE_CHECKBOX_CLASS = 'mt-0.5 border-gray-300 data-[state=checked]:border-blue-500';
201460
+
201461
+ const FIELD_NAMES = [
201462
+ 'name',
201463
+ 'env_var',
201464
+ 'allow_hosts',
201465
+ 'value',
201466
+ 'source_credential_id',
201467
+ 'source_field',
201468
+ ];
201469
+ const NAME_MAX_LENGTH = 255;
201470
+ const ENV_VAR_MAX_LENGTH = 128;
201471
+ /** Values that look masked (`sk-****`) are rejected by the backend too. */
201472
+ const MASKED_VALUE_PATTERN = /\*{3,}/;
201473
+ /**
201474
+ * Create / edit form for a VM secret. A secret gets its value in one of two
201475
+ * ways, offered as a choice: the admin pastes a key (stored, min 8
201476
+ * characters), or the secret points at one field of an integration
201477
+ * credential the org already saved and reads it at run time. No endpoint ever
201478
+ * returns the value, so the field is never prefilled — on edit it stays blank
201479
+ * to keep the current value — and `env_var` is read-only once created (delete
201480
+ * and recreate to rename). Backend 400s land next to the matching field or as
201481
+ * a form-level message.
201482
+ */
201483
+ function VmSecretDialog({ open, onOpenChange, tenantKey, secret = null, credentials, onSaved, }) {
201484
+ var _a;
201485
+ const t = useT();
201486
+ const baseId = useId$2();
201487
+ const ids = {
201488
+ name: `${baseId}-name`,
201489
+ envVar: `${baseId}-env-var`,
201490
+ envVarHelp: `${baseId}-env-var-help`,
201491
+ hosts: `${baseId}-hosts`,
201492
+ source: `${baseId}-source`,
201493
+ sourceValue: `${baseId}-source-value`,
201494
+ sourceCredential: `${baseId}-source-credential`,
201495
+ value: `${baseId}-value`,
201496
+ valueHelp: `${baseId}-value-help`,
201497
+ credential: `${baseId}-credential`,
201498
+ credentialLabel: `${baseId}-credential-label`,
201499
+ field: `${baseId}-field`,
201500
+ fieldLabel: `${baseId}-field-label`,
201501
+ };
201502
+ const [createSecret, { isLoading: isCreating }] = useCreateVirtualMachineSecretMutation();
201503
+ const [updateSecret, { isLoading: isUpdating }] = useUpdateVirtualMachineSecretMutation();
201504
+ const isSaving = isCreating || isUpdating;
201505
+ const [name, setName] = useState('');
201506
+ const [envVar, setEnvVar] = useState('');
201507
+ const [hosts, setHosts] = useState([]);
201508
+ const [source, setSource] = useState('value');
201509
+ const [value, setValue] = useState('');
201510
+ const [credentialId, setCredentialId] = useState('');
201511
+ const [field, setField] = useState('');
201512
+ const [fieldErrors, setFieldErrors] = useState({});
201513
+ const [formError, setFormError] = useState(null);
201514
+ // Start from the secret being edited (or blank) every time the dialog
201515
+ // opens. The value is deliberately never prefilled.
201516
+ useEffect(() => {
201517
+ var _a, _b, _c, _d;
201518
+ if (!open)
201519
+ return;
201520
+ setName((_a = secret === null || secret === void 0 ? void 0 : secret.name) !== null && _a !== void 0 ? _a : '');
201521
+ setEnvVar((_b = secret === null || secret === void 0 ? void 0 : secret.env_var) !== null && _b !== void 0 ? _b : '');
201522
+ setHosts((_c = secret === null || secret === void 0 ? void 0 : secret.allow_hosts) !== null && _c !== void 0 ? _c : []);
201523
+ const linked = (secret === null || secret === void 0 ? void 0 : secret.source_credential_id) != null;
201524
+ setSource(linked ? 'credential' : 'value');
201525
+ setValue('');
201526
+ setCredentialId(linked ? String(secret.source_credential_id) : '');
201527
+ setField(linked ? ((_d = secret.source_field) !== null && _d !== void 0 ? _d : '') : '');
201528
+ setFieldErrors({});
201529
+ setFormError(null);
201530
+ }, [open, secret]);
201531
+ const isEdit = secret !== null;
201532
+ const selectedCredential = credentials.find((option) => String(option.id) === credentialId);
201533
+ const credentialFields = (_a = selectedCredential === null || selectedCredential === void 0 ? void 0 : selectedCredential.fields) !== null && _a !== void 0 ? _a : [];
201534
+ const handleSubmit = async (event) => {
201535
+ event.preventDefault();
201536
+ const trimmedName = name.trim();
201537
+ const trimmedEnvVar = envVar.trim();
201538
+ const clientErrors = {};
201539
+ if (!trimmedName)
201540
+ clientErrors.name = t('virtualMachineSecrets.errorNameRequired');
201541
+ if (!isEdit) {
201542
+ if (!trimmedEnvVar) {
201543
+ clientErrors.env_var = t('virtualMachineSecrets.errorEnvVarRequired');
201544
+ }
201545
+ else if (!VIRTUAL_MACHINE_ENV_VAR_PATTERN.test(trimmedEnvVar)) {
201546
+ clientErrors.env_var = t('virtualMachineSecrets.errorEnvVarFormat');
201547
+ }
201548
+ }
201549
+ if (hosts.length === 0)
201550
+ clientErrors.allow_hosts = t('virtualMachineSecrets.errorHostsRequired');
201551
+ if (source === 'value') {
201552
+ if (!isEdit && !value) {
201553
+ clientErrors.value = t('virtualMachineSecrets.errorValueRequired');
201554
+ }
201555
+ else if (value && value.length < VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH) {
201556
+ clientErrors.value = t('virtualMachineSecrets.errorValueTooShort', {
201557
+ min: VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH,
201558
+ });
201559
+ }
201560
+ else if (value && MASKED_VALUE_PATTERN.test(value)) {
201561
+ clientErrors.value = t('virtualMachineSecrets.errorValueMasked');
201562
+ }
201563
+ }
201564
+ else {
201565
+ if (!credentialId) {
201566
+ clientErrors.source_credential_id = t('virtualMachineSecrets.errorCredentialRequired');
201567
+ }
201568
+ if (!field)
201569
+ clientErrors.source_field = t('virtualMachineSecrets.errorFieldRequired');
201570
+ }
201571
+ setFieldErrors(clientErrors);
201572
+ setFormError(null);
201573
+ if (Object.keys(clientErrors).length > 0)
201574
+ return;
201575
+ try {
201576
+ let saved;
201577
+ if (isEdit) {
201578
+ const body = { env_var: secret.env_var };
201579
+ if (trimmedName !== secret.name)
201580
+ body.name = trimmedName;
201581
+ if (hosts.length !== secret.allow_hosts.length ||
201582
+ hosts.some((host, index) => host !== secret.allow_hosts[index])) {
201583
+ body.allow_hosts = hosts;
201584
+ }
201585
+ if (source === 'value') {
201586
+ // A blank value keeps the stored one; a new value replaces it (and
201587
+ // drops any credential link on the backend).
201588
+ if (value)
201589
+ body.value = value;
201590
+ }
201591
+ else if (Number(credentialId) !== secret.source_credential_id ||
201592
+ field !== secret.source_field) {
201593
+ body.source_credential_id = Number(credentialId);
201594
+ body.source_field = field;
201595
+ }
201596
+ if (Object.keys(body).length === 1) {
201597
+ onOpenChange(false);
201598
+ return;
201599
+ }
201600
+ saved = await updateSecret({ org: tenantKey, body }).unwrap();
201601
+ toast.success(t('virtualMachineSecrets.updated'));
201602
+ }
201603
+ else {
201604
+ const body = {
201605
+ name: trimmedName,
201606
+ env_var: trimmedEnvVar,
201607
+ allow_hosts: hosts,
201608
+ ...(source === 'value'
201609
+ ? { value }
201610
+ : { source_credential_id: Number(credentialId), source_field: field }),
201611
+ };
201612
+ saved = await createSecret({ org: tenantKey, body }).unwrap();
201613
+ toast.success(t('virtualMachineSecrets.created'));
201614
+ }
201615
+ onSaved === null || onSaved === void 0 ? void 0 : onSaved(saved);
201616
+ onOpenChange(false);
201617
+ }
201618
+ catch (error) {
201619
+ const parsed = getVirtualMachineApiError(error);
201620
+ if (parsed.status === 403) {
201621
+ setFormError(t('virtualMachineSecrets.forbiddenWrite'));
201622
+ return;
201623
+ }
201624
+ const nextErrors = {};
201625
+ let unmatched = [];
201626
+ for (const [key, messages] of Object.entries(parsed.fieldErrors)) {
201627
+ if (FIELD_NAMES.includes(key)) {
201628
+ nextErrors[key] = messages.join(' ');
201629
+ }
201630
+ else {
201631
+ unmatched = unmatched.concat(messages);
201632
+ }
201633
+ }
201634
+ setFieldErrors(nextErrors);
201635
+ const message = [parsed.message, ...unmatched].filter(Boolean).join(' ');
201636
+ if (message) {
201637
+ setFormError(message);
201638
+ }
201639
+ else if (Object.keys(nextErrors).length === 0) {
201640
+ setFormError(t('virtualMachineSecrets.saveFailed'));
201641
+ }
201642
+ }
201643
+ };
201644
+ const fieldError = (key, id) => fieldErrors[key] ? (jsx("p", { id: `${id}-error`, role: "alert", className: "text-xs text-red-600", children: fieldErrors[key] })) : null;
201645
+ return (jsx(Dialog, { open: open, onOpenChange: (next) => !isSaving && onOpenChange(next), children: jsxs(DialogContent, { className: "max-h-[90vh] w-full max-w-lg overflow-y-auto", "data-testid": "vm-secret-dialog", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { className: "ibl-dialog-title", children: isEdit ? t('virtualMachineSecrets.editTitle') : t('virtualMachineSecrets.createTitle') }), jsx(DialogDescription, { children: t('virtualMachineSecrets.dialogDescription') })] }), jsxs("form", { onSubmit: handleSubmit, noValidate: true, className: "mt-1 grid gap-5", children: [formError && (jsx("div", { role: "alert", "data-testid": "vm-secret-form-error", className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: formError })), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: ids.name, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldName') }), jsx(Input, { id: ids.name, value: name, maxLength: NAME_MAX_LENGTH, onChange: (event) => setName(event.target.value), placeholder: t('virtualMachineSecrets.namePlaceholder'), "aria-invalid": fieldErrors.name ? true : undefined, "aria-describedby": fieldErrors.name ? `${ids.name}-error` : undefined, required: true }), fieldError('name', ids.name)] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: ids.envVar, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldEnvVar') }), jsx(Input, { id: ids.envVar, value: envVar, maxLength: ENV_VAR_MAX_LENGTH, readOnly: isEdit, disabled: isEdit, onChange: (event) => setEnvVar(event.target.value), placeholder: t('virtualMachineSecrets.envVarPlaceholder'), autoCapitalize: "characters", autoComplete: "off", spellCheck: false, className: "font-mono", "aria-invalid": fieldErrors.env_var ? true : undefined, "aria-describedby": [ids.envVarHelp, fieldErrors.env_var ? `${ids.envVar}-error` : '']
201646
+ .filter(Boolean)
201647
+ .join(' '), required: !isEdit }), jsx("p", { id: ids.envVarHelp, className: "text-xs text-gray-500", children: isEdit
201648
+ ? t('virtualMachineSecrets.envVarReadOnlyHelp')
201649
+ : t('virtualMachineSecrets.envVarHelp') }), fieldError('env_var', ids.envVar)] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: ids.hosts, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldHosts') }), jsx(HostPortChipInput, { id: ids.hosts, value: hosts, onChange: setHosts, hint: t('virtualMachineSecrets.hostsHint'), error: fieldErrors.allow_hosts, testId: "vm-secret-hosts" })] }), jsxs("fieldset", { className: "space-y-3", children: [jsx("legend", { id: ids.source, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldSource') }), jsxs(RadioGroup, { value: source, onValueChange: (next) => setSource(next), "aria-labelledby": ids.source, className: "gap-3", children: [jsxs("div", { className: "flex items-start gap-3", children: [jsx(RadioGroupItem, { value: "value", id: ids.sourceValue, "data-testid": "vm-secret-source-value", className: BLUE_RADIO_ITEM_CLASS }), jsxs(Label$2, { htmlFor: ids.sourceValue, className: "flex flex-col gap-1 font-normal", children: [jsx("span", { className: "text-[13px] font-medium text-[#646464]", children: t('virtualMachineSecrets.sourceValue') }), jsx("span", { className: "text-xs text-gray-500", children: t('virtualMachineSecrets.sourceValueDescription') })] })] }), jsxs("div", { className: "flex items-start gap-3", children: [jsx(RadioGroupItem, { value: "credential", id: ids.sourceCredential, "data-testid": "vm-secret-source-credential", className: BLUE_RADIO_ITEM_CLASS }), jsxs(Label$2, { htmlFor: ids.sourceCredential, className: "flex flex-col gap-1 font-normal", children: [jsx("span", { className: "text-[13px] font-medium text-[#646464]", children: t('virtualMachineSecrets.sourceCredential') }), jsx("span", { className: "text-xs text-gray-500", children: t('virtualMachineSecrets.sourceCredentialDescription') })] })] })] })] }), source === 'value' ? (jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { htmlFor: ids.value, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldValue') }), jsx(Input, { id: ids.value, type: "password", value: value, onChange: (event) => setValue(event.target.value), placeholder: isEdit
201650
+ ? t('virtualMachineSecrets.valueKeepPlaceholder')
201651
+ : t('virtualMachineSecrets.valuePlaceholder'), autoComplete: "new-password", spellCheck: false, "aria-invalid": fieldErrors.value ? true : undefined, "aria-describedby": [ids.valueHelp, fieldErrors.value ? `${ids.value}-error` : '']
201652
+ .filter(Boolean)
201653
+ .join(' '), required: !isEdit, "data-testid": "vm-secret-value" }), jsx("p", { id: ids.valueHelp, className: "text-xs text-gray-500", children: isEdit
201654
+ ? t('virtualMachineSecrets.valueKeepHelp')
201655
+ : t('virtualMachineSecrets.valueHelp', {
201656
+ min: VIRTUAL_MACHINE_SECRET_VALUE_MIN_LENGTH,
201657
+ }) }), fieldError('value', ids.value)] })) : (jsxs("div", { className: "space-y-4", children: [jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { id: ids.credentialLabel, htmlFor: ids.credential, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldCredential') }), credentials.length === 0 ? (jsx("p", { className: "text-xs text-gray-500", "data-testid": "vm-secret-no-credentials", children: t('virtualMachineSecrets.noCredentials') })) : (jsxs(Select$1, { value: credentialId, onValueChange: (next) => {
201658
+ setCredentialId(next);
201659
+ setField('');
201660
+ }, children: [jsx(SelectTrigger, { id: ids.credential, "aria-labelledby": ids.credentialLabel, "aria-invalid": fieldErrors.source_credential_id ? true : undefined, "data-testid": "vm-secret-credential-select", children: jsx(SelectValue, { placeholder: t('virtualMachineSecrets.credentialPlaceholder') }) }), jsx(SelectContent, { children: credentials.map((option) => (jsx(SelectItem, { value: String(option.id), children: option.name }, option.id))) })] })), fieldError('source_credential_id', ids.credential)] }), jsxs("div", { className: "space-y-1.5", children: [jsx(Label$2, { id: ids.fieldLabel, htmlFor: ids.field, className: "flex items-center text-sm font-medium text-[#646464]", children: t('virtualMachineSecrets.fieldCredentialField') }), jsxs(Select$1, { value: field, onValueChange: setField, disabled: !selectedCredential || credentialFields.length === 0, children: [jsx(SelectTrigger, { id: ids.field, "aria-labelledby": ids.fieldLabel, "aria-invalid": fieldErrors.source_field ? true : undefined, "data-testid": "vm-secret-field-select", children: jsx(SelectValue, { placeholder: t('virtualMachineSecrets.credentialFieldPlaceholder') }) }), jsx(SelectContent, { children: credentialFields.map((key) => (jsx(SelectItem, { value: key, children: key }, key))) })] }), selectedCredential && credentialFields.length === 0 && (jsx("p", { className: "text-xs text-gray-500", children: t('virtualMachineSecrets.credentialNoFields') })), fieldError('source_field', ids.field)] })] })), jsxs(DialogFooter, { children: [jsx(Button$1, { type: "button", variant: "outline", onClick: () => onOpenChange(false), disabled: isSaving, children: t('virtualMachineSecrets.cancel') }), jsxs(Button$1, { type: "submit", disabled: isSaving, "data-testid": "vm-secret-save", className: "bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isSaving && jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }), isEdit
201661
+ ? t('virtualMachineSecrets.saveChanges')
201662
+ : t('virtualMachineSecrets.createSecret')] })] })] })] }) }));
201663
+ }
201664
+
201665
+ const HOST_PREVIEW_COUNT = 3;
201666
+ /** Placeholder swapped for the inline variable in the delete sentence. */
201667
+ const RECORD_SLOT = '__record__';
201668
+ /** "3 days ago" for a backend timestamp, or a dash when it is unusable. */
201669
+ function relativeTime(value) {
201670
+ const date = new Date(value);
201671
+ return Number.isNaN(date.getTime()) ? '—' : formatDistanceToNow(date, { addSuffix: true });
201672
+ }
201673
+ /**
201674
+ * VM secrets table with create / edit / delete. A value is never displayed
201675
+ * — the Source column only says whether the secret holds a stored value or
201676
+ * follows an integration credential's field. Deleting a secret also unbinds
201677
+ * it from every agent that uses it, which the confirmation spells out.
201678
+ */
201679
+ function VmSecretsSection({ tenantKey, secrets, isLoading, isError, }) {
201680
+ const t = useT();
201681
+ const [dialogOpen, setDialogOpen] = useState(false);
201682
+ const [editingSecret, setEditingSecret] = useState(null);
201683
+ const [deletingSecret, setDeletingSecret] = useState(null);
201684
+ const [deleteError, setDeleteError] = useState(null);
201685
+ const [deleteSecret, { isLoading: isDeleting }] = useDeleteVirtualMachineSecretMutation();
201686
+ // The org's integration credentials feed the "use an existing credential"
201687
+ // source in the form and name the credential in the Source column.
201688
+ const { data: credentialsData } = useGetIntegrationCredentialsQuery({ org: tenantKey });
201689
+ const credentials = useMemo(() => toIntegrationCredentialOptions(credentialsData), [credentialsData]);
201690
+ const credentialNames = useMemo(() => new Map(credentials.map((option) => [option.id, option.name])), [credentials]);
201691
+ const sourceLabel = (secret) => {
201692
+ var _a;
201693
+ if (secret.source_credential_id == null)
201694
+ return t('virtualMachineSecrets.sourceStored');
201695
+ const credentialName = (_a = credentialNames.get(secret.source_credential_id)) !== null && _a !== void 0 ? _a : `#${secret.source_credential_id}`;
201696
+ return t('virtualMachineSecrets.sourceFromCredential', {
201697
+ credential: credentialName,
201698
+ field: secret.source_field,
201699
+ });
201700
+ };
201701
+ // The translated sentence with the variable rendered as the table's code chip.
201702
+ const [deleteConfirmBefore, deleteConfirmAfter] = t('virtualMachineSecrets.deleteConfirm', {
201703
+ envVar: RECORD_SLOT,
201704
+ }).split(RECORD_SLOT);
201705
+ const openCreate = () => {
201706
+ setEditingSecret(null);
201707
+ setDialogOpen(true);
201708
+ };
201709
+ const openEdit = (secret) => {
201710
+ setEditingSecret(secret);
201711
+ setDialogOpen(true);
201712
+ };
201713
+ const openDelete = (secret) => {
201714
+ setDeleteError(null);
201715
+ setDeletingSecret(secret);
201716
+ };
201717
+ const handleDelete = async (secret) => {
201718
+ var _a;
201719
+ try {
201720
+ await deleteSecret({ org: tenantKey, envVar: secret.env_var }).unwrap();
201721
+ toast.success(t('virtualMachineSecrets.deleted'));
201722
+ setDeletingSecret(null);
201723
+ }
201724
+ catch (error) {
201725
+ const parsed = getVirtualMachineApiError(error);
201726
+ setDeleteError(parsed.status === 403
201727
+ ? t('virtualMachineSecrets.forbiddenDelete')
201728
+ : ((_a = parsed.message) !== null && _a !== void 0 ? _a : t('virtualMachineSecrets.deleteFailed')));
201729
+ }
201730
+ };
201731
+ return (jsxs("div", { "data-testid": "vm-secrets-section", className: "space-y-4", children: [jsx("div", { className: "flex justify-end", children: jsxs(Button$1, { size: "sm", onClick: openCreate, "data-testid": "vm-secret-new", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineSecrets.newSecret')] }) }), isLoading ? (jsx("div", { className: "flex h-40 items-center justify-center", children: jsx(Spinner, {}) })) : isError ? (jsx("div", { role: "alert", className: "rounded-lg border border-red-200 bg-red-50 px-6 py-6 text-center text-sm text-red-700", children: t('virtualMachineSecrets.loadFailed') })) : (jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-white dark:border-gray-700 dark:bg-gray-800", children: jsxs("table", { className: "w-full text-sm", children: [jsx("thead", { className: "border-b border-gray-200 bg-gray-50 dark:border-gray-600 dark:bg-gray-700", children: jsxs("tr", { children: [jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colName') }), jsx("th", { className: "px-6 py-3 text-left font-medium whitespace-nowrap text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colEnvVar') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colHosts') }), jsx("th", { className: "px-6 py-3 text-left font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colSource') }), jsx("th", { className: "px-6 py-3 text-left font-medium whitespace-nowrap text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colUpdated') }), jsx("th", { className: "px-6 py-3 text-right font-medium text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.colActions') })] }) }), jsxs("tbody", { className: "divide-y divide-gray-200 dark:divide-gray-600", children: [secrets.map((secret) => {
201732
+ const preview = secret.allow_hosts.slice(0, HOST_PREVIEW_COUNT);
201733
+ const remaining = secret.allow_hosts.length - preview.length;
201734
+ return (jsxs("tr", { "data-testid": `vm-secret-row-${secret.env_var}`, children: [jsx("td", { className: "px-6 py-4 font-medium text-gray-900 dark:text-gray-100", children: secret.name }), jsx("td", { className: "px-6 py-4", children: jsx("code", { className: "rounded bg-gray-100 px-1.5 py-0.5 font-mono text-xs text-gray-800", children: secret.env_var }) }), jsx("td", { className: "px-6 py-4", children: jsxs("ul", { role: "list", "aria-label": t('virtualMachineSecrets.hostsListAriaLabel', {
201735
+ name: secret.name,
201736
+ }), className: "flex flex-wrap gap-1", children: [preview.map((host) => (jsx("li", { className: "rounded-md border border-gray-200 bg-gray-50 px-2 py-0.5 font-mono text-xs text-gray-700", children: host }, host))), remaining > 0 && (jsx("li", { className: "px-1 py-0.5 text-xs text-gray-500", children: t('virtualMachineSecrets.moreHosts', { count: remaining }) }))] }) }), jsx("td", { className: "px-6 py-4 text-gray-600 dark:text-gray-300", children: sourceLabel(secret) }), jsx("td", { className: "px-6 py-4 whitespace-nowrap text-gray-600 dark:text-gray-300", children: relativeTime(secret.updated_at) }), jsx("td", { className: "px-6 py-4", children: jsxs("div", { className: "flex justify-end gap-1", children: [jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openEdit(secret), "aria-label": t('virtualMachineSecrets.editAriaLabel', {
201737
+ name: secret.name,
201738
+ }), "data-testid": `vm-secret-edit-${secret.env_var}`, children: jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }) }), jsx(Button$1, { variant: "ghost", size: "sm", onClick: () => openDelete(secret), "aria-label": t('virtualMachineSecrets.deleteAriaLabel', {
201739
+ name: secret.name,
201740
+ }), "data-testid": `vm-secret-delete-${secret.env_var}`, className: "text-red-600 hover:text-red-700", children: jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }) })] }) })] }, secret.id));
201741
+ }), secrets.length === 0 && (jsx("tr", { children: jsx("td", { colSpan: 6, className: "px-6 py-8 text-center text-gray-500 dark:text-gray-400", children: t('virtualMachineSecrets.empty') }) }))] })] }) })), jsx(VmSecretDialog, { open: dialogOpen, onOpenChange: setDialogOpen, tenantKey: tenantKey, secret: editingSecret, credentials: credentials }), jsx(Dialog, { open: deletingSecret !== null, onOpenChange: (open) => {
201742
+ if (!open && !isDeleting) {
201743
+ setDeletingSecret(null);
201744
+ setDeleteError(null);
201745
+ }
201746
+ }, children: jsxs(DialogContent, { className: "max-w-md", "data-testid": "vm-secret-delete-dialog", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('virtualMachineSecrets.deleteTitle') }) }), jsxs(DialogDescription, { className: "text-gray-600", children: [deleteConfirmBefore, jsx("code", { className: "rounded bg-gray-100 px-1.5 py-0.5 font-mono text-xs text-gray-800", children: deletingSecret === null || deletingSecret === void 0 ? void 0 : deletingSecret.env_var }), deleteConfirmAfter] }), deleteError && (jsx("div", { role: "alert", "data-testid": "vm-secret-delete-error", className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: deleteError })), jsxs(DialogFooter, { className: "pt-2", children: [jsx(Button$1, { type: "button", variant: "outline", disabled: isDeleting, onClick: () => {
201747
+ setDeletingSecret(null);
201748
+ setDeleteError(null);
201749
+ }, children: t('virtualMachineSecrets.cancel') }), jsxs(Button$1, { type: "button", onClick: () => {
201750
+ if (deletingSecret)
201751
+ void handleDelete(deletingSecret);
201752
+ }, disabled: isDeleting, "data-testid": "vm-secret-delete-confirm", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isDeleting && jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }), t('virtualMachineSecrets.delete')] })] })] }) })] }));
201753
+ }
201754
+
201755
+ /**
201756
+ * Tenant-settings **Virtual Machine** tab. Org admins manage the two records
201757
+ * that decide what an agent's Virtual Machine Shell can reach: reusable
201758
+ * `host:port` network policies (bound under the `custom` egress profile) and
201759
+ * VM secrets the VM can use but never read. Each section is hidden when its
201760
+ * list call answers 403; when both do, a single permission notice remains.
201761
+ */
201762
+ function VirtualMachineAdminTab({ tenantKey, username, defaultTab = 'network-policies', }) {
201763
+ var _a, _b;
201764
+ const t = useT();
201765
+ const policiesQuery = useListVirtualMachineNetworkPoliciesQuery({ org: tenantKey });
201766
+ const secretsQuery = useListVirtualMachineSecretsQuery({ org: tenantKey });
201767
+ const policiesForbidden = getVirtualMachineErrorStatus(policiesQuery.error) === 403;
201768
+ const secretsForbidden = getVirtualMachineErrorStatus(secretsQuery.error) === 403;
201769
+ if (policiesQuery.isLoading && secretsQuery.isLoading) {
201770
+ return (jsxs("div", { className: "space-y-6", "data-testid": "vm-admin-loading", children: [jsx(Skeleton, { className: "h-10 w-full max-w-sm rounded-lg" }), jsx(Skeleton, { className: "h-40 w-full rounded-lg" })] }));
201771
+ }
201772
+ if (policiesForbidden && secretsForbidden) {
201773
+ return (jsx("div", { className: "max-w-2xl", children: jsx("div", { className: "rounded-lg border px-6 py-8 text-center", style: { borderColor: 'oklch(.922 0 0)' }, "data-testid": "vm-admin-forbidden", children: jsx("p", { className: "text-sm text-gray-500", children: t('virtualMachineAdminTab.forbidden') }) }) }));
201774
+ }
201775
+ const initialTab = (defaultTab === 'secrets' && !secretsForbidden) || policiesForbidden
201776
+ ? 'secrets'
201777
+ : 'network-policies';
201778
+ return (jsxs("div", { className: "space-y-4", children: [jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600", "data-testid": "vm-admin-info-box", children: t('virtualMachineAdminTab.infoBox') }), jsxs(Tabs, { defaultValue: initialTab, "aria-label": t('virtualMachineAdminTab.tabsAriaLabel'), children: [jsxs(TabsList, { "aria-label": t('virtualMachineAdminTab.tabsAriaLabel'), className: "max-w-full justify-start overflow-x-auto px-1", children: [!policiesForbidden && (jsx(TabsTrigger, { "data-testid": "vm-admin-sub-tab-policies", value: "network-policies", children: t('virtualMachineAdminTab.tabNetworkPolicies') })), !secretsForbidden && (jsx(TabsTrigger, { "data-testid": "vm-admin-sub-tab-secrets", value: "secrets", children: t('virtualMachineAdminTab.tabSecrets') }))] }), !policiesForbidden && (jsx(TabsContent, { className: "mt-4", value: "network-policies", children: jsx(NetworkPoliciesSection, { tenantKey: tenantKey, username: username, policies: (_a = policiesQuery.data) !== null && _a !== void 0 ? _a : [], isLoading: policiesQuery.isLoading, isError: policiesQuery.isError }) })), !secretsForbidden && (jsx(TabsContent, { className: "mt-4", value: "secrets", children: jsx(VmSecretsSection, { tenantKey: tenantKey, secrets: (_b = secretsQuery.data) !== null && _b !== void 0 ? _b : [], isLoading: secretsQuery.isLoading, isError: secretsQuery.isError }) }))] })] }));
201779
+ }
201780
+
200301
201781
  // ::- Persistent data structure representing an ordered mapping from
200302
201782
  // strings to values, with some convenient update methods.
200303
201783
  function OrderedMap(content) {
@@ -240453,6 +241933,264 @@ function LLMProviderModal({ isOpen, onClose, onSelect, llmProvider, isSelecting,
240453
241933
  }) })] }));
240454
241934
  }
240455
241935
 
241936
+ /** Copy for each egress profile, in the order the backend lists them. */
241937
+ const EGRESS_COPY = {
241938
+ none: {
241939
+ label: 'virtualMachineNetworkSection.egressNoneLabel',
241940
+ description: 'virtualMachineNetworkSection.egressNoneDescription',
241941
+ },
241942
+ registries: {
241943
+ label: 'virtualMachineNetworkSection.egressRegistriesLabel',
241944
+ description: 'virtualMachineNetworkSection.egressRegistriesDescription',
241945
+ },
241946
+ public: {
241947
+ label: 'virtualMachineNetworkSection.egressPublicLabel',
241948
+ description: 'virtualMachineNetworkSection.egressPublicDescription',
241949
+ },
241950
+ custom: {
241951
+ label: 'virtualMachineNetworkSection.egressCustomLabel',
241952
+ description: 'virtualMachineNetworkSection.egressCustomDescription',
241953
+ },
241954
+ };
241955
+ const sameIdSet = (a, b) => a.length === b.length && a.every((id) => b.includes(id));
241956
+ /**
241957
+ * The Virtual Machine Shell network settings of an agent: the egress profile
241958
+ * (a labelled radio group), the network policy picker (only under Custom,
241959
+ * required, with a "Create policy" shortcut) and the secrets multi-select
241960
+ * (only under Public or Custom, capped at 20), plus the billing notice.
241961
+ *
241962
+ * Mirrors the backend's rules before saving: Custom needs a policy; secrets
241963
+ * need a networked profile, so narrowing to None or Registries asks to
241964
+ * confirm and unbinds them in the same request; under Custom every host a
241965
+ * bound secret needs must be in the policy, and the missing ones can be
241966
+ * added to the policy (PATCH) right before the save. Backend 400s land next
241967
+ * to the matching control or as a form-level message. The secrets control is
241968
+ * hidden when the secrets list answers 403.
241969
+ */
241970
+ function VirtualMachineNetworkSection({ platformKey, mentorUniqueId, username, }) {
241971
+ var _a, _b, _c, _d;
241972
+ const t = useT();
241973
+ const baseId = useId$2();
241974
+ const ids = {
241975
+ egressLabel: `${baseId}-egress-label`,
241976
+ egressHelp: `${baseId}-egress-help`,
241977
+ policyLabel: `${baseId}-policy-label`,
241978
+ policySelect: `${baseId}-policy-select`,
241979
+ secretsLabel: `${baseId}-secrets-label`,
241980
+ secretsHelp: `${baseId}-secrets-help`,
241981
+ };
241982
+ const { data: settingsData, isLoading: isLoadingSettings } = useGetMentorSettingsQuery({ mentor: mentorUniqueId, org: platformKey }, { skip: !platformKey || !mentorUniqueId });
241983
+ const [editMentor, { isLoading: isSaving }] = useEditMentorMutation();
241984
+ const [updatePolicy, { isLoading: isUpdatingPolicy }] = useUpdateVirtualMachineNetworkPolicyMutation();
241985
+ const policiesQuery = useListVirtualMachineNetworkPoliciesQuery({ org: platformKey });
241986
+ const secretsQuery = useListVirtualMachineSecretsQuery({ org: platformKey });
241987
+ const policiesForbidden = getVirtualMachineErrorStatus(policiesQuery.error) === 403;
241988
+ const secretsForbidden = getVirtualMachineErrorStatus(secretsQuery.error) === 403;
241989
+ // What the server holds. The installed MentorSettings type predates these
241990
+ // fields, so the response is read through the typed slice.
241991
+ const settings = settingsData;
241992
+ const server = useMemo(() => {
241993
+ var _a, _b, _c;
241994
+ return ({
241995
+ egress: ((_a = settings === null || settings === void 0 ? void 0 : settings.virtual_machine_egress) !== null && _a !== void 0 ? _a : 'none'),
241996
+ policy: (_b = settings === null || settings === void 0 ? void 0 : settings.virtual_machine_network_policy) !== null && _b !== void 0 ? _b : null,
241997
+ secrets: (_c = settings === null || settings === void 0 ? void 0 : settings.virtual_machine_secrets) !== null && _c !== void 0 ? _c : [],
241998
+ });
241999
+ }, [settings]);
242000
+ const serverPolicyId = (_b = (_a = server.policy) === null || _a === void 0 ? void 0 : _a.id) !== null && _b !== void 0 ? _b : null;
242001
+ const serverSecretIds = useMemo(() => server.secrets.map((secret) => secret.id), [server]);
242002
+ const [egress, setEgress] = useState(server.egress);
242003
+ const [policyId, setPolicyId] = useState(serverPolicyId);
242004
+ const [secretIds, setSecretIds] = useState(serverSecretIds);
242005
+ const [fieldErrors, setFieldErrors] = useState({});
242006
+ const [formError, setFormError] = useState(null);
242007
+ const [policyDialogOpen, setPolicyDialogOpen] = useState(false);
242008
+ // `null` creates a new policy; a policy edits it in place.
242009
+ const [policyDialogPolicy, setPolicyDialogPolicy] = useState(null);
242010
+ const [confirmNarrowOpen, setConfirmNarrowOpen] = useState(false);
242011
+ // The org's policies + secrets admin UI, opened in place so the admin never
242012
+ // has to leave the agent settings to create what they need here.
242013
+ const [manageTab, setManageTab] = useState(null);
242014
+ const resetDraft = () => {
242015
+ setEgress(server.egress);
242016
+ setPolicyId(serverPolicyId);
242017
+ setSecretIds(serverSecretIds);
242018
+ setFieldErrors({});
242019
+ setFormError(null);
242020
+ };
242021
+ // Re-seed the draft whenever the saved settings change (after a save, or
242022
+ // when another admin's change is refetched).
242023
+ // eslint-disable-next-line react-hooks/exhaustive-deps
242024
+ useEffect(resetDraft, [server, serverPolicyId, serverSecretIds]);
242025
+ // The bound policy / secrets may be missing from the org lists (the list
242026
+ // may be forbidden, or the settings refetch may lag) — fold the refs the
242027
+ // settings carry into the options so the selection still renders.
242028
+ const policyOptions = useMemo(() => {
242029
+ var _a;
242030
+ const policies = (_a = policiesQuery.data) !== null && _a !== void 0 ? _a : [];
242031
+ if (server.policy && !policies.some((policy) => { var _a; return policy.id === ((_a = server.policy) === null || _a === void 0 ? void 0 : _a.id); })) {
242032
+ return [...policies, { ...server.policy, description: '', created_at: '', updated_at: '' }];
242033
+ }
242034
+ return policies;
242035
+ }, [policiesQuery.data, server.policy]);
242036
+ const secretOptions = useMemo(() => {
242037
+ var _a;
242038
+ const known = new Map();
242039
+ for (const secret of (_a = secretsQuery.data) !== null && _a !== void 0 ? _a : [])
242040
+ known.set(secret.id, secret);
242041
+ for (const ref of server.secrets) {
242042
+ if (!known.has(ref.id)) {
242043
+ known.set(ref.id, { ...ref, source_field: '', created_at: '', updated_at: '' });
242044
+ }
242045
+ }
242046
+ return [...known.values()].sort((a, b) => a.env_var.localeCompare(b.env_var));
242047
+ }, [secretsQuery.data, server.secrets]);
242048
+ const selectedPolicy = (_c = policyOptions.find((policy) => policy.id === policyId)) !== null && _c !== void 0 ? _c : null;
242049
+ const selectedSecrets = secretOptions.filter((secret) => secretIds.includes(secret.id));
242050
+ const isNetworked = isVirtualMachineEgressNetworked(egress);
242051
+ const uncovered = egress === 'custom' && selectedPolicy
242052
+ ? findUncoveredSecretHosts(selectedSecrets, selectedPolicy.allowed_hosts)
242053
+ : [];
242054
+ /** Only the keys that differ from the server, as the PUT partial update expects. */
242055
+ const buildPayload = () => {
242056
+ const payload = {};
242057
+ if (egress !== server.egress)
242058
+ payload.virtual_machine_egress = egress;
242059
+ if (egress === 'custom') {
242060
+ if (policyId !== serverPolicyId || egress !== server.egress) {
242061
+ payload.virtual_machine_network_policy_id = policyId;
242062
+ }
242063
+ }
242064
+ else if (server.egress === 'custom' && serverPolicyId !== null) {
242065
+ payload.virtual_machine_network_policy_id = null;
242066
+ }
242067
+ if (isNetworked) {
242068
+ if (!sameIdSet(secretIds, serverSecretIds))
242069
+ payload.virtual_machine_secret_ids = secretIds;
242070
+ }
242071
+ else if (serverSecretIds.length > 0) {
242072
+ payload.virtual_machine_secret_ids = [];
242073
+ }
242074
+ return payload;
242075
+ };
242076
+ const isDirty = Object.keys(buildPayload()).length > 0;
242077
+ const policyMissing = egress === 'custom' && policyId === null;
242078
+ const isValid = !policyMissing && uncovered.length === 0;
242079
+ const isBusy = isSaving || isUpdatingPolicy;
242080
+ const persist = async (payload) => {
242081
+ setFieldErrors({});
242082
+ setFormError(null);
242083
+ try {
242084
+ await editMentor({
242085
+ mentor: mentorUniqueId,
242086
+ org: platformKey,
242087
+ // @ts-expect-error userId is accepted at runtime (the pinned client's type omits it)
242088
+ userId: username,
242089
+ // The installed MentorSettingsRequest type predates the network fields.
242090
+ formData: payload,
242091
+ }).unwrap();
242092
+ toast.success(t('virtualMachineNetworkSection.saved'));
242093
+ }
242094
+ catch (error) {
242095
+ const parsed = getVirtualMachineApiError(error);
242096
+ if (parsed.status === 403) {
242097
+ setFormError(t('virtualMachineNetworkSection.forbiddenSave'));
242098
+ return;
242099
+ }
242100
+ const nextErrors = {};
242101
+ let unmatched = [];
242102
+ for (const [field, messages] of Object.entries(parsed.fieldErrors)) {
242103
+ const joined = messages.join(' ');
242104
+ if (field === 'virtual_machine_egress')
242105
+ nextErrors.egress = joined;
242106
+ else if (field === 'virtual_machine_network_policy_id')
242107
+ nextErrors.policy = joined;
242108
+ else if (field === 'virtual_machine_secret_ids')
242109
+ nextErrors.secrets = joined;
242110
+ else
242111
+ unmatched = unmatched.concat(messages);
242112
+ }
242113
+ setFieldErrors(nextErrors);
242114
+ const message = [parsed.message, ...unmatched].filter(Boolean).join(' ');
242115
+ if (message)
242116
+ setFormError(message);
242117
+ else if (Object.keys(nextErrors).length === 0) {
242118
+ setFormError(t('virtualMachineNetworkSection.saveFailed'));
242119
+ }
242120
+ }
242121
+ };
242122
+ const handleSave = () => {
242123
+ // Narrowing the profile while secrets are bound: confirm, then unbind
242124
+ // them in the same request (the backend refuses otherwise).
242125
+ if (!isNetworked && serverSecretIds.length > 0) {
242126
+ setConfirmNarrowOpen(true);
242127
+ return;
242128
+ }
242129
+ void persist(buildPayload());
242130
+ };
242131
+ const handleConfirmNarrow = () => {
242132
+ setConfirmNarrowOpen(false);
242133
+ void persist({ ...buildPayload(), virtual_machine_secret_ids: [] });
242134
+ };
242135
+ /** Add every uncovered host to the selected policy, then save the settings. */
242136
+ const handleAddHostsAndSave = async (policy) => {
242137
+ var _a;
242138
+ const missing = uncovered.flatMap((entry) => entry.hosts);
242139
+ const allowedHosts = Array.from(new Set([...policy.allowed_hosts, ...missing]));
242140
+ setFormError(null);
242141
+ try {
242142
+ await updatePolicy({
242143
+ org: platformKey,
242144
+ body: { id: policy.id, allowed_hosts: allowedHosts },
242145
+ }).unwrap();
242146
+ toast.success(t('virtualMachineNetworkSection.policyHostsAdded', { policy: policy.name }));
242147
+ }
242148
+ catch (error) {
242149
+ const parsed = getVirtualMachineApiError(error);
242150
+ setFormError(parsed.status === 403
242151
+ ? t('virtualMachineNetworkSection.policyUpdateForbidden')
242152
+ : ((_a = parsed.message) !== null && _a !== void 0 ? _a : t('virtualMachineNetworkSection.policyUpdateFailed')));
242153
+ return;
242154
+ }
242155
+ await persist(buildPayload());
242156
+ };
242157
+ const openPolicyDialog = (policy) => {
242158
+ setPolicyDialogPolicy(policy);
242159
+ setPolicyDialogOpen(true);
242160
+ };
242161
+ const toggleSecret = (id, checked) => {
242162
+ setSecretIds((current) => checked ? Array.from(new Set([...current, id])) : current.filter((x) => x !== id));
242163
+ };
242164
+ if (isLoadingSettings || policiesQuery.isLoading || secretsQuery.isLoading) {
242165
+ return (jsxs("div", { className: "space-y-3 rounded-lg border p-6", "data-testid": "vm-network-section-loading", children: [jsx(Skeleton, { className: "h-5 w-48 rounded" }), jsx(Skeleton, { className: "h-24 w-full rounded-lg" })] }));
242166
+ }
242167
+ const capReached = secretIds.length >= VIRTUAL_MACHINE_MAX_SECRETS_PER_MENTOR;
242168
+ return (jsxs("div", { className: "rounded-lg border p-6", "data-testid": "vm-network-section", children: [jsxs("div", { className: "mb-4", children: [jsx("h4", { className: "text-base font-semibold text-gray-900", children: t('virtualMachineNetworkSection.title') }), jsx("p", { className: "mt-1 text-sm text-gray-500", children: t('virtualMachineNetworkSection.description') })] }), jsx("div", { role: "note", "data-testid": "vm-billing-notice", className: "mb-5 rounded-md border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600", children: t('virtualMachineNetworkSection.billingNotice', {
242169
+ usd: VIRTUAL_MACHINE_DEFAULT_BILLING.usd,
242170
+ minutes: VIRTUAL_MACHINE_DEFAULT_BILLING.minutes,
242171
+ }) }), jsxs("div", { className: "space-y-6", children: [jsxs("fieldset", { className: "space-y-2", children: [jsx("legend", { id: ids.egressLabel, className: "text-base font-semibold text-gray-900", children: t('virtualMachineNetworkSection.egressLabel') }), jsx("p", { id: ids.egressHelp, className: "text-sm text-gray-500", children: t('virtualMachineNetworkSection.egressHelp') }), jsx(RadioGroup, { value: egress, onValueChange: (next) => setEgress(next), "aria-labelledby": ids.egressLabel, "aria-describedby": ids.egressHelp, "aria-invalid": fieldErrors.egress ? true : undefined, disabled: isBusy, className: "gap-2", "data-testid": "vm-egress-group", children: VIRTUAL_MACHINE_EGRESS_PROFILES.map((profile) => {
242172
+ const itemId = `${baseId}-egress-${profile}`;
242173
+ return (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-gray-200 p-3", children: [jsx(RadioGroupItem, { value: profile, id: itemId, "data-testid": `vm-egress-${profile}`, className: BLUE_RADIO_ITEM_CLASS }), jsxs(Label$2, { htmlFor: itemId, className: "flex cursor-pointer flex-col gap-0.5 font-normal", children: [jsx("span", { className: "font-medium text-gray-900", children: t(EGRESS_COPY[profile].label) }), jsx("span", { className: "text-xs text-gray-500", children: t(EGRESS_COPY[profile].description) })] })] }, profile));
242174
+ }) }), fieldErrors.egress && (jsx("p", { role: "alert", className: "text-xs text-red-600", children: fieldErrors.egress }))] }), egress === 'custom' && (jsxs("div", { className: "space-y-2", "data-testid": "vm-policy-picker", children: [jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsx(Label$2, { id: ids.policyLabel, htmlFor: ids.policySelect, className: "text-base leading-9 font-semibold text-gray-900", children: t('virtualMachineNetworkSection.policyLabel') }), !policiesForbidden && (jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2", children: [jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => openPolicyDialog(null), disabled: isBusy, "data-testid": "vm-policy-create", children: [jsx(Plus, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineNetworkSection.createPolicy')] }), jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => setManageTab('network-policies'), disabled: isBusy, "data-testid": "vm-manage-policies", children: [jsx(Settings, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineNetworkSection.managePolicies')] })] }))] }), policiesForbidden ? (jsx("p", { className: "text-xs text-gray-500", "data-testid": "vm-policies-forbidden", children: t('virtualMachineNetworkSection.policiesForbidden') })) : (jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [jsxs(Select$1, { value: policyId !== null ? String(policyId) : '', onValueChange: (next) => setPolicyId(Number(next)), disabled: isBusy || policyOptions.length === 0, children: [jsx(SelectTrigger, { id: ids.policySelect, "aria-labelledby": ids.policyLabel, "aria-invalid": policyMissing || fieldErrors.policy ? true : undefined, "data-testid": "vm-policy-select", className: "sm:w-80", children: jsx(SelectValue, { placeholder: policyOptions.length === 0
242175
+ ? t('virtualMachineNetworkSection.noPolicies')
242176
+ : t('virtualMachineNetworkSection.policyPlaceholder') }) }), jsx(SelectContent, { children: policyOptions.map((policy) => (jsx(SelectItem, { value: String(policy.id), children: policy.name }, policy.id))) })] }), selectedPolicy && (jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => openPolicyDialog(selectedPolicy), disabled: isBusy, "data-testid": "vm-policy-edit", children: [jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineNetworkSection.editPolicy')] }))] })), jsx("p", { "aria-live": "polite", className: policyMissing || fieldErrors.policy ? 'text-xs text-red-600' : 'sr-only', children: (_d = fieldErrors.policy) !== null && _d !== void 0 ? _d : (policyMissing ? t('virtualMachineNetworkSection.policyRequired') : '') }), selectedPolicy && (jsxs("div", { className: "text-xs text-gray-500", "data-testid": "vm-policy-hosts", children: [jsx("span", { children: t('virtualMachineNetworkSection.policyHosts') }), jsx("ul", { role: "list", className: "mt-1 flex flex-wrap gap-1", children: selectedPolicy.allowed_hosts.map((host) => (jsx("li", { className: "rounded-md border border-gray-200 bg-gray-50 px-2 py-0.5 font-mono text-xs text-gray-700", children: host }, host))) })] }))] })), isNetworked && !secretsForbidden && (jsxs("div", { className: "space-y-2", "data-testid": "vm-secrets-picker", children: [jsxs("div", { className: "flex items-center justify-between gap-2", children: [jsx("span", { id: ids.secretsLabel, className: "text-base leading-9 font-semibold text-gray-900", children: t('virtualMachineNetworkSection.secretsLabel') }), jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => setManageTab('secrets'), disabled: isBusy, "data-testid": "vm-manage-secrets", children: [jsx(Settings, { className: "h-4 w-4", "aria-hidden": "true" }), t('virtualMachineNetworkSection.manageSecrets')] })] }), jsx("p", { id: ids.secretsHelp, className: "text-sm text-gray-500", children: t('virtualMachineNetworkSection.secretsHelp', {
242177
+ max: VIRTUAL_MACHINE_MAX_SECRETS_PER_MENTOR,
242178
+ }) }), secretOptions.length === 0 ? (jsx("p", { className: "text-xs text-gray-500", "data-testid": "vm-no-secrets", children: t('virtualMachineNetworkSection.noSecrets') })) : (jsx("div", { role: "group", "aria-labelledby": ids.secretsLabel, "aria-describedby": ids.secretsHelp, className: "space-y-2", children: secretOptions.map((secret) => {
242179
+ const itemId = `${baseId}-secret-${secret.id}`;
242180
+ const checked = secretIds.includes(secret.id);
242181
+ return (jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-gray-200 p-3", children: [jsx(Checkbox, { id: itemId, checked: checked, disabled: isBusy || (!checked && capReached), onCheckedChange: (next) => toggleSecret(secret.id, next === true), "data-testid": `vm-secret-option-${secret.env_var}`, className: BLUE_CHECKBOX_CLASS }), jsxs(Label$2, { htmlFor: itemId, className: "flex cursor-pointer flex-col gap-0.5 font-normal", children: [jsxs("span", { className: "text-gray-900", children: [jsx("span", { className: "font-medium", children: secret.name }), ' ', jsx("code", { className: "rounded bg-gray-100 px-1 py-0.5 font-mono text-xs", children: secret.env_var })] }), jsx("span", { className: "text-xs text-gray-500", children: secret.allow_hosts.join(', ') })] })] }, secret.id));
242182
+ }) })), capReached && (jsx("p", { role: "status", className: "text-xs text-gray-500", "data-testid": "vm-secrets-cap", children: t('virtualMachineNetworkSection.secretsCapReached', {
242183
+ max: VIRTUAL_MACHINE_MAX_SECRETS_PER_MENTOR,
242184
+ }) })), fieldErrors.secrets && (jsx("p", { role: "alert", className: "text-xs text-red-600", children: fieldErrors.secrets }))] })), uncovered.length > 0 && selectedPolicy && (jsxs("div", { role: "alert", "data-testid": "vm-uncovered-hosts", className: "space-y-2 rounded-md border border-amber-200 bg-amber-50 p-3 text-xs text-amber-800", children: [jsxs("div", { className: "flex items-start gap-2", children: [jsx(TriangleAlert, { className: "mt-0.5 h-4 w-4 shrink-0", "aria-hidden": "true" }), jsxs("div", { children: [jsx("p", { className: "font-medium", children: t('virtualMachineNetworkSection.uncoveredTitle') }), jsx("ul", { role: "list", className: "mt-1 list-disc space-y-0.5 pl-5", children: uncovered.map((entry) => (jsx("li", { children: t('virtualMachineNetworkSection.uncoveredItem', {
242185
+ envVar: entry.envVar,
242186
+ hosts: entry.hosts.join(', '),
242187
+ policy: selectedPolicy.name,
242188
+ }) }, entry.envVar))) })] })] }), !policiesForbidden && (jsxs(Button$1, { type: "button", variant: "outline", size: "sm", onClick: () => void handleAddHostsAndSave(selectedPolicy), disabled: isBusy, "data-testid": "vm-add-hosts-and-save", children: [isUpdatingPolicy && (jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" })), t('virtualMachineNetworkSection.addHostsAndSave', { policy: selectedPolicy.name })] }))] })), formError && (jsx("div", { role: "alert", "data-testid": "vm-network-form-error", className: "rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-700", children: formError })), jsxs("div", { className: "flex items-center justify-end gap-2", children: [jsx(Button$1, { type: "button", variant: "outline", size: "sm", onClick: resetDraft, disabled: !isDirty || isBusy, "data-testid": "vm-network-discard", children: t('virtualMachineNetworkSection.discard') }), jsxs(Button$1, { type: "button", size: "sm", onClick: handleSave, disabled: !isDirty || !isValid || isBusy, "data-testid": "vm-network-save", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: [isSaving && jsx(LoaderCircle, { className: "h-4 w-4 animate-spin", "aria-hidden": "true" }), t('virtualMachineNetworkSection.save')] })] })] }), jsx(NetworkPolicyDialog, { open: policyDialogOpen, onOpenChange: setPolicyDialogOpen, tenantKey: platformKey, policy: policyDialogPolicy, onSaved: (policy) => setPolicyId(policy.id) }), jsx(Dialog, { open: manageTab !== null, onOpenChange: (open) => !open && setManageTab(null), children: jsxs(DialogContent, { className: "max-h-[90vh] w-full max-w-5xl overflow-y-auto", "data-testid": "vm-manage-dialog", children: [jsxs(DialogHeader, { children: [jsx(DialogTitle, { children: t('virtualMachineNetworkSection.manageTitle') }), jsx(DialogDescription, { children: t('virtualMachineNetworkSection.manageDescription') })] }), jsx(VirtualMachineAdminTab, { tenantKey: platformKey, username: username, defaultTab: manageTab !== null && manageTab !== void 0 ? manageTab : 'network-policies' })] }) }), jsx(Dialog, { open: confirmNarrowOpen, onOpenChange: setConfirmNarrowOpen, children: jsxs(DialogContent, { className: "max-w-md", "data-testid": "vm-narrow-confirm", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { children: t('virtualMachineNetworkSection.narrowTitle') }) }), jsx(DialogDescription, { className: "text-gray-600", children: t('virtualMachineNetworkSection.narrowDescription', {
242189
+ profile: t(EGRESS_COPY[egress].label),
242190
+ count: serverSecretIds.length,
242191
+ }) }), jsxs(DialogFooter, { className: "pt-2", children: [jsx(Button$1, { type: "button", variant: "outline", onClick: () => setConfirmNarrowOpen(false), children: t('virtualMachineNetworkSection.cancel') }), jsx(Button$1, { type: "button", onClick: handleConfirmNarrow, "data-testid": "vm-narrow-confirm-action", className: "cursor-pointer bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-white hover:opacity-90", children: t('virtualMachineNetworkSection.narrowConfirm') })] })] }) })] }));
242192
+ }
242193
+
240456
242194
  const HEALTHY_HEALTH_VALUES = new Set(['healthy', 'ok', 'pass', 'success']);
240457
242195
  const HEALTHY_STATUS_VALUES = new Set(['active', 'running', 'connected', 'ok', 'ready']);
240458
242196
  /** True when the instance's status is set and not one of the healthy values.
@@ -240944,7 +242682,7 @@ function SandboxConfig({ platformKey, mentorUniqueId, username }) {
240944
242682
  llm_name: currentModel.llmName,
240945
242683
  llm_provider: currentModel.provider,
240946
242684
  }, llms: availableLLMProviders }))] })) : null;
240947
- return (jsxs("div", { className: "space-y-6", children: [sandboxKindsSection, clawEnabled && (mentorConfig ? connectedSection : notConnectedSection)] }));
242685
+ return (jsxs("div", { className: "space-y-6", children: [sandboxKindsSection, sandboxFlags.enable_virtual_machine && (jsx(VirtualMachineNetworkSection, { platformKey: platformKey, mentorUniqueId: mentorUniqueId, username: resolvedUsername })), clawEnabled && (mentorConfig ? connectedSection : notConnectedSection)] }));
240948
242686
  }
240949
242687
  // ── Edit Instance Dialog ─────────────────────────────────────────────────────
240950
242688
  function EditInstanceDialog({ instance, org, onClose, onSaved, }) {
@@ -244213,7 +245951,7 @@ const ProjectFilesModal$1 = lazy(() => Promise.resolve().then(function () { retu
244213
245951
  const AddMentorToProjectModal$1 = lazy(() => Promise.resolve().then(function () { return addMentorToProjectModal; }).then((mod) => ({
244214
245952
  default: mod.AddMentorToProjectModal,
244215
245953
  })));
244216
- function ProjectLandingPage({ project, sessionId, onSubmit, onScreenSharingClick, isScreenSharingModalOpen, onPhoneCallClick, stopGenerating, tenantKey, username, enableSafetyDisclaimer, enableWebBrowsing, isStreaming, updateSessionTools, setSessionTools, activeTools, screenSharing, deepResearch, studyMode, imageGeneration, codeInterpreter, promptsIsEnabled, isPreviewMode, setMessage, mentorId, mentorUniqueId, projectId, googleSlidesIsEnabled, googleDocumentIsEnabled, artifactsEnabled, userIsStudent, isPublicRoute, navigateToProject, showExploreMentors = false, }) {
245954
+ function ProjectLandingPage({ project, sessionId, onSubmit, onScreenSharingClick, isScreenSharingModalOpen, onPhoneCallClick, stopGenerating, tenantKey, username, enableSafetyDisclaimer, enableWebBrowsing, isStreaming, updateSessionTools, setSessionTools, activeTools, screenSharing, deepResearch, studyMode, imageGeneration, codeInterpreter, promptsIsEnabled, isPreviewMode, setMessage, mentorId, mentorUniqueId, projectId, googleSlidesIsEnabled, googleDocumentIsEnabled, artifactsEnabled, userIsStudent, isPublicRoute, navigateToProject, showExploreMentors = false, disabledResourceTypes, maxUploadSizeMb, dropboxExtensions, }) {
244217
245955
  var _a;
244218
245956
  const [isInstructionsModalOpen, setIsInstructionsModalOpen] = useState(false);
244219
245957
  const [isFilesModalOpen, setIsFilesModalOpen] = useState(false);
@@ -244226,7 +245964,7 @@ function ProjectLandingPage({ project, sessionId, onSubmit, onScreenSharingClick
244226
245964
  name: mentor.name,
244227
245965
  avatar: mentor.profile_image,
244228
245966
  description: mentor.description,
244229
- })), onAddMentorClick: () => setIsAddMentorModalOpen(true), showTitle: true, tenantKey: tenantKey, mentorId: mentorId, projectId: projectId, username: username, navigateToProject: navigateToProject }))] })] })] }), isInstructionsModalOpen && (jsx(Suspense, { fallback: null, children: jsx(ProjectInstructionsModal$1, { isOpen: isInstructionsModalOpen, onClose: () => setIsInstructionsModalOpen(false), tenantKey: tenantKey, projectId: projectId, username: username }) })), isFilesModalOpen && (jsx(Suspense, { fallback: null, children: jsx(ProjectFilesModal$1, { isOpen: isFilesModalOpen, onClose: () => setIsFilesModalOpen(false), tenantKey: tenantKey, mentorId: mentorId, username: username }) })), isAddMentorModalOpen && (jsx(Suspense, { fallback: null, children: jsx(AddMentorToProjectModal$1, { isOpen: isAddMentorModalOpen, onClose: () => setIsAddMentorModalOpen(false), projectName: project.name, tenantKey: tenantKey, projectId: projectId, username: username, isPublicRoute: isPublicRoute }) }))] }));
245967
+ })), onAddMentorClick: () => setIsAddMentorModalOpen(true), showTitle: true, tenantKey: tenantKey, mentorId: mentorId, projectId: projectId, username: username, navigateToProject: navigateToProject }))] })] })] }), isInstructionsModalOpen && (jsx(Suspense, { fallback: null, children: jsx(ProjectInstructionsModal$1, { isOpen: isInstructionsModalOpen, onClose: () => setIsInstructionsModalOpen(false), tenantKey: tenantKey, projectId: projectId, username: username }) })), isFilesModalOpen && (jsx(Suspense, { fallback: null, children: jsx(ProjectFilesModal$1, { isOpen: isFilesModalOpen, onClose: () => setIsFilesModalOpen(false), tenantKey: tenantKey, mentorId: mentorId, username: username, disabledResourceTypes: disabledResourceTypes, maxUploadSizeMb: maxUploadSizeMb, dropboxExtensions: dropboxExtensions }) })), isAddMentorModalOpen && (jsx(Suspense, { fallback: null, children: jsx(AddMentorToProjectModal$1, { isOpen: isAddMentorModalOpen, onClose: () => setIsAddMentorModalOpen(false), projectName: project.name, tenantKey: tenantKey, projectId: projectId, username: username, isPublicRoute: isPublicRoute }) }))] }));
244230
245968
  }
244231
245969
 
244232
245970
  function MinimumMentorAlert({ open, onOpenChange }) {
@@ -244499,8 +246237,9 @@ const TrainOrDeleteModal$1 = dynamic(() => Promise.resolve().then(function () {
244499
246237
  */
244500
246238
  function isCrawlableDocType(documentType) {
244501
246239
  const docType = documentType === null || documentType === void 0 ? void 0 : documentType.toLowerCase();
244502
- // Uploaded documents (local files like PDF, DOCX, etc.)
244503
- const uploadedFileTypes = ['file'];
246240
+ // Uploaded documents: generic local files ('file') plus the local resource
246241
+ // types that send their own fileType as the document type.
246242
+ const uploadedFileTypes = ['file', 'image', 'audio', 'video', 'zip'];
244504
246243
  if (uploadedFileTypes.some((type) => docType === null || docType === void 0 ? void 0 : docType.includes(type))) {
244505
246244
  return false;
244506
246245
  }
@@ -244511,6 +246250,12 @@ function isCrawlableDocType(documentType) {
244511
246250
  }
244512
246251
  return true;
244513
246252
  }
246253
+ /**
246254
+ * The crawler User-Agent override only applies to web crawler documents.
246255
+ */
246256
+ function isWebCrawlerDocType(documentType) {
246257
+ return !!(documentType === null || documentType === void 0 ? void 0 : documentType.toLowerCase().includes('webcrawler'));
246258
+ }
244514
246259
  function DatasetItem({ dataset, onSelect, isSelected, labels }) {
244515
246260
  var _a, _b, _c, _d, _e;
244516
246261
  const t = useT();
@@ -244587,7 +246332,7 @@ function DatasetItem({ dataset, onSelect, isSelected, labels }) {
244587
246332
  }
244588
246333
  }, className: "text-gray-500 hover:text-gray-700", disabled: isEditTrainingDocumentLoading || disabled, children: dataset.access === 'private' ? (jsxs(Fragment$1, { children: [jsx(EyeOff, { className: "h-4 w-4" }), jsx("span", { className: "sr-only", children: t('datasetsTabDatasetItem.makePublic') })] })) : (jsxs(Fragment$1, { children: [jsx(Eye, { className: "h-4 w-4" }), jsx("span", { className: "sr-only", children: t('datasetsTabDatasetItem.makePrivate') })] })) })) }) }), jsx(TableCell, { className: "p-3 whitespace-nowrap", children: jsx(WithFormPermissions, { name: "is_trained",
244589
246334
  // @ts-ignore
244590
- permissions: (_e = dataset === null || dataset === void 0 ? void 0 : dataset.permissions) === null || _e === void 0 ? void 0 : _e.field, children: ({ disabled }) => (jsx(TrainingStatusSwitch, { is_trained: dataset.is_trained, url: dataset.url, disabled: disabled, training_status: dataset.training_status, handleEditTrainingDocument: handleEditTrainingDocument, onUntrainSuccess: () => setIsDeleteDatasetModalOpen(true), onTrainRequest: () => setIsTrainOrDeleteModalOpen(true) })) }) })] }, dataset.id), isDeleteDatasetModalOpen && (jsx(DeleteDatasetModal$1, { isOpen: isDeleteDatasetModalOpen, onClose: () => setIsDeleteDatasetModalOpen(false), dataset: dataset, labels: labels })), isRetrainScheduleModalOpen && (jsx(RetrainScheduleModal$1, { isOpen: isRetrainScheduleModalOpen, onClose: () => setIsRetrainScheduleModalOpen(false), dataset: dataset, labels: labels })), isTrainOrDeleteModalOpen && (jsx(TrainOrDeleteModal$1, { isOpen: isTrainOrDeleteModalOpen, onClose: () => setIsTrainOrDeleteModalOpen(false), onTrain: handleTrainDataset, onDelete: handleDeleteFromTrainModal, showUserAgentField: isCrawlableDocType(dataset.document_type), isLoading: isEditTrainingDocumentLoading, labels: labels }))] }));
246335
+ permissions: (_e = dataset === null || dataset === void 0 ? void 0 : dataset.permissions) === null || _e === void 0 ? void 0 : _e.field, children: ({ disabled }) => (jsx(TrainingStatusSwitch, { is_trained: dataset.is_trained, url: dataset.url, disabled: disabled, training_status: dataset.training_status, handleEditTrainingDocument: handleEditTrainingDocument, onUntrainSuccess: () => setIsDeleteDatasetModalOpen(true), onTrainRequest: () => setIsTrainOrDeleteModalOpen(true) })) }) })] }, dataset.id), isDeleteDatasetModalOpen && (jsx(DeleteDatasetModal$1, { isOpen: isDeleteDatasetModalOpen, onClose: () => setIsDeleteDatasetModalOpen(false), dataset: dataset, labels: labels })), isRetrainScheduleModalOpen && (jsx(RetrainScheduleModal$1, { isOpen: isRetrainScheduleModalOpen, onClose: () => setIsRetrainScheduleModalOpen(false), dataset: dataset, labels: labels })), isTrainOrDeleteModalOpen && (jsx(TrainOrDeleteModal$1, { isOpen: isTrainOrDeleteModalOpen, onClose: () => setIsTrainOrDeleteModalOpen(false), onTrain: handleTrainDataset, onDelete: handleDeleteFromTrainModal, showUserAgentField: isWebCrawlerDocType(dataset.document_type), isLoading: isEditTrainingDocumentLoading, labels: labels }))] }));
244591
246336
  }
244592
246337
  function TrainingStatusSwitch({ is_trained, url, training_status, disabled, handleEditTrainingDocument, onUntrainSuccess, onTrainRequest, }) {
244593
246338
  const t = useT();
@@ -244854,6 +246599,15 @@ const resourceTypes = [
244854
246599
  accept: 'text/plain',
244855
246600
  icon: (jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("rect", { width: "24", height: "24", rx: "4", fill: "url(#paint0_linear_text)" }), jsx("path", { d: "M7 7H17V9H13V17H11V9H7V7Z", fill: "white" }), jsx("defs", { children: jsxs("linearGradient", { id: "paint0_linear_text", x1: "0", y1: "0", x2: "24", y2: "24", gradientUnits: "userSpaceOnUse", children: [jsx("stop", { stopColor: "#4F46E5" }), jsx("stop", { offset: "1", stopColor: "#06B6D4" })] }) })] })),
244856
246601
  },
246602
+ {
246603
+ id: 'markdown',
246604
+ name: 'Markdown',
246605
+ bgColor: 'bg-blue-100',
246606
+ isActive: true,
246607
+ type: 'local',
246608
+ accept: 'text/markdown,.md,.markdown',
246609
+ icon: (jsxs("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true", children: [jsx("rect", { width: "24", height: "24", rx: "4", fill: "#1F2937" }), jsx("path", { d: "M4 16V8h2l2 3 2-3h2v8H9.5v-4.6L8 13.7l-1.5-2.3V16H4Zm11.5 0L13 13h1.75V8h1.5v5H18l-2.5 3Z", fill: "white" })] })),
246610
+ },
244857
246611
  {
244858
246612
  id: 'audio',
244859
246613
  name: 'Audio',
@@ -244915,6 +246669,26 @@ const resourceTypes = [
244915
246669
  },
244916
246670
  ];
244917
246671
 
246672
+ /**
246673
+ * Shared chrome for the Add Resources picker and every per-type resource
246674
+ * dialog, mirroring the Datasets tab: a fixed header (title + description bar
246675
+ * with a divider), a body that scrolls on its own (`ResourceDialogBody`), and
246676
+ * an optional fixed footer (`ResourceDialogFooter`).
246677
+ */
246678
+ function ResourceDialogContent({ title, description, className, children, }) {
246679
+ return (jsxs(DialogContent, { className: cn('flex max-h-[80vh] w-full max-w-lg flex-col gap-0 overflow-hidden p-0', className), children: [jsxs(DialogHeader, { className: "flex-shrink-0 space-y-1 border-b border-gray-200 bg-white px-4 pt-4 pr-10 pb-3 text-left", children: [jsx(DialogTitle, { className: "text-muted-foreground text-base", children: title }), jsx(DialogDescription, { className: "text-xs text-gray-600", children: description })] }), children] }));
246680
+ }
246681
+ /** The only scrolling part of a resource dialog; focusable so keyboard users can scroll it. */
246682
+ function ResourceDialogBody({ label, children }) {
246683
+ return (jsx("div", { className: "min-h-0 flex-1 space-y-4 overflow-x-hidden overflow-y-auto p-4", tabIndex: 0, role: "region", "aria-label": label, children: children }));
246684
+ }
246685
+ /** Actions pinned below the scroll region so they stay visible. */
246686
+ function ResourceDialogFooter({ children }) {
246687
+ return (jsx("div", { className: "flex flex-shrink-0 justify-end border-t border-gray-200 bg-white px-4 py-3", children: children }));
246688
+ }
246689
+ /** Submit button styling shared by every resource dialog footer. */
246690
+ const RESOURCE_SUBMIT_BUTTON_CLASS = 'bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-sm text-white hover:text-white hover:opacity-90';
246691
+
244918
246692
  /**
244919
246693
  * Spend-cap blocks (HTTP 429) carry the machine code in `error`/`error_code`
244920
246694
  * and a friendly, learner-safe sentence in `message`. Detection keys on
@@ -245009,7 +246783,7 @@ function GithubFileUploadModal() {
245009
246783
  const { branches, error, isLoading } = useGithubRepoDetails(debouncedGithubUrl, tenantKey);
245010
246784
  const isLoadingDisabled = !githubUrl || isLoading || isAddTrainingDocumentLoading;
245011
246785
  const isDisabled = isLoadingDisabled || !branch;
245012
- return (jsxs("div", { className: "flex flex-col space-y-4", children: [jsx(Input, { type: "url", placeholder: t('resourceModalGithubFileUploadModal.githubRepoUrlPlaceholder'), value: githubUrl, onChange: (e) => setGithubUrl(e.target.value), autoComplete: "url", disabled: isAddTrainingDocumentLoading }), jsx("p", { className: "text-xs font-light text-red-500", children: error }), jsxs(Select$1, { value: branch, onValueChange: (value) => setBranch(value), disabled: isLoadingDisabled, children: [jsx(SelectTrigger, { className: "w-full rounded-md border p-2", children: jsx(SelectValue, { placeholder: t('resourceModalGithubFileUploadModal.selectBranchPlaceholder') }) }), jsx(SelectContent, { children: branches.map((b) => (jsx(SelectItem, { value: b, children: b }, b))) })] }), jsx("div", { className: "flex justify-end", children: jsx(Button$1, { onClick: handleSubmit, disabled: isDisabled, className: `${githubUrl ? 'bg-gradient-to-r from-blue-600 to-blue-400 text-white' : 'bg-gray-100 text-gray-500'} hover:opacity-90`, children: t('resourceModalGithubFileUploadModal.submit') }) })] }));
246786
+ return (jsxs(Fragment$1, { children: [jsxs(ResourceDialogBody, { label: t('resourceModalGithubFileUploadModal.scrollRegionAriaLabel'), children: [jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: "github-repo-url", children: t('resourceModalGithubFileUploadModal.repoUrlLabel') }), jsx(Input, { id: "github-repo-url", type: "url", placeholder: t('resourceModalGithubFileUploadModal.githubRepoUrlPlaceholder'), value: githubUrl, onChange: (e) => setGithubUrl(e.target.value), autoComplete: "url", disabled: isAddTrainingDocumentLoading, "aria-describedby": error ? 'github-repo-url-error' : undefined, "aria-invalid": !!error }), error ? (jsx("p", { id: "github-repo-url-error", className: "text-destructive -mt-1 text-xs", role: "alert", children: error })) : null] }), jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: "github-branch", children: t('resourceModalGithubFileUploadModal.branchLabel') }), jsxs(Select$1, { value: branch, onValueChange: (value) => setBranch(value), disabled: isLoadingDisabled, children: [jsx(SelectTrigger, { id: "github-branch", className: "w-full", children: jsx(SelectValue, { placeholder: t('resourceModalGithubFileUploadModal.selectBranchPlaceholder') }) }), jsx(SelectContent, { children: branches.map((b) => (jsx(SelectItem, { value: b, children: b }, b))) })] })] })] }), jsx(ResourceDialogFooter, { children: jsx(Button$1, { onClick: handleSubmit, disabled: isDisabled, className: RESOURCE_SUBMIT_BUTTON_CLASS, children: t('resourceModalGithubFileUploadModal.submit') }) })] }));
245013
246787
  }
245014
246788
 
245015
246789
  /**
@@ -245129,18 +246903,18 @@ function LocalFileUploadModal({ resource, maxFileSizeMB = DEFAULT_MAX_FILE_SIZE_
245129
246903
  handleUploadFile();
245130
246904
  }
245131
246905
  };
245132
- return (jsxs("div", { className: "flex flex-col items-center", children: [jsxs("div", { className: `w-full max-w-sm border-2 border-dashed ${isDraggingFile
245133
- ? 'border-blue-400 bg-blue-50/50 backdrop-blur-sm'
245134
- : 'border-gray-300 bg-gray-50'} flex flex-col items-center justify-center rounded-lg p-8 transition-all duration-200 ${!isDraggingFile && 'hover:bg-gray-100'}`, onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [jsx("div", { className: "mb-4 rounded-full bg-blue-100 p-3", children: jsx(Upload, { className: "h-8 w-8 text-blue-600" }) }), jsx("h3", { className: "mb-1 text-lg font-medium text-gray-700", children: t('resourceModalLocalFileUploadModal.dragAndDrop') }), jsx("p", { className: "mb-4 text-sm text-gray-500", children: t('resourceModalLocalFileUploadModal.or') }), jsxs("label", { htmlFor: "file-upload", className: "cursor-pointer", children: [jsx("div", { className: "rounded-md bg-gradient-to-r from-blue-600 to-blue-400 px-4 py-2 font-medium text-white transition-colors hover:opacity-90", children: t('resourceModalLocalFileUploadModal.browseFiles') }), jsx("input", { id: "file-upload", type: "file", accept: resource.accept, className: "hidden", ref: fileInputRef, onChange: handleFileChange })] }), jsx("p", { className: "mt-4 text-xs text-gray-500", children: t('resourceModalLocalFileUploadModal.maximumFileSize', {
245135
- value: maxFileSize.value,
245136
- unit: maxFileSize.unit,
245137
- }) }), file && (jsxs(Fragment$1, { children: [jsx("div", { className: "mt-4 flex w-full items-center rounded-md border border-gray-200 bg-white p-3", children: jsxs("div", { className: "flex w-full items-center", children: [jsx("div", { className: "mr-3 flex-shrink-0 rounded-md bg-blue-100 p-2", children: jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z", fill: "#4285F4" }), jsx("path", { d: "M14 2V8H20L14 2Z", fill: "#A1C2FA" })] }) }), jsxs("div", { className: "isolate z-50 min-w-0 flex-1 overflow-hidden", children: [jsx("p", { className: "truncate text-sm font-medium", children: file.name }), jsxs("p", { className: "text-xs text-gray-500", children: [convertFromBytes(file.size).value, " ", convertFromBytes(file.size).unit] })] }), jsx("button", { onClick: () => {
245138
- setFile(null);
245139
- setDescription('');
245140
- if (fileInputRef.current) {
245141
- fileInputRef.current.value = '';
245142
- }
245143
- }, className: "ml-2 flex-shrink-0 rounded-full p-1 transition-colors hover:bg-gray-100", children: jsx(X$1, { className: "h-4 w-4 text-gray-500" }) })] }) }), isImageFile(file) && (jsxs("div", { className: "mt-3 w-full", children: [jsx("label", { htmlFor: "image-description", className: "mb-1 block text-sm font-medium text-gray-700", children: t('resourceModalLocalFileUploadModal.descriptionOptional') }), jsx(Textarea, { id: "image-description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: t('resourceModalLocalFileUploadModal.descriptionPlaceholder'), className: "resize-none", rows: 3 })] }))] }))] }), jsx("div", { className: "mt-4 flex w-full justify-end", children: jsx(Button$1, { onClick: handleSubmit, disabled: !file || isDisabled, className: `${file ? 'bg-gradient-to-r from-blue-600 to-blue-400 text-white' : 'bg-gray-100 text-gray-500'} hover:opacity-90`, children: isAddTrainingDocumentLoading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }), ' ', t('resourceModalLocalFileUploadModal.submitting')] })) : (jsx(Fragment$1, { children: t('resourceModalLocalFileUploadModal.submit') })) }) })] }));
246906
+ return (jsxs(Fragment$1, { children: [jsx(ResourceDialogBody, { label: t('resourceModalLocalFileUploadModal.scrollRegionAriaLabel'), children: jsxs("div", { className: `w-full border-2 border-dashed ${isDraggingFile
246907
+ ? 'border-blue-400 bg-blue-50/50 backdrop-blur-sm'
246908
+ : 'border-gray-300 bg-gray-50'} flex flex-col items-center justify-center rounded-lg p-8 transition-all duration-200 ${!isDraggingFile && 'hover:bg-gray-100'}`, onDragOver: handleDragOver, onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDrop: handleDrop, children: [jsx("div", { className: "mb-4 rounded-full bg-blue-100 p-3", children: jsx(Upload, { className: "h-8 w-8 text-blue-600" }) }), jsx("h3", { className: "mb-1 text-lg font-medium text-gray-700", children: t('resourceModalLocalFileUploadModal.dragAndDrop') }), jsx("p", { className: "mb-4 text-sm text-gray-500", children: t('resourceModalLocalFileUploadModal.or') }), jsxs("label", { htmlFor: "file-upload", className: "cursor-pointer", children: [jsx("div", { className: "rounded-md bg-gradient-to-r from-blue-600 to-blue-400 px-4 py-2 font-medium text-white transition-colors hover:opacity-90", children: t('resourceModalLocalFileUploadModal.browseFiles') }), jsx("input", { id: "file-upload", type: "file", accept: resource.accept, className: "hidden", ref: fileInputRef, onChange: handleFileChange })] }), jsx("p", { className: "mt-4 text-xs text-gray-500", children: t('resourceModalLocalFileUploadModal.maximumFileSize', {
246909
+ value: maxFileSize.value,
246910
+ unit: maxFileSize.unit,
246911
+ }) }), file && (jsxs(Fragment$1, { children: [jsx("div", { className: "mt-4 flex w-full items-center rounded-md border border-gray-200 bg-white p-3", children: jsxs("div", { className: "flex w-full items-center", children: [jsx("div", { className: "mr-3 flex-shrink-0 rounded-md bg-blue-100 p-2", children: jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [jsx("path", { d: "M14 2H6C4.9 2 4 2.9 4 4V20C4 21.1 4.9 22 6 22H18C19.1 22 20 21.1 20 20V8L14 2Z", fill: "#4285F4" }), jsx("path", { d: "M14 2V8H20L14 2Z", fill: "#A1C2FA" })] }) }), jsxs("div", { className: "isolate z-50 min-w-0 flex-1 overflow-hidden", children: [jsx("p", { className: "truncate text-sm font-medium", children: file.name }), jsxs("p", { className: "text-xs text-gray-500", children: [convertFromBytes(file.size).value, " ", convertFromBytes(file.size).unit] })] }), jsx("button", { onClick: () => {
246912
+ setFile(null);
246913
+ setDescription('');
246914
+ if (fileInputRef.current) {
246915
+ fileInputRef.current.value = '';
246916
+ }
246917
+ }, className: "ml-2 flex-shrink-0 rounded-full p-1 transition-colors hover:bg-gray-100", children: jsx(X$1, { className: "h-4 w-4 text-gray-500" }) })] }) }), isImageFile(file) && (jsxs("div", { className: "mt-3 grid w-full gap-2", children: [jsx(Label$2, { htmlFor: "image-description", children: t('resourceModalLocalFileUploadModal.descriptionOptional') }), jsx(Textarea, { id: "image-description", value: description, onChange: (e) => setDescription(e.target.value), placeholder: t('resourceModalLocalFileUploadModal.descriptionPlaceholder'), className: "resize-none", rows: 3 })] }))] }))] }) }), jsx(ResourceDialogFooter, { children: jsx(Button$1, { onClick: handleSubmit, disabled: !file || isDisabled, className: RESOURCE_SUBMIT_BUTTON_CLASS, children: isAddTrainingDocumentLoading ? (jsxs(Fragment$1, { children: [jsx(LoaderCircle, { className: "h-4 w-4 animate-spin" }), ' ', t('resourceModalLocalFileUploadModal.submitting')] })) : (jsx(Fragment$1, { children: t('resourceModalLocalFileUploadModal.submit') })) }) })] }));
245144
246918
  }
245145
246919
 
245146
246920
  function UrlUploadModal({ resource }) {
@@ -245209,9 +246983,7 @@ function UrlUploadModal({ resource }) {
245209
246983
  handleSubmitUrl();
245210
246984
  }
245211
246985
  };
245212
- return (jsxs("div", { className: "flex flex-col space-y-4", children: [jsx(Input, { type: "url", placeholder: t('resourceModalUrlUploadModal.urlPlaceholder'), value: url, onChange: handleUrlChange, autoComplete: "url" }), jsx("div", { className: "flex justify-end", children: jsx(Button$1, { onClick: handleSubmit, disabled: !url || isDisabled, className: cn('cursor-pointer hover:opacity-90', url
245213
- ? 'bg-gradient-to-r from-blue-600 to-blue-400 text-white'
245214
- : 'bg-gray-100 text-gray-500'), children: t('resourceModalUrlUploadModal.submit') }) })] }));
246986
+ return (jsxs(Fragment$1, { children: [jsx(ResourceDialogBody, { label: t('resourceModalUrlUploadModal.scrollRegionAriaLabel'), children: jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: "resource-url", children: t('resourceModalUrlUploadModal.urlLabel') }), jsx(Input, { id: "resource-url", type: "url", placeholder: t('resourceModalUrlUploadModal.urlPlaceholder'), value: url, onChange: handleUrlChange, autoComplete: "url" })] }) }), jsx(ResourceDialogFooter, { children: jsx(Button$1, { onClick: handleSubmit, disabled: !url || isDisabled, className: RESOURCE_SUBMIT_BUTTON_CLASS, children: t('resourceModalUrlUploadModal.submit') }) })] }));
245215
246987
  }
245216
246988
 
245217
246989
  function useWebsiteCrawlerResource(resource) {
@@ -245236,9 +247008,11 @@ function useWebsiteCrawlerResource(resource) {
245236
247008
  crawler_match_patterns: [],
245237
247009
  crawler_pattern_type: 'glob',
245238
247010
  temp_crawler_match_patterns: '',
247011
+ crawler_user_agent: '',
245239
247012
  },
245240
247013
  onSubmit: async ({ value }) => {
245241
247014
  try {
247015
+ const crawlerUserAgent = value.crawler_user_agent.trim();
245242
247016
  await addTrainingDocument({
245243
247017
  org: tenantKey,
245244
247018
  formData: {
@@ -245250,6 +247024,11 @@ function useWebsiteCrawlerResource(resource) {
245250
247024
  crawler_match_patterns: crawlerMatchPatterns,
245251
247025
  //@ts-expect-error crawler_pattern_type may not be on the typed args but is accepted by the API
245252
247026
  crawler_pattern_type: value.crawler_pattern_type,
247027
+ // Per-document User-Agent override; sent only when set so the
247028
+ // backend default applies otherwise.
247029
+ ...(crawlerUserAgent
247030
+ ? { crawler_extra_headers: { 'User-Agent': crawlerUserAgent } }
247031
+ : {}),
245253
247032
  },
245254
247033
  userId: username !== null && username !== void 0 ? username : '',
245255
247034
  }).unwrap();
@@ -245276,30 +247055,30 @@ function useWebsiteCrawlerResource(resource) {
245276
247055
  function WebsiteCrawlModal({ resource }) {
245277
247056
  const t = useT();
245278
247057
  const { form, handleCheckUrlIsValid, crawlerMatchPatterns, setCrawlerMatchPatterns } = useWebsiteCrawlerResource(resource);
245279
- return (jsxs(Card$1, { className: "mx-auto w-full max-w-2xl", role: "dialog", "aria-labelledby": "crawler-title", "aria-describedby": "crawler-description", children: [jsxs(CardHeader, { children: [jsx(CardTitle, { id: "crawler-title", className: "text-md font-semibold text-gray-600", children: t('resourceModalWebsiteCrawlModal.title') }), jsx("p", { id: "crawler-description", className: "text-muted-foreground text-sm", children: t('resourceModalWebsiteCrawlModal.description') })] }), jsx(CardContent, { children: jsx("form", { className: "space-y-4", onSubmit: (e) => {
245280
- e.preventDefault();
245281
- e.stopPropagation();
245282
- form.handleSubmit();
245283
- }, "aria-label": t('resourceModalWebsiteCrawlModal.formAriaLabel'), children: jsxs(Fragment$1, { children: [jsx(form.Field, { name: "url", validators: {
247058
+ return (jsxs("form", { className: "flex min-h-0 flex-1 flex-col", onSubmit: (e) => {
247059
+ e.preventDefault();
247060
+ e.stopPropagation();
247061
+ form.handleSubmit();
247062
+ }, "aria-label": t('resourceModalWebsiteCrawlModal.formAriaLabel'), children: [jsxs(ResourceDialogBody, { label: t('resourceModalWebsiteCrawlModal.scrollRegionAriaLabel'), children: [jsx("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-3 text-xs text-gray-600", children: t('resourceModalWebsiteCrawlModal.infoBox') }), jsxs(Fragment$1, { children: [jsx(form.Field, { name: "url", validators: {
245284
247063
  onChange: ({ value }) => (!value && t('resourceModalWebsiteCrawlModal.urlRequired')) ||
245285
247064
  (!handleCheckUrlIsValid(value) && t('resourceModalWebsiteCrawlModal.invalidUrl')),
245286
247065
  }, children: (field) => {
245287
247066
  var _a, _b, _c;
245288
- return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.urlLabel') }), jsx(Input, { id: field.name, type: "url", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), placeholder: t('resourceModalWebsiteCrawlModal.urlPlaceholder'), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive mb-2 text-sm", role: "alert", children: field.state.meta.errors })) : null] }));
247067
+ return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.urlLabel') }), jsx(Input, { id: field.name, type: "url", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), placeholder: t('resourceModalWebsiteCrawlModal.urlPlaceholder'), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive -mt-1 text-xs", role: "alert", children: field.state.meta.errors })) : null] }));
245289
247068
  } }), jsxs("div", { className: "grid grid-cols-2 gap-4", children: [jsx(form.Field, { name: "crawler_max_depth", validators: {
245290
247069
  onChange: ({ value }) => (!value && t('resourceModalWebsiteCrawlModal.fieldRequired')) ||
245291
247070
  (value < 1 && t('resourceModalWebsiteCrawlModal.mustBeGreaterThanZero')) ||
245292
247071
  (value > 10000 && t('resourceModalWebsiteCrawlModal.mustBeLessThan10000')),
245293
247072
  }, children: (field) => {
245294
247073
  var _a, _b, _c;
245295
- return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.maxCrawlDepthLabel') }), jsx(Input, { id: field.name, type: "number", min: "1", max: "10000", value: field.state.value, onChange: (e) => field.handleChange(Number.parseInt(e.target.value)), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive mb-2 text-sm", role: "alert", children: field.state.meta.errors })) : null] }));
247074
+ return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.maxCrawlDepthLabel') }), jsx(Input, { id: field.name, type: "number", min: "1", max: "10000", value: field.state.value, onChange: (e) => field.handleChange(Number.parseInt(e.target.value)), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive -mt-1 text-xs", role: "alert", children: field.state.meta.errors })) : null] }));
245296
247075
  } }), jsx(form.Field, { name: "crawler_max_pages_limit", validators: {
245297
247076
  onChange: ({ value }) => (!value && t('resourceModalWebsiteCrawlModal.fieldRequired')) ||
245298
247077
  (value < 1 && t('resourceModalWebsiteCrawlModal.mustBeGreaterThanZero')),
245299
247078
  }, children: (field) => {
245300
247079
  var _a, _b, _c;
245301
- return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.maxPagesLimitLabel') }), jsx(Input, { id: field.name, type: "number", min: "1", value: field.state.value, onChange: (e) => field.handleChange(Number.parseInt(e.target.value)), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive mb-2 text-sm", role: "alert", children: field.state.meta.errors })) : null] }));
245302
- } })] }), jsx(form.Field, { name: "crawler_pattern_type", children: (field) => (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: "pattern-type", children: t('resourceModalWebsiteCrawlModal.patternTypeLabel') }), jsxs(Select$1, { value: field.state.value, onValueChange: (value) => field.handleChange(value), children: [jsx(SelectTrigger, { id: "pattern-type", "aria-label": t('resourceModalWebsiteCrawlModal.selectPatternTypeAriaLabel'), children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "glob", children: t('resourceModalWebsiteCrawlModal.globPattern') }), jsx(SelectItem, { value: "regex", children: t('resourceModalWebsiteCrawlModal.regularExpression') })] })] })] })) }), jsx(form.Subscribe, { selector: (state) => [
247080
+ return (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.maxPagesLimitLabel') }), jsx(Input, { id: field.name, type: "number", min: "1", value: field.state.value, onChange: (e) => field.handleChange(Number.parseInt(e.target.value)), "aria-describedby": ((_a = field.state.meta.errors) === null || _a === void 0 ? void 0 : _a.length) ? `${field.name}-error` : undefined, "aria-invalid": !!((_b = field.state.meta.errors) === null || _b === void 0 ? void 0 : _b.length), "aria-required": "true" }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: `${field.name}-error`, className: "text-destructive -mt-1 text-xs", role: "alert", children: field.state.meta.errors })) : null] }));
247081
+ } })] }), jsx(form.Field, { name: "crawler_pattern_type", children: (field) => (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: "pattern-type", children: t('resourceModalWebsiteCrawlModal.patternTypeLabel') }), jsxs(Select$1, { value: field.state.value, onValueChange: (value) => field.handleChange(value), children: [jsx(SelectTrigger, { id: "pattern-type", "aria-label": t('resourceModalWebsiteCrawlModal.selectPatternTypeAriaLabel'), children: jsx(SelectValue, {}) }), jsxs(SelectContent, { children: [jsx(SelectItem, { value: "glob", children: t('resourceModalWebsiteCrawlModal.globPattern') }), jsx(SelectItem, { value: "regex", children: t('resourceModalWebsiteCrawlModal.regularExpression') })] })] })] })) }), jsx(form.Field, { name: "crawler_user_agent", children: (field) => (jsxs("div", { className: "grid gap-2", children: [jsx(Label$2, { htmlFor: field.name, children: t('resourceModalWebsiteCrawlModal.userAgentLabel') }), jsx(Input, { id: field.name, type: "text", value: field.state.value, onChange: (e) => field.handleChange(e.target.value), placeholder: t('resourceModalWebsiteCrawlModal.userAgentPlaceholder'), "aria-describedby": "user-agent-help" }), jsx("p", { id: "user-agent-help", className: "text-muted-foreground -mt-1 text-xs", children: t('resourceModalWebsiteCrawlModal.userAgentHelpText') })] })) }), jsx(form.Subscribe, { selector: (state) => [
245303
247082
  state.values.temp_crawler_match_patterns,
245304
247083
  state.values.crawler_pattern_type,
245305
247084
  ], children: ([tempCrawlerMatchPatterns, crawlerPatternType]) => (jsx(form.Field, { name: "temp_crawler_match_patterns", validators: {
@@ -245330,14 +247109,24 @@ function WebsiteCrawlModal({ resource }) {
245330
247109
  ]);
245331
247110
  field.handleChange('');
245332
247111
  }
245333
- }, disabled: !String(tempCrawlerMatchPatterns).trim(), size: "sm", "aria-label": t('resourceModalWebsiteCrawlModal.addPatternAriaLabel'), children: jsx(Plus, { className: "h-4 w-4" }) })] }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: "pattern-error", className: "text-destructive mb-2 text-sm", role: "alert", children: field.state.meta.errors })) : null, jsx("p", { id: "pattern-help", className: "text-muted-foreground text-sm", children: t('resourceModalWebsiteCrawlModal.patternHelpText') }), jsx("div", { className: "bg-muted/50 flex min-h-[2rem] flex-wrap gap-2 rounded-md border p-2", role: "list", "aria-label": t('resourceModalWebsiteCrawlModal.addedPatternsAriaLabel'), children: crawlerMatchPatterns.length === 0 ? (jsx("span", { className: "text-muted-foreground text-sm", children: t('resourceModalWebsiteCrawlModal.noPatternsAdded') })) : (crawlerMatchPatterns.map((pattern, index) => (jsxs(Badge, { variant: "secondary", className: "flex items-center gap-1", children: [jsx("span", { className: "text-xs", children: pattern }), jsx("button", { type: "button", onClick: () => setCrawlerMatchPatterns(crawlerMatchPatterns.filter((p) => p !== pattern)), className: "hover:bg-destructive/20 ml-1 rounded-full p-0.5", "aria-label": t('resourceModalWebsiteCrawlModal.removePatternAriaLabel', { pattern }), children: jsx(X$1, { className: "h-3 w-3" }) })] }, index)))) })] })] }));
245334
- } })) }), jsx(form.Subscribe, { selector: (state) => [state.isSubmitting], children: ([isSubmitting]) => (jsxs("div", { className: "flex gap-3 pt-4", children: [jsx(Button$1, { type: "submit", className: "w-full bg-gradient-to-r from-[#2563EB] to-[#93C5FD] text-sm text-white hover:text-white hover:opacity-90", disabled: isSubmitting, "aria-describedby": isSubmitting ? 'submitting-status' : undefined, children: isSubmitting
245335
- ? t('resourceModalWebsiteCrawlModal.submitting')
245336
- : t('resourceModalWebsiteCrawlModal.submit') }), isSubmitting && (jsx("div", { id: "submitting-status", className: "sr-only", role: "status", "aria-live": "polite", children: t('resourceModalWebsiteCrawlModal.formBeingSubmitted') }))] })) })] }) }) })] }));
245337
- }
245338
-
245339
- function ResourceModal({ resource, isOpen, onClose }) {
245340
- return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { className: "w-full max-w-md", children: [jsx(DialogHeader, { children: jsx(DialogTitle, { className: "text-muted-foreground text-base", children: resource.name }) }), resource.type === 'github' && jsx(GithubFileUploadModal, {}), resource.type === 'local' && jsx(LocalFileUploadModal, { resource: resource }), resource.type === 'webcrawler' && jsx(WebsiteCrawlModal, { resource: resource }), resource.type === 'url' && jsx(UrlUploadModal, { resource: resource })] }) }));
247112
+ }, disabled: !String(tempCrawlerMatchPatterns).trim(), size: "sm", "aria-label": t('resourceModalWebsiteCrawlModal.addPatternAriaLabel'), children: jsx(Plus, { className: "h-4 w-4" }) })] }), ((_c = field.state.meta.errors) === null || _c === void 0 ? void 0 : _c.length) ? (jsx("p", { id: "pattern-error", className: "text-destructive -mt-1 text-xs", role: "alert", children: field.state.meta.errors })) : null, jsx("p", { id: "pattern-help", className: "text-muted-foreground -mt-1 text-xs", children: t('resourceModalWebsiteCrawlModal.patternHelpText') }), jsx("div", { className: "bg-muted/50 flex min-h-[2rem] flex-wrap gap-2 rounded-md border p-2", role: "list", "aria-label": t('resourceModalWebsiteCrawlModal.addedPatternsAriaLabel'), children: crawlerMatchPatterns.length === 0 ? (jsx("span", { className: "text-muted-foreground text-xs", children: t('resourceModalWebsiteCrawlModal.noPatternsAdded') })) : (crawlerMatchPatterns.map((pattern, index) => (jsxs(Badge, { variant: "secondary", className: "flex items-center gap-1", children: [jsx("span", { className: "text-xs", children: pattern }), jsx("button", { type: "button", onClick: () => setCrawlerMatchPatterns(crawlerMatchPatterns.filter((p) => p !== pattern)), className: "hover:bg-destructive/20 ml-1 rounded-full p-0.5", "aria-label": t('resourceModalWebsiteCrawlModal.removePatternAriaLabel', { pattern }), children: jsx(X$1, { className: "h-3 w-3" }) })] }, index)))) })] })] }));
247113
+ } })) })] })] }), jsx(form.Subscribe, { selector: (state) => [state.isSubmitting], children: ([isSubmitting]) => (jsxs(ResourceDialogFooter, { children: [jsx(Button$1, { type: "submit", className: RESOURCE_SUBMIT_BUTTON_CLASS, disabled: isSubmitting, "aria-describedby": isSubmitting ? 'submitting-status' : undefined, children: isSubmitting
247114
+ ? t('resourceModalWebsiteCrawlModal.submitting')
247115
+ : t('resourceModalWebsiteCrawlModal.submit') }), isSubmitting && (jsx("div", { id: "submitting-status", className: "sr-only", role: "status", "aria-live": "polite", children: t('resourceModalWebsiteCrawlModal.formBeingSubmitted') }))] })) })] }));
247116
+ }
247117
+
247118
+ // `link` types open a cloud picker instead of this dialog; it maps to the
247119
+ // generic Add Resources copy only to keep the record total.
247120
+ const DESCRIPTION_KEYS = {
247121
+ github: 'resourceModalGithubFileUploadModal.description',
247122
+ local: 'resourceModalLocalFileUploadModal.description',
247123
+ url: 'resourceModalUrlUploadModal.description',
247124
+ webcrawler: 'resourceModalWebsiteCrawlModal.description',
247125
+ link: 'datasetsTabAddResourceModal.description',
247126
+ };
247127
+ function ResourceModal({ resource, isOpen, onClose, maxFileSizeMB }) {
247128
+ const t = useT();
247129
+ return (jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(ResourceDialogContent, { title: resource.name, description: t(DESCRIPTION_KEYS[resource.type]), children: [resource.type === 'github' && jsx(GithubFileUploadModal, {}), resource.type === 'local' && (jsx(LocalFileUploadModal, { resource: resource, maxFileSizeMB: maxFileSizeMB })), resource.type === 'webcrawler' && jsx(WebsiteCrawlModal, { resource: resource }), resource.type === 'url' && jsx(UrlUploadModal, { resource: resource })] }) }));
245341
247130
  }
245342
247131
 
245343
247132
  var bundle = {exports: {}};
@@ -245350,6 +247139,8 @@ var bundle = {exports: {}};
245350
247139
  var bundleExports = bundle.exports;
245351
247140
  var GoogleDrivePicker = /*@__PURE__*/getDefaultExportFromCjs(bundleExports);
245352
247141
 
247142
+ /** Least-privilege scope: only files the user picks/creates via the picker. */
247143
+ const GOOGLE_DRIVE_SCOPES = ['https://www.googleapis.com/auth/drive.file'];
245353
247144
  const useGoogleDrivePicker = ({ tenantKey, mentorId }) => {
245354
247145
  const [authToken, setAuthToken] = React__default.useState(null);
245355
247146
  const [driveFiles, setDriveFiles] = React__default.useState([]);
@@ -245424,10 +247215,7 @@ const useGoogleDrivePicker = ({ tenantKey, mentorId }) => {
245424
247215
  refresh_token: 'test-refresh token',
245425
247216
  client_id: credentials.client_id,
245426
247217
  token: authToken === null || authToken === void 0 ? void 0 : authToken.access_token,
245427
- scopes: [
245428
- 'https://www.googleapis.com/auth/drive.metadata.readonly',
245429
- 'https://www.googleapis.com/auth/drive.readonly',
245430
- ],
247218
+ scopes: GOOGLE_DRIVE_SCOPES,
245431
247219
  token_type: authToken === null || authToken === void 0 ? void 0 : authToken.token_type,
245432
247220
  expiry_date: new Date(Date.now() + ((authToken === null || authToken === void 0 ? void 0 : authToken.expires_in) || 0) * 1000).toISOString(),
245433
247221
  },
@@ -245521,10 +247309,7 @@ const useGoogleDrivePicker = ({ tenantKey, mentorId }) => {
245521
247309
  openPicker({
245522
247310
  clientId: credentials.client_id,
245523
247311
  developerKey: credentials.developer_key,
245524
- customScopes: [
245525
- 'https://www.googleapis.com/auth/drive.metadata.readonly',
245526
- 'https://www.googleapis.com/auth/drive.readonly',
245527
- ],
247312
+ customScopes: GOOGLE_DRIVE_SCOPES,
245528
247313
  // @ts-expect-error - token accepts null to force fresh auth per google-drive-picker API
245529
247314
  token: null, // Always start with null token to force fresh auth
245530
247315
  showUploadView: true,
@@ -245612,6 +247397,8 @@ const useGoogleDrivePicker = ({ tenantKey, mentorId }) => {
245612
247397
  forceClosePickerModal,
245613
247398
  pickerError,
245614
247399
  resetPickerState,
247400
+ credentialsLoaded: credentialLoaded,
247401
+ isConfigured: Boolean(credentials.client_id),
245615
247402
  };
245616
247403
  };
245617
247404
 
@@ -245682,6 +247469,7 @@ function ieOnEnd (script, cb) {
245682
247469
 
245683
247470
  var loadScript = /*@__PURE__*/getDefaultExportFromCjs(loadScript$1);
245684
247471
 
247472
+ /** Default file types offered by the Dropbox chooser. Hosts override via `extensions`. */
245685
247473
  const DROPBOX_EXTENSIONS = [
245686
247474
  '.pdf',
245687
247475
  '.doc',
@@ -245693,6 +247481,12 @@ const DROPBOX_EXTENSIONS = [
245693
247481
  '.xlsx',
245694
247482
  '.ppt',
245695
247483
  '.pptx',
247484
+ '.png',
247485
+ '.jpeg',
247486
+ '.jpg',
247487
+ '.mp3',
247488
+ '.wav',
247489
+ '.m4a',
245696
247490
  ];
245697
247491
  const DROPBOX_SDK_URL = 'https://www.dropbox.com/static/api/2/dropins.js';
245698
247492
  const SCRIPT_ID = 'dropboxjs';
@@ -245701,6 +247495,7 @@ const useDropboxPicker = ({ tenantKey, mentorId, cancel = () => { }, linkType =
245701
247495
  const [dropboxReady, setDropboxReady] = useState(false);
245702
247496
  const [openChooser, setOpenChooser] = useState(false);
245703
247497
  const [appKey, setAppKey] = useState(null);
247498
+ const [credentialsLoaded, setCredentialsLoaded] = useState(false);
245704
247499
  // Use RTK Query hooks
245705
247500
  const [getCredentials] = useLazyGetCredentialsQuery();
245706
247501
  const [addTrainingDocument] = useAddTrainingDocumentMutation();
@@ -245723,6 +247518,7 @@ const useDropboxPicker = ({ tenantKey, mentorId, cancel = () => { }, linkType =
245723
247518
  catch (error) {
245724
247519
  console.error(JSON.stringify({ tenant: tenantKey, error }));
245725
247520
  }
247521
+ setCredentialsLoaded(true);
245726
247522
  }
245727
247523
  };
245728
247524
  handleFetchAppKey();
@@ -245820,6 +247616,8 @@ const useDropboxPicker = ({ tenantKey, mentorId, cancel = () => { }, linkType =
245820
247616
  openChooser: onChoose,
245821
247617
  dropboxReady,
245822
247618
  appKey,
247619
+ credentialsLoaded,
247620
+ isConfigured: Boolean(appKey),
245823
247621
  };
245824
247622
  };
245825
247623
 
@@ -245829,6 +247627,7 @@ const useOneDrivePicker = ({ tenantKey, mentorId }) => {
245829
247627
  const [onedriveAppId, setOnedriveAppId] = useState(null);
245830
247628
  const [fullDomain, setFullDomain] = useState(null);
245831
247629
  const [isSDKLoaded, setIsSDKLoaded] = useState(false);
247630
+ const [credentialsLoaded, setCredentialsLoaded] = useState(false);
245832
247631
  // Use RTK Query hooks
245833
247632
  const [getCredentials] = useLazyGetCredentialsQuery();
245834
247633
  const [addTrainingDocument] = useAddTrainingDocumentMutation();
@@ -245943,6 +247742,7 @@ const useOneDrivePicker = ({ tenantKey, mentorId }) => {
245943
247742
  catch (error) {
245944
247743
  console.error(JSON.stringify({ tenant: tenantKey, error }));
245945
247744
  }
247745
+ setCredentialsLoaded(true);
245946
247746
  }
245947
247747
  };
245948
247748
  handleFetchAppId();
@@ -246035,46 +247835,81 @@ const useOneDrivePicker = ({ tenantKey, mentorId }) => {
246035
247835
  pickOneDriveFile,
246036
247836
  onedriveAppId,
246037
247837
  isSDKLoaded,
247838
+ credentialsLoaded,
247839
+ isConfigured: Boolean(onedriveAppId),
246038
247840
  };
246039
247841
  };
246040
247842
 
246041
- function AddResourceModal({ isOpen, onClose, disabledDatasets = [], }) {
247843
+ function AddResourceModal({ isOpen, onClose, disabledDatasets = [], maxFileSizeMB, dropboxExtensions, }) {
246042
247844
  const t = useT();
246043
247845
  const { tenantKey, mentorId } = useAgentSettings();
246044
247846
  const [selectedResource, setSelectedResource] = React__default.useState(null);
246045
- const { openChooser } = useDropboxPicker({
247847
+ const dropbox = useDropboxPicker({
246046
247848
  autoShow: false,
246047
247849
  tenantKey,
246048
247850
  mentorId,
246049
- });
246050
- const { handlePickerOpen, loadGoogleApiScript, isPickerLoaded, forceClosePickerModal, pickerError, } = useGoogleDrivePicker({ tenantKey, mentorId });
246051
- const { pickOneDriveFile } = useOneDrivePicker({ tenantKey, mentorId });
246052
- return (jsxs(Fragment$1, { children: [jsx(Script, { src: "https://apis.google.com/js/api.js", onLoad: loadGoogleApiScript, strategy: "lazyOnload" }), jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsxs(DialogContent, { className: "max-h-[90vh] overflow-y-auto sm:max-w-[800px]", children: [jsx(DialogHeader, { className: "flex flex-row items-center justify-between", children: jsx(DialogTitle, { children: t('datasetsTabAddResourceModal.title') }) }), jsx("div", { className: "pt-4", children: jsxs("div", { children: [jsx("p", { className: "mb-6 text-gray-600", children: t('datasetsTabAddResourceModal.description') }), pickerError && (jsx("div", { className: "mb-4 rounded-lg border border-red-200 bg-red-50 p-4", children: jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-red-800", children: t('datasetsTabAddResourceModal.googleDrivePickerError') }), jsx("p", { className: "mt-1 text-sm text-red-600", children: pickerError })] }), jsx("button", { onClick: forceClosePickerModal, className: "ml-4 rounded bg-red-100 px-3 py-1 text-sm font-medium text-red-800 hover:bg-red-200", children: t('datasetsTabAddResourceModal.forceClosePicker') })] }) })), jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: resourceTypes.map((resource) => (jsxs("button", { className: cn('flex items-center rounded-lg border p-4 transition-colors duration-200 hover:bg-gray-50', resource.isActive && !disabledDatasets.includes(resource.id.toLowerCase())
246053
- ? 'cursor-pointer'
246054
- : 'cursor-not-allowed opacity-50'), onClick: () => {
246055
- if (resource.type !== 'link') {
246056
- setSelectedResource(resource);
246057
- return;
246058
- }
246059
- if (resource.name === 'Google Drive') {
246060
- if (isPickerLoaded) {
246061
- handlePickerOpen();
246062
- }
246063
- else {
246064
- // Show loading state or toast
246065
- console.log('Google Picker is still loading...');
246066
- }
246067
- return;
246068
- }
246069
- if (resource.name === 'Dropbox') {
246070
- openChooser();
246071
- return;
247851
+ extensions: dropboxExtensions,
247852
+ });
247853
+ const { handlePickerOpen, loadGoogleApiScript, isPickerLoaded, forceClosePickerModal, pickerError, credentialsLoaded: googleCredentialsLoaded, isConfigured: googleIsConfigured, } = useGoogleDrivePicker({ tenantKey, mentorId });
247854
+ const oneDrive = useOneDrivePicker({ tenantKey, mentorId });
247855
+ // A cloud provider only works once the tenant has OAuth credentials for it
247856
+ // (GET .../integration-credential/?name=<provider>). Without them the SDK is
247857
+ // handed an empty client id and the click does nothing at all — no popup, no
247858
+ // error — which reads as a broken button. Disable and say why instead.
247859
+ // `loaded` gates on the lookup having finished so the buttons are not
247860
+ // disabled during the in-flight fetch.
247861
+ const cloudProviders = {
247862
+ 'google-drive': {
247863
+ loaded: googleCredentialsLoaded,
247864
+ configured: googleIsConfigured,
247865
+ },
247866
+ onedrive: {
247867
+ loaded: oneDrive.credentialsLoaded,
247868
+ configured: oneDrive.isConfigured,
247869
+ },
247870
+ dropbox: {
247871
+ loaded: dropbox.credentialsLoaded,
247872
+ configured: dropbox.isConfigured,
247873
+ },
247874
+ };
247875
+ return (jsxs(Fragment$1, { children: [jsx(Script, { src: "https://apis.google.com/js/api.js", onLoad: loadGoogleApiScript, strategy: "lazyOnload" }), jsx(Dialog, { open: isOpen, onOpenChange: onClose, children: jsx(ResourceDialogContent, { title: t('datasetsTabAddResourceModal.title'), description: t('datasetsTabAddResourceModal.description'), className: "sm:max-w-[800px]", children: jsxs(ResourceDialogBody, { label: t('datasetsTabAddResourceModal.scrollRegionAriaLabel'), children: [pickerError && (jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4", children: jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { children: [jsx("h4", { className: "text-sm font-medium text-red-800", children: t('datasetsTabAddResourceModal.googleDrivePickerError') }), jsx("p", { className: "mt-1 text-sm text-red-600", children: pickerError })] }), jsx("button", { onClick: forceClosePickerModal, className: "ml-4 rounded bg-red-100 px-3 py-1 text-sm font-medium text-red-800 hover:bg-red-200", children: t('datasetsTabAddResourceModal.forceClosePicker') })] }) })), jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: resourceTypes.map((resource) => {
247876
+ const cloudProvider = cloudProviders[resource.id];
247877
+ const notConfigured = cloudProvider
247878
+ ? cloudProvider.loaded && !cloudProvider.configured
247879
+ : false;
247880
+ const isDisabled = !resource.isActive ||
247881
+ disabledDatasets.includes(resource.id.toLowerCase()) ||
247882
+ notConfigured;
247883
+ return (jsxs("button", { title: notConfigured ? t('datasetsTabAddResourceModal.notConfigured') : undefined, className: cn('flex items-center rounded-lg border p-4 transition-colors duration-200 hover:bg-gray-50', !isDisabled ? 'cursor-pointer' : 'cursor-not-allowed opacity-50'), onClick: () => {
247884
+ if (resource.type !== 'link') {
247885
+ setSelectedResource(resource);
247886
+ return;
247887
+ }
247888
+ if (resource.name === 'Google Drive') {
247889
+ if (isPickerLoaded) {
247890
+ handlePickerOpen();
246072
247891
  }
246073
- if (resource.name === 'Microsoft OneDrive') {
246074
- pickOneDriveFile();
246075
- return;
247892
+ else {
247893
+ // Show loading state or toast
247894
+ console.log('Google Picker is still loading...');
246076
247895
  }
246077
- }, disabled: !resource.isActive || disabledDatasets.includes(resource.id.toLowerCase()), children: [jsx("div", { className: "mr-3 flex h-10 w-10 items-center justify-center rounded-md bg-blue-100", children: resource.icon }), jsx("span", { className: "text-gray-700", children: resource.name })] }, resource.id))) })] }) })] }) }), selectedResource && (jsx(ResourceModal, { resource: selectedResource, isOpen: !!selectedResource, onClose: () => setSelectedResource(null) }))] }));
247896
+ return;
247897
+ }
247898
+ if (resource.name === 'Dropbox') {
247899
+ dropbox.openChooser();
247900
+ return;
247901
+ }
247902
+ if (resource.name === 'Microsoft OneDrive') {
247903
+ oneDrive.pickOneDriveFile();
247904
+ return;
247905
+ }
247906
+ }, disabled: isDisabled, children: [jsx("div", { className: "mr-3 flex h-10 w-10 items-center justify-center rounded-md bg-blue-100", children: resource.icon }), jsxs("span", { className: "flex flex-col items-start text-left", children: [jsx("span", { className: "text-gray-700", children: resource.name }), notConfigured && (
247907
+ // aria-hidden: the same text is already the button's
247908
+ // `title`, so exposing it here too would append it to
247909
+ // the accessible name ("Dropbox Not configured for
247910
+ // this tenant") and break name-based lookups.
247911
+ jsx("span", { "aria-hidden": "true", className: "text-xs text-gray-500", children: t('datasetsTabAddResourceModal.notConfigured') }))] })] }, resource.id));
247912
+ }) })] }) }) }), selectedResource && (jsx(ResourceModal, { resource: selectedResource, isOpen: !!selectedResource, onClose: () => setSelectedResource(null), maxFileSizeMB: maxFileSizeMB }))] }));
246078
247913
  }
246079
247914
 
246080
247915
  // DatasetItemList requires a full labels object; without it the empty-state
@@ -246086,7 +247921,7 @@ const PROJECT_FILES_DATASET_LABELS = resolveDatasetsTabLabels({
246086
247921
  function ProjectFilesModal(props) {
246087
247922
  return (jsx(AgentSettingsProvider, { tenantKey: props.tenantKey, mentorId: props.mentorId, username: props.username, enableRBAC: false, children: jsx(ProjectFilesModalInner, { ...props }) }));
246088
247923
  }
246089
- function ProjectFilesModalInner({ isOpen, onClose }) {
247924
+ function ProjectFilesModalInner({ isOpen, onClose, disabledResourceTypes, maxUploadSizeMb, dropboxExtensions, }) {
246090
247925
  var _a;
246091
247926
  const t = useT();
246092
247927
  const [showAddResourceModal, setShowAddResourceModal] = React__default.useState(false);
@@ -246101,7 +247936,7 @@ function ProjectFilesModalInner({ isOpen, onClose }) {
246101
247936
  // @ts-expect-error - Type mismatch between RetrieverDocumentEmbedding[] and Dataset[], id property type difference
246102
247937
  , {
246103
247938
  // @ts-expect-error - Type mismatch between RetrieverDocumentEmbedding[] and Dataset[], id property type difference
246104
- datasets: (_a = datasets === null || datasets === void 0 ? void 0 : datasets.results) !== null && _a !== void 0 ? _a : [], labels: PROJECT_FILES_DATASET_LABELS })] })) }) }) }), jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isDatasetsFetching || isDatasetsLoading }), jsx(AddResourceModal, { isOpen: showAddResourceModal, onClose: () => closeAddResourceModal(), keepParentOpen: true }), isModalOpen && FreeTrialDialog && (jsx(FreeTrialDialog, { isOpen: isModalOpen, onClose: closeModal }))] }) })] }) }) }));
247939
+ datasets: (_a = datasets === null || datasets === void 0 ? void 0 : datasets.results) !== null && _a !== void 0 ? _a : [], labels: PROJECT_FILES_DATASET_LABELS })] })) }) }) }), jsx(IblPagination, { currentPage: currentPage, totalPages: totalPages, onPageChange: handlePageChange, disabled: isDatasetsFetching || isDatasetsLoading }), jsx(AddResourceModal, { isOpen: showAddResourceModal, onClose: () => closeAddResourceModal(), keepParentOpen: true, disabledDatasets: disabledResourceTypes, maxFileSizeMB: maxUploadSizeMb, dropboxExtensions: dropboxExtensions }), isModalOpen && FreeTrialDialog && (jsx(FreeTrialDialog, { isOpen: isModalOpen, onClose: closeModal }))] }) })] }) }) }));
246105
247940
  }
246106
247941
 
246107
247942
  var projectFilesModal = /*#__PURE__*/Object.freeze({
@@ -248735,5 +250570,5 @@ var trainOrDeleteModal = /*#__PURE__*/Object.freeze({
248735
250570
  TrainOrDeleteModal: TrainOrDeleteModal
248736
250571
  });
248737
250572
 
248738
- export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DEFAULT_USER_IDENTITY_ORDER, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DocumentSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, LlmModelBadge, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RetrievedDocumentsButton, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolCallIndicator, ToolDialogs, TopBanner, TranscriptFileCards, TranscriptRollupBadges, TranscriptTurnDetails, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, UserProfileDialog, UserProfileLink, Version, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, conversationDocuments, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatFileSize, formatLatencySeconds, formatResult, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getFriendlyToolName, getLLMModelDisplayName, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getQueryLabel, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasDistinctName, hasOnboardingContent, humanizeAuditToken, humanizeFieldName, inBrowserPrint, inIframe, inferProviderKeysFromModel, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isEmailLike, isFileReference, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, resolveUserIdentity, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, summarizeTranscriptTurns, toFileReference, toRetrievedDocuments, toToolCallInfos, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmDisplayName, useLlmProviderCatalogue, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
250573
+ export { ACCESS_COURSE_LABEL, ALL_CATEGORIES, AccessTimeHeatmap, AccessiblePaginate, AddMentorToProjectModal, AddSkillDialog, Admin, AdvancedTab, AgentConfigPrompts, AgentScopeLink, AgentSkills, AlertsTab, AnalyticsAgentPicker, AnalyticsAreaChart, AnalyticsAuditLogStats, AnalyticsBarChart, AnalyticsControlBar, AnalyticsCourseDetail, AnalyticsCourses, AnalyticsFinancialStats, AnalyticsGlobalFiltersProvider, AnalyticsLayout, AnalyticsLlmUsageStats, AnalyticsMemoryStats, AnalyticsMonetizationStats, AnalyticsNotice, AnalyticsOverview, AnalyticsPlatformAuditLog, AnalyticsProgramDetail, AnalyticsPrograms, AnalyticsReportDownload, AnalyticsReports, AnalyticsScopeProvider, AnalyticsSettingsProvider, AnalyticsTopicsStats, AnalyticsTraces, AnalyticsTranscriptsStats, AnalyticsUsersStats, AppleRestrictionModal, ApplyWizard, AuditLogChangesView, AuditLogJsonView, BUY_NOW_LABEL, BillingTab, CATALOGS, CHART_ORDINAL_RAMP, CHART_SERIES_COLORS, CHART_SINGLE_SERIES_COLOR, CREATE_AGENT_LABELS, CUA_DRIVER_REPO_URL, CUA_DRIVER_TAURI_COMMANDS, CUA_DRIVER_TAURI_EVENTS, CardInfoIcon, CategorizedDropdownMenu, ChartCardWrapper, ChartFiltersProvider, ChartLoading, ChartTooltipCard, ChatContext, ChatPrivacyToggle, ChatProvider, CompanyDialog, ConnectorManagementDialog, CopyButtonIcon, CourseAccessGuard, CourseCardSkeleton, CourseContentLoading, CourseOutline, CourseOutlineContext, CourseOutlineDrawer, CreateAgentForm, CreateAgentModal, CreateProjectModal, CreateWorkflowModal, CredentialBox, CredentialMiniBoxSkeleton, CredentialsList, CreditBalance, CustomDateRangePicker, CustomTooltip, DEFAULT_COWORK_REQUIRED_SIZE_GB, DEFAULT_LOCALE, DEFAULT_USER_IDENTITY_ORDER, DefaultEmptyBox, DeleteProjectModal, DeleteWorkflowModal, DiscoverContentCard, DiscoverFacetsFilter, DiscoverFilterDrawer, DocumentSidebar, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ENROLL_NOW_COURSE_STARTING_SOON_LABEL, ENROLL_NOW_LABEL, EditAlertDialog, EducationBox, EducationDialog, EducationTab, EdxIframeContext, EmptyStats, ExperienceBox, ExperienceDialog, ExperienceTab, FacetFilterContext, Footer, GradebookTab, GroupsFilterDropdown, HostPortChipInput, INVITATION_ONLY_LABEL, InstitutionDialog, IntegrationsTab, InviteUserContent, InviteUserDialog, InvitedUsersDialog, LOCAL_LLM_CHANGED_EVENT, LOCAL_MODELS, LlmModelBadge, Loader, LocalLLMTab, LocalModelsContent, LoginButton, MACOS_PERMISSIONS_COMMANDS, MEMORY_PAGE_SIZE, Markdown, MemoryAdminTab, MinimumMentorAlert, MonetizationTab, NotificationDisplay, NotificationDropdown, ONBOARDING_FIELD_TYPES, ONBOARDING_SECTORS, OnboardingField, OnboardingShell, OnboardingWizard, PLATFORM_AUDIT_DEEP_PAGING_THRESHOLD, PLATFORM_AUDIT_PAGE_SIZE, PaywallModal, PlacementExam, PlacementExamsDropdown, PlatformAuditLogDetailSheet, PlatformNavbar, PlatformNavbarSearch, Profile, ProfileTimeChart, ProgressDots, ProjectActionButtons, ProjectFilesModal, ProjectInstructionsModal, ProjectLandingPage, ProjectMentorsList, ProjectsPage, REQUEST_ACCESS_COURSE_STARTING_SOON_LABEL, REQUEST_ACCESS_LABEL, RankedBars, RenameProjectModal, ResumeBox, ResumeTab, RetrievedDocumentsButton, RichTextEditor, SERVICE_COLOR_ORDER, SUPPORTED_LOCALES, SandboxConfig, SearchableMultiSelect, SendNotificationDialog, SignupButton, SkeletonActivityStatBox, SkeletonAddSkillsLoading, SkeletonCreatePathwaySearchList, SkeletonDiscoverFilterBox, SkeletonEducationBox, SkeletonMultiplier, SkeletonPathwayBox, SkeletonProfileInfoCard, SkeletonSkillBox, SkillBox, SkillDetailModal, SkillLeaderboardChart, SkillsBox, SkillsList, SlashSkillPicker, SourceChip, Spinner, StatCard, StepHeader, SummerNoteTextEditor, TAURI_COMMANDS, TAURI_EVENTS, TRACES_PAGE_SIZE, TenantSwitcher, TimeFilter, TimeTrackingProvider, TimedExam, ToolCallIndicator, ToolDialogs, TopBanner, TranscriptFileCards, TranscriptRollupBadges, TranscriptTurnDetails, TrendSparkline, USER_ONBOARDING_FORM_SLUG, USER_ONBOARDING_FORM_VERSION, USER_ONBOARDING_SUBMISSION_SLUG, UpgradePackageModal, UserAvatar, UserOnboardingWizard, UserProfileDialog, UserProfileLink, Version, VirtualMachineAdminTab, VirtualMachineNetworkSection, WebContainersI18nProvider, WorkflowSidebar, addBookmarksTab, areChartFiltersEqual, availablePlacementExams, buildOnboardingAgentPath, buildUserOnboardingFormMetadata, buildUserOnboardingSubmissionMetadata, coerceOnboardingAgent, coerceOnboardingAgents, coerceUserOnboardingForm, colorForRank, colorForSeries, conversationDocuments, emptyUserOnboardingForm, findLastResumeBlock, findOnboardingAgent, findSequentialParent, flattenVerticalBlocks, formatAuditTimestamp, formatCompactNumber, formatFileSize, formatLatencySeconds, formatResult, formatTimeZoneLabel, getCatalog, getFirstAvailableUnit, getFriendlyToolName, getLLMModelDisplayName, getLocalLLMModel, getLocalLLMToolSupport, getNextUnitIframe, getOrg, getParentBlockById, getParentsInfosFromSublessonId, getPreviousUnitIframe, getQueryLabel, getRandomCourseImage, getSectorById, getTenant, getUnitToIframe, getUserId, getUserName, granularityForFilter, hasDistinctName, hasOnboardingContent, humanizeAuditToken, humanizeFieldName, inBrowserPrint, inIframe, inferProviderKeysFromModel, initialCuaDriverInstallState, initialModelDownloadState, isCoworkEnabled, isEmailLike, isFileReference, isLlmUsageForbidden, isLlmUsageUnavailable, isLocalLLMEnabled, isPlatformNavbarCreditBalanceVisible, isSlashCommandToken, isTauriApp, isValidLocale, isValidTimeZone, llmUsageDateArgs, components as markdownComponents, modelSupportsCowork, onboardingPrimaryButtonClass, onboardingSecondaryButtonClass, readUserOnboardingForm, readUserOnboardingSubmission, readUserOnboardingSubmissions, resolveAuditTimeZone, resolveCreateAgentLabels, resolveUserIdentity, sanitizeCss, setCoworkEnabled, setLocalLLMEnabled, setLocalLLMModel, setLocalLLMToolSupport, smallestCoworkModel, summarizeTranscriptTurns, toFileReference, toRetrievedDocuments, toToolCallInfos, useAnalyticsGlobalFilters, useAnalyticsScope, useAnalyticsSettings, useAuditLog, useCatalogSearch, useChartFilters, useChatPrivacy, useChatState, useCourseDetail, useCourseMetadata, useCourses, useCredentialsSchemaLogos, useCuaDriver, useDiscover, useEdxIframe, useFinancial, useIframeMessageHandler, useLlmDisplayName, useLlmProviderCatalogue, useLlmTraces, useLlmUsage, useLocalStorage, useMemory, useModelDownload, useMonetization, useOverview, usePlatformAuditLabels, usePlatformAuditLog, useProfileActivityStats, useProfileCredentials, useProfilePathways, useProfilePrograms, useProfileSkills, useProfileTimeSpent, usePrograms, useReports, useScopedMentorDbId, useScopedMentorId, useSelectedLocalModel, useSlashSkillPicker, useSlashSkills, useT, useTauri, useTimeTracking, useTopics, useTranscripts, useUserCourses, useUserMetadata, useUsers, useWebContainersI18n, useWebContainersLocale };
248739
250574
  //# sourceMappingURL=index.esm.js.map