@openagents-org/agent-launcher 0.1.11 → 0.1.13

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": "@openagents-org/agent-launcher",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
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/registry.json CHANGED
@@ -88,6 +88,8 @@
88
88
  ],
89
89
  "install": {
90
90
  "binary": "code",
91
+ "verify": "code --list-extensions 2>/dev/null | grep -qi claude-dev",
92
+ "verify_win": "code --list-extensions 2>nul | findstr /i claude-dev",
91
93
  "macos": "code --install-extension saoudrizwan.claude-dev",
92
94
  "linux": "code --install-extension saoudrizwan.claude-dev",
93
95
  "windows": "code --install-extension saoudrizwan.claude-dev"
@@ -166,6 +168,8 @@
166
168
  ],
167
169
  "install": {
168
170
  "binary": "gh",
171
+ "verify": "gh copilot --version >/dev/null 2>&1",
172
+ "verify_win": "gh copilot --version >nul 2>nul",
169
173
  "macos": "gh extension install github/gh-copilot",
170
174
  "linux": "gh extension install github/gh-copilot",
171
175
  "windows": "gh extension install github/gh-copilot"
@@ -185,6 +189,8 @@
185
189
  "builtin": true,
186
190
  "install": {
187
191
  "binary": "cursor",
192
+ "verify": "cursor --version 2>/dev/null | grep -qi cursor",
193
+ "verify_win": "cursor --version 2>nul | findstr /i cursor",
188
194
  "requires": [
189
195
  null
190
196
  ],
package/src/installer.js CHANGED
@@ -35,6 +35,18 @@ class Installer {
35
35
  * Checks binary on PATH first, then marker files.
36
36
  */
37
37
  isInstalled(agentType) {
38
+ // Use verify command if available for accurate detection
39
+ const entry = this.registry.getEntry(agentType);
40
+ const IS_WINDOWS = process.platform === 'win32';
41
+ const verifyCmd = entry && entry.install
42
+ ? (IS_WINDOWS ? entry.install.verify_win : entry.install.verify)
43
+ : null;
44
+ if (verifyCmd) {
45
+ try {
46
+ require('child_process').execSync(verifyCmd, { stdio: 'ignore', timeout: 5000 });
47
+ return true;
48
+ } catch { return false; }
49
+ }
38
50
  if (this._whichBinary(agentType)) return true;
39
51
  if (this._hasMarker(agentType)) {
40
52
  // Marker exists but binary not found — stale marker, clean it up
package/src/tui.js CHANGED
@@ -93,10 +93,19 @@ function loadAgentRows(connector) {
93
93
  }
94
94
 
95
95
  function loadCatalog(connector) {
96
+ const { execSync } = require('child_process');
96
97
  const entries = connector.registry.getCatalogSync();
97
98
  return entries.map(e => {
98
99
  let installed = false;
99
- try { const { whichBinary } = require('./paths'); installed = !!whichBinary((e.install && e.install.binary) || e.name); } catch {}
100
+
101
+ // If a verify command exists, use it for accurate detection
102
+ const verifyCmd = IS_WINDOWS ? (e.install && e.install.verify_win) : (e.install && e.install.verify);
103
+ if (verifyCmd) {
104
+ try { execSync(verifyCmd, { stdio: 'ignore', timeout: 5000 }); installed = true; } catch {}
105
+ } else {
106
+ try { const { whichBinary } = require('./paths'); installed = !!whichBinary((e.install && e.install.binary) || e.name); } catch {}
107
+ }
108
+
100
109
  if (!installed) {
101
110
  try {
102
111
  const f = path.join(connector._configDir, 'installed_agents.json');
@@ -225,14 +234,6 @@ function createTUI() {
225
234
  }
226
235
 
227
236
  // ── Footer rendering (context-aware, clickable) ──
228
- // Maps footer action labels to the key they simulate
229
- const footerKeyMap = {
230
- 'Install': 'i', 'New': 'n', 'Start': 's', 'Stop': 'x',
231
- 'Configure': 'e', 'Connect': 'c', 'Disconnect': 'd',
232
- 'Workspace': 'w', 'Remove': 'delete', 'Daemon': 'u',
233
- 'Refresh': 'r', 'Quit': 'q',
234
- };
235
-
236
237
  function updateFooter() {
237
238
  const agent = agentRows[agentList.selected];
238
239
  const items = [];
@@ -278,9 +279,9 @@ function createTUI() {
278
279
  content: `{cyan-fg}${item.key}{/cyan-fg} ${item.label}`,
279
280
  style: { bg: COLORS.footerBg, fg: COLORS.footerFg, hover: { bg: 'cyan', fg: 'black' } },
280
281
  });
281
- const actionKey = footerKeyMap[item.label];
282
- if (actionKey) {
283
- btn.on('click', () => { screen.emit('keypress', null, { full: actionKey, name: actionKey }); });
282
+ const action = footerActions[item.label];
283
+ if (action) {
284
+ btn.on('click', () => action());
284
285
  }
285
286
  footerButtons.push(btn);
286
287
  left += text.length + 2;
@@ -1221,115 +1222,112 @@ function createTUI() {
1221
1222
  // Key bindings
1222
1223
  // ────────────────────────────────────────────────────────────────────────
1223
1224
 
1224
- screen.key('q', () => { if (currentView === 'main') process.exit(0); });
1225
- screen.key('C-c', () => process.exit(0));
1226
-
1227
- screen.key('i', () => { if (currentView === 'main') showInstallScreen(); });
1228
-
1229
- screen.key('n', () => {
1230
- if (currentView !== 'main') return;
1231
- showSelectAgentTypeScreen((type) => {
1232
- showStartAgentScreen(type, (result) => {
1233
- try {
1234
- connector.addAgent({ name: result.name, type: result.type, path: result.path });
1235
- log(`{green-fg}\u2713{/green-fg} Created agent {cyan-fg}${result.name}{/cyan-fg} (${result.type})`);
1236
-
1237
- // Start daemon if not running
1238
- const pid = connector.getDaemonPid();
1239
- if (!pid) {
1240
- connector.startDaemon();
1241
- log('{green-fg}\u2713{/green-fg} Daemon starting...');
1242
- } else {
1243
- signalDaemonReload();
1225
+ // ── Action handlers (shared by keys and clickable footer) ──
1226
+ const footerActions = {
1227
+ Install() { if (currentView === 'main') showInstallScreen(); },
1228
+ New() {
1229
+ if (currentView !== 'main') return;
1230
+ showSelectAgentTypeScreen((type) => {
1231
+ showStartAgentScreen(type, (result) => {
1232
+ try {
1233
+ connector.addAgent({ name: result.name, type: result.type, path: result.path });
1234
+ log(`{green-fg}\u2713{/green-fg} Created agent {cyan-fg}${result.name}{/cyan-fg} (${result.type})`);
1235
+ const pid = connector.getDaemonPid();
1236
+ if (!pid) {
1237
+ connector.startDaemon();
1238
+ log('{green-fg}\u2713{/green-fg} Daemon starting...');
1239
+ } else {
1240
+ signalDaemonReload();
1241
+ }
1242
+ } catch (e) {
1243
+ log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1244
1244
  }
1245
- } catch (e) {
1246
- log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1247
- }
1248
- setTimeout(refreshAgentTable, 3000);
1245
+ setTimeout(refreshAgentTable, 3000);
1246
+ });
1249
1247
  });
1250
- });
1251
- });
1252
-
1253
- screen.key('r', () => {
1254
- if (currentView === 'main') {
1255
- refreshAgentTable();
1256
- log('{green-fg}\u2713{/green-fg} Refreshed');
1257
- }
1258
- });
1259
-
1260
- screen.key('s', () => {
1261
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1262
- const a = agentRows[agentList.selected];
1263
- if (!a.configured) return;
1264
- doStart(a.name);
1265
- });
1266
-
1267
- screen.key('x', () => {
1268
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1269
- const a = agentRows[agentList.selected];
1270
- if (!a.configured) return;
1271
- doStop(a.name);
1272
- });
1273
-
1274
- screen.key('u', () => {
1275
- if (currentView !== 'main') return;
1276
- const pid = connector.getDaemonPid();
1277
- if (pid) {
1278
- showConfirmDialog('Stop daemon? This will disconnect ALL agents.', (yes) => {
1279
- if (!yes) { log('{gray-fg}Cancelled{/gray-fg}'); return; }
1248
+ },
1249
+ Start() {
1250
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1251
+ const a = agentRows[agentList.selected];
1252
+ if (a.configured) doStart(a.name);
1253
+ },
1254
+ Stop() {
1255
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1256
+ const a = agentRows[agentList.selected];
1257
+ if (a.configured) doStop(a.name);
1258
+ },
1259
+ Configure() {
1260
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1261
+ const a = agentRows[agentList.selected];
1262
+ if (a.configured) showConfigureScreen(a);
1263
+ },
1264
+ Connect() {
1265
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1266
+ const a = agentRows[agentList.selected];
1267
+ if (a.configured && !a.workspace) showConnectWorkspaceScreen(a.name);
1268
+ },
1269
+ Disconnect() {
1270
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1271
+ const a = agentRows[agentList.selected];
1272
+ if (a.configured && a.workspace) doDisconnect(a.name);
1273
+ },
1274
+ Workspace() {
1275
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1276
+ const a = agentRows[agentList.selected];
1277
+ if (a.configured && a.workspace) doOpenWorkspace(a);
1278
+ },
1279
+ Remove() {
1280
+ if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1281
+ const a = agentRows[agentList.selected];
1282
+ if (a.configured) doRemove(a.name);
1283
+ },
1284
+ Daemon() {
1285
+ if (currentView !== 'main') return;
1286
+ const pid = connector.getDaemonPid();
1287
+ if (pid) {
1288
+ showConfirmDialog('Stop daemon? This will disconnect ALL agents.', (yes) => {
1289
+ if (!yes) { log('{gray-fg}Cancelled{/gray-fg}'); return; }
1290
+ try {
1291
+ connector.stopDaemon();
1292
+ log('{green-fg}\u2713{/green-fg} Daemon stopped');
1293
+ } catch (e) {
1294
+ log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1295
+ }
1296
+ setTimeout(refreshAgentTable, 1000);
1297
+ });
1298
+ } else {
1280
1299
  try {
1281
- connector.stopDaemon();
1282
- log('{green-fg}\u2713{/green-fg} Daemon stopped');
1300
+ connector.startDaemon();
1301
+ log('{green-fg}\u2713{/green-fg} Daemon starting...');
1283
1302
  } catch (e) {
1284
1303
  log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1285
1304
  }
1286
- setTimeout(refreshAgentTable, 1000);
1287
- });
1288
- } else {
1289
- try {
1290
- connector.startDaemon();
1291
- log('{green-fg}\u2713{/green-fg} Daemon starting...');
1292
- } catch (e) {
1293
- log(`{red-fg}\u2717{/red-fg} ${e.message}`);
1305
+ setTimeout(refreshAgentTable, 3000);
1294
1306
  }
1295
- setTimeout(refreshAgentTable, 3000);
1296
- }
1297
- });
1298
-
1299
- screen.key('c', () => {
1300
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1301
- const a = agentRows[agentList.selected];
1302
- if (!a.configured || a.workspace) return;
1303
- showConnectWorkspaceScreen(a.name);
1304
- });
1305
-
1306
- screen.key('d', () => {
1307
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1308
- const a = agentRows[agentList.selected];
1309
- if (!a.configured || !a.workspace) return;
1310
- doDisconnect(a.name);
1311
- });
1312
-
1313
- screen.key('w', () => {
1314
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1315
- const a = agentRows[agentList.selected];
1316
- if (!a.configured || !a.workspace) return;
1317
- doOpenWorkspace(a);
1318
- });
1319
-
1320
- screen.key('e', () => {
1321
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1322
- const a = agentRows[agentList.selected];
1323
- if (!a.configured) return;
1324
- showConfigureScreen(a);
1325
- });
1307
+ },
1308
+ Refresh() {
1309
+ if (currentView === 'main') {
1310
+ refreshAgentTable();
1311
+ log('{green-fg}\u2713{/green-fg} Refreshed');
1312
+ }
1313
+ },
1314
+ Quit() { if (currentView === 'main') process.exit(0); },
1315
+ };
1326
1316
 
1327
- screen.key('delete', () => {
1328
- if (currentView !== 'main' || !agentRows[agentList.selected]) return;
1329
- const a = agentRows[agentList.selected];
1330
- if (!a.configured) return;
1331
- doRemove(a.name);
1332
- });
1317
+ // Bind keyboard shortcuts
1318
+ screen.key('q', footerActions.Quit);
1319
+ screen.key('C-c', () => process.exit(0));
1320
+ screen.key('i', footerActions.Install);
1321
+ screen.key('n', footerActions.New);
1322
+ screen.key('r', footerActions.Refresh);
1323
+ screen.key('s', footerActions.Start);
1324
+ screen.key('x', footerActions.Stop);
1325
+ screen.key('u', footerActions.Daemon);
1326
+ screen.key('c', footerActions.Connect);
1327
+ screen.key('d', footerActions.Disconnect);
1328
+ screen.key('w', footerActions.Workspace);
1329
+ screen.key('e', footerActions.Configure);
1330
+ screen.key('delete', footerActions.Remove);
1333
1331
 
1334
1332
  // ── Init ──
1335
1333
  agentList.focus();