@openagents-org/agent-launcher 0.2.28 → 0.2.30
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/adapters/openclaw.js +132 -76
package/package.json
CHANGED
package/src/adapters/openclaw.js
CHANGED
|
@@ -180,6 +180,7 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
180
180
|
const sessionKey = `openagents-${this.workspaceId.slice(0, 8)}-${channel.slice(-8)}`;
|
|
181
181
|
|
|
182
182
|
const args = [
|
|
183
|
+
'--log-level', 'trace',
|
|
183
184
|
'agent', '--local',
|
|
184
185
|
'--agent', this.openclawAgentId,
|
|
185
186
|
'--session-id', sessionKey,
|
|
@@ -230,92 +231,147 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
230
231
|
}
|
|
231
232
|
};
|
|
232
233
|
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
if (exitCode !== 0) {
|
|
269
|
-
reject(new Error(`CLI exited ${exitCode}: ${output.slice(-300)}`));
|
|
270
|
-
return;
|
|
234
|
+
// Redirect stderr to temp file for real-time tool status polling.
|
|
235
|
+
// --log-level trace makes OpenClaw write diagnostic events to stderr
|
|
236
|
+
// even in non-TTY mode. We poll the temp file for new lines every 500ms.
|
|
237
|
+
const stderrFile = path.join(os.tmpdir(), `openclaw-stderr-${Date.now()}.log`);
|
|
238
|
+
const stderrFd = fs.openSync(stderrFile, 'w');
|
|
239
|
+
this._log('Spawn: stderr → ' + stderrFile);
|
|
240
|
+
|
|
241
|
+
let spawnBin = binary;
|
|
242
|
+
let spawnArgs = args;
|
|
243
|
+
if (IS_WINDOWS) {
|
|
244
|
+
spawnBin = process.env.COMSPEC || 'cmd.exe';
|
|
245
|
+
spawnArgs = ['/C', binary, ...args.map(a => a.includes(' ') ? `"${a}"` : a)];
|
|
246
|
+
}
|
|
247
|
+
const proc = spawn(spawnBin, spawnArgs, {
|
|
248
|
+
stdio: ['ignore', 'pipe', stderrFd],
|
|
249
|
+
env: spawnEnv,
|
|
250
|
+
timeout: 600000,
|
|
251
|
+
windowsHide: true,
|
|
252
|
+
});
|
|
253
|
+
if (proc.stdout) proc.stdout.on('data', (d) => { output += d; });
|
|
254
|
+
|
|
255
|
+
// Poll stderr file every 500ms for tool events
|
|
256
|
+
let stderrOffset = 0;
|
|
257
|
+
const pollInterval = setInterval(() => {
|
|
258
|
+
try {
|
|
259
|
+
const stat = fs.statSync(stderrFile);
|
|
260
|
+
if (stat.size > stderrOffset) {
|
|
261
|
+
const fd = fs.openSync(stderrFile, 'r');
|
|
262
|
+
const buf = Buffer.alloc(stat.size - stderrOffset);
|
|
263
|
+
fs.readSync(fd, buf, 0, buf.length, stderrOffset);
|
|
264
|
+
fs.closeSync(fd);
|
|
265
|
+
stderrOffset = stat.size;
|
|
266
|
+
const chunk = buf.toString('utf-8');
|
|
267
|
+
const lines = chunk.split('\n');
|
|
268
|
+
for (const line of lines) processLine(line);
|
|
271
269
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
270
|
+
} catch {}
|
|
271
|
+
}, 500);
|
|
272
|
+
|
|
273
|
+
const killTimeout = setTimeout(() => {
|
|
274
|
+
proc.kill();
|
|
275
|
+
reject(new Error('CLI timed out after 600 seconds'));
|
|
276
|
+
}, 600000);
|
|
277
|
+
|
|
278
|
+
proc.on('error', (err) => {
|
|
279
|
+
clearInterval(pollInterval);
|
|
280
|
+
clearTimeout(killTimeout);
|
|
281
|
+
fs.closeSync(stderrFd);
|
|
282
|
+
try { fs.unlinkSync(stderrFile); } catch {}
|
|
283
|
+
reject(err);
|
|
284
|
+
});
|
|
285
|
+
proc.on('exit', (code) => {
|
|
286
|
+
clearInterval(pollInterval);
|
|
287
|
+
clearTimeout(killTimeout);
|
|
288
|
+
fs.closeSync(stderrFd);
|
|
289
|
+
// Read full stderr content (contains JSON output + trace lines)
|
|
290
|
+
let stderrContent = '';
|
|
291
|
+
try {
|
|
292
|
+
stderrContent = fs.readFileSync(stderrFile, 'utf-8');
|
|
293
|
+
// Process any remaining lines for tool events
|
|
294
|
+
const remaining = stderrContent.slice(stderrOffset);
|
|
295
|
+
if (remaining) {
|
|
296
|
+
for (const line of remaining.split('\n')) processLine(line);
|
|
296
297
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
} catch {}
|
|
299
|
+
try { fs.unlinkSync(stderrFile); } catch {}
|
|
300
|
+
|
|
301
|
+
// OpenClaw --json writes JSON to stderr, so combine stdout + stderr
|
|
302
|
+
const allOutput = output + '\n' + stderrContent;
|
|
303
|
+
|
|
304
|
+
if (code !== 0) {
|
|
305
|
+
reject(new Error(`CLI exited ${code}: ${allOutput.slice(-300)}`));
|
|
306
|
+
return;
|
|
307
|
+
}
|
|
308
|
+
this._parseCliOutput(allOutput, resolve);
|
|
309
|
+
});
|
|
300
310
|
});
|
|
301
311
|
}
|
|
302
312
|
|
|
303
313
|
_parseCliOutput(output, resolve) {
|
|
304
314
|
const text = output.trim();
|
|
305
315
|
if (!text) { resolve(''); return; }
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
316
|
+
|
|
317
|
+
// OpenClaw --json outputs a JSON blob with {"payloads":[...]} structure.
|
|
318
|
+
// With --log-level trace, stderr also contains diagnostic lines.
|
|
319
|
+
// Find the JSON by looking for '{"payloads"' or the last complete JSON object.
|
|
320
|
+
let jsonStr = null;
|
|
321
|
+
|
|
322
|
+
// Strategy 1: find {"payloads" directly
|
|
323
|
+
const payloadsIdx = text.indexOf('{"payloads"');
|
|
324
|
+
if (payloadsIdx >= 0) {
|
|
325
|
+
// Find the matching closing brace by counting braces
|
|
326
|
+
let depth = 0;
|
|
327
|
+
for (let i = payloadsIdx; i < text.length; i++) {
|
|
328
|
+
if (text[i] === '{') depth++;
|
|
329
|
+
else if (text[i] === '}') { depth--; if (depth === 0) { jsonStr = text.slice(payloadsIdx, i + 1); break; } }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Strategy 2: find last '{' that starts a valid JSON with "payloads"
|
|
334
|
+
if (!jsonStr) {
|
|
335
|
+
for (let i = text.length - 1; i >= 0; i--) {
|
|
336
|
+
if (text[i] === '{') {
|
|
337
|
+
const candidate = text.slice(i);
|
|
338
|
+
try {
|
|
339
|
+
const d = JSON.parse(candidate);
|
|
340
|
+
if (d.payloads) { jsonStr = candidate; break; }
|
|
341
|
+
} catch {}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Strategy 3: try each line that starts with '{'
|
|
347
|
+
if (!jsonStr) {
|
|
348
|
+
for (const line of text.split('\n')) {
|
|
349
|
+
const trimmed = line.trim();
|
|
350
|
+
if (trimmed.startsWith('{')) {
|
|
351
|
+
try {
|
|
352
|
+
const d = JSON.parse(trimmed);
|
|
353
|
+
if (d.payloads) { jsonStr = trimmed; break; }
|
|
354
|
+
} catch {}
|
|
355
|
+
}
|
|
315
356
|
}
|
|
316
|
-
} catch {
|
|
317
|
-
resolve(text);
|
|
318
357
|
}
|
|
358
|
+
|
|
359
|
+
if (jsonStr) {
|
|
360
|
+
try {
|
|
361
|
+
const data = JSON.parse(jsonStr);
|
|
362
|
+
const payloads = data.payloads || [];
|
|
363
|
+
if (payloads.length > 0) {
|
|
364
|
+
resolve(payloads.filter(p => p.text).map(p => p.text).join('\n\n'));
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
} catch {}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Fallback: return non-diagnostic text
|
|
371
|
+
const cleanLines = text.split('\n').filter(l =>
|
|
372
|
+
!l.includes('[diagnostic]') && !l.includes('[agent/embedded]') && !l.includes('Registered plugin')
|
|
373
|
+
).map(l => l.trim()).filter(Boolean);
|
|
374
|
+
resolve(cleanLines.join('\n') || '');
|
|
319
375
|
}
|
|
320
376
|
// ------------------------------------------------------------------
|
|
321
377
|
// Static: configure OpenClaw's native auth from LLM env vars
|