@openagents-org/agent-launcher 0.2.12 → 0.2.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 +1 -1
- package/src/adapters/openclaw.js +62 -140
package/package.json
CHANGED
package/src/adapters/openclaw.js
CHANGED
|
@@ -36,23 +36,13 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
36
36
|
this.openclawAgentId = opts.openclawAgentId || 'main';
|
|
37
37
|
this.disabledModules = opts.disabledModules || new Set();
|
|
38
38
|
|
|
39
|
-
//
|
|
40
|
-
// Activated when LLM_API_KEY + LLM_BASE_URL are in agent env.
|
|
41
|
-
const env = opts.agentEnv || {};
|
|
42
|
-
this._directApiKey = env.OPENAI_API_KEY || env.LLM_API_KEY || '';
|
|
43
|
-
this._directBaseUrl = (env.OPENAI_BASE_URL || env.LLM_BASE_URL || '').replace(/\/+$/, '');
|
|
44
|
-
this._directModel = env.OPENCLAW_MODEL || env.LLM_MODEL || 'gpt-4o';
|
|
45
|
-
this._directMode = !!(this._directApiKey && this._directBaseUrl);
|
|
46
|
-
|
|
47
|
-
// Find the openclaw binary
|
|
39
|
+
// Find the openclaw binary — always use CLI/gateway mode for full tool support
|
|
48
40
|
this._openclawBinary = this._findOpenclawBinary();
|
|
49
41
|
|
|
50
|
-
if (this.
|
|
51
|
-
this._log(`Using direct LLM API mode (${this._directBaseUrl}, model=${this._directModel})`);
|
|
52
|
-
} else if (this._openclawBinary) {
|
|
42
|
+
if (this._openclawBinary) {
|
|
53
43
|
this._log(`Using OpenClaw CLI mode (${this._openclawBinary})`);
|
|
54
44
|
} else {
|
|
55
|
-
this._log('OpenClaw binary not found
|
|
45
|
+
this._log('OpenClaw binary not found — agent will not be able to process messages');
|
|
56
46
|
}
|
|
57
47
|
|
|
58
48
|
// Install workspace skill
|
|
@@ -161,12 +151,7 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
161
151
|
await this.sendStatus(msgChannel, 'thinking...');
|
|
162
152
|
|
|
163
153
|
try {
|
|
164
|
-
|
|
165
|
-
if (this._directMode) {
|
|
166
|
-
responseText = await this._runDirectApi(content, msgChannel);
|
|
167
|
-
} else {
|
|
168
|
-
responseText = await this._runCliAgent(content, msgChannel);
|
|
169
|
-
}
|
|
154
|
+
const responseText = await this._runCliAgent(content, msgChannel);
|
|
170
155
|
|
|
171
156
|
if (responseText) {
|
|
172
157
|
await this.sendResponse(msgChannel, responseText);
|
|
@@ -271,90 +256,18 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
271
256
|
});
|
|
272
257
|
});
|
|
273
258
|
}
|
|
274
|
-
// ------------------------------------------------------------------
|
|
275
|
-
// Direct API mode (bypass OpenClaw CLI, call LLM directly)
|
|
276
|
-
// ------------------------------------------------------------------
|
|
277
|
-
|
|
278
|
-
async _runDirectApi(userMessage, channel) {
|
|
279
|
-
const https = require('https');
|
|
280
|
-
const http = require('http');
|
|
281
|
-
const url = new URL(this._directBaseUrl + '/chat/completions');
|
|
282
|
-
const transport = url.protocol === 'https:' ? https : http;
|
|
283
|
-
|
|
284
|
-
// Build system prompt
|
|
285
|
-
let systemPrompt;
|
|
286
|
-
try {
|
|
287
|
-
const { buildOpenclawSystemPrompt } = require('./workspace-prompt');
|
|
288
|
-
systemPrompt = buildOpenclawSystemPrompt({
|
|
289
|
-
agentName: this.agentName,
|
|
290
|
-
workspaceId: this.workspaceId,
|
|
291
|
-
channelName: channel,
|
|
292
|
-
endpoint: this.client?.endpoint || '',
|
|
293
|
-
token: this.token || '',
|
|
294
|
-
});
|
|
295
|
-
} catch (e) {
|
|
296
|
-
this._log(`System prompt build failed (using fallback): ${e.message}`);
|
|
297
|
-
}
|
|
298
|
-
if (!systemPrompt) {
|
|
299
|
-
systemPrompt = `You are a helpful AI assistant named ${this.agentName}. You are connected to an OpenAgents workspace. Answer questions concisely and helpfully.`;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Simple conversation (no history for now)
|
|
303
|
-
const messages = [
|
|
304
|
-
{ role: 'system', content: systemPrompt || 'You are a helpful assistant.' },
|
|
305
|
-
{ role: 'user', content: userMessage },
|
|
306
|
-
];
|
|
307
|
-
|
|
308
|
-
const body = JSON.stringify({
|
|
309
|
-
model: this._directModel,
|
|
310
|
-
messages,
|
|
311
|
-
stream: false,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
this._log(`Direct API: ${url.hostname} model=${this._directModel} msg=${userMessage.slice(0, 50)}...`);
|
|
315
|
-
|
|
316
|
-
return new Promise((resolve, reject) => {
|
|
317
|
-
const req = transport.request(url, {
|
|
318
|
-
method: 'POST',
|
|
319
|
-
headers: {
|
|
320
|
-
'Content-Type': 'application/json',
|
|
321
|
-
'Authorization': `Bearer ${this._directApiKey}`,
|
|
322
|
-
'Content-Length': Buffer.byteLength(body),
|
|
323
|
-
},
|
|
324
|
-
timeout: 120000,
|
|
325
|
-
}, (res) => {
|
|
326
|
-
let data = '';
|
|
327
|
-
res.on('data', (chunk) => { data += chunk; });
|
|
328
|
-
res.on('end', () => {
|
|
329
|
-
try {
|
|
330
|
-
if (res.statusCode !== 200) {
|
|
331
|
-
reject(new Error(`${res.statusCode} ${data.slice(0, 200)}`));
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
const result = JSON.parse(data);
|
|
335
|
-
const text = result.choices?.[0]?.message?.content || '';
|
|
336
|
-
this._log(`Direct API response: ${text.slice(0, 80)}...`);
|
|
337
|
-
resolve(text);
|
|
338
|
-
} catch (e) {
|
|
339
|
-
reject(new Error(`Parse error: ${e.message}`));
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
req.on('error', reject);
|
|
344
|
-
req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
|
|
345
|
-
req.write(body);
|
|
346
|
-
req.end();
|
|
347
|
-
});
|
|
348
|
-
}
|
|
349
|
-
|
|
350
259
|
// ------------------------------------------------------------------
|
|
351
260
|
// Static: configure OpenClaw's native auth from LLM env vars
|
|
352
261
|
// ------------------------------------------------------------------
|
|
353
262
|
|
|
354
263
|
/**
|
|
355
|
-
*
|
|
356
|
-
*
|
|
264
|
+
* Configure OpenClaw's native auth and model from user-provided
|
|
265
|
+
* LLM_API_KEY / LLM_BASE_URL / LLM_MODEL values.
|
|
357
266
|
* Called by the Launcher's saveAgentEnv when type === 'openclaw'.
|
|
267
|
+
*
|
|
268
|
+
* For standard providers (OpenAI, Anthropic), uses auth-profiles.json.
|
|
269
|
+
* For custom endpoints, uses models.providers in openclaw.json which
|
|
270
|
+
* gives full tool support via the CLI gateway mode.
|
|
358
271
|
*/
|
|
359
272
|
static configureNativeAuth(env) {
|
|
360
273
|
const apiKey = env.LLM_API_KEY;
|
|
@@ -362,51 +275,60 @@ class OpenClawAdapter extends BaseAdapter {
|
|
|
362
275
|
const model = env.LLM_MODEL || 'gpt-4o';
|
|
363
276
|
if (!apiKey) return;
|
|
364
277
|
|
|
365
|
-
// Determine provider: if baseUrl is openai.com, use 'openai'; otherwise 'openai-compatible'
|
|
366
278
|
const isOpenAI = baseUrl.includes('api.openai.com');
|
|
367
279
|
const isAnthropic = baseUrl.includes('api.anthropic.com');
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
280
|
+
const configFile = path.join(OPENCLAW_STATE_DIR, 'openclaw.json');
|
|
281
|
+
|
|
282
|
+
if (isOpenAI || isAnthropic) {
|
|
283
|
+
// Standard provider — use auth-profiles.json
|
|
284
|
+
const provider = isAnthropic ? 'anthropic' : 'openai';
|
|
285
|
+
const profileId = `${provider}:manual`;
|
|
286
|
+
const agentDir = path.join(OPENCLAW_STATE_DIR, 'agents', 'main', 'agent');
|
|
287
|
+
|
|
288
|
+
try {
|
|
289
|
+
fs.mkdirSync(agentDir, { recursive: true });
|
|
290
|
+
const authFile = path.join(agentDir, 'auth-profiles.json');
|
|
291
|
+
let authData = { version: 1, profiles: {} };
|
|
292
|
+
try { authData = JSON.parse(fs.readFileSync(authFile, 'utf-8')); } catch {}
|
|
293
|
+
authData.profiles = authData.profiles || {};
|
|
294
|
+
authData.profiles[profileId] = { type: 'token', provider, token: apiKey };
|
|
295
|
+
authData.lastGood = authData.lastGood || {};
|
|
296
|
+
authData.lastGood[provider] = profileId;
|
|
297
|
+
fs.writeFileSync(authFile, JSON.stringify(authData, null, 2), 'utf-8');
|
|
298
|
+
} catch {}
|
|
299
|
+
|
|
300
|
+
// Set model
|
|
301
|
+
try {
|
|
302
|
+
let config = {};
|
|
303
|
+
try { config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); } catch {}
|
|
304
|
+
config.agents = config.agents || {};
|
|
305
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
306
|
+
config.agents.defaults.model = { primary: `${provider}/${model}` };
|
|
307
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
|
|
308
|
+
} catch {}
|
|
309
|
+
} else {
|
|
310
|
+
// Custom endpoint — use models.providers for full gateway/tool support
|
|
311
|
+
// This is the proper way to add custom LLM endpoints to OpenClaw.
|
|
312
|
+
// See: https://docs.openclaw.ai/concepts/model-providers
|
|
313
|
+
try {
|
|
314
|
+
let config = {};
|
|
315
|
+
try { config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); } catch {}
|
|
316
|
+
|
|
317
|
+
config.models = config.models || {};
|
|
318
|
+
config.models.providers = config.models.providers || {};
|
|
319
|
+
config.models.providers.custom = {
|
|
320
|
+
baseUrl: baseUrl.replace(/\/+$/, ''),
|
|
321
|
+
apiKey,
|
|
322
|
+
api: 'openai-completions',
|
|
323
|
+
models: [{ id: model, name: model }],
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
config.agents = config.agents || {};
|
|
327
|
+
config.agents.defaults = config.agents.defaults || {};
|
|
328
|
+
config.agents.defaults.model = { primary: `custom/${model}` };
|
|
329
|
+
|
|
330
|
+
fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
|
|
331
|
+
} catch {}
|
|
410
332
|
}
|
|
411
333
|
}
|
|
412
334
|
}
|