@shipfox/client-agent 5.0.0 → 6.0.2

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 (168) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +57 -0
  3. package/dist/components/available-provider-card.d.ts +2 -2
  4. package/dist/components/available-provider-card.d.ts.map +1 -1
  5. package/dist/components/available-provider-card.js.map +1 -1
  6. package/dist/components/available-providers-grid.d.ts +2 -2
  7. package/dist/components/available-providers-grid.d.ts.map +1 -1
  8. package/dist/components/available-providers-grid.js +1 -1
  9. package/dist/components/available-providers-grid.js.map +1 -1
  10. package/dist/components/change-default-model-form.d.ts +3 -4
  11. package/dist/components/change-default-model-form.d.ts.map +1 -1
  12. package/dist/components/change-default-model-form.js +2 -4
  13. package/dist/components/change-default-model-form.js.map +1 -1
  14. package/dist/components/custom-model-provider-api-options.d.ts +3 -3
  15. package/dist/components/custom-model-provider-api-options.d.ts.map +1 -1
  16. package/dist/components/custom-model-provider-api-options.js.map +1 -1
  17. package/dist/components/custom-model-provider-form.d.ts +2 -2
  18. package/dist/components/custom-model-provider-form.d.ts.map +1 -1
  19. package/dist/components/custom-model-provider-form.js +12 -10
  20. package/dist/components/custom-model-provider-form.js.map +1 -1
  21. package/dist/components/custom-model-provider-form.stories.d.ts.map +1 -1
  22. package/dist/components/custom-model-provider-form.stories.js +11 -11
  23. package/dist/components/custom-model-provider-form.stories.js.map +1 -1
  24. package/dist/components/custom-model-provider-payload.d.ts +9 -9
  25. package/dist/components/custom-model-provider-payload.d.ts.map +1 -1
  26. package/dist/components/custom-model-provider-payload.js +42 -26
  27. package/dist/components/custom-model-provider-payload.js.map +1 -1
  28. package/dist/components/custom-model-provider-slug.d.ts +1 -1
  29. package/dist/components/custom-model-provider-slug.d.ts.map +1 -1
  30. package/dist/components/custom-model-provider-slug.js +3 -3
  31. package/dist/components/custom-model-provider-slug.js.map +1 -1
  32. package/dist/components/default-model-field.d.ts +2 -2
  33. package/dist/components/default-model-field.d.ts.map +1 -1
  34. package/dist/components/default-model-field.js +1 -1
  35. package/dist/components/default-model-field.js.map +1 -1
  36. package/dist/components/harness-availability.d.ts +4 -7
  37. package/dist/components/harness-availability.d.ts.map +1 -1
  38. package/dist/components/harness-availability.js +2 -11
  39. package/dist/components/harness-availability.js.map +1 -1
  40. package/dist/components/harnesses-section.d.ts.map +1 -1
  41. package/dist/components/harnesses-section.js +4 -6
  42. package/dist/components/harnesses-section.js.map +1 -1
  43. package/dist/components/model-provider-usage-modal.d.ts +2 -2
  44. package/dist/components/model-provider-usage-modal.d.ts.map +1 -1
  45. package/dist/components/model-provider-usage-modal.js +4 -4
  46. package/dist/components/model-provider-usage-modal.js.map +1 -1
  47. package/dist/components/model-provider-usage-modal.stories.d.ts.map +1 -1
  48. package/dist/components/model-provider-usage-modal.stories.js +14 -16
  49. package/dist/components/model-provider-usage-modal.stories.js.map +1 -1
  50. package/dist/components/model-provider-usage-target.d.ts +5 -5
  51. package/dist/components/model-provider-usage-target.d.ts.map +1 -1
  52. package/dist/components/model-provider-usage-target.js +10 -8
  53. package/dist/components/model-provider-usage-target.js.map +1 -1
  54. package/dist/components/model-providers-section.d.ts.map +1 -1
  55. package/dist/components/model-providers-section.js +112 -94
  56. package/dist/components/model-providers-section.js.map +1 -1
  57. package/dist/components/model-providers-section.stories.js +8 -4
  58. package/dist/components/model-providers-section.stories.js.map +1 -1
  59. package/dist/components/test-and-save-form.d.ts +3 -3
  60. package/dist/components/test-and-save-form.d.ts.map +1 -1
  61. package/dist/components/test-and-save-form.js +7 -7
  62. package/dist/components/test-and-save-form.js.map +1 -1
  63. package/dist/core/harness-policy.d.ts +11 -0
  64. package/dist/core/harness-policy.d.ts.map +1 -0
  65. package/dist/core/harness-policy.js +71 -0
  66. package/dist/core/harness-policy.js.map +1 -0
  67. package/dist/core/model-provider-management-reducer.d.ts +51 -0
  68. package/dist/core/model-provider-management-reducer.d.ts.map +1 -0
  69. package/dist/core/model-provider-management-reducer.js +43 -0
  70. package/dist/core/model-provider-management-reducer.js.map +1 -0
  71. package/dist/core/models.d.ts +120 -0
  72. package/dist/core/models.d.ts.map +1 -0
  73. package/dist/core/models.js +3 -0
  74. package/dist/core/models.js.map +1 -0
  75. package/dist/core/onboarding-reducer.d.ts +36 -0
  76. package/dist/core/onboarding-reducer.d.ts.map +1 -0
  77. package/dist/core/onboarding-reducer.js +54 -0
  78. package/dist/core/onboarding-reducer.js.map +1 -0
  79. package/dist/core/provider-policy.d.ts +9 -0
  80. package/dist/core/provider-policy.d.ts.map +1 -0
  81. package/dist/core/provider-policy.js +37 -0
  82. package/dist/core/provider-policy.js.map +1 -0
  83. package/dist/feature.d.ts +7 -0
  84. package/dist/feature.d.ts.map +1 -1
  85. package/dist/feature.js +10 -9
  86. package/dist/feature.js.map +1 -1
  87. package/dist/hooks/api/model-provider-command-mapper.d.ts +11 -0
  88. package/dist/hooks/api/model-provider-command-mapper.d.ts.map +1 -0
  89. package/dist/hooks/api/model-provider-command-mapper.js +144 -0
  90. package/dist/hooks/api/model-provider-command-mapper.js.map +1 -0
  91. package/dist/hooks/api/model-provider-mapper.d.ts +11 -0
  92. package/dist/hooks/api/model-provider-mapper.d.ts.map +1 -0
  93. package/dist/hooks/api/model-provider-mapper.js +94 -0
  94. package/dist/hooks/api/model-provider-mapper.js.map +1 -0
  95. package/dist/hooks/api/model-providers.d.ts +60 -295
  96. package/dist/hooks/api/model-providers.d.ts.map +1 -1
  97. package/dist/hooks/api/model-providers.js +51 -37
  98. package/dist/hooks/api/model-providers.js.map +1 -1
  99. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
  100. package/dist/pages/model-provider-onboarding-page.js +57 -45
  101. package/dist/pages/model-provider-onboarding-page.js.map +1 -1
  102. package/dist/routes/inputs.d.ts +4 -0
  103. package/dist/routes/inputs.d.ts.map +1 -0
  104. package/dist/routes/inputs.js +12 -0
  105. package/dist/routes/inputs.js.map +1 -0
  106. package/dist/routes/model-provider.d.ts.map +1 -1
  107. package/dist/routes/model-provider.js +4 -5
  108. package/dist/routes/model-provider.js.map +1 -1
  109. package/dist/state/model-provider-onboarding.d.ts.map +1 -1
  110. package/dist/state/model-provider-onboarding.js +9 -18
  111. package/dist/state/model-provider-onboarding.js.map +1 -1
  112. package/dist/tsconfig.test.tsbuildinfo +1 -1
  113. package/package.json +9 -10
  114. package/src/components/available-provider-card.tsx +2 -2
  115. package/src/components/available-providers-grid.tsx +3 -3
  116. package/src/components/change-default-model-form.tsx +5 -6
  117. package/src/components/custom-model-provider-api-options.ts +3 -3
  118. package/src/components/custom-model-provider-form.stories.tsx +13 -13
  119. package/src/components/custom-model-provider-form.tsx +19 -12
  120. package/src/components/custom-model-provider-payload.test.ts +22 -22
  121. package/src/components/custom-model-provider-payload.ts +69 -54
  122. package/src/components/custom-model-provider-slug.test.ts +1 -1
  123. package/src/components/custom-model-provider-slug.ts +6 -3
  124. package/src/components/default-model-field.tsx +5 -5
  125. package/src/components/harness-availability.test.ts +13 -10
  126. package/src/components/harness-availability.ts +4 -28
  127. package/src/components/harnesses-section.tsx +7 -11
  128. package/src/components/model-provider-usage-modal.stories.tsx +18 -23
  129. package/src/components/model-provider-usage-modal.test.tsx +35 -7
  130. package/src/components/model-provider-usage-modal.tsx +12 -16
  131. package/src/components/model-provider-usage-target.ts +15 -18
  132. package/src/components/model-providers-section.stories.tsx +4 -1
  133. package/src/components/model-providers-section.tsx +112 -125
  134. package/src/components/test-and-save-form.tsx +13 -17
  135. package/src/core/harness-policy.test.ts +15 -0
  136. package/src/core/harness-policy.ts +83 -0
  137. package/src/core/model-provider-management-reducer.test.ts +59 -0
  138. package/src/core/model-provider-management-reducer.ts +72 -0
  139. package/src/core/models.ts +143 -0
  140. package/src/core/onboarding-reducer.test.ts +44 -0
  141. package/src/core/onboarding-reducer.ts +64 -0
  142. package/src/core/provider-policy.ts +53 -0
  143. package/src/feature.ts +12 -10
  144. package/src/hooks/api/model-provider-command-mapper.ts +122 -0
  145. package/src/hooks/api/model-provider-mapper.test.ts +27 -0
  146. package/src/hooks/api/model-provider-mapper.ts +120 -0
  147. package/src/hooks/api/model-providers.test.ts +15 -15
  148. package/src/hooks/api/model-providers.ts +146 -81
  149. package/src/pages/model-provider-onboarding-page.tsx +62 -56
  150. package/src/routes/inputs.test.ts +11 -0
  151. package/src/routes/inputs.ts +9 -0
  152. package/src/routes/model-provider.tsx +4 -3
  153. package/src/state/model-provider-onboarding.test.ts +4 -4
  154. package/src/state/model-provider-onboarding.ts +13 -23
  155. package/test/fixtures/model-providers.ts +1 -1
  156. package/tsconfig.build.tsbuildinfo +1 -1
  157. package/vercel.json +5 -0
  158. package/dist/components/provider-search.d.ts +0 -4
  159. package/dist/components/provider-search.d.ts.map +0 -1
  160. package/dist/components/provider-search.js +0 -13
  161. package/dist/components/provider-search.js.map +0 -1
  162. package/dist/components/supported-model-provider-catalog-entry.d.ts +0 -8
  163. package/dist/components/supported-model-provider-catalog-entry.d.ts.map +0 -1
  164. package/dist/components/supported-model-provider-catalog-entry.js +0 -9
  165. package/dist/components/supported-model-provider-catalog-entry.js.map +0 -1
  166. package/src/components/provider-search.test.ts +0 -45
  167. package/src/components/provider-search.ts +0 -18
  168. package/src/components/supported-model-provider-catalog-entry.ts +0 -19
