feat(web): per-thread/per-spoon runtime picker and Anthropic OAuth provider profile
This commit is contained in:
@@ -25,9 +25,17 @@ import {
|
||||
Textarea,
|
||||
} from '@spoon/ui';
|
||||
|
||||
type RuntimeName = 'codex' | 'opencode' | 'claude';
|
||||
|
||||
const runtimeLabels: Record<RuntimeName, string> = {
|
||||
codex: 'Codex',
|
||||
opencode: 'OpenCode',
|
||||
claude: 'Claude',
|
||||
};
|
||||
|
||||
type AgentSettings = {
|
||||
defaultBaseBranch?: string;
|
||||
runtime?: 'opencode' | 'openai_direct';
|
||||
runtime?: RuntimeName | 'openai_direct';
|
||||
agentModel: string;
|
||||
reasoningEffort: string;
|
||||
envFilePath?: string;
|
||||
@@ -72,6 +80,7 @@ export const ThreadWorkspaceForm = ({
|
||||
const [aiProviderProfileId, setAiProviderProfileId] = useState(
|
||||
agentSettings?.aiProviderProfileId ?? '__settings',
|
||||
);
|
||||
const [runtime, setRuntime] = useState<RuntimeName | undefined>();
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const effectiveProviderProfileId =
|
||||
aiProviderProfileId === '__settings'
|
||||
@@ -87,6 +96,18 @@ export const ThreadWorkspaceForm = ({
|
||||
(agentSettings?.aiProviderProfileId ?? defaultProfile?._id)
|
||||
: profile._id === aiProviderProfileId,
|
||||
);
|
||||
const supportedRuntimes = (selectedProfile?.supportedRuntimes ??
|
||||
[]) as RuntimeName[];
|
||||
const settingsRuntime = agentSettings?.runtime as RuntimeName | undefined;
|
||||
const effectiveRuntime =
|
||||
runtime && supportedRuntimes.includes(runtime)
|
||||
? runtime
|
||||
: settingsRuntime && supportedRuntimes.includes(settingsRuntime)
|
||||
? settingsRuntime
|
||||
: supportedRuntimes[0];
|
||||
const runtimeValid = Boolean(
|
||||
effectiveRuntime && supportedRuntimes.includes(effectiveRuntime),
|
||||
);
|
||||
|
||||
const submit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
@@ -97,6 +118,7 @@ export const ThreadWorkspaceForm = ({
|
||||
prompt,
|
||||
baseBranch,
|
||||
requestedBranchName: requestedBranchName || undefined,
|
||||
runtime: effectiveRuntime,
|
||||
materializeEnvFile,
|
||||
envFilePath: materializeEnvFile ? envFilePath : undefined,
|
||||
aiProviderProfileId:
|
||||
@@ -140,7 +162,22 @@ export const ThreadWorkspaceForm = ({
|
||||
<div className='grid gap-3 md:grid-cols-2'>
|
||||
<div className='grid gap-2'>
|
||||
<Label>Workspace runtime</Label>
|
||||
<Input value='OpenCode workspace' disabled />
|
||||
<Select
|
||||
value={effectiveRuntime ?? ''}
|
||||
onValueChange={(value) => setRuntime(value as RuntimeName)}
|
||||
disabled={!supportedRuntimes.length}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder='Select a provider first' />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{supportedRuntimes.map((name) => (
|
||||
<SelectItem key={name} value={name}>
|
||||
{runtimeLabels[name]}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='grid gap-2'>
|
||||
<Label>AI provider</Label>
|
||||
@@ -219,7 +256,10 @@ export const ThreadWorkspaceForm = ({
|
||||
<strong>{selectedProfile?.reasoningEffort ?? 'medium'}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<Button type='submit' disabled={submitting || !hasProvider}>
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={submitting || !hasProvider || !runtimeValid}
|
||||
>
|
||||
{submitting ? 'Creating...' : 'Create thread'}
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
Reference in New Issue
Block a user