@shipfox/client-agent 17.0.0 → 22.0.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 (58) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +34 -0
  3. package/dist/components/add-custom-provider-card.d.ts.map +1 -1
  4. package/dist/components/add-custom-provider-card.js +11 -31
  5. package/dist/components/add-custom-provider-card.js.map +1 -1
  6. package/dist/components/available-provider-card.d.ts.map +1 -1
  7. package/dist/components/available-provider-card.js +11 -31
  8. package/dist/components/available-provider-card.js.map +1 -1
  9. package/dist/components/available-providers-grid.d.ts +0 -1
  10. package/dist/components/available-providers-grid.d.ts.map +1 -1
  11. package/dist/components/available-providers-grid.js +33 -27
  12. package/dist/components/available-providers-grid.js.map +1 -1
  13. package/dist/components/custom-model-provider-form.stories.js +1 -1
  14. package/dist/components/custom-model-provider-form.stories.js.map +1 -1
  15. package/dist/components/harnesses-section.d.ts.map +1 -1
  16. package/dist/components/harnesses-section.js +147 -134
  17. package/dist/components/harnesses-section.js.map +1 -1
  18. package/dist/components/harnesses-section.stories.js +1 -1
  19. package/dist/components/harnesses-section.stories.js.map +1 -1
  20. package/dist/components/model-provider-grid-skeleton.d.ts +4 -0
  21. package/dist/components/model-provider-grid-skeleton.d.ts.map +1 -0
  22. package/dist/components/model-provider-grid-skeleton.js +34 -0
  23. package/dist/components/model-provider-grid-skeleton.js.map +1 -0
  24. package/dist/components/model-provider-usage-modal.stories.js +1 -1
  25. package/dist/components/model-provider-usage-modal.stories.js.map +1 -1
  26. package/dist/components/model-providers-section.d.ts.map +1 -1
  27. package/dist/components/model-providers-section.js +253 -245
  28. package/dist/components/model-providers-section.js.map +1 -1
  29. package/dist/components/model-providers-section.stories.js +1 -1
  30. package/dist/components/model-providers-section.stories.js.map +1 -1
  31. package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
  32. package/dist/pages/model-provider-onboarding-page.js +32 -64
  33. package/dist/pages/model-provider-onboarding-page.js.map +1 -1
  34. package/dist/pages/model-provider-onboarding-page.stories.js +1 -1
  35. package/dist/pages/model-provider-onboarding-page.stories.js.map +1 -1
  36. package/dist/routes/agents-settings.d.ts +3 -0
  37. package/dist/routes/agents-settings.js +3 -0
  38. package/dist/routes/agents-settings.js.map +1 -1
  39. package/dist/routes/model-provider.d.ts +3 -0
  40. package/dist/routes/model-provider.js +3 -0
  41. package/dist/routes/model-provider.js.map +1 -1
  42. package/dist/tsconfig.test.tsbuildinfo +1 -1
  43. package/package.json +4 -4
  44. package/src/components/add-custom-provider-card.tsx +9 -23
  45. package/src/components/available-provider-card.tsx +9 -23
  46. package/src/components/available-providers-grid.tsx +36 -29
  47. package/src/components/custom-model-provider-form.stories.tsx +1 -1
  48. package/src/components/harnesses-section.stories.tsx +1 -1
  49. package/src/components/harnesses-section.tsx +116 -106
  50. package/src/components/model-provider-grid-skeleton.tsx +21 -0
  51. package/src/components/model-provider-usage-modal.stories.tsx +1 -1
  52. package/src/components/model-providers-section.stories.tsx +1 -1
  53. package/src/components/model-providers-section.tsx +208 -191
  54. package/src/pages/model-provider-onboarding-page.stories.tsx +1 -1
  55. package/src/pages/model-provider-onboarding-page.tsx +26 -54
  56. package/src/routes/agents-settings.tsx +1 -0
  57. package/src/routes/model-provider.tsx +1 -0
  58. package/tsconfig.build.tsbuildinfo +1 -1
@@ -4,17 +4,16 @@ import { IconButton } from '@shipfox/react-ui/button';
4
4
  import { Callout } from '@shipfox/react-ui/callout';
5
5
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@shipfox/react-ui/dropdown-menu';
6
6
  import { Icon } from '@shipfox/react-ui/icon';
7
+ import { Panel, PanelBody, PanelRow } from '@shipfox/react-ui/panel';
7
8
  import { Skeleton } from '@shipfox/react-ui/skeleton';
8
9
  import { toast } from '@shipfox/react-ui/toast';
9
10
  import { Tooltip, TooltipContent, TooltipTrigger } from '@shipfox/react-ui/tooltip';
10
11
  import { Header, Text } from '@shipfox/react-ui/typography';
11
- import { cn } from '@shipfox/react-ui/utils';
12
12
  import { useRef, useState } from 'react';
13
13
  import { DEFAULT_HARNESS, listHarnesses } from '#core/harness-policy.js';
14
14
  import { useModelProviderConfigsQuery, useSetDefaultHarnessMutation } from '#hooks/api/model-providers.js';
15
15
  import { modelProviderConfigErrorToFormError } from './form-errors.js';
16
16
  import { isHarnessAvailable } from './harness-availability.js';
17
- const SURFACE_CLASS = 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
18
17
  export function WorkspaceHarnessesSection({ workspaceId }) {
19
18
  const configsQuery = useModelProviderConfigsQuery(workspaceId);
20
19
  const setDefaultHarness = useSetDefaultHarnessMutation();
@@ -84,134 +83,141 @@ export function WorkspaceHarnessesSection({ workspaceId }) {
84
83
  ]
85
84
  }),
86
85
  configsQuery.isPending ? /*#__PURE__*/ _jsx(HarnessRowsSkeleton, {}) : null,
87
- configsQuery.isError && configsQuery.data === undefined ? /*#__PURE__*/ _jsx("div", {
88
- className: cn(SURFACE_CLASS, 'px-row'),
86
+ configsQuery.isError && configsQuery.data === undefined ? /*#__PURE__*/ _jsx(Panel, {
89
87
  children: /*#__PURE__*/ _jsx(QueryLoadError, {
90
88
  query: configsQuery,
91
- subject: "harnesses"
89
+ subject: "harnesses",
90
+ variant: "panel"
92
91
  })
93
92
  }) : null,
94
- configsQuery.data !== undefined ? /*#__PURE__*/ _jsx("ul", {
95
- className: cn('divide-y divide-border-neutral-base', SURFACE_CLASS),
96
- children: listHarnesses().map((harness)=>/*#__PURE__*/ _jsx(HarnessRow, {
97
- harness: harness,
98
- isDefault: harness.id === defaultHarnessId,
99
- isAvailable: isHarnessAvailable(harness, configs),
100
- isSettingDefault: pendingDefaultHarness?.workspaceId === workspaceId,
101
- defaultError: defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id ? defaultError.message : undefined,
102
- onSetDefault: handleSetDefault
103
- }, harness.id))
93
+ configsQuery.data !== undefined ? /*#__PURE__*/ _jsx(Panel, {
94
+ children: /*#__PURE__*/ _jsx(PanelBody, {
95
+ asChild: true,
96
+ children: /*#__PURE__*/ _jsx("ul", {
97
+ children: listHarnesses().map((harness)=>/*#__PURE__*/ _jsx(HarnessRow, {
98
+ harness: harness,
99
+ isDefault: harness.id === defaultHarnessId,
100
+ isAvailable: isHarnessAvailable(harness, configs),
101
+ isSettingDefault: pendingDefaultHarness?.workspaceId === workspaceId,
102
+ defaultError: defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id ? defaultError.message : undefined,
103
+ onSetDefault: handleSetDefault
104
+ }, harness.id))
105
+ })
106
+ })
104
107
  }) : null
105
108
  ]
106
109
  });
107
110
  }
