@openagents-org/agent-launcher 0.1.15 → 0.1.17

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.15",
3
+ "version": "0.1.17",
4
4
  "description": "OpenAgents Launcher — install, configure, and run AI coding agents from your terminal",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -31,13 +31,14 @@ class BaseAdapter {
31
31
  * @param {string} opts.agentName
32
32
  * @param {string} [opts.endpoint]
33
33
  */
34
- constructor({ workspaceId, channelName, token, agentName, endpoint, agentEnv }) {
34
+ constructor({ workspaceId, channelName, token, agentName, endpoint, agentEnv, agentType }) {
35
35
  this.workspaceId = workspaceId;
36
36
  this.channelName = channelName;
37
37
  this.token = token;
38
38
  this.agentName = agentName;
39
39
  this.endpoint = endpoint || DEFAULT_ENDPOINT;
40
40
  this.agentEnv = agentEnv || process.env;
41
+ this.agentType = agentType;
41
42
  this.client = new WorkspaceClient(this.endpoint);
42
43
  this._lastEventId = null;
43
44
  this._running = false;
@@ -60,6 +61,18 @@ class BaseAdapter {
60
61
 
61
62
  async run() {
62
63
  this._running = true;
64
+
65
+ // Announce agent to workspace
66
+ try {
67
+ await this.client.joinNetwork(this.agentName, this.token, {
68
+ network: this.workspaceId,
69
+ agentType: this.agentType || 'agent',
70
+ });
71
+ this._log(`Joined workspace ${this.workspaceId}`);
72
+ } catch (e) {
73
+ this._log(`Warning: join failed: ${e.message}`);
74
+ }
75
+
63
76
  await this._skipExistingEvents();
64
77
 
65
78
  const heartbeatInterval = setInterval(() => this._heartbeat(), 30000);
@@ -59,7 +59,8 @@ class OpenClawAdapter extends BaseAdapter {
59
59
  _findOpenclawBinary() {
60
60
  try {
61
61
  const cmd = IS_WINDOWS ? 'where openclaw' : 'which openclaw';
62
- const result = execSync(cmd, { encoding: 'utf-8', timeout: 5000 })
62
+ const { getEnhancedEnv } = require('../paths');
63
+ const result = execSync(cmd, { encoding: 'utf-8', timeout: 5000, env: getEnhancedEnv() })
63
64
  .split(/\r?\n/)[0].trim();
64
65
  if (result) return result;
65
66
  } catch {}
@@ -69,6 +70,9 @@ class OpenClawAdapter extends BaseAdapter {
69
70
  if (IS_WINDOWS) {
70
71
  const appdata = process.env.APPDATA || '';
71
72
  if (appdata) dirs.push(path.join(appdata, 'npm'));
73
+ // Portable Node.js installed by OpenAgents Launcher
74
+ const home = process.env.USERPROFILE || process.env.HOME || '';
75
+ if (home) dirs.push(path.join(home, '.openagents', 'nodejs'));
72
76
  } else {
73
77
  const home = process.env.HOME || '';
74
78
  dirs.push(path.join(home, '.npm-global', 'bin'), '/usr/local/bin');
package/src/daemon.js CHANGED
@@ -67,6 +67,8 @@ class Daemon {
67
67
 
68
68
  this._writeStatus();
69
69
  this._cachedAgentNames = new Set(agents.map(a => a.name));
70
+ this._cachedAgentConfigs = {};
71
+ for (const a of agents) this._cachedAgentConfigs[a.name] = a.network || '';
70
72
  this._log(`Daemon started with ${agents.length} agent(s)`);
71
73
 
72
74
  // Block until shutdown
@@ -260,7 +262,10 @@ class Daemon {
260
262
  if (network) {
261
263
  this._adapterLoop(name, agentCfg, info, network);
262
264
  } else {
263
- this._spawnLoop(name, agentCfg, info);
265
+ // No workspace connected — agent is ready but not connected
266
+ info.state = 'idle';
267
+ this._writeStatus();
268
+ this._log(`${name} ready (no workspace connected)`);
264
269
  }
265
270
  }
266
271
 
@@ -355,6 +360,7 @@ class Daemon {
355
360
  token: network.token,
356
361
  agentName: name,
357
362
  endpoint,
363
+ agentType,
358
364
  openclawAgentId: agentCfg.openclaw_agent_id || 'main',
359
365
  disabledModules: new Set(),
360
366
  agentEnv: this._buildAgentEnv(agentCfg),
@@ -574,9 +580,12 @@ class Daemon {
574
580
  _reload() {
575
581
  this._log('Reloading config...');
576
582
  const oldNames = this._cachedAgentNames || new Set();
583
+ const oldConfigs = this._cachedAgentConfigs || {};
577
584
  // Re-read config from disk
578
585
  const newAgents = this.config.getAgents();
579
586
  const newNames = new Set(newAgents.map(a => a.name));
587
+ const newConfigs = {};
588
+ for (const a of newAgents) newConfigs[a.name] = a.network || '';
580
589
 
581
590
  // Stop removed agents
582
591
  for (const name of oldNames) {
@@ -586,15 +595,21 @@ class Daemon {
586
595
  }
587
596
  }
588
597
 
589
- // Start new agents
598
+ // Start new agents or restart agents whose network changed
590
599
  for (const agent of newAgents) {
591
600
  if (!oldNames.has(agent.name)) {
592
601
  this._launchAgent(agent);
593
602
  this._log(`Reload: started new agent '${agent.name}'`);
603
+ } else if ((oldConfigs[agent.name] || '') !== (agent.network || '')) {
604
+ // Network config changed — restart agent
605
+ this.stopAgent(agent.name);
606
+ this._launchAgent(agent);
607
+ this._log(`Reload: restarted '${agent.name}' (network changed)`);
594
608
  }
595
609
  }
596
610
 
597
611
  this._cachedAgentNames = newNames;
612
+ this._cachedAgentConfigs = newConfigs;
598
613
  this._writeStatus();
599
614
  }
600
615
 
package/src/installer.js CHANGED
@@ -429,7 +429,7 @@ class Installer {
429
429
  const { spawn: spawnProc } = require('child_process');
430
430
  const https = require('https');
431
431
  const os = require('os');
432
- const nodeVersion = 'v22.14.0';
432
+ const nodeVersion = 'v22.16.0';
433
433
  const plat = Installer.platform();
434
434
 
435
435
  if (onData) onData(`Node.js not found. Installing Node.js ${nodeVersion}...\n\n`);
package/src/tui.js CHANGED
@@ -37,18 +37,24 @@ const COLORS = {
37
37
  };
38
38
 
39
39
  const STATE_DISPLAY = {
40
- online: { sym: '\u25CF', color: COLORS.stateRunning },
41
- running: { sym: '\u25CF', color: COLORS.stateRunning },
42
- starting: { sym: '\u25D0', color: COLORS.stateStarting },
43
- reconnecting: { sym: '\u25D0', color: COLORS.stateStarting },
44
- stopped: { sym: '\u25CB', color: COLORS.stateStopped },
45
- 'not configured': { sym: '\u25CB', color: COLORS.stateStopped },
46
- error: { sym: '\u2717', color: COLORS.stateError },
40
+ online: { sym: '\u25CF', color: COLORS.stateRunning, label: 'connected' },
41
+ running: { sym: '\u25CF', color: COLORS.stateRunning, label: 'connected' },
42
+ idle: { sym: '\u25CB', color: COLORS.stateStarting, label: 'ready' },
43
+ starting: { sym: '\u25D0', color: COLORS.stateStarting, label: 'starting' },
44
+ reconnecting: { sym: '\u25D0', color: COLORS.stateStarting, label: 'reconnecting' },
45
+ stopped: { sym: '\u25CB', color: COLORS.stateStopped, label: 'stopped' },
46
+ 'not configured': { sym: '\u25CB', color: COLORS.stateStopped, label: 'not configured' },
47
+ error: { sym: '\u2717', color: COLORS.stateError, label: 'error' },
47
48
  };
48
49
 
49
- function stateMarkup(state) {
50
- const d = STATE_DISPLAY[state] || { sym: '?', color: 'white' };
51
- return `{${d.color}-fg}${d.sym} ${state}{/${d.color}-fg}`;
50
+ function stateMarkup(state, hasWorkspace) {
51
+ const d = STATE_DISPLAY[state] || { sym: '?', color: 'white', label: state };
52
+ let label = d.label;
53
+ // For running/connected agents, clarify workspace status
54
+ if ((state === 'running' || state === 'online') && !hasWorkspace) {
55
+ label = 'running';
56
+ }
57
+ return `{${d.color}-fg}${d.sym} ${label}{/${d.color}-fg}`;
52
58
  }
53
59
 
54
60
  // ── Data helpers ────────────────────────────────────────────────────────────
@@ -299,7 +305,7 @@ function createTUI() {
299
305
  agentList.setItems([' {gray-fg}No agents configured. Press {bold}i{/bold} to install, {bold}n{/bold} to create.{/gray-fg}']);
300
306
  } else {
301
307
  const items = agentRows.map(r => {
302
- const state = stateMarkup(r.state);
308
+ const state = stateMarkup(r.state, !!r.workspace);
303
309
  const ws = r.workspace || '{gray-fg}-{/gray-fg}';
304
310
  const pathInfo = r.path ? `{gray-fg} ${r.path}{/gray-fg}` : '';
305
311
  return ` ${r.name.padEnd(22)} ${r.type.padEnd(14)} ${state.padEnd(30)} ${ws}${pathInfo}`;
@@ -1128,15 +1134,17 @@ function createTUI() {
1128
1134
  opened = true;
1129
1135
  } catch {}
1130
1136
 
1131
- // Show URL in a dialog
1137
+ // Show URL in a dialog — full width, auto-height for long URLs
1138
+ const innerW = screen.width - 4;
1139
+ const urlLines = Math.ceil(url.length / innerW);
1132
1140
  const dialog = blessed.box({
1133
- top: 'center', left: 'center',
1134
- width: 70, height: 7,
1141
+ top: 'center', left: 0,
1142
+ width: '100%', height: 4 + urlLines + 2,
1135
1143
  border: { type: 'line' },
1136
1144
  tags: true,
1137
1145
  label: ' {bold}Workspace URL{/bold} ',
1138
1146
  style: { border: { fg: COLORS.accent }, bg: COLORS.surface },
1139
- content: `\n ${url}\n\n {gray-fg}${opened ? 'Opened in browser.' : 'Copy the URL above.'} Press Esc to close.{/gray-fg}`,
1147
+ content: `\n {bold}${url}{/bold}\n\n {gray-fg}${opened ? 'Opened in browser.' : 'Copy the URL above.'} Press Esc to close.{/gray-fg}`,
1140
1148
  });
1141
1149
 
1142
1150
  screen.append(dialog);