@@ -11,7 +11,9 @@ import { toast } from '@shipfox/react-ui/toast';
11
11
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
12
12
  import { Header, Text } from '@shipfox/react-ui/typography';
13
13
  import { cn } from '@shipfox/react-ui/utils';
14
- import { useEffect, useMemo, useRef, useState } from 'react';
14
+ import { useEffect, useMemo, useReducer, useRef, useState } from 'react';
15
+ import { managementModalReducer } from '#core/model-provider-management-reducer.js';
16
+ import { customProviderCardMatchesSearch, isSupportedProvider, availableProviders as selectAvailableProviders } from '#core/provider-policy.js';
15
17
  import { useDeleteModelProviderConfigMutation, useModelProviderCatalogQuery, useModelProviderConfigsQuery, useSetDefaultModelProviderMutation } from '#hooks/api/model-providers.js';
16
18
  import { AddCustomProviderCard } from './add-custom-provider-card.js';
17
19
  import { AvailableProvidersGrid, PROVIDER_GRID_CLASS } from './available-providers-grid.js';
@@ -20,56 +22,56 @@ import { CustomModelProviderForm } from './custom-model-provider-form.js';
20
22
  import { modelProviderConfigErrorToFormError } from './form-errors.js';
21
23
  import { ModelProviderUsageModal } from './model-provider-usage-modal.js';
