@shipfox/client-agent 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/.storybook/main.ts +1 -0
  2. package/.storybook/preview.css +4 -0
  3. package/.storybook/preview.tsx +56 -0
  4. package/.turbo/turbo-build.log +2 -0
  5. package/.turbo/turbo-type$colon$emit.log +1 -0
  6. package/.turbo/turbo-type.log +1 -0
  7. package/CHANGELOG.md +93 -0
  8. package/LICENSE +21 -0
  9. package/dist/components/add-custom-provider-card.d.ts +4 -0
  10. package/dist/components/add-custom-provider-card.d.ts.map +1 -0
  11. package/dist/components/add-custom-provider-card.js +41 -0
  12. package/dist/components/add-custom-provider-card.js.map +1 -0
  13. package/dist/components/agent-workflow-example.d.ts +11 -0
  14. package/dist/components/agent-workflow-example.d.ts.map +1 -0
  15. package/dist/components/agent-workflow-example.js +38 -0
  16. package/dist/components/agent-workflow-example.js.map +1 -0
  17. package/dist/components/available-provider-card.d.ts +6 -0
  18. package/dist/components/available-provider-card.d.ts.map +1 -0
  19. package/dist/components/available-provider-card.js +41 -0
  20. package/dist/components/available-provider-card.js.map +1 -0
  21. package/dist/components/available-providers-grid.d.ts +10 -0
  22. package/dist/components/available-providers-grid.d.ts.map +1 -0
  23. package/dist/components/available-providers-grid.js +89 -0
  24. package/dist/components/available-providers-grid.js.map +1 -0
  25. package/dist/components/change-default-model-form.d.ts +9 -0
  26. package/dist/components/change-default-model-form.d.ts.map +1 -0
  27. package/dist/components/change-default-model-form.js +98 -0
  28. package/dist/components/change-default-model-form.js.map +1 -0
  29. package/dist/components/custom-model-provider-api-options.d.ts +7 -0
  30. package/dist/components/custom-model-provider-api-options.d.ts.map +1 -0
  31. package/dist/components/custom-model-provider-api-options.js +23 -0
  32. package/dist/components/custom-model-provider-api-options.js.map +1 -0
  33. package/dist/components/custom-model-provider-form.d.ts +8 -0
  34. package/dist/components/custom-model-provider-form.d.ts.map +1 -0
  35. package/dist/components/custom-model-provider-form.js +667 -0
  36. package/dist/components/custom-model-provider-form.js.map +1 -0
  37. package/dist/components/custom-model-provider-form.stories.d.ts +20 -0
  38. package/dist/components/custom-model-provider-form.stories.d.ts.map +1 -0
  39. package/dist/components/custom-model-provider-form.stories.js +104 -0
  40. package/dist/components/custom-model-provider-form.stories.js.map +1 -0
  41. package/dist/components/custom-model-provider-payload.d.ts +39 -0
  42. package/dist/components/custom-model-provider-payload.d.ts.map +1 -0
  43. package/dist/components/custom-model-provider-payload.js +203 -0
  44. package/dist/components/custom-model-provider-payload.js.map +1 -0
  45. package/dist/components/custom-model-provider-slug.d.ts +3 -0
  46. package/dist/components/custom-model-provider-slug.d.ts.map +1 -0
  47. package/dist/components/custom-model-provider-slug.js +14 -0
  48. package/dist/components/custom-model-provider-slug.js.map +1 -0
  49. package/dist/components/default-model-field.d.ts +16 -0
  50. package/dist/components/default-model-field.d.ts.map +1 -0
  51. package/dist/components/default-model-field.js +60 -0
  52. package/dist/components/default-model-field.js.map +1 -0
  53. package/dist/components/form-errors.d.ts +11 -0
  54. package/dist/components/form-errors.d.ts.map +1 -0
  55. package/dist/components/form-errors.js +101 -0
  56. package/dist/components/form-errors.js.map +1 -0
  57. package/dist/components/harness-availability.d.ts +8 -0
  58. package/dist/components/harness-availability.d.ts.map +1 -0
  59. package/dist/components/harness-availability.js +16 -0
  60. package/dist/components/harness-availability.js.map +1 -0
  61. package/dist/components/harnesses-section.d.ts +4 -0
  62. package/dist/components/harnesses-section.d.ts.map +1 -0
  63. package/dist/components/harnesses-section.js +249 -0
  64. package/dist/components/harnesses-section.js.map +1 -0
  65. package/dist/components/harnesses-section.stories.d.ts +25 -0
  66. package/dist/components/harnesses-section.stories.d.ts.map +1 -0
  67. package/dist/components/harnesses-section.stories.js +138 -0
  68. package/dist/components/harnesses-section.stories.js.map +1 -0
  69. package/dist/components/model-provider-usage-modal.d.ts +11 -0
  70. package/dist/components/model-provider-usage-modal.d.ts.map +1 -0
  71. package/dist/components/model-provider-usage-modal.js +349 -0
  72. package/dist/components/model-provider-usage-modal.js.map +1 -0
  73. package/dist/components/model-provider-usage-modal.stories.d.ts +21 -0
  74. package/dist/components/model-provider-usage-modal.stories.d.ts.map +1 -0
  75. package/dist/components/model-provider-usage-modal.stories.js +115 -0
  76. package/dist/components/model-provider-usage-modal.stories.js.map +1 -0
  77. package/dist/components/model-provider-usage-target.d.ts +14 -0
  78. package/dist/components/model-provider-usage-target.d.ts.map +1 -0
  79. package/dist/components/model-provider-usage-target.js +20 -0
  80. package/dist/components/model-provider-usage-target.js.map +1 -0
  81. package/dist/components/model-providers-section.d.ts +4 -0
  82. package/dist/components/model-providers-section.d.ts.map +1 -0
  83. package/dist/components/model-providers-section.js +668 -0
  84. package/dist/components/model-providers-section.js.map +1 -0
  85. package/dist/components/model-providers-section.stories.d.ts +32 -0
  86. package/dist/components/model-providers-section.stories.d.ts.map +1 -0
  87. package/dist/components/model-providers-section.stories.js +442 -0
  88. package/dist/components/model-providers-section.stories.js.map +1 -0
  89. package/dist/components/provider-search.d.ts +4 -0
  90. package/dist/components/provider-search.d.ts.map +1 -0
  91. package/dist/components/provider-search.js +13 -0
  92. package/dist/components/provider-search.js.map +1 -0
  93. package/dist/components/supported-model-provider-catalog-entry.d.ts +8 -0
  94. package/dist/components/supported-model-provider-catalog-entry.d.ts.map +1 -0
  95. package/dist/components/supported-model-provider-catalog-entry.js +9 -0
  96. package/dist/components/supported-model-provider-catalog-entry.js.map +1 -0
  97. package/dist/components/test-and-save-form.d.ts +10 -0
  98. package/dist/components/test-and-save-form.d.ts.map +1 -0
  99. package/dist/components/test-and-save-form.js +138 -0
  100. package/dist/components/test-and-save-form.js.map +1 -0
  101. package/dist/feature.d.ts +9 -0
  102. package/dist/feature.d.ts.map +1 -0
  103. package/dist/feature.js +13 -0
  104. package/dist/feature.js.map +1 -0
  105. package/dist/hooks/api/model-providers.d.ts +334 -0
  106. package/dist/hooks/api/model-providers.d.ts.map +1 -0
  107. package/dist/hooks/api/model-providers.js +190 -0
  108. package/dist/hooks/api/model-providers.js.map +1 -0
  109. package/dist/index.d.ts +9 -0
  110. package/dist/index.d.ts.map +1 -0
  111. package/dist/index.js +10 -0
  112. package/dist/index.js.map +1 -0
  113. package/dist/pages/model-provider-onboarding-page.d.ts +6 -0
  114. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -0
  115. package/dist/pages/model-provider-onboarding-page.js +295 -0
  116. package/dist/pages/model-provider-onboarding-page.js.map +1 -0
  117. package/dist/pages/model-provider-onboarding-page.stories.d.ts +27 -0
  118. package/dist/pages/model-provider-onboarding-page.stories.d.ts.map +1 -0
  119. package/dist/pages/model-provider-onboarding-page.stories.js +368 -0
  120. package/dist/pages/model-provider-onboarding-page.stories.js.map +1 -0
  121. package/dist/routes/model-provider.d.ts +5 -0
  122. package/dist/routes/model-provider.d.ts.map +1 -0
  123. package/dist/routes/model-provider.js +28 -0
  124. package/dist/routes/model-provider.js.map +1 -0
  125. package/dist/state/model-provider-onboarding.d.ts +3 -0
  126. package/dist/state/model-provider-onboarding.d.ts.map +1 -0
  127. package/dist/state/model-provider-onboarding.js +28 -0
  128. package/dist/state/model-provider-onboarding.js.map +1 -0
  129. package/dist/tsconfig.test.tsbuildinfo +1 -0
  130. package/package.json +114 -0
  131. package/src/components/add-custom-provider-card.tsx +32 -0
  132. package/src/components/agent-workflow-example.test.ts +74 -0
  133. package/src/components/agent-workflow-example.ts +55 -0
  134. package/src/components/available-provider-card.tsx +39 -0
  135. package/src/components/available-providers-grid.test.tsx +55 -0
  136. package/src/components/available-providers-grid.tsx +106 -0
  137. package/src/components/change-default-model-form.tsx +104 -0
  138. package/src/components/custom-model-provider-api-options.test.ts +18 -0
  139. package/src/components/custom-model-provider-api-options.ts +12 -0
  140. package/src/components/custom-model-provider-form.stories.tsx +91 -0
  141. package/src/components/custom-model-provider-form.tsx +710 -0
  142. package/src/components/custom-model-provider-payload.test.ts +170 -0
  143. package/src/components/custom-model-provider-payload.ts +264 -0
  144. package/src/components/custom-model-provider-slug.test.ts +40 -0
  145. package/src/components/custom-model-provider-slug.ts +23 -0
  146. package/src/components/default-model-field.tsx +85 -0
  147. package/src/components/form-errors.test.ts +159 -0
  148. package/src/components/form-errors.ts +102 -0
  149. package/src/components/harness-availability.test.ts +45 -0
  150. package/src/components/harness-availability.ts +35 -0
  151. package/src/components/harnesses-section.stories.tsx +129 -0
  152. package/src/components/harnesses-section.test.tsx +188 -0
  153. package/src/components/harnesses-section.tsx +245 -0
  154. package/src/components/model-provider-usage-modal.stories.tsx +106 -0
  155. package/src/components/model-provider-usage-modal.test.tsx +210 -0
  156. package/src/components/model-provider-usage-modal.tsx +326 -0
  157. package/src/components/model-provider-usage-target.ts +36 -0
  158. package/src/components/model-providers-section.stories.tsx +345 -0
  159. package/src/components/model-providers-section.test.tsx +831 -0
  160. package/src/components/model-providers-section.tsx +645 -0
  161. package/src/components/provider-search.test.ts +45 -0
  162. package/src/components/provider-search.ts +18 -0
  163. package/src/components/supported-model-provider-catalog-entry.ts +19 -0
  164. package/src/components/test-and-save-form.tsx +166 -0
  165. package/src/feature.ts +12 -0
  166. package/src/hooks/api/model-providers.test.ts +167 -0
  167. package/src/hooks/api/model-providers.ts +281 -0
  168. package/src/index.ts +8 -0
  169. package/src/pages/model-provider-onboarding-page.stories.tsx +284 -0
  170. package/src/pages/model-provider-onboarding-page.test.tsx +368 -0
  171. package/src/pages/model-provider-onboarding-page.tsx +290 -0
  172. package/src/routes/model-provider.tsx +20 -0
  173. package/src/state/model-provider-onboarding.test.ts +55 -0
  174. package/src/state/model-provider-onboarding.ts +34 -0
  175. package/test/fixtures/model-providers.ts +111 -0
  176. package/test/setup.ts +3 -0
  177. package/tsconfig.build.json +9 -0
  178. package/tsconfig.build.tsbuildinfo +1 -0
  179. package/tsconfig.json +3 -0
  180. package/tsconfig.test.json +8 -0
  181. package/vercel.json +8 -0
  182. package/vitest.config.ts +72 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/model-providers-section.tsx"],"sourcesContent":["import type {\n CustomModelProviderConfigDto,\n ModelProviderCatalogEntryDto,\n ModelProviderConfigDto,\n ModelProviderConfigResponseDto,\n} from '@shipfox/api-agent-dto';\nimport {QueryLoadError} from '@shipfox/client-ui';\nimport {Button, IconButton} from '@shipfox/react-ui/button';\nimport {Callout} from '@shipfox/react-ui/callout';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@shipfox/react-ui/dropdown-menu';\nimport {EmptyState} from '@shipfox/react-ui/empty-state';\nimport {Icon} from '@shipfox/react-ui/icon';\nimport {\n Modal,\n ModalBody,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalTitle,\n} from '@shipfox/react-ui/modal';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\nimport {toast} from '@shipfox/react-ui/toast';\nimport {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';\nimport {Header, Text} from '@shipfox/react-ui/typography';\nimport {cn} from '@shipfox/react-ui/utils';\nimport {useEffect, useMemo, useRef, useState} from 'react';\nimport {\n useDeleteModelProviderConfigMutation,\n useModelProviderCatalogQuery,\n useModelProviderConfigsQuery,\n useSetDefaultModelProviderMutation,\n} from '#hooks/api/model-providers.js';\nimport {AddCustomProviderCard} from './add-custom-provider-card.js';\nimport {AvailableProvidersGrid, PROVIDER_GRID_CLASS} from './available-providers-grid.js';\nimport {ChangeDefaultModelForm} from './change-default-model-form.js';\nimport {CustomModelProviderForm} from './custom-model-provider-form.js';\nimport {modelProviderConfigErrorToFormError} from './form-errors.js';\nimport {ModelProviderUsageModal} from './model-provider-usage-modal.js';\nimport {\n type ModelProviderUsageTarget,\n usageTargetFromCatalogEntry,\n usageTargetFromCustomConfig,\n} from './model-provider-usage-target.js';\nimport {customProviderCardMatchesSearch} from './provider-search.js';\nimport {\n isSupportedCatalogEntry,\n type SupportedModelProviderCatalogEntry,\n toSupportedCatalogEntry,\n} from './supported-model-provider-catalog-entry.js';\nimport {ModelProviderTestAndSaveForm} from './test-and-save-form.js';\n\nconst SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\ntype ModelProviderFormState =\n | {mode: 'configure'; entry: SupportedModelProviderCatalogEntry; config?: undefined}\n | {mode: 'edit'; entry: SupportedModelProviderCatalogEntry; config: ModelProviderConfigDto};\ntype CustomModelProviderFormState =\n | {mode: 'create'}\n | {mode: 'edit'; config: CustomModelProviderConfigDto};\ntype ModelFormState = {entry: SupportedModelProviderCatalogEntry; config: ModelProviderConfigDto};\ntype UsageTarget = {\n target: ModelProviderUsageTarget;\n initialModel: string | null;\n restoreFocusToConfiguredProviders: boolean;\n};\n\nconst USAGE_MODAL_OPEN_DELAY_MS = 250;\n\nexport function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: string}) {\n const catalogQuery = useModelProviderCatalogQuery();\n const configsQuery = useModelProviderConfigsQuery(workspaceId);\n const [formState, setFormState] = useState<ModelProviderFormState | null>(null);\n const [customFormState, setCustomFormState] = useState<CustomModelProviderFormState | null>(null);\n const [modelFormState, setModelFormState] = useState<ModelFormState | null>(null);\n const [usageTarget, setUsageTarget] = useState<UsageTarget | null>(null);\n const [pendingUsageTarget, setPendingUsageTarget] = useState<UsageTarget | null>(null);\n const configuredProvidersRegionRef = useRef<HTMLElement | null>(null);\n\n const providers = catalogQuery.data?.providers ?? [];\n const configs = configsQuery.data?.configs ?? [];\n const configsLoaded = configsQuery.data !== undefined;\n const defaultProviderId = configsQuery.data?.default_provider_id ?? null;\n const providerById = useMemo(\n () =>\n new Map<string, ModelProviderCatalogEntryDto>(\n providers.map((provider) => [provider.id, provider]),\n ),\n [providers],\n );\n const configuredIds = useMemo(\n () => new Set<string>(configs.map((config) => config.provider_id)),\n [configs],\n );\n const availableProviders = configsLoaded\n ? providers.filter(\n (provider): provider is SupportedModelProviderCatalogEntry =>\n isSupportedCatalogEntry(provider) && !configuredIds.has(provider.id),\n )\n : [];\n const unsupportedProviders = providers.filter(\n (provider) => provider.support_status === 'unsupported',\n );\n\n function closeForm() {\n setFormState(null);\n }\n\n function closeModelForm() {\n setModelFormState(null);\n }\n\n function closeCustomForm() {\n setCustomFormState(null);\n }\n\n useEffect(() => {\n if (pendingUsageTarget === null || formState !== null || customFormState !== null)\n return undefined;\n\n const timer = window.setTimeout(() => {\n setUsageTarget(pendingUsageTarget);\n setPendingUsageTarget(null);\n }, USAGE_MODAL_OPEN_DELAY_MS);\n\n return () => window.clearTimeout(timer);\n }, [customFormState, formState, pendingUsageTarget]);\n\n return (\n <div className=\"flex min-w-0 flex-col gap-32\">\n <section\n ref={configuredProvidersRegionRef}\n className=\"flex flex-col gap-16 outline-none\"\n aria-label=\"Configured providers\"\n tabIndex={-1}\n >\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Configured providers</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Workspace credentials available to agent steps.\n </Text>\n </div>\n\n {configsQuery.isPending ? (\n <ModelProviderRowsSkeleton label=\"Loading configured providers\" />\n ) : null}\n\n {configsQuery.isError && configsQuery.data === undefined ? (\n <div className={cn(SURFACE_CLASS, 'px-16')}>\n <QueryLoadError query={configsQuery} subject=\"model provider configs\" />\n </div>\n ) : null}\n\n {configsQuery.data !== undefined && configs.length === 0 ? (\n <div className={cn(SURFACE_CLASS, 'px-16')}>\n <EmptyState\n icon=\"key2Line\"\n title=\"No providers configured\"\n description=\"Configure a provider below to run agent steps with workspace-managed credentials.\"\n />\n </div>\n ) : null}\n\n {configs.length > 0 ? (\n <ul className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}>\n {configs.map((config) => {\n const entry = toSupportedCatalogEntry(providerById.get(config.provider_id));\n const builtinConfig = isBuiltinModelProviderConfig(config) ? config : undefined;\n const customConfig = isCustomModelProviderConfig(config) ? config : undefined;\n return (\n <ConfiguredProviderRow\n key={config.provider_id}\n workspaceId={workspaceId}\n config={config}\n entry={entry}\n isDefault={config.provider_id === defaultProviderId}\n onEdit={() => {\n if (entry && builtinConfig) {\n setFormState({mode: 'edit', entry, config: builtinConfig});\n } else if (customConfig) {\n setCustomFormState({mode: 'edit', config: customConfig});\n }\n }}\n onChangeDefaultModel={() => {\n if (entry && builtinConfig) setModelFormState({entry, config: builtinConfig});\n }}\n onShowUsage={() => {\n if (entry && builtinConfig) {\n setPendingUsageTarget(null);\n setUsageTarget({\n target: usageTargetFromCatalogEntry(entry),\n initialModel: builtinConfig.default_model,\n restoreFocusToConfiguredProviders: false,\n });\n } else if (customConfig) {\n setPendingUsageTarget(null);\n setUsageTarget({\n target: usageTargetFromCustomConfig(customConfig),\n initialModel: customConfig.default_model,\n restoreFocusToConfiguredProviders: false,\n });\n }\n }}\n />\n );\n })}\n </ul>\n ) : null}\n </section>\n\n <section className=\"flex flex-col gap-16\" aria-label=\"Available providers\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Available providers</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Providers that can be configured for agent steps in this workspace.\n </Text>\n </div>\n\n {catalogQuery.isPending || configsQuery.isPending ? <ModelProviderGridSkeleton /> : null}\n\n {catalogQuery.isError && catalogQuery.data === undefined ? (\n <div className={cn(SURFACE_CLASS, 'px-16')}>\n <QueryLoadError query={catalogQuery} subject=\"model provider catalog\" />\n </div>\n ) : null}\n\n {configsLoaded ? (\n <AvailableProvidersGrid\n entries={availableProviders}\n onSelect={(entry) => setFormState({mode: 'configure', entry})}\n trailingCard={\n <AddCustomProviderCard onConfigure={() => setCustomFormState({mode: 'create'})} />\n }\n trailingCardMatchesSearch={customProviderCardMatchesSearch}\n />\n ) : null}\n </section>\n\n <section className=\"flex flex-col gap-16\" aria-label=\"Unsupported providers\">\n <div className=\"flex flex-col gap-4\">\n <Header variant=\"h3\">Unsupported providers</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Providers that cannot be configured in this workspace yet.\n </Text>\n </div>\n\n {catalogQuery.isPending ? (\n <ModelProviderRowsSkeleton label=\"Loading unsupported providers\" />\n ) : null}\n\n {unsupportedProviders.length > 0 ? (\n <ul className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}>\n {unsupportedProviders.map((entry) => (\n <li key={entry.id} className=\"flex items-start gap-12 px-16 py-12 opacity-70\">\n <Icon\n name=\"forbid2Line\"\n className=\"mt-2 size-18 shrink-0 text-foreground-neutral-muted\"\n aria-hidden\n />\n <div className=\"flex min-w-0 flex-1 flex-col gap-2\">\n <Text size=\"md\" bold className=\"truncate\">\n {entry.label}\n </Text>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n {entry.unsupported_reason}\n </Text>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n\n <Modal open={formState !== null} onOpenChange={(open) => (open ? undefined : closeForm())}>\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">{modelProviderFormTitle(formState)}</ModalTitle>\n <ModalHeader>\n <Text\n size=\"lg\"\n aria-hidden=\"true\"\n className=\"overflow-ellipsis overflow-hidden whitespace-nowrap\"\n >\n {modelProviderFormTitle(formState)}\n </Text>\n </ModalHeader>\n {formState ? (\n <ModelProviderTestAndSaveForm\n workspaceId={workspaceId}\n entry={formState.entry}\n existingConfig={formState.config}\n onSaved={(savedDefaultModel) => {\n toast.success(`${formState.entry.label} saved`);\n if (formState.mode === 'configure' && configs.length === 0) {\n setPendingUsageTarget({\n target: usageTargetFromCatalogEntry(formState.entry),\n initialModel: savedDefaultModel,\n restoreFocusToConfiguredProviders: true,\n });\n }\n closeForm();\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <Modal\n open={modelFormState !== null}\n onOpenChange={(open) => (open ? undefined : closeModelForm())}\n >\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">Change default model</ModalTitle>\n <ModalHeader>\n <Text\n size=\"lg\"\n aria-hidden=\"true\"\n className=\"overflow-ellipsis overflow-hidden whitespace-nowrap\"\n >\n Change default model for {modelFormState?.entry.label}\n </Text>\n </ModalHeader>\n {modelFormState ? (\n <ChangeDefaultModelForm\n workspaceId={workspaceId}\n entry={modelFormState.entry}\n config={modelFormState.config}\n onSaved={() => {\n toast.success(`${modelFormState.entry.label} default model saved`);\n closeModelForm();\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <ModelProviderUsageModal\n target={usageTarget?.target ?? null}\n initialModel={usageTarget?.initialModel ?? null}\n workspaceDefaultHarnessId={configsQuery.data?.default_harness_id ?? null}\n open={usageTarget !== null}\n closeFocusTarget={\n usageTarget?.restoreFocusToConfiguredProviders\n ? configuredProvidersRegionRef.current\n : null\n }\n onOpenChange={(open) => {\n if (!open) setUsageTarget(null);\n }}\n />\n\n <Modal\n open={customFormState !== null}\n onOpenChange={(open) => (open ? undefined : closeCustomForm())}\n >\n <ModalContent\n aria-describedby={undefined}\n className=\"max-h-[calc(100vh-32px)] max-w-[760px]\"\n >\n <ModalTitle className=\"sr-only\">\n {customModelProviderFormTitle(customFormState)}\n </ModalTitle>\n <ModalHeader>\n <div className=\"flex min-w-0 flex-col gap-2\">\n <Text size=\"lg\" aria-hidden=\"true\" className=\"truncate\">\n {customModelProviderFormTitle(customFormState)}\n </Text>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Connect an OpenAI-, Anthropic-, or Gemini-compatible endpoint.\n </Text>\n </div>\n </ModalHeader>\n {customFormState ? (\n <CustomModelProviderForm\n workspaceId={workspaceId}\n existingConfig={customFormState.mode === 'edit' ? customFormState.config : undefined}\n onSaved={() => {\n toast.success(\n customFormState.mode === 'edit'\n ? `${customFormState.config.display_name} saved`\n : 'Custom provider saved',\n );\n closeCustomForm();\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n </div>\n );\n}\n\nfunction modelProviderFormTitle(formState: ModelProviderFormState | null): string {\n if (formState === null) return '';\n if (formState.mode === 'edit') return `Edit credentials for ${formState.entry.label}`;\n return `Configure ${formState.entry.label}`;\n}\n\nfunction customModelProviderFormTitle(formState: CustomModelProviderFormState | null): string {\n if (formState === null) return '';\n if (formState.mode === 'edit') return `Edit ${formState.config.display_name}`;\n return 'Add custom provider';\n}\n\nfunction isBuiltinModelProviderConfig(\n config: ModelProviderConfigResponseDto,\n): config is ModelProviderConfigDto {\n return config.kind === 'builtin';\n}\n\nfunction isCustomModelProviderConfig(\n config: ModelProviderConfigResponseDto,\n): config is CustomModelProviderConfigDto {\n return config.kind === 'custom';\n}\n\nfunction ConfiguredProviderRow({\n workspaceId,\n config,\n entry,\n isDefault,\n onEdit,\n onChangeDefaultModel,\n onShowUsage,\n}: {\n workspaceId: string;\n config: ModelProviderConfigResponseDto;\n entry: SupportedModelProviderCatalogEntry | undefined;\n isDefault: boolean;\n onEdit: () => void;\n onChangeDefaultModel: () => void;\n onShowUsage: () => void;\n}) {\n const setDefault = useSetDefaultModelProviderMutation();\n const deleteConfig = useDeleteModelProviderConfigMutation();\n const [defaultError, setDefaultError] = useState<string | undefined>();\n const [deleteOpen, setDeleteOpen] = useState(false);\n const [deleteError, setDeleteError] = useState<string | undefined>();\n const customConfig = isCustomModelProviderConfig(config) ? config : undefined;\n const label = customConfig?.display_name ?? entry?.label ?? config.provider_id;\n const canUse = entry !== undefined || customConfig !== undefined;\n const canEdit = entry !== undefined || customConfig !== undefined;\n const isBuiltinConfig = isBuiltinModelProviderConfig(config);\n\n async function handleSetDefault() {\n setDefaultError(undefined);\n try {\n await setDefault.mutateAsync({\n workspaceId,\n body: {provider_id: config.provider_id},\n });\n toast.success(`${label} is now the default provider`);\n } catch (error) {\n const mapped = modelProviderConfigErrorToFormError(error);\n setDefaultError(mapped.message);\n }\n }\n\n async function handleDelete() {\n setDeleteError(undefined);\n try {\n await deleteConfig.mutateAsync({workspaceId, providerId: config.provider_id});\n toast.success(`${label} deleted`);\n setDeleteOpen(false);\n } catch (error) {\n const mapped = modelProviderConfigErrorToFormError(error);\n setDeleteError(mapped.message);\n }\n }\n\n function handleDeleteOpenChange(nextOpen: boolean) {\n setDeleteOpen(nextOpen);\n if (nextOpen) {\n setDeleteError(undefined);\n deleteConfig.reset();\n }\n }\n\n return (\n <li className=\"flex flex-col gap-10 px-16 py-12 transition-colors hover:bg-background-components-hover\">\n <div className=\"flex items-center justify-between gap-12\">\n <div className=\"flex min-w-0 items-center gap-8\">\n {isDefault ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"starLine\"\n className=\"size-16 text-foreground-neutral-muted\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>Default provider</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">Default provider</span>\n </>\n ) : null}\n <Text size=\"md\" bold className=\"truncate\">\n {label}\n </Text>\n </div>\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n aria-label={`Open ${label} provider actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {!isDefault ? (\n <DropdownMenuItem\n icon=\"starLine\"\n disabled={setDefault.isPending || (!entry && !customConfig)}\n onSelect={() => {\n void handleSetDefault();\n }}\n >\n Set as default\n </DropdownMenuItem>\n ) : null}\n {isBuiltinConfig ? (\n <DropdownMenuItem\n icon=\"settings3Line\"\n disabled={!entry}\n onSelect={onChangeDefaultModel}\n >\n Change default model\n </DropdownMenuItem>\n ) : null}\n <DropdownMenuItem icon=\"bookOpenLine\" disabled={!canUse} onSelect={onShowUsage}>\n View workflow example\n </DropdownMenuItem>\n <DropdownMenuItem icon=\"editLine\" disabled={!canEdit} onSelect={onEdit}>\n {customConfig ? 'Edit' : 'Edit credentials'}\n </DropdownMenuItem>\n <DropdownMenuItem\n icon=\"deleteBinLine\"\n onSelect={() => {\n setDeleteOpen(true);\n }}\n >\n Delete\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n </div>\n {defaultError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{defaultError}</Text>\n </Callout>\n ) : null}\n <DeleteModelProviderDialog\n open={deleteOpen}\n onOpenChange={handleDeleteOpenChange}\n label={label}\n errorMessage={deleteError}\n isLoading={deleteConfig.isPending}\n onDelete={handleDelete}\n />\n </li>\n );\n}\n\nfunction DeleteModelProviderDialog({\n open,\n onOpenChange,\n label,\n errorMessage,\n isLoading,\n onDelete,\n}: {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n label: string;\n errorMessage: string | undefined;\n isLoading: boolean;\n onDelete: () => void;\n}) {\n return (\n <Modal open={open} onOpenChange={onOpenChange}>\n <ModalContent aria-describedby={undefined} className=\"max-w-[420px]\">\n <ModalTitle className=\"sr-only\">Delete model provider</ModalTitle>\n <ModalHeader title=\"Delete model provider\" />\n <ModalBody className=\"gap-16\">\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Delete {label} credentials from this workspace? Agent jobs cannot use this provider\n until it is configured again.\n </Text>\n {errorMessage ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{errorMessage}</Text>\n </Callout>\n ) : null}\n </ModalBody>\n <ModalFooter>\n <Button size=\"sm\" variant=\"secondary\" onClick={() => onOpenChange(false)}>\n Cancel\n </Button>\n <Button size=\"sm\" variant=\"danger\" isLoading={isLoading} onClick={onDelete}>\n Delete\n </Button>\n </ModalFooter>\n </ModalContent>\n </Modal>\n );\n}\n\nfunction ModelProviderRowsSkeleton({label}: {label: string}) {\n return (\n <ul\n role=\"status\"\n aria-label={label}\n className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}\n >\n {[0, 1, 2].map((row) => (\n <li key={row} className=\"flex items-center gap-12 px-16 py-12\">\n <Skeleton className=\"size-32 shrink-0\" />\n <div className=\"flex min-w-0 flex-1 flex-col gap-6\">\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"h-14 w-180\" />\n </div>\n <Skeleton className=\"h-28 w-96 shrink-0\" />\n </li>\n ))}\n </ul>\n );\n}\n\nfunction ModelProviderGridSkeleton() {\n return (\n <div role=\"status\" aria-label=\"Loading available providers\" className={PROVIDER_GRID_CLASS}>\n {[0, 1, 2, 3].map((card) => (\n <Skeleton key={card} className=\"h-136 w-full\" />\n ))}\n </div>\n );\n}\n"],"names":["QueryLoadError","Button","IconButton","Callout","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","EmptyState","Icon","Modal","ModalBody","ModalContent","ModalFooter","ModalHeader","ModalTitle","Skeleton","toast","Tooltip","TooltipContent","TooltipTrigger","Header","Text","cn","useEffect","useMemo","useRef","useState","useDeleteModelProviderConfigMutation","useModelProviderCatalogQuery","useModelProviderConfigsQuery","useSetDefaultModelProviderMutation","AddCustomProviderCard","AvailableProvidersGrid","PROVIDER_GRID_CLASS","ChangeDefaultModelForm","CustomModelProviderForm","modelProviderConfigErrorToFormError","ModelProviderUsageModal","usageTargetFromCatalogEntry","usageTargetFromCustomConfig","customProviderCardMatchesSearch","isSupportedCatalogEntry","toSupportedCatalogEntry","ModelProviderTestAndSaveForm","SURFACE_CLASS","USAGE_MODAL_OPEN_DELAY_MS","WorkspaceModelProvidersSection","workspaceId","catalogQuery","configsQuery","formState","setFormState","customFormState","setCustomFormState","modelFormState","setModelFormState","usageTarget","setUsageTarget","pendingUsageTarget","setPendingUsageTarget","configuredProvidersRegionRef","providers","data","configs","configsLoaded","undefined","defaultProviderId","default_provider_id","providerById","Map","map","provider","id","configuredIds","Set","config","provider_id","availableProviders","filter","has","unsupportedProviders","support_status","closeForm","closeModelForm","closeCustomForm","timer","window","setTimeout","clearTimeout","div","className","section","ref","aria-label","tabIndex","variant","size","isPending","ModelProviderRowsSkeleton","label","isError","query","subject","length","icon","title","description","ul","entry","get","builtinConfig","isBuiltinModelProviderConfig","customConfig","isCustomModelProviderConfig","ConfiguredProviderRow","isDefault","onEdit","mode","onChangeDefaultModel","onShowUsage","target","initialModel","default_model","restoreFocusToConfiguredProviders","ModelProviderGridSkeleton","entries","onSelect","trailingCard","onConfigure","trailingCardMatchesSearch","li","name","aria-hidden","bold","unsupported_reason","open","onOpenChange","aria-describedby","modelProviderFormTitle","existingConfig","onSaved","savedDefaultModel","success","workspaceDefaultHarnessId","default_harness_id","closeFocusTarget","current","customModelProviderFormTitle","display_name","kind","setDefault","deleteConfig","defaultError","setDefaultError","deleteOpen","setDeleteOpen","deleteError","setDeleteError","canUse","canEdit","isBuiltinConfig","handleSetDefault","mutateAsync","body","error","mapped","message","handleDelete","providerId","handleDeleteOpenChange","nextOpen","reset","asChild","span","align","disabled","role","type","DeleteModelProviderDialog","errorMessage","isLoading","onDelete","onClick","row","card"],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { StoryObj } from '@storybook/react';
