openaxies 0.5.3 → 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 +224 -197
- 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,93 +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
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
thinkStarted = true;
|
|
190
|
-
thinkStartRef.current = Date.now();
|
|
191
|
-
setThinkingElapsed(0);
|
|
192
|
-
}
|
|
193
|
-
if (inside === false && thinkStarted === true) {
|
|
194
|
-
thinkStarted = false;
|
|
195
|
-
thinkStartRef.current = null;
|
|
196
|
-
setThinkingElapsed(0);
|
|
197
|
-
}
|
|
198
|
-
await new Promise(function (r) { setTimeout(r, 0); });
|
|
199
|
-
}
|
|
154
|
+
const c = typeof event.content === 'string' ? event.content : '';
|
|
155
|
+
accumulated = accumulated + c;
|
|
156
|
+
const inside = checkThink(accumulated);
|
|
157
|
+
setStreamText(accumulated);
|
|
158
|
+
setIsThinking(inside);
|
|
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); }
|
|
200
161
|
}
|
|
201
162
|
if (event.type === 'done') break;
|
|
202
163
|
if (event.type === 'timeout') {
|
|
203
164
|
if (accumulated.length > 0) accumulated = accumulated + '\n\n\u2014 stream timed out \u2014';
|
|
204
|
-
setStreamText(accumulated);
|
|
205
|
-
break;
|
|
165
|
+
setStreamText(accumulated); break;
|
|
206
166
|
}
|
|
207
167
|
}
|
|
208
168
|
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
209
|
-
setIsThinking(false);
|
|
210
|
-
thinkStartRef.current = null;
|
|
211
|
-
setThinkingElapsed(0);
|
|
212
|
-
setStreamingActive(false);
|
|
213
|
-
const finalText = streamText;
|
|
169
|
+
setIsThinking(false); thinkStartRef.current = null; setThinkingElapsed(0); setStreamingActive(false);
|
|
214
170
|
setStreamText('');
|
|
215
|
-
if (
|
|
216
|
-
setMessages(function (p) { return p.concat([{ role: 'assistant', content:
|
|
171
|
+
if (accumulated.length > 0) {
|
|
172
|
+
setMessages(function (p) { return p.concat([{ role: 'assistant', content: accumulated, id: 'a-' + Date.now() }]); });
|
|
217
173
|
}
|
|
218
174
|
} catch (err) {
|
|
219
175
|
if (connRef.current !== null) clearInterval(connRef.current), connRef.current = null;
|
|
220
|
-
setIsThinking(false);
|
|
221
|
-
thinkStartRef.current = null;
|
|
222
|
-
setThinkingElapsed(0);
|
|
223
|
-
setStreamingActive(false);
|
|
224
|
-
setStreamText('');
|
|
176
|
+
setIsThinking(false); thinkStartRef.current = null; setThinkingElapsed(0); setStreamingActive(false); setStreamText('');
|
|
225
177
|
const errMsg = err !== null && err !== undefined ? (err.message || String(err)) : 'Unknown error';
|
|
226
|
-
setMessages(function (p) {
|
|
227
|
-
return p.concat([{ role: 'assistant', content: 'Error: ' + errMsg + '\n\nSpace cold-starting? Try again.', id: 'er-' + Date.now() }]);
|
|
228
|
-
});
|
|
178
|
+
setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'Error: ' + errMsg, id: 'er-' + Date.now() }]); });
|
|
229
179
|
}
|
|
230
180
|
}
|
|
231
181
|
|
|
232
182
|
function handleChange(v) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
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); }
|
|
237
186
|
}
|
|
238
187
|
|
|
239
188
|
function handleCmd(item) {
|
|
240
189
|
if (item === null || item === undefined) return;
|
|
241
|
-
const t = item.
|
|
190
|
+
const t = item.trigger;
|
|
242
191
|
if (typeof t !== 'string') return;
|
|
243
192
|
if (t === '/exit') process.exit(0);
|
|
244
|
-
if (t === '/clear') setMessages([]);
|
|
193
|
+
if (t === '/clear') { setMessages([]); }
|
|
245
194
|
if (t === '/model') cycleModel();
|
|
246
|
-
if (t === '/
|
|
247
|
-
|
|
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);
|
|
248
199
|
}
|
|
249
|
-
|
|
250
|
-
setShowOverlay(false);
|
|
251
|
-
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);
|
|
252
202
|
}
|
|
253
203
|
|
|
254
204
|
useInput(function handleInput(input, key) {
|
|
255
205
|
if (showOverlay === true) {
|
|
256
206
|
if (key.escape === true) { setShowOverlay(false); setInputBuffer(''); setOverlayIndex(0); return; }
|
|
257
|
-
if (key.upArrow === true) { setOverlayIndex(function (p) { return p > 0 ? p - 1 :
|
|
258
|
-
if (key.downArrow === true) { setOverlayIndex(function (p) { return p <
|
|
259
|
-
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
|
+
}
|
|
260
217
|
return;
|
|
261
218
|
}
|
|
262
219
|
if (key.return === true) {
|
|
@@ -264,144 +221,214 @@ function AppRoot() {
|
|
|
264
221
|
if (s.length > 0 && streamingActive === false) handleSubmit(s);
|
|
265
222
|
return;
|
|
266
223
|
}
|
|
267
|
-
if (key.ctrl === true
|
|
268
|
-
if (
|
|
269
|
-
|
|
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; }
|
|
270
239
|
}
|
|
271
240
|
});
|
|
272
241
|
|
|
242
|
+
function filterText(t) {
|
|
243
|
+
if (showThoughts === true) return t;
|
|
244
|
+
return t.split('<think>').join('').split('</think>').join('');
|
|
245
|
+
}
|
|
246
|
+
|
|
273
247
|
const activeInfo = getModelById(activeModel);
|
|
274
248
|
const activeLabel = activeInfo !== null ? activeInfo.label : 'OpenAxies';
|
|
275
249
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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' });
|
|
281
263
|
}
|
|
282
|
-
const logo = h(Box, { flexDirection: 'column', width: '100%', flexShrink: 0, paddingLeft: 0 }, ...logoEls);
|
|
283
264
|
|
|
284
|
-
|
|
265
|
+
// Conversation history
|
|
285
266
|
for (let i = 0; i < messages.length; i++) {
|
|
286
267
|
const msg = messages[i];
|
|
287
268
|
if (msg === null || typeof msg !== 'object') continue;
|
|
288
269
|
const content = typeof msg.content === 'string' ? msg.content : '';
|
|
289
270
|
if (content.length === 0) continue;
|
|
271
|
+
|
|
290
272
|
if (msg.role === 'user') {
|
|
291
|
-
|
|
273
|
+
sections.push({ t: 'sep' });
|
|
274
|
+
sections.push({ t: 'user', text: content });
|
|
292
275
|
} else {
|
|
293
|
-
|
|
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 });
|
|
294
279
|
}
|
|
295
280
|
}
|
|
296
281
|
|
|
282
|
+
// Streaming section
|
|
297
283
|
const hasStream = typeof streamText === 'string' && streamText.length > 0;
|
|
298
284
|
if (hasStream === true) {
|
|
285
|
+
const clean = filterText(streamText);
|
|
286
|
+
|
|
299
287
|
if (toolInfo !== null && toolInfo.used === true && toolInfo.query.length > 0) {
|
|
300
|
-
|
|
288
|
+
sections.push({ t: 'tool', tool: toolInfo.tool, query: toolInfo.query, sites: toolInfo.sites });
|
|
301
289
|
}
|
|
290
|
+
|
|
302
291
|
if (isThinking === true) {
|
|
303
|
-
|
|
304
|
-
const clean = streamText.split('<think>').join('').split('</think>').join('');
|
|
305
|
-
viewLines.push({ t: 'think-text', text: clean });
|
|
292
|
+
sections.push({ t: 'thinking', spin: SPINNER[spinnerIdx], elapsed: fmtTime(thinkingElapsed), content: clean });
|
|
306
293
|
} else {
|
|
307
|
-
|
|
308
|
-
viewLines.push({ t: 'text', text: clean });
|
|
294
|
+
sections.push({ t: 'stream', text: clean });
|
|
309
295
|
}
|
|
310
296
|
}
|
|
311
297
|
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
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;
|
|
317
313
|
}
|
|
318
314
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
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;
|
|
322
331
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
if (
|
|
326
|
-
|
|
327
|
-
h(Box, { key: '
|
|
328
|
-
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)
|
|
329
349
|
)
|
|
330
350
|
);
|
|
331
|
-
} else if (
|
|
332
|
-
|
|
333
|
-
h(Box, { key: '
|
|
334
|
-
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)
|
|
335
355
|
)
|
|
336
356
|
);
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
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)
|
|
341
360
|
)
|
|
342
361
|
);
|
|
343
|
-
} else if (
|
|
344
|
-
|
|
345
|
-
h(Box, { key:
|
|
346
|
-
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)')
|
|
347
366
|
)
|
|
348
367
|
);
|
|
349
|
-
} else if (
|
|
350
|
-
|
|
351
|
-
h(Box, { key:
|
|
352
|
-
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 || '')
|
|
353
372
|
)
|
|
354
373
|
);
|
|
355
|
-
} else if (
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
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 || ''))
|
|
359
381
|
)
|
|
360
382
|
);
|
|
361
|
-
} else if (
|
|
362
|
-
|
|
363
|
-
h(Box, { key:
|
|
364
|
-
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)
|
|
365
387
|
)
|
|
366
388
|
);
|
|
367
389
|
}
|
|
368
390
|
}
|
|
369
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
|
+
|
|
370
397
|
const viewport = h(Box, {
|
|
371
398
|
flexGrow: 1,
|
|
372
399
|
height: availLines,
|
|
373
400
|
flexDirection: 'column',
|
|
374
401
|
overflow: 'hidden',
|
|
375
|
-
|
|
376
|
-
paddingBottom: 0,
|
|
377
|
-
}, ...viewEls);
|
|
378
|
-
|
|
379
|
-
const slashOverlay = showOverlay === true ? createSlashOverlay(commands, overlayIndex) : null;
|
|
380
|
-
|
|
381
|
-
const dock = createComposerDock({
|
|
382
|
-
value: inputBuffer,
|
|
383
|
-
onChange: handleChange,
|
|
384
|
-
onSubmit: function () {},
|
|
385
|
-
inputActive: showOverlay === false,
|
|
386
|
-
isStreaming: streamingActive,
|
|
387
|
-
terminalWidth: cols,
|
|
388
|
-
modelLabel: activeLabel + ' \u25CF',
|
|
389
|
-
modelStatus: true,
|
|
390
|
-
});
|
|
402
|
+
}, ...sectionEls);
|
|
391
403
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
height:
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
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(),
|
|
401
430
|
viewport,
|
|
402
|
-
|
|
403
|
-
|
|
431
|
+
statusBar,
|
|
432
|
+
promptLine
|
|
404
433
|
);
|
|
405
434
|
}
|
|
406
|
-
|
|
407
|
-
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,
|