@openagents-org/agent-launcher 0.1.0 → 0.1.1
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/tui.js +205 -158
package/package.json
CHANGED
package/src/tui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Interactive TUI dashboard for OpenAgents — `
|
|
2
|
+
* Interactive TUI dashboard for OpenAgents — `openagents` or `openagents tui`
|
|
3
3
|
*
|
|
4
4
|
* Ported from Python Textual TUI (cli_tui.py). Uses blessed for terminal UI.
|
|
5
5
|
*/
|
|
@@ -49,14 +49,12 @@ function loadAgentRows(connector) {
|
|
|
49
49
|
function loadCatalog(connector) {
|
|
50
50
|
const registry = connector.getRegistry();
|
|
51
51
|
const entries = registry.list();
|
|
52
|
-
// Check installed status
|
|
53
52
|
return entries.map(e => {
|
|
54
53
|
let installed = false;
|
|
55
54
|
try {
|
|
56
55
|
const { whichBinary } = require('./paths');
|
|
57
56
|
installed = !!whichBinary(e.install?.binary || e.name);
|
|
58
57
|
} catch {}
|
|
59
|
-
// Also check installed_agents.json marker
|
|
60
58
|
if (!installed) {
|
|
61
59
|
try {
|
|
62
60
|
const markerFile = path.join(connector.config?.configDir || '', 'installed_agents.json');
|
|
@@ -75,18 +73,14 @@ function loadCatalog(connector) {
|
|
|
75
73
|
});
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
const
|
|
79
|
-
online: '
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
'
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
online: '●', running: '●',
|
|
87
|
-
starting: '◐', reconnecting: '◐',
|
|
88
|
-
stopped: '○', error: '✗',
|
|
89
|
-
'not configured': '○',
|
|
76
|
+
const STATE_STYLES = {
|
|
77
|
+
online: { sym: '●', color: 'green', label: 'running' },
|
|
78
|
+
running: { sym: '●', color: 'green', label: 'running' },
|
|
79
|
+
starting: { sym: '◐', color: 'yellow', label: 'starting' },
|
|
80
|
+
reconnecting: { sym: '◐', color: 'yellow', label: 'reconnecting' },
|
|
81
|
+
stopped: { sym: '○', color: 'gray', label: 'stopped' },
|
|
82
|
+
error: { sym: '✗', color: 'red', label: 'error' },
|
|
83
|
+
'not configured': { sym: '○', color: 'gray', label: 'not configured' },
|
|
90
84
|
};
|
|
91
85
|
|
|
92
86
|
// ── Main TUI ─────────────────────────────────────────────────────────────
|
|
@@ -99,78 +93,93 @@ function createTUI() {
|
|
|
99
93
|
});
|
|
100
94
|
|
|
101
95
|
const connector = getConnector();
|
|
96
|
+
let pkg;
|
|
97
|
+
try { pkg = require('../package.json'); } catch { pkg = { version: '?' }; }
|
|
102
98
|
|
|
103
99
|
// ── Layout ──
|
|
104
100
|
|
|
105
|
-
// Header
|
|
101
|
+
// Header bar
|
|
106
102
|
const header = blessed.box({
|
|
107
|
-
top: 0, left: 0, width: '100%', height:
|
|
108
|
-
content: '{bold} OpenAgents{/bold} {gray-fg}Interactive Setup{/gray-fg}',
|
|
103
|
+
top: 0, left: 0, width: '100%', height: 1,
|
|
109
104
|
tags: true,
|
|
110
|
-
style: { bg: 'blue', fg: 'white' },
|
|
105
|
+
style: { bg: 'blue', fg: 'white', bold: true },
|
|
111
106
|
});
|
|
112
107
|
|
|
113
|
-
//
|
|
114
|
-
const
|
|
115
|
-
top:
|
|
116
|
-
border: { type: 'line' },
|
|
117
|
-
label: ' Agents ',
|
|
108
|
+
// Title area below header
|
|
109
|
+
const titleBox = blessed.box({
|
|
110
|
+
top: 1, left: 0, width: '100%', height: 3,
|
|
118
111
|
tags: true,
|
|
119
|
-
|
|
120
|
-
keys: true,
|
|
121
|
-
vi: true,
|
|
112
|
+
content: `\n {bold}OpenAgents{/bold} {gray-fg}v${pkg.version}{/gray-fg} {gray-fg}Local AI Agent Manager{/gray-fg}`,
|
|
122
113
|
});
|
|
123
114
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
top:
|
|
115
|
+
// Column headers for agent table
|
|
116
|
+
const colHeaders = blessed.box({
|
|
117
|
+
top: 4, left: 0, width: '100%', height: 1,
|
|
127
118
|
tags: true,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
119
|
+
style: { bg: 'white', fg: 'black' },
|
|
120
|
+
content: ` ${'NAME'.padEnd(22)} ${'TYPE'.padEnd(14)} ${'STATUS'.padEnd(14)} WORKSPACE`,
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Agent list
|
|
124
|
+
const agentList = blessed.list({
|
|
125
|
+
top: 5, left: 0, width: '100%', height: '50%-2',
|
|
126
|
+
tags: true, keys: true, vi: true, mouse: true,
|
|
127
|
+
border: { type: 'line', left: false, right: false, top: false },
|
|
131
128
|
style: {
|
|
132
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
129
|
+
selected: { bg: 'blue', fg: 'white', bold: true },
|
|
133
130
|
item: { fg: 'white' },
|
|
131
|
+
border: { fg: 'gray' },
|
|
134
132
|
},
|
|
135
133
|
});
|
|
136
134
|
|
|
135
|
+
// Separator
|
|
136
|
+
const separator = blessed.line({
|
|
137
|
+
top: '50%+3', left: 0, width: '100%',
|
|
138
|
+
orientation: 'horizontal',
|
|
139
|
+
style: { fg: 'gray' },
|
|
140
|
+
});
|
|
141
|
+
|
|
137
142
|
// Activity log
|
|
138
|
-
const
|
|
139
|
-
top: '
|
|
140
|
-
border: { type: 'line' },
|
|
141
|
-
label: ' Activity Log ',
|
|
143
|
+
const logLabel = blessed.box({
|
|
144
|
+
top: '50%+4', left: 0, width: '100%', height: 1,
|
|
142
145
|
tags: true,
|
|
143
|
-
|
|
146
|
+
content: ' {bold}Activity{/bold}',
|
|
147
|
+
style: { fg: 'white' },
|
|
144
148
|
});
|
|
145
149
|
|
|
146
150
|
const logContent = blessed.log({
|
|
147
|
-
|
|
148
|
-
top: 0, left: 0, width: '100%-2', height: '100%-2',
|
|
151
|
+
top: '50%+5', left: 0, width: '100%', height: '50%-8',
|
|
149
152
|
tags: true,
|
|
150
153
|
scrollable: true,
|
|
151
154
|
scrollOnInput: true,
|
|
155
|
+
padding: { left: 2 },
|
|
156
|
+
style: { fg: 'gray' },
|
|
152
157
|
});
|
|
153
158
|
|
|
154
|
-
// Footer
|
|
159
|
+
// Footer
|
|
155
160
|
const footer = blessed.box({
|
|
156
|
-
bottom: 0, left: 0, width: '100%', height:
|
|
161
|
+
bottom: 0, left: 0, width: '100%', height: 1,
|
|
157
162
|
tags: true,
|
|
158
163
|
style: { bg: 'blue', fg: 'white' },
|
|
159
164
|
});
|
|
160
165
|
|
|
161
166
|
screen.append(header);
|
|
162
|
-
screen.append(
|
|
163
|
-
screen.append(
|
|
167
|
+
screen.append(titleBox);
|
|
168
|
+
screen.append(colHeaders);
|
|
169
|
+
screen.append(agentList);
|
|
170
|
+
screen.append(separator);
|
|
171
|
+
screen.append(logLabel);
|
|
172
|
+
screen.append(logContent);
|
|
164
173
|
screen.append(footer);
|
|
165
174
|
|
|
166
175
|
// ── State ──
|
|
167
176
|
|
|
168
177
|
let agentRows = [];
|
|
169
|
-
let currentView = 'main';
|
|
178
|
+
let currentView = 'main';
|
|
170
179
|
|
|
171
180
|
function log(msg) {
|
|
172
|
-
const ts = new Date().toLocaleTimeString();
|
|
173
|
-
logContent.log(`{gray-fg}${ts}{/gray-fg}
|
|
181
|
+
const ts = new Date().toLocaleTimeString('en-US', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
|
182
|
+
logContent.log(`{gray-fg}${ts}{/gray-fg} ${msg}`);
|
|
174
183
|
screen.render();
|
|
175
184
|
}
|
|
176
185
|
|
|
@@ -182,27 +191,27 @@ function createTUI() {
|
|
|
182
191
|
} catch { agentRows = []; }
|
|
183
192
|
|
|
184
193
|
const items = agentRows.length ? agentRows.map(r => {
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}) : [' {gray-fg}No agents configured — press {bold}i{/bold} to install one{/gray-fg}'];
|
|
194
|
+
const st = STATE_STYLES[r.state] || STATE_STYLES.stopped;
|
|
195
|
+
const ws = r.workspace || '{gray-fg}—{/gray-fg}';
|
|
196
|
+
return ` {${st.color}-fg}${st.sym}{/${st.color}-fg} ${r.name.padEnd(20)} ${r.type.padEnd(14)} {${st.color}-fg}${st.label.padEnd(14)}{/${st.color}-fg} ${ws}`;
|
|
197
|
+
}) : [' {gray-fg}No agents configured. Press {bold}i{/bold} to install, {bold}n{/bold} to create.{/gray-fg}'];
|
|
190
198
|
|
|
191
199
|
agentList.setItems(items);
|
|
200
|
+
updateHeader();
|
|
192
201
|
updateFooter();
|
|
193
202
|
screen.render();
|
|
194
203
|
}
|
|
195
204
|
|
|
196
|
-
function
|
|
205
|
+
function updateHeader() {
|
|
197
206
|
const pid = connector.getDaemonPid();
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
footer.setContent(
|
|
207
|
+
const dot = pid ? '{green-fg}●{/green-fg}' : '{gray-fg}○{/gray-fg}';
|
|
208
|
+
const state = pid ? 'Daemon running' : 'Daemon idle';
|
|
209
|
+
const count = agentRows.length;
|
|
210
|
+
header.setContent(` ${dot} ${state} │ ${count} agent${count !== 1 ? 's' : ''} configured`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function updateFooter() {
|
|
214
|
+
footer.setContent(' {bold}i{/bold} Install {bold}n{/bold} New {bold}s{/bold} Start {bold}x{/bold} Stop {bold}c{/bold} Connect {bold}u{/bold} Daemon {bold}r{/bold} Refresh {bold}q{/bold} Quit');
|
|
206
215
|
}
|
|
207
216
|
|
|
208
217
|
// ── Install Screen ──
|
|
@@ -222,61 +231,93 @@ function createTUI() {
|
|
|
222
231
|
tags: true,
|
|
223
232
|
});
|
|
224
233
|
|
|
234
|
+
// Header
|
|
225
235
|
const installHeader = blessed.box({
|
|
226
236
|
parent: installBox,
|
|
227
|
-
top: 0, left: 0, width: '100%', height:
|
|
228
|
-
content: '{bold} Install Agent Runtime{/bold} {gray-fg}Enter to install, Escape to go back{/gray-fg}',
|
|
237
|
+
top: 0, left: 0, width: '100%', height: 1,
|
|
229
238
|
tags: true,
|
|
230
|
-
style: { bg: 'blue', fg: 'white' },
|
|
239
|
+
style: { bg: 'blue', fg: 'white', bold: true },
|
|
240
|
+
content: ' Install Agent Runtimes',
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Subtitle
|
|
244
|
+
blessed.box({
|
|
245
|
+
parent: installBox,
|
|
246
|
+
top: 1, left: 0, width: '100%', height: 2,
|
|
247
|
+
tags: true,
|
|
248
|
+
content: '\n {gray-fg}Select a runtime and press Enter to install or update.{/gray-fg}',
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Column headers
|
|
252
|
+
blessed.box({
|
|
253
|
+
parent: installBox,
|
|
254
|
+
top: 3, left: 0, width: '100%', height: 1,
|
|
255
|
+
tags: true,
|
|
256
|
+
style: { bg: 'white', fg: 'black' },
|
|
257
|
+
content: ` ${'AGENT'.padEnd(25)} ${'STATUS'.padEnd(16)} DESCRIPTION`,
|
|
231
258
|
});
|
|
232
259
|
|
|
260
|
+
// Install list
|
|
233
261
|
const installList = blessed.list({
|
|
234
262
|
parent: installBox,
|
|
235
|
-
top:
|
|
236
|
-
border: { type: 'line' },
|
|
263
|
+
top: 4, left: 0, width: '100%', height: '100%-7',
|
|
237
264
|
tags: true, keys: true, vi: true, mouse: true,
|
|
238
265
|
style: {
|
|
239
|
-
selected: { bg: 'blue', fg: 'white' },
|
|
266
|
+
selected: { bg: 'blue', fg: 'white', bold: true },
|
|
240
267
|
item: { fg: 'white' },
|
|
241
268
|
},
|
|
242
269
|
});
|
|
243
270
|
|
|
244
|
-
|
|
271
|
+
// Status bar at bottom
|
|
272
|
+
const statusBar = blessed.box({
|
|
245
273
|
parent: installBox,
|
|
246
|
-
bottom:
|
|
274
|
+
bottom: 1, left: 0, width: '100%', height: 1,
|
|
247
275
|
tags: true,
|
|
248
|
-
|
|
249
|
-
content: ' {bold}Enter{/bold}:Install/Update {bold}Escape{/bold}:Back',
|
|
276
|
+
content: '',
|
|
250
277
|
});
|
|
251
278
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
279
|
+
// Footer
|
|
280
|
+
blessed.box({
|
|
281
|
+
parent: installBox,
|
|
282
|
+
bottom: 0, left: 0, width: '100%', height: 1,
|
|
283
|
+
tags: true,
|
|
284
|
+
style: { bg: 'blue', fg: 'white' },
|
|
285
|
+
content: ' {bold}Enter{/bold} Install/Update {bold}Esc{/bold} Back',
|
|
256
286
|
});
|
|
257
287
|
|
|
258
|
-
|
|
288
|
+
function renderCatalog() {
|
|
289
|
+
const items = catalog.map(e => {
|
|
290
|
+
const st = e.installed
|
|
291
|
+
? '{green-fg}● installed{/green-fg} '
|
|
292
|
+
: '{yellow-fg}○ available{/yellow-fg} ';
|
|
293
|
+
const desc = e.description ? `{gray-fg}${e.description.substring(0, 45)}{/gray-fg}` : '';
|
|
294
|
+
return ` ${e.label.padEnd(25)} ${st} ${desc}`;
|
|
295
|
+
});
|
|
296
|
+
installList.setItems(items);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
renderCatalog();
|
|
259
300
|
installList.focus();
|
|
260
301
|
|
|
261
|
-
installList.on('select', (
|
|
302
|
+
installList.on('select', (_item, idx) => {
|
|
262
303
|
const entry = catalog[idx];
|
|
263
304
|
if (!entry) return;
|
|
264
305
|
|
|
265
306
|
const verb = entry.installed ? 'Update' : 'Install';
|
|
266
|
-
// Show confirm dialog
|
|
267
307
|
const confirm = blessed.question({
|
|
268
308
|
parent: installBox,
|
|
269
309
|
top: 'center', left: 'center',
|
|
270
|
-
width:
|
|
310
|
+
width: 55, height: 7,
|
|
271
311
|
border: { type: 'line' },
|
|
272
312
|
tags: true,
|
|
273
|
-
|
|
313
|
+
label: ` ${verb} `,
|
|
314
|
+
style: { bg: 'black', fg: 'white', border: { fg: 'cyan' }, label: { fg: 'cyan', bold: true } },
|
|
274
315
|
});
|
|
275
316
|
|
|
276
|
-
confirm.ask(`${verb} ${entry.label}
|
|
317
|
+
confirm.ask(`${verb} ${entry.label}? (y/n)`, (_err, ok) => {
|
|
277
318
|
confirm.destroy();
|
|
278
319
|
if (!ok) { installList.focus(); screen.render(); return; }
|
|
279
|
-
doInstall(entry,
|
|
320
|
+
doInstall(entry, statusBar, installList, catalog, renderCatalog);
|
|
280
321
|
});
|
|
281
322
|
});
|
|
282
323
|
|
|
@@ -293,32 +334,27 @@ function createTUI() {
|
|
|
293
334
|
screen.render();
|
|
294
335
|
}
|
|
295
336
|
|
|
296
|
-
function doInstall(entry,
|
|
297
|
-
|
|
298
|
-
parent: installBox,
|
|
299
|
-
bottom: 3, left: 0, width: '100%', height: 1,
|
|
300
|
-
tags: true,
|
|
301
|
-
content: ` Installing ${entry.name}...`,
|
|
302
|
-
});
|
|
337
|
+
function doInstall(entry, statusBar, installList, catalog, renderCatalog) {
|
|
338
|
+
statusBar.setContent(` {cyan-fg}Installing ${entry.name}...{/cyan-fg}`);
|
|
303
339
|
screen.render();
|
|
304
340
|
|
|
305
341
|
const installer = connector.getInstaller();
|
|
306
342
|
const installCmd = installer._resolveInstallCommand(entry.name);
|
|
307
343
|
if (!installCmd) {
|
|
308
|
-
|
|
344
|
+
statusBar.setContent(` {red-fg}✗ No install command for ${entry.name}{/red-fg}`);
|
|
309
345
|
screen.render();
|
|
346
|
+
installList.focus();
|
|
310
347
|
return;
|
|
311
348
|
}
|
|
312
349
|
|
|
313
|
-
log(`
|
|
314
|
-
|
|
350
|
+
log(`{cyan-fg}$ ${installCmd}{/cyan-fg}`);
|
|
351
|
+
statusBar.setContent(` {gray-fg}$ ${installCmd.substring(0, 80)}{/gray-fg}`);
|
|
315
352
|
screen.render();
|
|
316
353
|
|
|
317
354
|
const env = { ...process.env };
|
|
318
355
|
env.npm_config_yes = 'true';
|
|
319
356
|
env.CI = '1';
|
|
320
357
|
|
|
321
|
-
// Enhance PATH
|
|
322
358
|
const extraDirs = getExtraBinDirs();
|
|
323
359
|
if (extraDirs.length) {
|
|
324
360
|
const sep = IS_WINDOWS ? ';' : ':';
|
|
@@ -331,11 +367,15 @@ function createTUI() {
|
|
|
331
367
|
});
|
|
332
368
|
|
|
333
369
|
let lastLine = '';
|
|
370
|
+
let lineCount = 0;
|
|
334
371
|
const onData = (data) => {
|
|
335
372
|
const lines = data.toString().split('\n').filter(l => l.trim());
|
|
336
373
|
for (const line of lines) {
|
|
337
374
|
lastLine = line.trim().substring(0, 100);
|
|
375
|
+
lineCount++;
|
|
338
376
|
log(` ${lastLine}`);
|
|
377
|
+
statusBar.setContent(` {gray-fg}[${lineCount} lines] ${lastLine.substring(0, 70)}{/gray-fg}`);
|
|
378
|
+
screen.render();
|
|
339
379
|
}
|
|
340
380
|
};
|
|
341
381
|
proc.stdout.on('data', onData);
|
|
@@ -343,9 +383,9 @@ function createTUI() {
|
|
|
343
383
|
|
|
344
384
|
proc.on('close', (code) => {
|
|
345
385
|
if (code === 0) {
|
|
346
|
-
|
|
347
|
-
log(`{green-fg}✓ ${entry.name} installed{/green-fg}`);
|
|
348
|
-
// Mark
|
|
386
|
+
statusBar.setContent(` {green-fg}✓ ${entry.name} installed successfully{/green-fg}`);
|
|
387
|
+
log(`{green-fg}✓ ${entry.name} installed successfully{/green-fg}`);
|
|
388
|
+
// Mark installed
|
|
349
389
|
try {
|
|
350
390
|
const markerFile = path.join(connector.config?.configDir || '', 'installed_agents.json');
|
|
351
391
|
let markers = {};
|
|
@@ -353,18 +393,20 @@ function createTUI() {
|
|
|
353
393
|
markers[entry.name] = { installed_at: new Date().toISOString() };
|
|
354
394
|
fs.writeFileSync(markerFile, JSON.stringify(markers, null, 2));
|
|
355
395
|
} catch {}
|
|
356
|
-
// Refresh
|
|
396
|
+
// Refresh
|
|
357
397
|
try {
|
|
358
398
|
const newCatalog = loadCatalog(connector);
|
|
359
|
-
|
|
360
|
-
const
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
399
|
+
for (let i = 0; i < catalog.length; i++) {
|
|
400
|
+
const updated = newCatalog.find(c => c.name === catalog[i].name);
|
|
401
|
+
if (updated) catalog[i] = updated;
|
|
402
|
+
}
|
|
403
|
+
// Also mark the just-installed one
|
|
404
|
+
const idx = catalog.findIndex(c => c.name === entry.name);
|
|
405
|
+
if (idx >= 0) catalog[idx].installed = true;
|
|
406
|
+
renderCatalog();
|
|
365
407
|
} catch {}
|
|
366
408
|
} else {
|
|
367
|
-
|
|
409
|
+
statusBar.setContent(` {red-fg}✗ Failed (exit ${code}): ${lastLine.substring(0, 60)}{/red-fg}`);
|
|
368
410
|
log(`{red-fg}✗ ${entry.name} install failed (exit ${code}){/red-fg}`);
|
|
369
411
|
}
|
|
370
412
|
installList.focus();
|
|
@@ -372,80 +414,94 @@ function createTUI() {
|
|
|
372
414
|
});
|
|
373
415
|
}
|
|
374
416
|
|
|
375
|
-
// ── New Agent ──
|
|
417
|
+
// ── New Agent Dialog ──
|
|
376
418
|
|
|
377
419
|
function showNewAgentDialog() {
|
|
378
|
-
const
|
|
420
|
+
const dialog = blessed.box({
|
|
379
421
|
top: 'center', left: 'center',
|
|
380
|
-
width: 60, height:
|
|
422
|
+
width: 60, height: 16,
|
|
381
423
|
border: { type: 'line' },
|
|
382
424
|
tags: true, keys: true,
|
|
383
|
-
label: '
|
|
384
|
-
style: { border: { fg: '
|
|
425
|
+
label: ' {bold}Create Agent{/bold} ',
|
|
426
|
+
style: { border: { fg: 'cyan' }, label: { fg: 'cyan' } },
|
|
385
427
|
});
|
|
386
428
|
|
|
387
|
-
blessed.text({ parent:
|
|
429
|
+
blessed.text({ parent: dialog, top: 1, left: 2, tags: true, content: '{bold}Name:{/bold}' });
|
|
388
430
|
const nameInput = blessed.textbox({
|
|
389
|
-
parent:
|
|
431
|
+
parent: dialog, top: 2, left: 2, width: 44, height: 3,
|
|
390
432
|
border: { type: 'line' },
|
|
391
433
|
inputOnFocus: true,
|
|
392
|
-
style: { focus: { border: { fg: '
|
|
434
|
+
style: { border: { fg: 'gray' }, focus: { border: { fg: 'cyan' } } },
|
|
393
435
|
});
|
|
394
436
|
|
|
395
|
-
blessed.text({ parent:
|
|
437
|
+
blessed.text({ parent: dialog, top: 5, left: 2, tags: true, content: '{bold}Type:{/bold} {gray-fg}(openclaw, claude, codex, aider, goose){/gray-fg}' });
|
|
396
438
|
const typeInput = blessed.textbox({
|
|
397
|
-
parent:
|
|
439
|
+
parent: dialog, top: 6, left: 2, width: 44, height: 3,
|
|
398
440
|
border: { type: 'line' },
|
|
399
441
|
inputOnFocus: true,
|
|
400
442
|
value: 'openclaw',
|
|
401
|
-
style: { focus: { border: { fg: '
|
|
443
|
+
style: { border: { fg: 'gray' }, focus: { border: { fg: 'cyan' } } },
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
const statusLabel = blessed.text({ parent: dialog, top: 10, left: 2, tags: true, content: '' });
|
|
447
|
+
|
|
448
|
+
const createBtn = blessed.button({
|
|
449
|
+
parent: dialog, top: 12, left: 2, width: 14, height: 1,
|
|
450
|
+
content: ' Create ', tags: true, mouse: true,
|
|
451
|
+
style: { bg: 'blue', fg: 'white', focus: { bg: 'cyan' }, hover: { bg: 'cyan' } },
|
|
402
452
|
});
|
|
403
453
|
|
|
404
|
-
blessed.button({
|
|
405
|
-
parent:
|
|
406
|
-
content: '
|
|
407
|
-
style: { bg: '
|
|
408
|
-
|
|
409
|
-
|
|
454
|
+
const cancelBtn = blessed.button({
|
|
455
|
+
parent: dialog, top: 12, left: 18, width: 14, height: 1,
|
|
456
|
+
content: ' Cancel ', tags: true, mouse: true,
|
|
457
|
+
style: { bg: 'gray', fg: 'white', focus: { bg: 'red' }, hover: { bg: 'red' } },
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
function doCreate() {
|
|
410
461
|
const name = nameInput.getValue().trim();
|
|
411
462
|
const type = typeInput.getValue().trim();
|
|
412
|
-
if (!name
|
|
463
|
+
if (!name) { statusLabel.setContent('{red-fg}Name is required{/red-fg}'); screen.render(); return; }
|
|
464
|
+
if (!type) { statusLabel.setContent('{red-fg}Type is required{/red-fg}'); screen.render(); return; }
|
|
413
465
|
try {
|
|
414
466
|
connector.createAgent(name, type);
|
|
415
467
|
log(`{green-fg}✓ Agent '${name}' (${type}) created{/green-fg}`);
|
|
468
|
+
screen.remove(dialog);
|
|
469
|
+
dialog.destroy();
|
|
470
|
+
agentList.focus();
|
|
471
|
+
refreshAgentTable();
|
|
416
472
|
} catch (e) {
|
|
417
|
-
|
|
473
|
+
statusLabel.setContent(`{red-fg}${e.message}{/red-fg}`);
|
|
474
|
+
screen.render();
|
|
418
475
|
}
|
|
419
|
-
|
|
420
|
-
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
createBtn.on('press', doCreate);
|
|
479
|
+
cancelBtn.on('press', () => {
|
|
480
|
+
screen.remove(dialog);
|
|
481
|
+
dialog.destroy();
|
|
421
482
|
agentList.focus();
|
|
422
|
-
|
|
483
|
+
screen.render();
|
|
423
484
|
});
|
|
424
485
|
|
|
425
|
-
|
|
426
|
-
screen.remove(
|
|
427
|
-
|
|
486
|
+
dialog.key('escape', () => {
|
|
487
|
+
screen.remove(dialog);
|
|
488
|
+
dialog.destroy();
|
|
428
489
|
agentList.focus();
|
|
429
490
|
screen.render();
|
|
430
491
|
});
|
|
431
492
|
|
|
432
|
-
screen.append(
|
|
493
|
+
screen.append(dialog);
|
|
433
494
|
nameInput.focus();
|
|
434
495
|
screen.render();
|
|
435
496
|
}
|
|
436
497
|
|
|
437
498
|
// ── Keybindings ──
|
|
438
499
|
|
|
439
|
-
screen.key('q', () => process.exit(0));
|
|
500
|
+
screen.key('q', () => { if (currentView === 'main') process.exit(0); });
|
|
440
501
|
screen.key('C-c', () => process.exit(0));
|
|
441
502
|
|
|
442
|
-
screen.key('i', () => {
|
|
443
|
-
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
screen.key('n', () => {
|
|
447
|
-
if (currentView === 'main') showNewAgentDialog();
|
|
448
|
-
});
|
|
503
|
+
screen.key('i', () => { if (currentView === 'main') showInstallScreen(); });
|
|
504
|
+
screen.key('n', () => { if (currentView === 'main') showNewAgentDialog(); });
|
|
449
505
|
|
|
450
506
|
screen.key('r', () => {
|
|
451
507
|
if (currentView === 'main') {
|
|
@@ -458,13 +514,11 @@ function createTUI() {
|
|
|
458
514
|
if (currentView !== 'main') return;
|
|
459
515
|
const idx = agentList.selected;
|
|
460
516
|
const agent = agentRows[idx];
|
|
461
|
-
if (!agent) return;
|
|
517
|
+
if (!agent) { log('{yellow-fg}No agent selected{/yellow-fg}'); return; }
|
|
462
518
|
try {
|
|
463
519
|
connector.startAgent(agent.name);
|
|
464
520
|
log(`Starting ${agent.name}...`);
|
|
465
|
-
} catch (e) {
|
|
466
|
-
log(`{red-fg}Error: ${e.message}{/red-fg}`);
|
|
467
|
-
}
|
|
521
|
+
} catch (e) { log(`{red-fg}Error: ${e.message}{/red-fg}`); }
|
|
468
522
|
setTimeout(refreshAgentTable, 2000);
|
|
469
523
|
});
|
|
470
524
|
|
|
@@ -472,13 +526,11 @@ function createTUI() {
|
|
|
472
526
|
if (currentView !== 'main') return;
|
|
473
527
|
const idx = agentList.selected;
|
|
474
528
|
const agent = agentRows[idx];
|
|
475
|
-
if (!agent) return;
|
|
529
|
+
if (!agent) { log('{yellow-fg}No agent selected{/yellow-fg}'); return; }
|
|
476
530
|
try {
|
|
477
531
|
connector.stopAgent(agent.name);
|
|
478
532
|
log(`Stopped ${agent.name}`);
|
|
479
|
-
} catch (e) {
|
|
480
|
-
log(`{red-fg}Error: ${e.message}{/red-fg}`);
|
|
481
|
-
}
|
|
533
|
+
} catch (e) { log(`{red-fg}Error: ${e.message}{/red-fg}`); }
|
|
482
534
|
setTimeout(refreshAgentTable, 1000);
|
|
483
535
|
});
|
|
484
536
|
|
|
@@ -490,7 +542,7 @@ function createTUI() {
|
|
|
490
542
|
log('Daemon stopped');
|
|
491
543
|
} else {
|
|
492
544
|
connector.startDaemon();
|
|
493
|
-
log('Daemon
|
|
545
|
+
log('Daemon starting...');
|
|
494
546
|
}
|
|
495
547
|
setTimeout(refreshAgentTable, 2000);
|
|
496
548
|
});
|
|
@@ -499,22 +551,19 @@ function createTUI() {
|
|
|
499
551
|
if (currentView !== 'main') return;
|
|
500
552
|
const idx = agentList.selected;
|
|
501
553
|
const agent = agentRows[idx];
|
|
502
|
-
if (!agent) return;
|
|
503
|
-
|
|
504
|
-
log(`Connect: use 'agent-connector connect ${agent.name} <workspace-slug>' from terminal`);
|
|
554
|
+
if (!agent) { log('{yellow-fg}No agent selected{/yellow-fg}'); return; }
|
|
555
|
+
log(`{gray-fg}Use: openagents connect ${agent.name} <workspace-token>{/gray-fg}`);
|
|
505
556
|
});
|
|
506
557
|
|
|
507
558
|
screen.key('d', () => {
|
|
508
559
|
if (currentView !== 'main') return;
|
|
509
560
|
const idx = agentList.selected;
|
|
510
561
|
const agent = agentRows[idx];
|
|
511
|
-
if (!agent) return;
|
|
562
|
+
if (!agent) { log('{yellow-fg}No agent selected{/yellow-fg}'); return; }
|
|
512
563
|
try {
|
|
513
564
|
connector.disconnectAgent(agent.name);
|
|
514
565
|
log(`Disconnected ${agent.name}`);
|
|
515
|
-
} catch (e) {
|
|
516
|
-
log(`{red-fg}Error: ${e.message}{/red-fg}`);
|
|
517
|
-
}
|
|
566
|
+
} catch (e) { log(`{red-fg}Error: ${e.message}{/red-fg}`); }
|
|
518
567
|
refreshAgentTable();
|
|
519
568
|
});
|
|
520
569
|
|
|
@@ -522,11 +571,9 @@ function createTUI() {
|
|
|
522
571
|
|
|
523
572
|
agentList.focus();
|
|
524
573
|
refreshAgentTable();
|
|
525
|
-
log('
|
|
574
|
+
log('Welcome to {bold}OpenAgents{/bold}. Press {bold}i{/bold} to install agents, {bold}n{/bold} to create one.');
|
|
526
575
|
|
|
527
|
-
// Auto-refresh every 5 seconds
|
|
528
576
|
setInterval(refreshAgentTable, 5000);
|
|
529
|
-
|
|
530
577
|
screen.render();
|
|
531
578
|
return screen;
|
|
532
579
|
}
|