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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openaxies",
3
- "version": "0.5.3",
3
+ "version": "0.6.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
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
- import { getCommands } from './config/commands.js';
4
+
5
5
  import { getModels, getModelById, DEFAULT_MODEL_ID } from './config/models.js';
6
6
  import { callModel } from './providers/index.js';
7
- import { createComposerDock, DOCK_HEIGHT } from './components/ComposerDock.js';
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 formatTimer(s) {
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, color) {
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: c }, s);
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 commands = getCommands();
72
- const LOGO_H = 5;
73
- const overlayH = showOverlay ? 6 : 0;
74
- const fixedH = LOGO_H + overlayH + DOCK_HEIGHT;
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
- spinnerRef.current = setInterval(function () {
97
- idx = (idx + 1) % SPINNER.length;
98
- setSpinnerIdx(idx);
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
- if (spinnerRef.current !== null) {
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
- setStreamText('');
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
- firstEvent = false;
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 content = typeof event.content === 'string' ? event.content : '';
183
- for (let i = 0; i < content.length; i++) {
184
- accumulated = accumulated + content[i];
185
- const inside = checkThink(accumulated);
186
- setStreamText(accumulated);
187
- setIsThinking(inside);
188
- if (inside === true && thinkStarted === false) {
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 (finalText.length > 0) {
216
- setMessages(function (p) { return p.concat([{ role: 'assistant', content: finalText, id: 'a-' + Date.now() }]); });
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
- const s = typeof v === 'string' ? v : '';
234
- setInputBuffer(s);
235
- if (s.length === 1 && s[0] === '/' && showOverlay === false) { setShowOverlay(true); setOverlayIndex(0); }
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.value;
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 === '/help') {
247
- setMessages(function (p) { return p.concat([{ role: 'assistant', content: 'Commands: /help /clear /model /exit', id: 'h-' + Date.now() }]); });
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
- setInputBuffer('');
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 : commands.length - 1; }); return; }
258
- if (key.downArrow === true) { setOverlayIndex(function (p) { return p < commands.length - 1 ? p + 1 : 0; }); return; }
259
- if (key.return === true) { const cmd = commands[overlayIndex]; if (cmd !== null && cmd !== undefined) handleCmd({ value: cmd.trigger }); 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;
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 && (input === 'c' || input === 'C')) {
268
- if (abortRef.current !== null) { try { abortRef.current.abort(); } catch (_) {} abortRef.current = null; setStreamingActive(false); setStreamText(''); }
269
- process.exit(0);
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
- const logoEls = [];
277
- for (let r = 0; r < LOGO.length; r++) {
278
- logoEls.push(
279
- h(Text, { key: 'l' + r, color: LOGO_COLORS[r % LOGO_COLORS.length], bold: true }, LOGO[r])
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
- const viewLines = [];
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
- viewLines.push({ t: 'user', text: '\u25B7 ' + content });
273
+ sections.push({ t: 'sep' });
274
+ sections.push({ t: 'user', text: content });
292
275
  } else {
293
- viewLines.push({ t: 'text', text: content });
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
- viewLines.push({ t: 'tool', text: '\u2500\u2500 ' + toolInfo.tool + ': "' + toolInfo.query + '" (' + toolInfo.sites + ' sites)' });
288
+ sections.push({ t: 'tool', tool: toolInfo.tool, query: toolInfo.query, sites: toolInfo.sites });
301
289
  }
290
+
302
291
  if (isThinking === true) {
303
- viewLines.push({ t: 'think', spin: SPINNER[spinnerIdx], elapsed: formatTimer(thinkingElapsed) });
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
- const clean = streamText.split('<think>').join('').split('</think>').join('');
308
- viewLines.push({ t: 'text', text: clean });
294
+ sections.push({ t: 'stream', text: clean });
309
295
  }
310
296
  }
311
297
 
312
- if (viewLines.length === 0) {
313
- viewLines.push({ t: 'idle', text: ' \u25B8 type a message to begin | /model to switch models | /help for commands' });
314
- viewLines.push({ t: 'idle', text: '' });
315
- viewLines.push({ t: 'idle-sm', text: ' Models: OpenAxies Llama | OpenAxies GPT | OpenAxies DeepSeek' });
316
- viewLines.push({ t: 'idle-sm', text: ' Current: ' + activeLabel });
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
- const visible = Math.max(1, availLines - 1);
320
- const display = viewLines.slice(-visible);
321
- const viewEls = [];
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
- for (let i = 0; i < display.length; i++) {
324
- const l = display[i];
325
- if (l.t === 'think') {
326
- viewEls.push(
327
- h(Box, { key: 'k-' + i, height: 1, paddingLeft: 2 },
328
- h(Text, { color: '#FF9F43', bold: true }, l.spin + ' Thinking \u2022 ' + l.elapsed)
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 (l.t === 'think-text') {
332
- viewEls.push(
333
- h(Box, { key: 'kt-' + i, height: 1, paddingLeft: 3 },
334
- h(Text, { color: '#FF9F43' }, l.text)
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
- } else if (l.t === 'tool') {
338
- viewEls.push(
339
- h(Box, { key: 'tl-' + i, height: 1, paddingLeft: 2 },
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 (l.t === 'user') {
344
- viewEls.push(
345
- h(Box, { key: 'u-' + i, height: 1, paddingLeft: 1 },
346
- h(Text, { color: hex.neonBlue }, l.text)
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 (l.t === 'text') {
350
- viewEls.push(
351
- h(Box, { key: 'a-' + i, height: 1, paddingLeft: 2 },
352
- h(Text, { color: hex.primary }, l.text)
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 (l.t === 'idle') {
356
- viewEls.push(
357
- h(Box, { key: 'i-' + i, height: 1, paddingLeft: 1 },
358
- h(Text, { color: '#555577' }, l.text)
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 (l.t === 'idle-sm') {
362
- viewEls.push(
363
- h(Box, { key: 'is-' + i, height: 1, paddingLeft: 1 },
364
- h(Text, { color: '#333355' }, l.text)
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
- paddingTop: 0,
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
- return h(Box, {
393
- flexDirection: 'column',
394
- width: '100%',
395
- height: rows,
396
- backgroundColor: hex.black,
397
- overflow: 'hidden',
398
- },
399
- logo,
400
- h(Box, { height: 1 }, hr(cols, '#1A1A2E')),
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
- slashOverlay,
403
- dock
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,