2
+ type Scenario = 'mixed' | 'empty-configured' | 'all-configured' | 'custom-configured' | 'loading' | 'configs-error' | 'catalog-error' | 'long-names';
3
+ interface ModelProvidersStoryProps {
4
+ scenario: Scenario;
5
+ }
6
+ declare function ModelProvidersStory({ scenario }: ModelProvidersStoryProps): import("react").JSX.Element;
7
+ declare const meta: {
8
+ title: string;
9
+ component: typeof ModelProvidersStory;
10
+ parameters: {
11
+ layout: string;
12
+ };
13
+ args: {
14
+ scenario: "mixed";
15
+ };
16
+ };
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+ export declare const Playground: Story;
20
+ export declare const EmptyConfigured: Story;
21
+ export declare const AllConfigured: Story;
22
+ export declare const CustomConfigured: Story;
23
+ export declare const Loading: Story;
24
+ export declare const ConfigsError: Story;
25
+ export declare const CatalogError: Story;
26
+ export declare const LongNames: Story;
27
+ export declare const FilteredAvailableProviders: Story;
28
+ export declare const NoMatchingAvailableProviders: Story;
29
+ export declare const ConfigureModalOpen: Story;
30
+ export declare const CustomProviderModalOpen: Story;
31
+ export declare const WorkflowExampleModalOpen: Story;
32
+ //# sourceMappingURL=model-providers-section.stories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-providers-section.stories.d.ts","sourceRoot":"","sources":["../../src/components/model-providers-section.stories.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAO,QAAQ,EAAC,MAAM,kBAAkB,CAAC;AAQrD,KAAK,QAAQ,GACT,OAAO,GACP,kBAAkB,GAClB,gBAAgB,GAChB,mBAAmB,GACnB,SAAS,GACT,eAAe,GACf,eAAe,GACf,YAAY,CAAC;AAEjB,UAAU,wBAAwB;IAChC,QAAQ,EAAE,QAAQ,CAAC;CACpB;AA+ED,iBAAS,mBAAmB,CAAC,EAAC,QAAQ,EAAC,EAAE,wBAAwB,+BAmBhE;AAED,QAAA,MAAM,IAAI;;;;;;;;;CAKkC,CAAC;AAE7C,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,eAAO,MAAM,UAAU,EAAE,KAAU,CAAC;AAEpC,eAAO,MAAM,eAAe,EAAE,KAE7B,CAAC;AAEF,eAAO,MAAM,aAAa,EAAE,KAE3B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,KAE9B,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAErB,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAE1B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,KAE1B,CAAC;AAEF,eAAO,MAAM,SAAS,EAAE,KAEvB,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,KAUxC,CAAC;AAEF,eAAO,MAAM,4BAA4B,EAAE,KAU1C,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAOhC,CAAC;AAEF,eAAO,MAAM,uBAAuB,EAAE,KAOrC,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,KAUtC,CAAC"}
@@ -0,0 +1,442 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { configureApiClient } from '@shipfox/client-api';
3
+ import { Toaster } from '@shipfox/react-ui/toast';
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
+ import { useMemo } from 'react';
6
+ import { screen, userEvent, within } from 'storybook/test';
7
+ import { WorkspaceModelProvidersSection } from './model-providers-section.js';
8
+ const WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';
9
+ const CATALOG = [
10
+ providerEntry({
11
+ id: 'anthropic',
12
+ label: 'Anthropic',
13
+ default_model: 'claude-opus-4-8',
14
+ models: [
15
+ {
16
+ id: 'claude-opus-4-8',
17
+ label: 'Claude Opus 4.8'
18
+ },
19
+ {
20
+ id: 'claude-haiku-4-5',
21
+ label: 'Claude Haiku 4.5'
22
+ }
23
+ ]
24
+ }),
25
+ providerEntry({
26
+ id: 'openai',
27
+ label: 'OpenAI',
28
+ default_model: 'gpt-5.5-pro',
29
+ models: [
30
+ {
31
+ id: 'gpt-5.5-pro',
32
+ label: 'GPT-5.5 Pro'
33
+ },
34
+ {
35
+ id: 'gpt-5-mini',
36
+ label: 'GPT-5 Mini'
37
+ }
38
+ ]
39
+ }),
40
+ providerEntry({
41
+ id: 'xai',
42
+ label: 'xAI',
43
+ default_model: 'grok-code-fast-1',
44
+ models: [
45
+ {
46
+ id: 'grok-code-fast-1',
47
+ label: 'Grok Code Fast 1'
48
+ }
49
+ ]
50
+ }),
51
+ providerEntry({
52
+ id: 'openrouter',
53
+ label: 'OpenRouter',
54
+ default_model: 'openrouter/auto',
55
+ models: [
56
+ {
57
+ id: 'openrouter/auto',
58
+ label: 'OpenRouter Auto'
59
+ }
60
+ ]
61
+ }),
62
+ providerEntry({
63
+ id: 'google',
64
+ label: 'Google Gemini',
65
+ default_model: 'gemini-3-pro',
66
+ models: [
67
+ {
68
+ id: 'gemini-3-pro',
69
+ label: 'Gemini 3 Pro'
70
+ }
71
+ ]
72
+ }),
73
+ providerEntry({
74
+ id: 'mistral',
75
+ label: 'Mistral AI',
76
+ default_model: 'codestral-latest',
77
+ models: [
78
+ {
79
+ id: 'codestral-latest',
80
+ label: 'Codestral Latest'
81
+ }
82
+ ]
83
+ }),
84
+ providerEntry({
85
+ id: 'groq',
86
+ label: 'Groq',
87
+ default_model: 'llama-4-fast',
88
+ models: [
89
+ {
90
+ id: 'llama-4-fast',
91
+ label: 'Llama 4 Fast'
92
+ }
93
+ ]
94
+ }),
95
+ providerEntry({
96
+ id: 'deepseek',
97
+ label: 'DeepSeek',
98
+ default_model: 'deepseek-coder',
99
+ models: [
100
+ {
101
+ id: 'deepseek-coder',
102
+ label: 'DeepSeek Coder'
103
+ }
104
+ ]
105
+ }),
106
+ providerEntry({
107
+ id: 'moonshotai',
108
+ label: 'Moonshot AI',
109
+ default_model: 'kimi-k2',
110
+ models: [
111
+ {
112
+ id: 'kimi-k2',
113
+ label: 'Kimi K2'
114
+ }
115
+ ]
116
+ }),
117
+ providerEntry({
118
+ id: 'azure-openai-responses',
119
+ label: 'Azure OpenAI Responses',
120
+ default_model: 'gpt-5.5-pro',
121
+ models: [
122
+ {
123
+ id: 'gpt-5.5-pro',
124
+ label: 'GPT-5.5 Pro'
125
+ }
126
+ ]
127
+ }),
128
+ providerEntry({
129
+ id: 'amazon-bedrock',
130
+ label: 'Amazon Bedrock',
131
+ support_status: 'unsupported',
132
+ credential_fields: [],
133
+ unsupported_reason: 'AWS cloud credentials are not supported yet.',
134
+ models: []
135
+ })
136
+ ];
137
+ function ModelProvidersStory({ scenario }) {
138
+ configureApiClient({
139
+ baseUrl: 'https://api.example.test',
140
+ fetchImpl: fetchForScenario(scenario)
141
+ });
142
+ const queryClient = useMemo(()=>new QueryClient({
143
+ defaultOptions: {
144
+ queries: {
145
+ retry: false
146
+ }
147
+ }
148
+ }), []);
149
+ return /*#__PURE__*/ _jsxs(QueryClientProvider, {
150
+ client: queryClient,
151
+ children: [
152
+ /*#__PURE__*/ _jsx("div", {
153
+ className: "mx-auto w-full max-w-[760px] bg-background-neutral-background p-24",
154
+ children: /*#__PURE__*/ _jsx(WorkspaceModelProvidersSection, {
155
+ workspaceId: WORKSPACE_ID
156
+ })
157
+ }),
158
+ /*#__PURE__*/ _jsx(Toaster, {})
159
+ ]
160
+ });
161
+ }
162
+ const meta = {
163
+ title: 'Agent/ProviderSettings',
164
+ component: ModelProvidersStory,
165
+ parameters: {
166
+ layout: 'fullscreen'
167
+ },
168
+ args: {
169
+ scenario: 'mixed'
170
+ }
171
+ };
172
+ export default meta;
173
+ export const Playground = {};
174
+ export const EmptyConfigured = {
175
+ args: {
176
+ scenario: 'empty-configured'
177
+ }
178
+ };
179
+ export const AllConfigured = {
180
+ args: {
181
+ scenario: 'all-configured'
182
+ }
183
+ };
184
+ export const CustomConfigured = {
185
+ args: {
186
+ scenario: 'custom-configured'
187
+ }
188
+ };
189
+ export const Loading = {
190
+ args: {
191
+ scenario: 'loading'
192
+ }
193
+ };
194
+ export const ConfigsError = {
195
+ args: {
196
+ scenario: 'configs-error'
197
+ }
198
+ };
199
+ export const CatalogError = {
200
+ args: {
201
+ scenario: 'catalog-error'
202
+ }
203
+ };
204
+ export const LongNames = {
205
+ args: {
206
+ scenario: 'long-names'
207
+ }
208
+ };
209
+ export const FilteredAvailableProviders = {
210
+ args: {
211
+ scenario: 'empty-configured'
212
+ },
213
+ play: async ({ canvasElement })=>{
214
+ const canvas = within(canvasElement);
215
+ await userEvent.type(await canvas.findByRole('searchbox', {
216
+ name: 'Search providers'
217
+ }), 'openrouter');
218
+ await canvas.findByRole('button', {
219
+ name: 'Configure OpenRouter'
220
+ });
221
+ }
222
+ };
223
+ export const NoMatchingAvailableProviders = {
224
+ args: {
225
+ scenario: 'empty-configured'
226
+ },
227
+ play: async ({ canvasElement })=>{
228
+ const canvas = within(canvasElement);
229
+ await userEvent.type(await canvas.findByRole('searchbox', {
230
+ name: 'Search providers'
231
+ }), 'not-a-provider');
232
+ await canvas.findByRole('button', {
233
+ name: 'Clear search'
234
+ });
235
+ }
236
+ };
237
+ export const ConfigureModalOpen = {
238
+ args: {
239
+ scenario: 'empty-configured'
240
+ },
241
+ play: async ({ canvasElement })=>{
242
+ const canvas = within(canvasElement);
243
+ await userEvent.click(await canvas.findByRole('button', {
244
+ name: 'Configure Anthropic'
245
+ }));
246
+ await screen.findByLabelText('API key');
247
+ }
248
+ };
249
+ export const CustomProviderModalOpen = {
250
+ args: {
251
+ scenario: 'empty-configured'
252
+ },
253
+ play: async ({ canvasElement })=>{
254
+ const canvas = within(canvasElement);
255
+ await userEvent.click(await canvas.findByRole('button', {
256
+ name: 'Configure custom provider'
257
+ }));
258
+ await screen.findByRole('dialog', {
259
+ name: 'Add custom provider'
260
+ });
261
+ }
262
+ };
263
+ export const WorkflowExampleModalOpen = {
264
+ args: {
265
+ scenario: 'mixed'
266
+ },
267
+ play: async ({ canvasElement })=>{
268
+ const canvas = within(canvasElement);
269
+ await userEvent.click(await canvas.findByRole('button', {
270
+ name: 'Open Anthropic provider actions'
271
+ }));
272
+ await userEvent.click(await screen.findByRole('menuitem', {
273
+ name: 'View workflow example'
274
+ }));
275
+ await screen.findByRole('dialog', {
276
+ name: 'Use Anthropic in a workflow'
277
+ });
278
+ }
279
+ };
280
+ function fetchForScenario(scenario) {
281
+ return (input)=>{
282
+ const url = requestUrl(input);
283
+ if (scenario === 'loading') return new Promise(()=>undefined);
284
+ if (url.pathname.endsWith('/agent/model-provider-catalog')) {
285
+ if (scenario === 'catalog-error') return Promise.resolve(errorResponse());
286
+ return Promise.resolve(jsonResponse({
287
+ providers: catalogForScenario(scenario)
288
+ }));
289
+ }
290
+ if (url.pathname.endsWith('/agent/model-providers')) {
291
+ if (scenario === 'configs-error') return Promise.resolve(errorResponse());
292
+ return Promise.resolve(jsonResponse(configsForScenario(scenario)));
293
+ }
294
+ return Promise.resolve(jsonResponse({}, {
295
+ status: 404
296
+ }));
297
+ };
298
+ }
299
+ function catalogForScenario(scenario) {
300
+ if (scenario !== 'long-names') return CATALOG;
301
+ return [
302
+ providerEntry({
303
+ id: 'anthropic',
304
+ label: 'Anthropic Enterprise Production Claude Provider With Long Label',
305
+ default_model: 'claude-opus-4-8'
306
+ }),
307
+ providerEntry({
308
+ id: 'openai',
309
+ label: 'OpenAI Platform Shared Workspace Credentials With Long Label',
310
+ default_model: 'gpt-5.5-pro',
311
+ models: [
312
+ {
313
+ id: 'gpt-5.5-pro',
314
+ label: 'GPT-5.5 Pro'
315
+ }
316
+ ]
317
+ })
318
+ ];
319
+ }
320
+ function configsForScenario(scenario) {
321
+ if (scenario === 'empty-configured' || scenario === 'configs-error') {
322
+ return {
323
+ configs: [],
324
+ default_provider_id: null
325
+ };
326
+ }
327
+ if (scenario === 'all-configured') {
328
+ return {
329
+ configs: CATALOG.filter((entry)=>entry.support_status === 'supported').map((entry)=>providerConfig({
330
+ provider_id: entry.id
331
+ })),
332
+ default_provider_id: 'anthropic'
333
+ };
334
+ }
335
+ if (scenario === 'custom-configured') {
336
+ return {
337
+ configs: [
338
+ providerConfig({
339
+ provider_id: 'anthropic'
340
+ }),
341
+ customProviderConfig()
342
+ ],
343
+ default_provider_id: 'local-vllm'
344
+ };
345
+ }
346
+ return {
347
+ configs: [
348
+ providerConfig({
349
+ provider_id: 'anthropic'
350
+ })
351
+ ],
352
+ default_provider_id: 'anthropic'
353
+ };
354
+ }
355
+ function customProviderConfig() {
356
+ return {
357
+ kind: 'custom',
358
+ provider_id: 'local-vllm',
359
+ display_name: 'Local vLLM',
360
+ api: 'openai-completions',
361
+ base_url: 'http://localhost:8000/v1',
362
+ headers: [
363
+ {
364
+ name: 'x-region',
365
+ value: 'us'
366
+ }
367
+ ],
368
+ secret_header_names: [
369
+ 'authorization'
370
+ ],
371
+ models: [
372
+ {
373
+ id: 'llama-3.1',
374
+ label: 'Llama 3.1'
375
+ },
376
+ {
377
+ id: 'qwen-coder',
378
+ label: 'Qwen Coder'
379
+ }
380
+ ],
381
+ default_model: 'llama-3.1',
382
+ created_at: '2026-05-08T00:00:00.000Z',
383
+ updated_at: '2026-05-08T00:00:00.000Z'
384
+ };
385
+ }
386
+ function providerEntry(overrides = {}) {
387
+ return {
388
+ id: 'anthropic',
389
+ label: 'Anthropic',
390
+ support_status: 'supported',
391
+ default_model: null,
392
+ credential_fields: [
393
+ {
394
+ key: 'api_key',
395
+ label: 'API key',
396
+ secret: true
397
+ }
398
+ ],
399
+ unsupported_reason: null,
400
+ models: [
401
+ {
402
+ id: 'claude-opus-4-8',
403
+ label: 'Claude Opus 4.8'
404
+ }
405
+ ],
406
+ ...overrides
407
+ };
408
+ }
409
+ function providerConfig(overrides = {}) {
410
+ return {
411
+ kind: 'builtin',
412
+ provider_id: 'anthropic',
413
+ default_model: null,
414
+ created_at: '2026-05-08T00:00:00.000Z',
415
+ updated_at: '2026-05-08T00:00:00.000Z',
416
+ ...overrides
417
+ };
418
+ }
419
+ function requestUrl(input) {
420
+ if (typeof input === 'string') return new URL(input);
421
+ if (input instanceof URL) return input;
422
+ return new URL(input.url);
423
+ }
424
+ function jsonResponse(body, init = {}) {
425
+ return new Response(JSON.stringify(body), {
426
+ status: 200,
427
+ headers: {
428
+ 'content-type': 'application/json'
429
+ },
430
+ ...init
431
+ });
432
+ }
433
+ function errorResponse() {
434
+ return jsonResponse({
435
+ code: 'server-error'
436
+ }, {
437
+ status: 500,
438
+ statusText: 'Server error'
439
+ });
440
+ }
441
+
442
+ //# sourceMappingURL=model-providers-section.stories.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/model-providers-section.stories.tsx"],"sourcesContent":["import type {\n CustomModelProviderConfigDto,\n ModelProviderCatalogEntryDto,\n ModelProviderConfigDto,\n SupportedModelProviderId,\n} from '@shipfox/api-agent-dto';\nimport {configureApiClient} from '@shipfox/client-api';\nimport {Toaster} from '@shipfox/react-ui/toast';\nimport type {Meta, StoryObj} from '@storybook/react';\nimport {QueryClient, QueryClientProvider} from '@tanstack/react-query';\nimport {useMemo} from 'react';\nimport {screen, userEvent, within} from 'storybook/test';\nimport {WorkspaceModelProvidersSection} from './model-providers-section.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\n\ntype Scenario =\n | 'mixed'\n | 'empty-configured'\n | 'all-configured'\n | 'custom-configured'\n | 'loading'\n | 'configs-error'\n | 'catalog-error'\n | 'long-names';\n\ninterface ModelProvidersStoryProps {\n scenario: Scenario;\n}\n\nconst CATALOG: ModelProviderCatalogEntryDto[] = [\n providerEntry({\n id: 'anthropic',\n label: 'Anthropic',\n default_model: 'claude-opus-4-8',\n models: [\n {id: 'claude-opus-4-8', label: 'Claude Opus 4.8'},\n {id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5'},\n ],\n }),\n providerEntry({\n id: 'openai',\n label: 'OpenAI',\n default_model: 'gpt-5.5-pro',\n models: [\n {id: 'gpt-5.5-pro', label: 'GPT-5.5 Pro'},\n {id: 'gpt-5-mini', label: 'GPT-5 Mini'},\n ],\n }),\n providerEntry({\n id: 'xai',\n label: 'xAI',\n default_model: 'grok-code-fast-1',\n models: [{id: 'grok-code-fast-1', label: 'Grok Code Fast 1'}],\n }),\n providerEntry({\n id: 'openrouter',\n label: 'OpenRouter',\n default_model: 'openrouter/auto',\n models: [{id: 'openrouter/auto', label: 'OpenRouter Auto'}],\n }),\n providerEntry({\n id: 'google',\n label: 'Google Gemini',\n default_model: 'gemini-3-pro',\n models: [{id: 'gemini-3-pro', label: 'Gemini 3 Pro'}],\n }),\n providerEntry({\n id: 'mistral',\n label: 'Mistral AI',\n default_model: 'codestral-latest',\n models: [{id: 'codestral-latest', label: 'Codestral Latest'}],\n }),\n providerEntry({\n id: 'groq',\n label: 'Groq',\n default_model: 'llama-4-fast',\n models: [{id: 'llama-4-fast', label: 'Llama 4 Fast'}],\n }),\n providerEntry({\n id: 'deepseek',\n label: 'DeepSeek',\n default_model: 'deepseek-coder',\n models: [{id: 'deepseek-coder', label: 'DeepSeek Coder'}],\n }),\n providerEntry({\n id: 'moonshotai',\n label: 'Moonshot AI',\n default_model: 'kimi-k2',\n models: [{id: 'kimi-k2', label: 'Kimi K2'}],\n }),\n providerEntry({\n id: 'azure-openai-responses',\n label: 'Azure OpenAI Responses',\n default_model: 'gpt-5.5-pro',\n models: [{id: 'gpt-5.5-pro', label: 'GPT-5.5 Pro'}],\n }),\n providerEntry({\n id: 'amazon-bedrock',\n label: 'Amazon Bedrock',\n support_status: 'unsupported',\n credential_fields: [],\n unsupported_reason: 'AWS cloud credentials are not supported yet.',\n models: [],\n }),\n];\n\nfunction ModelProvidersStory({scenario}: ModelProvidersStoryProps) {\n configureApiClient({\n baseUrl: 'https://api.example.test',\n fetchImpl: fetchForScenario(scenario),\n });\n\n const queryClient = useMemo(\n () => new QueryClient({defaultOptions: {queries: {retry: false}}}),\n [],\n );\n\n return (\n <QueryClientProvider client={queryClient}>\n <div className=\"mx-auto w-full max-w-[760px] bg-background-neutral-background p-24\">\n <WorkspaceModelProvidersSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Agent/ProviderSettings',\n component: ModelProvidersStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'mixed'},\n} satisfies Meta<typeof ModelProvidersStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const EmptyConfigured: Story = {\n args: {scenario: 'empty-configured'},\n};\n\nexport const AllConfigured: Story = {\n args: {scenario: 'all-configured'},\n};\n\nexport const CustomConfigured: Story = {\n args: {scenario: 'custom-configured'},\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const ConfigsError: Story = {\n args: {scenario: 'configs-error'},\n};\n\nexport const CatalogError: Story = {\n args: {scenario: 'catalog-error'},\n};\n\nexport const LongNames: Story = {\n args: {scenario: 'long-names'},\n};\n\nexport const FilteredAvailableProviders: Story = {\n args: {scenario: 'empty-configured'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n await userEvent.type(\n await canvas.findByRole('searchbox', {name: 'Search providers'}),\n 'openrouter',\n );\n await canvas.findByRole('button', {name: 'Configure OpenRouter'});\n },\n};\n\nexport const NoMatchingAvailableProviders: Story = {\n args: {scenario: 'empty-configured'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n await userEvent.type(\n await canvas.findByRole('searchbox', {name: 'Search providers'}),\n 'not-a-provider',\n );\n await canvas.findByRole('button', {name: 'Clear search'});\n },\n};\n\nexport const ConfigureModalOpen: Story = {\n args: {scenario: 'empty-configured'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n await userEvent.click(await canvas.findByRole('button', {name: 'Configure Anthropic'}));\n await screen.findByLabelText('API key');\n },\n};\n\nexport const CustomProviderModalOpen: Story = {\n args: {scenario: 'empty-configured'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n await userEvent.click(await canvas.findByRole('button', {name: 'Configure custom provider'}));\n await screen.findByRole('dialog', {name: 'Add custom provider'});\n },\n};\n\nexport const WorkflowExampleModalOpen: Story = {\n args: {scenario: 'mixed'},\n play: async ({canvasElement}) => {\n const canvas = within(canvasElement);\n await userEvent.click(\n await canvas.findByRole('button', {name: 'Open Anthropic provider actions'}),\n );\n await userEvent.click(await screen.findByRole('menuitem', {name: 'View workflow example'}));\n await screen.findByRole('dialog', {name: 'Use Anthropic in a workflow'});\n },\n};\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return (input) => {\n const url = requestUrl(input);\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (url.pathname.endsWith('/agent/model-provider-catalog')) {\n if (scenario === 'catalog-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse({providers: catalogForScenario(scenario)}));\n }\n if (url.pathname.endsWith('/agent/model-providers')) {\n if (scenario === 'configs-error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse(configsForScenario(scenario)));\n }\n return Promise.resolve(jsonResponse({}, {status: 404}));\n };\n}\n\nfunction catalogForScenario(scenario: Scenario): ModelProviderCatalogEntryDto[] {\n if (scenario !== 'long-names') return CATALOG;\n return [\n providerEntry({\n id: 'anthropic',\n label: 'Anthropic Enterprise Production Claude Provider With Long Label',\n default_model: 'claude-opus-4-8',\n }),\n providerEntry({\n id: 'openai',\n label: 'OpenAI Platform Shared Workspace Credentials With Long Label',\n default_model: 'gpt-5.5-pro',\n models: [{id: 'gpt-5.5-pro', label: 'GPT-5.5 Pro'}],\n }),\n ];\n}\n\nfunction configsForScenario(scenario: Scenario) {\n if (scenario === 'empty-configured' || scenario === 'configs-error') {\n return {configs: [], default_provider_id: null};\n }\n if (scenario === 'all-configured') {\n return {\n configs: CATALOG.filter((entry) => entry.support_status === 'supported').map((entry) =>\n providerConfig({\n provider_id: entry.id as SupportedModelProviderId,\n }),\n ),\n default_provider_id: 'anthropic',\n };\n }\n if (scenario === 'custom-configured') {\n return {\n configs: [providerConfig({provider_id: 'anthropic'}), customProviderConfig()],\n default_provider_id: 'local-vllm',\n };\n }\n return {\n configs: [providerConfig({provider_id: 'anthropic'})],\n default_provider_id: 'anthropic',\n };\n}\n\nfunction customProviderConfig(): CustomModelProviderConfigDto {\n return {\n kind: 'custom',\n provider_id: 'local-vllm',\n display_name: 'Local vLLM',\n api: 'openai-completions',\n base_url: 'http://localhost:8000/v1',\n headers: [{name: 'x-region', value: 'us'}],\n secret_header_names: ['authorization'],\n models: [\n {id: 'llama-3.1', label: 'Llama 3.1'},\n {id: 'qwen-coder', label: 'Qwen Coder'},\n ],\n default_model: 'llama-3.1',\n created_at: '2026-05-08T00:00:00.000Z',\n updated_at: '2026-05-08T00:00:00.000Z',\n };\n}\n\nfunction providerEntry(\n overrides: Partial<ModelProviderCatalogEntryDto> = {},\n): ModelProviderCatalogEntryDto {\n return {\n id: 'anthropic',\n label: 'Anthropic',\n support_status: 'supported',\n default_model: null,\n credential_fields: [{key: 'api_key', label: 'API key', secret: true}],\n unsupported_reason: null,\n models: [{id: 'claude-opus-4-8', label: 'Claude Opus 4.8'}],\n ...overrides,\n };\n}\n\nfunction providerConfig(\n overrides: Partial<Omit<ModelProviderConfigDto, 'kind'>> = {},\n): ModelProviderConfigDto {\n return {\n kind: 'builtin',\n provider_id: 'anthropic',\n default_model: null,\n created_at: '2026-05-08T00:00:00.000Z',\n updated_at: '2026-05-08T00:00:00.000Z',\n ...overrides,\n };\n}\n\nfunction requestUrl(input: RequestInfo | URL): URL {\n if (typeof input === 'string') return new URL(input);\n if (input instanceof URL) return input;\n return new URL(input.url);\n}\n\nfunction jsonResponse(body: unknown, init: ResponseInit = {}) {\n return new Response(JSON.stringify(body), {\n status: 200,\n headers: {'content-type': 'application/json'},\n ...init,\n });\n}\n\nfunction errorResponse() {\n return jsonResponse({code: 'server-error'}, {status: 500, statusText: 'Server error'});\n}\n"],"names":["configureApiClient","Toaster","QueryClient","QueryClientProvider","useMemo","screen","userEvent","within","WorkspaceModelProvidersSection","WORKSPACE_ID","CATALOG","providerEntry","id","label","default_model","models","support_status","credential_fields","unsupported_reason","ModelProvidersStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","parameters","layout","args","Playground","EmptyConfigured","AllConfigured","CustomConfigured","Loading","ConfigsError","CatalogError","LongNames","FilteredAvailableProviders","play","canvasElement","canvas","type","findByRole","name","NoMatchingAvailableProviders","ConfigureModalOpen","click","findByLabelText","CustomProviderModalOpen","WorkflowExampleModalOpen","input","url","requestUrl","Promise","undefined","pathname","endsWith","resolve","errorResponse","jsonResponse","providers","catalogForScenario","configsForScenario","status","configs","default_provider_id","filter","entry","map","providerConfig","provider_id","customProviderConfig","kind","display_name","api","base_url","headers","value","secret_header_names","created_at","updated_at","overrides","key","secret","URL","body","init","Response","JSON","stringify","code","statusText"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ModelProviderCatalogEntryDto } from '@shipfox/api-agent-dto';
2
+ export declare function providerMatchesSearch(entry: ModelProviderCatalogEntryDto, query: string): boolean;
3
+ export declare function customProviderCardMatchesSearch(query: string): boolean;
4
+ //# sourceMappingURL=provider-search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-search.d.ts","sourceRoot":"","sources":["../../src/components/provider-search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,4BAA4B,EAAC,MAAM,wBAAwB,CAAC;AAEzE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,4BAA4B,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAQjG;AAED,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAKtE"}
@@ -0,0 +1,13 @@
1
+ export function providerMatchesSearch(entry, query) {
2
+ const needle = query.trim().toLowerCase();
3
+ if (needle === '') return true;
4
+ const haystack = `${entry.label} ${entry.id} ${entry.models.map((model)=>`${model.id} ${model.label}`).join(' ')}`.toLowerCase();
5
+ return haystack.includes(needle);
6
+ }
7
+ export function customProviderCardMatchesSearch(query) {
8
+ const needle = query.trim().toLowerCase();
9
+ if (needle === '') return true;
10
+ return 'custom provider openai anthropic google endpoint https public'.includes(needle);
11
+ }
12
+
13
+ //# sourceMappingURL=provider-search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/provider-search.ts"],"sourcesContent":["import type {ModelProviderCatalogEntryDto} from '@shipfox/api-agent-dto';\n\nexport function providerMatchesSearch(entry: ModelProviderCatalogEntryDto, query: string): boolean {\n const needle = query.trim().toLowerCase();\n if (needle === '') return true;\n\n const haystack = `${entry.label} ${entry.id} ${entry.models\n .map((model) => `${model.id} ${model.label}`)\n .join(' ')}`.toLowerCase();\n return haystack.includes(needle);\n}\n\nexport function customProviderCardMatchesSearch(query: string): boolean {\n const needle = query.trim().toLowerCase();\n if (needle === '') return true;\n\n return 'custom provider openai anthropic google endpoint https public'.includes(needle);\n}\n"],"names":["providerMatchesSearch","entry","query","needle","trim","toLowerCase","haystack","label","id","models","map","model","join","includes","customProviderCardMatchesSearch"],"mappings":"AAEA,OAAO,SAASA,sBAAsBC,KAAmC,EAAEC,KAAa;IACtF,MAAMC,SAASD,MAAME,IAAI,GAAGC,WAAW;IACvC,IAAIF,WAAW,IAAI,OAAO;IAE1B,MAAMG,WAAW,GAAGL,MAAMM,KAAK,CAAC,CAAC,EAAEN,MAAMO,EAAE,CAAC,CAAC,EAAEP,MAAMQ,MAAM,CACxDC,GAAG,CAAC,CAACC,QAAU,GAAGA,MAAMH,EAAE,CAAC,CAAC,EAAEG,MAAMJ,KAAK,EAAE,EAC3CK,IAAI,CAAC,MAAM,CAACP,WAAW;IAC1B,OAAOC,SAASO,QAAQ,CAACV;AAC3B;AAEA,OAAO,SAASW,gCAAgCZ,KAAa;IAC3D,MAAMC,SAASD,MAAME,IAAI,GAAGC,WAAW;IACvC,IAAIF,WAAW,IAAI,OAAO;IAE1B,OAAO,gEAAgEU,QAAQ,CAACV;AAClF"}
@@ -0,0 +1,8 @@
1
+ import type { ModelProviderCatalogEntryDto, SupportedModelProviderId } from '@shipfox/api-agent-dto';
2
+ export type SupportedModelProviderCatalogEntry = ModelProviderCatalogEntryDto & {
3
+ id: SupportedModelProviderId;
4
+ support_status: 'supported';
5
+ };
6
+ export declare function isSupportedCatalogEntry(entry: ModelProviderCatalogEntryDto | undefined): entry is SupportedModelProviderCatalogEntry;
7
+ export declare function toSupportedCatalogEntry(entry: ModelProviderCatalogEntryDto | undefined): SupportedModelProviderCatalogEntry | undefined;
8
+ //# sourceMappingURL=supported-model-provider-catalog-entry.d.ts.map