@love-moon/ai-sdk 0.2.18 → 0.2.19

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.
@@ -1,10 +1,12 @@
1
1
  export function normalizeBackend(backend: any): string;
2
2
  export function isSupportedBackend(backend: any): boolean;
3
- export function providerVariantForBackend(backend: any): "codex-app-server" | "claude-agent-sdk";
4
- export function assertSupportedBackend(backend: any): "codex" | "claude";
5
- export function createLocalAiSession(backend: any, options?: {}): CodexAppServerSession | ClaudeAgentSdkSession;
3
+ export function providerVariantForBackend(backend: any): "codex-app-server" | "claude-agent-sdk" | "opencode-sdk";
4
+ export function assertSupportedBackend(backend: any): "codex" | "claude" | "opencode";
5
+ export function createLocalAiSession(backend: any, options?: {}): CodexAppServerSession | ClaudeAgentSdkSession | OpencodeSdkSession;
6
6
  export const DEFAULT_PROVIDER_VARIANT: "codex-app-server";
7
7
  export const CLAUDE_PROVIDER_VARIANT: "claude-agent-sdk";
8
+ export const OPENCODE_PROVIDER_VARIANT: "opencode-sdk";
8
9
  import { CodexAppServerSession } from "./providers/codex-app-server-session.js";
9
10
  import { ClaudeAgentSdkSession } from "./providers/claude-agent-sdk-session.js";
10
- export { CodexAppServerSession, ClaudeAgentSdkSession };
11
+ import { OpencodeSdkSession } from "./providers/opencode-sdk-session.js";
12
+ export { CodexAppServerSession, ClaudeAgentSdkSession, OpencodeSdkSession };
@@ -1,7 +1,9 @@
1
1
  import { CodexAppServerSession } from "./providers/codex-app-server-session.js";
2
2
  import { ClaudeAgentSdkSession } from "./providers/claude-agent-sdk-session.js";
3
+ import { OpencodeSdkSession } from "./providers/opencode-sdk-session.js";
3
4
  export const DEFAULT_PROVIDER_VARIANT = "codex-app-server";
4
5
  export const CLAUDE_PROVIDER_VARIANT = "claude-agent-sdk";
