File size: 4,109 Bytes
e6312c4
c5a51e4
e6312c4
17d50b3
 
 
c5a51e4
 
 
 
 
 
e6312c4
 
 
c5a51e4
e6312c4
17d50b3
c5a51e4
 
 
 
 
17d50b3
c5a51e4
17d50b3
 
c5a51e4
17d50b3
 
c5a51e4
 
 
 
17d50b3
c5a51e4
17d50b3
c5a51e4
17d50b3
 
c5a51e4
 
17d50b3
c5a51e4
17d50b3
c5a51e4
 
 
17d50b3
 
c5a51e4
 
 
17d50b3
c5a51e4
 
 
 
 
 
 
 
 
17d50b3
c5a51e4
 
 
 
 
 
17d50b3
c5a51e4
 
 
 
 
 
 
e6312c4
c5a51e4
 
 
 
 
 
 
e6312c4
c5a51e4
 
17d50b3
 
 
 
c5a51e4
 
 
 
 
17d50b3
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import React, { useState, useEffect } from 'react';
import { FaRobot, FaSpinner, FaCheck, FaTimes } from 'react-icons/fa';
import axios from 'axios';

const AgentPanel: React.FC<{ projectPath: string }> = ({ projectPath }) => {
  const [prompt, setPrompt] = useState('');
  const [running, setRunning] = useState(false);
  const [taskId, setTaskId] = useState<string | null>(null);
  const [status, setStatus] = useState('');
  const [logs, setLogs] = useState<string[]>([]);
  const [pendingChanges, setPendingChanges] = useState<any[]>([]);
  const [selectedModel, setSelectedModel] = useState('groq-llama-3.3-70b-versatile');
  const [models, setModels] = useState<any[]>([]);

  useEffect(() => {
    axios.get('/api/models').then(res => setModels(res.data));
  }, []);

  const startTask = async () => {
    setRunning(true);
    setLogs([]);
    setPendingChanges([]);
    setStatus('running');
    try {
      const resp = await fetch('/api/agent/run', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ prompt, project_path: projectPath, model: selectedModel }),
      });
      const data = await resp.json();
      setTaskId(data.task_id);
      setStatus(data.status);
      setLogs(data.logs || []);
      setPendingChanges(data.pending_changes || []);
    } catch (e) {
      setLogs(['Error: ' + e]);
    }
    setRunning(false);
  };

  const approve = async () => {
    if (!taskId) return;
    try {
      const resp = await fetch(`/api/agent/approve/${taskId}`, { method: 'POST' });
      const data = await resp.json();
      setStatus(data.status);
      setLogs(prev => [...prev, ...(data.logs || [])]);
    } catch (e) {}
  };

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: 'var(--bg-primary)', padding: 12 }}>
      <div style={{ fontWeight: 600, marginBottom: 8 }}><FaRobot /> AI Agent</div>

      <select
        style={{ width: '100%', background: 'var(--bg-tertiary)', color: 'var(--text-primary)', border: '1px solid var(--border)', padding: '6px 8px', borderRadius: 6, marginBottom: 8 }}
        value={selectedModel}
        onChange={e => setSelectedModel(e.target.value)}
      >
        {models.map((m: any) => (
          <option key={m.id} value={m.id}>{m.name}</option>
        ))}
      </select>

      <textarea
        style={{ width: '100%', height: 60, background: 'var(--bg-tertiary)', color: 'var(--text-primary)', border: '1px solid var(--border)', borderRadius: 6, padding: 8, resize: 'vertical', marginBottom: 8 }}
        value={prompt}
        onChange={e => setPrompt(e.target.value)}
        placeholder="E.g., 'Create a REST API for todos with a database'"
      />

      <button
        onClick={startTask}
        disabled={running}
        style={{ background: 'var(--accent)', color: 'white', padding: '6px 16px', borderRadius: 6, fontWeight: 600, marginBottom: 8 }}
      >
        {running ? <><FaSpinner className="spin" /> Running...</> : 'Start Agent'}
      </button>

      {status === 'awaiting_approval' && pendingChanges.length > 0 && (
        <div style={{ marginBottom: 8 }}>
          <div style={{ color: 'var(--warning)', marginBottom: 4 }}>Pending changes ({pendingChanges.length}):</div>
          {pendingChanges.map((c, i) => (
            <div key={i} style={{ fontSize: 12, marginBottom: 2 }}>
              {c.action} – {c.filepath}
            </div>
          ))}
          <button onClick={approve} style={{ background: 'var(--success)', color: 'white', padding: '4px 12px', borderRadius: 4 }}>
            <FaCheck /> Approve & Apply All
          </button>
        </div>
      )}

      <div style={{ flex: 1, overflowY: 'auto', fontFamily: 'monospace', fontSize: 12, whiteSpace: 'pre-wrap', background: 'var(--bg-secondary)', padding: 8, borderRadius: 4 }}>
        {logs.map((log, i) => (
          <div key={i}>• {log}</div>
        ))}
        {status === 'completed' && <div style={{ color: 'var(--success)' }}>✅ Task completed.</div>}
      </div>
    </div>
  );
};

export default AgentPanel;