@jini-ai/ui 0.5.5 → 0.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -2
- package/dist/core/theme/validation.js +40 -38
- package/dist/features/a2ui/renderer.js +15 -13
- package/dist/features/about/react/components/AboutTab.js +64 -34
- package/dist/features/about/rules.d.ts +2 -2
- package/dist/features/about/rules.js +156 -129
- package/dist/features/admin-widgets/components/ImagePreviewModal.d.ts +4 -3
- package/dist/features/admin-widgets/components/ImagePreviewModal.js +6 -10
- package/dist/features/admin-widgets/components/InfoTip.d.ts +3 -2
- package/dist/features/admin-widgets/components/InfoTip.hooks.js +2 -0
- package/dist/features/admin-widgets/components/InfoTip.js +34 -7
- package/dist/features/admin-widgets/components/InfoTip.rules.d.ts +13 -0
- package/dist/features/admin-widgets/components/InfoTip.rules.js +43 -10
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.d.ts +30 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.d.ts +40 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.hooks.js +128 -0
- package/dist/features/admin-widgets/components/Lightbox/Lightbox.js +18 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.d.ts +14 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.js +12 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.d.ts +20 -0
- package/dist/features/admin-widgets/components/MiddleTruncate.rules.js +21 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.d.ts +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.d.ts +16 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.hooks.js +45 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.js +18 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.d.ts +32 -0
- package/dist/features/admin-widgets/components/ScrollJump/ScrollJump.rules.js +26 -0
- package/dist/features/admin-widgets/components/SuggestInput/SuggestInput.hooks.js +29 -14
- package/dist/features/admin-widgets/index.d.ts +14 -2
- package/dist/features/admin-widgets/index.js +7 -1
- package/dist/features/admin-widgets/styles/admin-widgets.css +1 -0
- package/dist/features/asset-grid/react/components/AssetCard.js +7 -1
- package/dist/features/asset-grid/react/components/AssetGrid.d.ts +1 -1
- package/dist/features/asset-grid/react/components/AssetGrid.js +38 -18
- package/dist/features/asset-grid/react/components/useAssetGridFilters.d.ts +21 -0
- package/dist/features/asset-grid/react/components/useAssetGridFilters.js +19 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridKeyboardShortcuts.js +16 -11
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.d.ts +2 -0
- package/dist/features/asset-grid/react/hooks/useAssetGridLiveUpdates.js +35 -11
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.d.ts +6 -6
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowser.js +10 -129
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.d.ts +50 -0
- package/dist/features/asset-tree-browser/react/components/AssetTreeBrowserParts.js +95 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.d.ts +56 -0
- package/dist/features/asset-tree-browser/react/components/useAssetTreeBrowserController.js +80 -0
- package/dist/features/asset-tree-browser/rules.js +40 -22
- package/dist/features/browser-chrome/rules.js +74 -70
- package/dist/features/charts/browser.js +11 -5
- package/dist/features/charts/react.js +2 -2
- package/dist/features/charts/table.js +2 -2
- package/dist/features/command-palette/react/hooks/useCommandPalette.js +3 -9
- package/dist/features/connectors/ConnectorsBrowser.d.ts +4 -4
- package/dist/features/connectors/ConnectorsBrowser.js +26 -14
- package/dist/features/connectors/components/ConnectorCard.js +67 -38
- package/dist/features/connectors/components/ConnectorDetailDrawer.js +62 -31
- package/dist/features/connectors/components/ConnectorGrid.js +11 -6
- package/dist/features/connectors/hooks/useConnectorAuthorization.js +6 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.d.ts +2 -0
- package/dist/features/connectors/hooks/useConnectorCatalog.js +32 -13
- package/dist/features/connectors/hooks/useWiredConnectorsBrowser.d.ts +3 -3
- package/dist/features/connectors/rules.d.ts +8 -1
- package/dist/features/connectors/rules.js +42 -20
- package/dist/features/connectors/types.d.ts +11 -0
- package/dist/features/execution/constants.d.ts +4 -10
- package/dist/features/execution/constants.js +9 -94
- package/dist/features/execution/react/components/ByokProviderForm.d.ts +2 -4
- package/dist/features/execution/react/components/ByokProviderForm.js +140 -43
- package/dist/features/execution/react/components/ExecutionTab.d.ts +1 -2
- package/dist/features/execution/react/components/ExecutionTab.js +52 -30
- package/dist/features/execution/react/components/LocalCliAgentCard.js +12 -3
- package/dist/features/execution/react/components/LocalCliAgentList.js +14 -3
- package/dist/features/execution/react/components/LocalCliAgentSettings.js +14 -3
- package/dist/features/execution/react/hooks/useLocalCliAgentCard.js +54 -36
- package/dist/features/execution/react/hooks/useReasoningControl.js +114 -78
- package/dist/features/execution/rules.d.ts +2 -2
- package/dist/features/execution/rules.js +31 -15
- package/dist/features/execution/types.d.ts +4 -42
- package/dist/features/execution/types.js +0 -17
- package/dist/features/external-mcp/react/components/ExternalMcpTab.d.ts +6 -2
- package/dist/features/external-mcp/react/components/ExternalMcpTab.js +9 -2
- package/dist/features/file-dropzone/react/components/FileDropzone.js +24 -25
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.d.ts +2 -2
- package/dist/features/file-dropzone/react/components/FileDropzoneLightbox.js +35 -30
- package/dist/features/file-dropzone/react/components/FileDropzoneThumbnailGrid.js +30 -24
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.d.ts +14 -0
- package/dist/features/file-dropzone/react/components/FileDropzoneZone.js +26 -0
- package/dist/features/html-editor/canvas-content-wrapper.js +13 -13
- package/dist/features/html-editor/canvas-embed-placeholders.js +46 -37
- package/dist/features/html-editor/css.js +76 -69
- package/dist/features/html-editor/editor-blocks.js +47 -33
- package/dist/features/html-editor/editor-serialization.js +6 -5
- package/dist/features/html-editor/protected-elements.js +39 -28
- package/dist/features/html-editor/react/hooks/useInteractiveHtmlEditor.js +4 -4
- package/dist/features/html-editor/source-splice.js +51 -30
- package/dist/features/i18n/dictionaries/settings-dialog.ja.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-CN.js +1 -0
- package/dist/features/i18n/dictionaries/settings-dialog.zh-TW.js +1 -0
- package/dist/features/i18n/locale.js +20 -16
- package/dist/features/iframe-pool/react/dom-sync.js +8 -6
- package/dist/features/integrations/react/components/CodexInstallToggleButton.js +19 -4
- package/dist/features/integrations/react/components/IntegrationsTab.js +60 -14
- package/dist/features/interactive-ui/interactive-ui.css +1 -1
- package/dist/features/interactive-ui/providers/shadcn/data-table.js +10 -7
- package/dist/features/lexical-rich-text-editor/mention-parser.js +60 -57
- package/dist/features/lexical-rich-text-editor/react/hooks/useKeyboardCommands.js +33 -38
- package/dist/features/lexical-rich-text-editor/rules.js +87 -74
- package/dist/features/lexical-rich-text-editor/serialize.js +27 -25
- package/dist/features/mcp-ui/secret-card.js +111 -61
- package/dist/features/mcp-ui/surfaces/confirmation.js +39 -21
- package/dist/features/mcp-ui/surfaces/document.d.ts +1 -1
- package/dist/features/mcp-ui/surfaces/document.js +10 -2
- package/dist/features/mcp-ui/surfaces/fields.js +30 -50
- package/dist/features/mcp-ui/surfaces/form.d.ts +6 -0
- package/dist/features/mcp-ui/surfaces/form.js +43 -21
- package/dist/features/mcp-ui/surfaces/outcome.js +30 -20
- package/dist/features/mcp-ui/surfaces/text-input.js +33 -21
- package/dist/features/mcp-ui/surfaces/tokens.d.ts +6 -1
- package/dist/features/mcp-ui/surfaces/tokens.js +5 -2
- package/dist/features/media-providers/constants.d.ts +1 -0
- package/dist/features/media-providers/constants.js +2 -81
- package/dist/features/media-providers/index.d.ts +1 -1
- package/dist/features/media-providers/ports.d.ts +2 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.d.ts +22 -0
- package/dist/features/media-providers/react/components/MediaProviderCard.js +50 -0
- package/dist/features/media-providers/react/components/MediaProvidersTab.d.ts +5 -6
- package/dist/features/media-providers/react/components/MediaProvidersTab.js +40 -72
- package/dist/features/media-providers/react/components/media-providers-tab-labels.d.ts +11 -0
- package/dist/features/media-providers/react/components/media-providers-tab-labels.js +57 -0
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.d.ts +4 -2
- package/dist/features/media-providers/react/hooks/useMediaProvidersTab.js +37 -23
- package/dist/features/media-providers/rules.js +17 -10
- package/dist/features/media-providers/types.d.ts +4 -22
- package/dist/features/media-providers/types.js +3 -8
- package/dist/features/memory/formatters.js +127 -92
- package/dist/features/memory/react/components/MemoryConnectedPanel.js +92 -36
- package/dist/features/memory/react/components/MemoryList.js +37 -18
- package/dist/features/memory/react/hooks/useMemoryExtractions.store.js +42 -36
- package/dist/features/notifications/react/components/NotificationsTab.js +28 -28
- package/dist/features/observability/long-task.d.ts +0 -12
- package/dist/features/observability/long-task.js +15 -19
- package/dist/features/observability/resource-error.d.ts +0 -10
- package/dist/features/observability/resource-error.js +18 -13
- package/dist/features/observability/stuck-run.js +10 -6
- package/dist/features/observability/telemetry-url.d.ts +6 -0
- package/dist/features/observability/telemetry-url.js +16 -0
- package/dist/features/observability/white-screen.js +12 -24
- package/dist/features/panel-kit/fetch-query/adapter.react.d.ts +2 -2
- package/dist/features/panel-kit/fetch-query/adapter.react.js +4 -2
- package/dist/features/panel-kit/fetch-query/adapter.tanstack.js +46 -33
- package/dist/features/panel-kit/helpers/standing-draft-local-backup.js +3 -1
- package/dist/features/panel-kit/hooks/use-settings-slice.hooks.js +3 -1
- package/dist/features/privacy/react/components/PrivacyTab.js +24 -13
- package/dist/features/progress-card/components/ProgressCard.js +7 -3
- package/dist/features/progress-card/reference-adapters.js +59 -54
- package/dist/features/project-locations/react/components/ProjectLocationsTab.js +24 -15
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.d.ts +8 -0
- package/dist/features/project-locations/react/hooks/useProjectLocationsTab.js +31 -23
- package/dist/features/resource-dashboard/react/components/ResourceBoard.js +6 -47
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.d.ts +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceBoardView.js +3 -6
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.d.ts +15 -0
- package/dist/features/resource-dashboard/react/components/ResourceBoardViewParts.js +35 -0
- package/dist/features/resource-dashboard/react/components/ResourceCard.js +5 -16
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.d.ts +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceCardParts.js +34 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowList.js +3 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListItem.js +2 -2
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListItemParts.js +11 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.d.ts +1 -1
- package/dist/features/resource-dashboard/react/components/ResourceRowListView.js +4 -4
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.d.ts +10 -0
- package/dist/features/resource-dashboard/react/components/ResourceRowListViewParts.js +20 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.d.ts +48 -0
- package/dist/features/resource-dashboard/react/components/resourceBoardProps.js +70 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.d.ts +19 -0
- package/dist/features/resource-dashboard/react/components/resourceRowListProps.js +14 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.d.ts +16 -0
- package/dist/features/resource-dashboard/react/components/translatedErrorLabelProps.js +13 -0
- package/dist/features/resource-dashboard/rules.js +2 -8
- package/dist/features/sketch-editor/dom.js +150 -99
- package/dist/features/sketch-editor/react/components/SketchEditor.js +26 -15
- package/dist/features/skills/react/components/SkillDraftForm.js +13 -19
- package/dist/features/skills/react/components/SkillDraftForm.labels.d.ts +31 -0
- package/dist/features/skills/react/components/SkillDraftForm.labels.js +54 -0
- package/dist/features/skills/react/components/SkillRow.js +38 -18
- package/dist/features/skills/react/components/SkillRow.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillRow.labels.js +18 -0
- package/dist/features/skills/react/components/SkillsTab.js +24 -14
- package/dist/features/skills/react/components/SkillsTab.labels.d.ts +7 -0
- package/dist/features/skills/react/components/SkillsTab.labels.js +11 -0
- package/dist/features/skills/rules.js +32 -23
- package/dist/features/source-config-list/agent-handles.js +2 -1
- package/dist/features/source-config-list/dependencies.js +53 -49
- package/dist/features/source-config-list/index.d.ts +3 -3
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigAddForm.js +38 -7
- package/dist/features/source-config-list/react/components/SourceConfigField.js +69 -25
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.d.ts +6 -5
- package/dist/features/source-config-list/react/components/SourceConfigItemCard.js +76 -20
- package/dist/features/source-config-list/react/components/SourceConfigListView.d.ts +5 -3
- package/dist/features/source-config-list/react/components/SourceConfigListView.js +33 -3
- package/dist/features/source-config-list/react/components/SourceConfigTestControl.js +17 -1
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.d.ts +11 -0
- package/dist/features/source-config-list/react/components/SourceConfigTrustOptions.js +12 -0
- package/dist/features/source-config-list/types.d.ts +14 -0
- package/dist/features/tab-launcher-menu/react/components/TabLauncherMenu.js +25 -1
- package/dist/features/tab-launcher-menu/react/hooks/useTabLauncherMenu.js +15 -16
- package/dist/features/tab-strip/react/components/TabBar.js +8 -1
- package/dist/features/tabbed-dialog/react/components/TabbedDialog.js +8 -30
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.d.ts +15 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogChrome.js +8 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.d.ts +12 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogHeader.js +6 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.d.ts +27 -0
- package/dist/features/tabbed-dialog/react/components/TabbedDialogSidebar.js +12 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.d.ts +43 -0
- package/dist/features/tabbed-dialog/react/components/tabbedDialogView.js +59 -0
- package/dist/features/version-manager/dependencies.d.ts +3 -1
- package/dist/features/version-manager/dependencies.js +3 -2
- package/dist/features/version-manager/react/components/VersionManagerModal.js +10 -6
- package/dist/features/version-manager/react/hooks/useVersionManager.js +7 -4
- package/dist/features/viewer-shell/react/components/CommentSidePanel.js +103 -50
- package/dist/features/viewer-shell/react/components/MarkdownSplitPane.js +28 -8
- package/dist/features/viewer-shell/react/hooks/useCommentReorder.js +22 -16
- package/dist/features/viewer-shell/rules.js +19 -23
- package/dist/react/components/CustomSelect.d.ts +38 -1
- package/dist/react/components/CustomSelect.js +94 -38
- package/dist/react/components/OnboardingDropdown.js +20 -8
- package/dist/react/components/RemixIcon.stylesheet.js +3 -1
- package/dist/react/components/Toast.js +13 -2
- package/dist/react/components/TooltipLayer.js +5 -14
- package/dist/react/components/WorkingDirPicker.js +78 -50
- package/dist/react/components/remixicon-font-recovery.js +38 -30
- package/dist/react/components/select-popover.d.ts +8 -6
- package/dist/react/components/select-popover.js +33 -29
- package/dist/react/mcp-ui/McpUiHost.d.ts +1 -1
- package/dist/react/mcp-ui/McpUiHost.js +10 -11
- package/dist/react/mcp-ui/host-message-source.d.ts +14 -2
- package/dist/react/mcp-ui/host-message-source.js +15 -5
- package/dist/react/mcp-ui/mcp-ui-host-frame.d.ts +41 -0
- package/dist/react/mcp-ui/mcp-ui-host-frame.js +39 -0
- package/dist/renderers/annotation-canvas/react/components/AnnotationCanvas.js +97 -69
- package/dist/renderers/preview-modal-shell/react/components/PreviewModalShell.js +59 -30
- package/dist/renderers/react/components/ArtifactView.js +9 -6
- package/dist/renderers/srcdoc/build.d.ts +4 -1
- package/dist/renderers/srcdoc/build.js +62 -48
- package/dist/styles/admin.css +29 -14
- package/dist/styles/lightbox.css +142 -0
- package/dist/styles/variables.css +128 -0
- package/dist/theme/apply.js +9 -5
- package/dist/utils/auto-open-file.d.ts +2 -1
- package/dist/utils/auto-open-file.js +69 -85
- package/dist/utils/markdown-scroll-sync.js +108 -87
- package/dist/utils/polygon-selection.js +25 -26
- package/dist/utils/smooth-scroll-to-top.d.ts +1 -1
- package/dist/utils/smooth-scroll-to-top.js +2 -32
- package/dist/utils/sse.js +26 -26
- package/dist/utils/unit-bezier.d.ts +7 -0
- package/dist/utils/unit-bezier.js +38 -0
- package/package.json +9 -12
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.d.ts +0 -13
- package/dist/features/admin-widgets/components/ImagePreviewModal.hooks.js +0 -46
|
@@ -78,4 +78,15 @@ export interface ConnectorAuthResultEvent {
|
|
|
78
78
|
result: 'success' | 'failed' | 'cancelled';
|
|
79
79
|
errorCode?: string;
|
|
80
80
|
}
|
|
81
|
+
/** Derived connect/disconnect state shared by the connector card and detail drawer. */
|
|
82
|
+
export interface ConnectorActionFlags {
|
|
83
|
+
isConnecting: boolean;
|
|
84
|
+
isDisconnecting: boolean;
|
|
85
|
+
isConnected: boolean;
|
|
86
|
+
isAuthorizationPending: boolean;
|
|
87
|
+
canConnect: boolean;
|
|
88
|
+
canDisconnect: boolean;
|
|
89
|
+
/** The pending authorization's redirect URL — only set while authorization is pending. */
|
|
90
|
+
redirectUrl: string | undefined;
|
|
91
|
+
}
|
|
81
92
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { AgentCliEnvFieldSpec, ApiProtocol, ProviderPreset } from './types.js';
|
|
2
|
-
/**
|
|
3
|
-
* the operator has not typed a base URL. Origin: `DEFAULT_BASE_URL_BY_PROTOCOL`. */
|
|
2
|
+
/** @deprecated Endpoints come from host presets. Blank means the operator must supply an endpoint. */
|
|
4
3
|
export declare const DEFAULT_BASE_URL_BY_PROTOCOL: Readonly<Record<ApiProtocol, string>>;
|
|
5
4
|
/** Display order and English labels for the protocol row. Values are i18n
|
|
6
5
|
* keys (this package's convention is "the English string is the key"). */
|
|
@@ -18,8 +17,8 @@ export declare const CUSTOM_MODEL_SENTINEL = "__custom__";
|
|
|
18
17
|
/**
|
|
19
18
|
* The shortest string `apiKeyFormatWarning` will let pass without remark.
|
|
20
19
|
*
|
|
21
|
-
* Chosen from evidence rather than taste. The shortest real key shape
|
|
22
|
-
*
|
|
20
|
+
* Chosen from evidence rather than taste. The shortest real key shape in the integrations
|
|
21
|
+
* starter presets is Azure OpenAI's bare 32-character hex string;
|
|
23
22
|
* Google's is `AIza` + 35 = 39, OpenAI's `sk-` + 48 = 51, OpenRouter's `sk-or-v1-` + 64 = 73, and
|
|
24
23
|
* Anthropic's longer again. 20 therefore sits 12 characters below that floor, so no credential this
|
|
25
24
|
* catalog knows about can trip it, while it still catches the failure the check exists for: the
|
|
@@ -45,12 +44,7 @@ export declare const API_KEY_CROSS_VENDOR_WARNING = "This looks like an API key
|
|
|
45
44
|
/** Shown when a key is shorter than {@link MIN_PLAUSIBLE_API_KEY_LENGTH}. Carries no placeholders:
|
|
46
45
|
* the length floor is global, so there is no vendor to name. */
|
|
47
46
|
export declare const API_KEY_TOO_SHORT_WARNING = "This is shorter than any provider API key \u2014 check that the whole key was pasted. You can still save and test it.";
|
|
48
|
-
/**
|
|
49
|
-
* A small, vendor-shaped starting catalog. Deliberately NOT the origin's full
|
|
50
|
-
* ~30-entry list: that list is hand-curated product content that goes stale,
|
|
51
|
-
* and this package's rule for tables like this (see `LanguageTab`) is that the
|
|
52
|
-
* host owns them. Pass your own `presets` to replace this wholesale.
|
|
53
|
-
*/
|
|
47
|
+
/** @deprecated Supply host presets. The former starter set is exported by integrations/media-providers/catalog. */
|
|
54
48
|
export declare const DEFAULT_PROVIDER_PRESETS: readonly ProviderPreset[];
|
|
55
49
|
/**
|
|
56
50
|
* One-line descriptions for the code-agent CLIs this package knows about,
|
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
/** Fallback endpoint per protocol, used when a host supplies no preset and
|
|
3
|
-
* the operator has not typed a base URL. Origin: `DEFAULT_BASE_URL_BY_PROTOCOL`. */
|
|
1
|
+
/** @deprecated Endpoints come from host presets. Blank means the operator must supply an endpoint. */
|
|
4
2
|
export const DEFAULT_BASE_URL_BY_PROTOCOL = {
|
|
5
|
-
anthropic:
|
|
6
|
-
openai:
|
|
7
|
-
azure:
|
|
8
|
-
google:
|
|
3
|
+
anthropic: '',
|
|
4
|
+
openai: '',
|
|
5
|
+
azure: '',
|
|
6
|
+
google: '',
|
|
9
7
|
};
|
|
10
8
|
/** Display order and English labels for the protocol row. Values are i18n
|
|
11
9
|
* keys (this package's convention is "the English string is the key"). */
|
|
@@ -25,8 +23,8 @@ export const CUSTOM_MODEL_SENTINEL = '__custom__';
|
|
|
25
23
|
/**
|
|
26
24
|
* The shortest string `apiKeyFormatWarning` will let pass without remark.
|
|
27
25
|
*
|
|
28
|
-
* Chosen from evidence rather than taste. The shortest real key shape
|
|
29
|
-
*
|
|
26
|
+
* Chosen from evidence rather than taste. The shortest real key shape in the integrations
|
|
27
|
+
* starter presets is Azure OpenAI's bare 32-character hex string;
|
|
30
28
|
* Google's is `AIza` + 35 = 39, OpenAI's `sk-` + 48 = 51, OpenRouter's `sk-or-v1-` + 64 = 73, and
|
|
31
29
|
* Anthropic's longer again. 20 therefore sits 12 characters below that floor, so no credential this
|
|
32
30
|
* catalog knows about can trip it, while it still catches the failure the check exists for: the
|
|
@@ -52,91 +50,8 @@ export const API_KEY_CROSS_VENDOR_WARNING = 'This looks like an API key for {ven
|
|
|
52
50
|
/** Shown when a key is shorter than {@link MIN_PLAUSIBLE_API_KEY_LENGTH}. Carries no placeholders:
|
|
53
51
|
* the length floor is global, so there is no vendor to name. */
|
|
54
52
|
export const API_KEY_TOO_SHORT_WARNING = 'This is shorter than any provider API key — check that the whole key was pasted. You can still save and test it.';
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
* ~30-entry list: that list is hand-curated product content that goes stale,
|
|
58
|
-
* and this package's rule for tables like this (see `LanguageTab`) is that the
|
|
59
|
-
* host owns them. Pass your own `presets` to replace this wholesale.
|
|
60
|
-
*/
|
|
61
|
-
export const DEFAULT_PROVIDER_PRESETS = [
|
|
62
|
-
{
|
|
63
|
-
id: 'anthropic',
|
|
64
|
-
title: 'Anthropic',
|
|
65
|
-
protocol: 'anthropic',
|
|
66
|
-
baseUrl: VENDOR_API_BASE_URLS.anthropic,
|
|
67
|
-
preferredModels: ['claude-sonnet-5', 'claude-opus-5', 'claude-haiku-4-5'],
|
|
68
|
-
kind: 'protocol',
|
|
69
|
-
apiKeyPrefix: 'sk-ant-',
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: 'openai',
|
|
73
|
-
title: 'OpenAI',
|
|
74
|
-
protocol: 'openai',
|
|
75
|
-
baseUrl: VENDOR_API_BASE_URLS.openai,
|
|
76
|
-
// `gpt-4o`/`gpt-4o-mini`/`o3`/`o4-mini` are stale: OpenAI's GPT-5.6 family
|
|
77
|
-
// (released 2026-07-09, per the API changelog) replaced the old
|
|
78
|
-
// flagship/mini/nano naming with named tiers — sol/terra/luna. `sol` is
|
|
79
|
-
// the one the changelog and pricing docs call out as "recommended for
|
|
80
|
-
// production API usage", so it leads (see `preferredModels[0]` default
|
|
81
|
-
// in `rules.ts`) rather than the newer, pricier `gpt-6-astra` flagship
|
|
82
|
-
// (released 2026-09-03) — picking that as the silent default would jump
|
|
83
|
-
// an existing user's cost without them choosing it.
|
|
84
|
-
preferredModels: ['gpt-5.6-sol', 'gpt-5.6-terra', 'gpt-5.6-luna'],
|
|
85
|
-
kind: 'protocol',
|
|
86
|
-
apiKeyPrefix: 'sk-',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
id: 'azure-openai',
|
|
90
|
-
title: 'Azure OpenAI',
|
|
91
|
-
protocol: 'azure',
|
|
92
|
-
baseUrl: VENDOR_API_BASE_URLS.azure,
|
|
93
|
-
preferredModels: [],
|
|
94
|
-
kind: 'protocol',
|
|
95
|
-
// No `apiKeyPrefix` deliberately: an Azure OpenAI key is a bare hex string with no vendor
|
|
96
|
-
// prefix, so any value here would be a guess — and a wrong prefix claim does not merely stay
|
|
97
|
-
// silent, it lets some OTHER vendor's valid key be announced as Azure's. Its 32 hex characters
|
|
98
|
-
// are also the shortest real key shape in this catalog, which is what sets
|
|
99
|
-
// `MIN_PLAUSIBLE_API_KEY_LENGTH`.
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
id: 'google-gemini',
|
|
103
|
-
title: 'Google Gemini',
|
|
104
|
-
protocol: 'google',
|
|
105
|
-
baseUrl: VENDOR_API_BASE_URLS.google,
|
|
106
|
-
// Ordered newest-first: `rules.ts`'s `saved?.model ?? preset.preferredModels[0]` makes entry [0]
|
|
107
|
-
// the default for anyone who has never picked a model, so a stale head silently pins new users
|
|
108
|
-
// to an old generation. Reported live: the field defaulted to `gemini-2.5-flash` while the
|
|
109
|
-
// operator was actually running `gemini-3.6-flash`. This list is only a FALLBACK — the form
|
|
110
|
-
// prefers live `listModels` discovery and drops back here when that call fails.
|
|
111
|
-
preferredModels: ['gemini-3.6-flash', 'gemini-2.5-flash', 'gemini-2.5-pro'],
|
|
112
|
-
kind: 'protocol',
|
|
113
|
-
// `AIza` + 35 characters is the classic Google key shape, and it is recorded here so a key of
|
|
114
|
-
// this shape is recognised as GOOGLE'S when it turns up in some other provider's field.
|
|
115
|
-
// Emphatically not a test that a Google key must look like this: an operator pasted a real,
|
|
116
|
-
// working Gemini key in a newer shape and the old allowlist told them it was not a Google key.
|
|
117
|
-
apiKeyPrefix: 'AIza',
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
id: 'openrouter',
|
|
121
|
-
title: 'OpenRouter',
|
|
122
|
-
protocol: 'openai',
|
|
123
|
-
baseUrl: VENDOR_API_BASE_URLS.openrouter,
|
|
124
|
-
preferredModels: ['anthropic/claude-sonnet-5', 'google/gemini-2.5-pro', 'openai/gpt-5.6-sol'],
|
|
125
|
-
kind: 'gateway',
|
|
126
|
-
// Longer than OpenAI's `sk-`, and deliberately so: `apiKeyFormatWarning` resolves a key to the
|
|
127
|
-
// MOST specific prefix that claims it, so `sk-or-v1-…` is attributed here rather than to OpenAI.
|
|
128
|
-
apiKeyPrefix: 'sk-or-',
|
|
129
|
-
},
|
|
130
|
-
{
|
|
131
|
-
id: 'ollama',
|
|
132
|
-
title: 'Ollama',
|
|
133
|
-
protocol: 'openai',
|
|
134
|
-
baseUrl: VENDOR_API_BASE_URLS.ollama,
|
|
135
|
-
preferredModels: [],
|
|
136
|
-
kind: 'gateway',
|
|
137
|
-
requiresApiKey: false,
|
|
138
|
-
},
|
|
139
|
-
];
|
|
53
|
+
/** @deprecated Supply host presets. The former starter set is exported by integrations/media-providers/catalog. */
|
|
54
|
+
export const DEFAULT_PROVIDER_PRESETS = [];
|
|
140
55
|
/**
|
|
141
56
|
* One-line descriptions for the code-agent CLIs this package knows about,
|
|
142
57
|
* keyed by the agent id `ExecutionPort` reports. Origin:
|
|
@@ -10,10 +10,8 @@ export interface ByokProviderFormProps {
|
|
|
10
10
|
* as ANOTHER row's, so `apiKeyFormatWarning` can say whose it looks like. Never used to validate
|
|
11
11
|
* the selected preset's own key — see that function for why that direction is forbidden.
|
|
12
12
|
*
|
|
13
|
-
* Defaults to
|
|
14
|
-
*
|
|
15
|
-
* than losing the check silently. A host with its own catalog should pass it, exactly as
|
|
16
|
-
* `ExecutionTab` does.
|
|
13
|
+
* Defaults to no presets. Hosts rendering this card standalone must pass their catalog
|
|
14
|
+
* to enable cross-provider prefix hints, exactly as `ExecutionTab` does.
|
|
17
15
|
*/
|
|
18
16
|
presets?: readonly ProviderPreset[];
|
|
19
17
|
/** Live model-discovery result. `'ok'` suggestions win over the preset's
|
|
@@ -2,20 +2,22 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useId, useState } from 'react';
|
|
3
3
|
import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
|
|
4
4
|
import { useT } from '../../../i18n/index.js';
|
|
5
|
-
import { CUSTOM_MODEL_SENTINEL
|
|
5
|
+
import { CUSTOM_MODEL_SENTINEL } from '../../constants.js';
|
|
6
6
|
import { apiKeyFormatWarning, parseMaxTokens, presetRequiresApiKey, shouldShowCustomModelInput, showsBaseUrlField, } from '../../rules.js';
|
|
7
7
|
import { SearchableModelSelect } from './SearchableModelSelect.js';
|
|
8
8
|
import { useByokFieldValidation } from '../hooks/use-byok-field-validation.hooks.js';
|
|
9
|
+
/** `' is-missing'` when `field` is a required field the operator still has to fill. */
|
|
10
|
+
function missingClass(validation, field) {
|
|
11
|
+
return validation.isMissing({ field }) ? ' is-missing' : '';
|
|
12
|
+
}
|
|
9
13
|
/**
|
|
10
14
|
* The BYOK credential card: API key, base URL, optional token cap, and model.
|
|
11
15
|
* Origin: `components/byok/*` (`ByokKeyField`, `ByokProviderBaseUrl`,
|
|
12
16
|
* `ByokModelField`, `ByokConnectionTestControl`) — already factored out
|
|
13
17
|
* upstream, so this is assembly plus the origin's required-field marking.
|
|
14
18
|
*/
|
|
15
|
-
export function ByokProviderForm({ config, onConfigChange, preset, presets =
|
|
19
|
+
export function ByokProviderForm({ config, onConfigChange, preset, presets = [], modelDiscovery, connectionTest, onTestConnection, canTestConnection = true, apiKeyFooter, formFooter, modelFooter, apiKeyStoredExternally = false, apiKeyPlaceholder, agentHandle, apiKeyActions, collapseAdvanced = false, deferMissingFeedback = false, }) {
|
|
16
20
|
const t = useT();
|
|
17
|
-
const keyInputId = useId();
|
|
18
|
-
const KeyField = apiKeyActions ? 'div' : 'label';
|
|
19
21
|
const [revealKey, setRevealKey] = useState(false);
|
|
20
22
|
/** Whether the operator explicitly picked "Custom…" in the model picker — distinct from "the
|
|
21
23
|
* saved model simply isn't in the live list". Same UI-local toggle, for the same reason, as
|
|
@@ -36,12 +38,53 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
|
|
|
36
38
|
useEffect(() => {
|
|
37
39
|
setRevealKey(false);
|
|
38
40
|
}, [config.providerId]);
|
|
39
|
-
const
|
|
41
|
+
const validation = useByokFieldValidation({ config, preset, apiKeyStoredExternally }, { deferMissingFeedback });
|
|
42
|
+
const patch = (next) => onConfigChange({ ...config, ...next });
|
|
43
|
+
const part = { config, patch, preset, agentHandle, validation };
|
|
44
|
+
const connectionFields = _jsx(ByokConnectionFields, { ...part });
|
|
45
|
+
return (_jsxs("div", { className: "jini-byok-card", children: [_jsx("div", { className: "jini-byok-card-head", children: _jsx("h4", { className: "jini-byok-card-title", children: preset ? preset.title : t('Custom endpoint') }) }), presetRequiresApiKey(preset) ? (_jsx(ByokApiKeyField, { ...part, presets: presets, revealKey: revealKey, onToggleReveal: () => setRevealKey((shown) => !shown), apiKeyFooter: apiKeyFooter, apiKeyPlaceholder: apiKeyPlaceholder, apiKeyActions: apiKeyActions })) : null, collapseAdvanced ? null : connectionFields, _jsx(ByokModelRow, { ...part, modelDiscovery: modelDiscovery, explicitCustomModel: explicitCustomModel, setExplicitCustomModel: setExplicitCustomModel, connectionTest: connectionTest, onTestConnection: onTestConnection, canTestConnection: canTestConnection }), _jsx(ByokDiscoveryError, { modelDiscovery: modelDiscovery }), _jsx(ByokConnectionResult, { connectionTest: connectionTest, canTestConnection: canTestConnection }), _jsx(ByokSlot, { className: "jini-byok-model-footer", children: modelFooter }), collapseAdvanced ? _jsx(ByokAdvanced, { validation: validation, children: connectionFields }) : null, _jsx(ByokSlot, { className: "jini-byok-form-footer", children: formFooter })] }));
|
|
46
|
+
}
|
|
47
|
+
/** Base URL (unless the preset has a fixed origin) and Max tokens, on one wrapping row. */
|
|
48
|
+
function ByokConnectionFields({ config, patch, preset, agentHandle, validation }) {
|
|
49
|
+
const t = useT();
|
|
50
|
+
return (_jsx(_Fragment, { children: _jsxs("div", { className: "jini-byok-field-row", children: [showsBaseUrlField(preset) ? (_jsxs("label", { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'baseUrl' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Base URL'), preset?.baseUrl ? null : _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), _jsx("input", { className: 'jini-input' + missingClass(validation, 'baseUrl'), type: "url", inputMode: "url", spellCheck: false, value: config.baseUrl, onChange: (event) => patch({ baseUrl: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'base-url', role: 'field', label: t('Base URL') }) }) }), _jsx("span", { className: 'jini-field-hint' + (validation.baseUrlInvalid ? ' is-error' : ''), children: validation.baseUrlInvalid
|
|
51
|
+
? t('Enter an absolute http(s) URL.')
|
|
52
|
+
: t('Default endpoint. Usually no need to change this.') })] })) : null, _jsxs("label", { className: "jini-field", children: [_jsx("span", { className: "jini-field-label", children: t('Max tokens (optional)') }), _jsx("input", { className: "jini-input", type: "number", min: 1, step: 1, value: config.maxTokens ?? '', onChange: (event) => patch({ maxTokens: parseMaxTokens(event.target.value) }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'max-tokens', role: 'field', label: t('Max tokens (optional)') }) }) }), _jsx("span", { className: "jini-field-hint", children: t('Cap on the response length. Leave blank to use the model default.') })] })] }) }));
|
|
53
|
+
}
|
|
54
|
+
/** The API-key field (with Show/Hide, host actions and the advisory format warning) plus the
|
|
55
|
+
* host's `apiKeyFooter` slot beneath it. */
|
|
56
|
+
function ByokApiKeyField({ config, patch, preset, agentHandle, validation, presets, revealKey, onToggleReveal, apiKeyFooter, apiKeyPlaceholder, apiKeyActions, }) {
|
|
57
|
+
const t = useT();
|
|
58
|
+
const keyInputId = useId();
|
|
59
|
+
const KeyField = apiKeyActions ? 'div' : 'label';
|
|
40
60
|
/** Deliberately NOT folded into `missing` above: that set drives `is-missing` styling and the
|
|
41
61
|
* Test-connection disable, and a wrong-SHAPED key must never reach either. */
|
|
42
62
|
const keyFormatWarning = apiKeyFormatWarning(config, preset, presets);
|
|
43
|
-
|
|
44
|
-
|
|
63
|
+
return (_jsxs(_Fragment, { children: [_jsxs(KeyField, { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'apiKey' }), children: [_jsxs("span", { className: "jini-field-label", children: [_jsx(ByokApiKeyLabel, { keyInputId: apiKeyActions ? keyInputId : undefined }), preset?.apiKeyConsoleUrl ? (_jsx("a", { className: "jini-field-link", href: preset.apiKeyConsoleUrl, target: "_blank", rel: "noreferrer noopener", children: t('Get key') })) : null] }), _jsxs("span", { className: "jini-field-input-row", children: [_jsx("input", { ...(apiKeyActions ? { id: keyInputId } : {}), className: 'jini-input' + missingClass(validation, 'apiKey'), type: revealKey ? 'text' : 'password',
|
|
64
|
+
/* `new-password`, NOT `"off"` — Chrome deliberately ignores `off` on credential-shaped
|
|
65
|
+
fields (a long-standing intentional decision, not a bug). `off` here is what let the
|
|
66
|
+
reported autofill through: Chrome filled this field with the admin's own saved
|
|
67
|
+
PASSWORD, the form sent it, and the provider answered with its own "API key not
|
|
68
|
+
valid" — a true message about a string the operator never typed. `new-password` is
|
|
69
|
+
the value Chrome/Safari/Firefox actually honor for "this is not a saved-login
|
|
70
|
+
field", which suppresses both the credential dropdown and the silent fill.
|
|
71
|
+
|
|
72
|
+
Known trade-off, not fixed here: Chrome may now offer to GENERATE a password on
|
|
73
|
+
this field — a suggestion popup, not a silently wrong value. */
|
|
74
|
+
autoComplete: "new-password", spellCheck: false, placeholder: apiKeyPlaceholder, value: config.apiKey, onChange: (event) => patch({ apiKey: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'api-key', role: 'field', label: t('API key') }) }) }), _jsx("button", { type: "button", className: "jini-input-affix-btn", "aria-pressed": revealKey, onClick: onToggleReveal, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'reveal-key', role: 'button', label: t('Reveal API key') }) }), children: revealKey ? t('Hide') : t('Show') })] }), apiKeyActions ? _jsx("span", { className: "jini-byok-key-actions", children: apiKeyActions }) : null, _jsx("span", { className: "jini-field-hint", children: t('Stored only by this host.') }), keyFormatWarning ? (_jsx("span", { className: "jini-field-hint is-error", role: "status", children: t(keyFormatWarning.message, keyFormatWarning.vars) })) : null] }), _jsx(ByokSlot, { className: "jini-byok-key-footer", children: apiKeyFooter })] }));
|
|
75
|
+
}
|
|
76
|
+
/** "API key *". With host key actions the field is a `<div>`, so the name gets its own `<label>`
|
|
77
|
+
* pointing at the input by id; otherwise the whole field is already the label. */
|
|
78
|
+
function ByokApiKeyLabel({ keyInputId }) {
|
|
79
|
+
const t = useT();
|
|
80
|
+
if (keyInputId) {
|
|
81
|
+
return (_jsxs("label", { className: "jini-field-label", htmlFor: keyInputId, children: [_jsx("span", { children: t('API key') }), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }));
|
|
82
|
+
}
|
|
83
|
+
return (_jsxs(_Fragment, { children: [t('API key'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }));
|
|
84
|
+
}
|
|
85
|
+
/** The Model field (live picker, free-text box, or both) with "Test connection" beside it. */
|
|
86
|
+
function ByokModelRow({ config, patch, preset, agentHandle, validation, modelDiscovery, explicitCustomModel, setExplicitCustomModel, connectionTest, onTestConnection, canTestConnection, }) {
|
|
87
|
+
const t = useT();
|
|
45
88
|
/**
|
|
46
89
|
* Live discovery turns the Model field from a text box into a real picker.
|
|
47
90
|
*
|
|
@@ -66,41 +109,95 @@ export function ByokProviderForm({ config, onConfigChange, preset, presets = DEF
|
|
|
66
109
|
// opens the box for an explicit "Custom…" pick AND for a value that simply isn't in the list, so
|
|
67
110
|
// a hydrated or hand-typed id is never silently replaced by whatever sorts first.
|
|
68
111
|
const customModelActive = showModelPicker && shouldShowCustomModelInput(config.model, liveModels, explicitCustomModel);
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
const modelClassName = 'jini-input' + missingClass(validation, 'model');
|
|
113
|
+
return (
|
|
114
|
+
// "Test connection" sits BESIDE the Model control (owner ruling, 2026-09-02), not on a row of
|
|
115
|
+
// its own underneath it. It probes the endpoint the Model field belongs to, and a row's
|
|
116
|
+
// distance made it read as unrelated to any particular field.
|
|
117
|
+
//
|
|
118
|
+
// A SIBLING of the label, never a child: a `<button>` inside a `<label>` makes every click on
|
|
119
|
+
// the button also focus and activate the labelled control — here, opening the model picker —
|
|
120
|
+
// and nesting interactive controls in a label is an accessibility defect regardless. Same rule
|
|
121
|
+
// `apiKeyFooter` documents.
|
|
122
|
+
//
|
|
123
|
+
// Wrapping, and `align-items: flex-end`, so the button sits on the input's own line at a
|
|
124
|
+
// comfortable width and drops below the field at a narrow one. Both status lines
|
|
125
|
+
// (discovery error, connection result) are deliberately OUTSIDE this row: inside it they would
|
|
126
|
+
// either sit beside the button or, as label children, push it out of line the moment either
|
|
127
|
+
// appeared.
|
|
128
|
+
_jsxs("div", { className: "jini-byok-model-row", children: [_jsxs("label", { className: "jini-field", onBlurCapture: () => validation.markTouched({ field: 'model' }), children: [_jsxs("span", { className: "jini-field-label", children: [t('Model'), _jsx("span", { className: "jini-field-required", "aria-hidden": "true", children: "*" })] }), showModelPicker ? (_jsx(ByokLiveModelSelect, { className: modelClassName, agentHandle: agentHandle, value: customModelActive ? CUSTOM_MODEL_SENTINEL : config.model, liveModels: liveModels, patch: patch, setExplicitCustomModel: setExplicitCustomModel })) : null, !showModelPicker || customModelActive ? (_jsx(ByokModelTextInput, { className: modelClassName, value: config.model, patch: patch, agentHandle: agentHandle, suggestions: showModelPicker ? [] : modelSuggestions(modelDiscovery, preset) })) : null] }), _jsx(ByokTestConnectionButton, { agentHandle: agentHandle, validation: validation, connectionTest: connectionTest, onTestConnection: onTestConnection, canTestConnection: canTestConnection })] }));
|
|
129
|
+
}
|
|
130
|
+
/** The probe's RESULT keeps its own full-width line. Beside the button it would have made the
|
|
131
|
+
* Model row's width depend on the length of a provider's error message. Renders nothing until
|
|
132
|
+
* the probe has finished. */
|
|
133
|
+
function ByokConnectionResult({ connectionTest, canTestConnection }) {
|
|
134
|
+
const t = useT();
|
|
135
|
+
if (!canTestConnection)
|
|
136
|
+
return null;
|
|
137
|
+
if (connectionTest.status !== 'ok' && connectionTest.status !== 'error')
|
|
138
|
+
return null;
|
|
139
|
+
return (_jsx("div", { className: "jini-byok-test-row", children: _jsx("span", { className: `jini-byok-test-status is-${connectionTest.status}`, role: connectionTest.status === 'error' ? 'alert' : 'status', children: connectionTest.status === 'ok'
|
|
140
|
+
? (connectionTest.message !== undefined ? t(connectionTest.message) : t('Connection succeeded'))
|
|
141
|
+
: t(connectionTest.message) }) }));
|
|
142
|
+
}
|
|
143
|
+
/** Discovery failure, as an inline hint below the Model row. Renders nothing otherwise. */
|
|
144
|
+
function ByokDiscoveryError({ modelDiscovery }) {
|
|
145
|
+
const t = useT();
|
|
146
|
+
if (modelDiscovery.status !== 'error')
|
|
147
|
+
return null;
|
|
148
|
+
return (
|
|
149
|
+
// Non-blocking (the field above stays editable, with the preset's static
|
|
150
|
+
// suggestions) but never silent — a discovery failure is an operator-actionable
|
|
151
|
+
// fact (bad key, wrong base URL, unreachable endpoint), not "no models exist".
|
|
152
|
+
//
|
|
153
|
+
// Moved out of the Model `<label>` when the probe button moved up beside it: as a label
|
|
154
|
+
// child it grew the field and pushed the bottom-aligned button off the input's line every
|
|
155
|
+
// time discovery failed. Same element, same `.jini-field-hint.is-error[role="status"]`
|
|
156
|
+
// identity host suites locate it by — only its parent changed.
|
|
157
|
+
_jsx("span", { className: "jini-field-hint is-error", role: "status", children: t('Could not load live models: {message}', { message: t(modelDiscovery.message) }) }));
|
|
158
|
+
}
|
|
159
|
+
/** A host slot's wrapper `<div>`, rendered only when the host supplied content for it. */
|
|
160
|
+
function ByokSlot({ className, children }) {
|
|
161
|
+
return children ? _jsx("div", { className: className, children: children }) : null;
|
|
162
|
+
}
|
|
163
|
+
/** The collapsed "Advanced" disclosure for Base URL and Max tokens. Opens on its own while Base URL
|
|
164
|
+
* is invalid or missing, so a required-field problem is never hidden. */
|
|
165
|
+
function ByokAdvanced({ validation, children }) {
|
|
166
|
+
const t = useT();
|
|
167
|
+
return (_jsxs("details", { className: "jini-byok-advanced", open: validation.baseUrlInvalid || validation.missing.has('baseUrl') || undefined, children: [_jsx("summary", { children: t('Advanced') }), children] }));
|
|
168
|
+
}
|
|
169
|
+
/** The live-model picker. "Custom…" opens the free-text box beside it; any real pick closes it. */
|
|
170
|
+
function ByokLiveModelSelect({ className, agentHandle, value, liveModels, patch, setExplicitCustomModel, }) {
|
|
171
|
+
const t = useT();
|
|
172
|
+
return (_jsx(SearchableModelSelect, { className: className, ariaLabel: t('Model'), searchPlaceholder: t('Search models'), testId: "jini-byok-model-select", searchInputTestId: "jini-byok-model-search", ...(agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action: 'model' }) } : {}), value: value, models: liveModels.map((model) => ({ id: model, label: model })), additionalOptions: [{ value: CUSTOM_MODEL_SENTINEL, label: t('Custom…') }], onChange: (next) => {
|
|
173
|
+
if (next === CUSTOM_MODEL_SENTINEL) {
|
|
174
|
+
// Open the free-text box WITHOUT clearing `config.model`. Blanking it here would
|
|
175
|
+
// discard a working model the moment someone opened the picker to look at it.
|
|
176
|
+
setExplicitCustomModel(true);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
setExplicitCustomModel(false);
|
|
180
|
+
patch({ model: next });
|
|
181
|
+
} }));
|
|
182
|
+
}
|
|
183
|
+
/** "Test connection", beside the Model field. Disabled while a probe runs or a required field is
|
|
184
|
+
* blank; absent for hosts with no probe endpoint. */
|
|
185
|
+
function ByokTestConnectionButton({ agentHandle, validation, connectionTest, onTestConnection, canTestConnection, }) {
|
|
186
|
+
const t = useT();
|
|
187
|
+
if (!canTestConnection)
|
|
188
|
+
return null;
|
|
189
|
+
return (_jsx("button", { type: "button", className: "jini-btn jini-byok-test-btn", disabled: connectionTest.status === 'testing' || validation.missing.size > 0, onClick: onTestConnection, ...agentHandleProps({}, { base: agentHandle, ...({ action: 'test-connection', role: 'button', label: t('Test connection') }) }), children: connectionTest.status === 'testing' ? t('Testing…') : t('Test connection') }));
|
|
190
|
+
}
|
|
191
|
+
/** Autocomplete hints for the free-text Model box: the live list when discovery succeeded, else the
|
|
192
|
+
* preset's static `preferredModels`. */
|
|
193
|
+
function modelSuggestions(modelDiscovery, preset) {
|
|
194
|
+
return modelDiscovery.status === 'ok' ? modelDiscovery.models : (preset?.preferredModels ?? []);
|
|
195
|
+
}
|
|
196
|
+
/** The free-text Model box, with a `<datalist>` autocomplete when there are suggestions. */
|
|
197
|
+
function ByokModelTextInput({ className, value, patch, agentHandle, suggestions, }) {
|
|
198
|
+
const t = useT();
|
|
199
|
+
const modelListId = 'jini-byok-model-options';
|
|
200
|
+
const showDatalist = suggestions.length > 0;
|
|
201
|
+
return (_jsxs(_Fragment, { children: [_jsx("input", { className: className, list: showDatalist ? modelListId : undefined, spellCheck: false, value: value, onChange: (event) => patch({ model: event.target.value }), ...agentHandleProps({}, { base: agentHandle, ...({ action: 'model', role: 'field', label: t('Model') }) }) }), showDatalist ? (_jsx("datalist", { id: modelListId, children: suggestions.map((model) => (_jsx("option", { value: model }, model))) })) : null] }));
|
|
105
202
|
}
|
|
106
203
|
//# sourceMappingURL=ByokProviderForm.js.map
|
|
@@ -5,8 +5,7 @@ export interface ExecutionTabProps {
|
|
|
5
5
|
config: ExecutionConfig;
|
|
6
6
|
onConfigChange: (config: ExecutionConfig) => void;
|
|
7
7
|
port: ExecutionPort;
|
|
8
|
-
/**
|
|
9
|
-
* with its own provider list passes it here rather than editing ours. */
|
|
8
|
+
/** Host-owned endpoint catalog. Omitted means manual endpoint entry. */
|
|
10
9
|
presets?: readonly ProviderPreset[];
|
|
11
10
|
/** Per-agent CLI env-var catalog (proxy URLs, custom config dirs, a
|
|
12
11
|
* binary-path override). Defaults to this package's starter set for the
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useEffect } from 'react';
|
|
3
3
|
import { agentHandleProps, agentSubHandle } from '@jini-ai/agentic';
|
|
4
4
|
import { useT } from '../../../i18n/index.js';
|
|
5
|
-
import { DEFAULT_AGENT_CLI_ENV_FIELDS
|
|
5
|
+
import { DEFAULT_AGENT_CLI_ENV_FIELDS } from '../../constants.js';
|
|
6
6
|
import { groupPresets, isProviderConfigured, nextConfigForAgentCliEnvChange, nextConfigForAgentModel, nextConfigForAgentReasoning, nextConfigForAgentSelect, nextConfigForModeChange, nextConfigForPresetSelect, resolveSelectedPreset, selectedAgentModel, } from '../../rules.js';
|
|
7
7
|
import { useExecutionTab } from '../hooks/useExecutionTab.js';
|
|
8
8
|
import { ByokProviderForm } from './ByokProviderForm.js';
|
|
@@ -20,13 +20,14 @@ import { ProviderChipGroup } from './ProviderChipGroup.js';
|
|
|
20
20
|
* wallet — balance polling, top-up, sign-in coachmarks — is deliberately not
|
|
21
21
|
* ported; it is product-bound, not generic.
|
|
22
22
|
*/
|
|
23
|
-
export function ExecutionTab({ config, onConfigChange, port, presets =
|
|
23
|
+
export function ExecutionTab({ config, onConfigChange, port, presets = [], cliEnvFields = DEFAULT_AGENT_CLI_ENV_FIELDS, localCliUnavailableReason, renderAgentIcon, localCliScopeLabel, autoDetect = true, ariaLabel, apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder, canDiscoverModels = true, describeProbeError, agentHandle, }) {
|
|
24
24
|
const t = useT();
|
|
25
|
-
const
|
|
25
|
+
const tab = useExecutionTab({
|
|
26
26
|
port,
|
|
27
27
|
autoDetect: autoDetect && config.mode === 'local-cli',
|
|
28
28
|
...(describeProbeError ? { describeProbeError } : {}),
|
|
29
29
|
});
|
|
30
|
+
const { loadModels, resetModelDiscovery } = tab;
|
|
30
31
|
const selectedPreset = resolveSelectedPreset(presets, config.byok);
|
|
31
32
|
/**
|
|
32
33
|
* Whether a key EXISTS, never what it is. Keyed on the boolean specifically so the effect below
|
|
@@ -68,39 +69,60 @@ export function ExecutionTab({ config, onConfigChange, port, presets = DEFAULT_P
|
|
|
68
69
|
if (!canDiscoverModels)
|
|
69
70
|
resetModelDiscovery();
|
|
70
71
|
}, [canDiscoverModels, resetModelDiscovery]);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
// No "local-cli while unavailable" guard here: `ExecutionModeSwitch` renders that tab `disabled`
|
|
73
|
+
// whenever `localCliUnavailableReason` is set, and React never dispatches `onClick` for a disabled
|
|
74
|
+
// button, so `setMode('local-cli')` cannot be reached in that state.
|
|
74
75
|
const setMode = (mode) => {
|
|
75
|
-
if (mode === 'local-cli' && localCliDisabled)
|
|
76
|
-
return;
|
|
77
76
|
onConfigChange(nextConfigForModeChange(config, mode));
|
|
78
77
|
};
|
|
79
|
-
const selectPreset = (preset) => onConfigChange({ ...config, byok: nextConfigForPresetSelect(config.byok, preset) });
|
|
80
78
|
const modes = [
|
|
81
79
|
{ id: 'local-cli', title: t('Local CLI'), meta: t('Run an agent installed on this machine') },
|
|
82
80
|
{ id: 'byok', title: t('BYOK'), meta: t('Use your own API credentials') },
|
|
83
81
|
];
|
|
84
|
-
return (_jsxs("div", { className: "jini-settings-execution", "aria-label": ariaLabel ?? t('Execution mode'), children: [_jsx(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
82
|
+
return (_jsxs("div", { className: "jini-settings-execution", "aria-label": ariaLabel ?? t('Execution mode'), children: [_jsx(ExecutionModeSwitch, { modes: modes, activeMode: config.mode, localCliUnavailableReason: localCliUnavailableReason, onSelect: setMode, agentHandle: agentHandle }), config.mode === 'local-cli' ? (_jsx(LocalCliPane, { config: config, onConfigChange: onConfigChange, tab: tab, cliEnvFields: cliEnvFields, renderAgentIcon: renderAgentIcon, localCliScopeLabel: localCliScopeLabel, agentHandle: agentHandle })) : (_jsx(ByokPane, { config: config, onConfigChange: onConfigChange, presets: presets, selectedPreset: selectedPreset, tab: tab, canDiscoverModels: canDiscoverModels, canListModels: typeof port.listModels === 'function', formProps: { apiKeyFooter, formFooter, apiKeyStoredExternally, apiKeyPlaceholder }, agentHandle: agentHandle }))] }));
|
|
83
|
+
}
|
|
84
|
+
/** The Local CLI ↔ BYOK segmented control. Local CLI is disabled, with the host's reason as its
|
|
85
|
+
* tooltip and caption, while the host says it is unavailable. */
|
|
86
|
+
function ExecutionModeSwitch({ modes, activeMode, localCliUnavailableReason, onSelect, agentHandle }) {
|
|
87
|
+
const t = useT();
|
|
88
|
+
const localCliDisabled = Boolean(localCliUnavailableReason);
|
|
89
|
+
return (_jsx("div", { className: "jini-seg-control", role: "tablist", "aria-label": t('Execution mode'), style: { '--seg-cols': modes.length }, children: modes.map((mode) => {
|
|
90
|
+
const disabled = mode.id === 'local-cli' && localCliDisabled;
|
|
91
|
+
return (_jsxs("button", { type: "button", role: "tab", "aria-selected": activeMode === mode.id, disabled: disabled, title: disabled ? localCliUnavailableReason : undefined, className: 'jini-seg-btn' + (activeMode === mode.id ? ' active' : ''), onClick: () => onSelect(mode.id), ...agentHandleProps({}, { base: agentHandle, ...({ action: `mode-${mode.id}`, role: 'button', label: mode.title }) }), children: [_jsx("span", { className: "jini-seg-title", children: mode.title }), _jsx("span", { className: "jini-seg-meta", children: disabled ? localCliUnavailableReason : mode.meta })] }, mode.id));
|
|
92
|
+
}) }));
|
|
93
|
+
}
|
|
94
|
+
/** The BYOK half of the tab: protocol and gateway chip rows over the credential card. */
|
|
95
|
+
function ByokPane({ config, onConfigChange, presets, selectedPreset, tab, canDiscoverModels, canListModels, formProps, agentHandle, }) {
|
|
96
|
+
const t = useT();
|
|
97
|
+
const { protocols, gateways } = groupPresets(presets);
|
|
98
|
+
const configuredPresetIds = new Set(presets.filter((preset) => isProviderConfigured(config.byok, preset)).map((preset) => preset.id));
|
|
99
|
+
const selectPreset = (preset) => onConfigChange({ ...config, byok: nextConfigForPresetSelect(config.byok, preset) });
|
|
100
|
+
const selectedPresetId = selectedPreset?.id ?? null;
|
|
101
|
+
return (_jsxs("section", { className: "jini-settings-section jini-settings-byok", children: [_jsx(ProviderChipGroup, { label: t('Protocols'), presets: protocols, selectedPresetId: selectedPresetId, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...subHandleProps(agentHandle, 'protocol') }), _jsx(ProviderChipGroup, { label: t('Gateways'), presets: gateways, selectedPresetId: selectedPresetId, configuredPresetIds: configuredPresetIds, onSelect: selectPreset, configuredLabel: t('Configured'), unsetLabel: t('Not configured'), ...subHandleProps(agentHandle, 'gateway') }), _jsx(ByokProviderForm, { config: config.byok, onConfigChange: (byok) => onConfigChange({ ...config, byok }), preset: selectedPreset, ...subHandleProps(agentHandle, 'byok'),
|
|
102
|
+
// The WHOLE catalog, not the `protocols`/`gateways` halves the chip rows take: the card
|
|
103
|
+
// only reads it to recognise a pasted key as some other row's, and a key pasted from a
|
|
104
|
+
// gateway into a protocol field is exactly the mistake worth naming.
|
|
105
|
+
presets: presets, modelDiscovery: tab.modelDiscovery, connectionTest: tab.connectionTest,
|
|
106
|
+
// Also re-runs discovery with the CURRENT byok config, not just the
|
|
107
|
+
// connectivity probe — see this file's model-discovery effect above
|
|
108
|
+
// for why that matters: a stale "could not load live models" error
|
|
109
|
+
// from before this key existed (or from any earlier transient
|
|
110
|
+
// failure) would otherwise never clear, even once Test Connection
|
|
111
|
+
// itself goes green with the same config.
|
|
112
|
+
onTestConnection: () => {
|
|
113
|
+
tab.testConnection(config.byok);
|
|
114
|
+
if (canDiscoverModels && canListModels)
|
|
115
|
+
tab.loadModels(config.byok);
|
|
116
|
+
}, ...(formProps.apiKeyFooter !== undefined ? { apiKeyFooter: formProps.apiKeyFooter } : {}), ...(formProps.formFooter !== undefined ? { formFooter: formProps.formFooter } : {}), ...(formProps.apiKeyStoredExternally !== undefined ? { apiKeyStoredExternally: formProps.apiKeyStoredExternally } : {}), ...(formProps.apiKeyPlaceholder !== undefined ? { apiKeyPlaceholder: formProps.apiKeyPlaceholder } : {}) })] }));
|
|
117
|
+
}
|
|
118
|
+
/** The Local CLI half of the tab: detected agents, wired to the config's next-state rules. */
|
|
119
|
+
function LocalCliPane({ config, onConfigChange, tab, cliEnvFields, renderAgentIcon, localCliScopeLabel, agentHandle }) {
|
|
120
|
+
return (_jsx(LocalCliAgentList, { agents: tab.agents, config: config.localCli, scan: tab.scan, agentTest: tab.agentTest, onSelect: (agentId) => onConfigChange(nextConfigForAgentSelect(config, agentId)), onModelChange: (agentId, model) => onConfigChange(nextConfigForAgentModel(config, agentId, model)), onReasoningChange: (agentId, reasoning) => onConfigChange(nextConfigForAgentReasoning(config, agentId, reasoning)), onEnvChange: (agentId, envKey, value) => onConfigChange(nextConfigForAgentCliEnvChange(config, agentId, envKey, value)), cliEnvFields: cliEnvFields, onRescan: tab.canRescan ? tab.rescan : undefined, onTest: tab.canTestAgent
|
|
121
|
+
? (agent) => tab.testAgent(agent.id, selectedAgentModel(config.localCli, agent) || undefined)
|
|
122
|
+
: undefined, renderAgentIcon: renderAgentIcon, scopeLabel: localCliScopeLabel, ...subHandleProps(agentHandle, 'local-cli') }));
|
|
123
|
+
}
|
|
124
|
+
/** `{ agentHandle }` for a child's sub-handle under this tab's base, or nothing when no base is set. */
|
|
125
|
+
function subHandleProps(agentHandle, action) {
|
|
126
|
+
return agentHandle ? { agentHandle: agentSubHandle({ base: agentHandle, action }) } : {};
|
|
105
127
|
}
|
|
106
128
|
//# sourceMappingURL=ExecutionTab.js.map
|