@shipfox/client-agent 21.0.0 → 22.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +24 -0
- package/dist/components/add-custom-provider-card.d.ts.map +1 -1
- package/dist/components/add-custom-provider-card.js +11 -31
- package/dist/components/add-custom-provider-card.js.map +1 -1
- package/dist/components/available-provider-card.d.ts.map +1 -1
- package/dist/components/available-provider-card.js +11 -31
- package/dist/components/available-provider-card.js.map +1 -1
- package/dist/components/available-providers-grid.d.ts +0 -1
- package/dist/components/available-providers-grid.d.ts.map +1 -1
- package/dist/components/available-providers-grid.js +33 -27
- package/dist/components/available-providers-grid.js.map +1 -1
- package/dist/components/harnesses-section.d.ts.map +1 -1
- package/dist/components/harnesses-section.js +152 -146
- package/dist/components/harnesses-section.js.map +1 -1
- package/dist/components/model-provider-grid-skeleton.d.ts +4 -0
- package/dist/components/model-provider-grid-skeleton.d.ts.map +1 -0
- package/dist/components/model-provider-grid-skeleton.js +34 -0
- package/dist/components/model-provider-grid-skeleton.js.map +1 -0
- package/dist/components/model-providers-section.d.ts.map +1 -1
- package/dist/components/model-providers-section.js +268 -281
- package/dist/components/model-providers-section.js.map +1 -1
- package/dist/pages/model-provider-onboarding-page.d.ts.map +1 -1
- package/dist/pages/model-provider-onboarding-page.js +32 -64
- package/dist/pages/model-provider-onboarding-page.js.map +1 -1
- package/dist/routes/agents-settings.js +15 -5
- package/dist/routes/agents-settings.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/add-custom-provider-card.tsx +9 -23
- package/src/components/available-provider-card.tsx +9 -23
- package/src/components/available-providers-grid.tsx +36 -29
- package/src/components/harnesses-section.tsx +116 -109
- package/src/components/model-provider-grid-skeleton.tsx +21 -0
- package/src/components/model-providers-section.test.tsx +2 -2
- package/src/components/model-providers-section.tsx +208 -200
- package/src/pages/model-provider-onboarding-page.tsx +26 -54
- package/src/routes/agents-settings.tsx +7 -3
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -17,11 +17,11 @@ import {
|
|
|
17
17
|
ModalHeader,
|
|
18
18
|
ModalTitle,
|
|
19
19
|
} from '@shipfox/react-ui/modal';
|
|
20
|
+
import {Panel, PanelBody, PanelRow} from '@shipfox/react-ui/panel';
|
|
20
21
|
import {Skeleton} from '@shipfox/react-ui/skeleton';
|
|
21
22
|
import {toast} from '@shipfox/react-ui/toast';
|
|
22
23
|
import {Tooltip, TooltipContent, TooltipTrigger} from '@shipfox/react-ui/tooltip';
|
|
23
24
|
import {Header, Text} from '@shipfox/react-ui/typography';
|
|
24
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
25
25
|
import {useEffect, useMemo, useReducer, useRef, useState} from 'react';
|
|
26
26
|
import {
|
|
27
27
|
type ManagementModal,
|
|
@@ -46,10 +46,11 @@ import {
|
|
|
46
46
|
useSetDefaultModelProviderMutation,
|
|
47
47
|
} from '#hooks/api/model-providers.js';
|
|
48
48
|
import {AddCustomProviderCard} from './add-custom-provider-card.js';
|
|
49
|
-
import {AvailableProvidersGrid
|
|
49
|
+
import {AvailableProvidersGrid} from './available-providers-grid.js';
|
|
50
50
|
import {ChangeDefaultModelForm} from './change-default-model-form.js';
|
|
51
51
|
import {CustomModelProviderForm} from './custom-model-provider-form.js';
|
|
52
52
|
import {modelProviderConfigErrorToFormError} from './form-errors.js';
|
|
53
|
+
import {ModelProviderGridSkeleton} from './model-provider-grid-skeleton.js';
|
|
53
54
|
import {ModelProviderUsageModal} from './model-provider-usage-modal.js';
|
|
54
55
|
import {
|
|
55
56
|
type ModelProviderUsageTarget,
|
|
@@ -58,9 +59,6 @@ import {
|
|
|
58
59
|
} from './model-provider-usage-target.js';
|
|
59
60
|
import {ModelProviderTestAndSaveForm} from './test-and-save-form.js';
|
|
60
61
|
|
|
61
|
-
const SURFACE_CLASS =
|
|
62
|
-
'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
63
|
-
|
|
64
62
|
type UsageTarget = {
|
|
65
63
|
target: ModelProviderUsageTarget;
|
|
66
64
|
initialModel: string | null;
|
|
@@ -122,9 +120,6 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
|
|
|
122
120
|
>
|
|
123
121
|
<div className="flex flex-col gap-tight">
|
|
124
122
|
<Header variant="h3">Configured providers</Header>
|
|
125
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
126
|
-
Workspace credentials available to agent steps.
|
|
127
|
-
</Text>
|
|
128
123
|
</div>
|
|
129
124
|
|
|
130
125
|
{configsQuery.isPending ? (
|
|
@@ -132,91 +127,99 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
|
|
|
132
127
|
) : null}
|
|
133
128
|
|
|
134
129
|
{configsQuery.isError && configsQuery.data === undefined ? (
|
|
135
|
-
<
|
|
136
|
-
<QueryLoadError query={configsQuery} subject="model provider configs" />
|
|
137
|
-
</
|
|
130
|
+
<Panel>
|
|
131
|
+
<QueryLoadError query={configsQuery} subject="model provider configs" variant="panel" />
|
|
132
|
+
</Panel>
|
|
138
133
|
) : null}
|
|
139
134
|
|
|
140
135
|
{configsQuery.data !== undefined && configs.length === 0 ? (
|
|
141
|
-
<
|
|
136
|
+
<Panel>
|
|
142
137
|
<EmptyState
|
|
143
138
|
icon="key2Line"
|
|
144
139
|
title="No providers configured"
|
|
145
140
|
description="Configure a provider below to run agent steps with workspace-managed credentials."
|
|
141
|
+
variant="panel"
|
|
146
142
|
/>
|
|
147
|
-
</
|
|
143
|
+
</Panel>
|
|
148
144
|
) : null}
|
|
149
145
|
|
|
150
146
|
{configs.length > 0 ? (
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
147
|
+
<Panel>
|
|
148
|
+
<PanelBody asChild>
|
|
149
|
+
<ul>
|
|
150
|
+
{configs.map((config) => {
|
|
151
|
+
const catalogEntry = providerById.get(config.providerId);
|
|
152
|
+
const entry =
|
|
153
|
+
catalogEntry && isSupportedProvider(catalogEntry) ? catalogEntry : undefined;
|
|
154
|
+
const builtinConfig = isBuiltinModelProviderConfig(config) ? config : undefined;
|
|
155
|
+
const customConfig = isCustomModelProviderConfig(config) ? config : undefined;
|
|
156
|
+
return (
|
|
157
|
+
<ConfiguredProviderRow
|
|
158
|
+
key={config.providerId}
|
|
159
|
+
workspaceId={workspaceId}
|
|
160
|
+
config={config}
|
|
161
|
+
entry={entry}
|
|
162
|
+
isDefault={config.providerId === defaultProviderId}
|
|
163
|
+
onEdit={() => {
|
|
164
|
+
if (entry && builtinConfig) {
|
|
165
|
+
dispatchModal({
|
|
166
|
+
type: 'edit-builtin',
|
|
167
|
+
provider: entry,
|
|
168
|
+
config: builtinConfig,
|
|
169
|
+
});
|
|
170
|
+
} else if (customConfig) {
|
|
171
|
+
dispatchModal({type: 'edit-custom', config: customConfig});
|
|
172
|
+
}
|
|
173
|
+
}}
|
|
174
|
+
onChangeDefaultModel={() => {
|
|
175
|
+
if (entry && builtinConfig)
|
|
176
|
+
dispatchModal({
|
|
177
|
+
type: 'change-default-model',
|
|
178
|
+
provider: entry,
|
|
179
|
+
config: builtinConfig,
|
|
180
|
+
});
|
|
181
|
+
}}
|
|
182
|
+
onShowUsage={() => {
|
|
183
|
+
if (entry && builtinConfig) {
|
|
184
|
+
setPendingUsageTarget(null);
|
|
185
|
+
dispatchModal({
|
|
186
|
+
type: 'show-usage',
|
|
187
|
+
providerId: entry.id,
|
|
188
|
+
initialModel: builtinConfig.defaultModel,
|
|
189
|
+
restoreFocusToConfiguredProviders: false,
|
|
190
|
+
});
|
|
191
|
+
} else if (customConfig) {
|
|
192
|
+
setPendingUsageTarget(null);
|
|
193
|
+
dispatchModal({
|
|
194
|
+
type: 'show-usage',
|
|
195
|
+
providerId: customConfig.providerId,
|
|
196
|
+
initialModel: customConfig.defaultModel,
|
|
197
|
+
restoreFocusToConfiguredProviders: false,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
);
|
|
203
|
+
})}
|
|
204
|
+
</ul>
|
|
205
|
+
</PanelBody>
|
|
206
|
+
</Panel>
|
|
203
207
|
) : null}
|
|
204
208
|
</section>
|
|
205
209
|
|
|
206
210
|
<section className="flex flex-col gap-group" aria-label="Available providers">
|
|
207
211
|
<div className="flex flex-col gap-tight">
|
|
208
212
|
<Header variant="h3">Available providers</Header>
|
|
209
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
210
|
-
Providers that can be configured for agent steps in this workspace.
|
|
211
|
-
</Text>
|
|
212
213
|
</div>
|
|
213
214
|
|
|
214
|
-
{catalogQuery.isPending || configsQuery.isPending ?
|
|
215
|
+
{catalogQuery.isPending || configsQuery.isPending ? (
|
|
216
|
+
<ModelProviderGridSkeleton label="Loading available providers" />
|
|
217
|
+
) : null}
|
|
215
218
|
|
|
216
219
|
{catalogQuery.isError && catalogQuery.data === undefined ? (
|
|
217
|
-
<
|
|
218
|
-
<QueryLoadError query={catalogQuery} subject="model provider catalog" />
|
|
219
|
-
</
|
|
220
|
+
<Panel>
|
|
221
|
+
<QueryLoadError query={catalogQuery} subject="model provider catalog" variant="panel" />
|
|
222
|
+
</Panel>
|
|
220
223
|
) : null}
|
|
221
224
|
|
|
222
225
|
{configsLoaded ? (
|
|
@@ -234,9 +237,6 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
|
|
|
234
237
|
<section className="flex flex-col gap-group" aria-label="Unsupported providers">
|
|
235
238
|
<div className="flex flex-col gap-tight">
|
|
236
239
|
<Header variant="h3">Unsupported providers</Header>
|
|
237
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
238
|
-
Providers that cannot be configured in this workspace yet.
|
|
239
|
-
</Text>
|
|
240
240
|
</div>
|
|
241
241
|
|
|
242
242
|
{catalogQuery.isPending ? (
|
|
@@ -244,25 +244,35 @@ export function WorkspaceModelProvidersSection({workspaceId}: {workspaceId: stri
|
|
|
244
244
|
) : null}
|
|
245
245
|
|
|
246
246
|
{unsupportedProviders.length > 0 ? (
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
247
|
+
<Panel>
|
|
248
|
+
<PanelBody asChild>
|
|
249
|
+
<ul>
|
|
250
|
+
{unsupportedProviders.map((entry) => (
|
|
251
|
+
<PanelRow
|
|
252
|
+
asChild
|
|
253
|
+
className="items-start justify-start gap-cluster opacity-70 hover:bg-background-neutral-base"
|
|
254
|
+
key={entry.id}
|
|
255
|
+
>
|
|
256
|
+
<li>
|
|
257
|
+
<Icon
|
|
258
|
+
name="forbid2Line"
|
|
259
|
+
className="mt-[2px] size-18 shrink-0 text-foreground-neutral-muted"
|
|
260
|
+
aria-hidden
|
|
261
|
+
/>
|
|
262
|
+
<div className="flex min-w-0 flex-1 flex-col gap-tight">
|
|
263
|
+
<Text size="md" bold className="truncate">
|
|
264
|
+
{entry.label}
|
|
265
|
+
</Text>
|
|
266
|
+
<Text size="sm" className="text-foreground-neutral-muted">
|
|
267
|
+
{entry.unsupportedReason}
|
|
268
|
+
</Text>
|
|
269
|
+
</div>
|
|
270
|
+
</li>
|
|
271
|
+
</PanelRow>
|
|
272
|
+
))}
|
|
273
|
+
</ul>
|
|
274
|
+
</PanelBody>
|
|
275
|
+
</Panel>
|
|
266
276
|
) : null}
|
|
267
277
|
</section>
|
|
268
278
|
|
|
@@ -468,91 +478,93 @@ function ConfiguredProviderRow({
|
|
|
468
478
|
}
|
|
469
479
|
|
|
470
480
|
return (
|
|
471
|
-
<
|
|
472
|
-
<
|
|
473
|
-
<div className="flex
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
<
|
|
478
|
-
<
|
|
479
|
-
<
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
<
|
|
497
|
-
<
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
481
|
+
<PanelRow asChild className="flex-col items-stretch gap-inline">
|
|
482
|
+
<li>
|
|
483
|
+
<div className="flex items-center justify-between gap-cluster">
|
|
484
|
+
<div className="flex min-w-0 items-center gap-inline">
|
|
485
|
+
{isDefault ? (
|
|
486
|
+
<>
|
|
487
|
+
<Tooltip>
|
|
488
|
+
<TooltipTrigger asChild>
|
|
489
|
+
<span className="inline-flex size-16 shrink-0 items-center justify-center">
|
|
490
|
+
<Icon
|
|
491
|
+
name="starLine"
|
|
492
|
+
className="size-16 text-foreground-neutral-muted"
|
|
493
|
+
aria-hidden
|
|
494
|
+
/>
|
|
495
|
+
</span>
|
|
496
|
+
</TooltipTrigger>
|
|
497
|
+
<TooltipContent>Default provider</TooltipContent>
|
|
498
|
+
</Tooltip>
|
|
499
|
+
<span className="sr-only">Default provider</span>
|
|
500
|
+
</>
|
|
501
|
+
) : null}
|
|
502
|
+
<Text size="md" bold className="truncate">
|
|
503
|
+
{label}
|
|
504
|
+
</Text>
|
|
505
|
+
</div>
|
|
506
|
+
<DropdownMenu>
|
|
507
|
+
<DropdownMenuTrigger asChild>
|
|
508
|
+
<IconButton
|
|
509
|
+
size="sm"
|
|
510
|
+
variant="transparent"
|
|
511
|
+
icon="more2Line"
|
|
512
|
+
aria-label={`Open ${label} provider actions`}
|
|
513
|
+
/>
|
|
514
|
+
</DropdownMenuTrigger>
|
|
515
|
+
<DropdownMenuContent align="end">
|
|
516
|
+
{!isDefault ? (
|
|
517
|
+
<DropdownMenuItem
|
|
518
|
+
icon="starLine"
|
|
519
|
+
disabled={setDefault.isPending || (!entry && !customConfig)}
|
|
520
|
+
onSelect={() => {
|
|
521
|
+
void handleSetDefault();
|
|
522
|
+
}}
|
|
523
|
+
>
|
|
524
|
+
Set as default
|
|
525
|
+
</DropdownMenuItem>
|
|
526
|
+
) : null}
|
|
527
|
+
{isBuiltinConfig ? (
|
|
528
|
+
<DropdownMenuItem
|
|
529
|
+
icon="settings3Line"
|
|
530
|
+
disabled={!entry}
|
|
531
|
+
onSelect={onChangeDefaultModel}
|
|
532
|
+
>
|
|
533
|
+
Change default model
|
|
534
|
+
</DropdownMenuItem>
|
|
535
|
+
) : null}
|
|
536
|
+
<DropdownMenuItem icon="bookOpenLine" disabled={!canUse} onSelect={onShowUsage}>
|
|
537
|
+
View workflow example
|
|
538
|
+
</DropdownMenuItem>
|
|
539
|
+
<DropdownMenuItem icon="editLine" disabled={!canEdit} onSelect={onEdit}>
|
|
540
|
+
{customConfig ? 'Edit' : 'Edit credentials'}
|
|
541
|
+
</DropdownMenuItem>
|
|
506
542
|
<DropdownMenuItem
|
|
507
|
-
icon="
|
|
508
|
-
disabled={setDefault.isPending || (!entry && !customConfig)}
|
|
543
|
+
icon="deleteBinLine"
|
|
509
544
|
onSelect={() => {
|
|
510
|
-
|
|
545
|
+
setDeleteOpen(true);
|
|
511
546
|
}}
|
|
512
547
|
>
|
|
513
|
-
|
|
514
|
-
</DropdownMenuItem>
|
|
515
|
-
) : null}
|
|
516
|
-
{isBuiltinConfig ? (
|
|
517
|
-
<DropdownMenuItem
|
|
518
|
-
icon="settings3Line"
|
|
519
|
-
disabled={!entry}
|
|
520
|
-
onSelect={onChangeDefaultModel}
|
|
521
|
-
>
|
|
522
|
-
Change default model
|
|
548
|
+
Delete
|
|
523
549
|
</DropdownMenuItem>
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
{defaultError ? (
|
|
543
|
-
<Callout role="alert" type="error">
|
|
544
|
-
<Text size="sm">{defaultError}</Text>
|
|
545
|
-
</Callout>
|
|
546
|
-
) : null}
|
|
547
|
-
<DeleteModelProviderDialog
|
|
548
|
-
open={deleteOpen}
|
|
549
|
-
onOpenChange={handleDeleteOpenChange}
|
|
550
|
-
label={label}
|
|
551
|
-
errorMessage={deleteError}
|
|
552
|
-
isLoading={deleteConfig.isPending}
|
|
553
|
-
onDelete={handleDelete}
|
|
554
|
-
/>
|
|
555
|
-
</li>
|
|
550
|
+
</DropdownMenuContent>
|
|
551
|
+
</DropdownMenu>
|
|
552
|
+
</div>
|
|
553
|
+
{defaultError ? (
|
|
554
|
+
<Callout role="alert" type="error">
|
|
555
|
+
<Text size="sm">{defaultError}</Text>
|
|
556
|
+
</Callout>
|
|
557
|
+
) : null}
|
|
558
|
+
<DeleteModelProviderDialog
|
|
559
|
+
open={deleteOpen}
|
|
560
|
+
onOpenChange={handleDeleteOpenChange}
|
|
561
|
+
label={label}
|
|
562
|
+
errorMessage={deleteError}
|
|
563
|
+
isLoading={deleteConfig.isPending}
|
|
564
|
+
onDelete={handleDelete}
|
|
565
|
+
/>
|
|
566
|
+
</li>
|
|
567
|
+
</PanelRow>
|
|
556
568
|
);
|
|
557
569
|
}
|
|
558
570
|
|
|
@@ -602,31 +614,27 @@ function DeleteModelProviderDialog({
|
|
|
602
614
|
|
|
603
615
|
function ModelProviderRowsSkeleton({label}: {label: string}) {
|
|
604
616
|
return (
|
|
605
|
-
<
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
{[0, 1, 2, 3].map((card) => (
|
|
628
|
-
<Skeleton key={card} className="h-136 w-full" />
|
|
629
|
-
))}
|
|
630
|
-
</div>
|
|
617
|
+
<Panel>
|
|
618
|
+
<PanelBody asChild>
|
|
619
|
+
<ul role="status" aria-label={label}>
|
|
620
|
+
{[0, 1, 2].map((row) => (
|
|
621
|
+
<PanelRow
|
|
622
|
+
asChild
|
|
623
|
+
className="justify-start gap-cluster hover:bg-background-neutral-base"
|
|
624
|
+
key={row}
|
|
625
|
+
>
|
|
626
|
+
<li>
|
|
627
|
+
<Skeleton className="size-32 shrink-0" />
|
|
628
|
+
<div className="flex min-w-0 flex-1 flex-col gap-inline">
|
|
629
|
+
<Skeleton className="h-16 w-120" />
|
|
630
|
+
<Skeleton className="h-14 w-180" />
|
|
631
|
+
</div>
|
|
632
|
+
<Skeleton className="h-28 w-96 shrink-0" />
|
|
633
|
+
</li>
|
|
634
|
+
</PanelRow>
|
|
635
|
+
))}
|
|
636
|
+
</ul>
|
|
637
|
+
</PanelBody>
|
|
638
|
+
</Panel>
|
|
631
639
|
);
|
|
632
640
|
}
|
|
@@ -2,15 +2,14 @@ import {QueryLoadError} from '@shipfox/client-ui';
|
|
|
2
2
|
import {Button} from '@shipfox/react-ui/button';
|
|
3
3
|
import {Callout} from '@shipfox/react-ui/callout';
|
|
4
4
|
import {EmptyState} from '@shipfox/react-ui/empty-state';
|
|
5
|
-
import {Icon} from '@shipfox/react-ui/icon';
|
|
6
5
|
import {Modal, ModalContent, ModalHeader, ModalTitle} from '@shipfox/react-ui/modal';
|
|
7
|
-
import {
|
|
6
|
+
import {Panel, PanelBody, PanelCell, PanelCellAction, PanelGrid} from '@shipfox/react-ui/panel';
|
|
8
7
|
import {toast} from '@shipfox/react-ui/toast';
|
|
9
8
|
import {Header, Text} from '@shipfox/react-ui/typography';
|
|
10
|
-
import {cn} from '@shipfox/react-ui/utils';
|
|
11
9
|
import {useEffect, useMemo, useReducer} from 'react';
|
|
12
|
-
import {AvailableProvidersGrid
|
|
10
|
+
import {AvailableProvidersGrid} from '#components/available-providers-grid.js';
|
|
13
11
|
import {modelProviderConfigErrorToFormError} from '#components/form-errors.js';
|
|
12
|
+
import {ModelProviderGridSkeleton} from '#components/model-provider-grid-skeleton.js';
|
|
14
13
|
import {ModelProviderTestAndSaveForm} from '#components/test-and-save-form.js';
|
|
15
14
|
import {DEFAULT_HARNESS, harnessSupportsProvider, listHarnesses} from '#core/harness-policy.js';
|
|
16
15
|
import type {HarnessDescriptor, HarnessId, SupportedProvider} from '#core/models.js';
|
|
@@ -22,9 +21,6 @@ import {
|
|
|
22
21
|
} from '#hooks/api/model-providers.js';
|
|
23
22
|
import {dismissModelProviderOnboarding} from '#state/model-provider-onboarding.js';
|
|
24
23
|
|
|
25
|
-
const SURFACE_CLASS =
|
|
26
|
-
'overflow-hidden rounded-8 border border-border-neutral-base bg-background-neutral-base';
|
|
27
|
-
|
|
28
24
|
export function ModelProviderOnboardingPage({
|
|
29
25
|
workspaceId,
|
|
30
26
|
onSkip,
|
|
@@ -104,7 +100,7 @@ export function ModelProviderOnboardingPage({
|
|
|
104
100
|
: 'model-provider-provider-step';
|
|
105
101
|
|
|
106
102
|
return (
|
|
107
|
-
<div className="
|
|
103
|
+
<div className="flex w-full flex-col gap-section">
|
|
108
104
|
<header className="flex flex-col gap-cluster">
|
|
109
105
|
<div className="flex items-start justify-between gap-group max-[520px]:flex-col max-[520px]:gap-inline">
|
|
110
106
|
<Header id={headingId} variant="h1" tabIndex={-1} className="outline-none">
|
|
@@ -216,42 +212,32 @@ export function ModelProviderOnboardingPage({
|
|
|
216
212
|
|
|
217
213
|
function HarnessPicker({onSelect}: {onSelect: (harness: HarnessId) => void}) {
|
|
218
214
|
return (
|
|
219
|
-
<
|
|
220
|
-
|
|
221
|
-
<
|
|
222
|
-
|
|
223
|
-
|
|
215
|
+
<Panel>
|
|
216
|
+
<PanelBody>
|
|
217
|
+
<PanelGrid aria-label="Agent harnesses">
|
|
218
|
+
{listHarnesses().map((harness) => (
|
|
219
|
+
<HarnessCard key={harness.id} harness={harness} onChoose={() => onSelect(harness.id)} />
|
|
220
|
+
))}
|
|
221
|
+
</PanelGrid>
|
|
222
|
+
</PanelBody>
|
|
223
|
+
</Panel>
|
|
224
224
|
);
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
function HarnessCard({harness, onChoose}: {harness: HarnessDescriptor; onChoose: () => void}) {
|
|
228
228
|
return (
|
|
229
|
-
<
|
|
230
|
-
<
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
<Text size="md" bold className="min-w-0 truncate">
|
|
242
|
-
{harness.label}
|
|
243
|
-
</Text>
|
|
244
|
-
<Text size="sm" className="text-foreground-neutral-muted">
|
|
245
|
-
{harness.description}
|
|
246
|
-
</Text>
|
|
247
|
-
</div>
|
|
248
|
-
<div className="flex shrink-0 items-center gap-tight text-foreground-neutral-muted transition-colors group-hover:text-foreground-highlight-interactive">
|
|
249
|
-
<Text size="sm">Choose</Text>
|
|
250
|
-
<Icon name="chevronRight" className="size-16" />
|
|
251
|
-
</div>
|
|
252
|
-
</div>
|
|
253
|
-
</button>
|
|
254
|
-
</li>
|
|
229
|
+
<PanelCell>
|
|
230
|
+
<PanelCellAction action="Choose" aria-label={`Choose ${harness.label}`} onClick={onChoose}>
|
|
231
|
+
<span className="flex min-w-0 flex-col gap-tight">
|
|
232
|
+
<Text as="span" size="md" bold className="min-w-0 truncate">
|
|
233
|
+
{harness.label}
|
|
234
|
+
</Text>
|
|
235
|
+
<Text as="span" size="sm" className="text-foreground-neutral-muted">
|
|
236
|
+
{harness.description}
|
|
237
|
+
</Text>
|
|
238
|
+
</span>
|
|
239
|
+
</PanelCellAction>
|
|
240
|
+
</PanelCell>
|
|
255
241
|
);
|
|
256
242
|
}
|
|
257
243
|
|
|
@@ -265,11 +251,7 @@ function ModelProviderPicker({
|
|
|
265
251
|
onSelect: (entry: SupportedProvider) => void;
|
|
266
252
|
}) {
|
|
267
253
|
if (catalogQuery.isPending) {
|
|
268
|
-
return
|
|
269
|
-
<div role="status" aria-busy="true" aria-label="Loading model providers">
|
|
270
|
-
<ModelProviderGridSkeleton />
|
|
271
|
-
</div>
|
|
272
|
-
);
|
|
254
|
+
return <ModelProviderGridSkeleton label="Loading model providers" />;
|
|
273
255
|
}
|
|
274
256
|
|
|
275
257
|
if (catalogQuery.isError && catalogQuery.data === undefined) {
|
|
@@ -288,13 +270,3 @@ function ModelProviderPicker({
|
|
|
288
270
|
|
|
289
271
|
return <AvailableProvidersGrid entries={supportedProviders} onSelect={onSelect} />;
|
|
290
272
|
}
|
|
291
|
-
|
|
292
|
-
function ModelProviderGridSkeleton() {
|
|
293
|
-
return (
|
|
294
|
-
<div className={PROVIDER_GRID_CLASS}>
|
|
295
|
-
{[0, 1, 2, 3].map((card) => (
|
|
296
|
-
<Skeleton key={card} className="h-136 w-full" />
|
|
297
|
-
))}
|
|
298
|
-
</div>
|
|
299
|
-
);
|
|
300
|
-
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {defineRoute, useActiveWorkspace} from '@shipfox/client-shell/runtime';
|
|
2
|
+
import {Header} from '@shipfox/react-ui/typography';
|
|
2
3
|
import {WorkspaceHarnessesSection, WorkspaceModelProvidersSection} from '#index.js';
|
|
3
4
|
|
|
4
5
|
export default defineRoute({
|
|
@@ -6,9 +7,12 @@ export default defineRoute({
|
|
|
6
7
|
component: () => {
|
|
7
8
|
const workspace = useActiveWorkspace();
|
|
8
9
|
return (
|
|
9
|
-
<div className="flex flex-col gap-
|
|
10
|
-
<
|
|
11
|
-
<
|
|
10
|
+
<div className="flex min-w-0 flex-col gap-section">
|
|
11
|
+
<Header variant="h1">Agents</Header>
|
|
12
|
+
<div className="flex flex-col gap-region">
|
|
13
|
+
<WorkspaceHarnessesSection workspaceId={workspace.id} />
|
|
14
|
+
<WorkspaceModelProvidersSection workspaceId={workspace.id} />
|
|
15
|
+
</div>
|
|
12
16
|
</div>
|
|
13
17
|
);
|
|
14
18
|
},
|