openaxies 0.6.0 → 1.0.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/openaxies-debug.log +6 -0
- package/package.json +1 -1
- package/src/App.js +153 -396
- package/src/components/timeline/ActivityItem.js +46 -0
- package/src/components/timeline/TodoTracker.js +23 -0
- package/src/components/ui/Separator.js +10 -0
- package/src/components/ui/Typography.js +15 -0
- package/src/providers/index.js +0 -3
- package/src/providers/streaming.js +2 -2
- package/src/components/BrandHeader.js +0 -36
- package/src/components/ComposerDock.js +0 -66
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
The above error occurred in the <AppRoot> component:
|
|
2
|
+
|
|
3
|
+
at AppRoot (file:///storage/emulated/0/agent-open/openaxis/src/App.js:60:22)
|
|
4
|
+
at App (file:///storage/emulated/0/agent-open/openaxis/node_modules/ink/build/components/App.js:17:16)
|
|
5
|
+
|
|
6
|
+
React will try to recreate this component tree from scratch using the error boundary you provided, InternalApp.
|
package/package.json
CHANGED
package/src/App.js
CHANGED
|
@@ -1,434 +1,191 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { Box, Text,
|
|
2
|
+
import { Box, Text, useStdout } from 'ink';
|
|
3
|
+
import TextInput from 'ink-text-input';
|
|
3
4
|
import { hex } from './config/theme.js';
|
|
4
|
-
|
|
5
5
|
import { getModels, getModelById, DEFAULT_MODEL_ID } from './config/models.js';
|
|
6
6
|
import { callModel } from './providers/index.js';
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
7
|
+
import { Typography } from './components/ui/Typography.js';
|
|
8
|
+
import { Separator } from './components/ui/Separator.js';
|
|
9
|
+
import { ActivityItem } from './components/timeline/ActivityItem.js';
|
|
10
|
+
import { TodoTracker } from './components/timeline/TodoTracker.js';
|
|
9
11
|
|
|
10
12
|
const h = React.createElement;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (m === null || m === undefined || typeof m !== 'object') continue;
|
|
19
|
-
if (m.role === 'user' || m.role === 'assistant') {
|
|
20
|
-
const c = typeof m.content === 'string' ? m.content : '';
|
|
21
|
-
h.push({ role: m.role, content: c });
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (typeof newText === 'string' && newText.length > 0) {
|
|
25
|
-
h.push({ role: 'user', content: newText });
|
|
26
|
-
}
|
|
27
|
-
return h;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function fmtTime(s) {
|
|
31
|
-
if (typeof s !== 'number' || s < 0) return '0.0s';
|
|
32
|
-
return s.toFixed(1) + 's';
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function hr(cols) {
|
|
36
|
-
const n = typeof cols === 'number' && cols > 0 ? cols : 80;
|
|
37
|
-
let s = '';
|
|
38
|
-
for (let i = 0; i < n; i++) s = s + '\u2500';
|
|
39
|
-
return h(Text, { color: '#1A1A28' }, s);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const SPINNER = ['\u25CB', '\u25D4', '\u25D0', '\u25D5', '\u25CF', '\u25D5', '\u25D0', '\u25D4', '\u25D1', '\u25D2', '\u25D3'];
|
|
43
|
-
|
|
44
|
-
const COMMANDS = [
|
|
45
|
-
{ id: 'model', trigger: '/model', desc: 'Switch model' },
|
|
46
|
-
{ id: 'thoughts', trigger: '/thoughts', desc: 'Toggle thinking visibility' },
|
|
47
|
-
{ id: 'resume', trigger: '/resume', desc: 'Re-run last query' },
|
|
48
|
-
{ id: 'clear', trigger: '/clear', desc: 'Clear conversation' },
|
|
49
|
-
{ id: 'help', trigger: '/help', desc: 'Show commands' },
|
|
50
|
-
{ id: 'exit', trigger: '/exit', desc: 'Quit' },
|
|
14
|
+
const STARTUP_LOGO = [
|
|
15
|
+
' \u2588\u2588\u2588\u2588\u2588\u2588\u2557',
|
|
16
|
+
' \u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557',
|
|
17
|
+
' \u2588\u2588\u2551 \u2588\u2588\u2551',
|
|
18
|
+
' \u2588\u2588\u2551 \u2588\u2588\u2551',
|
|
19
|
+
' \u255a\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255d',
|
|
51
20
|
];
|
|
52
21
|
|
|
53
|
-
function AppRoot() {
|
|
22
|
+
export default function AppRoot() {
|
|
54
23
|
const { stdout } = useStdout();
|
|
55
24
|
const rows = stdout.rows || 24;
|
|
56
25
|
const cols = stdout.columns || 80;
|
|
57
26
|
|
|
58
27
|
const [messages, setMessages] = React.useState([]);
|
|
59
28
|
const [activeModel, setActiveModel] = React.useState(DEFAULT_MODEL_ID);
|
|
60
|
-
const [
|
|
29
|
+
const [inputBuffer, setInputBuffer] = React.useState('');
|
|
61
30
|
const [streamingActive, setStreamingActive] = React.useState(false);
|
|
62
|
-
const [
|
|
63
|
-
const [
|
|
31
|
+
const [streamContent, setStreamContent] = React.useState('');
|
|
32
|
+
const [thoughtContent, setThoughtContent] = React.useState('');
|
|
64
33
|
const [thinkingElapsed, setThinkingElapsed] = React.useState(0);
|
|
65
|
-
const [
|
|
66
|
-
const [
|
|
67
|
-
const [
|
|
68
|
-
const [toolInfo, setToolInfo] = React.useState(null);
|
|
69
|
-
const [showThoughts, setShowThoughts] = React.useState(true);
|
|
70
|
-
|
|
71
|
-
const STATUS_H = 3;
|
|
72
|
-
const DOCK_H = 1;
|
|
73
|
-
const fixedH = BRAND_HEIGHT + STATUS_H + DOCK_H;
|
|
74
|
-
const availLines = Math.max(10, rows - fixedH);
|
|
34
|
+
const [isThinking, setIsThinking] = React.useState(false);
|
|
35
|
+
const [startupDone, setStartupDone] = React.useState(false);
|
|
36
|
+
const [todos, setTodos] = React.useState([]);
|
|
75
37
|
|
|
76
|
-
const
|
|
77
|
-
const connRef = React.useRef(null);
|
|
78
|
-
const spinnerRef = React.useRef(null);
|
|
79
|
-
const timerRef = React.useRef(null);
|
|
80
|
-
const thinkStartRef = React.useRef(null);
|
|
81
|
-
const lastQueryRef = React.useRef('');
|
|
38
|
+
const abortControllerRef = React.useRef(null);
|
|
82
39
|
|
|
83
|
-
React.useEffect(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
timerRef.current = ti;
|
|
92
|
-
return function () {
|
|
93
|
-
clearInterval(si); clearInterval(ti);
|
|
94
|
-
};
|
|
40
|
+
React.useEffect(() => {
|
|
41
|
+
let timer;
|
|
42
|
+
if (isThinking) {
|
|
43
|
+
timer = setInterval(() => {
|
|
44
|
+
setThinkingElapsed(prev => prev + 0.1);
|
|
45
|
+
}, 100);
|
|
46
|
+
}
|
|
47
|
+
return () => clearInterval(timer);
|
|
95
48
|
}, [isThinking]);
|
|
96
49
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
setActiveModel(function (prev) {
|
|
100
|
-
let found = false;
|
|
101
|
-
for (let i = 0; i < models.length; i++) {
|
|
102
|
-
if (found) return models[i].id;
|
|
103
|
-
if (models[i].id === prev) found = true;
|
|
104
|
-
}
|
|
105
|
-
return models[0].id;
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function checkThink(text) {
|
|
110
|
-
const o = text.lastIndexOf('<think>');
|
|
111
|
-
const c = text.lastIndexOf('</think>');
|
|
112
|
-
if (o === -1 && c === -1) return false;
|
|
113
|
-
return o > c;
|
|
114
|
-
}
|
|
50
|
+
const model = getModelById(activeModel);
|
|
51
|
+
const modelLabel = model ? model.label : 'Unknown Model';
|
|
115
52
|
|
|
116
53
|
async function handleSubmit(text) {
|
|
117
|
-
|
|
118
|
-
if (
|
|
119
|
-
|
|
54
|
+
if (!text || streamingActive) return;
|
|
55
|
+
if (!startupDone) setStartupDone(true);
|
|
56
|
+
|
|
57
|
+
const userMsg = { role: 'user', content: text };
|
|
58
|
+
setMessages(prev => [...prev, userMsg]);
|
|
59
|
+
setInputBuffer('');
|
|
120
60
|
setStreamingActive(true);
|
|
121
|
-
|
|
122
|
-
|
|
61
|
+
setStreamContent('');
|
|
62
|
+
setThoughtContent('');
|
|
123
63
|
setThinkingElapsed(0);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if (p === ' connecting.') return ' connecting..';
|
|
128
|
-
if (p === ' connecting..') return ' connecting...';
|
|
129
|
-
return ' connecting';
|
|
130
|
-
});
|
|
131
|
-
}, 400);
|
|
132
|
-
setMessages(function (p) { return p.concat([{ role: 'user', content: safe, id: 'u-' + Date.now() }]); });
|
|
133
|
-
setInputBuffer('');
|
|
134
|
-
const modelInfo = getModelById(activeModel);
|
|
135
|
-
if (modelInfo === null) {
|
|
136
|
-
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
137
|
-
setStreamingActive(false); setStreamText('');
|
|
138
|
-
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'No model selected.', id: 'e-' + Date.now() }]); });
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const history = buildHistory(messages, safe);
|
|
142
|
-
const abortController = new AbortController();
|
|
143
|
-
abortRef.current = abortController;
|
|
64
|
+
|
|
65
|
+
abortControllerRef.current = new AbortController();
|
|
66
|
+
|
|
144
67
|
try {
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
setStreamText(accumulated); break;
|
|
68
|
+
const history = [...messages, userMsg];
|
|
69
|
+
const stream = callModel({ id: activeModel }, history, abortControllerRef.current.signal);
|
|
70
|
+
|
|
71
|
+
for await (const event of stream) {
|
|
72
|
+
if (event.type === 'tool') {
|
|
73
|
+
setMessages(prev => [...prev, { type: 'tool', tool: event.tool, query: event.query }]);
|
|
74
|
+
} else if (event.type === 'token') {
|
|
75
|
+
const content = event.content;
|
|
76
|
+
if (content.includes('<think>')) {
|
|
77
|
+
setIsThinking(true);
|
|
78
|
+
setThoughtContent(prev => prev + content.replace('<think>', ''));
|
|
79
|
+
} else if (content.includes('</think>')) {
|
|
80
|
+
setIsThinking(false);
|
|
81
|
+
setThoughtContent(prev => prev + content.replace('</think>', ''));
|
|
82
|
+
setMessages(prev => [...prev, { type: 'thought', content: thoughtContent, elapsed: thinkingElapsed.toFixed(1) + 's' }]);
|
|
83
|
+
} else if (isThinking) {
|
|
84
|
+
setThoughtContent(prev => prev + content);
|
|
85
|
+
} else {
|
|
86
|
+
setStreamContent(prev => prev + content);
|
|
87
|
+
}
|
|
166
88
|
}
|
|
167
89
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
90
|
+
} catch (e) {
|
|
91
|
+
setMessages(prev => [...prev, { type: 'assistant', content: 'Error connecting to model.' }]);
|
|
92
|
+
} finally {
|
|
93
|
+
setStreamingActive(false);
|
|
94
|
+
setIsThinking(false);
|
|
95
|
+
if (streamContent) {
|
|
96
|
+
setMessages(prev => [...prev, { type: 'assistant', content: streamContent }]);
|
|
173
97
|
}
|
|
174
|
-
} catch (err) {
|
|
175
|
-
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
176
|
-
setIsThinking(false); thinkStartRef.current = null; setThinkingElapsed(0); setStreamingActive(false); setStreamText('');
|
|
177
|
-
const errMsg = err !== null && err !== undefined ? (err.message || String(err)) : 'Unknown error';
|
|
178
|
-
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'Error: ' + errMsg, id: 'er-' + Date.now() }]); });
|
|
179
98
|
}
|
|
180
99
|
}
|
|
181
100
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
101
|
+
const renderHeader = () => {
|
|
102
|
+
if (!startupDone) return null;
|
|
103
|
+
return h(Box, { height: 1, paddingLeft: 1, paddingRight: 1 },
|
|
104
|
+
h(Typography.Header, 'OpenAxies'),
|
|
105
|
+
h(Typography.Muted, ` \u2022 ${modelLabel}`),
|
|
106
|
+
h(Box, { flexGrow: 1 }),
|
|
107
|
+
h(Typography.Dim, 'local')
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const renderFooter = () => {
|
|
112
|
+
return h(Box, {
|
|
113
|
+
height: 1,
|
|
114
|
+
paddingLeft: 1,
|
|
115
|
+
paddingRight: 1,
|
|
116
|
+
flexDirection: 'row',
|
|
117
|
+
alignItems: 'center'
|
|
118
|
+
},
|
|
119
|
+
h(Typography.Dim, 'esc interrupt \u2022 ctrl+t thoughts \u2022 ctrl+p models \u2022 ctrl+k commands'),
|
|
120
|
+
h(Box, { flexGrow: 1 }),
|
|
121
|
+
h(Typography.Muted, `${modelLabel.toLowerCase()} \u2022 local`)
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const renderStartup = () => {
|
|
126
|
+
return h(Box, {
|
|
127
|
+
flexDirection: 'column',
|
|
128
|
+
paddingLeft: 1,
|
|
129
|
+
paddingTop: 2,
|
|
130
|
+
backgroundColor: '#000000'
|
|
131
|
+
},
|
|
132
|
+
...STARTUP_LOGO.map(line => h(Typography.Accent, line)),
|
|
133
|
+
h(Box, { height: 1 }),
|
|
134
|
+
h(Typography.Header, 'OpenAxies'),
|
|
135
|
+
h(Typography.Muted, 'local ai agent runtime'),
|
|
136
|
+
h(Box, { height: 2 }),
|
|
137
|
+
h(Typography.Dim, 'Press Enter to begin')
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const renderTimeline = () => {
|
|
142
|
+
const events = [...messages];
|
|
143
|
+
if (isThinking) {
|
|
144
|
+
events.push({ type: 'thought', content: thoughtContent, elapsed: thinkingElapsed.toFixed(1) + 's' });
|
|
199
145
|
}
|
|
200
|
-
if (
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
useInput(function handleInput(input, key) {
|
|
205
|
-
if (showOverlay === true) {
|
|
206
|
-
if (key.escape === true) { setShowOverlay(false); setInputBuffer(''); setOverlayIndex(0); return; }
|
|
207
|
-
if (key.upArrow === true) { setOverlayIndex(function (p) { return p > 0 ? p - 1 : COMMANDS.length - 1; }); return; }
|
|
208
|
-
if (key.downArrow === true) { setOverlayIndex(function (p) { return p < COMMANDS.length - 1 ? p + 1 : 0; }); return; }
|
|
209
|
-
if (key.return === true) { const cmd = COMMANDS[overlayIndex]; if (cmd !== null && cmd !== undefined) handleCmd(cmd); return; }
|
|
210
|
-
return;
|
|
146
|
+
if (streamingActive && streamContent) {
|
|
147
|
+
events.push({ type: 'assistant', content: streamContent });
|
|
211
148
|
}
|
|
212
|
-
if (key.escape === true) {
|
|
213
|
-
if (streamingActive === true) {
|
|
214
|
-
if (abortRef.current !== null) { try { abortRef.current.abort(); } catch (_) {} abortRef.current = null; }
|
|
215
|
-
setStreamingActive(false); setStreamText(''); setIsThinking(false);
|
|
216
|
-
}
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
if (key.return === true) {
|
|
220
|
-
const s = typeof inputBuffer === 'string' ? inputBuffer : '';
|
|
221
|
-
if (s.length > 0 && streamingActive === false) handleSubmit(s);
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
if (key.ctrl === true) {
|
|
225
|
-
if (input === 'c' || input === 'C') {
|
|
226
|
-
if (abortRef.current !== null) { try { abortRef.current.abort(); } catch (_) {} abortRef.current = null; }
|
|
227
|
-
setStreamingActive(false); setStreamText(''); setIsThinking(false);
|
|
228
|
-
process.exit(0);
|
|
229
|
-
}
|
|
230
|
-
if (input === 't' || input === 'T') { setShowThoughts(function (p) { return !p; }); return; }
|
|
231
|
-
if (input === 'p' || input === 'P') { cycleModel(); return; }
|
|
232
|
-
if (input === 'r' || input === 'R') {
|
|
233
|
-
const q = lastQueryRef.current;
|
|
234
|
-
if (q.length > 0 && streamingActive === false) handleSubmit(q);
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (input === 'l' || input === 'L') { setMessages([]); return; }
|
|
238
|
-
if (input === 'k' || input === 'K') { setInputBuffer('/'); setShowOverlay(true); setOverlayIndex(0); return; }
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
function filterText(t) {
|
|
243
|
-
if (showThoughts === true) return t;
|
|
244
|
-
return t.split('<think>').join('').split('</think>').join('');
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const activeInfo = getModelById(activeModel);
|
|
248
|
-
const activeLabel = activeInfo !== null ? activeInfo.label : 'OpenAxies';
|
|
249
|
-
|
|
250
|
-
// Build sections
|
|
251
|
-
const sections = [];
|
|
252
|
-
|
|
253
|
-
// Show overlay items
|
|
254
|
-
if (showOverlay === true) {
|
|
255
|
-
sections.push({ t: 'sep' });
|
|
256
|
-
sections.push({ t: 'header_bright', text: 'Commands' });
|
|
257
|
-
for (let i = 0; i < COMMANDS.length; i++) {
|
|
258
|
-
const c = COMMANDS[i];
|
|
259
|
-
const sel = i === overlayIndex;
|
|
260
|
-
sections.push({ t: 'cmd', trigger: c.trigger, desc: c.desc, selected: sel });
|
|
261
|
-
}
|
|
262
|
-
sections.push({ t: 'sep' });
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
// Conversation history
|
|
266
|
-
for (let i = 0; i < messages.length; i++) {
|
|
267
|
-
const msg = messages[i];
|
|
268
|
-
if (msg === null || typeof msg !== 'object') continue;
|
|
269
|
-
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
270
|
-
if (content.length === 0) continue;
|
|
271
|
-
|
|
272
|
-
if (msg.role === 'user') {
|
|
273
|
-
sections.push({ t: 'sep' });
|
|
274
|
-
sections.push({ t: 'user', text: content });
|
|
275
|
-
} else {
|
|
276
|
-
if (sections.length > 0 && sections[sections.length - 1].t !== 'sep') sections.push({ t: 'sep' });
|
|
277
|
-
const display = filterText(content);
|
|
278
|
-
sections.push({ t: 'text', text: display });
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Streaming section
|
|
283
|
-
const hasStream = typeof streamText === 'string' && streamText.length > 0;
|
|
284
|
-
if (hasStream === true) {
|
|
285
|
-
const clean = filterText(streamText);
|
|
286
|
-
|
|
287
|
-
if (toolInfo !== null && toolInfo.used === true && toolInfo.query.length > 0) {
|
|
288
|
-
sections.push({ t: 'tool', tool: toolInfo.tool, query: toolInfo.query, sites: toolInfo.sites });
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
if (isThinking === true) {
|
|
292
|
-
sections.push({ t: 'thinking', spin: SPINNER[spinnerIdx], elapsed: fmtTime(thinkingElapsed), content: clean });
|
|
293
|
-
} else {
|
|
294
|
-
sections.push({ t: 'stream', text: clean });
|
|
295
|
-
}
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Idle ready state
|
|
299
|
-
if (sections.length === 0) {
|
|
300
|
-
sections.push({ t: 'sep' });
|
|
301
|
-
sections.push({ t: 'ready', text: 'Ready' });
|
|
302
|
-
sections.push({ t: 'sep' });
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
// Estimate line count per section
|
|
306
|
-
function estLines(s) {
|
|
307
|
-
if (s.t === 'sep' || s.t === 'ready' || s.t === 'stream' || s.t === 'text' || s.t === 'tool') return 1;
|
|
308
|
-
if (s.t === 'cmd') return 1;
|
|
309
|
-
if (s.t === 'header_bright') return 1;
|
|
310
|
-
if (s.t === 'user') return 2;
|
|
311
|
-
if (s.t === 'thinking') return 2;
|
|
312
|
-
return 1;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Limit to available lines - show the LAST (newest) sections
|
|
316
|
-
let totalEst = 0;
|
|
317
|
-
let lastN = 0;
|
|
318
|
-
for (let i = sections.length - 1; i >= 0; i--) {
|
|
319
|
-
const l = estLines(sections[i]);
|
|
320
|
-
if (totalEst + l > availLines) break;
|
|
321
|
-
totalEst += l;
|
|
322
|
-
lastN = sections.length - i;
|
|
323
|
-
}
|
|
324
|
-
const visible = sections.slice(sections.length - lastN);
|
|
325
|
-
|
|
326
|
-
// Render
|
|
327
|
-
const sectionEls = [];
|
|
328
|
-
for (let i = 0; i < visible.length; i++) {
|
|
329
|
-
const s = visible[i];
|
|
330
|
-
const ki = 's-' + i;
|
|
331
|
-
|
|
332
|
-
if (s.t === 'sep') {
|
|
333
|
-
sectionEls.push(h(Box, { key: ki, height: 1 }, hr(cols)));
|
|
334
|
-
} else if (s.t === 'user') {
|
|
335
|
-
sectionEls.push(
|
|
336
|
-
h(Box, { key: ki + '-h', height: 1, paddingLeft: 1 },
|
|
337
|
-
h(Text, { color: '#888899', bold: true }, 'You')
|
|
338
|
-
)
|
|
339
|
-
);
|
|
340
|
-
sectionEls.push(
|
|
341
|
-
h(Box, { key: ki + '-t', height: 1, paddingLeft: 2 },
|
|
342
|
-
h(Text, { color: hex.neonBlue, wrap: 'wrap' }, s.text)
|
|
343
|
-
)
|
|
344
|
-
);
|
|
345
|
-
} else if (s.t === 'text' || s.t === 'stream') {
|
|
346
|
-
sectionEls.push(
|
|
347
|
-
h(Box, { key: ki, height: 1, paddingLeft: 1 },
|
|
348
|
-
h(Text, { color: hex.primary, wrap: 'wrap' }, s.text)
|
|
349
|
-
)
|
|
350
|
-
);
|
|
351
|
-
} else if (s.t === 'thinking') {
|
|
352
|
-
sectionEls.push(
|
|
353
|
-
h(Box, { key: ki + '-h', height: 1, paddingLeft: 1 },
|
|
354
|
-
h(Text, { color: '#FF9F43', bold: true }, ' ' + s.spin + ' Thinking \u2022 ' + s.elapsed)
|
|
355
|
-
)
|
|
356
|
-
);
|
|
357
|
-
sectionEls.push(
|
|
358
|
-
h(Box, { key: ki + '-c', height: 1, paddingLeft: 2 },
|
|
359
|
-
h(Text, { color: '#FF9F43', wrap: 'wrap' }, s.content)
|
|
360
|
-
)
|
|
361
|
-
);
|
|
362
|
-
} else if (s.t === 'tool') {
|
|
363
|
-
sectionEls.push(
|
|
364
|
-
h(Box, { key: ki, height: 1, paddingLeft: 1 },
|
|
365
|
-
h(Text, { color: '#5B5B8A' }, '\u2500\u2500 ' + s.tool + ' \u2014 ' + (s.query || '') + ' (' + (s.sites || 0) + ' sites)')
|
|
366
|
-
)
|
|
367
|
-
);
|
|
368
|
-
} else if (s.t === 'header_bright') {
|
|
369
|
-
sectionEls.push(
|
|
370
|
-
h(Box, { key: ki, height: 1, paddingLeft: 1 },
|
|
371
|
-
h(Text, { color: '#888899', bold: true }, s.text || '')
|
|
372
|
-
)
|
|
373
|
-
);
|
|
374
|
-
} else if (s.t === 'cmd') {
|
|
375
|
-
const prefix = s.selected === true ? '\u276F ' : ' ';
|
|
376
|
-
const col = s.selected === true ? hex.neonBlue : '#666688';
|
|
377
|
-
sectionEls.push(
|
|
378
|
-
h(Box, { key: ki, height: 1, paddingLeft: 2 },
|
|
379
|
-
h(Text, { color: col }, prefix + s.trigger),
|
|
380
|
-
h(Text, { color: '#444466' }, ' ' + (s.desc || ''))
|
|
381
|
-
)
|
|
382
|
-
);
|
|
383
|
-
} else if (s.t === 'ready') {
|
|
384
|
-
sectionEls.push(
|
|
385
|
-
h(Box, { key: ki, height: 1, paddingLeft: 1 },
|
|
386
|
-
h(Text, { color: '#00FF88', bold: true }, s.text)
|
|
387
|
-
)
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// Fill remaining lines
|
|
393
|
-
while (sectionEls.length < availLines) {
|
|
394
|
-
sectionEls.push(h(Box, { key: 'fl-' + sectionEls.length, height: 1 }, h(Text, {}, '')));
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
const viewport = h(Box, {
|
|
398
|
-
flexGrow: 1,
|
|
399
|
-
height: availLines,
|
|
400
|
-
flexDirection: 'column',
|
|
401
|
-
overflow: 'hidden',
|
|
402
|
-
}, ...sectionEls);
|
|
403
149
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
150
|
+
return h(Box, {
|
|
151
|
+
flexGrow: 1,
|
|
152
|
+
flexDirection: 'column',
|
|
153
|
+
overflow: 'hidden',
|
|
154
|
+
paddingLeft: 0,
|
|
155
|
+
paddingRight: 0,
|
|
156
|
+
},
|
|
157
|
+
...events.slice(-15).map((event, i) => h(ActivityItem, { key: i, event }))
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return h(Box, {
|
|
162
|
+
flexDirection: 'column',
|
|
163
|
+
width: '100%',
|
|
164
|
+
height: rows,
|
|
165
|
+
overflow: 'hidden'
|
|
166
|
+
},
|
|
167
|
+
renderStartup() && !startupDone ? renderStartup() : null,
|
|
168
|
+
startupDone && renderHeader(),
|
|
169
|
+
startupDone && h(Separator),
|
|
170
|
+
startupDone && renderTimeline(),
|
|
171
|
+
startupDone && h(Box, { height: 1 }, h(Separator)),
|
|
172
|
+
startupDone && h(Box, {
|
|
173
|
+
height: 1,
|
|
174
|
+
paddingLeft: 1,
|
|
175
|
+
paddingRight: 1,
|
|
176
|
+
flexDirection: 'row',
|
|
177
|
+
alignItems: 'center'
|
|
178
|
+
},
|
|
179
|
+
h(Typography.Accent, '▌ Ask OpenAxies'),
|
|
180
|
+
h(Box, { flexGrow: 1 }),
|
|
181
|
+
h(TextInput, {
|
|
182
|
+
value: inputBuffer,
|
|
183
|
+
onChange: setInputBuffer,
|
|
184
|
+
onSubmit: handleSubmit,
|
|
185
|
+
focus: true,
|
|
186
|
+
showCursor: true
|
|
187
|
+
})
|
|
409
188
|
),
|
|
410
|
-
|
|
411
|
-
h(Text, { color: '#444466' }, 'ctrl+l clear ctrl+r resume ctrl+k commands'),
|
|
412
|
-
)
|
|
413
|
-
);
|
|
414
|
-
|
|
415
|
-
// Prompt
|
|
416
|
-
const promptLine = h(Box, { height: DOCK_H, flexShrink: 0, paddingLeft: 2, paddingRight: 2 },
|
|
417
|
-
h(Text, { color: hex.greenOnline }, '\u258C'),
|
|
418
|
-
h(TextInput, {
|
|
419
|
-
value: inputBuffer,
|
|
420
|
-
onChange: handleChange,
|
|
421
|
-
onSubmit: function () {},
|
|
422
|
-
placeholder: '',
|
|
423
|
-
focus: showOverlay === false,
|
|
424
|
-
showCursor: true,
|
|
425
|
-
})
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
return h(Box, { flexDirection: 'column', width: '100%', height: rows, overflow: 'hidden' },
|
|
429
|
-
createBrandHeader(),
|
|
430
|
-
viewport,
|
|
431
|
-
statusBar,
|
|
432
|
-
promptLine
|
|
189
|
+
startupDone && renderFooter()
|
|
433
190
|
);
|
|
434
191
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { Typography } from '../ui/Typography.js';
|
|
4
|
+
import { hex } from '../../config/theme.js';
|
|
5
|
+
|
|
6
|
+
const h = React.createElement;
|
|
7
|
+
|
|
8
|
+
export function ActivityItem({ event }) {
|
|
9
|
+
switch (event.type) {
|
|
10
|
+
case 'user':
|
|
11
|
+
return h(Box, { flexDirection: 'column', paddingLeft: 1, paddingRight: 1, marginBottom: 1 },
|
|
12
|
+
h(Typography.Subheader, 'You'),
|
|
13
|
+
h(Box, { paddingLeft: 1 }, h(Typography.Body, { color: hex.neonBlue }, event.content))
|
|
14
|
+
);
|
|
15
|
+
case 'assistant':
|
|
16
|
+
return h(Box, { flexDirection: 'column', paddingLeft: 1, paddingRight: 1, marginBottom: 1 },
|
|
17
|
+
h(Typography.Body, event.content)
|
|
18
|
+
);
|
|
19
|
+
case 'thought':
|
|
20
|
+
return h(Box, {
|
|
21
|
+
flexDirection: 'column',
|
|
22
|
+
paddingLeft: 1,
|
|
23
|
+
paddingRight: 1,
|
|
24
|
+
marginBottom: 1,
|
|
25
|
+
backgroundColor: '#12121A'
|
|
26
|
+
},
|
|
27
|
+
h(Box, { flexDirection: 'row', alignItems: 'center' },
|
|
28
|
+
h(Typography.Thinking, `\u25CB Thinking \u2022 ${event.elapsed || '0.0s'}`)
|
|
29
|
+
),
|
|
30
|
+
h(Box, { paddingLeft: 1 }, h(Typography.Thinking, event.content))
|
|
31
|
+
);
|
|
32
|
+
case 'tool':
|
|
33
|
+
return h(Box, { paddingLeft: 1, paddingRight: 1, marginBottom: 0.5 },
|
|
34
|
+
h(Typography.Muted, `\u2500\u2500 ${event.tool} ${event.query ? ' \u2014 ' + event.query : ''}`)
|
|
35
|
+
);
|
|
36
|
+
case 'todo':
|
|
37
|
+
const icon = event.status === 'completed' ? '\u2713' : (event.status === 'running' ? '\u25B2' : '\u25CB');
|
|
38
|
+
const color = event.status === 'completed' ? '#00FF88' : (event.status === 'running' ? hex.neonBlue : '#666688');
|
|
39
|
+
return h(Box, { paddingLeft: 1, paddingRight: 1 },
|
|
40
|
+
h(Text, { color }, `${icon} `),
|
|
41
|
+
h(Typography.Muted, event.text)
|
|
42
|
+
);
|
|
43
|
+
default:
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Box } from 'ink';
|
|
3
|
+
import { ActivityItem } from './ActivityItem.js';
|
|
4
|
+
|
|
5
|
+
const h = React.createElement;
|
|
6
|
+
|
|
7
|
+
export function TodoTracker({ todos }) {
|
|
8
|
+
if (!todos || todos.length === 0) return null;
|
|
9
|
+
|
|
10
|
+
return h(Box, {
|
|
11
|
+
flexDirection: 'column',
|
|
12
|
+
paddingLeft: 1,
|
|
13
|
+
paddingRight: 1,
|
|
14
|
+
marginBottom: 1,
|
|
15
|
+
borderStyle: 'single',
|
|
16
|
+
borderColor: '#1A1A28'
|
|
17
|
+
},
|
|
18
|
+
...todos.map((todo, i) => h(ActivityItem, {
|
|
19
|
+
key: i,
|
|
20
|
+
event: { type: 'todo', text: todo.text, status: todo.status }
|
|
21
|
+
}))
|
|
22
|
+
);
|
|
23
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Text } from 'ink';
|
|
3
|
+
import { hex } from '../../config/theme.js';
|
|
4
|
+
|
|
5
|
+
const h = React.createElement;
|
|
6
|
+
|
|
7
|
+
export const Typography = {
|
|
8
|
+
Header: ({ children }) => h(Text, { color: '#FFFFFF', bold: true }, children),
|
|
9
|
+
Subheader: ({ children }) => h(Text, { color: '#AAAAAA', bold: true }, children),
|
|
10
|
+
Body: ({ children, color = '#FFFFFF' }) => h(Text, { color, wrap: 'wrap' }, children),
|
|
11
|
+
Muted: ({ children }) => h(Text, { color: '#666688' }, children),
|
|
12
|
+
Dim: ({ children }) => h(Text, { color: '#444466' }, children),
|
|
13
|
+
Accent: ({ children }) => h(Text, { color: hex.neonBlue, bold: true }, children),
|
|
14
|
+
Thinking: ({ children }) => h(Text, { color: '#FF9F43' }, children),
|
|
15
|
+
};
|
package/src/providers/index.js
CHANGED
|
@@ -4,15 +4,12 @@ import { runWebSearchGraph, shouldUseWebSearch } from './websearch.js';
|
|
|
4
4
|
const MODEL_ROUTES = Object.freeze({
|
|
5
5
|
'openaxis/openaxis-llama': Object.freeze([
|
|
6
6
|
'https://universal-618-clarity-main.hf.space/v1/chat/completions',
|
|
7
|
-
'https://universal-618-clarity-4.hf.space/v1/chat/completions',
|
|
8
7
|
]),
|
|
9
8
|
'openaxis/openaxis-gpt': Object.freeze([
|
|
10
9
|
'https://universal-618-clarity-2.hf.space/v1/chat/completions',
|
|
11
|
-
'https://universal-618-clarity-5.hf.space/v1/chat/completions',
|
|
12
10
|
]),
|
|
13
11
|
'openaxis/openaxis-deepseek': Object.freeze([
|
|
14
12
|
'https://universal-618-clarity-3.hf.space/v1/chat/completions',
|
|
15
|
-
'https://universal-618-clarity-6.hf.space/v1/chat/completions',
|
|
16
13
|
]),
|
|
17
14
|
});
|
|
18
15
|
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { hex } from '../config/theme.js';
|
|
4
|
-
|
|
5
|
-
const h = React.createElement;
|
|
6
|
-
|
|
7
|
-
const LOGO = [
|
|
8
|
-
'██████╗ ██████╗ ███████╗███╗ ██╗ █████╗ ██╗ ██╗██╗███████╗███████╗',
|
|
9
|
-
'██╔═══██╗██╔══██╗██╔════╝████╗ ██║██╔══██╗╚██╗██╔╝██║██╔════╝██╔════╝',
|
|
10
|
-
'██║ ██║██████╔╝█████╗ ██╔██╗ ██║███████║ ╚███╔╝ ██║█████╗ ███████╗',
|
|
11
|
-
'██║ ██║██╔═══╝ ██╔══╝ ██║╚██╗██║██╔══██║ ██╔██╗ ██║██╔══╝ ╚════██║',
|
|
12
|
-
'╚██████╔╝██║ ███████╗██║ ╚████║██║ ██║██╔╝ ██╗██║███████╗███████║',
|
|
13
|
-
' ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
export function createBrandHeader() {
|
|
17
|
-
const els = [];
|
|
18
|
-
for (let r = 0; r < LOGO.length; r++) {
|
|
19
|
-
els.push(
|
|
20
|
-
h(Text, { key: 'l' + r, color: '#00BBFF', bold: true }, LOGO[r])
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
els.push(
|
|
24
|
-
h(Text, { key: 'sub', color: '#666688' }, 'OpenAxies Agent Runtime')
|
|
25
|
-
);
|
|
26
|
-
return h(Box, {
|
|
27
|
-
flexDirection: 'column',
|
|
28
|
-
width: '100%',
|
|
29
|
-
flexShrink: 0,
|
|
30
|
-
paddingLeft: 0,
|
|
31
|
-
paddingTop: 0,
|
|
32
|
-
paddingBottom: 0,
|
|
33
|
-
}, ...els);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export const BRAND_HEIGHT = 7;
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Box, Text, useInput, useStdout } from 'ink';
|
|
3
|
-
import TextInput from 'ink-text-input';
|
|
4
|
-
import { hex } from '../config/theme.js';
|
|
5
|
-
|
|
6
|
-
const h = React.createElement;
|
|
7
|
-
|
|
8
|
-
export const DOCK_HEIGHT = 4;
|
|
9
|
-
|
|
10
|
-
const PROMPT = '> ';
|
|
11
|
-
|
|
12
|
-
export function createComposerDock(config) {
|
|
13
|
-
if (config === null || config === undefined || typeof config !== 'object') {
|
|
14
|
-
throw new Error('createComposerDock requires a config object');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const value = typeof config.value === 'string' ? config.value : '';
|
|
18
|
-
const onChange = config.onChange;
|
|
19
|
-
const onSubmit = config.onSubmit;
|
|
20
|
-
const inputActive = config.inputActive !== false;
|
|
21
|
-
const isStreaming = config.isStreaming === true;
|
|
22
|
-
const modelLabel = typeof config.modelLabel === 'string' ? config.modelLabel : '';
|
|
23
|
-
const modelStatus = config.modelStatus === true;
|
|
24
|
-
const safeCols = typeof config.terminalWidth === 'number' && config.terminalWidth > 0 ? config.terminalWidth : 80;
|
|
25
|
-
|
|
26
|
-
const placeholder = isStreaming ? 'Ctrl+C to interrupt...' : 'type a message...';
|
|
27
|
-
const innerW = safeCols - 2;
|
|
28
|
-
|
|
29
|
-
return h(Box, {
|
|
30
|
-
width: '100%',
|
|
31
|
-
height: DOCK_HEIGHT,
|
|
32
|
-
flexDirection: 'column',
|
|
33
|
-
flexShrink: 0,
|
|
34
|
-
},
|
|
35
|
-
h(Box, { height: 1, paddingLeft: 1 },
|
|
36
|
-
h(Text, { color: '#1A1A28' }, '\u2500'.repeat(innerW))
|
|
37
|
-
),
|
|
38
|
-
h(Box, { height: 1, paddingLeft: 1, paddingRight: 1 },
|
|
39
|
-
h(Text, { color: hex.border }, '\u2502'),
|
|
40
|
-
h(Text, { color: inputActive ? hex.neonBlue : '#444466', bold: true }, ' ' + PROMPT),
|
|
41
|
-
h(Box, { flexGrow: 1, height: 1, overflow: 'hidden' },
|
|
42
|
-
h(TextInput, {
|
|
43
|
-
value: value,
|
|
44
|
-
onChange: onChange,
|
|
45
|
-
onSubmit: onSubmit,
|
|
46
|
-
placeholder: placeholder,
|
|
47
|
-
focus: inputActive,
|
|
48
|
-
showCursor: true,
|
|
49
|
-
})
|
|
50
|
-
),
|
|
51
|
-
h(Text, { color: '#444466' }, ' '),
|
|
52
|
-
h(Text, { color: hex.border }, '\u2502')
|
|
53
|
-
),
|
|
54
|
-
h(Box, { height: 1, paddingLeft: 1, paddingRight: 1 },
|
|
55
|
-
h(Text, { color: hex.border }, '\u2514'),
|
|
56
|
-
h(Box, { flexGrow: 1, height: 1, overflow: 'hidden' },
|
|
57
|
-
h(Text, { color: '#333344' }, '\u2500'.repeat(innerW - 2))
|
|
58
|
-
),
|
|
59
|
-
h(Text, { color: hex.border }, '\u2518')
|
|
60
|
-
),
|
|
61
|
-
h(Box, { height: 1, paddingRight: 1 },
|
|
62
|
-
h(Box, { flexGrow: 1 }),
|
|
63
|
-
h(Text, { color: '#444466', bold: modelStatus }, modelLabel || '')
|
|
64
|
-
)
|
|
65
|
-
);
|
|
66
|
-
}
|