@benzsiangco/jarvis 1.0.0 → 1.1.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.
- package/README.md +5 -0
- package/bin/{jarvis.js → jarvis} +1 -1
- package/dist/cli.js +476 -350
- package/dist/electron/main.js +160 -0
- package/dist/electron/preload.js +19 -0
- package/package.json +21 -8
- package/skills.md +147 -0
- package/src/agents/index.ts +248 -0
- package/src/brain/loader.ts +136 -0
- package/src/cli.ts +411 -0
- package/src/config/index.ts +363 -0
- package/src/core/executor.ts +222 -0
- package/src/core/plugins.ts +148 -0
- package/src/core/types.ts +217 -0
- package/src/electron/main.ts +192 -0
- package/src/electron/preload.ts +25 -0
- package/src/electron/types.d.ts +20 -0
- package/src/index.ts +12 -0
- package/src/providers/antigravity-loader.ts +233 -0
- package/src/providers/antigravity.ts +585 -0
- package/src/providers/index.ts +523 -0
- package/src/sessions/index.ts +194 -0
- package/src/tools/index.ts +436 -0
- package/src/tui/index.tsx +784 -0
- package/src/utils/auth-prompt.ts +394 -0
- package/src/utils/index.ts +180 -0
- package/src/utils/native-picker.ts +71 -0
- package/src/utils/skills.ts +99 -0
- package/src/utils/table-integration-examples.ts +617 -0
- package/src/utils/table-utils.ts +401 -0
- package/src/web/build-ui.ts +27 -0
- package/src/web/server.ts +674 -0
- package/src/web/ui/dist/.gitkeep +0 -0
- package/src/web/ui/dist/main.css +1 -0
- package/src/web/ui/dist/main.js +320 -0
- package/src/web/ui/dist/main.js.map +20 -0
- package/src/web/ui/index.html +46 -0
- package/src/web/ui/src/App.tsx +143 -0
- package/src/web/ui/src/Modules/Safety/GuardianModal.tsx +83 -0
- package/src/web/ui/src/components/Layout/ContextPanel.tsx +243 -0
- package/src/web/ui/src/components/Layout/Header.tsx +91 -0
- package/src/web/ui/src/components/Layout/ModelSelector.tsx +235 -0
- package/src/web/ui/src/components/Layout/SessionStats.tsx +369 -0
- package/src/web/ui/src/components/Layout/Sidebar.tsx +895 -0
- package/src/web/ui/src/components/Modules/Chat/ChatStage.tsx +620 -0
- package/src/web/ui/src/components/Modules/Chat/MessageItem.tsx +446 -0
- package/src/web/ui/src/components/Modules/Editor/CommandInspector.tsx +71 -0
- package/src/web/ui/src/components/Modules/Editor/DiffViewer.tsx +83 -0
- package/src/web/ui/src/components/Modules/Terminal/TabbedTerminal.tsx +202 -0
- package/src/web/ui/src/components/Settings/SettingsModal.tsx +935 -0
- package/src/web/ui/src/config/models.ts +70 -0
- package/src/web/ui/src/main.tsx +13 -0
- package/src/web/ui/src/store/agentStore.ts +41 -0
- package/src/web/ui/src/store/uiStore.ts +64 -0
- package/src/web/ui/src/types/index.ts +54 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
|
2
|
+
import { Terminal as XTerm } from 'xterm';
|
|
3
|
+
import { FitAddon } from 'xterm-addon-fit';
|
|
4
|
+
import { X, Plus, Terminal as LucideTerminal, Trash2, Skull, MousePointer2 } from 'lucide-react';
|
|
5
|
+
import 'xterm/css/xterm.css';
|
|
6
|
+
|
|
7
|
+
interface TerminalTab {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function TabbedTerminal({ onClose }: { onClose: () => void }) {
|
|
13
|
+
const [tabs, setTabs] = useState<TerminalTab[]>([{ id: 'default', name: 'Primary Core' }]);
|
|
14
|
+
const [activeTabId, setActiveTabId] = useState('default');
|
|
15
|
+
const instanceRefs = useRef<Record<string, { kill: () => void, clear: () => void }>>({});
|
|
16
|
+
|
|
17
|
+
const addTab = () => {
|
|
18
|
+
const id = 'term-' + Date.now();
|
|
19
|
+
setTabs(prev => [...prev, { id, name: `Segment ${prev.length + 1}` }]);
|
|
20
|
+
setActiveTabId(id);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const closeTab = (id: string) => {
|
|
24
|
+
if (tabs.length === 1) {
|
|
25
|
+
onClose();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const updatedTabs = tabs.filter(t => t.id !== id);
|
|
29
|
+
setTabs(updatedTabs);
|
|
30
|
+
if (activeTabId === id) setActiveTabId(updatedTabs[0]?.id || 'default');
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const handleKill = () => {
|
|
34
|
+
instanceRefs.current[activeTabId]?.kill();
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleClear = () => {
|
|
38
|
+
instanceRefs.current[activeTabId]?.clear();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<div className="flex flex-col h-full bg-zinc-950 border-t border-zinc-900 shadow-2xl overflow-hidden relative">
|
|
43
|
+
<div className="h-10 bg-zinc-900/80 backdrop-blur-md flex items-center justify-between px-4 border-b border-zinc-800 shrink-0">
|
|
44
|
+
<div className="flex items-center gap-0.5 h-full overflow-x-auto no-scrollbar">
|
|
45
|
+
{tabs.map(tab => (
|
|
46
|
+
<button
|
|
47
|
+
key={tab.id}
|
|
48
|
+
onClick={() => setActiveTabId(tab.id)}
|
|
49
|
+
className={`px-5 h-full flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest border-r border-zinc-800/50 transition-all ${
|
|
50
|
+
activeTabId === tab.id ? 'bg-zinc-950 text-white border-b-2 border-b-cyan-500 shadow-inner' : 'text-zinc-500 hover:bg-zinc-800/50 hover:text-zinc-400'
|
|
51
|
+
}`}
|
|
52
|
+
>
|
|
53
|
+
<LucideTerminal size={12} className={activeTabId === tab.id ? "text-cyan-500" : "text-zinc-700"} />
|
|
54
|
+
<span>{tab.name}</span>
|
|
55
|
+
<X
|
|
56
|
+
size={10}
|
|
57
|
+
className="ml-3 opacity-0 group-hover:opacity-100 hover:text-red-500 transition-all"
|
|
58
|
+
onClick={(e) => { e.stopPropagation(); closeTab(tab.id); }}
|
|
59
|
+
/>
|
|
60
|
+
</button>
|
|
61
|
+
))}
|
|
62
|
+
<button onClick={addTab} className="px-4 h-full text-zinc-600 hover:text-white hover:bg-zinc-800 transition-colors border-r border-zinc-800/50">
|
|
63
|
+
<Plus size={16} />
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
<div className="flex items-center gap-4">
|
|
68
|
+
<button
|
|
69
|
+
onClick={handleKill}
|
|
70
|
+
className="flex items-center gap-1.5 px-3 py-1 rounded-md bg-red-500/10 text-red-500 hover:bg-red-500/20 transition-all text-[9px] font-black uppercase tracking-widest border border-red-500/20 active:scale-95"
|
|
71
|
+
>
|
|
72
|
+
<Skull size={10} /> Abort Task
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
onClick={handleClear}
|
|
76
|
+
className="text-[9px] font-black uppercase tracking-widest text-zinc-600 hover:text-zinc-300 transition-colors italic"
|
|
77
|
+
>
|
|
78
|
+
Wipe Buffer
|
|
79
|
+
</button>
|
|
80
|
+
<div className="w-px h-4 bg-zinc-800 mx-2" />
|
|
81
|
+
<button onClick={onClose} className="p-1 text-zinc-600 hover:text-white transition-all active:scale-90"><X size={18} /></button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
<div className="flex-1 relative overflow-hidden bg-zinc-950/50 backdrop-blur-sm">
|
|
86
|
+
{tabs.map(tab => (
|
|
87
|
+
<TerminalInstance
|
|
88
|
+
key={tab.id}
|
|
89
|
+
visible={activeTabId === tab.id}
|
|
90
|
+
onRegister={(actions) => { instanceRefs.current[tab.id] = actions; }}
|
|
91
|
+
/>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function TerminalInstance({ visible, onRegister }: { visible: boolean, onRegister: (actions: { kill: () => void, clear: () => void }) => void }) {
|
|
99
|
+
const terminalRef = useRef<HTMLDivElement>(null);
|
|
100
|
+
const xtermRef = useRef<XTerm | null>(null);
|
|
101
|
+
const socketRef = useRef<WebSocket | null>(null);
|
|
102
|
+
const fitAddonRef = useRef<FitAddon | null>(null);
|
|
103
|
+
|
|
104
|
+
const kill = useCallback(() => {
|
|
105
|
+
if (socketRef.current?.readyState === WebSocket.OPEN) {
|
|
106
|
+
socketRef.current.send(JSON.stringify({ action: 'kill' }));
|
|
107
|
+
}
|
|
108
|
+
}, []);
|
|
109
|
+
|
|
110
|
+
const clear = useCallback(() => {
|
|
111
|
+
xtermRef.current?.clear();
|
|
112
|
+
xtermRef.current?.focus();
|
|
113
|
+
}, []);
|
|
114
|
+
|
|
115
|
+
useEffect(() => {
|
|
116
|
+
onRegister({ kill, clear });
|
|
117
|
+
}, [kill, clear, onRegister]);
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
if (!terminalRef.current || xtermRef.current) return;
|
|
121
|
+
|
|
122
|
+
const term = new XTerm({
|
|
123
|
+
theme: { background: 'transparent', foreground: '#d4d4d8', cursor: '#06b6d4' },
|
|
124
|
+
fontFamily: '"JetBrains Mono", "Cascadia Code", monospace',
|
|
125
|
+
fontSize: 13,
|
|
126
|
+
cursorBlink: true,
|
|
127
|
+
allowTransparency: true,
|
|
128
|
+
lineHeight: 1.4,
|
|
129
|
+
scrollback: 5000
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const fitAddon = new FitAddon();
|
|
133
|
+
term.loadAddon(fitAddon);
|
|
134
|
+
term.open(terminalRef.current);
|
|
135
|
+
|
|
136
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
137
|
+
const socket = new WebSocket(`${protocol}//${window.location.host}/terminal`);
|
|
138
|
+
socket.binaryType = 'arraybuffer';
|
|
139
|
+
|
|
140
|
+
socket.onopen = () => {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
if (term && socket.readyState === WebSocket.OPEN) {
|
|
143
|
+
socket.send(JSON.stringify({ resize: { cols: term.cols, rows: term.rows } }));
|
|
144
|
+
}
|
|
145
|
+
}, 500);
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
socket.onmessage = (ev) => {
|
|
149
|
+
if (typeof ev.data === 'string') term.write(ev.data);
|
|
150
|
+
else term.write(new Uint8Array(ev.data));
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
term.onData(data => {
|
|
154
|
+
if (socket.readyState === WebSocket.OPEN) socket.send(data);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
term.onResize(size => {
|
|
158
|
+
if (socket.readyState === WebSocket.OPEN) {
|
|
159
|
+
socket.send(JSON.stringify({ resize: { cols: size.cols, rows: size.rows } }));
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
xtermRef.current = term;
|
|
164
|
+
socketRef.current = socket;
|
|
165
|
+
fitAddonRef.current = fitAddon;
|
|
166
|
+
|
|
167
|
+
const onResize = () => {
|
|
168
|
+
try { fitAddon.fit(); } catch(e) {}
|
|
169
|
+
};
|
|
170
|
+
window.addEventListener('resize', onResize);
|
|
171
|
+
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
onResize();
|
|
174
|
+
if (visible) term.focus();
|
|
175
|
+
}, 200);
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
window.removeEventListener('resize', onResize);
|
|
179
|
+
term.dispose();
|
|
180
|
+
socket.close();
|
|
181
|
+
xtermRef.current = null;
|
|
182
|
+
};
|
|
183
|
+
}, []);
|
|
184
|
+
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (visible && xtermRef.current) {
|
|
187
|
+
xtermRef.current.focus();
|
|
188
|
+
setTimeout(() => fitAddonRef.current?.fit(), 100);
|
|
189
|
+
}
|
|
190
|
+
}, [visible]);
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div
|
|
194
|
+
ref={terminalRef}
|
|
195
|
+
className={`absolute inset-0 p-4 transition-opacity duration-300 cursor-text ${visible ? 'opacity-100 z-10' : 'opacity-0 -z-10 pointer-events-none'}`}
|
|
196
|
+
onMouseDown={(e) => {
|
|
197
|
+
e.stopPropagation();
|
|
198
|
+
xtermRef.current?.focus();
|
|
199
|
+
}}
|
|
200
|
+
/>
|
|
201
|
+
);
|
|
202
|
+
}
|