@openagents-org/agent-launcher 0.2.26 → 0.2.28
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 +4 -1
- package/src/adapters/openclaw.js +97 -109
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openagents-org/agent-launcher",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"description": "OpenAgents Launcher — install, configure, and run AI coding agents from your terminal",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -39,5 +39,8 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"blessed": "^0.1.81",
|
|
41
41
|
"ws": "^8.18.0"
|
|
42
|
+
},
|
|
43
|
+
"optionalDependencies": {
|
|
44
|
+
"node-pty": "^1.1.0"
|
|
42
45
|
}
|
|
43
46
|
}
|
package/src/adapters/openclaw.js
CHANGED
|
@@ -191,64 +191,16 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
191
191
|
|
|
192
192
|
const spawnEnv = { ...(this.agentEnv || process.env) };
|
|
193
193
|
if (IS_WINDOWS) {
|
|
194
|
-
// Ensure node and npm global bin are on PATH
|
|
195
194
|
const nodeBinDir = path.dirname(process.execPath);
|
|
196
195
|
const npmBin = path.join(process.env.APPDATA || '', 'npm');
|
|
197
|
-
const
|
|
198
|
-
for (const p of
|
|
196
|
+
const portableDir = path.join(os.homedir(), '.openagents', 'nodejs');
|
|
197
|
+
for (const p of [nodeBinDir, npmBin, portableDir]) {
|
|
199
198
|
if (p && !(spawnEnv.PATH || '').includes(p)) {
|
|
200
|
-
spawnEnv.PATH = p +
|
|
199
|
+
spawnEnv.PATH = p + path.delimiter + (spawnEnv.PATH || '');
|
|
201
200
|
}
|
|
202
201
|
}
|
|
203
202
|
}
|
|
204
203
|
|
|
205
|
-
let spawnBinary = binary;
|
|
206
|
-
let spawnArgs = args;
|
|
207
|
-
const spawnOpts = {
|
|
208
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
209
|
-
env: spawnEnv,
|
|
210
|
-
timeout: 600000,
|
|
211
|
-
windowsHide: true,
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
if (IS_WINDOWS) {
|
|
215
|
-
// Find node.exe to spawn openclaw.mjs directly (unbuffered stderr)
|
|
216
|
-
const portableNodeDir = path.join(os.homedir(), '.openagents', 'nodejs');
|
|
217
|
-
const searchDirs = [portableNodeDir];
|
|
218
|
-
try { const { getExtraBinDirs } = require('../paths'); searchDirs.push(...getExtraBinDirs()); } catch {}
|
|
219
|
-
let nodeBin = null;
|
|
220
|
-
for (const d of searchDirs) {
|
|
221
|
-
const candidate = path.join(d, 'node.exe');
|
|
222
|
-
if (fs.existsSync(candidate)) { nodeBin = candidate; break; }
|
|
223
|
-
}
|
|
224
|
-
// Find openclaw entry point
|
|
225
|
-
const possibleEntries = [
|
|
226
|
-
path.join(path.dirname(binary), 'node_modules', 'openclaw', 'openclaw.mjs'),
|
|
227
|
-
path.join(os.homedir(), '.openagents', 'nodejs', 'node_modules', 'openclaw', 'openclaw.mjs'),
|
|
228
|
-
];
|
|
229
|
-
let entryPoint = null;
|
|
230
|
-
for (const e of possibleEntries) {
|
|
231
|
-
if (fs.existsSync(e)) { entryPoint = e; break; }
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
if (nodeBin && entryPoint) {
|
|
235
|
-
// Direct spawn — unbuffered stderr for real-time tool streaming
|
|
236
|
-
spawnBinary = nodeBin;
|
|
237
|
-
spawnArgs = [entryPoint, ...args];
|
|
238
|
-
this._log(`Spawn: direct node (${nodeBin}) → ${entryPoint}`);
|
|
239
|
-
} else {
|
|
240
|
-
// Fallback to cmd.exe (buffered stderr — no real-time status)
|
|
241
|
-
spawnBinary = process.env.COMSPEC || 'cmd.exe';
|
|
242
|
-
const quotedArgs = args.map((a) => a.includes(' ') ? `"${a}"` : a);
|
|
243
|
-
spawnArgs = ['/C', binary, ...quotedArgs];
|
|
244
|
-
this._log(`Spawn: cmd.exe fallback (nodeBin=${nodeBin}, entry=${entryPoint})`);
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
const proc = spawn(spawnBinary, spawnArgs, spawnOpts);
|
|
249
|
-
let stdout = '';
|
|
250
|
-
let stderr = '';
|
|
251
|
-
|
|
252
204
|
// Tool name → human-readable status
|
|
253
205
|
const toolLabels = {
|
|
254
206
|
exec: 'Running command...',
|
|
@@ -263,72 +215,108 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
263
215
|
memory_search: 'Searching memory...',
|
|
264
216
|
};
|
|
265
217
|
|
|
266
|
-
|
|
267
|
-
let
|
|
268
|
-
if (proc.stdout) proc.stdout.on('data', (d) => { stdout += d; });
|
|
269
|
-
if (proc.stderr) proc.stderr.on('data', (d) => {
|
|
270
|
-
const chunk = d.toString();
|
|
271
|
-
stderr += chunk;
|
|
272
|
-
stdout += chunk;
|
|
273
|
-
stderrBuffer += chunk;
|
|
274
|
-
|
|
275
|
-
// Process complete lines
|
|
276
|
-
const lines = stderrBuffer.split('\n');
|
|
277
|
-
stderrBuffer = lines.pop() || ''; // keep incomplete last line
|
|
278
|
-
|
|
279
|
-
for (const line of lines) {
|
|
280
|
-
// Debug: log all diagnostic lines from stderr
|
|
281
|
-
if (line.includes('[agent/embedded]') || line.includes('[diagnostic]')) {
|
|
282
|
-
this._log(`stderr: ${line.trim().slice(0, 120)}`);
|
|
283
|
-
}
|
|
218
|
+
let output = '';
|
|
219
|
+
let lineBuffer = '';
|
|
284
220
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
const agentStart = line.match(/embedded run agent start/);
|
|
293
|
-
if (agentStart) {
|
|
294
|
-
this.sendStatus(channel, 'thinking...').catch(() => {});
|
|
295
|
-
}
|
|
221
|
+
const processLine = (line) => {
|
|
222
|
+
const toolStart = line.match(/embedded run tool start:.*tool=(\w+)/);
|
|
223
|
+
if (toolStart) {
|
|
224
|
+
const label = toolLabels[toolStart[1]] || `Using ${toolStart[1]}...`;
|
|
225
|
+
this._log(`Tool: ${label}`);
|
|
226
|
+
this.sendStatus(channel, label).catch(() => {});
|
|
296
227
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
proc.on('error', (err) => reject(err));
|
|
300
|
-
proc.on('exit', (code) => {
|
|
301
|
-
if (code !== 0) {
|
|
302
|
-
reject(new Error(`CLI exited ${code}: ${stderr.slice(0, 300)}`));
|
|
303
|
-
return;
|
|
228
|
+
if (line.match(/embedded run agent start/)) {
|
|
229
|
+
this.sendStatus(channel, 'thinking...').catch(() => {});
|
|
304
230
|
}
|
|
231
|
+
};
|
|
305
232
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
233
|
+
// Try node-pty for line-buffered output (real-time tool streaming)
|
|
234
|
+
let pty;
|
|
235
|
+
try { pty = require('node-pty'); } catch {}
|
|
236
|
+
|
|
237
|
+
if (pty) {
|
|
238
|
+
// On Windows, .cmd files can't be spawned directly by pty — use cmd.exe
|
|
239
|
+
const ptyBin = IS_WINDOWS ? (process.env.COMSPEC || 'cmd.exe') : binary;
|
|
240
|
+
const ptyArgs = IS_WINDOWS ? ['/C', binary, ...args] : args;
|
|
241
|
+
this._log('Spawn: node-pty (line-buffered)');
|
|
242
|
+
const proc = pty.spawn(ptyBin, ptyArgs, {
|
|
243
|
+
name: 'xterm',
|
|
244
|
+
cols: 200,
|
|
245
|
+
rows: 50,
|
|
246
|
+
cwd: process.cwd(),
|
|
247
|
+
env: spawnEnv,
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
proc.onData((data) => {
|
|
251
|
+
output += data;
|
|
252
|
+
lineBuffer += data;
|
|
253
|
+
const lines = lineBuffer.split('\n');
|
|
254
|
+
lineBuffer = lines.pop() || '';
|
|
255
|
+
for (const line of lines) processLine(line);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const timeout = setTimeout(() => {
|
|
259
|
+
proc.kill();
|
|
260
|
+
reject(new Error('CLI timed out after 600 seconds'));
|
|
261
|
+
}, 600000);
|
|
262
|
+
|
|
263
|
+
proc.onExit(({ exitCode }) => {
|
|
264
|
+
clearTimeout(timeout);
|
|
265
|
+
// Process remaining buffer
|
|
266
|
+
if (lineBuffer) processLine(lineBuffer);
|
|
267
|
+
|
|
268
|
+
if (exitCode !== 0) {
|
|
269
|
+
reject(new Error(`CLI exited ${exitCode}: ${output.slice(-300)}`));
|
|
270
|
+
return;
|
|
324
271
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
272
|
+
this._parseCliOutput(output, resolve);
|
|
273
|
+
});
|
|
274
|
+
} else {
|
|
275
|
+
// Fallback: regular spawn (buffered on Windows)
|
|
276
|
+
this._log('Spawn: fallback (no node-pty)');
|
|
277
|
+
let spawnBin = binary;
|
|
278
|
+
let spawnArgs = args;
|
|
279
|
+
if (IS_WINDOWS) {
|
|
280
|
+
spawnBin = process.env.COMSPEC || 'cmd.exe';
|
|
281
|
+
spawnArgs = ['/C', binary, ...args.map(a => a.includes(' ') ? `"${a}"` : a)];
|
|
328
282
|
}
|
|
329
|
-
|
|
283
|
+
const proc = spawn(spawnBin, spawnArgs, {
|
|
284
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
285
|
+
env: spawnEnv,
|
|
286
|
+
timeout: 600000,
|
|
287
|
+
windowsHide: true,
|
|
288
|
+
});
|
|
289
|
+
if (proc.stdout) proc.stdout.on('data', (d) => { output += d; });
|
|
290
|
+
if (proc.stderr) proc.stderr.on('data', (d) => { output += d; });
|
|
291
|
+
proc.on('error', (err) => reject(err));
|
|
292
|
+
proc.on('exit', (code) => {
|
|
293
|
+
if (code !== 0) {
|
|
294
|
+
reject(new Error(`CLI exited ${code}: ${output.slice(-300)}`));
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
this._parseCliOutput(output, resolve);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
330
300
|
});
|
|
331
301
|
}
|
|
302
|
+
|
|
303
|
+
_parseCliOutput(output, resolve) {
|
|
304
|
+
const text = output.trim();
|
|
305
|
+
if (!text) { resolve(''); return; }
|
|
306
|
+
const jsonStart = text.indexOf('{');
|
|
307
|
+
if (jsonStart < 0) { resolve(text); return; }
|
|
308
|
+
try {
|
|
309
|
+
const data = JSON.parse(text.slice(jsonStart));
|
|
310
|
+
const payloads = data.payloads || [];
|
|
311
|
+
if (payloads.length > 0) {
|
|
312
|
+
resolve(payloads.filter(p => p.text).map(p => p.text).join('\n\n'));
|
|
313
|
+
} else {
|
|
314
|
+
resolve('');
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
resolve(text);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
332
320
|
// ------------------------------------------------------------------
|
|
333
321
|
// Static: configure OpenClaw's native auth from LLM env vars
|
|
334
322
|
// ------------------------------------------------------------------
|