@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.
Files changed (55) hide show
  1. package/README.md +5 -0
  2. package/bin/{jarvis.js → jarvis} +1 -1
  3. package/dist/cli.js +476 -350
  4. package/dist/electron/main.js +160 -0
  5. package/dist/electron/preload.js +19 -0
  6. package/package.json +21 -8
  7. package/skills.md +147 -0
  8. package/src/agents/index.ts +248 -0
  9. package/src/brain/loader.ts +136 -0
  10. package/src/cli.ts +411 -0
  11. package/src/config/index.ts +363 -0
  12. package/src/core/executor.ts +222 -0
  13. package/src/core/plugins.ts +148 -0
  14. package/src/core/types.ts +217 -0
  15. package/src/electron/main.ts +192 -0
  16. package/src/electron/preload.ts +25 -0
  17. package/src/electron/types.d.ts +20 -0
  18. package/src/index.ts +12 -0
  19. package/src/providers/antigravity-loader.ts +233 -0
  20. package/src/providers/antigravity.ts +585 -0
  21. package/src/providers/index.ts +523 -0
  22. package/src/sessions/index.ts +194 -0
  23. package/src/tools/index.ts +436 -0
  24. package/src/tui/index.tsx +784 -0
  25. package/src/utils/auth-prompt.ts +394 -0
  26. package/src/utils/index.ts +180 -0
  27. package/src/utils/native-picker.ts +71 -0
  28. package/src/utils/skills.ts +99 -0
  29. package/src/utils/table-integration-examples.ts +617 -0
  30. package/src/utils/table-utils.ts +401 -0
  31. package/src/web/build-ui.ts +27 -0
  32. package/src/web/server.ts +674 -0
  33. package/src/web/ui/dist/.gitkeep +0 -0
  34. package/src/web/ui/dist/main.css +1 -0
  35. package/src/web/ui/dist/main.js +320 -0
  36. package/src/web/ui/dist/main.js.map +20 -0
  37. package/src/web/ui/index.html +46 -0
  38. package/src/web/ui/src/App.tsx +143 -0
  39. package/src/web/ui/src/Modules/Safety/GuardianModal.tsx +83 -0
  40. package/src/web/ui/src/components/Layout/ContextPanel.tsx +243 -0
  41. package/src/web/ui/src/components/Layout/Header.tsx +91 -0
  42. package/src/web/ui/src/components/Layout/ModelSelector.tsx +235 -0
  43. package/src/web/ui/src/components/Layout/SessionStats.tsx +369 -0
  44. package/src/web/ui/src/components/Layout/Sidebar.tsx +895 -0
  45. package/src/web/ui/src/components/Modules/Chat/ChatStage.tsx +620 -0
  46. package/src/web/ui/src/components/Modules/Chat/MessageItem.tsx +446 -0
  47. package/src/web/ui/src/components/Modules/Editor/CommandInspector.tsx +71 -0
  48. package/src/web/ui/src/components/Modules/Editor/DiffViewer.tsx +83 -0
  49. package/src/web/ui/src/components/Modules/Terminal/TabbedTerminal.tsx +202 -0
  50. package/src/web/ui/src/components/Settings/SettingsModal.tsx +935 -0
  51. package/src/web/ui/src/config/models.ts +70 -0
  52. package/src/web/ui/src/main.tsx +13 -0
  53. package/src/web/ui/src/store/agentStore.ts +41 -0
  54. package/src/web/ui/src/store/uiStore.ts +64 -0
  55. 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
+ }