6
+ export const OPENCODE_PROVIDER_VARIANT = "opencode-sdk";
5
7
  export function normalizeBackend(backend) {
6
8
  const normalized = String(backend || "").trim().toLowerCase();
7
9
  if (normalized === "code") {
@@ -10,28 +12,42 @@ export function normalizeBackend(backend) {
10
12
  if (normalized === "claude-code") {
11
13
  return "claude";
12
14
  }
15
+ if (normalized === "open-code" || normalized === "open_code") {
16
+ return "opencode";
17
+ }
13
18
  return normalized;
14
19
  }
15
20
  export function isSupportedBackend(backend) {
16
21
  const normalized = normalizeBackend(backend);
17
- return normalized === "codex" || normalized === "claude";
22
+ return normalized === "codex" || normalized === "claude" || normalized === "opencode";
18
23
  }
19
24
  export function providerVariantForBackend(backend) {
20
- return normalizeBackend(backend) === "claude" ? CLAUDE_PROVIDER_VARIANT : DEFAULT_PROVIDER_VARIANT;
25
+ const normalized = normalizeBackend(backend);
26
+ if (normalized === "claude") {
27
+ return CLAUDE_PROVIDER_VARIANT;
28
+ }
29
+ if (normalized === "opencode") {
30
+ return OPENCODE_PROVIDER_VARIANT;
31
+ }
32
+ return DEFAULT_PROVIDER_VARIANT;
21
33
  }
22
34
  export function assertSupportedBackend(backend) {
23
35
  const normalized = normalizeBackend(backend);
24
- if (normalized === "codex" || normalized === "claude") {
36
+ if (normalized === "codex" || normalized === "claude" || normalized === "opencode") {
25
37
  return normalized;
26
38
  }
27
- throw new Error(`Unsupported AI SDK backend "${backend}". Only codex app-server and claude agent-sdk are supported.`);
39
+ throw new Error(`Unsupported AI SDK backend "${backend}". Only codex app-server, claude agent-sdk, and opencode sdk are supported.`);
28
40
  }
29
41
  export function createLocalAiSession(backend, options = {}) {
30
42
  const normalized = assertSupportedBackend(backend);
31
43
  if (normalized === "claude") {
32
44
  return new ClaudeAgentSdkSession(normalized, options);
33
45
  }
46
+ if (normalized === "opencode") {
47
+ return new OpencodeSdkSession(normalized, options);
48
+ }
34
49
  return new CodexAppServerSession(normalized, options);
35
50
  }
36
51
  export { CodexAppServerSession };
37
52
  export { ClaudeAgentSdkSession };
53
+ export { OpencodeSdkSession };
package/dist/shared.js CHANGED
@@ -58,7 +58,64 @@ export function parseCommandParts(commandLine) {
58
58
  if (!normalized) {
59
59
  return { command: "", args: [] };
60
60
  }
61
- const parts = normalized.split(/\s+/);
61
+ const parts = [];
62
+ let current = "";
63
+ let quote = "";
64
+ let escaping = false;
65
+ let tokenStarted = false;
66
+ for (const char of normalized) {
67
+ if (escaping) {
68
+ current += char;
69
+ escaping = false;
70
+ tokenStarted = true;
71
+ continue;
72
+ }
73
+ if (char === "\\") {
74
+ if (quote === "'") {
75
+ current += char;
76
+ }
77
+ else {
78
+ escaping = true;
79
+ }
80
+ tokenStarted = true;
81
+ continue;
82
+ }
83
+ if (quote) {
84
+ if (char === quote) {
85
+ quote = "";
86
+ }
87
+ else {
88
+ current += char;
89
+ }
90
+ tokenStarted = true;
91
+ continue;
92
+ }
93
+ if (char === "'" || char === "\"") {
94
+ quote = char;
95
+ tokenStarted = true;
96
+ continue;
97
+ }
98
+ if (/\s/.test(char)) {
99
+ if (tokenStarted) {
100
+ parts.push(current);
101
+ current = "";
102
+ tokenStarted = false;
103
+ }
104
+ continue;
105
+ }
106
+ current += char;
107
+ tokenStarted = true;
108
+ }
109
+ if (escaping) {
110
+ current += "\\";
111
+ tokenStarted = true;
112
+ }
113
+ if (quote) {
114
+ throw new Error(`Invalid command line: unterminated ${quote === "\"" ? "double" : "single"} quote`);
115
+ }
116
+ if (tokenStarted) {
117
+ parts.push(current);
118
+ }
62
119
  return {
63
120
  command: parts[0],
64
121
  args: parts.slice(1),
@@ -0,0 +1,62 @@
1
+ export class OpencodeServerTransport extends EventEmitter<[never]> {
2
+ constructor(options?: {});
3
+ options: {};
4
+ logger: any;
5
+ cwd: any;
6
+ command: string;
7
+ baseArgs: string[];
8
+ hasExplicitHostnameArg: boolean;
9
+ hasExplicitPortArg: boolean;
10
+ explicitPortRaw: string;
11
+ explicitPort: number;
12
+ hostname: any;
13
+ port: number;
14
+ bootTimeoutMs: number;
15
+ env: any;
16
+ config: any;
17
+ child: import("child_process").ChildProcessByStdio<null, import("stream").Readable, import("stream").Readable> | null;
18
+ childContext: {
19
+ child: import("child_process").ChildProcessByStdio<null, import("stream").Readable, import("stream").Readable>;
20
+ stdoutReader: null;
21
+ stderrReader: null;
22
+ suppressExit: boolean;
23
+ onError: null;
24
+ onExit: null;
25
+ } | null;
26
+ stdoutReader: any;
27
+ stderrReader: any;
28
+ bootPromise: Promise<{
29
+ url: string;
30
+ }> | null;
31
+ booted: boolean;
32
+ closeRequested: boolean;
33
+ closed: boolean;
34
+ url: string;
35
+ stderrTail: any[];
36
+ stderrTailMax: number;
37
+ resolveReady: ((value: any) => void) | null;
38
+ rejectReady: ((reason?: any) => void) | null;
39
+ readyTimer: NodeJS.Timeout | null;
40
+ fixedPortWarningLogged: boolean;
41
+ log(message: any): void;
42
+ boot(): Promise<{
43
+ url: string;
44
+ }>;
45
+ bootInternal(): Promise<{
46
+ url: string;
47
+ }>;
48
+ buildArgs(): string[];
49
+ spawnChild(): Promise<void>;
50
+ handleStdoutLine(line: any, context?: null): void;
51
+ handleStderrLine(line: any, context?: null): void;
52
+ finishReady(): void;
53
+ failReady(error: any): void;
54
+ clearChildContext(context: any): void;
55
+ terminateChild(context: any, { suppressExit }?: {
56
+ suppressExit?: boolean | undefined;
57
+ }): Promise<void>;
58
+ get pid(): number | null;
59
+ getRecentStderr(): any[];
60
+ close(): Promise<void>;
61
+ }
62
+ import { EventEmitter } from "node:events";
@@ -0,0 +1,442 @@
1
+ import { EventEmitter } from "node:events";
2
+ import { spawn } from "node:child_process";
3
+ import net from "node:net";
4
+ import readline from "node:readline";
5
+ import { emitLog, normalizeLogger, parseCommandParts, sanitizeForLog, serializeError, } from "../shared.js";
6
+ const DEFAULT_OPENCODE_COMMAND = "opencode";
7
+ const DEFAULT_HOSTNAME = "127.0.0.1";
8
+ const DEFAULT_BOOT_TIMEOUT_MS = 5000;
9
+ function createBootError(message, extras = {}) {
10
+ const error = new Error(message);
11
+ for (const [key, value] of Object.entries(extras)) {
12
+ error[key] = value;
13
+ }
14
+ return error;
15
+ }
16
+ function parseServerUrlFromOutput(line) {
17
+ const normalized = String(line || "").trim();
18
+ if (!normalized.startsWith("opencode server listening")) {
19
+ return "";
20
+ }
21
+ const match = normalized.match(/on\s+(https?:\/\/[^\s]+)/i);
22
+ return match?.[1] ? String(match[1]).trim() : "";
23
+ }
24
+ function readExplicitServerOptionValue(args, index) {
25
+ const next = args[index + 1];
26
+ if (next === undefined) {
27
+ return { value: "", nextIndex: index };
28
+ }
29
+ const normalized = String(next).trim();
30
+ if (!normalized || normalized.startsWith("--")) {
31
+ return { value: "", nextIndex: index };
32
+ }
33
+ return {
34
+ value: normalized,
35
+ nextIndex: index + 1,
36
+ };
37
+ }
38
+ function parseExplicitServerOptions(args) {
39
+ let hasExplicitHostname = false;
40
+ let explicitHostname = "";
41
+ let hasExplicitPort = false;
42
+ let explicitPortRaw = "";
43
+ for (let index = 0; index < args.length; index += 1) {
44
+ const arg = String(args[index] || "").trim();
45
+ if (!arg) {
46
+ continue;
47
+ }
48
+ if (arg.startsWith("--hostname=")) {
49
+ hasExplicitHostname = true;
50
+ explicitHostname = arg.slice("--hostname=".length).trim();
51
+ continue;
52
+ }
53
+ if (arg === "--hostname") {
54
+ const parsed = readExplicitServerOptionValue(args, index);
55
+ hasExplicitHostname = true;
56
+ explicitHostname = parsed.value;
57
+ index = parsed.nextIndex;
58
+ continue;
59
+ }
60
+ if (arg.startsWith("--port=")) {
61
+ hasExplicitPort = true;
62
+ explicitPortRaw = arg.slice("--port=".length).trim();
63
+ continue;
64
+ }
65
+ if (arg === "--port") {
66
+ const parsed = readExplicitServerOptionValue(args, index);
67
+ hasExplicitPort = true;
68
+ explicitPortRaw = parsed.value;
69
+ index = parsed.nextIndex;
70
+ }
71
+ }
72
+ const explicitPort = /^\d+$/.test(explicitPortRaw) && Number.parseInt(explicitPortRaw, 10) > 0
73
+ ? Number.parseInt(explicitPortRaw, 10)
74
+ : 0;
75
+ return {
76
+ hasExplicitHostname,
77
+ explicitHostname,
78
+ hasExplicitPort,
79
+ explicitPort,
80
+ explicitPortRaw,
81
+ };
82
+ }
83
+ async function allocatePort(hostname) {
84
+ return await new Promise((resolve, reject) => {
85
+ const server = net.createServer();
86
+ server.unref();
87
+ server.on("error", reject);
88
+ server.listen(0, hostname, () => {
89
+ const address = server.address();
90
+ const port = typeof address === "object" && address ? address.port : 0;
91
+ server.close((closeError) => {
92
+ if (closeError) {
93
+ reject(closeError);
94
+ return;
95
+ }
96
+ if (!port) {
97
+ reject(new Error("Failed to allocate opencode server port"));
98
+ return;
99
+ }
100
+ resolve(port);
101
+ });
102
+ });
103
+ });
104
+ }
105
+ export class OpencodeServerTransport extends EventEmitter {
106
+ constructor(options = {}) {
107
+ super();
108
+ this.options = options;
109
+ this.logger = normalizeLogger(options.logger);
110
+ this.cwd =
111
+ typeof options.cwd === "string" && options.cwd.trim()
112
+ ? options.cwd.trim()
113
+ : process.cwd();
114
+ const commandLine = process.env.CONDUCTOR_OPENCODE_COMMAND ||
115
+ options.commandLine ||
116
+ process.env.CONDUCTOR_CLI_COMMAND ||
117
+ DEFAULT_OPENCODE_COMMAND;
118
+ const { command, args } = parseCommandParts(commandLine);
119
+ if (!command) {
120
+ throw new Error("Invalid opencode command");
121
+ }
122
+ this.command = command;
123
+ this.baseArgs = args;
124
+ const explicitServerOptions = parseExplicitServerOptions(args);
125
+ this.hasExplicitHostnameArg = explicitServerOptions.hasExplicitHostname;
126
+ this.hasExplicitPortArg = explicitServerOptions.hasExplicitPort;
127
+ this.explicitPortRaw = explicitServerOptions.explicitPortRaw;
128
+ this.explicitPort = explicitServerOptions.explicitPort;
129
+ this.hostname =
130
+ explicitServerOptions.explicitHostname ||
131
+ (typeof options.hostname === "string" && options.hostname.trim() ? options.hostname.trim() : DEFAULT_HOSTNAME);
132
+ this.port =
133
+ explicitServerOptions.explicitPort ||
134
+ (Number.isFinite(Number(options.port)) && Number(options.port) > 0 ? Number(options.port) : 0);
135
+ this.bootTimeoutMs = Number.isFinite(Number(options.timeout)) && Number(options.timeout) > 0
136
+ ? Number(options.timeout)
137
+ : DEFAULT_BOOT_TIMEOUT_MS;
138
+ this.env = options.env && typeof options.env === "object" ? { ...options.env } : {};
139
+ this.config = options.config && typeof options.config === "object" ? { ...options.config } : {};
140
+ this.child = null;
141
+ this.childContext = null;
142
+ this.stdoutReader = null;
143
+ this.stderrReader = null;
144
+ this.bootPromise = null;
145
+ this.booted = false;
146
+ this.closeRequested = false;
147
+ this.closed = false;
148
+ this.url = "";
149
+ this.stderrTail = [];
150
+ this.stderrTailMax = 20;
151
+ this.resolveReady = null;
152
+ this.rejectReady = null;
153
+ this.readyTimer = null;
154
+ this.fixedPortWarningLogged = false;
155
+ }
156
+ log(message) {
157
+ emitLog(this.logger, message);
158
+ }
159
+ async boot() {
160
+ if (this.booted) {
161
+ return { url: this.url };
162
+ }
163
+ if (this.bootPromise) {
164
+ return this.bootPromise;
165
+ }
166
+ this.bootPromise = this.bootInternal();
167
+ try {
168
+ const result = await this.bootPromise;
169
+ this.booted = true;
170
+ return result;
171
+ }
172
+ finally {
173
+ this.bootPromise = null;
174
+ }
175
+ }
176
+ async bootInternal() {
177
+ if (!this.hasExplicitPortArg && !this.port) {
178
+ this.port = await allocatePort(this.hostname);
179
+ }
180
+ await this.spawnChild();
181
+ if (!this.url) {
182
+ throw createBootError("Opencode server did not expose a URL", {
183
+ reason: "missing_server_url",
184
+ });
185
+ }
186
+ return { url: this.url };
187
+ }
188
+ buildArgs() {
189
+ const args = [...this.baseArgs];
190
+ if (!args.includes("serve")) {
191
+ args.push("serve");
192
+ }
193
+ if (!this.hasExplicitHostnameArg) {
194
+ args.push(`--hostname=${this.hostname}`);
195
+ }
196
+ if (!this.hasExplicitPortArg) {
197
+ args.push(`--port=${this.port}`);
198
+ }
199
+ else if (!this.fixedPortWarningLogged) {
200
+ const configuredPort = this.explicitPort || this.explicitPortRaw || "<unknown>";
201
+ this.log(`[opencode-server] using configured --port ${configuredPort}; concurrent opencode tasks on the same machine may fail to start`);
202
+ this.fixedPortWarningLogged = true;
203
+ }
204
+ return args;
205
+ }
206
+ async spawnChild() {
207
+ if (this.childContext) {
208
+ if (this.url) {
209
+ return;
210
+ }
211
+ await this.terminateChild(this.childContext, { suppressExit: true });
212
+ }
213
+ const args = this.buildArgs();
214
+ this.log(`[opencode-server] spawn ${[this.command, ...args].join(" ")} (cwd: ${this.cwd})`);
215
+ const child = spawn(this.command, args, {
216
+ cwd: this.cwd,
217
+ env: {
218
+ ...process.env,
219
+ ...this.env,
220
+ OPENCODE_CONFIG_CONTENT: JSON.stringify(this.config || {}),
221
+ },
222
+ stdio: ["ignore", "pipe", "pipe"],
223
+ });
224
+ const context = {
225
+ child,
226
+ stdoutReader: null,
227
+ stderrReader: null,
228
+ suppressExit: false,
229
+ onError: null,
230
+ onExit: null,
231
+ };
232
+ this.childContext = context;
233
+ this.child = child;
234
+ this.stdoutReader = null;
235
+ this.stderrReader = null;
236
+ const readyPromise = new Promise((resolve, reject) => {
237
+ this.resolveReady = resolve;
238
+ this.rejectReady = reject;
239
+ this.readyTimer = setTimeout(() => {
240
+ reject(createBootError(`Timed out waiting for opencode server after ${this.bootTimeoutMs}ms`, {
241
+ reason: "boot_timeout",
242
+ stderr: this.stderrTail.slice(),
243
+ }));
244
+ }, this.bootTimeoutMs);
245
+ if (typeof this.readyTimer.unref === "function") {
246
+ this.readyTimer.unref();
247
+ }
248
+ });
249
+ context.stdoutReader = readline.createInterface({ input: child.stdout });
250
+ this.stdoutReader = context.stdoutReader;
251
+ context.stdoutReader.on("line", (line) => {
252
+ this.handleStdoutLine(line, context);
253
+ });
254
+ context.stderrReader = readline.createInterface({ input: child.stderr });
255
+ this.stderrReader = context.stderrReader;
256
+ context.stderrReader.on("line", (line) => {
257
+ this.handleStderrLine(line, context);
258
+ });
259
+ context.onError = (error) => {
260
+ if (this.childContext !== context || context.suppressExit) {
261
+ return;
262
+ }
263
+ this.failReady(error);
264
+ this.emit("process_error", serializeError(error));
265
+ };
266
+ child.on("error", context.onError);
267
+ context.onExit = (code, signal) => {
268
+ const info = {
269
+ code,
270
+ signal,
271
+ stderr: this.stderrTail.slice(),
272
+ };
273
+ const isActiveContext = this.childContext === context;
274
+ this.clearChildContext(context);
275
+ if (!isActiveContext || context.suppressExit) {
276
+ return;
277
+ }
278
+ const error = createBootError(this.closeRequested
279
+ ? "Opencode server closed"
280
+ : `Opencode server exited (code=${code ?? "null"} signal=${signal ?? "null"})`, {
281
+ reason: this.closeRequested ? "session_closed" : "transport_exited",
282
+ code,
283
+ signal,
284
+ stderr: info.stderr,
285
+ });
286
+ this.failReady(error);
287
+ this.closed = true;
288
+ this.emit("process_exit", info);
289
+ };
290
+ child.on("exit", context.onExit);
291
+ try {
292
+ await readyPromise;
293
+ }
294
+ catch (error) {
295
+ this.failReady(error);
296
+ await this.terminateChild(context, { suppressExit: true });
297
+ throw error;
298
+ }
299
+ }
300
+ handleStdoutLine(line, context = null) {
301
+ if (context && (this.childContext !== context || context.suppressExit)) {
302
+ return;
303
+ }
304
+ const normalized = String(line || "").trim();
305
+ if (!normalized) {
306
+ return;
307
+ }
308
+ const url = parseServerUrlFromOutput(normalized);
309
+ if (url) {
310
+ this.url = url;
311
+ this.log(`[opencode-server] ready ${url}`);
312
+ this.finishReady();
313
+ return;
314
+ }
315
+ this.log(`[opencode-server] stdout ${sanitizeForLog(normalized, 300)}`);
316
+ }
317
+ handleStderrLine(line, context = null) {
318
+ if (context && (this.childContext !== context || context.suppressExit)) {
319
+ return;
320
+ }
321
+ const normalized = String(line || "");
322
+ if (!normalized.trim()) {
323
+ return;
324
+ }
325
+ this.stderrTail.push(normalized);
326
+ if (this.stderrTail.length > this.stderrTailMax) {
327
+ this.stderrTail.shift();
328
+ }
329
+ this.log(`[opencode-server] stderr ${sanitizeForLog(normalized, 300)}`);
330
+ this.emit("stderr", { line: normalized });
331
+ }
332
+ finishReady() {
333
+ if (this.readyTimer) {
334
+ clearTimeout(this.readyTimer);
335
+ this.readyTimer = null;
336
+ }
337
+ this.resolveReady?.({ url: this.url });
338
+ this.resolveReady = null;
339
+ this.rejectReady = null;
340
+ }
341
+ failReady(error) {
342
+ if (this.readyTimer) {
343
+ clearTimeout(this.readyTimer);
344
+ this.readyTimer = null;
345
+ }
346
+ this.rejectReady?.(error);
347
+ this.resolveReady = null;
348
+ this.rejectReady = null;
349
+ this.booted = false;
350
+ this.url = "";
351
+ }
352
+ clearChildContext(context) {
353
+ if (!context) {
354
+ return;
355
+ }
356
+ if (context.stdoutReader) {
357
+ context.stdoutReader.removeAllListeners();
358
+ context.stdoutReader.close();
359
+ }
360
+ if (context.stderrReader) {
361
+ context.stderrReader.removeAllListeners();
362
+ context.stderrReader.close();
363
+ }
364
+ if (context.child && typeof context.onError === "function") {
365
+ context.child.off("error", context.onError);
366
+ }
367
+ if (context.child && typeof context.onExit === "function") {
368
+ context.child.off("exit", context.onExit);
369
+ }
370
+ if (this.childContext === context) {
371
+ this.childContext = null;
372
+ this.child = null;
373
+ this.stdoutReader = null;
374
+ this.stderrReader = null;
375
+ }
376
+ }
377
+ async terminateChild(context, { suppressExit = false } = {}) {
378
+ if (!context?.child) {
379
+ this.clearChildContext(context);
380
+ return;
381
+ }
382
+ context.suppressExit = context.suppressExit || suppressExit;
383
+ const child = context.child;
384
+ if (child.exitCode !== null || child.signalCode !== null) {
385
+ this.clearChildContext(context);
386
+ return;
387
+ }
388
+ await new Promise((resolve) => {
389
+ let settled = false;
390
+ let timer = null;
391
+ const finalize = () => {
392
+ if (settled) {
393
+ return;
394
+ }
395
+ settled = true;
396
+ child.off("exit", finalize);
397
+ if (timer) {
398
+ clearTimeout(timer);
399
+ }
400
+ resolve();
401
+ };
402
+ child.once("exit", finalize);
403
+ try {
404
+ child.kill("SIGTERM");
405
+ }
406
+ catch {
407
+ finalize();
408
+ return;
409
+ }
410
+ timer = setTimeout(() => {
411
+ try {
412
+ child.kill("SIGKILL");
413
+ }
414
+ catch {
415
+ // ignore
416
+ }
417
+ }, 1500);
418
+ if (typeof timer.unref === "function") {
419
+ timer.unref();
420
+ }
421
+ });
422
+ this.clearChildContext(context);
423
+ }
424
+ get pid() {
425
+ return this.child?.pid || null;
426
+ }
427
+ getRecentStderr() {
428
+ return this.stderrTail.slice();
429
+ }
430
+ async close() {
431
+ if (this.closed) {
432
+ return;
433
+ }
434
+ this.closeRequested = true;
435
+ if (!this.childContext) {
436
+ this.closed = true;
437
+ return;
438
+ }
439
+ await this.terminateChild(this.childContext);
440
+ this.closed = true;
441
+ }
442
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@love-moon/ai-sdk",
3
- "version": "0.2.18",
3
+ "version": "0.2.19",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@anthropic-ai/claude-agent-sdk": "^0.2.72",
20
+ "@opencode-ai/sdk": "^1.2.25",
20
21
  "js-yaml": "^4.1.1",
21
22
  "zod": "^4.1.5"
22
23
  },
@@ -24,5 +25,5 @@
24
25
  "@types/node": "^22.10.2",
25
26
  "typescript": "^5.6.3"
26
27
  },
27
- "gitCommitId": "942418b"
28
+ "gitCommitId": "346e048"
28
29
  }