@openagents-org/agent-launcher 0.1.10 → 0.1.12

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tui.js +141 -119
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagents-org/agent-launcher",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "OpenAgents Launcher — install, configure, and run AI coding agents from your terminal",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/tui.js CHANGED
@@ -203,18 +203,19 @@ function createTUI() {
203
203
  style: { fg: 'white' },
204
204
  });
205
205
 
206
- // ── Footer ──
207
- const footer = blessed.box({
206
+ // ── Footer (clickable buttons) ──
207
+ const footerBar = blessed.box({
208
208
  bottom: 0, left: 0, width: '100%', height: 1,
209
209
  tags: true,
210
210
  style: { bg: COLORS.footerBg, fg: COLORS.footerFg },
211
211
  });
212
+ let footerButtons = [];
212
213
 
213
214
  screen.append(header);
214
215
  screen.append(titleBox);
215
216
  screen.append(agentPanel);
216
217
  screen.append(logPanel);
217
- screen.append(footer);
218
+ screen.append(footerBar);
218
219
 
219
220
  // ── Log helper ──
220
221
  function log(msg) {
@@ -223,36 +224,60 @@ function createTUI() {
223
224
  screen.render();
224
225
  }
225
226
 
226
- // ── Footer rendering (context-aware) ──
227
+ // ── Footer rendering (context-aware, clickable) ──
227
228
  function updateFooter() {
228
229
  const agent = agentRows[agentList.selected];
229
- const parts = [];
230
+ const items = [];
230
231
 
231
- parts.push('{cyan-fg}i{/cyan-fg} Install');
232
- parts.push('{cyan-fg}n{/cyan-fg} New');
232
+ items.push({ key: 'i', label: 'Install' });
233
+ items.push({ key: 'n', label: 'New' });
233
234
 
234
235
  if (agent && agent.configured) {
235
236
  const isRunning = ['running', 'online', 'starting', 'reconnecting'].includes(agent.state);
236
237
  const isStopped = ['stopped', 'error'].includes(agent.state);
237
238
 
238
- if (isStopped) parts.push('{cyan-fg}s{/cyan-fg} Start');
239
- if (isRunning) parts.push('{cyan-fg}x{/cyan-fg} Stop');
239
+ if (isStopped) items.push({ key: 's', label: 'Start' });
240
+ if (isRunning) items.push({ key: 'x', label: 'Stop' });
240
241
 
241
242
  const envFields = connector.registry.getEnvFields(agent.type);
242
- if (envFields && envFields.length > 0) parts.push('{cyan-fg}e{/cyan-fg} Configure');
243
+ if (envFields && envFields.length > 0) items.push({ key: 'e', label: 'Configure' });
243
244
 
244
- if (!agent.workspace) parts.push('{cyan-fg}c{/cyan-fg} Connect');
245
- if (agent.workspace) parts.push('{cyan-fg}d{/cyan-fg} Disconnect');
246
- if (agent.workspace) parts.push('{cyan-fg}w{/cyan-fg} Workspace');
245
+ if (!agent.workspace) items.push({ key: 'c', label: 'Connect' });
246
+ if (agent.workspace) items.push({ key: 'd', label: 'Disconnect' });
247
+ if (agent.workspace) items.push({ key: 'w', label: 'Workspace' });
247
248
 
248
- parts.push('{cyan-fg}Del{/cyan-fg} Remove');
249
+ items.push({ key: 'Del', label: 'Remove' });
249
250
  }
250
251
 
251
- parts.push('{cyan-fg}u{/cyan-fg} Daemon');
252
- parts.push('{cyan-fg}r{/cyan-fg} Refresh');
253
- parts.push('{cyan-fg}q{/cyan-fg} Quit');
252
+ items.push({ key: 'u', label: 'Daemon' });
253
+ items.push({ key: 'r', label: 'Refresh' });
254
+ items.push({ key: 'q', label: 'Quit' });
255
+
256
+ // Remove old buttons
257
+ for (const btn of footerButtons) { footerBar.remove(btn); btn.destroy(); }
258
+ footerButtons = [];
259
+
260
+ let left = 1;
261
+ for (const item of items) {
262
+ const text = `${item.key} ${item.label}`;
263
+ const btn = blessed.box({
264
+ parent: footerBar,
265
+ left, top: 0, height: 1,
266
+ width: text.length + 2,
267
+ tags: true,
268
+ mouse: true,
269
+ clickable: true,
270
+ content: `{cyan-fg}${item.key}{/cyan-fg} ${item.label}`,
271
+ style: { bg: COLORS.footerBg, fg: COLORS.footerFg, hover: { bg: 'cyan', fg: 'black' } },
272
+ });
273
+ const action = footerActions[item.label];
274
+ if (action) {
275
+ btn.on('click', () => action());
276
+ }
277
+ footerButtons.push(btn);
278
+ left += text.length + 2;
279
+ }
254
280
 
255
- footer.setContent(' ' + parts.join(' '));
256
281
  screen.render();
257
282
  }
258
283
 
@@ -1188,115 +1213,112 @@ function createTUI() {
1188
1213
  // Key bindings
1189
1214
  // ────────────────────────────────────────────────────────────────────────
1190
1215
 
1191
- screen.key('q', () => { if (currentView === 'main') process.exit(0); });
1192
- screen.key('C-c', () => process.exit(0));
1193
-
1194
- screen.key('i', () => { if (currentView === 'main') showInstallScreen(); });
1195
-
1196
- screen.key('n', () => {
1197
- if (currentView !== 'main') return;
1198
- showSelectAgentTypeScreen((type) => {
1199
- showStartAgentScreen(type, (result) => {
1200
- try {
1201
- connector.addAgent({ name: result.name, type: result.type, path: result.path });
1202
- log(`{green-fg}\u2713{/green-fg} Created agent {cyan-fg}${result.name}{/cyan-fg} (${result.type})`);
1203
-
1204
- // Start daemon if not running
1205
- const pid = connector.getDaemonPid();
1206
- if (!pid) {
1207
- connector.startDaemon();
1208
- log('{green-fg}\u2713{/green-fg} Daemon starting...');
1209
- } else {
1210
- signalDaemonReload();
1216
+ // ── Action handlers (shared by keys and clickable footer) ──
1217
+ const footerActions = {
1218
+ Install() { if (currentView === 'main') showInstallScreen(); },
1219
+ New() {
1220
+ if (currentView !== 'main') return;
1221
+ showSelectAgentTypeScreen((type) => {
1222
+ showStartAgentScreen(type, (result) => {
1223
+ try {
1224
+ connector.addAgent({ name: result.name, type: result.type, path: result.path });
1225
+ log(`{green-fg}\u2713{/green-fg} Created agent {cyan-fg}${result.name}{/cyan-fg} (${result.type})`);
1226
+ const pid = connector.getDaemonPid();
1227
+ if (!pid) {
1228
+ connector.startDaemon();
1229
+ log('{green-fg}\u2713{/green-fg} Daemon starting...');
1230
+ } else {
1231
+ signalDaemonReload();
1232
+ }
1233
+ } catch (e) {
1234
+ log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1211
1235
  }
1212
- } catch (e) {
1213
- log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1214
- }
1215
- setTimeout(refreshAgentTable, 3000);
1236
+ setTimeout(refreshAgentTable, 3000);
1237
+ });
1216
1238
  });
1217
- });
1218
- });
1219
-
1220
- screen.key('r', () => {
1221
- if (currentView === 'main') {
1222
- refreshAgentTable();
1223
- log('{green-fg}\u2713{/green-fg} Refreshed');
1224
- }
1225
- });
1226
-
1227
- screen.key('s', () => {
1228
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1229
- const a = agentRows[agentList.selected];
1230
- if (!a.configured) return;
1231
- doStart(a.name);
1232
- });
1233
-
1234
- screen.key('x', () => {
1235
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1236
- const a = agentRows[agentList.selected];
1237
- if (!a.configured) return;
1238
- doStop(a.name);
1239
- });
1240
-
1241
- screen.key('u', () => {
1242
- if (currentView !== 'main') return;
1243
- const pid = connector.getDaemonPid();
1244
- if (pid) {
1245
- showConfirmDialog('Stop daemon? This will disconnect ALL agents.', (yes) => {
1246
- if (!yes) { log('{gray-fg}Cancelled{/gray-fg}'); return; }
1239
+ },
1240
+ Start() {
1241
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1242
+ const a = agentRows[agentList.selected];
1243
+ if (a.configured) doStart(a.name);
1244
+ },
1245
+ Stop() {
1246
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1247
+ const a = agentRows[agentList.selected];
1248
+ if (a.configured) doStop(a.name);
1249
+ },
1250
+ Configure() {
1251
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1252
+ const a = agentRows[agentList.selected];
1253
+ if (a.configured) showConfigureScreen(a);
1254
+ },
1255
+ Connect() {
1256
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1257
+ const a = agentRows[agentList.selected];
1258
+ if (a.configured && !a.workspace) showConnectWorkspaceScreen(a.name);
1259
+ },
1260
+ Disconnect() {
1261
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1262
+ const a = agentRows[agentList.selected];
1263
+ if (a.configured && a.workspace) doDisconnect(a.name);
1264
+ },
1265
+ Workspace() {
1266
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1267
+ const a = agentRows[agentList.selected];
1268
+ if (a.configured && a.workspace) doOpenWorkspace(a);
1269
+ },
1270
+ Remove() {
1271
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1272
+ const a = agentRows[agentList.selected];
1273
+ if (a.configured) doRemove(a.name);
1274
+ },
1275
+ Daemon() {
1276
+ if (currentView !== 'main') return;
1277
+ const pid = connector.getDaemonPid();
1278
+ if (pid) {
1279
+ showConfirmDialog('Stop daemon? This will disconnect ALL agents.', (yes) => {
1280
+ if (!yes) { log('{gray-fg}Cancelled{/gray-fg}'); return; }
1281
+ try {
1282
+ connector.stopDaemon();
1283
+ log('{green-fg}\u2713{/green-fg} Daemon stopped');
1284
+ } catch (e) {
1285
+ log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1286
+ }
1287
+ setTimeout(refreshAgentTable, 1000);
1288
+ });
1289
+ } else {
1247
1290
  try {
1248
- connector.stopDaemon();
1249
- log('{green-fg}\u2713{/green-fg} Daemon stopped');
1291
+ connector.startDaemon();
1292
+ log('{green-fg}\u2713{/green-fg} Daemon starting...');
1250
1293
  } catch (e) {
1251
1294
  log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1252
1295
  }
1253
- setTimeout(refreshAgentTable, 1000);
1254
- });
1255
- } else {
1256
- try {
1257
- connector.startDaemon();
1258
- log('{green-fg}\u2713{/green-fg} Daemon starting...');
1259
- } catch (e) {
1260
- log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1296
+ setTimeout(refreshAgentTable, 3000);
1261
1297
  }
1262
- setTimeout(refreshAgentTable, 3000);
1263
- }
1264
- });
1265
-
1266
- screen.key('c', () => {
1267
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1268
- const a = agentRows[agentList.selected];
1269
- if (!a.configured || a.workspace) return;
1270
- showConnectWorkspaceScreen(a.name);
1271
- });
1272
-
1273
- screen.key('d', () => {
1274
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1275
- const a = agentRows[agentList.selected];
1276
- if (!a.configured || !a.workspace) return;
1277
- doDisconnect(a.name);
1278
- });
1279
-
1280
- screen.key('w', () => {
1281
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1282
- const a = agentRows[agentList.selected];
1283
- if (!a.configured || !a.workspace) return;
1284
- doOpenWorkspace(a);
1285
- });
1286
-
1287
- screen.key('e', () => {
1288
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1289
- const a = agentRows[agentList.selected];
1290
- if (!a.configured) return;
1291
- showConfigureScreen(a);
1292
- });
1298
+ },
1299
+ Refresh() {
1300
+ if (currentView === 'main') {
1301
+ refreshAgentTable();
1302
+ log('{green-fg}\u2713{/green-fg} Refreshed');
1303
+ }
1304
+ },
1305
+ Quit() { if (currentView === 'main') process.exit(0); },
1306
+ };
1293
1307
 
1294
- screen.key('delete', () => {
1295
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1296
- const a = agentRows[agentList.selected];
1297
- if (!a.configured) return;
1298
- doRemove(a.name);
1299
- });
1308
+ // Bind keyboard shortcuts
1309
+ screen.key('q', footerActions.Quit);
1310
+ screen.key('C-c', () => process.exit(0));
1311
+ screen.key('i', footerActions.Install);
1312
+ screen.key('n', footerActions.New);
1313
+ screen.key('r', footerActions.Refresh);
1314
+ screen.key('s', footerActions.Start);
1315
+ screen.key('x', footerActions.Stop);
1316
+ screen.key('u', footerActions.Daemon);
1317
+ screen.key('c', footerActions.Connect);
1318
+ screen.key('d', footerActions.Disconnect);
1319
+ screen.key('w', footerActions.Workspace);
1320
+ screen.key('e', footerActions.Configure);
1321
+ screen.key('delete', footerActions.Remove);
1300
1322
 
1301
1323
  // ── Init ──
1302
1324
  agentList.focus();