opc-agent 4.0.3 → 4.0.5

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.
@@ -330,12 +330,9 @@ class ClaudeCLIProvider {
330
330
  this.model = model || 'sonnet';
331
331
  }
332
332
  async chat(messages, systemPrompt, options) {
333
- const { execFile } = await Promise.resolve().then(() => __importStar(require('child_process')));
334
- const { promisify } = await Promise.resolve().then(() => __importStar(require('util')));
335
333
  const { writeFileSync, unlinkSync, mkdtempSync } = await Promise.resolve().then(() => __importStar(require('fs')));
336
334
  const { join } = await Promise.resolve().then(() => __importStar(require('path')));
337
335
  const { tmpdir } = await Promise.resolve().then(() => __importStar(require('os')));
338
- const execFileAsync = promisify(execFile);
339
336
  // Build the prompt from messages
340
337
  const lastMessage = messages[messages.length - 1];
341
338
  if (!lastMessage)
@@ -345,7 +342,7 @@ class ClaudeCLIProvider {
345
342
  if (options?.tools && options.tools.length > 0) {
346
343
  prompt += buildToolPrompt(options.tools);
347
344
  }
348
- const args = ['-p', '--bare'];
345
+ const args = ['-p'];
349
346
  // Write system prompt to temp file to avoid shell escaping issues
350
347
  let tmpFile;
351
348
  if (systemPrompt) {
@@ -359,22 +356,44 @@ class ClaudeCLIProvider {
359
356
  }
360
357
  args.push(prompt);
361
358
  try {
362
- const { stdout } = await execFileAsync('claude', args, {
363
- timeout: 120_000,
364
- maxBuffer: 10 * 1024 * 1024,
365
- env: { ...process.env },
359
+ const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
360
+ const result = await new Promise((resolve, reject) => {
361
+ const proc = spawn('claude', args, {
362
+ env: { ...process.env },
363
+ stdio: ['pipe', 'pipe', 'pipe'],
364
+ });
365
+ // Close stdin immediately to avoid "no stdin data" warning
366
+ proc.stdin.end();
367
+ let stdout = '';
368
+ let stderr = '';
369
+ proc.stdout.on('data', (d) => { stdout += d.toString(); });
370
+ proc.stderr.on('data', (d) => { stderr += d.toString(); });
371
+ proc.on('close', (code) => {
372
+ if (code === 0 || stdout.trim()) {
373
+ resolve(stdout.trim());
374
+ }
375
+ else {
376
+ reject(new Error(`Claude CLI exited ${code}: ${stderr}`));
377
+ }
378
+ });
379
+ proc.on('error', (err) => {
380
+ if (err.code === 'ENOENT') {
381
+ reject(new Error('Claude CLI not found. Install it: npm install -g @anthropic-ai/claude-code\n' +
382
+ 'Then authenticate: claude login'));
383
+ }
384
+ else {
385
+ reject(err);
386
+ }
387
+ });
388
+ // Timeout
389
+ setTimeout(() => {
390
+ proc.kill();
391
+ reject(new Error('Claude CLI timed out after 120s'));
392
+ }, 120_000);
366
393
  });
367
- return stdout.trim();
394
+ return result;
368
395
  }
369
396
  catch (err) {
370
- if (err.code === 'ENOENT') {
371
- throw new Error('Claude CLI not found. Install it: npm install -g @anthropic-ai/claude-code\n' +
372
- 'Then authenticate: claude login');
373
- }
374
- // If claude returns non-zero but has stdout, use it
375
- if (err.stdout && err.stdout.trim()) {
376
- return err.stdout.trim();
377
- }
378
397
  throw new Error(`Claude CLI error: ${err.message}`);
379
398
  }
380
399
  finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opc-agent",
3
- "version": "4.0.3",
3
+ "version": "4.0.5",
4
4
  "description": "Open Agent Framework — Build, test, and run AI Agents for business workstations",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -334,12 +334,9 @@ class ClaudeCLIProvider implements LLMProvider {
334
334
  }
335
335
 
336
336
  async chat(messages: Message[], systemPrompt?: string, options?: ChatOptions): Promise<string> {
337
- const { execFile } = await import('child_process');
338
- const { promisify } = await import('util');
339
337
  const { writeFileSync, unlinkSync, mkdtempSync } = await import('fs');
340
338
  const { join } = await import('path');
341
339
  const { tmpdir } = await import('os');
342
- const execFileAsync = promisify(execFile);
343
340
 
344
341
  // Build the prompt from messages
345
342
  const lastMessage = messages[messages.length - 1];
@@ -352,7 +349,7 @@ class ClaudeCLIProvider implements LLMProvider {
352
349
  prompt += buildToolPrompt(options.tools);
353
350
  }
354
351
 
355
- const args = ['-p', '--bare'];
352
+ const args = ['-p'];
356
353
  // Write system prompt to temp file to avoid shell escaping issues
357
354
  let tmpFile: string | undefined;
358
355
  if (systemPrompt) {
@@ -367,23 +364,43 @@ class ClaudeCLIProvider implements LLMProvider {
367
364
  args.push(prompt);
368
365
 
369
366
  try {
370
- const { stdout } = await execFileAsync('claude', args, {
371
- timeout: 120_000,
372
- maxBuffer: 10 * 1024 * 1024,
373
- env: { ...process.env },
367
+ const { spawn } = await import('child_process');
368
+ const result = await new Promise<string>((resolve, reject) => {
369
+ const proc = spawn('claude', args, {
370
+ env: { ...process.env },
371
+ stdio: ['pipe', 'pipe', 'pipe'],
372
+ });
373
+ // Close stdin immediately to avoid "no stdin data" warning
374
+ proc.stdin.end();
375
+ let stdout = '';
376
+ let stderr = '';
377
+ proc.stdout.on('data', (d: Buffer) => { stdout += d.toString(); });
378
+ proc.stderr.on('data', (d: Buffer) => { stderr += d.toString(); });
379
+ proc.on('close', (code) => {
380
+ if (code === 0 || stdout.trim()) {
381
+ resolve(stdout.trim());
382
+ } else {
383
+ reject(new Error(`Claude CLI exited ${code}: ${stderr}`));
384
+ }
385
+ });
386
+ proc.on('error', (err: any) => {
387
+ if (err.code === 'ENOENT') {
388
+ reject(new Error(
389
+ 'Claude CLI not found. Install it: npm install -g @anthropic-ai/claude-code\n' +
390
+ 'Then authenticate: claude login'
391
+ ));
392
+ } else {
393
+ reject(err);
394
+ }
395
+ });
396
+ // Timeout
397
+ setTimeout(() => {
398
+ proc.kill();
399
+ reject(new Error('Claude CLI timed out after 120s'));
400
+ }, 120_000);
374
401
  });
375
- return stdout.trim();
402
+ return result;
376
403
  } catch (err: any) {
377
- if (err.code === 'ENOENT') {
378
- throw new Error(
379
- 'Claude CLI not found. Install it: npm install -g @anthropic-ai/claude-code\n' +
380
- 'Then authenticate: claude login'
381
- );
382
- }
383
- // If claude returns non-zero but has stdout, use it
384
- if (err.stdout && err.stdout.trim()) {
385
- return err.stdout.trim();
386
- }
387
404
  throw new Error(`Claude CLI error: ${err.message}`);
388
405
  } finally {
389
406
  if (tmpFile) {