108
111
  function HarnessRow({ harness, isDefault, isAvailable, isSettingDefault, defaultError, onSetDefault }) {
109
112
  const unavailableCopy = harnessUnavailableCopy(isDefault);
110
- return /*#__PURE__*/ _jsxs("li", {
111
- className: "flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover",
112
- children: [
113
- /*#__PURE__*/ _jsxs("div", {
114
- className: "flex items-center justify-between gap-cluster",
115
- children: [
116
- /*#__PURE__*/ _jsxs("div", {
117
- className: "flex min-w-0 items-center gap-inline",
118
- children: [
119
- isDefault ? /*#__PURE__*/ _jsxs(_Fragment, {
120
- children: [
121
- /*#__PURE__*/ _jsxs(Tooltip, {
122
- children: [
123
- /*#__PURE__*/ _jsx(TooltipTrigger, {
124
- asChild: true,
125
- children: /*#__PURE__*/ _jsx("span", {
126
- className: "inline-flex size-16 shrink-0 items-center justify-center",
127
- children: /*#__PURE__*/ _jsx(Icon, {
128
- name: "starLine",
129
- className: "size-16 text-foreground-neutral-muted",
130
- "aria-hidden": true
113
+ return /*#__PURE__*/ _jsx(PanelRow, {
114
+ asChild: true,
115
+ className: "flex-col items-stretch gap-inline",
116
+ children: /*#__PURE__*/ _jsxs("li", {
117
+ children: [
118
+ /*#__PURE__*/ _jsxs("div", {
119
+ className: "flex items-center justify-between gap-cluster",
120
+ children: [
121
+ /*#__PURE__*/ _jsxs("div", {
122
+ className: "flex min-w-0 items-center gap-inline",
123
+ children: [
124
+ isDefault ? /*#__PURE__*/ _jsxs(_Fragment, {
125
+ children: [
126
+ /*#__PURE__*/ _jsxs(Tooltip, {
127
+ children: [
128
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
129
+ asChild: true,
130
+ children: /*#__PURE__*/ _jsx("span", {
131
+ className: "inline-flex size-16 shrink-0 items-center justify-center",
132
+ children: /*#__PURE__*/ _jsx(Icon, {
133
+ name: "starLine",
134
+ className: "size-16 text-foreground-neutral-muted",
135
+ "aria-hidden": true
136
+ })
131
137
  })
138
+ }),
139
+ /*#__PURE__*/ _jsx(TooltipContent, {
140
+ children: "Default harness"
132
141
  })
133
- }),
134
- /*#__PURE__*/ _jsx(TooltipContent, {
135
- children: "Default harness"
136
- })
137
- ]
138
- }),
139
- /*#__PURE__*/ _jsx("span", {
140
- className: "sr-only",
141
- children: "Default harness"
142
- })
143
- ]
144
- }) : null,
145
- /*#__PURE__*/ _jsx(Text, {
146
- size: "md",
147
- bold: true,
148
- className: "truncate",
149
- children: harness.label
150
- }),
151
- !isAvailable ? /*#__PURE__*/ _jsxs(_Fragment, {
152
- children: [
153
- /*#__PURE__*/ _jsxs(Tooltip, {
154
- children: [
155
- /*#__PURE__*/ _jsx(TooltipTrigger, {
156
- asChild: true,
157
- children: /*#__PURE__*/ _jsx("span", {
158
- className: "inline-flex size-16 shrink-0 items-center justify-center",
159
- children: /*#__PURE__*/ _jsx(Icon, {
160
- name: "errorWarningLine",
161
- className: "size-16 text-foreground-warning-base",
162
- "aria-hidden": true
142
+ ]
143
+ }),
144
+ /*#__PURE__*/ _jsx("span", {
145
+ className: "sr-only",
146
+ children: "Default harness"
147
+ })
148
+ ]
149
+ }) : null,
150
+ /*#__PURE__*/ _jsx(Text, {
151
+ size: "md",
152
+ bold: true,
153
+ className: "truncate",
154
+ children: harness.label
155
+ }),
156
+ !isAvailable ? /*#__PURE__*/ _jsxs(_Fragment, {
157
+ children: [
158
+ /*#__PURE__*/ _jsxs(Tooltip, {
159
+ children: [
160
+ /*#__PURE__*/ _jsx(TooltipTrigger, {
161
+ asChild: true,
162
+ children: /*#__PURE__*/ _jsx("span", {
163
+ className: "inline-flex size-16 shrink-0 items-center justify-center",
164
+ children: /*#__PURE__*/ _jsx(Icon, {
165
+ name: "errorWarningLine",
166
+ className: "size-16 text-foreground-warning-base",
167
+ "aria-hidden": true
168
+ })
163
169
  })
170
+ }),
171
+ /*#__PURE__*/ _jsx(TooltipContent, {
172
+ children: unavailableCopy
164
173
  })
165
- }),
166
- /*#__PURE__*/ _jsx(TooltipContent, {
167
- children: unavailableCopy
168
- })
169
- ]
170
- }),
171
- /*#__PURE__*/ _jsx("span", {
172
- className: "sr-only",
173
- children: unavailableCopy
174
+ ]
175
+ }),
176
+ /*#__PURE__*/ _jsx("span", {
177
+ className: "sr-only",
178
+ children: unavailableCopy
179
+ })
180
+ ]
181
+ }) : null
182
+ ]
183
+ }),
184
+ !isDefault ? /*#__PURE__*/ _jsxs(DropdownMenu, {
185
+ children: [
186
+ /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
187
+ asChild: true,
188
+ children: /*#__PURE__*/ _jsx(IconButton, {
189
+ size: "sm",
190
+ variant: "transparent",
191
+ icon: "more2Line",
192
+ disabled: isSettingDefault,
193
+ "aria-label": `Open ${harness.label} harness actions`
194
+ })
195
+ }),
196
+ /*#__PURE__*/ _jsx(DropdownMenuContent, {
197
+ align: "end",
198
+ children: /*#__PURE__*/ _jsx(DropdownMenuItem, {
199
+ icon: "starLine",
200
+ disabled: isSettingDefault || !isAvailable,
201
+ onSelect: ()=>{
202
+ onSetDefault(harness);
203
+ },
204
+ children: "Set as default"
174
205
  })
175
- ]
176
- }) : null
177
- ]
178
- }),
179
- !isDefault ? /*#__PURE__*/ _jsxs(DropdownMenu, {
180
- children: [
181
- /*#__PURE__*/ _jsx(DropdownMenuTrigger, {
182
- asChild: true,
183
- children: /*#__PURE__*/ _jsx(IconButton, {
184
- size: "sm",
185
- variant: "transparent",
186
- icon: "more2Line",
187
- disabled: isSettingDefault,
188
- "aria-label": `Open ${harness.label} harness actions`
189
- })
190
- }),
191
- /*#__PURE__*/ _jsx(DropdownMenuContent, {
192
- align: "end",
193
- children: /*#__PURE__*/ _jsx(DropdownMenuItem, {
194
- icon: "starLine",
195
- disabled: isSettingDefault || !isAvailable,
196
- onSelect: ()=>{
197
- onSetDefault(harness);
198
- },
199
- children: "Set as default"
200
206
  })
201
- })
202
- ]
203
- }) : null
204
- ]
205
- }),
206
- defaultError ? /*#__PURE__*/ _jsx(Callout, {
207
- role: "alert",
208
- type: "error",
209
- children: /*#__PURE__*/ _jsx(Text, {
210
- size: "sm",
211
- children: defaultError
212
- })
213
- }) : null
214
- ]
207
+ ]
208
+ }) : null
209
+ ]
210
+ }),
211
+ defaultError ? /*#__PURE__*/ _jsx(Callout, {
212
+ role: "alert",
213
+ type: "error",
214
+ children: /*#__PURE__*/ _jsx(Text, {
215
+ size: "sm",
216
+ children: defaultError
217
+ })
218
+ }) : null
219
+ ]
220
+ })
215
221
  });
216
222
  }
217
223
  function harnessUnavailableCopy(isDefault) {
@@ -220,27 +226,34 @@ function harnessUnavailableCopy(isDefault) {
220
226
  return `${base} The stored default harness cannot currently run in this workspace.`;
221
227
  }
222
228
  function HarnessRowsSkeleton() {
223
- return /*#__PURE__*/ _jsx("ul", {
224
- role: "status",
225
- "aria-label": "Loading harnesses",
226
- className: cn('divide-y divide-border-neutral-base', SURFACE_CLASS),
227
- children: [
228
- 0,
229
- 1
230
- ].map((row)=>/*#__PURE__*/ _jsxs("li", {
231
- className: "flex items-center gap-cluster px-row py-row",
229
+ return /*#__PURE__*/ _jsx(Panel, {
230
+ children: /*#__PURE__*/ _jsx(PanelBody, {
231
+ asChild: true,
232
+ children: /*#__PURE__*/ _jsx("ul", {
233
+ role: "status",
234
+ "aria-label": "Loading harnesses",
232
235
  children: [
233
- /*#__PURE__*/ _jsx(Skeleton, {
234
- className: "size-16 shrink-0"
235
- }),
236
- /*#__PURE__*/ _jsx(Skeleton, {
237
- className: "h-16 w-120"
238
- }),
239
- /*#__PURE__*/ _jsx(Skeleton, {
240
- className: "ml-auto size-28 shrink-0"
241
- })
242
- ]
243
- }, row))
236
+ 0,
237
+ 1
238
+ ].map((row)=>/*#__PURE__*/ _jsx(PanelRow, {
239
+ asChild: true,
240
+ className: "justify-start gap-cluster hover:bg-background-neutral-base",
241
+ children: /*#__PURE__*/ _jsxs("li", {
242
+ children: [
243
+ /*#__PURE__*/ _jsx(Skeleton, {
244
+ className: "size-16 shrink-0"
245
+ }),
246
+ /*#__PURE__*/ _jsx(Skeleton, {
247
+ className: "h-16 w-120"
248
+ }),
249
+ /*#__PURE__*/ _jsx(Skeleton, {
250
+ className: "ml-auto size-28 shrink-0"
251
+ })
252
+ ]
253
+ })
254
+ }, row))
255
+ })
256
+ })
244
257
  });
245
258
  }
246
259
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/harnesses-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {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 {Icon} from '@shipfox/react-ui/icon';\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 {useRef, useState} from 'react';\nimport {DEFAULT_HARNESS, listHarnesses} from '#core/harness-policy.js';\nimport type {HarnessDescriptor, HarnessId} from '#core/models.js';\nimport {\n useModelProviderConfigsQuery,\n useSetDefaultHarnessMutation,\n} from '#hooks/api/model-providers.js';\nimport {modelProviderConfigErrorToFormError} from './form-errors.js';\nimport {isHarnessAvailable} from './harness-availability.js';\n\nconst SURFACE_CLASS =\n 'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';\n\nexport function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string}) {\n const configsQuery = useModelProviderConfigsQuery(workspaceId);\n const setDefaultHarness = useSetDefaultHarnessMutation();\n const activeWorkspaceIdRef = useRef(workspaceId);\n const defaultRequestSeqRef = useRef(0);\n const activeDefaultRequestRef = useRef<{workspaceId: string; id: number} | null>(null);\n const [pendingDefaultHarness, setPendingDefaultHarness] = useState<{\n workspaceId: string;\n harnessId: HarnessId;\n } | null>(null);\n const [defaultError, setDefaultError] = useState<\n {workspaceId: string; harnessId: HarnessId; message: string} | undefined\n >();\n const configs = configsQuery.data?.configs ?? [];\n const defaultHarnessId = configsQuery.data?.defaultHarnessId ?? DEFAULT_HARNESS;\n activeWorkspaceIdRef.current = workspaceId;\n\n function isActiveDefaultRequest(request: {workspaceId: string; id: number}) {\n const activeRequest = activeDefaultRequestRef.current;\n return (\n activeWorkspaceIdRef.current === request.workspaceId &&\n activeRequest?.workspaceId === request.workspaceId &&\n activeRequest.id === request.id\n );\n }\n\n async function handleSetDefault(harness: HarnessDescriptor) {\n if (activeDefaultRequestRef.current?.workspaceId === workspaceId) return;\n\n const request = {workspaceId, id: defaultRequestSeqRef.current + 1};\n defaultRequestSeqRef.current = request.id;\n activeDefaultRequestRef.current = request;\n setPendingDefaultHarness({workspaceId, harnessId: harness.id});\n setDefaultError(undefined);\n try {\n await setDefaultHarness.mutateAsync({\n workspaceId,\n harnessId: harness.id,\n });\n if (!isActiveDefaultRequest(request)) return;\n toast.success(`${harness.label} is now the default harness`);\n } catch (error) {\n if (!isActiveDefaultRequest(request)) return;\n const mapped = modelProviderConfigErrorToFormError(error);\n setDefaultError({\n workspaceId,\n harnessId: harness.id,\n message: mapped.message || 'Could not save default harness. Try again.',\n });\n } finally {\n if (isActiveDefaultRequest(request)) {\n activeDefaultRequestRef.current = null;\n setPendingDefaultHarness(null);\n }\n }\n }\n\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Harnesses\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Harnesses</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Harnesses available to run agent steps in this workspace.\n </Text>\n </div>\n\n {configsQuery.isPending ? <HarnessRowsSkeleton /> : null}\n\n {configsQuery.isError && configsQuery.data === undefined ? (\n <div className={cn(SURFACE_CLASS, 'px-row')}>\n <QueryLoadError query={configsQuery} subject=\"harnesses\" />\n </div>\n ) : null}\n\n {configsQuery.data !== undefined ? (\n <ul className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}>\n {listHarnesses().map((harness) => (\n <HarnessRow\n key={harness.id}\n harness={harness}\n isDefault={harness.id === defaultHarnessId}\n isAvailable={isHarnessAvailable(harness, configs)}\n isSettingDefault={pendingDefaultHarness?.workspaceId === workspaceId}\n defaultError={\n defaultError?.workspaceId === workspaceId && defaultError.harnessId === harness.id\n ? defaultError.message\n : undefined\n }\n onSetDefault={handleSetDefault}\n />\n ))}\n </ul>\n ) : null}\n </section>\n );\n}\n\nfunction HarnessRow({\n harness,\n isDefault,\n isAvailable,\n isSettingDefault,\n defaultError,\n onSetDefault,\n}: {\n harness: HarnessDescriptor;\n isDefault: boolean;\n isAvailable: boolean;\n isSettingDefault: boolean;\n defaultError: string | undefined;\n onSetDefault: (harness: HarnessDescriptor) => void;\n}) {\n const unavailableCopy = harnessUnavailableCopy(isDefault);\n\n return (\n <li className=\"flex flex-col gap-inline px-row py-row transition-colors hover:bg-background-components-hover\">\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-inline\">\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 harness</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">Default harness</span>\n </>\n ) : null}\n <Text size=\"md\" bold className=\"truncate\">\n {harness.label}\n </Text>\n {!isAvailable ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"errorWarningLine\"\n className=\"size-16 text-foreground-warning-base\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{unavailableCopy}</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">{unavailableCopy}</span>\n </>\n ) : null}\n </div>\n {!isDefault ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n disabled={isSettingDefault}\n aria-label={`Open ${harness.label} harness actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n icon=\"starLine\"\n disabled={isSettingDefault || !isAvailable}\n onSelect={() => {\n onSetDefault(harness);\n }}\n >\n Set as default\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n {defaultError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{defaultError}</Text>\n </Callout>\n ) : null}\n </li>\n );\n}\n\nfunction harnessUnavailableCopy(isDefault: boolean): string {\n const base = 'Configure a compatible model provider to use this harness.';\n if (!isDefault) return base;\n return `${base} The stored default harness cannot currently run in this workspace.`;\n}\n\nfunction HarnessRowsSkeleton() {\n return (\n <ul\n role=\"status\"\n aria-label=\"Loading harnesses\"\n className={cn('divide-y divide-border-neutral-base', SURFACE_CLASS)}\n >\n {[0, 1].map((row) => (\n <li key={row} className=\"flex items-center gap-cluster px-row py-row\">\n <Skeleton className=\"size-16 shrink-0\" />\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"ml-auto size-28 shrink-0\" />\n </li>\n ))}\n </ul>\n );\n}\n"],"names":["QueryLoadError","IconButton","Callout","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Icon","Skeleton","toast","Tooltip","TooltipContent","TooltipTrigger","Header","Text","cn","useRef","useState","DEFAULT_HARNESS","listHarnesses","useModelProviderConfigsQuery","useSetDefaultHarnessMutation","modelProviderConfigErrorToFormError","isHarnessAvailable","SURFACE_CLASS","WorkspaceHarnessesSection","workspaceId","configsQuery","setDefaultHarness","activeWorkspaceIdRef","defaultRequestSeqRef","activeDefaultRequestRef","pendingDefaultHarness","setPendingDefaultHarness","defaultError","setDefaultError","configs","data","defaultHarnessId","current","isActiveDefaultRequest","request","activeRequest","id","handleSetDefault","harness","harnessId","undefined","mutateAsync","success","label","error","mapped","message","section","className","aria-label","div","variant","size","isPending","HarnessRowsSkeleton","isError","query","subject","ul","map","HarnessRow","isDefault","isAvailable","isSettingDefault","onSetDefault","unavailableCopy","harnessUnavailableCopy","li","asChild","span","name","aria-hidden","bold","icon","disabled","align","onSelect","role","type","base","row"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/components/harnesses-section.tsx"],"sourcesContent":["import {QueryLoadError} from '@shipfox/client-ui';\nimport {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 {Icon} from '@shipfox/react-ui/icon';\nimport {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';\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 {useRef, useState} from 'react';\nimport {DEFAULT_HARNESS, listHarnesses} from '#core/harness-policy.js';\nimport type {HarnessDescriptor, HarnessId} from '#core/models.js';\nimport {\n useModelProviderConfigsQuery,\n useSetDefaultHarnessMutation,\n} from '#hooks/api/model-providers.js';\nimport {modelProviderConfigErrorToFormError} from './form-errors.js';\nimport {isHarnessAvailable} from './harness-availability.js';\n\nexport function WorkspaceHarnessesSection({workspaceId}: {workspaceId: string}) {\n const configsQuery = useModelProviderConfigsQuery(workspaceId);\n const setDefaultHarness = useSetDefaultHarnessMutation();\n const activeWorkspaceIdRef = useRef(workspaceId);\n const defaultRequestSeqRef = useRef(0);\n const activeDefaultRequestRef = useRef<{workspaceId: string; id: number} | null>(null);\n const [pendingDefaultHarness, setPendingDefaultHarness] = useState<{\n workspaceId: string;\n harnessId: HarnessId;\n } | null>(null);\n const [defaultError, setDefaultError] = useState<\n {workspaceId: string; harnessId: HarnessId; message: string} | undefined\n >();\n const configs = configsQuery.data?.configs ?? [];\n const defaultHarnessId = configsQuery.data?.defaultHarnessId ?? DEFAULT_HARNESS;\n activeWorkspaceIdRef.current = workspaceId;\n\n function isActiveDefaultRequest(request: {workspaceId: string; id: number}) {\n const activeRequest = activeDefaultRequestRef.current;\n return (\n activeWorkspaceIdRef.current === request.workspaceId &&\n activeRequest?.workspaceId === request.workspaceId &&\n activeRequest.id === request.id\n );\n }\n\n async function handleSetDefault(harness: HarnessDescriptor) {\n if (activeDefaultRequestRef.current?.workspaceId === workspaceId) return;\n\n const request = {workspaceId, id: defaultRequestSeqRef.current + 1};\n defaultRequestSeqRef.current = request.id;\n activeDefaultRequestRef.current = request;\n setPendingDefaultHarness({workspaceId, harnessId: harness.id});\n setDefaultError(undefined);\n try {\n await setDefaultHarness.mutateAsync({\n workspaceId,\n harnessId: harness.id,\n });\n if (!isActiveDefaultRequest(request)) return;\n toast.success(`${harness.label} is now the default harness`);\n } catch (error) {\n if (!isActiveDefaultRequest(request)) return;\n const mapped = modelProviderConfigErrorToFormError(error);\n setDefaultError({\n workspaceId,\n harnessId: harness.id,\n message: mapped.message || 'Could not save default harness. Try again.',\n });\n } finally {\n if (isActiveDefaultRequest(request)) {\n activeDefaultRequestRef.current = null;\n setPendingDefaultHarness(null);\n }\n }\n }\n\n return (\n <section className=\"flex flex-col gap-group\" aria-label=\"Harnesses\">\n <div className=\"flex flex-col gap-tight\">\n <Header variant=\"h3\">Harnesses</Header>\n <Text size=\"sm\" className=\"text-foreground-neutral-muted\">\n Harnesses available to run agent steps in this workspace.\n </Text>\n </div>\n\n {configsQuery.isPending ? <HarnessRowsSkeleton /> : null}\n\n {configsQuery.isError && configsQuery.data === undefined ? (\n <Panel>\n <QueryLoadError query={configsQuery} subject=\"harnesses\" variant=\"panel\" />\n </Panel>\n ) : null}\n\n {configsQuery.data !== undefined ? (\n <Panel>\n <PanelBody asChild>\n <ul>\n {listHarnesses().map((harness) => (\n <HarnessRow\n key={harness.id}\n harness={harness}\n isDefault={harness.id === defaultHarnessId}\n isAvailable={isHarnessAvailable(harness, configs)}\n isSettingDefault={pendingDefaultHarness?.workspaceId === workspaceId}\n defaultError={\n defaultError?.workspaceId === workspaceId &&\n defaultError.harnessId === harness.id\n ? defaultError.message\n : undefined\n }\n onSetDefault={handleSetDefault}\n />\n ))}\n </ul>\n </PanelBody>\n </Panel>\n ) : null}\n </section>\n );\n}\n\nfunction HarnessRow({\n harness,\n isDefault,\n isAvailable,\n isSettingDefault,\n defaultError,\n onSetDefault,\n}: {\n harness: HarnessDescriptor;\n isDefault: boolean;\n isAvailable: boolean;\n isSettingDefault: boolean;\n defaultError: string | undefined;\n onSetDefault: (harness: HarnessDescriptor) => void;\n}) {\n const unavailableCopy = harnessUnavailableCopy(isDefault);\n\n return (\n <PanelRow asChild className=\"flex-col items-stretch gap-inline\">\n <li>\n <div className=\"flex items-center justify-between gap-cluster\">\n <div className=\"flex min-w-0 items-center gap-inline\">\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 harness</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">Default harness</span>\n </>\n ) : null}\n <Text size=\"md\" bold className=\"truncate\">\n {harness.label}\n </Text>\n {!isAvailable ? (\n <>\n <Tooltip>\n <TooltipTrigger asChild>\n <span className=\"inline-flex size-16 shrink-0 items-center justify-center\">\n <Icon\n name=\"errorWarningLine\"\n className=\"size-16 text-foreground-warning-base\"\n aria-hidden\n />\n </span>\n </TooltipTrigger>\n <TooltipContent>{unavailableCopy}</TooltipContent>\n </Tooltip>\n <span className=\"sr-only\">{unavailableCopy}</span>\n </>\n ) : null}\n </div>\n {!isDefault ? (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <IconButton\n size=\"sm\"\n variant=\"transparent\"\n icon=\"more2Line\"\n disabled={isSettingDefault}\n aria-label={`Open ${harness.label} harness actions`}\n />\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem\n icon=\"starLine\"\n disabled={isSettingDefault || !isAvailable}\n onSelect={() => {\n onSetDefault(harness);\n }}\n >\n Set as default\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n ) : null}\n </div>\n {defaultError ? (\n <Callout role=\"alert\" type=\"error\">\n <Text size=\"sm\">{defaultError}</Text>\n </Callout>\n ) : null}\n </li>\n </PanelRow>\n );\n}\n\nfunction harnessUnavailableCopy(isDefault: boolean): string {\n const base = 'Configure a compatible model provider to use this harness.';\n if (!isDefault) return base;\n return `${base} The stored default harness cannot currently run in this workspace.`;\n}\n\nfunction HarnessRowsSkeleton() {\n return (\n <Panel>\n <PanelBody asChild>\n <ul role=\"status\" aria-label=\"Loading harnesses\">\n {[0, 1].map((row) => (\n <PanelRow\n asChild\n className=\"justify-start gap-cluster hover:bg-background-neutral-base\"\n key={row}\n >\n <li>\n <Skeleton className=\"size-16 shrink-0\" />\n <Skeleton className=\"h-16 w-120\" />\n <Skeleton className=\"ml-auto size-28 shrink-0\" />\n </li>\n </PanelRow>\n ))}\n </ul>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["QueryLoadError","IconButton","Callout","DropdownMenu","DropdownMenuContent","DropdownMenuItem","DropdownMenuTrigger","Icon","Panel","PanelBody","PanelRow","Skeleton","toast","Tooltip","TooltipContent","TooltipTrigger","Header","Text","useRef","useState","DEFAULT_HARNESS","listHarnesses","useModelProviderConfigsQuery","useSetDefaultHarnessMutation","modelProviderConfigErrorToFormError","isHarnessAvailable","WorkspaceHarnessesSection","workspaceId","configsQuery","setDefaultHarness","activeWorkspaceIdRef","defaultRequestSeqRef","activeDefaultRequestRef","pendingDefaultHarness","setPendingDefaultHarness","defaultError","setDefaultError","configs","data","defaultHarnessId","current","isActiveDefaultRequest","request","activeRequest","id","handleSetDefault","harness","harnessId","undefined","mutateAsync","success","label","error","mapped","message","section","className","aria-label","div","variant","size","isPending","HarnessRowsSkeleton","isError","query","subject","asChild","ul","map","HarnessRow","isDefault","isAvailable","isSettingDefault","onSetDefault","unavailableCopy","harnessUnavailableCopy","li","span","name","aria-hidden","bold","icon","disabled","align","onSelect","role","type","base","row"],"mappings":"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"}
@@ -21,7 +21,7 @@ function HarnessesStory({ scenario }) {
21
21
  client: queryClient,
22
22
  children: [
23
23
  /*#__PURE__*/ _jsx("div", {
24
- className: "mx-auto w-full max-w-[760px] bg-background-neutral-background p-24",
24
+ className: "mx-auto w-full max-w-[760px] bg-background-subtle-base p-24",
25
25
  children: /*#__PURE__*/ _jsx(WorkspaceHarnessesSection, {
26
26
  workspaceId: WORKSPACE_ID
27
27
  })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/harnesses-section.stories.tsx"],"sourcesContent":["import type {ModelProviderConfigDto} 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 {WorkspaceHarnessesSection} from './harnesses-section.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\n\ntype Scenario =\n | 'default-pi'\n | 'default-claude'\n | 'claude-unavailable'\n | 'default-claude-unavailable'\n | 'loading'\n | 'error';\n\ninterface HarnessesStoryProps {\n scenario: Scenario;\n}\n\nfunction HarnessesStory({scenario}: HarnessesStoryProps) {\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 <WorkspaceHarnessesSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Agent/HarnessSettings',\n component: HarnessesStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'default-pi'},\n} satisfies Meta<typeof HarnessesStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const DefaultClaude: Story = {\n args: {scenario: 'default-claude'},\n};\n\nexport const ClaudeUnavailable: Story = {\n args: {scenario: 'claude-unavailable'},\n};\n\nexport const DefaultClaudeUnavailable: Story = {\n args: {scenario: 'default-claude-unavailable'},\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const ErrorState: Story = {\n args: {scenario: 'error'},\n};\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return () => {\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (scenario === 'error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse(configsForScenario(scenario)));\n };\n}\n\nfunction configsForScenario(scenario: Scenario) {\n if (scenario === 'claude-unavailable') {\n return {\n configs: [providerConfig({provider_id: 'openai'})],\n default_provider_id: 'openai',\n default_harness_id: null,\n };\n }\n if (scenario === 'default-claude-unavailable') {\n return {\n configs: [providerConfig({provider_id: 'openai'})],\n default_provider_id: 'openai',\n default_harness_id: 'claude',\n };\n }\n return {\n configs: [providerConfig({provider_id: 'anthropic'})],\n default_provider_id: 'anthropic',\n default_harness_id: scenario === 'default-claude' ? 'claude' : null,\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 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","WorkspaceHarnessesSection","WORKSPACE_ID","HarnessesStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","parameters","layout","args","Playground","DefaultClaude","ClaudeUnavailable","DefaultClaudeUnavailable","Loading","ErrorState","Promise","undefined","resolve","errorResponse","jsonResponse","configsForScenario","configs","providerConfig","provider_id","default_provider_id","default_harness_id","overrides","kind","default_model","created_at","updated_at","body","init","Response","JSON","stringify","status","headers","code","statusText"],"mappings":";AACA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,OAAO,QAAO,0BAA0B;AAEhD,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAAQC,yBAAyB,QAAO,yBAAyB;AAEjE,MAAMC,eAAe;AAcrB,SAASC,eAAe,EAACC,QAAQ,EAAsB;IACrDR,mBAAmB;QACjBS,SAAS;QACTC,WAAWC,iBAAiBH;IAC9B;IAEA,MAAMI,cAAcR,QAClB,IAAM,IAAIF,YAAY;YAACW,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAGJ,qBACE,MAACZ;QAAoBa,QAAQJ;;0BAC3B,KAACK;gBAAIC,WAAU;0BACb,cAAA,KAACb;oBAA0Bc,aAAab;;;0BAE1C,KAACL;;;AAGP;AAEA,MAAMmB,OAAO;IACXC,OAAO;IACPC,WAAWf;IACXgB,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAACjB,UAAU;IAAY;AAC/B;AAEA,eAAeY,KAAK;AAGpB,OAAO,MAAMM,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,gBAAuB;IAClCF,MAAM;QAACjB,UAAU;IAAgB;AACnC,EAAE;AAEF,OAAO,MAAMoB,oBAA2B;IACtCH,MAAM;QAACjB,UAAU;IAAoB;AACvC,EAAE;AAEF,OAAO,MAAMqB,2BAAkC;IAC7CJ,MAAM;QAACjB,UAAU;IAA4B;AAC/C,EAAE;AAEF,OAAO,MAAMsB,UAAiB;IAC5BL,MAAM;QAACjB,UAAU;IAAS;AAC5B,EAAE;AAEF,OAAO,MAAMuB,aAAoB;IAC/BN,MAAM;QAACjB,UAAU;IAAO;AAC1B,EAAE;AAEF,SAASG,iBAAiBH,QAAkB;IAC1C,OAAO;QACL,IAAIA,aAAa,WAAW,OAAO,IAAIwB,QAAkB,IAAMC;QAC/D,IAAIzB,aAAa,SAAS,OAAOwB,QAAQE,OAAO,CAACC;QACjD,OAAOH,QAAQE,OAAO,CAACE,aAAaC,mBAAmB7B;IACzD;AACF;AAEA,SAAS6B,mBAAmB7B,QAAkB;IAC5C,IAAIA,aAAa,sBAAsB;QACrC,OAAO;YACL8B,SAAS;gBAACC,eAAe;oBAACC,aAAa;gBAAQ;aAAG;YAClDC,qBAAqB;YACrBC,oBAAoB;QACtB;IACF;IACA,IAAIlC,aAAa,8BAA8B;QAC7C,OAAO;YACL8B,SAAS;gBAACC,eAAe;oBAACC,aAAa;gBAAQ;aAAG;YAClDC,qBAAqB;YACrBC,oBAAoB;QACtB;IACF;IACA,OAAO;QACLJ,SAAS;YAACC,eAAe;gBAACC,aAAa;YAAW;SAAG;QACrDC,qBAAqB;QACrBC,oBAAoBlC,aAAa,mBAAmB,WAAW;IACjE;AACF;AAEA,SAAS+B,eACPI,YAA2D,CAAC,CAAC;IAE7D,OAAO;QACLC,MAAM;QACNJ,aAAa;QACbK,eAAe;QACfC,YAAY;QACZC,YAAY;QACZ,GAAGJ,SAAS;IACd;AACF;AAEA,SAASP,aAAaY,IAAa,EAAEC,OAAqB,CAAC,CAAC;IAC1D,OAAO,IAAIC,SAASC,KAAKC,SAAS,CAACJ,OAAO;QACxCK,QAAQ;QACRC,SAAS;YAAC,gBAAgB;QAAkB;QAC5C,GAAGL,IAAI;IACT;AACF;AAEA,SAASd;IACP,OAAOC,aAAa;QAACmB,MAAM;IAAc,GAAG;QAACF,QAAQ;QAAKG,YAAY;IAAc;AACtF"}
1
+ {"version":3,"sources":["../../src/components/harnesses-section.stories.tsx"],"sourcesContent":["import type {ModelProviderConfigDto} 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 {WorkspaceHarnessesSection} from './harnesses-section.js';\n\nconst WORKSPACE_ID = '11111111-1111-4111-8111-111111111111';\n\ntype Scenario =\n | 'default-pi'\n | 'default-claude'\n | 'claude-unavailable'\n | 'default-claude-unavailable'\n | 'loading'\n | 'error';\n\ninterface HarnessesStoryProps {\n scenario: Scenario;\n}\n\nfunction HarnessesStory({scenario}: HarnessesStoryProps) {\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-subtle-base p-24\">\n <WorkspaceHarnessesSection workspaceId={WORKSPACE_ID} />\n </div>\n <Toaster />\n </QueryClientProvider>\n );\n}\n\nconst meta = {\n title: 'Agent/HarnessSettings',\n component: HarnessesStory,\n parameters: {layout: 'fullscreen'},\n args: {scenario: 'default-pi'},\n} satisfies Meta<typeof HarnessesStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const DefaultClaude: Story = {\n args: {scenario: 'default-claude'},\n};\n\nexport const ClaudeUnavailable: Story = {\n args: {scenario: 'claude-unavailable'},\n};\n\nexport const DefaultClaudeUnavailable: Story = {\n args: {scenario: 'default-claude-unavailable'},\n};\n\nexport const Loading: Story = {\n args: {scenario: 'loading'},\n};\n\nexport const ErrorState: Story = {\n args: {scenario: 'error'},\n};\n\nfunction fetchForScenario(scenario: Scenario): typeof fetch {\n return () => {\n if (scenario === 'loading') return new Promise<Response>(() => undefined);\n if (scenario === 'error') return Promise.resolve(errorResponse());\n return Promise.resolve(jsonResponse(configsForScenario(scenario)));\n };\n}\n\nfunction configsForScenario(scenario: Scenario) {\n if (scenario === 'claude-unavailable') {\n return {\n configs: [providerConfig({provider_id: 'openai'})],\n default_provider_id: 'openai',\n default_harness_id: null,\n };\n }\n if (scenario === 'default-claude-unavailable') {\n return {\n configs: [providerConfig({provider_id: 'openai'})],\n default_provider_id: 'openai',\n default_harness_id: 'claude',\n };\n }\n return {\n configs: [providerConfig({provider_id: 'anthropic'})],\n default_provider_id: 'anthropic',\n default_harness_id: scenario === 'default-claude' ? 'claude' : null,\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 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","WorkspaceHarnessesSection","WORKSPACE_ID","HarnessesStory","scenario","baseUrl","fetchImpl","fetchForScenario","queryClient","defaultOptions","queries","retry","client","div","className","workspaceId","meta","title","component","parameters","layout","args","Playground","DefaultClaude","ClaudeUnavailable","DefaultClaudeUnavailable","Loading","ErrorState","Promise","undefined","resolve","errorResponse","jsonResponse","configsForScenario","configs","providerConfig","provider_id","default_provider_id","default_harness_id","overrides","kind","default_model","created_at","updated_at","body","init","Response","JSON","stringify","status","headers","code","statusText"],"mappings":";AACA,SAAQA,kBAAkB,QAAO,sBAAsB;AACvD,SAAQC,OAAO,QAAO,0BAA0B;AAEhD,SAAQC,WAAW,EAAEC,mBAAmB,QAAO,wBAAwB;AACvE,SAAQC,OAAO,QAAO,QAAQ;AAC9B,SAAQC,yBAAyB,QAAO,yBAAyB;AAEjE,MAAMC,eAAe;AAcrB,SAASC,eAAe,EAACC,QAAQ,EAAsB;IACrDR,mBAAmB;QACjBS,SAAS;QACTC,WAAWC,iBAAiBH;IAC9B;IAEA,MAAMI,cAAcR,QAClB,IAAM,IAAIF,YAAY;YAACW,gBAAgB;gBAACC,SAAS;oBAACC,OAAO;gBAAK;YAAC;QAAC,IAChE,EAAE;IAGJ,qBACE,MAACZ;QAAoBa,QAAQJ;;0BAC3B,KAACK;gBAAIC,WAAU;0BACb,cAAA,KAACb;oBAA0Bc,aAAab;;;0BAE1C,KAACL;;;AAGP;AAEA,MAAMmB,OAAO;IACXC,OAAO;IACPC,WAAWf;IACXgB,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAACjB,UAAU;IAAY;AAC/B;AAEA,eAAeY,KAAK;AAGpB,OAAO,MAAMM,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,gBAAuB;IAClCF,MAAM;QAACjB,UAAU;IAAgB;AACnC,EAAE;AAEF,OAAO,MAAMoB,oBAA2B;IACtCH,MAAM;QAACjB,UAAU;IAAoB;AACvC,EAAE;AAEF,OAAO,MAAMqB,2BAAkC;IAC7CJ,MAAM;QAACjB,UAAU;IAA4B;AAC/C,EAAE;AAEF,OAAO,MAAMsB,UAAiB;IAC5BL,MAAM;QAACjB,UAAU;IAAS;AAC5B,EAAE;AAEF,OAAO,MAAMuB,aAAoB;IAC/BN,MAAM;QAACjB,UAAU;IAAO;AAC1B,EAAE;AAEF,SAASG,iBAAiBH,QAAkB;IAC1C,OAAO;QACL,IAAIA,aAAa,WAAW,OAAO,IAAIwB,QAAkB,IAAMC;QAC/D,IAAIzB,aAAa,SAAS,OAAOwB,QAAQE,OAAO,CAACC;QACjD,OAAOH,QAAQE,OAAO,CAACE,aAAaC,mBAAmB7B;IACzD;AACF;AAEA,SAAS6B,mBAAmB7B,QAAkB;IAC5C,IAAIA,aAAa,sBAAsB;QACrC,OAAO;YACL8B,SAAS;gBAACC,eAAe;oBAACC,aAAa;gBAAQ;aAAG;YAClDC,qBAAqB;YACrBC,oBAAoB;QACtB;IACF;IACA,IAAIlC,aAAa,8BAA8B;QAC7C,OAAO;YACL8B,SAAS;gBAACC,eAAe;oBAACC,aAAa;gBAAQ;aAAG;YAClDC,qBAAqB;YACrBC,oBAAoB;QACtB;IACF;IACA,OAAO;QACLJ,SAAS;YAACC,eAAe;gBAACC,aAAa;YAAW;SAAG;QACrDC,qBAAqB;QACrBC,oBAAoBlC,aAAa,mBAAmB,WAAW;IACjE;AACF;AAEA,SAAS+B,eACPI,YAA2D,CAAC,CAAC;IAE7D,OAAO;QACLC,MAAM;QACNJ,aAAa;QACbK,eAAe;QACfC,YAAY;QACZC,YAAY;QACZ,GAAGJ,SAAS;IACd;AACF;AAEA,SAASP,aAAaY,IAAa,EAAEC,OAAqB,CAAC,CAAC;IAC1D,OAAO,IAAIC,SAASC,KAAKC,SAAS,CAACJ,OAAO;QACxCK,QAAQ;QACRC,SAAS;YAAC,gBAAgB;QAAkB;QAC5C,GAAGL,IAAI;IACT;AACF;AAEA,SAASd;IACP,OAAOC,aAAa;QAACmB,MAAM;IAAc,GAAG;QAACF,QAAQ;QAAKG,YAAY;IAAc;AACtF"}
@@ -0,0 +1,4 @@
1
+ export declare function ModelProviderGridSkeleton({ label }: {
2
+ label: string;
3
+ }): import("react").JSX.Element;
4
+ //# sourceMappingURL=model-provider-grid-skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-provider-grid-skeleton.d.ts","sourceRoot":"","sources":["../../src/components/model-provider-grid-skeleton.tsx"],"names":[],"mappings":"AAGA,wBAAgB,yBAAyB,CAAC,EAAC,KAAK,EAAC,EAAE;IAAC,KAAK,EAAE,MAAM,CAAA;CAAC,+BAiBjE"}
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Panel, PanelBody, PanelCell, PanelGrid } from '@shipfox/react-ui/panel';
3
+ import { Skeleton } from '@shipfox/react-ui/skeleton';
4
+ export function ModelProviderGridSkeleton({ label }) {
5
+ return /*#__PURE__*/ _jsx(Panel, {
6
+ children: /*#__PURE__*/ _jsx(PanelBody, {
7
+ children: /*#__PURE__*/ _jsx(PanelGrid, {
8
+ role: "status",
9
+ "aria-busy": "true",
10
+ "aria-label": label,
11
+ children: [
12
+ 0,
13
+ 1,
14
+ 2,
15
+ 3
16
+ ].map((tile)=>/*#__PURE__*/ _jsx(PanelCell, {
17
+ children: /*#__PURE__*/ _jsxs("div", {
18
+ className: "flex items-center justify-between gap-cluster px-row py-row",
19
+ children: [
20
+ /*#__PURE__*/ _jsx(Skeleton, {
21
+ className: "h-16 w-100"
22
+ }),
23
+ /*#__PURE__*/ _jsx(Skeleton, {
24
+ className: "h-16 w-64 shrink-0"
25
+ })
26
+ ]
27
+ })
28
+ }, tile))
29
+ })
30
+ })
31
+ });
32
+ }
33
+
34
+ //# sourceMappingURL=model-provider-grid-skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/model-provider-grid-skeleton.tsx"],"sourcesContent":["import {Panel, PanelBody, PanelCell, PanelGrid} from '@shipfox/react-ui/panel';\nimport {Skeleton} from '@shipfox/react-ui/skeleton';\n\nexport function ModelProviderGridSkeleton({label}: {label: string}) {\n return (\n <Panel>\n <PanelBody>\n <PanelGrid role=\"status\" aria-busy=\"true\" aria-label={label}>\n {[0, 1, 2, 3].map((tile) => (\n <PanelCell key={tile}>\n <div className=\"flex items-center justify-between gap-cluster px-row py-row\">\n <Skeleton className=\"h-16 w-100\" />\n <Skeleton className=\"h-16 w-64 shrink-0\" />\n </div>\n </PanelCell>\n ))}\n </PanelGrid>\n </PanelBody>\n </Panel>\n );\n}\n"],"names":["Panel","PanelBody","PanelCell","PanelGrid","Skeleton","ModelProviderGridSkeleton","label","role","aria-busy","aria-label","map","tile","div","className"],"mappings":";AAAA,SAAQA,KAAK,EAAEC,SAAS,EAAEC,SAAS,EAAEC,SAAS,QAAO,0BAA0B;AAC/E,SAAQC,QAAQ,QAAO,6BAA6B;AAEpD,OAAO,SAASC,0BAA0B,EAACC,KAAK,EAAkB;IAChE,qBACE,KAACN;kBACC,cAAA,KAACC;sBACC,cAAA,KAACE;gBAAUI,MAAK;gBAASC,aAAU;gBAAOC,cAAYH;0BACnD;oBAAC;oBAAG;oBAAG;oBAAG;iBAAE,CAACI,GAAG,CAAC,CAACC,qBACjB,KAACT;kCACC,cAAA,MAACU;4BAAIC,WAAU;;8CACb,KAACT;oCAASS,WAAU;;8CACpB,KAACT;oCAASS,WAAU;;;;uBAHRF;;;;AAW5B"}
@@ -7,7 +7,7 @@ function ModelProviderUsageModalStory({ variant }) {
7
7
  const entry = variant === 'long-list' ? longListEntry() : anthropicEntry();
8
8
  const target = variant === 'custom' ? usageTargetFromCustomConfig(customProviderConfig()) : usageTargetFromCatalogEntry(entry);
9
9
  return /*#__PURE__*/ _jsx("div", {
10
- className: "min-h-screen bg-background-neutral-background p-24",
10
+ className: "min-h-screen bg-background-subtle-base p-24",
11
11
  children: /*#__PURE__*/ _jsx(ModelProviderUsageModal, {
12
12
  target: target,
13
13
  initialModel: target.defaultModel,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/model-provider-usage-modal.stories.tsx"],"sourcesContent":["import type {Meta, StoryObj} from '@storybook/react';\nimport {useState} from 'react';\nimport type {CustomProviderConfig, SupportedProvider} from '#core/models.js';\nimport {ModelProviderUsageModal} from './model-provider-usage-modal.js';\nimport {\n usageTargetFromCatalogEntry,\n usageTargetFromCustomConfig,\n} from './model-provider-usage-target.js';\n\ninterface ModelProviderUsageModalStoryProps {\n variant: 'anthropic' | 'custom' | 'long-list';\n}\n\nfunction ModelProviderUsageModalStory({variant}: ModelProviderUsageModalStoryProps) {\n const [open, setOpen] = useState(true);\n const entry = variant === 'long-list' ? longListEntry() : anthropicEntry();\n const target =\n variant === 'custom'\n ? usageTargetFromCustomConfig(customProviderConfig())\n : usageTargetFromCatalogEntry(entry);\n\n return (\n <div className=\"min-h-screen bg-background-neutral-background p-24\">\n <ModelProviderUsageModal\n target={target}\n initialModel={target.defaultModel}\n workspaceDefaultHarnessId=\"claude\"\n open={open}\n onOpenChange={setOpen}\n />\n </div>\n );\n}\n\nconst meta = {\n title: 'Agent/ProviderUsageModal',\n component: ModelProviderUsageModalStory,\n parameters: {layout: 'fullscreen'},\n args: {variant: 'anthropic'},\n} satisfies Meta<typeof ModelProviderUsageModalStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongModelList: Story = {\n args: {variant: 'long-list'},\n};\n\nexport const CustomProvider: Story = {\n args: {variant: 'custom'},\n};\n\nfunction anthropicEntry(): SupportedProvider {\n return {\n kind: 'supported',\n id: 'anthropic',\n label: 'Anthropic',\n defaultModel: 'claude-opus-4-8',\n credentialFields: [{key: 'api_key', label: 'API key', secret: true}],\n models: [\n {id: 'claude-opus-4-8', label: 'Claude Opus 4.8'},\n {id: 'claude-sonnet-4-8', label: 'Claude Sonnet 4.8'},\n {id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5'},\n ],\n };\n}\n\nfunction longListEntry(): SupportedProvider {\n return {\n kind: 'supported',\n id: 'cloudflare-workers-ai',\n label: 'Cloudflare Workers AI',\n defaultModel: '@cf/moonshotai/kimi-k2.7-code',\n credentialFields: [{key: 'api_token', label: 'API token', secret: true}],\n models: Array.from({length: 56}, (_, index) => ({\n id:\n index === 0\n ? '@cf/moonshotai/kimi-k2.7-code'\n : `provider/research-lab/model-family-${String(index).padStart(2, '0')}-large-context`,\n label: index === 0 ? 'Kimi K2.7 Code' : `Research Model ${index}`,\n })),\n };\n}\n\nfunction customProviderConfig(): CustomProviderConfig {\n return {\n kind: 'custom',\n providerId: 'local-vllm',\n displayName: 'Local vLLM',\n api: 'openai-completions',\n baseUrl: 'http://localhost:8000/v1',\n headers: [],\n secretHeaderNames: [],\n models: [{id: 'llama-3.1', label: 'Llama 3.1'}],\n defaultModel: 'llama-3.1',\n createdAt: '2026-05-08T00:00:00.000Z',\n updatedAt: '2026-05-08T00:00:00.000Z',\n };\n}\n"],"names":["useState","ModelProviderUsageModal","usageTargetFromCatalogEntry","usageTargetFromCustomConfig","ModelProviderUsageModalStory","variant","open","setOpen","entry","longListEntry","anthropicEntry","target","customProviderConfig","div","className","initialModel","defaultModel","workspaceDefaultHarnessId","onOpenChange","meta","title","component","parameters","layout","args","Playground","LongModelList","CustomProvider","kind","id","label","credentialFields","key","secret","models","Array","from","length","_","index","String","padStart","providerId","displayName","api","baseUrl","headers","secretHeaderNames","createdAt","updatedAt"],"mappings":";AACA,SAAQA,QAAQ,QAAO,QAAQ;AAE/B,SAAQC,uBAAuB,QAAO,kCAAkC;AACxE,SACEC,2BAA2B,EAC3BC,2BAA2B,QACtB,mCAAmC;AAM1C,SAASC,6BAA6B,EAACC,OAAO,EAAoC;IAChF,MAAM,CAACC,MAAMC,QAAQ,GAAGP,SAAS;IACjC,MAAMQ,QAAQH,YAAY,cAAcI,kBAAkBC;IAC1D,MAAMC,SACJN,YAAY,WACRF,4BAA4BS,0BAC5BV,4BAA4BM;IAElC,qBACE,KAACK;QAAIC,WAAU;kBACb,cAAA,KAACb;YACCU,QAAQA;YACRI,cAAcJ,OAAOK,YAAY;YACjCC,2BAA0B;YAC1BX,MAAMA;YACNY,cAAcX;;;AAItB;AAEA,MAAMY,OAAO;IACXC,OAAO;IACPC,WAAWjB;IACXkB,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAACnB,SAAS;IAAW;AAC7B;AAEA,eAAec,KAAK;AAGpB,OAAO,MAAMM,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,gBAAuB;IAClCF,MAAM;QAACnB,SAAS;IAAW;AAC7B,EAAE;AAEF,OAAO,MAAMsB,iBAAwB;IACnCH,MAAM;QAACnB,SAAS;IAAQ;AAC1B,EAAE;AAEF,SAASK;IACP,OAAO;QACLkB,MAAM;QACNC,IAAI;QACJC,OAAO;QACPd,cAAc;QACde,kBAAkB;YAAC;gBAACC,KAAK;gBAAWF,OAAO;gBAAWG,QAAQ;YAAI;SAAE;QACpEC,QAAQ;YACN;gBAACL,IAAI;gBAAmBC,OAAO;YAAiB;YAChD;gBAACD,IAAI;gBAAqBC,OAAO;YAAmB;YACpD;gBAACD,IAAI;gBAAoBC,OAAO;YAAkB;SACnD;IACH;AACF;AAEA,SAASrB;IACP,OAAO;QACLmB,MAAM;QACNC,IAAI;QACJC,OAAO;QACPd,cAAc;QACde,kBAAkB;YAAC;gBAACC,KAAK;gBAAaF,OAAO;gBAAaG,QAAQ;YAAI;SAAE;QACxEC,QAAQC,MAAMC,IAAI,CAAC;YAACC,QAAQ;QAAE,GAAG,CAACC,GAAGC,QAAW,CAAA;gBAC9CV,IACEU,UAAU,IACN,kCACA,CAAC,mCAAmC,EAAEC,OAAOD,OAAOE,QAAQ,CAAC,GAAG,KAAK,cAAc,CAAC;gBAC1FX,OAAOS,UAAU,IAAI,mBAAmB,CAAC,eAAe,EAAEA,OAAO;YACnE,CAAA;IACF;AACF;AAEA,SAAS3B;IACP,OAAO;QACLgB,MAAM;QACNc,YAAY;QACZC,aAAa;QACbC,KAAK;QACLC,SAAS;QACTC,SAAS,EAAE;QACXC,mBAAmB,EAAE;QACrBb,QAAQ;YAAC;gBAACL,IAAI;gBAAaC,OAAO;YAAW;SAAE;QAC/Cd,cAAc;QACdgC,WAAW;QACXC,WAAW;IACb;AACF"}
1
+ {"version":3,"sources":["../../src/components/model-provider-usage-modal.stories.tsx"],"sourcesContent":["import type {Meta, StoryObj} from '@storybook/react';\nimport {useState} from 'react';\nimport type {CustomProviderConfig, SupportedProvider} from '#core/models.js';\nimport {ModelProviderUsageModal} from './model-provider-usage-modal.js';\nimport {\n usageTargetFromCatalogEntry,\n usageTargetFromCustomConfig,\n} from './model-provider-usage-target.js';\n\ninterface ModelProviderUsageModalStoryProps {\n variant: 'anthropic' | 'custom' | 'long-list';\n}\n\nfunction ModelProviderUsageModalStory({variant}: ModelProviderUsageModalStoryProps) {\n const [open, setOpen] = useState(true);\n const entry = variant === 'long-list' ? longListEntry() : anthropicEntry();\n const target =\n variant === 'custom'\n ? usageTargetFromCustomConfig(customProviderConfig())\n : usageTargetFromCatalogEntry(entry);\n\n return (\n <div className=\"min-h-screen bg-background-subtle-base p-24\">\n <ModelProviderUsageModal\n target={target}\n initialModel={target.defaultModel}\n workspaceDefaultHarnessId=\"claude\"\n open={open}\n onOpenChange={setOpen}\n />\n </div>\n );\n}\n\nconst meta = {\n title: 'Agent/ProviderUsageModal',\n component: ModelProviderUsageModalStory,\n parameters: {layout: 'fullscreen'},\n args: {variant: 'anthropic'},\n} satisfies Meta<typeof ModelProviderUsageModalStory>;\n\nexport default meta;\ntype Story = StoryObj<typeof meta>;\n\nexport const Playground: Story = {};\n\nexport const LongModelList: Story = {\n args: {variant: 'long-list'},\n};\n\nexport const CustomProvider: Story = {\n args: {variant: 'custom'},\n};\n\nfunction anthropicEntry(): SupportedProvider {\n return {\n kind: 'supported',\n id: 'anthropic',\n label: 'Anthropic',\n defaultModel: 'claude-opus-4-8',\n credentialFields: [{key: 'api_key', label: 'API key', secret: true}],\n models: [\n {id: 'claude-opus-4-8', label: 'Claude Opus 4.8'},\n {id: 'claude-sonnet-4-8', label: 'Claude Sonnet 4.8'},\n {id: 'claude-haiku-4-5', label: 'Claude Haiku 4.5'},\n ],\n };\n}\n\nfunction longListEntry(): SupportedProvider {\n return {\n kind: 'supported',\n id: 'cloudflare-workers-ai',\n label: 'Cloudflare Workers AI',\n defaultModel: '@cf/moonshotai/kimi-k2.7-code',\n credentialFields: [{key: 'api_token', label: 'API token', secret: true}],\n models: Array.from({length: 56}, (_, index) => ({\n id:\n index === 0\n ? '@cf/moonshotai/kimi-k2.7-code'\n : `provider/research-lab/model-family-${String(index).padStart(2, '0')}-large-context`,\n label: index === 0 ? 'Kimi K2.7 Code' : `Research Model ${index}`,\n })),\n };\n}\n\nfunction customProviderConfig(): CustomProviderConfig {\n return {\n kind: 'custom',\n providerId: 'local-vllm',\n displayName: 'Local vLLM',\n api: 'openai-completions',\n baseUrl: 'http://localhost:8000/v1',\n headers: [],\n secretHeaderNames: [],\n models: [{id: 'llama-3.1', label: 'Llama 3.1'}],\n defaultModel: 'llama-3.1',\n createdAt: '2026-05-08T00:00:00.000Z',\n updatedAt: '2026-05-08T00:00:00.000Z',\n };\n}\n"],"names":["useState","ModelProviderUsageModal","usageTargetFromCatalogEntry","usageTargetFromCustomConfig","ModelProviderUsageModalStory","variant","open","setOpen","entry","longListEntry","anthropicEntry","target","customProviderConfig","div","className","initialModel","defaultModel","workspaceDefaultHarnessId","onOpenChange","meta","title","component","parameters","layout","args","Playground","LongModelList","CustomProvider","kind","id","label","credentialFields","key","secret","models","Array","from","length","_","index","String","padStart","providerId","displayName","api","baseUrl","headers","secretHeaderNames","createdAt","updatedAt"],"mappings":";AACA,SAAQA,QAAQ,QAAO,QAAQ;AAE/B,SAAQC,uBAAuB,QAAO,kCAAkC;AACxE,SACEC,2BAA2B,EAC3BC,2BAA2B,QACtB,mCAAmC;AAM1C,SAASC,6BAA6B,EAACC,OAAO,EAAoC;IAChF,MAAM,CAACC,MAAMC,QAAQ,GAAGP,SAAS;IACjC,MAAMQ,QAAQH,YAAY,cAAcI,kBAAkBC;IAC1D,MAAMC,SACJN,YAAY,WACRF,4BAA4BS,0BAC5BV,4BAA4BM;IAElC,qBACE,KAACK;QAAIC,WAAU;kBACb,cAAA,KAACb;YACCU,QAAQA;YACRI,cAAcJ,OAAOK,YAAY;YACjCC,2BAA0B;YAC1BX,MAAMA;YACNY,cAAcX;;;AAItB;AAEA,MAAMY,OAAO;IACXC,OAAO;IACPC,WAAWjB;IACXkB,YAAY;QAACC,QAAQ;IAAY;IACjCC,MAAM;QAACnB,SAAS;IAAW;AAC7B;AAEA,eAAec,KAAK;AAGpB,OAAO,MAAMM,aAAoB,CAAC,EAAE;AAEpC,OAAO,MAAMC,gBAAuB;IAClCF,MAAM;QAACnB,SAAS;IAAW;AAC7B,EAAE;AAEF,OAAO,MAAMsB,iBAAwB;IACnCH,MAAM;QAACnB,SAAS;IAAQ;AAC1B,EAAE;AAEF,SAASK;IACP,OAAO;QACLkB,MAAM;QACNC,IAAI;QACJC,OAAO;QACPd,cAAc;QACde,kBAAkB;YAAC;gBAACC,KAAK;gBAAWF,OAAO;gBAAWG,QAAQ;YAAI;SAAE;QACpEC,QAAQ;YACN;gBAACL,IAAI;gBAAmBC,OAAO;YAAiB;YAChD;gBAACD,IAAI;gBAAqBC,OAAO;YAAmB;YACpD;gBAACD,IAAI;gBAAoBC,OAAO;YAAkB;SACnD;IACH;AACF;AAEA,SAASrB;IACP,OAAO;QACLmB,MAAM;QACNC,IAAI;QACJC,OAAO;QACPd,cAAc;QACde,kBAAkB;YAAC;gBAACC,KAAK;gBAAaF,OAAO;gBAAaG,QAAQ;YAAI;SAAE;QACxEC,QAAQC,MAAMC,IAAI,CAAC;YAACC,QAAQ;QAAE,GAAG,CAACC,GAAGC,QAAW,CAAA;gBAC9CV,IACEU,UAAU,IACN,kCACA,CAAC,mCAAmC,EAAEC,OAAOD,OAAOE,QAAQ,CAAC,GAAG,KAAK,cAAc,CAAC;gBAC1FX,OAAOS,UAAU,IAAI,mBAAmB,CAAC,eAAe,EAAEA,OAAO;YACnE,CAAA;IACF;AACF;AAEA,SAAS3B;IACP,OAAO;QACLgB,MAAM;QACNc,YAAY;QACZC,aAAa;QACbC,KAAK;QACLC,SAAS;QACTC,SAAS,EAAE;QACXC,mBAAmB,EAAE;QACrBb,QAAQ;YAAC;gBAACL,IAAI;gBAAaC,OAAO;YAAW;SAAE;QAC/Cd,cAAc;QACdgC,WAAW;QACXC,WAAW;IACb;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"model-providers-section.d.ts","sourceRoot":"","sources":["../../src/components/model-providers-section.tsx"],"names":[],"mappings":"AAuEA,wBAAgB,8BAA8B,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BA2TlF"}
1
+ {"version":3,"file":"model-providers-section.d.ts","sourceRoot":"","sources":["../../src/components/model-providers-section.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,8BAA8B,CAAC,EAAC,WAAW,EAAC,EAAE;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,+BAgVlF"}