v2.1.0-provider-picker: BooCoder systemd migration + provider picker
- BooCoder moves from Docker to host systemd service (boocoder.service) - Agent dispatch (ACP + PTY) switches from SSH to direct spawn/exec - SSH helpers marked @deprecated (kept for one release cycle) - Provider registry (5 providers: boocode, opencode, goose, claude, qwen) - Agent probe with direct which/exec + model discovery (qwen settings, static claude models) - GET /api/providers route with installed status, models, transport fallback - ProviderPicker frontend component in CoderPane header - External provider messages route through tasks row instead of inference enqueue - Smart scroll: MessageList only auto-scrolls when near bottom (150px threshold) - DB: available_agents gets models, label, transport columns - Bug fix: loadContext SELECT includes allowed_read_paths - Bug fix: cap hit sentinel inserted before buildMessagesPayload - docker-compose.yml: boocoder service commented out, BOOCODER_URL env var added - CLAUDE.md: updated docs for systemd, provider registry, JSONB gotcha, loadContext
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { Code, Send, Check, X, RefreshCw } from 'lucide-react';
|
||||
import { MarkdownRenderer } from '@/components/MarkdownRenderer';
|
||||
import { ProviderPicker } from '@/components/ProviderPicker';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -300,6 +301,8 @@ export function CoderPane({ sessionId }: Props) {
|
||||
const { changes, loading, refresh, approve, reject } = usePendingChanges(sessionId);
|
||||
const [input, setInput] = useState('');
|
||||
const [sending, setSending] = useState(false);
|
||||
const [provider, setProvider] = useState('boocode');
|
||||
const [model, setModel] = useState('qwen3.6-35b-a3b-mxfp4');
|
||||
const messagesEndRef = useRef<HTMLDivElement>(null);
|
||||
const inputRef = useRef<HTMLTextAreaElement>(null);
|
||||
|
||||
@@ -331,7 +334,11 @@ export function CoderPane({ sessionId }: Props) {
|
||||
const res = await fetch(`/api/coder/sessions/${sessionId}/messages`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ content: text }),
|
||||
body: JSON.stringify({
|
||||
content: text,
|
||||
provider: provider !== 'boocode' ? provider : undefined,
|
||||
model: model || undefined,
|
||||
}),
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
@@ -347,7 +354,7 @@ export function CoderPane({ sessionId }: Props) {
|
||||
} finally {
|
||||
setSending(false);
|
||||
}
|
||||
}, [input, sending, sessionId, setMessages]);
|
||||
}, [input, sending, sessionId, provider, model, setMessages]);
|
||||
|
||||
const handleKeyDown = useCallback(
|
||||
(e: React.KeyboardEvent) => {
|
||||
@@ -363,11 +370,18 @@ export function CoderPane({ sessionId }: Props) {
|
||||
<div className="flex flex-col h-full bg-background">
|
||||
{/* Header */}
|
||||
<div className="flex items-center gap-2 px-3 py-1.5 border-b border-border bg-muted/30 shrink-0">
|
||||
<Code size={14} className="text-muted-foreground" />
|
||||
<span className="text-xs font-medium text-muted-foreground">BooCoder</span>
|
||||
<Code size={14} className="text-muted-foreground shrink-0" />
|
||||
<ProviderPicker
|
||||
provider={provider}
|
||||
model={model}
|
||||
onChange={(prov, mod) => {
|
||||
setProvider(prov);
|
||||
setModel(mod);
|
||||
}}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
'inline-block w-1.5 h-1.5 rounded-full ml-auto',
|
||||
'inline-block w-1.5 h-1.5 rounded-full ml-auto shrink-0',
|
||||
connected ? 'bg-green-500' : 'bg-red-500'
|
||||
)}
|
||||
title={connected ? 'Connected' : 'Disconnected'}
|
||||
|
||||
Reference in New Issue
Block a user