openaxies 0.5.4 → 0.6.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/package.json +1 -1
- package/src/App.js +219 -189
- package/src/components/BrandHeader.js +36 -0
- package/src/providers/streaming.js +3 -0
package/package.json
CHANGED
package/src/App.js
CHANGED
|
@@ -1,25 +1,16 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { Box, Text, useInput, useStdout } from 'ink';
|
|
3
3
|
import { hex } from './config/theme.js';
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
import { getModels, getModelById, DEFAULT_MODEL_ID } from './config/models.js';
|
|
6
6
|
import { callModel } from './providers/index.js';
|
|
7
|
-
import {
|
|
7
|
+
import { createBrandHeader, BRAND_HEIGHT } from './components/BrandHeader.js';
|
|
8
|
+
import TextInput from 'ink-text-input';
|
|
8
9
|
|
|
9
10
|
const h = React.createElement;
|
|
10
11
|
|
|
11
12
|
export default AppRoot;
|
|
12
13
|
|
|
13
|
-
const LOGO = [
|
|
14
|
-
' \u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588',
|
|
15
|
-
'\u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 ',
|
|
16
|
-
'\u2588 \u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588\u2588\u2588\u2588 \u2588\u2588\u2588',
|
|
17
|
-
'\u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588',
|
|
18
|
-
' \u2588\u2588\u2588 \u2588 \u2588\u2588\u2588\u2588\u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588 \u2588\u2588\u2588\u2588\u2588 \u2588\u2588\u2588 ',
|
|
19
|
-
];
|
|
20
|
-
|
|
21
|
-
const LOGO_COLORS = ['#00F0FF', '#00DDFF', '#00CCFF', '#00BBFF', '#00AAFF'];
|
|
22
|
-
|
|
23
14
|
function buildHistory(messages, newText) {
|
|
24
15
|
const h = [];
|
|
25
16
|
for (let i = 0; i < messages.length; i++) {
|
|
@@ -36,20 +27,28 @@ function buildHistory(messages, newText) {
|
|
|
36
27
|
return h;
|
|
37
28
|
}
|
|
38
29
|
|
|
39
|
-
function
|
|
30
|
+
function fmtTime(s) {
|
|
40
31
|
if (typeof s !== 'number' || s < 0) return '0.0s';
|
|
41
32
|
return s.toFixed(1) + 's';
|
|
42
33
|
}
|
|
43
34
|
|
|
44
|
-
function hr(cols
|
|
35
|
+
function hr(cols) {
|
|
45
36
|
const n = typeof cols === 'number' && cols > 0 ? cols : 80;
|
|
46
|
-
const c = typeof color === 'string' ? color : '#1A1A28';
|
|
47
37
|
let s = '';
|
|
48
38
|
for (let i = 0; i < n; i++) s = s + '\u2500';
|
|
49
|
-
return h(Text, { color:
|
|
39
|
+
return h(Text, { color: '#1A1A28' }, s);
|
|
50
40
|
}
|
|
51
41
|
|
|
52
|
-
const SPINNER = ['\u25CB', '\u25D4', '\u25D0', '\u25D5', '\u25CF', '\u25D5', '\u25D0', '\u25D4'];
|
|
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' },
|
|
51
|
+
];
|
|
53
52
|
|
|
54
53
|
function AppRoot() {
|
|
55
54
|
const { stdout } = useStdout();
|
|
@@ -67,50 +66,31 @@ function AppRoot() {
|
|
|
67
66
|
const [showOverlay, setShowOverlay] = React.useState(false);
|
|
68
67
|
const [overlayIndex, setOverlayIndex] = React.useState(0);
|
|
69
68
|
const [toolInfo, setToolInfo] = React.useState(null);
|
|
69
|
+
const [showThoughts, setShowThoughts] = React.useState(true);
|
|
70
70
|
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const availLines = Math.max(1, rows - fixedH);
|
|
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);
|
|
76
75
|
|
|
77
76
|
const abortRef = React.useRef(null);
|
|
78
77
|
const connRef = React.useRef(null);
|
|
79
78
|
const spinnerRef = React.useRef(null);
|
|
80
79
|
const timerRef = React.useRef(null);
|
|
81
80
|
const thinkStartRef = React.useRef(null);
|
|
81
|
+
const lastQueryRef = React.useRef('');
|
|
82
82
|
|
|
83
83
|
React.useEffect(function () {
|
|
84
|
-
if (isThinking === false)
|
|
85
|
-
if (spinnerRef.current !== null) {
|
|
86
|
-
clearInterval(spinnerRef.current);
|
|
87
|
-
spinnerRef.current = null;
|
|
88
|
-
}
|
|
89
|
-
if (timerRef.current !== null) {
|
|
90
|
-
clearInterval(timerRef.current);
|
|
91
|
-
timerRef.current = null;
|
|
92
|
-
}
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
84
|
+
if (isThinking === false) return;
|
|
95
85
|
let idx = 0;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
}, 100);
|
|
100
|
-
timerRef.current = setInterval(function () {
|
|
101
|
-
if (thinkStartRef.current !== null) {
|
|
102
|
-
setThinkingElapsed((Date.now() - thinkStartRef.current) / 1000);
|
|
103
|
-
}
|
|
86
|
+
const si = setInterval(function () { idx = (idx + 1) % SPINNER.length; setSpinnerIdx(idx); }, 120);
|
|
87
|
+
const ti = setInterval(function () {
|
|
88
|
+
if (thinkStartRef.current !== null) setThinkingElapsed((Date.now() - thinkStartRef.current) / 1000);
|
|
104
89
|
}, 100);
|
|
90
|
+
spinnerRef.current = si;
|
|
91
|
+
timerRef.current = ti;
|
|
105
92
|
return function () {
|
|
106
|
-
|
|
107
|
-
clearInterval(spinnerRef.current);
|
|
108
|
-
spinnerRef.current = null;
|
|
109
|
-
}
|
|
110
|
-
if (timerRef.current !== null) {
|
|
111
|
-
clearInterval(timerRef.current);
|
|
112
|
-
timerRef.current = null;
|
|
113
|
-
}
|
|
93
|
+
clearInterval(si); clearInterval(ti);
|
|
114
94
|
};
|
|
115
95
|
}, [isThinking]);
|
|
116
96
|
|
|
@@ -136,6 +116,7 @@ function AppRoot() {
|
|
|
136
116
|
async function handleSubmit(text) {
|
|
137
117
|
const safe = typeof text === 'string' ? text : '';
|
|
138
118
|
if (safe.length === 0 || streamingActive === true) return;
|
|
119
|
+
lastQueryRef.current = safe;
|
|
139
120
|
setStreamingActive(true);
|
|
140
121
|
setToolInfo(null);
|
|
141
122
|
setStreamText(' connecting');
|
|
@@ -148,16 +129,13 @@ function AppRoot() {
|
|
|
148
129
|
return ' connecting';
|
|
149
130
|
});
|
|
150
131
|
}, 400);
|
|
151
|
-
setMessages(function (p) {
|
|
152
|
-
return p.concat([{ role: 'user', content: safe, id: 'u-' + Date.now() }]);
|
|
153
|
-
});
|
|
132
|
+
setMessages(function (p) { return p.concat([{ role: 'user', content: safe, id: 'u-' + Date.now() }]); });
|
|
154
133
|
setInputBuffer('');
|
|
155
134
|
const modelInfo = getModelById(activeModel);
|
|
156
135
|
if (modelInfo === null) {
|
|
157
136
|
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
158
|
-
setStreamingActive(false);
|
|
159
|
-
|
|
160
|
-
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'No model selected. Use /model.', id: 'e-' + Date.now() }]); });
|
|
137
|
+
setStreamingActive(false); setStreamText('');
|
|
138
|
+
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'No model selected.', id: 'e-' + Date.now() }]); });
|
|
161
139
|
return;
|
|
162
140
|
}
|
|
163
141
|
const history = buildHistory(messages, safe);
|
|
@@ -170,90 +148,72 @@ function AppRoot() {
|
|
|
170
148
|
let thinkStarted = false;
|
|
171
149
|
for await (const event of gen) {
|
|
172
150
|
if (abortController.signal.aborted === true) break;
|
|
173
|
-
if (firstEvent === true) {
|
|
174
|
-
|
|
175
|
-
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
176
|
-
}
|
|
177
|
-
if (event.type === 'tool') {
|
|
178
|
-
setToolInfo({ tool: event.tool, used: event.used, sites: event.sites, query: event.query });
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
151
|
+
if (firstEvent === true) { firstEvent = false; if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null; }
|
|
152
|
+
if (event.type === 'tool') { setToolInfo({ tool: event.tool, used: event.used, sites: event.sites, query: event.query }); continue; }
|
|
181
153
|
if (event.type === 'thinking' || event.type === 'token') {
|
|
182
|
-
const
|
|
183
|
-
accumulated = accumulated +
|
|
154
|
+
const c = typeof event.content === 'string' ? event.content : '';
|
|
155
|
+
accumulated = accumulated + c;
|
|
184
156
|
const inside = checkThink(accumulated);
|
|
185
157
|
setStreamText(accumulated);
|
|
186
158
|
setIsThinking(inside);
|
|
187
|
-
if (inside === true && thinkStarted === false) {
|
|
188
|
-
|
|
189
|
-
thinkStartRef.current = Date.now();
|
|
190
|
-
setThinkingElapsed(0);
|
|
191
|
-
}
|
|
192
|
-
if (inside === false && thinkStarted === true) {
|
|
193
|
-
thinkStarted = false;
|
|
194
|
-
thinkStartRef.current = null;
|
|
195
|
-
setThinkingElapsed(0);
|
|
196
|
-
}
|
|
197
|
-
await new Promise(function (r) { setTimeout(r, 0); });
|
|
159
|
+
if (inside === true && thinkStarted === false) { thinkStarted = true; thinkStartRef.current = Date.now(); setThinkingElapsed(0); }
|
|
160
|
+
if (inside === false && thinkStarted === true) { thinkStarted = false; thinkStartRef.current = null; setThinkingElapsed(0); }
|
|
198
161
|
}
|
|
199
162
|
if (event.type === 'done') break;
|
|
200
163
|
if (event.type === 'timeout') {
|
|
201
164
|
if (accumulated.length > 0) accumulated = accumulated + '\n\n\u2014 stream timed out \u2014';
|
|
202
|
-
setStreamText(accumulated);
|
|
203
|
-
break;
|
|
165
|
+
setStreamText(accumulated); break;
|
|
204
166
|
}
|
|
205
167
|
}
|
|
206
168
|
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
207
|
-
setIsThinking(false);
|
|
208
|
-
thinkStartRef.current = null;
|
|
209
|
-
setThinkingElapsed(0);
|
|
210
|
-
setStreamingActive(false);
|
|
169
|
+
setIsThinking(false); thinkStartRef.current = null; setThinkingElapsed(0); setStreamingActive(false);
|
|
211
170
|
setStreamText('');
|
|
212
171
|
if (accumulated.length > 0) {
|
|
213
172
|
setMessages(function (p) { return p.concat([{ role: 'assistant', content: accumulated, id: 'a-' + Date.now() }]); });
|
|
214
173
|
}
|
|
215
174
|
} catch (err) {
|
|
216
175
|
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
217
|
-
setIsThinking(false);
|
|
218
|
-
thinkStartRef.current = null;
|
|
219
|
-
setThinkingElapsed(0);
|
|
220
|
-
setStreamingActive(false);
|
|
221
|
-
setStreamText('');
|
|
176
|
+
setIsThinking(false); thinkStartRef.current = null; setThinkingElapsed(0); setStreamingActive(false); setStreamText('');
|
|
222
177
|
const errMsg = err !== null && err !== undefined ? (err.message || String(err)) : 'Unknown error';
|
|
223
|
-
setMessages(function (p) {
|
|
224
|
-
return p.concat([{ role: 'assistant', content: 'Error: ' + errMsg + '\n\nSpace cold-starting? Try again.', id: 'er-' + Date.now() }]);
|
|
225
|
-
});
|
|
178
|
+
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'Error: ' + errMsg, id: 'er-' + Date.now() }]); });
|
|
226
179
|
}
|
|
227
180
|
}
|
|
228
181
|
|
|
229
182
|
function handleChange(v) {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if (
|
|
233
|
-
if (showOverlay === true && (s.length === 0 || s[0] !== '/')) { setShowOverlay(false); }
|
|
183
|
+
setInputBuffer(v);
|
|
184
|
+
if (v.length === 1 && v[0] === '/') { setShowOverlay(true); setOverlayIndex(0); }
|
|
185
|
+
if (showOverlay === true && (v.length === 0 || v[0] !== '/')) { setShowOverlay(false); }
|
|
234
186
|
}
|
|
235
187
|
|
|
236
188
|
function handleCmd(item) {
|
|
237
189
|
if (item === null || item === undefined) return;
|
|
238
|
-
const t = item.
|
|
190
|
+
const t = item.trigger;
|
|
239
191
|
if (typeof t !== 'string') return;
|
|
240
192
|
if (t === '/exit') process.exit(0);
|
|
241
|
-
if (t === '/clear') setMessages([]);
|
|
193
|
+
if (t === '/clear') { setMessages([]); }
|
|
242
194
|
if (t === '/model') cycleModel();
|
|
243
|
-
if (t === '/
|
|
244
|
-
|
|
195
|
+
if (t === '/thoughts') { setShowThoughts(function (p) { return !p; }); }
|
|
196
|
+
if (t === '/resume') {
|
|
197
|
+
const q = lastQueryRef.current;
|
|
198
|
+
if (q.length > 0 && streamingActive === false) handleSubmit(q);
|
|
245
199
|
}
|
|
246
|
-
|
|
247
|
-
setShowOverlay(false);
|
|
248
|
-
setOverlayIndex(0);
|
|
200
|
+
if (t === '/help') { setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'Commands: /help /clear /model /thoughts /resume /exit', id: 'h-' + Date.now() }]); }); }
|
|
201
|
+
setInputBuffer(''); setShowOverlay(false); setOverlayIndex(0);
|
|
249
202
|
}
|
|
250
203
|
|
|
251
204
|
useInput(function handleInput(input, key) {
|
|
252
205
|
if (showOverlay === true) {
|
|
253
206
|
if (key.escape === true) { setShowOverlay(false); setInputBuffer(''); setOverlayIndex(0); return; }
|
|
254
|
-
if (key.upArrow === true) { setOverlayIndex(function (p) { return p > 0 ? p - 1 :
|
|
255
|
-
if (key.downArrow === true) { setOverlayIndex(function (p) { return p <
|
|
256
|
-
if (key.return === true) { const cmd =
|
|
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;
|
|
211
|
+
}
|
|
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
|
+
}
|
|
257
217
|
return;
|
|
258
218
|
}
|
|
259
219
|
if (key.return === true) {
|
|
@@ -261,144 +221,214 @@ function AppRoot() {
|
|
|
261
221
|
if (s.length > 0 && streamingActive === false) handleSubmit(s);
|
|
262
222
|
return;
|
|
263
223
|
}
|
|
264
|
-
if (key.ctrl === true
|
|
265
|
-
if (
|
|
266
|
-
|
|
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; }
|
|
267
239
|
}
|
|
268
240
|
});
|
|
269
241
|
|
|
242
|
+
function filterText(t) {
|
|
243
|
+
if (showThoughts === true) return t;
|
|
244
|
+
return t.split('<think>').join('').split('</think>').join('');
|
|
245
|
+
}
|
|
246
|
+
|
|
270
247
|
const activeInfo = getModelById(activeModel);
|
|
271
248
|
const activeLabel = activeInfo !== null ? activeInfo.label : 'OpenAxies';
|
|
272
249
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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' });
|
|
278
263
|
}
|
|
279
|
-
const logo = h(Box, { flexDirection: 'column', width: '100%', flexShrink: 0, paddingLeft: 0 }, ...logoEls);
|
|
280
264
|
|
|
281
|
-
|
|
265
|
+
// Conversation history
|
|
282
266
|
for (let i = 0; i < messages.length; i++) {
|
|
283
267
|
const msg = messages[i];
|
|
284
268
|
if (msg === null || typeof msg !== 'object') continue;
|
|
285
269
|
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
286
270
|
if (content.length === 0) continue;
|
|
271
|
+
|
|
287
272
|
if (msg.role === 'user') {
|
|
288
|
-
|
|
273
|
+
sections.push({ t: 'sep' });
|
|
274
|
+
sections.push({ t: 'user', text: content });
|
|
289
275
|
} else {
|
|
290
|
-
|
|
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 });
|
|
291
279
|
}
|
|
292
280
|
}
|
|
293
281
|
|
|
282
|
+
// Streaming section
|
|
294
283
|
const hasStream = typeof streamText === 'string' && streamText.length > 0;
|
|
295
284
|
if (hasStream === true) {
|
|
285
|
+
const clean = filterText(streamText);
|
|
286
|
+
|
|
296
287
|
if (toolInfo !== null && toolInfo.used === true && toolInfo.query.length > 0) {
|
|
297
|
-
|
|
288
|
+
sections.push({ t: 'tool', tool: toolInfo.tool, query: toolInfo.query, sites: toolInfo.sites });
|
|
298
289
|
}
|
|
290
|
+
|
|
299
291
|
if (isThinking === true) {
|
|
300
|
-
|
|
301
|
-
const clean = streamText.split('<think>').join('').split('</think>').join('');
|
|
302
|
-
viewLines.push({ t: 'think-text', text: clean });
|
|
292
|
+
sections.push({ t: 'thinking', spin: SPINNER[spinnerIdx], elapsed: fmtTime(thinkingElapsed), content: clean });
|
|
303
293
|
} else {
|
|
304
|
-
|
|
305
|
-
viewLines.push({ t: 'text', text: clean });
|
|
294
|
+
sections.push({ t: 'stream', text: clean });
|
|
306
295
|
}
|
|
307
296
|
}
|
|
308
297
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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;
|
|
314
313
|
}
|
|
315
314
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
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;
|
|
319
331
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (
|
|
323
|
-
|
|
324
|
-
h(Box, { key: '
|
|
325
|
-
h(Text, { color: '#
|
|
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)
|
|
326
349
|
)
|
|
327
350
|
);
|
|
328
|
-
} else if (
|
|
329
|
-
|
|
330
|
-
h(Box, { key: '
|
|
331
|
-
h(Text, { color: '#FF9F43' },
|
|
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)
|
|
332
355
|
)
|
|
333
356
|
);
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
h(Text, { color: '#5B5B8A' }, l.text)
|
|
357
|
+
sectionEls.push(
|
|
358
|
+
h(Box, { key: ki + '-c', height: 1, paddingLeft: 2 },
|
|
359
|
+
h(Text, { color: '#FF9F43', wrap: 'wrap' }, s.content)
|
|
338
360
|
)
|
|
339
361
|
);
|
|
340
|
-
} else if (
|
|
341
|
-
|
|
342
|
-
h(Box, { key:
|
|
343
|
-
h(Text, { color:
|
|
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)')
|
|
344
366
|
)
|
|
345
367
|
);
|
|
346
|
-
} else if (
|
|
347
|
-
|
|
348
|
-
h(Box, { key:
|
|
349
|
-
h(Text, { color:
|
|
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 || '')
|
|
350
372
|
)
|
|
351
373
|
);
|
|
352
|
-
} else if (
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
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 || ''))
|
|
356
381
|
)
|
|
357
382
|
);
|
|
358
|
-
} else if (
|
|
359
|
-
|
|
360
|
-
h(Box, { key:
|
|
361
|
-
h(Text, { color: '#
|
|
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)
|
|
362
387
|
)
|
|
363
388
|
);
|
|
364
389
|
}
|
|
365
390
|
}
|
|
366
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
|
+
|
|
367
397
|
const viewport = h(Box, {
|
|
368
398
|
flexGrow: 1,
|
|
369
399
|
height: availLines,
|
|
370
400
|
flexDirection: 'column',
|
|
371
401
|
overflow: 'hidden',
|
|
372
|
-
|
|
373
|
-
paddingBottom: 0,
|
|
374
|
-
}, ...viewEls);
|
|
375
|
-
|
|
376
|
-
const slashOverlay = showOverlay === true ? createSlashOverlay(commands, overlayIndex) : null;
|
|
377
|
-
|
|
378
|
-
const dock = createComposerDock({
|
|
379
|
-
value: inputBuffer,
|
|
380
|
-
onChange: handleChange,
|
|
381
|
-
onSubmit: function () {},
|
|
382
|
-
inputActive: showOverlay === false,
|
|
383
|
-
isStreaming: streamingActive,
|
|
384
|
-
terminalWidth: cols,
|
|
385
|
-
modelLabel: activeLabel + ' \u25CF',
|
|
386
|
-
modelStatus: true,
|
|
387
|
-
});
|
|
402
|
+
}, ...sectionEls);
|
|
388
403
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
height:
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
404
|
+
// Status bar
|
|
405
|
+
const statusBar = h(Box, { flexDirection: 'column', height: STATUS_H, flexShrink: 0 },
|
|
406
|
+
h(Box, { height: 1 }, hr(cols)),
|
|
407
|
+
h(Box, { height: 1, paddingLeft: 2 },
|
|
408
|
+
h(Text, { color: '#444466' }, 'esc interrupt ctrl+t thoughts ctrl+p models'),
|
|
409
|
+
),
|
|
410
|
+
h(Box, { height: 1, paddingLeft: 2 },
|
|
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(),
|
|
398
430
|
viewport,
|
|
399
|
-
|
|
400
|
-
|
|
431
|
+
statusBar,
|
|
432
|
+
promptLine
|
|
401
433
|
);
|
|
402
434
|
}
|
|
403
|
-
|
|
404
|
-
import { createSlashOverlay } from './components/SlashOverlay.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
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;
|
|
@@ -44,6 +44,9 @@ export async function* streamResponse(endpoint, body, signal) {
|
|
|
44
44
|
headers: {
|
|
45
45
|
'Content-Type': 'application/json',
|
|
46
46
|
'Accept': 'text/event-stream',
|
|
47
|
+
'Authorization': typeof process !== 'undefined' && process.env && process.env.OPENAXIES_HF_TOKEN
|
|
48
|
+
? 'Bearer ' + process.env.OPENAXIES_HF_TOKEN
|
|
49
|
+
: undefined,
|
|
47
50
|
},
|
|
48
51
|
body: JSON.stringify(body),
|
|
49
52
|
signal: combinedSignal,
|