@openagents-org/agent-launcher 0.2.11 → 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.11",
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,80 +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 from workspace skill
285
- const { buildOpenclawSystemPrompt } = require('./workspace-prompt');
286
- const systemPrompt = buildOpenclawSystemPrompt({
287
- workspaceId: this.workspaceId,
288
- agentName: this.agentName,
289
- channelName: channel,
290
- });
291
-
292
- // Simple conversation (no history for now)
293
- const messages = [
294
- { role: 'system', content: systemPrompt || 'You are a helpful assistant.' },
295
- { role: 'user', content: userMessage },
296
- ];
297
-
298
- const body = JSON.stringify({
299
- model: this._directModel,
300
- messages,
301
- stream: false,
302
- });
303
-
304
- this._log(`Direct API: ${url.hostname} model=${this._directModel} msg=${userMessage.slice(0, 50)}...`);
305
-
306
- return new Promise((resolve, reject) => {
307
- const req = transport.request(url, {
308
- method: 'POST',
309
- headers: {
310
- 'Content-Type': 'application/json',
311
- 'Authorization': `Bearer ${this._directApiKey}`,
312
- 'Content-Length': Buffer.byteLength(body),
313
- },
314
- timeout: 120000,
315
- }, (res) => {
316
- let data = '';
317
- res.on('data', (chunk) => { data += chunk; });
318
- res.on('end', () => {
319
- try {
320
- if (res.statusCode !== 200) {
321
- reject(new Error(`${res.statusCode} ${data.slice(0, 200)}`));
322
- return;
323
- }
324
- const result = JSON.parse(data);
325
- const text = result.choices?.[0]?.message?.content || '';
326
- this._log(`Direct API response: ${text.slice(0, 80)}...`);
327
- resolve(text);
328
- } catch (e) {
329
- reject(new Error(`Parse error: ${e.message}`));
330
- }
331
- });
332
- });
333
- req.on('error', reject);
334
- req.on('timeout', () => { req.destroy(); reject(new Error('Request timeout')); });
335
- req.write(body);
336
- req.end();
337
- });
338
- }
339
-
340
259
  // ------------------------------------------------------------------
341
260
  // Static: configure OpenClaw's native auth from LLM env vars
342
261
  // ------------------------------------------------------------------
343
262
 
344
263
  /**
345
- * Write OpenClaw's auth-profiles.json and openclaw.json from the
346
- * 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.
347
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.
348
271
  */
349
272
  static configureNativeAuth(env) {
350
273
  const apiKey = env.LLM_API_KEY;
@@ -352,51 +275,60 @@ class OpenClawAdapter extends BaseAdapter {
352
275
  const model = env.LLM_MODEL || 'gpt-4o';
353
276
  if (!apiKey) return;
354
277
 
355
- // Determine provider: if baseUrl is openai.com, use 'openai'; otherwise 'openai-compatible'
356
278
  const isOpenAI = baseUrl.includes('api.openai.com');
357
279
  const isAnthropic = baseUrl.includes('api.anthropic.com');
358
- let provider = 'openai';
359
- if (isAnthropic) provider = 'anthropic';
360
- else if (!isOpenAI) provider = 'openai'; // custom endpoints use openai provider with baseUrl override
361
-
362
- const profileId = `${provider}:manual`;
363
- const agentDir = path.join(OPENCLAW_STATE_DIR, 'agents', 'main', 'agent');
364
-
365
- // Write auth-profiles.json
366
- try {
367
- fs.mkdirSync(agentDir, { recursive: true });
368
- const authFile = path.join(agentDir, 'auth-profiles.json');
369
- let authData = { version: 1, profiles: {} };
370
- try { authData = JSON.parse(fs.readFileSync(authFile, 'utf-8')); } catch {}
371
- authData.profiles = authData.profiles || {};
372
- const profile = { type: 'token', provider, token: apiKey };
373
- if (!isOpenAI && !isAnthropic) profile.baseUrl = baseUrl;
374
- authData.profiles[profileId] = profile;
375
- authData.lastGood = authData.lastGood || {};
376
- authData.lastGood[provider] = profileId;
377
- fs.writeFileSync(authFile, JSON.stringify(authData, null, 2), 'utf-8');
378
- } catch {}
379
-
380
- // Write model config in openclaw.json
381
- try {
382
- const configFile = path.join(OPENCLAW_STATE_DIR, 'openclaw.json');
383
- let config = {};
384
- try { config = JSON.parse(fs.readFileSync(configFile, 'utf-8')); } catch {}
385
- config.agents = config.agents || {};
386
- config.agents.defaults = config.agents.defaults || {};
387
- config.agents.defaults.model = config.agents.defaults.model || {};
388
-
389
- const modelId = isAnthropic ? `anthropic/${model}` : `openai/${model}`;
390
- config.agents.defaults.model.primary = modelId;
391
- config.agents.defaults.models = config.agents.defaults.models || {};
392
- config.agents.defaults.models[modelId] = {};
393
- fs.writeFileSync(configFile, JSON.stringify(config, null, 2), 'utf-8');
394
- } catch {}
395
-
396
- // Set OPENAI_BASE_URL env var for non-standard endpoints
397
- if (!isOpenAI && !isAnthropic) {
398
- env.OPENAI_BASE_URL = baseUrl;
399
- 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 {}
400
332
  }
401
333
  }
402
334
  }