@data-club/ai-hub 0.0.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.
- package/dist/auth/passwordPolicy.d.ts +55 -0
- package/dist/auth/passwordPolicy.d.ts.map +1 -0
- package/dist/auth/passwordPolicy.js +63 -0
- package/dist/auth/passwordPolicy.js.map +1 -0
- package/dist/auth/texts.d.ts +815 -0
- package/dist/auth/texts.d.ts.map +1 -0
- package/dist/auth/texts.js +429 -0
- package/dist/auth/texts.js.map +1 -0
- package/dist/client/AIHubClient.d.ts +1058 -0
- package/dist/client/AIHubClient.d.ts.map +1 -0
- package/dist/client/AIHubClient.js +950 -0
- package/dist/client/AIHubClient.js.map +1 -0
- package/dist/client/index.d.ts +11 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +9 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/sseParser.d.ts +41 -0
- package/dist/client/sseParser.d.ts.map +1 -0
- package/dist/client/sseParser.js +153 -0
- package/dist/client/sseParser.js.map +1 -0
- package/dist/client/tokenStorage.d.ts +56 -0
- package/dist/client/tokenStorage.d.ts.map +1 -0
- package/dist/client/tokenStorage.js +116 -0
- package/dist/client/tokenStorage.js.map +1 -0
- package/dist/components/AgentsPicker/index.d.ts +70 -0
- package/dist/components/AgentsPicker/index.d.ts.map +1 -0
- package/dist/components/AgentsPicker/index.js +63 -0
- package/dist/components/AgentsPicker/index.js.map +1 -0
- package/dist/components/AuthGate/index.d.ts +79 -0
- package/dist/components/AuthGate/index.d.ts.map +1 -0
- package/dist/components/AuthGate/index.js +61 -0
- package/dist/components/AuthGate/index.js.map +1 -0
- package/dist/components/CacheBadge/index.d.ts +88 -0
- package/dist/components/CacheBadge/index.d.ts.map +1 -0
- package/dist/components/CacheBadge/index.js +127 -0
- package/dist/components/CacheBadge/index.js.map +1 -0
- package/dist/components/ChangePassword/index.d.ts +109 -0
- package/dist/components/ChangePassword/index.d.ts.map +1 -0
- package/dist/components/ChangePassword/index.js +142 -0
- package/dist/components/ChangePassword/index.js.map +1 -0
- package/dist/components/ChatShell/ShellHeader.d.ts +44 -0
- package/dist/components/ChatShell/ShellHeader.d.ts.map +1 -0
- package/dist/components/ChatShell/ShellHeader.js +14 -0
- package/dist/components/ChatShell/ShellHeader.js.map +1 -0
- package/dist/components/ChatShell/UserMenu.d.ts +48 -0
- package/dist/components/ChatShell/UserMenu.d.ts.map +1 -0
- package/dist/components/ChatShell/UserMenu.js +86 -0
- package/dist/components/ChatShell/UserMenu.js.map +1 -0
- package/dist/components/ChatShell/index.d.ts +98 -0
- package/dist/components/ChatShell/index.d.ts.map +1 -0
- package/dist/components/ChatShell/index.js +124 -0
- package/dist/components/ChatShell/index.js.map +1 -0
- package/dist/components/DataClubAIHub/index.d.ts +132 -0
- package/dist/components/DataClubAIHub/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHub/index.js +124 -0
- package/dist/components/DataClubAIHub/index.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts +89 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js +199 -0
- package/dist/components/DataClubAIHubAdmin/AgentEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts +33 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js +137 -0
- package/dist/components/DataClubAIHubAdmin/AgentsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts +50 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js +93 -0
- package/dist/components/DataClubAIHubAdmin/CreateUserModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts +49 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js +94 -0
- package/dist/components/DataClubAIHubAdmin/PasswordRevealModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts +37 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js +44 -0
- package/dist/components/DataClubAIHubAdmin/ResetPasswordModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts +56 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js +158 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessageEditorModal.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts +29 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js +183 -0
- package/dist/components/DataClubAIHubAdmin/RotatingMessagesPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts +38 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js +294 -0
- package/dist/components/DataClubAIHubAdmin/SessionsOverviewPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts +36 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js +373 -0
- package/dist/components/DataClubAIHubAdmin/SettingsPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts +47 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js +233 -0
- package/dist/components/DataClubAIHubAdmin/UsersPage.js.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts +87 -0
- package/dist/components/DataClubAIHubAdmin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubAdmin/index.js +137 -0
- package/dist/components/DataClubAIHubAdmin/index.js.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts +113 -0
- package/dist/components/DataClubAIHubLogin/index.d.ts.map +1 -0
- package/dist/components/DataClubAIHubLogin/index.js +123 -0
- package/dist/components/DataClubAIHubLogin/index.js.map +1 -0
- package/dist/components/EffortControl/index.d.ts +57 -0
- package/dist/components/EffortControl/index.d.ts.map +1 -0
- package/dist/components/EffortControl/index.js +84 -0
- package/dist/components/EffortControl/index.js.map +1 -0
- package/dist/components/Layout/index.d.ts +63 -0
- package/dist/components/Layout/index.d.ts.map +1 -0
- package/dist/components/Layout/index.js +45 -0
- package/dist/components/Layout/index.js.map +1 -0
- package/dist/components/MainColumn/index.d.ts +69 -0
- package/dist/components/MainColumn/index.d.ts.map +1 -0
- package/dist/components/MainColumn/index.js +47 -0
- package/dist/components/MainColumn/index.js.map +1 -0
- package/dist/components/MarkdownRenderer/index.d.ts +66 -0
- package/dist/components/MarkdownRenderer/index.d.ts.map +1 -0
- package/dist/components/MarkdownRenderer/index.js +153 -0
- package/dist/components/MarkdownRenderer/index.js.map +1 -0
- package/dist/components/MessageInput/index.d.ts +85 -0
- package/dist/components/MessageInput/index.d.ts.map +1 -0
- package/dist/components/MessageInput/index.js +139 -0
- package/dist/components/MessageInput/index.js.map +1 -0
- package/dist/components/MessageList/index.d.ts +156 -0
- package/dist/components/MessageList/index.d.ts.map +1 -0
- package/dist/components/MessageList/index.js +352 -0
- package/dist/components/MessageList/index.js.map +1 -0
- package/dist/components/ReasoningSelector/index.d.ts +77 -0
- package/dist/components/ReasoningSelector/index.d.ts.map +1 -0
- package/dist/components/ReasoningSelector/index.js +156 -0
- package/dist/components/ReasoningSelector/index.js.map +1 -0
- package/dist/components/ReasoningView/index.d.ts +94 -0
- package/dist/components/ReasoningView/index.d.ts.map +1 -0
- package/dist/components/ReasoningView/index.js +83 -0
- package/dist/components/ReasoningView/index.js.map +1 -0
- package/dist/components/ScrollToPresentButton/index.d.ts +54 -0
- package/dist/components/ScrollToPresentButton/index.d.ts.map +1 -0
- package/dist/components/ScrollToPresentButton/index.js +44 -0
- package/dist/components/ScrollToPresentButton/index.js.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts +79 -0
- package/dist/components/SessionsPanel/SessionTitleModal.d.ts.map +1 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js +102 -0
- package/dist/components/SessionsPanel/SessionTitleModal.js.map +1 -0
- package/dist/components/SessionsPanel/index.d.ts +100 -0
- package/dist/components/SessionsPanel/index.d.ts.map +1 -0
- package/dist/components/SessionsPanel/index.js +176 -0
- package/dist/components/SessionsPanel/index.js.map +1 -0
- package/dist/components/SystemNotice/index.d.ts +66 -0
- package/dist/components/SystemNotice/index.d.ts.map +1 -0
- package/dist/components/SystemNotice/index.js +81 -0
- package/dist/components/SystemNotice/index.js.map +1 -0
- package/dist/components/ThemeSwitcher/index.d.ts +66 -0
- package/dist/components/ThemeSwitcher/index.d.ts.map +1 -0
- package/dist/components/ThemeSwitcher/index.js +73 -0
- package/dist/components/ThemeSwitcher/index.js.map +1 -0
- package/dist/components/ToolCallView/formatContent.d.ts +36 -0
- package/dist/components/ToolCallView/formatContent.d.ts.map +1 -0
- package/dist/components/ToolCallView/formatContent.js +53 -0
- package/dist/components/ToolCallView/formatContent.js.map +1 -0
- package/dist/components/ToolCallView/index.d.ts +120 -0
- package/dist/components/ToolCallView/index.d.ts.map +1 -0
- package/dist/components/ToolCallView/index.js +104 -0
- package/dist/components/ToolCallView/index.js.map +1 -0
- package/dist/components/TpmBanner/index.d.ts +53 -0
- package/dist/components/TpmBanner/index.d.ts.map +1 -0
- package/dist/components/TpmBanner/index.js +116 -0
- package/dist/components/TpmBanner/index.js.map +1 -0
- package/dist/components/TurnStatusIndicator/index.d.ts +77 -0
- package/dist/components/TurnStatusIndicator/index.d.ts.map +1 -0
- package/dist/components/TurnStatusIndicator/index.js +122 -0
- package/dist/components/TurnStatusIndicator/index.js.map +1 -0
- package/dist/components/internal/PasswordInput.d.ts +62 -0
- package/dist/components/internal/PasswordInput.d.ts.map +1 -0
- package/dist/components/internal/PasswordInput.js +42 -0
- package/dist/components/internal/PasswordInput.js.map +1 -0
- package/dist/context/AIHubContext.d.ts +220 -0
- package/dist/context/AIHubContext.d.ts.map +1 -0
- package/dist/context/AIHubContext.js +19 -0
- package/dist/context/AIHubContext.js.map +1 -0
- package/dist/context/AIHubProvider.d.ts +75 -0
- package/dist/context/AIHubProvider.d.ts.map +1 -0
- package/dist/context/AIHubProvider.js +954 -0
- package/dist/context/AIHubProvider.js.map +1 -0
- package/dist/context/index.d.ts +9 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +9 -0
- package/dist/context/index.js.map +1 -0
- package/dist/context/theme.d.ts +43 -0
- package/dist/context/theme.d.ts.map +1 -0
- package/dist/context/theme.js +70 -0
- package/dist/context/theme.js.map +1 -0
- package/dist/context/useAIHub.d.ts +104 -0
- package/dist/context/useAIHub.d.ts.map +1 -0
- package/dist/context/useAIHub.js +63 -0
- package/dist/context/useAIHub.js.map +1 -0
- package/dist/context/useAIHubAuth.d.ts +44 -0
- package/dist/context/useAIHubAuth.d.ts.map +1 -0
- package/dist/context/useAIHubAuth.js +35 -0
- package/dist/context/useAIHubAuth.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +57 -0
- package/dist/index.js.map +1 -0
- package/dist/primitives/index.d.ts +99 -0
- package/dist/primitives/index.d.ts.map +1 -0
- package/dist/primitives/index.js +97 -0
- package/dist/primitives/index.js.map +1 -0
- package/dist/state/affordances.d.ts +182 -0
- package/dist/state/affordances.d.ts.map +1 -0
- package/dist/state/affordances.js +189 -0
- package/dist/state/affordances.js.map +1 -0
- package/dist/state/stateReducer.d.ts +72 -0
- package/dist/state/stateReducer.d.ts.map +1 -0
- package/dist/state/stateReducer.js +122 -0
- package/dist/state/stateReducer.js.map +1 -0
- package/dist/stories/EffortControl.stories.d.ts +14 -0
- package/dist/stories/EffortControl.stories.d.ts.map +1 -0
- package/dist/stories/EffortControl.stories.js +43 -0
- package/dist/stories/EffortControl.stories.js.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts +25 -0
- package/dist/stories/MarkdownRenderer.stories.d.ts.map +1 -0
- package/dist/stories/MarkdownRenderer.stories.js +178 -0
- package/dist/stories/MarkdownRenderer.stories.js.map +1 -0
- package/dist/stories/MessageBubble.stories.d.ts +21 -0
- package/dist/stories/MessageBubble.stories.d.ts.map +1 -0
- package/dist/stories/MessageBubble.stories.js +59 -0
- package/dist/stories/MessageBubble.stories.js.map +1 -0
- package/dist/stories/ReasoningView.stories.d.ts +18 -0
- package/dist/stories/ReasoningView.stories.d.ts.map +1 -0
- package/dist/stories/ReasoningView.stories.js +62 -0
- package/dist/stories/ReasoningView.stories.js.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts +18 -0
- package/dist/stories/ScrollToPresentButton.stories.d.ts.map +1 -0
- package/dist/stories/ScrollToPresentButton.stories.js +34 -0
- package/dist/stories/ScrollToPresentButton.stories.js.map +1 -0
- package/dist/stories/SystemNotice.stories.d.ts +17 -0
- package/dist/stories/SystemNotice.stories.d.ts.map +1 -0
- package/dist/stories/SystemNotice.stories.js +42 -0
- package/dist/stories/SystemNotice.stories.js.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts +15 -0
- package/dist/stories/ThemeSwitcher.stories.d.ts.map +1 -0
- package/dist/stories/ThemeSwitcher.stories.js +35 -0
- package/dist/stories/ThemeSwitcher.stories.js.map +1 -0
- package/dist/stories/ToolCallView.stories.d.ts +20 -0
- package/dist/stories/ToolCallView.stories.d.ts.map +1 -0
- package/dist/stories/ToolCallView.stories.js +65 -0
- package/dist/stories/ToolCallView.stories.js.map +1 -0
- package/dist/stories/TpmBanner.stories.d.ts +24 -0
- package/dist/stories/TpmBanner.stories.d.ts.map +1 -0
- package/dist/stories/TpmBanner.stories.js +63 -0
- package/dist/stories/TpmBanner.stories.js.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts +25 -0
- package/dist/stories/TurnStatusIndicator.stories.d.ts.map +1 -0
- package/dist/stories/TurnStatusIndicator.stories.js +56 -0
- package/dist/stories/TurnStatusIndicator.stories.js.map +1 -0
- package/dist/stories/_mockProvider.d.ts +44 -0
- package/dist/stories/_mockProvider.d.ts.map +1 -0
- package/dist/stories/_mockProvider.js +84 -0
- package/dist/stories/_mockProvider.js.map +1 -0
- package/dist/types.d.ts +787 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/webcomponent/defineWebComponent.d.ts +106 -0
- package/dist/webcomponent/defineWebComponent.d.ts.map +1 -0
- package/dist/webcomponent/defineWebComponent.js +126 -0
- package/dist/webcomponent/defineWebComponent.js.map +1 -0
- package/dist/webcomponent/index.d.ts +7 -0
- package/dist/webcomponent/index.d.ts.map +1 -0
- package/dist/webcomponent/index.js +7 -0
- package/dist/webcomponent/index.js.map +1 -0
- package/package.json +63 -0
- package/src/__lint-canary__/step1-lint-canary.ts.disabled +28 -0
- package/src/auth/passwordPolicy.ts +75 -0
- package/src/auth/texts.ts +1294 -0
- package/src/client/AIHubClient.ts +1718 -0
- package/src/client/index.ts +30 -0
- package/src/client/sseParser.ts +168 -0
- package/src/client/tokenStorage.ts +131 -0
- package/src/components/AgentsPicker/index.tsx +147 -0
- package/src/components/AuthGate/index.tsx +132 -0
- package/src/components/CacheBadge/index.tsx +245 -0
- package/src/components/ChangePassword/index.tsx +375 -0
- package/src/components/ChatShell/ShellHeader.tsx +80 -0
- package/src/components/ChatShell/UserMenu.tsx +196 -0
- package/src/components/ChatShell/index.tsx +307 -0
- package/src/components/DataClubAIHub/index.tsx +300 -0
- package/src/components/DataClubAIHubAdmin/AgentEditorModal.tsx +505 -0
- package/src/components/DataClubAIHubAdmin/AgentsPage.tsx +342 -0
- package/src/components/DataClubAIHubAdmin/CreateUserModal.tsx +214 -0
- package/src/components/DataClubAIHubAdmin/PasswordRevealModal.tsx +204 -0
- package/src/components/DataClubAIHubAdmin/ResetPasswordModal.tsx +122 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessageEditorModal.tsx +408 -0
- package/src/components/DataClubAIHubAdmin/RotatingMessagesPage.tsx +373 -0
- package/src/components/DataClubAIHubAdmin/SessionsOverviewPage.tsx +639 -0
- package/src/components/DataClubAIHubAdmin/SettingsPage.tsx +979 -0
- package/src/components/DataClubAIHubAdmin/UsersPage.tsx +495 -0
- package/src/components/DataClubAIHubAdmin/index.tsx +277 -0
- package/src/components/DataClubAIHubLogin/index.tsx +282 -0
- package/src/components/EffortControl/index.tsx +138 -0
- package/src/components/Layout/index.tsx +92 -0
- package/src/components/MainColumn/index.tsx +131 -0
- package/src/components/MarkdownRenderer/index.tsx +256 -0
- package/src/components/MessageInput/index.tsx +283 -0
- package/src/components/MessageList/index.tsx +753 -0
- package/src/components/ReasoningSelector/index.tsx +268 -0
- package/src/components/ReasoningView/index.tsx +183 -0
- package/src/components/ScrollToPresentButton/index.tsx +85 -0
- package/src/components/SessionsPanel/SessionTitleModal.tsx +256 -0
- package/src/components/SessionsPanel/index.tsx +406 -0
- package/src/components/SystemNotice/index.tsx +151 -0
- package/src/components/ThemeSwitcher/index.tsx +136 -0
- package/src/components/ToolCallView/formatContent.ts +50 -0
- package/src/components/ToolCallView/index.tsx +274 -0
- package/src/components/TpmBanner/index.tsx +172 -0
- package/src/components/TurnStatusIndicator/index.tsx +203 -0
- package/src/components/internal/PasswordInput.tsx +145 -0
- package/src/context/AIHubContext.ts +249 -0
- package/src/context/AIHubProvider.tsx +1084 -0
- package/src/context/index.ts +9 -0
- package/src/context/theme.ts +76 -0
- package/src/context/useAIHub.ts +151 -0
- package/src/context/useAIHubAuth.ts +65 -0
- package/src/index.ts +275 -0
- package/src/primitives/index.ts +101 -0
- package/src/state/affordances.ts +321 -0
- package/src/state/stateReducer.ts +131 -0
- package/src/stories/EffortControl.stories.tsx +63 -0
- package/src/stories/MarkdownRenderer.stories.tsx +201 -0
- package/src/stories/MessageBubble.stories.tsx +101 -0
- package/src/stories/ReasoningView.stories.tsx +81 -0
- package/src/stories/ScrollToPresentButton.stories.tsx +52 -0
- package/src/stories/SystemNotice.stories.tsx +59 -0
- package/src/stories/ThemeSwitcher.stories.tsx +55 -0
- package/src/stories/ToolCallView.stories.tsx +95 -0
- package/src/stories/TpmBanner.stories.tsx +93 -0
- package/src/stories/TurnStatusIndicator.stories.tsx +86 -0
- package/src/stories/_mockProvider.tsx +116 -0
- package/src/styles/CLASSES.md +356 -0
- package/src/styles/default.css +2937 -0
- package/src/types.ts +867 -0
- package/src/webcomponent/defineWebComponent.tsx +193 -0
- package/src/webcomponent/index.ts +11 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public re-exports for the `client/` directory.
|
|
3
|
+
*
|
|
4
|
+
* @see SPECIFICATIONS.md §7.8 — client SDK
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { AIHubClient, AIHubRequestError } from "./AIHubClient.js";
|
|
8
|
+
export type {
|
|
9
|
+
AIHubClientOptions,
|
|
10
|
+
LoginResponse,
|
|
11
|
+
ListSessionsOptions,
|
|
12
|
+
CreateSessionOptions,
|
|
13
|
+
PatchSessionOptions,
|
|
14
|
+
PatchSessionReasoningOptions,
|
|
15
|
+
AIHubEffort,
|
|
16
|
+
StreamChatBody,
|
|
17
|
+
StreamChatOptions,
|
|
18
|
+
AgentSummary,
|
|
19
|
+
CancelTurnResponse,
|
|
20
|
+
AdminUserView,
|
|
21
|
+
CreateUserBody,
|
|
22
|
+
AdminUserPasswordReveal,
|
|
23
|
+
CreateUserResponse,
|
|
24
|
+
} from "./AIHubClient.js";
|
|
25
|
+
export {
|
|
26
|
+
createMemoryTokenStorage,
|
|
27
|
+
resolveTokenStorage,
|
|
28
|
+
} from "./tokenStorage.js";
|
|
29
|
+
export type { TokenStorage, TokenStorageMode } from "./tokenStorage.js";
|
|
30
|
+
export { parseSseStream } from "./sseParser.js";
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSE → `AIHubEvent` parser.
|
|
3
|
+
*
|
|
4
|
+
* The ai-hub server emits each event as:
|
|
5
|
+
*
|
|
6
|
+
* ```
|
|
7
|
+
* event: <event-name>
|
|
8
|
+
* data: <json-payload>
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
*
|
|
12
|
+
* where `<json-payload>` is `{ seq, data?, state }` (see
|
|
13
|
+
* `packages/ai-hub-server/src/handlers/sse.ts`). The library does not use
|
|
14
|
+
* the browser's `EventSource` API (§9.5 — it can't carry custom auth
|
|
15
|
+
* headers); instead the SDK consumes the response body via
|
|
16
|
+
* `fetch` + `ReadableStream` and runs it through this parser.
|
|
17
|
+
*
|
|
18
|
+
* Heartbeat lines (`:hb\n\n`) are SSE comments and are skipped entirely —
|
|
19
|
+
* they reach the parser as a block whose only line starts with `:` and
|
|
20
|
+
* produce no event.
|
|
21
|
+
*
|
|
22
|
+
* @see SPECIFICATIONS.md §9.5 — why SSE (not EventSource)
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import type { AIHubEvent } from "../types.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parses a streaming `ReadableStream<Uint8Array>` of SSE bytes into an
|
|
29
|
+
* `AsyncIterable<AIHubEvent>`.
|
|
30
|
+
*
|
|
31
|
+
* The async generator stops when the underlying stream ends or when the
|
|
32
|
+
* stream's reader is released (e.g. on `AbortSignal` abort upstream).
|
|
33
|
+
* Malformed event blocks (no `event:` line, or `data:` JSON that doesn't
|
|
34
|
+
* parse) are skipped silently — the contract is "yield every valid event,"
|
|
35
|
+
* not "throw on the first garbage chunk." Real network corruption surfaces
|
|
36
|
+
* as a `stream_end` from the server (or a transport-level error from
|
|
37
|
+
* fetch, which the caller catches separately).
|
|
38
|
+
*
|
|
39
|
+
* @param body - the response body to parse.
|
|
40
|
+
* @yields each `AIHubEvent` decoded from the byte stream, in emission order.
|
|
41
|
+
*/
|
|
42
|
+
export async function* parseSseStream(
|
|
43
|
+
body: ReadableStream<Uint8Array>,
|
|
44
|
+
): AsyncIterable<AIHubEvent> {
|
|
45
|
+
const reader = body.getReader();
|
|
46
|
+
const decoder = new TextDecoder("utf-8");
|
|
47
|
+
let buffer = "";
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
while (true) {
|
|
51
|
+
const { done, value } = await reader.read();
|
|
52
|
+
if (done) {
|
|
53
|
+
// Flush any trailing block (server didn't send the terminating
|
|
54
|
+
// blank line). Defensive — the server always closes cleanly.
|
|
55
|
+
const tail = buffer.trim();
|
|
56
|
+
if (tail.length > 0) {
|
|
57
|
+
const event = parseEventBlock(tail);
|
|
58
|
+
if (event) yield event;
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
buffer += decoder.decode(value, { stream: true });
|
|
64
|
+
|
|
65
|
+
// Event blocks are separated by a blank line. Handle both `\n\n`
|
|
66
|
+
// (what our server emits) and `\r\n\r\n` (what some intermediaries
|
|
67
|
+
// and the spec officially require) for resilience.
|
|
68
|
+
// eslint-disable-next-line no-constant-condition
|
|
69
|
+
while (true) {
|
|
70
|
+
const sep = findBlockSeparator(buffer);
|
|
71
|
+
if (sep === null) break;
|
|
72
|
+
const block = buffer.slice(0, sep.end);
|
|
73
|
+
buffer = buffer.slice(sep.next);
|
|
74
|
+
const event = parseEventBlock(block);
|
|
75
|
+
if (event) yield event;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} finally {
|
|
79
|
+
// Release the reader so the stream can be cancelled cleanly upstream.
|
|
80
|
+
try {
|
|
81
|
+
reader.releaseLock();
|
|
82
|
+
} catch {
|
|
83
|
+
/* already released */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Indices identifying an SSE event-block separator inside the buffer.
|
|
90
|
+
* `end` is the exclusive end of the block content; `next` is where the
|
|
91
|
+
* next block's content starts.
|
|
92
|
+
*/
|
|
93
|
+
interface SeparatorIndex {
|
|
94
|
+
/** Exclusive end-of-block index (everything before this is one event). */
|
|
95
|
+
end: number;
|
|
96
|
+
/** Start-of-next-block index (the parser slices the buffer here). */
|
|
97
|
+
next: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Standalone helper so `parseSseStream` stays readable. */
|
|
101
|
+
function findBlockSeparator(s: string): SeparatorIndex | null {
|
|
102
|
+
const lf = s.indexOf("\n\n");
|
|
103
|
+
const crlf = s.indexOf("\r\n\r\n");
|
|
104
|
+
if (lf === -1 && crlf === -1) return null;
|
|
105
|
+
if (crlf !== -1 && (lf === -1 || crlf < lf)) {
|
|
106
|
+
return { end: crlf, next: crlf + 4 };
|
|
107
|
+
}
|
|
108
|
+
return { end: lf, next: lf + 2 };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parses one event block (lines up to but not including the blank-line
|
|
113
|
+
* separator) into an `AIHubEvent`, or returns null if the block is a
|
|
114
|
+
* comment / malformed.
|
|
115
|
+
*
|
|
116
|
+
* @param block - the raw text of one block.
|
|
117
|
+
* @returns the parsed event, or null on skip.
|
|
118
|
+
*/
|
|
119
|
+
function parseEventBlock(block: string): AIHubEvent | null {
|
|
120
|
+
let eventName: string | null = null;
|
|
121
|
+
// Per the SSE spec, multi-line `data:` payloads are concatenated with
|
|
122
|
+
// newlines. Our server only ever emits single-line JSON, but accept
|
|
123
|
+
// multi-line payloads defensively.
|
|
124
|
+
const dataLines: string[] = [];
|
|
125
|
+
|
|
126
|
+
for (const line of block.split(/\r?\n/)) {
|
|
127
|
+
if (line.length === 0) continue;
|
|
128
|
+
if (line.startsWith(":")) continue; // SSE comment — skip
|
|
129
|
+
const colonIdx = line.indexOf(":");
|
|
130
|
+
if (colonIdx === -1) continue;
|
|
131
|
+
const field = line.slice(0, colonIdx);
|
|
132
|
+
// Per the spec, a single space after the colon is stripped if present.
|
|
133
|
+
const valueStart = line.charAt(colonIdx + 1) === " " ? colonIdx + 2 : colonIdx + 1;
|
|
134
|
+
const value = line.slice(valueStart);
|
|
135
|
+
|
|
136
|
+
if (field === "event") eventName = value;
|
|
137
|
+
else if (field === "data") dataLines.push(value);
|
|
138
|
+
// id / retry fields are unused — ignore them.
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!eventName || dataLines.length === 0) return null;
|
|
142
|
+
|
|
143
|
+
let payload: { seq: number; data?: unknown; state: { status: string; phase: string | null } };
|
|
144
|
+
try {
|
|
145
|
+
payload = JSON.parse(dataLines.join("\n")) as typeof payload;
|
|
146
|
+
} catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
if (
|
|
150
|
+
typeof payload !== "object" ||
|
|
151
|
+
payload === null ||
|
|
152
|
+
typeof payload.seq !== "number" ||
|
|
153
|
+
typeof payload.state !== "object" ||
|
|
154
|
+
payload.state === null
|
|
155
|
+
) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// The shape on the wire mirrors `AIHubEvent` exactly minus the discriminator
|
|
160
|
+
// (which lives on the `event:` line, not in the JSON payload). Re-attach it
|
|
161
|
+
// here. `heartbeat` events carry no `data` field per the union.
|
|
162
|
+
return {
|
|
163
|
+
event: eventName,
|
|
164
|
+
seq: payload.seq,
|
|
165
|
+
state: payload.state,
|
|
166
|
+
...(payload.data !== undefined ? { data: payload.data } : {}),
|
|
167
|
+
} as AIHubEvent;
|
|
168
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token storage adapters for `AIHubClient`.
|
|
3
|
+
*
|
|
4
|
+
* Three modes per §7.2 (the `tokenStorage` prop on `<AIHubProvider>`):
|
|
5
|
+
*
|
|
6
|
+
* - `"localStorage"` (default) — persists across tabs and browser restarts.
|
|
7
|
+
* - `"sessionStorage"` — persists across reloads within a tab; cleared on
|
|
8
|
+
* tab close. Useful for embeds where the embedding page is itself
|
|
9
|
+
* ephemeral.
|
|
10
|
+
* - `"memory"` — never persisted. Useful for tests, server-side rendering,
|
|
11
|
+
* and embedding contexts where the host page provides its own persistence
|
|
12
|
+
* strategy.
|
|
13
|
+
*
|
|
14
|
+
* The adapter is a tiny three-method interface so future modes (e.g. an
|
|
15
|
+
* IndexedDB-backed one for very-large session counts, or a host-supplied
|
|
16
|
+
* one for embedding contexts that want to share storage with the parent
|
|
17
|
+
* page) are an additive change.
|
|
18
|
+
*
|
|
19
|
+
* @see SPECIFICATIONS.md §7.2 — `<AIHubProvider>` props
|
|
20
|
+
* @see SPECIFICATIONS.md §6.7 — token lifetimes
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The storage adapter `AIHubClient` reads/writes the session JWT through.
|
|
25
|
+
* The contract is intentionally minimal — three pure methods, no async.
|
|
26
|
+
*/
|
|
27
|
+
export interface TokenStorage {
|
|
28
|
+
/** Returns the stored token, or `null` if none is set. */
|
|
29
|
+
get(): string | null;
|
|
30
|
+
/** Replaces any existing token. */
|
|
31
|
+
set(token: string): void;
|
|
32
|
+
/** Removes the stored token (idempotent — no-op when none was set). */
|
|
33
|
+
clear(): void;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Identifier strings for the three built-in adapters. */
|
|
37
|
+
export type TokenStorageMode = "localStorage" | "sessionStorage" | "memory";
|
|
38
|
+
|
|
39
|
+
const STORAGE_KEY = "data-club-ai-hub:token";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Builds an in-memory token storage. Used by the `"memory"` mode and as a
|
|
43
|
+
* fallback when the browser's storage APIs throw (Safari private mode, etc.).
|
|
44
|
+
*
|
|
45
|
+
* @returns a fresh in-memory storage adapter.
|
|
46
|
+
*/
|
|
47
|
+
export function createMemoryTokenStorage(): TokenStorage {
|
|
48
|
+
let value: string | null = null;
|
|
49
|
+
return {
|
|
50
|
+
get: () => value,
|
|
51
|
+
set: (token) => {
|
|
52
|
+
value = token;
|
|
53
|
+
},
|
|
54
|
+
clear: () => {
|
|
55
|
+
value = null;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Builds a `Storage`-backed adapter (localStorage or sessionStorage).
|
|
62
|
+
*
|
|
63
|
+
* Falls back to an in-memory adapter if the underlying storage throws on
|
|
64
|
+
* the first call (Safari private mode, embedded iframe with storage
|
|
65
|
+
* blocked, etc.) — we'd rather degrade silently than break the auth flow.
|
|
66
|
+
*
|
|
67
|
+
* @param storage - the underlying `Storage` instance.
|
|
68
|
+
* @returns an adapter that reads/writes through the given storage.
|
|
69
|
+
*/
|
|
70
|
+
function createWebStorageTokenStorage(storage: Storage): TokenStorage {
|
|
71
|
+
try {
|
|
72
|
+
storage.getItem(STORAGE_KEY);
|
|
73
|
+
} catch {
|
|
74
|
+
return createMemoryTokenStorage();
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
get: () => {
|
|
78
|
+
try {
|
|
79
|
+
return storage.getItem(STORAGE_KEY);
|
|
80
|
+
} catch {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
set: (token) => {
|
|
85
|
+
try {
|
|
86
|
+
storage.setItem(STORAGE_KEY, token);
|
|
87
|
+
} catch {
|
|
88
|
+
/* ignore */
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
clear: () => {
|
|
92
|
+
try {
|
|
93
|
+
storage.removeItem(STORAGE_KEY);
|
|
94
|
+
} catch {
|
|
95
|
+
/* ignore */
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolves a `TokenStorageMode` (or a custom `TokenStorage` instance) into
|
|
103
|
+
* a concrete adapter.
|
|
104
|
+
*
|
|
105
|
+
* In non-browser environments (SSR, Node tests without jsdom), the
|
|
106
|
+
* `"localStorage"` / `"sessionStorage"` modes silently fall back to
|
|
107
|
+
* `"memory"` — this is what keeps SSR-rendering an `<AIHubProvider>` from
|
|
108
|
+
* crashing on missing globals.
|
|
109
|
+
*
|
|
110
|
+
* @param mode - either one of the three mode strings or a custom adapter.
|
|
111
|
+
* @returns the concrete adapter.
|
|
112
|
+
*/
|
|
113
|
+
export function resolveTokenStorage(
|
|
114
|
+
mode: TokenStorageMode | TokenStorage,
|
|
115
|
+
): TokenStorage {
|
|
116
|
+
if (typeof mode === "object") return mode;
|
|
117
|
+
if (mode === "memory") return createMemoryTokenStorage();
|
|
118
|
+
const globalRef = typeof globalThis !== "undefined" ? globalThis : undefined;
|
|
119
|
+
const win =
|
|
120
|
+
globalRef && "window" in globalRef
|
|
121
|
+
? (globalRef as { window?: Window }).window
|
|
122
|
+
: undefined;
|
|
123
|
+
if (!win) return createMemoryTokenStorage();
|
|
124
|
+
if (mode === "localStorage" && win.localStorage) {
|
|
125
|
+
return createWebStorageTokenStorage(win.localStorage);
|
|
126
|
+
}
|
|
127
|
+
if (mode === "sessionStorage" && win.sessionStorage) {
|
|
128
|
+
return createWebStorageTokenStorage(win.sessionStorage);
|
|
129
|
+
}
|
|
130
|
+
return createMemoryTokenStorage();
|
|
131
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `<AgentsPicker />` — the post-login landing view.
|
|
3
|
+
*
|
|
4
|
+
* Renders one card per non-archived ai-hub agent the caller has access to,
|
|
5
|
+
* with the caller-scoped `session_count` baked into the subtitle so a
|
|
6
|
+
* single round-trip populates the entire picker (§6.3.3). Clicking a card
|
|
7
|
+
* calls `useAIHub().setActiveAgent(id)` — the parent shell then renders
|
|
8
|
+
* the chat layout for that agent.
|
|
9
|
+
*
|
|
10
|
+
* Auto-skip behaviour (consumed by `ChatShell` above): the picker is
|
|
11
|
+
* skipped entirely when `showAgentsPicker={false}` *or* when the
|
|
12
|
+
* deployment ships exactly one agent — those skips happen at the
|
|
13
|
+
* `ChatShell` level so this component stays a pure listing view.
|
|
14
|
+
*
|
|
15
|
+
* Exported under the `AIHub.AgentsPicker` Tier-3 primitive.
|
|
16
|
+
*
|
|
17
|
+
* @see SPECIFICATIONS.md §7.1 — exports
|
|
18
|
+
* @see SPECIFICATIONS.md §6.3.3 — `/agents`
|
|
19
|
+
* @see ui-sketches/03-chat.md — visual contract for the picker
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { type ReactNode } from "react";
|
|
23
|
+
|
|
24
|
+
import { resolveAIHubTexts, type AIHubTexts, type DeepPartial } from "../../auth/texts.js";
|
|
25
|
+
import { useAIHub } from "../../context/useAIHub.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* BEM class names emitted by `<AgentsPicker />`. Per-customer wrappers
|
|
29
|
+
* pass a partial of this map via the `classNames` prop to override
|
|
30
|
+
* individual names for utility-CSS frameworks.
|
|
31
|
+
*/
|
|
32
|
+
export interface AgentsPickerClassNames {
|
|
33
|
+
/** Outer wrapper (centers the picker, scrolls when too tall). */
|
|
34
|
+
root: string;
|
|
35
|
+
/** Page heading at the top. */
|
|
36
|
+
heading: string;
|
|
37
|
+
/** Loading line shown while `/agents` is in flight. */
|
|
38
|
+
loading: string;
|
|
39
|
+
/** Error banner shown when `/agents` fails. */
|
|
40
|
+
error: string;
|
|
41
|
+
/** Empty-state container. */
|
|
42
|
+
empty: string;
|
|
43
|
+
/** Empty-state heading. */
|
|
44
|
+
emptyHeading: string;
|
|
45
|
+
/** Empty-state body. */
|
|
46
|
+
emptyBody: string;
|
|
47
|
+
/** Card grid wrapper. */
|
|
48
|
+
grid: string;
|
|
49
|
+
/** A single agent card (the clickable tile). */
|
|
50
|
+
card: string;
|
|
51
|
+
/** Card display name. */
|
|
52
|
+
cardName: string;
|
|
53
|
+
/** Card subtitle line (session count). */
|
|
54
|
+
cardSubtitle: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Default BEM class names. Stable, kebab-case, scoped under
|
|
59
|
+
* `data-club-ai-hub__agents-picker`. Wrappers override individual entries
|
|
60
|
+
* via the `classNames` prop.
|
|
61
|
+
*/
|
|
62
|
+
const DEFAULT_CLASS_NAMES: AgentsPickerClassNames = Object.freeze({
|
|
63
|
+
root: "data-club-ai-hub__agents-picker",
|
|
64
|
+
heading: "data-club-ai-hub__agents-picker__heading",
|
|
65
|
+
loading: "data-club-ai-hub__agents-picker__loading",
|
|
66
|
+
error: "data-club-ai-hub__agents-picker__error",
|
|
67
|
+
empty: "data-club-ai-hub__agents-picker__empty",
|
|
68
|
+
emptyHeading: "data-club-ai-hub__agents-picker__empty-heading",
|
|
69
|
+
emptyBody: "data-club-ai-hub__agents-picker__empty-body",
|
|
70
|
+
grid: "data-club-ai-hub__agents-picker__grid",
|
|
71
|
+
card: "data-club-ai-hub__agent-card",
|
|
72
|
+
cardName: "data-club-ai-hub__agent-card__name",
|
|
73
|
+
cardSubtitle: "data-club-ai-hub__agent-card__subtitle",
|
|
74
|
+
}) as AgentsPickerClassNames;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Props for `<AgentsPicker />`.
|
|
78
|
+
*/
|
|
79
|
+
export interface AgentsPickerProps {
|
|
80
|
+
/** Extra class merged onto the root wrapper. */
|
|
81
|
+
className?: string;
|
|
82
|
+
/** Per-element class-name overrides. */
|
|
83
|
+
classNames?: Partial<AgentsPickerClassNames>;
|
|
84
|
+
/** Wrapper-supplied i18n. */
|
|
85
|
+
texts?: DeepPartial<AIHubTexts>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Renders the agents picker.
|
|
90
|
+
*
|
|
91
|
+
* @param props - see `AgentsPickerProps`.
|
|
92
|
+
* @returns the rendered picker.
|
|
93
|
+
*/
|
|
94
|
+
export function AgentsPicker(props: AgentsPickerProps): ReactNode {
|
|
95
|
+
const { agents, agentsLoading, setActiveAgent } = useAIHub();
|
|
96
|
+
const texts = resolveAIHubTexts(props.texts);
|
|
97
|
+
const cls = { ...DEFAULT_CLASS_NAMES, ...(props.classNames ?? {}) };
|
|
98
|
+
const rootClass = props.className
|
|
99
|
+
? `${cls.root} ${props.className}`
|
|
100
|
+
: cls.root;
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div className={rootClass} data-testid="agents-picker">
|
|
104
|
+
<h1 className={cls.heading}>{texts.chat.agentsPickerHeading}</h1>
|
|
105
|
+
{agentsLoading && agents.length === 0 ? (
|
|
106
|
+
<p className={cls.loading} data-testid="agents-picker-loading">
|
|
107
|
+
{texts.chat.agentsPickerLoading}
|
|
108
|
+
</p>
|
|
109
|
+
) : null}
|
|
110
|
+
{!agentsLoading && agents.length === 0 ? (
|
|
111
|
+
<div className={cls.empty} data-testid="agents-picker-empty">
|
|
112
|
+
<h2 className={cls.emptyHeading}>
|
|
113
|
+
{texts.chat.agentsPickerEmptyHeading}
|
|
114
|
+
</h2>
|
|
115
|
+
<p className={cls.emptyBody}>{texts.chat.agentsPickerEmptyBody}</p>
|
|
116
|
+
</div>
|
|
117
|
+
) : null}
|
|
118
|
+
{agents.length > 0 ? (
|
|
119
|
+
<div className={cls.grid} role="list">
|
|
120
|
+
{agents.map((agent) => {
|
|
121
|
+
const subtitle =
|
|
122
|
+
agent.session_count === 0
|
|
123
|
+
? texts.chat.agentCardNoSessionsSubtitle
|
|
124
|
+
: texts.chat.agentCardSessionsSubtitle.replace(
|
|
125
|
+
"{count}",
|
|
126
|
+
String(agent.session_count),
|
|
127
|
+
);
|
|
128
|
+
return (
|
|
129
|
+
<button
|
|
130
|
+
key={agent.id}
|
|
131
|
+
type="button"
|
|
132
|
+
role="listitem"
|
|
133
|
+
className={cls.card}
|
|
134
|
+
data-testid="agent-card"
|
|
135
|
+
data-agent-id={agent.id}
|
|
136
|
+
onClick={() => setActiveAgent(agent.id)}
|
|
137
|
+
>
|
|
138
|
+
<span className={cls.cardName}>{agent.display_name}</span>
|
|
139
|
+
<span className={cls.cardSubtitle}>{subtitle}</span>
|
|
140
|
+
</button>
|
|
141
|
+
);
|
|
142
|
+
})}
|
|
143
|
+
</div>
|
|
144
|
+
) : null}
|
|
145
|
+
</div>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `AIHub.AuthGate` — the auth-state router.
|
|
3
|
+
*
|
|
4
|
+
* Reads `useAIHubAuth()` and renders:
|
|
5
|
+
*
|
|
6
|
+
* - `<DataClubAIHubLogin />` when no user is present.
|
|
7
|
+
* - `<DataClubAIHubChangePassword variant="forced" />` when a user is
|
|
8
|
+
* present but `mustChangePassword === true`.
|
|
9
|
+
* - `children` (the chat / admin / whatever) otherwise.
|
|
10
|
+
*
|
|
11
|
+
* While the provider is still hydrating from a stored token (`hydrating ===
|
|
12
|
+
* true`) the gate renders nothing — preventing a one-frame flash of the
|
|
13
|
+
* login card before the cached session resolves.
|
|
14
|
+
*
|
|
15
|
+
* Tier 3 wrappers compose this primitive as the second layer (after
|
|
16
|
+
* `AIHub.Provider`) so the auth UX is handled automatically; Tier 1 / 2
|
|
17
|
+
* wrappers get the same behavior for free via `<DataClubAIHub />`.
|
|
18
|
+
*
|
|
19
|
+
* @see SPECIFICATIONS.md §7.1 — exports
|
|
20
|
+
* @see SPECIFICATIONS.md §7.2.1 — auth-owned-by-library policy
|
|
21
|
+
* @see SPECIFICATIONS.md §7.9 — Tier-3 composition example
|
|
22
|
+
* @see ui-sketches/01-login.md
|
|
23
|
+
* @see ui-sketches/02-change-password.md
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { type ReactNode } from "react";
|
|
27
|
+
|
|
28
|
+
import { useAIHubAuth } from "../../context/useAIHubAuth.js";
|
|
29
|
+
import type { AIHubTexts, DeepPartial } from "../../auth/texts.js";
|
|
30
|
+
import {
|
|
31
|
+
DataClubAIHubLogin,
|
|
32
|
+
type AIHubLoginClassNames,
|
|
33
|
+
} from "../DataClubAIHubLogin/index.js";
|
|
34
|
+
import {
|
|
35
|
+
DataClubAIHubChangePassword,
|
|
36
|
+
type AIHubChangePasswordClassNames,
|
|
37
|
+
} from "../ChangePassword/index.js";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Props for `AIHub.AuthGate`. The branding / i18n props mirror
|
|
41
|
+
* `<DataClubAIHub />` — Tier 3 wrappers pass them through here so the
|
|
42
|
+
* auto-rendered Login + ChangePassword screens get branded without the
|
|
43
|
+
* wrapper having to render those screens manually.
|
|
44
|
+
*/
|
|
45
|
+
export interface AuthGateProps {
|
|
46
|
+
/**
|
|
47
|
+
* Rendered when the user is authenticated and is not in the forced
|
|
48
|
+
* change-password state. The wrapper is responsible for the actual
|
|
49
|
+
* chat / admin tree.
|
|
50
|
+
*/
|
|
51
|
+
children: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Title text on the login card. Forwarded to `<DataClubAIHubLogin />`.
|
|
54
|
+
* Defaults to `"ai-hub"`.
|
|
55
|
+
*/
|
|
56
|
+
productName?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Placeholder shown inside the username input on the login card.
|
|
59
|
+
* Forwarded to `<DataClubAIHubLogin />`. Defaults to empty.
|
|
60
|
+
*/
|
|
61
|
+
usernamePlaceholder?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Wrapper-supplied i18n applied to both auto-rendered auth screens.
|
|
64
|
+
*/
|
|
65
|
+
texts?: DeepPartial<AIHubTexts>;
|
|
66
|
+
/** Per-element class overrides forwarded to `<DataClubAIHubLogin />`. */
|
|
67
|
+
loginClassNames?: Partial<AIHubLoginClassNames>;
|
|
68
|
+
/**
|
|
69
|
+
* Per-element class overrides forwarded to
|
|
70
|
+
* `<DataClubAIHubChangePassword />`.
|
|
71
|
+
*/
|
|
72
|
+
changePasswordClassNames?: Partial<AIHubChangePasswordClassNames>;
|
|
73
|
+
/**
|
|
74
|
+
* Optional `className` forwarded onto the login root.
|
|
75
|
+
*/
|
|
76
|
+
loginClassName?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Optional `className` forwarded onto the change-password root.
|
|
79
|
+
*/
|
|
80
|
+
changePasswordClassName?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Renders the right screen for the current auth state.
|
|
85
|
+
*
|
|
86
|
+
* @param props - see `AuthGateProps`.
|
|
87
|
+
* @returns the rendered subtree.
|
|
88
|
+
*/
|
|
89
|
+
export function AuthGate(props: AuthGateProps): ReactNode {
|
|
90
|
+
const auth = useAIHubAuth();
|
|
91
|
+
|
|
92
|
+
if (auth.hydrating) {
|
|
93
|
+
// Render nothing — we don't know yet whether the stored token is valid.
|
|
94
|
+
// FOUC-of-login-card avoidance.
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!auth.user) {
|
|
99
|
+
return (
|
|
100
|
+
<DataClubAIHubLogin
|
|
101
|
+
{...(props.productName !== undefined ? { productName: props.productName } : {})}
|
|
102
|
+
{...(props.usernamePlaceholder !== undefined
|
|
103
|
+
? { usernamePlaceholder: props.usernamePlaceholder }
|
|
104
|
+
: {})}
|
|
105
|
+
{...(props.texts !== undefined ? { texts: props.texts } : {})}
|
|
106
|
+
{...(props.loginClassNames !== undefined
|
|
107
|
+
? { classNames: props.loginClassNames }
|
|
108
|
+
: {})}
|
|
109
|
+
{...(props.loginClassName !== undefined
|
|
110
|
+
? { className: props.loginClassName }
|
|
111
|
+
: {})}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (auth.mustChangePassword) {
|
|
117
|
+
return (
|
|
118
|
+
<DataClubAIHubChangePassword
|
|
119
|
+
variant="forced"
|
|
120
|
+
{...(props.texts !== undefined ? { texts: props.texts } : {})}
|
|
121
|
+
{...(props.changePasswordClassNames !== undefined
|
|
122
|
+
? { classNames: props.changePasswordClassNames }
|
|
123
|
+
: {})}
|
|
124
|
+
{...(props.changePasswordClassName !== undefined
|
|
125
|
+
? { className: props.changePasswordClassName }
|
|
126
|
+
: {})}
|
|
127
|
+
/>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return <>{props.children}</>;
|
|
132
|
+
}
|