ocpipe 0.3.5 → 0.3.7

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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/agent.ts +37 -15
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ocpipe",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "SDK for LLM pipelines with OpenCode and Zod",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -29,7 +29,7 @@
29
29
  "bun": ">=1.0.0"
30
30
  },
31
31
  "dependencies": {
32
- "opencode-ai": "1.1.16"
32
+ "opencode-ai": "1.1.25"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "zod": "4.3.5"
package/src/agent.ts CHANGED
@@ -5,24 +5,42 @@
5
5
  */
6
6
 
7
7
  import { spawn, execSync } from 'child_process'
8
+ import { existsSync } from 'fs'
8
9
  import { mkdir } from 'fs/promises'
10
+ import { join } from 'path'
9
11
  import { PROJECT_ROOT, TMP_DIR } from './paths.js'
10
12
  import type { RunAgentOptions, RunAgentResult } from './types.js'
11
13
 
12
- /** Check if opencode is available in system PATH */
13
- function hasSystemOpencode(): boolean {
14
- try {
15
- execSync('which opencode', { stdio: 'ignore' })
16
- return true
17
- } catch {
18
- return false
14
+ /** Find opencode binary from PATH, preferring non-node_modules locations */
15
+ function findOpencode(): string | null {
16
+ const pathDirs = (process.env.PATH || '').split(':')
17
+
18
+ // First pass: look for opencode in non-node_modules directories
19
+ for (const dir of pathDirs) {
20
+ if (dir.includes('node_modules')) continue
21
+ const candidate = join(dir, 'opencode')
22
+ if (existsSync(candidate)) {
23
+ return candidate
24
+ }
19
25
  }
26
+
27
+ // Second pass: check node_modules/.bin as fallback
28
+ for (const dir of pathDirs) {
29
+ if (!dir.includes('node_modules')) continue
30
+ const candidate = join(dir, 'opencode')
31
+ if (existsSync(candidate)) {
32
+ return candidate
33
+ }
34
+ }
35
+
36
+ return null
20
37
  }
21
38
 
22
39
  /** Get command and args to invoke opencode */
23
40
  function getOpencodeCommand(args: string[]): { cmd: string; args: string[] } {
24
- if (hasSystemOpencode()) {
25
- return { cmd: 'opencode', args }
41
+ const opencode = findOpencode()
42
+ if (opencode) {
43
+ return { cmd: opencode, args }
26
44
  }
27
45
  // Fallback to bunx with ocpipe package (which has opencode-ai as dependency)
28
46
  return { cmd: 'bunx', args: ['-p', 'ocpipe', 'opencode', ...args] }
@@ -56,19 +74,24 @@ export async function runAgent(
56
74
  args.push('--session', sessionId)
57
75
  }
58
76
 
77
+ // Pass prompt as positional argument (stdin doesn't work without TTY)
78
+ args.push(prompt)
79
+
59
80
  return new Promise((resolve, reject) => {
60
81
  const opencodeCmd = getOpencodeCommand(args)
61
82
  const proc = spawn(opencodeCmd.cmd, opencodeCmd.args, {
62
83
  cwd: workdir ?? PROJECT_ROOT,
63
- stdio: ['pipe', 'pipe', 'pipe'],
84
+ stdio: ['ignore', 'pipe', 'pipe'],
64
85
  })
65
86
 
66
87
  let newSessionId = sessionId || ''
67
88
  const stdoutChunks: string[] = []
89
+ const stderrChunks: string[] = []
68
90
 
69
91
  // Stream stderr in real-time (OpenCode progress output)
70
92
  proc.stderr.on('data', (data: Buffer) => {
71
93
  const text = data.toString()
94
+ stderrChunks.push(text)
72
95
 
73
96
  // Parse session ID from output
74
97
  for (const line of text.split('\n')) {
@@ -92,10 +115,6 @@ export async function runAgent(
92
115
  process.stderr.write(text)
93
116
  })
94
117
 
95
- // Send prompt to stdin
96
- proc.stdin.write(prompt)
97
- proc.stdin.end()
98
-
99
118
  // Timeout handling (0 = no timeout)
100
119
  const timeout =
101
120
  timeoutSec > 0 ?
@@ -109,7 +128,10 @@ export async function runAgent(
109
128
  if (timeout) clearTimeout(timeout)
110
129
 
111
130
  if (code !== 0) {
112
- reject(new Error(`OpenCode exited with code ${code}`))
131
+ const stderr = stderrChunks.join('').trim()
132
+ const lastLines = stderr.split('\n').slice(-5).join('\n')
133
+ const detail = lastLines ? `\n${lastLines}` : ''
134
+ reject(new Error(`OpenCode exited with code ${code}${detail}`))
113
135
  return
114
136
  }
115
137