@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openagents-org/agent-launcher",
3
- "version": "0.2.12",
3
+ "version": "0.2.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": {
@@ -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
- // Direct API mode: call LLM via HTTP (no OpenClaw CLI needed).
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._directMode) {
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 and no direct API config — agent will not be able to process messages');
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
- let responseText;
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
- * Write OpenClaw's auth-profiles.json and openclaw.json from the
356
- * user-provided LLM_API_KEY / LLM_BASE_URL / LLM_MODEL values.
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
- let provider = 'openai';
369
- if (isAnthropic) provider = 'anthropic';
370
- else if (!isOpenAI) provider = 'openai'; // custom endpoints use openai provider with baseUrl override
371
-
372
- const profileId = `${provider}:manual`;
373
- const agentDir = path.join(OPENCLAW_STATE_DIR, 'agents', 'main', 'agent');
374
-
375
- // Write auth-profiles.json
376
- try {
377
- fs.mkdirSync(agentDir, { recursive: true });
378
- const authFile = path.join(agentDir, 'auth-profiles.json');
379
- let authData = { version: 1, profiles: {} };
380
- try { authData = JSON.parse(fs.readFileSync(authFile, 'utf-8')); } catch {}
381
- authData.profiles = authData.profiles || {};
382
- const profile = { type: 'token', provider, token: apiKey };
383
- if (!isOpenAI && !isAnthropic) profile.baseUrl = baseUrl;
384
- authData.profiles[profileId] = profile;
385
- authData.lastGood = authData.lastGood || {};
386
- authData.lastGood[provider] = profileId;
387
- fs.writeFileSync(authFile, JSON.stringify(authData, null, 2), 'utf-8');
388
- } catch {}
389
-
390
- // Write model config in openclaw.json
391
- try {
392
- const configFile = path.join(OPENCLAW_STATE_DIR, 'openclaw.json');
393
- let config = {};
394
- try { config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); } catch {}
395
- config.agents = config.agents || {};
396
- config.agents.defaults = config.agents.defaults || {};
397
- config.agents.defaults.model = config.agents.defaults.model || {};
398
-
399
- const modelId = isAnthropic ? `anthropic/${model}` : `openai/${model}`;
400
- config.agents.defaults.model.primary = modelId;
401
- config.agents.defaults.models = config.agents.defaults.models || {};
402
- config.agents.defaults.models[modelId] = {};
403
- fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
404
- } catch {}
405
-
406
- // Set OPENAI_BASE_URL env var for non-standard endpoints
407
- if (!isOpenAI && !isAnthropic) {
408
- env.OPENAI_BASE_URL = baseUrl;
409
- env.OPENAI_API_KEY = apiKey;
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
  }