ocpipe 0.4.0 → 0.4.2

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": "ocpipe",
3
- "version": "0.4.0",
3
+ "version": "0.4.2",
4
4
  "description": "SDK for LLM pipelines with OpenCode and Zod",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
package/src/agent.ts CHANGED
@@ -53,6 +53,8 @@ async function runOpencodeAgent(
53
53
 
54
54
  const args = [
55
55
  'run',
56
+ '--dir',
57
+ cwd,
56
58
  '--format',
57
59
  'default',
58
60
  '--agent',
@@ -69,6 +71,7 @@ async function runOpencodeAgent(
69
71
 
70
72
  return new Promise((resolve, reject) => {
71
73
  const opencodeCmd = getOpencodeCommand(args)
74
+ console.error(`[DEBUG] Running: ${opencodeCmd.cmd} ${opencodeCmd.args.join(' ')}`)
72
75
  const proc = spawn(opencodeCmd.cmd, opencodeCmd.args, {
73
76
  cwd,
74
77
  stdio: ['ignore', 'pipe', 'pipe'],
package/src/parsing.ts CHANGED
@@ -307,33 +307,69 @@ export function zodTypeToString(zodType: z.ZodType): string {
307
307
  return 'unknown'
308
308
  }
309
309
 
310
+ /**
311
+ * extractBalancedObject extracts a balanced JSON object starting at startIdx.
312
+ * Returns the object substring or null if unbalanced.
313
+ */
314
+ function extractBalancedObject(text: string, startIdx: number): string | null {
315
+ if (startIdx === -1 || startIdx >= text.length || text[startIdx] !== '{') {
316
+ return null
317
+ }
318
+
319
+ let braceCount = 0
320
+ let endIdx = startIdx
321
+ for (let i = startIdx; i < text.length; i++) {
322
+ if (text[i] === '{') braceCount++
323
+ else if (text[i] === '}') {
324
+ braceCount--
325
+ if (braceCount === 0) {
326
+ endIdx = i + 1
327
+ break
328
+ }
329
+ }
330
+ }
331
+
332
+ if (endIdx > startIdx && braceCount === 0) {
333
+ return text.slice(startIdx, endIdx)
334
+ }
335
+ return null
336
+ }
337
+
310
338
  /** extractJsonString finds and extracts JSON from a response string. */
311
339
  export function extractJsonString(response: string): string | null {
312
340
  // Try to find JSON in code blocks first
313
341
  const codeBlockMatch = response.match(/```(?:json)?\s*(\{[\s\S]*?)```/)
314
342
  if (codeBlockMatch?.[1]) {
315
- return codeBlockMatch[1].trim()
343
+ const candidate = codeBlockMatch[1].trim()
344
+ // Validate it's actually parseable JSON
345
+ try {
346
+ JSON.parse(candidate)
347
+ return candidate
348
+ } catch {
349
+ // Code block content is malformed, try other methods
350
+ }
316
351
  }
317
352
 
318
- // Try to find raw JSON by counting braces
319
- const startIdx = response.indexOf('{')
320
- if (startIdx !== -1) {
321
- let braceCount = 0
322
- let endIdx = startIdx
323
- for (let i = startIdx; i < response.length; i++) {
324
- if (response[i] === '{') braceCount++
325
- else if (response[i] === '}') {
326
- braceCount--
327
- if (braceCount === 0) {
328
- endIdx = i + 1
329
- break
330
- }
353
+ // Try to find raw JSON by counting braces, starting from each { position
354
+ // This handles cases where the model started outputting JSON, then restarted
355
+ // (e.g., "{"response":{"embeds":[{"title":"Haze{"response":{"embeds":[...]}}}")
356
+ // We try each { position until we find one that produces valid JSON
357
+ let searchFrom = 0
358
+ while (searchFrom < response.length) {
359
+ const startIdx = response.indexOf('{', searchFrom)
360
+ if (startIdx === -1) break
361
+
362
+ const candidate = extractBalancedObject(response, startIdx)
363
+ if (candidate) {
364
+ try {
365
+ JSON.parse(candidate)
366
+ return candidate
367
+ } catch {
368
+ // This { position produced invalid JSON, try the next one
331
369
  }
332
370
  }
333
371
 
334
- if (endIdx > startIdx) {
335
- return response.slice(startIdx, endIdx)
336
- }
372
+ searchFrom = startIdx + 1
337
373
  }
338
374
 
339
375
  return null