22
24
  import { usageTargetFromCatalogEntry, usageTargetFromCustomConfig } from './model-provider-usage-target.js';
23
- import { customProviderCardMatchesSearch } from './provider-search.js';
24
- import { isSupportedCatalogEntry, toSupportedCatalogEntry } from './supported-model-provider-catalog-entry.js';
25
25
  import { ModelProviderTestAndSaveForm } from './test-and-save-form.js';
26
26
  const SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
27
27
  const USAGE_MODAL_OPEN_DELAY_MS = 250;
28
28
  export function WorkspaceModelProvidersSection({ workspaceId }) {
29
29
  const catalogQuery = useModelProviderCatalogQuery();
30
30
  const configsQuery = useModelProviderConfigsQuery(workspaceId);
31
- const [formState, setFormState] = useState(null);
32
- const [customFormState, setCustomFormState] = useState(null);
33
- const [modelFormState, setModelFormState] = useState(null);
34
- const [usageTarget, setUsageTarget] = useState(null);
31
+ const [modal, dispatchModal] = useReducer(managementModalReducer, {
32
+ kind: 'closed'
33
+ });
35
34
  const [pendingUsageTarget, setPendingUsageTarget] = useState(null);
36
35
  const configuredProvidersRegionRef = useRef(null);
37
36
  const providers = catalogQuery.data?.providers ?? [];
38
37
  const configs = configsQuery.data?.configs ?? [];
39
38
  const configsLoaded = configsQuery.data !== undefined;
40
- const defaultProviderId = configsQuery.data?.default_provider_id ?? null;
39
+ const defaultProviderId = configsQuery.data?.defaultProviderId ?? null;
41
40
  const providerById = useMemo(()=>new Map(providers.map((provider)=>[
42
41
  provider.id,
43
42
  provider
44
43
  ])), [
45
44
  providers
46
45
  ]);
47
- const configuredIds = useMemo(()=>new Set(configs.map((config)=>config.provider_id)), [
48
- configs
49
- ]);
50
- const availableProviders = configsLoaded ? providers.filter((provider)=>isSupportedCatalogEntry(provider) && !configuredIds.has(provider.id)) : [];
51
- const unsupportedProviders = providers.filter((provider)=>provider.support_status === 'unsupported');
52
- function closeForm() {
53
- setFormState(null);
54
- }
55
- function closeModelForm() {
56
- setModelFormState(null);
57
- }
58
- function closeCustomForm() {
59
- setCustomFormState(null);
60
- }
46
+ const availableProviders = configsLoaded ? selectAvailableProviders(providers, configs) : [];
47
+ const unsupportedProviders = providers.filter((provider)=>!isSupportedProvider(provider));
61
48
  useEffect(()=>{
62
- if (pendingUsageTarget === null || formState !== null || customFormState !== null) return undefined;
49
+ if (pendingUsageTarget === null || modal.kind !== 'closed') return undefined;
63
50
  const timer = window.setTimeout(()=>{
64
- setUsageTarget(pendingUsageTarget);
51
+ dispatchModal({
52
+ type: 'show-usage',
53
+ providerId: pendingUsageTarget.target.id,
54
+ initialModel: pendingUsageTarget.initialModel,
55
+ restoreFocusToConfiguredProviders: pendingUsageTarget.restoreFocusToConfiguredProviders
56
+ });
65
57
  setPendingUsageTarget(null);
66
58
  }, USAGE_MODAL_OPEN_DELAY_MS);
67
59
  return ()=>window.clearTimeout(timer);
68
60
  }, [
69
- customFormState,
70
- formState,
61
+ modal.kind,
71
62
  pendingUsageTarget
72
63
  ]);
64
+ const usageTarget = useMemo(()=>{
65
+ if (modal.kind !== 'show-usage') return null;
66
+ const config = configs.find((item)=>item.providerId === modal.providerId);
67
+ if (config?.kind === 'custom') return usageTargetFromCustomConfig(config);
68
+ const entry = providerById.get(modal.providerId);
69
+ return entry && isSupportedProvider(entry) ? usageTargetFromCatalogEntry(entry) : null;
70
+ }, [
71
+ configs,
72
+ modal,
73
+ providerById
74
+ ]);
73
75
  return /*#__PURE__*/ _jsxs("div", {
74
76
  className: "flex min-w-0 flex-col gap-32",
75
77
  children: [
@@ -114,52 +116,56 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
114
116
  configs.length > 0 ? /*#__PURE__*/ _jsx("ul", {
115
117
  className: cn('divide-y divide-border-neutral-base', SURFACE_CLASS),
116
118
  children: configs.map((config)=>{
117
- const entry = toSupportedCatalogEntry(providerById.get(config.provider_id));
119
+ const catalogEntry = providerById.get(config.providerId);
120
+ const entry = catalogEntry && isSupportedProvider(catalogEntry) ? catalogEntry : undefined;
118
121
  const builtinConfig = isBuiltinModelProviderConfig(config) ? config : undefined;
119
122
  const customConfig = isCustomModelProviderConfig(config) ? config : undefined;
120
123
  return /*#__PURE__*/ _jsx(ConfiguredProviderRow, {
121
124
  workspaceId: workspaceId,
122
125
  config: config,
123
126
  entry: entry,
124
- isDefault: config.provider_id === defaultProviderId,
127
+ isDefault: config.providerId === defaultProviderId,
125
128
  onEdit: ()=>{
126
129
  if (entry && builtinConfig) {
127
- setFormState({
128
- mode: 'edit',
129
- entry,
130
+ dispatchModal({
131
+ type: 'edit-builtin',
132
+ provider: entry,
130
133
  config: builtinConfig
131
134
  });
132
135
  } else if (customConfig) {
133
- setCustomFormState({
134
- mode: 'edit',
136
+ dispatchModal({
137
+ type: 'edit-custom',
135
138
  config: customConfig
136
139
  });
137
140
  }
138
141
  },
139
142
  onChangeDefaultModel: ()=>{
140
- if (entry && builtinConfig) setModelFormState({
141
- entry,
143
+ if (entry && builtinConfig) dispatchModal({
144
+ type: 'change-default-model',
145
+ provider: entry,
142
146
  config: builtinConfig
143
147
  });
144
148
  },
145
149
  onShowUsage: ()=>{
146
150
  if (entry && builtinConfig) {
147
151
  setPendingUsageTarget(null);
148
- setUsageTarget({
149
- target: usageTargetFromCatalogEntry(entry),
150
- initialModel: builtinConfig.default_model,
152
+ dispatchModal({
153
+ type: 'show-usage',
154
+ providerId: entry.id,
155
+ initialModel: builtinConfig.defaultModel,
151
156
  restoreFocusToConfiguredProviders: false
152
157
  });
153
158
  } else if (customConfig) {
154
159
  setPendingUsageTarget(null);
155
- setUsageTarget({
156
- target: usageTargetFromCustomConfig(customConfig),
157
- initialModel: customConfig.default_model,
160
+ dispatchModal({
161
+ type: 'show-usage',
162
+ providerId: customConfig.providerId,
163
+ initialModel: customConfig.defaultModel,
158
164
  restoreFocusToConfiguredProviders: false
159
165
  });
160
166
  }
161
167
  }
162
- }, config.provider_id);
168
+ }, config.providerId);
163
169
  })
164
170
  }) : null
165
171
  ]
@@ -192,13 +198,13 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
192
198
  }) : null,
193
199
  configsLoaded ? /*#__PURE__*/ _jsx(AvailableProvidersGrid, {
194
200
  entries: availableProviders,
195
- onSelect: (entry)=>setFormState({
196
- mode: 'configure',
197
- entry
201
+ onSelect: (entry)=>dispatchModal({
202
+ type: 'configure-builtin',
203
+ provider: entry
198
204
  }),
199
205
  trailingCard: /*#__PURE__*/ _jsx(AddCustomProviderCard, {
200
- onConfigure: ()=>setCustomFormState({
201
- mode: 'create'
206
+ onConfigure: ()=>dispatchModal({
207
+ type: 'create-custom'
202
208
  })
203
209
  }),
204
210
  trailingCardMatchesSearch: customProviderCardMatchesSearch
@@ -248,7 +254,7 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
248
254
  /*#__PURE__*/ _jsx(Text, {
249
255
  size: "sm",
250
256
  className: "text-foreground-neutral-muted",
251
- children: entry.unsupported_reason
257
+ children: entry.unsupportedReason
252
258
  })
253
259
  ]
254
260
  })
@@ -258,45 +264,51 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
258
264
  ]
259
265
  }),
260
266
  /*#__PURE__*/ _jsx(Modal, {
261
- open: formState !== null,
262
- onOpenChange: (open)=>open ? undefined : closeForm(),
267
+ open: modal.kind === 'configure-builtin' || modal.kind === 'edit-builtin',
268
+ onOpenChange: (open)=>open ? undefined : dispatchModal({
269
+ type: 'close'
270
+ }),
263
271
  children: /*#__PURE__*/ _jsxs(ModalContent, {
264
272
  "aria-describedby": undefined,
265
273
  children: [
266
274
  /*#__PURE__*/ _jsx(ModalTitle, {
267
275
  className: "sr-only",
268
- children: modelProviderFormTitle(formState)
276
+ children: modelProviderFormTitle(modal)
269
277
  }),
270
278
  /*#__PURE__*/ _jsx(ModalHeader, {
271
279
  children: /*#__PURE__*/ _jsx(Text, {
272
280
  size: "lg",
273
281
  "aria-hidden": "true",
274
282
  className: "overflow-ellipsis overflow-hidden whitespace-nowrap",
275
- children: modelProviderFormTitle(formState)
283
+ children: modelProviderFormTitle(modal)
276
284
  })
277
285
  }),
278
- formState ? /*#__PURE__*/ _jsx(ModelProviderTestAndSaveForm, {
286
+ modal.kind === 'configure-builtin' || modal.kind === 'edit-builtin' ? /*#__PURE__*/ _jsx(ModelProviderTestAndSaveForm, {
279
287
  workspaceId: workspaceId,
280
- entry: formState.entry,
281
- existingConfig: formState.config,
288
+ entry: modal.provider,
289
+ existingConfig: modal.kind === 'edit-builtin' ? modal.config : undefined,
282
290
  onSaved: (savedDefaultModel)=>{
283
- toast.success(`${formState.entry.label} saved`);
284
- if (formState.mode === 'configure' && configs.length === 0) {
291
+ toast.success(`${modal.provider.label} saved`);
292
+ if (modal.kind === 'configure-builtin' && configs.length === 0) {
285
293
  setPendingUsageTarget({
286
- target: usageTargetFromCatalogEntry(formState.entry),
294
+ target: usageTargetFromCatalogEntry(modal.provider),
287
295
  initialModel: savedDefaultModel,
288
296
  restoreFocusToConfiguredProviders: true
289
297
  });
290
298
  }
291
- closeForm();
299
+ dispatchModal({
300
+ type: 'close'
301
+ });
292
302
  }
293
303
  }) : null
294
304
  ]
295
305
  })
296
306
  }),
297
307
  /*#__PURE__*/ _jsx(Modal, {
298
- open: modelFormState !== null,
299
- onOpenChange: (open)=>open ? undefined : closeModelForm(),
308
+ open: modal.kind === 'change-default-model',
309
+ onOpenChange: (open)=>open ? undefined : dispatchModal({
310
+ type: 'close'
311
+ }),
300
312
  children: /*#__PURE__*/ _jsxs(ModalContent, {
301
313
  "aria-describedby": undefined,
302
314
  children: [
@@ -310,43 +322,50 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
310
322
  "aria-hidden": "true",
311
323
  className: "overflow-ellipsis overflow-hidden whitespace-nowrap",
312
324
  children: [
313
- "Change default model for ",
314
- modelFormState?.entry.label
325
+ "Change default model for",
326
+ ' ',
327
+ modal.kind === 'change-default-model' ? modal.provider.label : ''
315
328
  ]
316
329
  })
317
330
  }),
318
- modelFormState ? /*#__PURE__*/ _jsx(ChangeDefaultModelForm, {
331
+ modal.kind === 'change-default-model' ? /*#__PURE__*/ _jsx(ChangeDefaultModelForm, {
319
332
  workspaceId: workspaceId,
320
- entry: modelFormState.entry,
321
- config: modelFormState.config,
333
+ entry: modal.provider,
334
+ config: modal.config,
322
335
  onSaved: ()=>{
323
- toast.success(`${modelFormState.entry.label} default model saved`);
324
- closeModelForm();
336
+ toast.success(`${modal.provider.label} default model saved`);
337
+ dispatchModal({
338
+ type: 'close'
339
+ });
325
340
  }
326
341
  }) : null
327
342
  ]
328
343
  })
329
344
  }),
330
345
  /*#__PURE__*/ _jsx(ModelProviderUsageModal, {
331
- target: usageTarget?.target ?? null,
332
- initialModel: usageTarget?.initialModel ?? null,
333
- workspaceDefaultHarnessId: configsQuery.data?.default_harness_id ?? null,
334
- open: usageTarget !== null,
335
- closeFocusTarget: usageTarget?.restoreFocusToConfiguredProviders ? configuredProvidersRegionRef.current : null,
346
+ target: usageTarget,
347
+ initialModel: modal.kind === 'show-usage' ? modal.initialModel : null,
348
+ workspaceDefaultHarnessId: configsQuery.data?.defaultHarnessId ?? null,
349
+ open: modal.kind === 'show-usage',
350
+ closeFocusTarget: modal.kind === 'show-usage' && modal.restoreFocusToConfiguredProviders ? configuredProvidersRegionRef.current : null,
336
351
  onOpenChange: (open)=>{
337
- if (!open) setUsageTarget(null);
352
+ if (!open) dispatchModal({
353
+ type: 'close'
354
+ });
338
355
  }
339
356
  }),
340
357
  /*#__PURE__*/ _jsx(Modal, {
341
- open: customFormState !== null,
342
- onOpenChange: (open)=>open ? undefined : closeCustomForm(),
358
+ open: modal.kind === 'create-custom' || modal.kind === 'edit-custom',
359
+ onOpenChange: (open)=>open ? undefined : dispatchModal({
360
+ type: 'close'
361
+ }),
343
362
  children: /*#__PURE__*/ _jsxs(ModalContent, {
344
363
  "aria-describedby": undefined,
345
364
  className: "max-h-[calc(100vh-32px)] max-w-[760px]",
346
365
  children: [
347
366
  /*#__PURE__*/ _jsx(ModalTitle, {
348
367
  className: "sr-only",
349
- children: customModelProviderFormTitle(customFormState)
368
+ children: customModelProviderFormTitle(modal)
350
369
  }),
351
370
  /*#__PURE__*/ _jsx(ModalHeader, {
352
371
  children: /*#__PURE__*/ _jsxs("div", {
@@ -356,7 +375,7 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
356
375
  size: "lg",
357
376
  "aria-hidden": "true",
358
377
  className: "truncate",
359
- children: customModelProviderFormTitle(customFormState)
378
+ children: customModelProviderFormTitle(modal)
360
379
  }),
361
380
  /*#__PURE__*/ _jsx(Text, {
362
381
  size: "sm",
@@ -366,12 +385,14 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
366
385
  ]
367
386
  })
368
387
  }),
369
- customFormState ? /*#__PURE__*/ _jsx(CustomModelProviderForm, {
388
+ modal.kind === 'create-custom' || modal.kind === 'edit-custom' ? /*#__PURE__*/ _jsx(CustomModelProviderForm, {
370
389
  workspaceId: workspaceId,
371
- existingConfig: customFormState.mode === 'edit' ? customFormState.config : undefined,
390
+ existingConfig: modal.kind === 'edit-custom' ? modal.config : undefined,
372
391
  onSaved: ()=>{
373
- toast.success(customFormState.mode === 'edit' ? `${customFormState.config.display_name} saved` : 'Custom provider saved');
374
- closeCustomForm();
392
+ toast.success(modal.kind === 'edit-custom' ? `${modal.config.displayName} saved` : 'Custom provider saved');
393
+ dispatchModal({
394
+ type: 'close'
395
+ });
375
396
  }
376
397
  }) : null
377
398
  ]
@@ -380,14 +401,13 @@ export function WorkspaceModelProvidersSection({ workspaceId }) {
380
401
  ]
381
402
  });
382
403
  }
383
- function modelProviderFormTitle(formState) {
384
- if (formState === null) return '';
385
- if (formState.mode === 'edit') return `Edit credentials for ${formState.entry.label}`;
386
- return `Configure ${formState.entry.label}`;
404
+ function modelProviderFormTitle(modal) {
405
+ if (modal.kind === 'edit-builtin') return `Edit credentials for ${modal.provider.label}`;
406
+ if (modal.kind === 'configure-builtin') return `Configure ${modal.provider.label}`;
407
+ return '';
387
408
  }
388
- function customModelProviderFormTitle(formState) {
389
- if (formState === null) return '';
390
- if (formState.mode === 'edit') return `Edit ${formState.config.display_name}`;
409
+ function customModelProviderFormTitle(modal) {
410
+ if (modal.kind === 'edit-custom') return `Edit ${modal.config.displayName}`;
391
411
  return 'Add custom provider';
392
412
  }
393
413
  function isBuiltinModelProviderConfig(config) {
@@ -403,7 +423,7 @@ function ConfiguredProviderRow({ workspaceId, config, entry, isDefault, onEdit,
403
423
  const [deleteOpen, setDeleteOpen] = useState(false);
404
424
  const [deleteError, setDeleteError] = useState();
405
425
  const customConfig = isCustomModelProviderConfig(config) ? config : undefined;
406
- const label = customConfig?.display_name ?? entry?.label ?? config.provider_id;
426
+ const label = customConfig?.displayName ?? entry?.label ?? config.providerId;
407
427
  const canUse = entry !== undefined || customConfig !== undefined;
408
428
  const canEdit = entry !== undefined || customConfig !== undefined;
409
429
  const isBuiltinConfig = isBuiltinModelProviderConfig(config);
@@ -412,9 +432,7 @@ function ConfiguredProviderRow({ workspaceId, config, entry, isDefault, onEdit,
412
432
  try {
413
433
  await setDefault.mutateAsync({
414
434
  workspaceId,
415
- body: {
416
- provider_id: config.provider_id
417
- }
435
+ providerId: config.providerId
418
436
  });
419
437
  toast.success(`${label} is now the default provider`);
420
438
  } catch (error) {
@@ -427,7 +445,7 @@ function ConfiguredProviderRow({ workspaceId, config, entry, isDefault, onEdit,
427
445
  try {
428
446
  await deleteConfig.mutateAsync({
429
447
  workspaceId,
430
- providerId: config.provider_id
448
+ providerId: config.providerId
431
449
  });
432
450
  toast.success(`${label} deleted`);
433
451
  setDeleteOpen(false);
@@ -1 +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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"}
1
+ {"version":3,"sources":["../../src/components/model-providers-section.tsx"],"sourcesContent":["import {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, useReducer, useRef, useState} from 'react';\nimport {\n type ManagementModal,\n managementModalReducer,\n} from '#core/model-provider-management-reducer.js';\nimport type {\n BuiltinProviderConfig,\n CustomProviderConfig,\n ProviderCatalogEntry,\n ProviderConfig,\n SupportedProvider,\n} from '#core/models.js';\nimport {\n customProviderCardMatchesSearch,\n isSupportedProvider,\n availableProviders as selectAvailableProviders,\n} from '#core/provider-policy.js';\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 {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 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 [modal, dispatchModal] = useReducer(managementModalReducer, {kind: 'closed'});\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?.defaultProviderId ?? null;\n const providerById = useMemo(\n () =>\n new Map<string, ProviderCatalogEntry>(providers.map((provider) => [provider.id, provider])),\n [providers],\n );\n const availableProviders = configsLoaded ? selectAvailableProviders(providers, configs) : [];\n const unsupportedProviders = providers.filter((provider) => !isSupportedProvider(provider));\n\n useEffect(() => {\n if (pendingUsageTarget === null || modal.kind !== 'closed') return undefined;\n\n const timer = window.setTimeout(() => {\n dispatchModal({\n type: 'show-usage',\n providerId: pendingUsageTarget.target.id,\n initialModel: pendingUsageTarget.initialModel,\n restoreFocusToConfiguredProviders: pendingUsageTarget.restoreFocusToConfiguredProviders,\n });\n setPendingUsageTarget(null);\n }, USAGE_MODAL_OPEN_DELAY_MS);\n\n return () => window.clearTimeout(timer);\n }, [modal.kind, pendingUsageTarget]);\n\n const usageTarget = useMemo(() => {\n if (modal.kind !== 'show-usage') return null;\n const config = configs.find((item) => item.providerId === modal.providerId);\n if (config?.kind === 'custom') return usageTargetFromCustomConfig(config);\n const entry = providerById.get(modal.providerId);\n return entry && isSupportedProvider(entry) ? usageTargetFromCatalogEntry(entry) : null;\n }, [configs, modal, providerById]);\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 catalogEntry = providerById.get(config.providerId);\n const entry =\n catalogEntry && isSupportedProvider(catalogEntry) ? catalogEntry : undefined;\n const builtinConfig = isBuiltinModelProviderConfig(config) ? config : undefined;\n const customConfig = isCustomModelProviderConfig(config) ? config : undefined;\n return (\n <ConfiguredProviderRow\n key={config.providerId}\n workspaceId={workspaceId}\n config={config}\n entry={entry}\n isDefault={config.providerId === defaultProviderId}\n onEdit={() => {\n if (entry && builtinConfig) {\n dispatchModal({type: 'edit-builtin', provider: entry, config: builtinConfig});\n } else if (customConfig) {\n dispatchModal({type: 'edit-custom', config: customConfig});\n }\n }}\n onChangeDefaultModel={() => {\n if (entry && builtinConfig)\n dispatchModal({\n type: 'change-default-model',\n provider: entry,\n config: builtinConfig,\n });\n }}\n onShowUsage={() => {\n if (entry && builtinConfig) {\n setPendingUsageTarget(null);\n dispatchModal({\n type: 'show-usage',\n providerId: entry.id,\n initialModel: builtinConfig.defaultModel,\n restoreFocusToConfiguredProviders: false,\n });\n } else if (customConfig) {\n setPendingUsageTarget(null);\n dispatchModal({\n type: 'show-usage',\n providerId: customConfig.providerId,\n initialModel: customConfig.defaultModel,\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) => dispatchModal({type: 'configure-builtin', provider: entry})}\n trailingCard={\n <AddCustomProviderCard onConfigure={() => dispatchModal({type: 'create-custom'})} />\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.unsupportedReason}\n </Text>\n </div>\n </li>\n ))}\n </ul>\n ) : null}\n </section>\n\n <Modal\n open={modal.kind === 'configure-builtin' || modal.kind === 'edit-builtin'}\n onOpenChange={(open) => (open ? undefined : dispatchModal({type: 'close'}))}\n >\n <ModalContent aria-describedby={undefined}>\n <ModalTitle className=\"sr-only\">{modelProviderFormTitle(modal)}</ModalTitle>\n <ModalHeader>\n <Text\n size=\"lg\"\n aria-hidden=\"true\"\n className=\"overflow-ellipsis overflow-hidden whitespace-nowrap\"\n >\n {modelProviderFormTitle(modal)}\n </Text>\n </ModalHeader>\n {modal.kind === 'configure-builtin' || modal.kind === 'edit-builtin' ? (\n <ModelProviderTestAndSaveForm\n workspaceId={workspaceId}\n entry={modal.provider}\n existingConfig={modal.kind === 'edit-builtin' ? modal.config : undefined}\n onSaved={(savedDefaultModel) => {\n toast.success(`${modal.provider.label} saved`);\n if (modal.kind === 'configure-builtin' && configs.length === 0) {\n setPendingUsageTarget({\n target: usageTargetFromCatalogEntry(modal.provider),\n initialModel: savedDefaultModel,\n restoreFocusToConfiguredProviders: true,\n });\n }\n dispatchModal({type: 'close'});\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <Modal\n open={modal.kind === 'change-default-model'}\n onOpenChange={(open) => (open ? undefined : dispatchModal({type: 'close'}))}\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{' '}\n {modal.kind === 'change-default-model' ? modal.provider.label : ''}\n </Text>\n </ModalHeader>\n {modal.kind === 'change-default-model' ? (\n <ChangeDefaultModelForm\n workspaceId={workspaceId}\n entry={modal.provider}\n config={modal.config}\n onSaved={() => {\n toast.success(`${modal.provider.label} default model saved`);\n dispatchModal({type: 'close'});\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n\n <ModelProviderUsageModal\n target={usageTarget}\n initialModel={modal.kind === 'show-usage' ? modal.initialModel : null}\n workspaceDefaultHarnessId={configsQuery.data?.defaultHarnessId ?? null}\n open={modal.kind === 'show-usage'}\n closeFocusTarget={\n modal.kind === 'show-usage' && modal.restoreFocusToConfiguredProviders\n ? configuredProvidersRegionRef.current\n : null\n }\n onOpenChange={(open) => {\n if (!open) dispatchModal({type: 'close'});\n }}\n />\n\n <Modal\n open={modal.kind === 'create-custom' || modal.kind === 'edit-custom'}\n onOpenChange={(open) => (open ? undefined : dispatchModal({type: 'close'}))}\n >\n <ModalContent\n aria-describedby={undefined}\n className=\"max-h-[calc(100vh-32px)] max-w-[760px]\"\n >\n <ModalTitle className=\"sr-only\">{customModelProviderFormTitle(modal)}</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(modal)}\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 {modal.kind === 'create-custom' || modal.kind === 'edit-custom' ? (\n <CustomModelProviderForm\n workspaceId={workspaceId}\n existingConfig={modal.kind === 'edit-custom' ? modal.config : undefined}\n onSaved={() => {\n toast.success(\n modal.kind === 'edit-custom'\n ? `${modal.config.displayName} saved`\n : 'Custom provider saved',\n );\n dispatchModal({type: 'close'});\n }}\n />\n ) : null}\n </ModalContent>\n </Modal>\n </div>\n );\n}\n\nfunction modelProviderFormTitle(modal: ManagementModal): string {\n if (modal.kind === 'edit-builtin') return `Edit credentials for ${modal.provider.label}`;\n if (modal.kind === 'configure-builtin') return `Configure ${modal.provider.label}`;\n return '';\n}\n\nfunction customModelProviderFormTitle(modal: ManagementModal): string {\n if (modal.kind === 'edit-custom') return `Edit ${modal.config.displayName}`;\n return 'Add custom provider';\n}\n\nfunction isBuiltinModelProviderConfig(config: ProviderConfig): config is BuiltinProviderConfig {\n return config.kind === 'builtin';\n}\n\nfunction isCustomModelProviderConfig(config: ProviderConfig): config is CustomProviderConfig {\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: ProviderConfig;\n entry: SupportedProvider | 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?.displayName ?? entry?.label ?? config.providerId;\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 providerId: config.providerId,\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.providerId});\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","useReducer","useRef","useState","managementModalReducer","customProviderCardMatchesSearch","isSupportedProvider","availableProviders","selectAvailableProviders","useDeleteModelProviderConfigMutation","useModelProviderCatalogQuery","useModelProviderConfigsQuery","useSetDefaultModelProviderMutation","AddCustomProviderCard","AvailableProvidersGrid","PROVIDER_GRID_CLASS","ChangeDefaultModelForm","CustomModelProviderForm","modelProviderConfigErrorToFormError","ModelProviderUsageModal","usageTargetFromCatalogEntry","usageTargetFromCustomConfig","ModelProviderTestAndSaveForm","SURFACE_CLASS","USAGE_MODAL_OPEN_DELAY_MS","WorkspaceModelProvidersSection","workspaceId","catalogQuery","configsQuery","modal","dispatchModal","kind","pendingUsageTarget","setPendingUsageTarget","configuredProvidersRegionRef","providers","data","configs","configsLoaded","undefined","defaultProviderId","providerById","Map","map","provider","id","unsupportedProviders","filter","timer","window","setTimeout","type","providerId","target","initialModel","restoreFocusToConfiguredProviders","clearTimeout","usageTarget","config","find","item","entry","get","div","className","section","ref","aria-label","tabIndex","variant","size","isPending","ModelProviderRowsSkeleton","label","isError","query","subject","length","icon","title","description","ul","catalogEntry","builtinConfig","isBuiltinModelProviderConfig","customConfig","isCustomModelProviderConfig","ConfiguredProviderRow","isDefault","onEdit","onChangeDefaultModel","onShowUsage","defaultModel","ModelProviderGridSkeleton","entries","onSelect","trailingCard","onConfigure","trailingCardMatchesSearch","li","name","aria-hidden","bold","unsupportedReason","open","onOpenChange","aria-describedby","modelProviderFormTitle","existingConfig","onSaved","savedDefaultModel","success","workspaceDefaultHarnessId","defaultHarnessId","closeFocusTarget","current","customModelProviderFormTitle","displayName","setDefault","deleteConfig","defaultError","setDefaultError","deleteOpen","setDeleteOpen","deleteError","setDeleteError","canUse","canEdit","isBuiltinConfig","handleSetDefault","mutateAsync","error","mapped","message","handleDelete","handleDeleteOpenChange","nextOpen","reset","asChild","span","align","disabled","role","DeleteModelProviderDialog","errorMessage","isLoading","onDelete","onClick","row","card"],"mappings":"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"}