@opencode/client 0.0.0-dev-20558 → 0.0.0-dev-20560

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.
@@ -5,12 +5,12 @@ import {
5
5
  ClientError3
6
6
  } from "./service-7f0py9js.js";
7
7
  import {
8
- make5
9
- } from "./service-5yf5aaev.js";
8
+ make6
9
+ } from "./service-d59rj3hx.js";
10
10
  import {
11
- make6,
11
+ make5,
12
12
  aborted2
13
- } from "./service-w1w809zr.js";
13
+ } from "./service-mnyft54j.js";
14
14
  import {
15
15
  __export
16
16
  } from "./service-9rqn6x4v.js";
@@ -30,7 +30,7 @@ var CurrentHeaders = Context.Reference("@opencode/client/effect/rpc/headers", {
30
30
  });
31
31
  var make4 = Effect.fn("OpenCode.make")(function* (options) {
32
32
  const httpClient = yield* HttpClient.HttpClient;
33
- const raw = yield* make5(options).pipe(Effect.provideService(HttpClient.HttpClient, HttpClient.mapRequestEffect(httpClient, (request) => Effect.map(CurrentHeaders, (headers) => headers ? HttpClientRequest.setHeaders(request, new Headers(headers)) : request))));
33
+ const raw = yield* make6(options).pipe(Effect.provideService(HttpClient.HttpClient, HttpClient.mapRequestEffect(httpClient, (request) => Effect.map(CurrentHeaders, (headers) => headers ? HttpClientRequest.setHeaders(request, new Headers(headers)) : request))));
34
34
  const context = yield* Effect.context();
35
35
  const native = raw.event.subscribe();
36
36
 
@@ -45,7 +45,7 @@ var make4 = Effect.fn("OpenCode.make")(function* (options) {
45
45
  return {
46
46
  ...raw,
47
47
  event: { ...raw.event, subscribe },
48
- rpc: Object.assign(make6((input, options) => raw.rpc.call(input).pipe(Effect.provideService(CurrentHeaders, options?.headers)), subscribe), raw.rpc)
48
+ rpc: Object.assign(make5((input, options) => raw.rpc.call(input).pipe(Effect.provideService(CurrentHeaders, options?.headers)), subscribe), raw.rpc)
49
49
  };
50
50
  });
51
51
 
@@ -0,0 +1,75 @@
1
+ import {
2
+ defaultEnsureTiming2
3
+ } from "./service-xgnj2w4v.js";
4
+
5
+ // src/service-probe.ts
6
+ import { homedir } from "node:os";
7
+ import { join } from "node:path";
8
+ function fallback2() {
9
+ return join(process.env["XDG_STATE_HOME"] ?? join(homedir(), ".local", "state"), "opencode", "service.json");
10
+ }
11
+ function same2(left, right) {
12
+ return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
13
+ }
14
+ async function probeResult2(info, timeout = defaultEnsureTiming2.requestTimeout) {
15
+ const endpoint = {
16
+ url: info.url,
17
+ auth: info.password === undefined ? undefined : { type: "basic", username: "opencode", password: info.password }
18
+ };
19
+ const signal = AbortSignal.timeout(timeout);
20
+ const result = await fetch(new URL("/api/info", info.url), { headers: headers2(endpoint), signal }).then(async (response) => ({
21
+ response,
22
+ body: response.status === 404 ? undefined : await response.json()
23
+ })).catch(() => {
24
+ return;
25
+ });
26
+ if (result === undefined)
27
+ return { service: undefined, timedOut: signal.aborted };
28
+ const response = result.response;
29
+ if (response.status === 404)
30
+ return {
31
+ service: {
32
+ info,
33
+ endpoint,
34
+ version: info.version,
35
+ state: "ready",
36
+ compatible: false
37
+ },
38
+ timedOut: false
39
+ };
40
+ const serverInfo = decodeInfo(result.body);
41
+ if (serverInfo === undefined)
42
+ return { service: undefined, timedOut: false };
43
+ if (serverInfo.pid !== info.pid)
44
+ return { service: undefined, timedOut: false };
45
+ if (info.version !== undefined && serverInfo.version !== info.version)
46
+ return { service: undefined, timedOut: false };
47
+ return {
48
+ service: {
49
+ info,
50
+ endpoint,
51
+ version: serverInfo.version,
52
+ state: response.ok ? "ready" : response.status === 500 ? "failed" : "waiting",
53
+ compatible: true
54
+ },
55
+ timedOut: false
56
+ };
57
+ }
58
+ function headers2(endpoint) {
59
+ if (endpoint.auth === undefined)
60
+ return;
61
+ return {
62
+ authorization: "Basic " + Buffer.from(endpoint.auth.username + ":" + endpoint.auth.password).toString("base64")
63
+ };
64
+ }
65
+ function decodeInfo(input) {
66
+ if (typeof input !== "object" || input === null)
67
+ return;
68
+ if (!("version" in input) || typeof input.version !== "string")
69
+ return;
70
+ if (!("pid" in input) || typeof input.pid !== "number" || !Number.isInteger(input.pid) || input.pid < 0)
71
+ return;
72
+ return { version: input.version, pid: input.pid };
73
+ }
74
+
75
+ export { fallback2, same2, probeResult2, headers2 };
@@ -11,7 +11,7 @@ import {
11
11
  // src/effect/generated/client.ts
12
12
  var exports_client4 = {};
13
13
  __export(exports_client4, {
14
- make: () => make5
14
+ make: () => make6
15
15
  });
16
16
  import { Effect, Stream, Schema } from "effect";
17
17
  import { Sse } from "effect/unstable/encoding";
@@ -613,6 +613,6 @@ var adaptClient = (raw) => ({
613
613
  websearch: adaptGroupWebsearch(raw["server.websearch"]),
614
614
  config: adaptGroupConfig(raw["server.config"])
615
615
  });
616
- var make5 = (options) => HttpApiClient.make(ClientApi2, options).pipe(Effect.map(adaptClient));
616
+ var make6 = (options) => HttpApiClient.make(ClientApi2, options).pipe(Effect.map(adaptClient));
617
617
 
618
- export { make5, exports_client4 };
618
+ export { make6, exports_client4 };
@@ -13,10 +13,10 @@ var exports_rpc = {};
13
13
  __export(exports_rpc, {
14
14
  RpcClientRuntime: () => exports_rpc,
15
15
  aborted: () => aborted2,
16
- make: () => make6
16
+ make: () => make5
17
17
  });
18
18
  import { Effect, Stream } from "effect";
19
- function make6(call, subscribe) {
19
+ function make5(call, subscribe) {
20
20
  return (definition) => {
21
21
  const methods = Object.fromEntries(Object.entries(definition.methods).map(([name, method]) => [
22
22
  name,
@@ -59,4 +59,4 @@ function aborted2(signal) {
59
59
  });
60
60
  }
61
61
 
62
- export { make6, aborted2, exports_rpc };
62
+ export { make5, aborted2, exports_rpc };
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  make4,
3
3
  exports_client3
4
- } from "../chunks/service-vvwt8khz.js";
4
+ } from "../chunks/service-7fhbdh77.js";
5
5
  import"../chunks/service-6mptkzbc.js";
6
- import"../chunks/service-5yf5aaev.js";
6
+ import"../chunks/service-mnyft54j.js";
7
+ import"../chunks/service-d59rj3hx.js";
7
8
  import"../chunks/service-7f0py9js.js";
8
- import"../chunks/service-w1w809zr.js";
9
- import"../chunks/service-vmytr4q8.js";
10
- import"../chunks/service-t1xa31vm.js";
11
9
  import"../chunks/service-frbwqjmf.js";
10
+ import"../chunks/service-t1xa31vm.js";
11
+ import"../chunks/service-vmytr4q8.js";
12
12
  export {
13
13
  exports_client3 as OpenCode,
14
14
  make4 as make
@@ -1,8 +1,8 @@
1
1
  import {
2
- make5
3
- } from "../../chunks/service-5yf5aaev.js";
2
+ make6
3
+ } from "../../chunks/service-d59rj3hx.js";
4
4
  import"../../chunks/service-7f0py9js.js";
5
5
  import"../../chunks/service-vmytr4q8.js";
6
6
  export {
7
- make5 as make
7
+ make6 as make
8
8
  };
@@ -1,7 +1,7 @@
1
1
  import"../../chunks/service-6mptkzbc.js";
2
2
  import {
3
3
  exports_client4
4
- } from "../../chunks/service-5yf5aaev.js";
4
+ } from "../../chunks/service-d59rj3hx.js";
5
5
  import {
6
6
  ClientError3
7
7
  } from "../../chunks/service-7f0py9js.js";
@@ -2,14 +2,14 @@ import {
2
2
  ClientError3
3
3
  } from "../chunks/service-7f0py9js.js";
4
4
  import"../chunks/service-vmytr4q8.js";
5
- import"../chunks/service-5yf5aaev.js";
5
+ import"../chunks/service-d59rj3hx.js";
6
6
  import"../chunks/service-6mptkzbc.js";
7
7
  import"../chunks/service-frbwqjmf.js";
8
8
  import"../chunks/service-t1xa31vm.js";
9
- import"../chunks/service-w1w809zr.js";
9
+ import"../chunks/service-mnyft54j.js";
10
10
  import {
11
11
  exports_client3
12
- } from "../chunks/service-vvwt8khz.js";
12
+ } from "../chunks/service-7fhbdh77.js";
13
13
 
14
14
  // src/effect/index.ts
15
15
  import { Agent } from "@opencode/schema/agent";
@@ -1,11 +1,11 @@
1
1
  import {
2
- make6,
2
+ make5,
3
3
  aborted2,
4
4
  exports_rpc
5
- } from "../chunks/service-w1w809zr.js";
5
+ } from "../chunks/service-mnyft54j.js";
6
6
  import"../chunks/service-t1xa31vm.js";
7
7
  export {
8
8
  exports_rpc as RpcClientRuntime,
9
9
  aborted2 as aborted,
10
- make6 as make
10
+ make5 as make
11
11
  };
@@ -1,6 +1,8 @@
1
1
  import { Effect, FileSystem, Schema } from "effect";
2
- import type { DiscoverOptions, Endpoint, EnsureOptions, StopOptions } from "../service.js";
2
+ import type { DiscoverOptions, EnsureOptions, StopOptions } from "../service.js";
3
+ import { headers } from "../service-probe.js";
3
4
  export * from "../service.js";
5
+ export { headers };
4
6
  /** Contents of the local service registration file. */
5
7
  export type Info = import("../service.js").Info;
6
8
  /** Discover a healthy, compatible local service without starting one. */
@@ -27,13 +29,9 @@ export declare const incumbent: (options: DiscoverOptions & {
27
29
  state: "failed" | "ready" | "waiting";
28
30
  } | undefined, never, FileSystem.FileSystem>;
29
31
  /** Ensure a healthy, compatible local service is running. */
30
- export declare const ensure: (options?: EnsureOptions | undefined) => Effect.Effect<Endpoint, Error, FileSystem.FileSystem>;
32
+ export declare const ensure: (options?: EnsureOptions | undefined) => Effect.Effect<import("./service.js").Endpoint, Error, FileSystem.FileSystem>;
31
33
  /** Stop the registered local service. */
32
34
  export declare const stop: (options?: StopOptions | undefined) => Effect.Effect<void, Error, FileSystem.FileSystem>;
33
- /** Create HTTP authentication headers for a service endpoint. */
34
- export declare function headers(endpoint: Endpoint): {
35
- authorization: string;
36
- } | undefined;
37
35
  /** Schema for the local service registration file. */
38
36
  export declare const Info: Schema.Struct<{
39
37
  readonly id: Schema.optional<Schema.String>;
@@ -65,7 +63,7 @@ export declare const Service: {
65
63
  };
66
64
  state: "failed" | "ready" | "waiting";
67
65
  } | undefined, never, FileSystem.FileSystem>;
68
- ensure: (options?: EnsureOptions | undefined) => Effect.Effect<Endpoint, Error, FileSystem.FileSystem>;
66
+ ensure: (options?: EnsureOptions | undefined) => Effect.Effect<import("./service.js").Endpoint, Error, FileSystem.FileSystem>;
69
67
  stop: (options?: StopOptions | undefined) => Effect.Effect<void, Error, FileSystem.FileSystem>;
70
68
  headers: typeof headers;
71
69
  Info: Schema.Struct<{
@@ -16,12 +16,16 @@ import {
16
16
  complete2,
17
17
  clear2
18
18
  } from "../chunks/service-71wkqa80.js";
19
+ import {
20
+ fallback2,
21
+ same2,
22
+ probeResult2,
23
+ headers2
24
+ } from "../chunks/service-ank0d71z.js";
19
25
  import"../chunks/service-qav6mfdz.js";
20
26
 
21
27
  // src/effect/service.ts
22
28
  import { Effect, FileSystem, Option, Schedule, Schema } from "effect";
23
- import { homedir } from "node:os";
24
- import { join } from "node:path";
25
29
  var discover = Effect.fn("service.discover")(function* (options = {}) {
26
30
  const found = (yield* registered(options.file)).service;
27
31
  if (found?.state !== "ready")
@@ -34,10 +38,10 @@ var discover = Effect.fn("service.discover")(function* (options = {}) {
34
38
  });
35
39
  var incumbent = Effect.fn("service.incumbent")(function* (options) {
36
40
  const info = yield* read(options.file);
37
- const found = info === undefined ? undefined : yield* probe({ ...info, url: options.url });
38
- if (found === undefined)
41
+ if (info === undefined)
39
42
  return;
40
- if (!found.compatible)
43
+ const found = (yield* Effect.promise(() => probeResult2({ ...info, url: options.url }))).service;
44
+ if (!found?.compatible)
41
45
  return;
42
46
  if (!matchesVersion2(found.version, options))
43
47
  return;
@@ -61,7 +65,7 @@ var ensure = Effect.fn("service.ensure")(function* (options = {}) {
61
65
  const [command, ...args] = options.command ?? ["opencode", "serve", "--service"];
62
66
  if (command === undefined)
63
67
  return yield* Effect.fail(new Error("Missing service command"));
64
- const env = yield* Effect.tryPromise(() => environment2(options.file ?? fallback(), options.env));
68
+ const env = yield* Effect.tryPromise(() => environment2(options.file ?? fallback2(), options.env));
65
69
  return yield* Effect.try({
66
70
  try: () => {
67
71
  return spawnServiceContender2(command, args, env);
@@ -76,12 +80,12 @@ var ensure = Effect.fn("service.ensure")(function* (options = {}) {
76
80
  if (registration.timedOut && info !== undefined) {
77
81
  timeouts = {
78
82
  info,
79
- count: timeouts !== undefined && same(timeouts.info, info) ? timeouts.count + 1 : 1
83
+ count: timeouts !== undefined && same2(timeouts.info, info) ? timeouts.count + 1 : 1
80
84
  };
81
85
  if (timeouts.count >= 3) {
82
86
  yield* announce("missing");
83
87
  yield* Effect.logWarning("Background service is unresponsive; recovery cannot preserve persistent terminals");
84
- yield* Effect.tryPromise(() => clear2(options.file ?? fallback()));
88
+ yield* Effect.tryPromise(() => clear2(options.file ?? fallback2()));
85
89
  yield* terminate(info, options, timing);
86
90
  for (const item of contenders) {
87
91
  if (item.child.pid === info.pid || contenderFinished2(item)) {
@@ -102,7 +106,7 @@ var ensure = Effect.fn("service.ensure")(function* (options = {}) {
102
106
  if (!service2.compatible && versionMatches)
103
107
  return yield* Effect.fail(new Error("Background service uses an incompatible health protocol. Update this client or explicitly restart the service."));
104
108
  if (compatible && service2.state === "ready") {
105
- yield* Effect.tryPromise(() => complete2(options.file ?? fallback(), service2.info));
109
+ yield* Effect.tryPromise(() => complete2(options.file ?? fallback2(), service2.info));
106
110
  return Option.some(service2);
107
111
  }
108
112
  if (compatible && service2.state === "failed")
@@ -151,19 +155,10 @@ var ensure = Effect.fn("service.ensure")(function* (options = {}) {
151
155
  });
152
156
  var stop = Effect.fn("service.stop")(function* (options = {}) {
153
157
  const info = yield* read(options.file);
154
- yield* Effect.tryPromise(() => options.pty === "handoff" && info !== undefined ? prepare2(options.file ?? fallback(), info, defaultEnsureTiming2.requestTimeout) : clear2(options.file ?? fallback())).pipe(Effect.catch((cause) => Effect.logWarning("Failed to prepare persistent terminals for replacement", cause)));
158
+ yield* Effect.tryPromise(() => options.pty === "handoff" && info !== undefined ? prepare2(options.file ?? fallback2(), info, defaultEnsureTiming2.requestTimeout) : clear2(options.file ?? fallback2())).pipe(Effect.catch((cause) => Effect.logWarning("Failed to prepare persistent terminals for replacement", cause)));
155
159
  if (info !== undefined)
156
160
  yield* terminate(info, options, ensureTiming2(options));
157
161
  });
158
- function fallback() {
159
- const state = process.env["XDG_STATE_HOME"] ?? join(homedir(), ".local", "state");
160
- return join(state, "opencode", "service.json");
161
- }
162
- function headers(endpoint) {
163
- if (endpoint.auth === undefined)
164
- return;
165
- return { authorization: "Basic " + btoa(endpoint.auth.username + ":" + endpoint.auth.password) };
166
- }
167
162
  var Info = Schema.Struct({
168
163
  id: Schema.optional(Schema.String),
169
164
  version: Schema.optional(Schema.String),
@@ -172,69 +167,18 @@ var Info = Schema.Struct({
172
167
  password: Schema.optional(Schema.String)
173
168
  });
174
169
  var decode = Schema.decodeUnknownEffect(Schema.fromJsonString(Info));
175
- var decodeInfo = Schema.decodeUnknownOption(Schema.Struct({
176
- version: Schema.String,
177
- pid: Schema.Int.check(Schema.isGreaterThanOrEqualTo(0))
178
- }));
179
170
  var read = Effect.fnUntraced(function* (file) {
180
171
  const fs = yield* FileSystem.FileSystem;
181
- const text = yield* fs.readFileString(file ?? fallback()).pipe(Effect.option);
172
+ const text = yield* fs.readFileString(file ?? fallback2()).pipe(Effect.option);
182
173
  if (Option.isNone(text))
183
174
  return;
184
175
  return yield* decode(text.value).pipe(Effect.option, Effect.map(Option.getOrUndefined));
185
176
  });
186
- var probe = Effect.fnUntraced(function* (info) {
187
- return (yield* probeResult(info)).service;
188
- });
189
- var probeResult = Effect.fnUntraced(function* (info, timeout = defaultEnsureTiming2.requestTimeout) {
190
- const endpoint = {
191
- url: info.url,
192
- auth: info.password === undefined ? undefined : { type: "basic", username: "opencode", password: info.password }
193
- };
194
- const signal = AbortSignal.timeout(timeout);
195
- const result = yield* Effect.promise(() => fetch(new URL("/api/info", info.url), { headers: headers(endpoint), signal }).then(async (response) => ({
196
- response,
197
- body: response.status === 404 ? undefined : await response.json()
198
- })).then((value) => ({ value }), (cause) => ({ cause })));
199
- if ("cause" in result)
200
- return { service: undefined, timedOut: signal.aborted };
201
- const response = result.value.response;
202
- if (response.status === 404)
203
- return {
204
- service: {
205
- info,
206
- endpoint,
207
- version: info.version,
208
- state: "ready",
209
- compatible: false
210
- },
211
- timedOut: false
212
- };
213
- const body = result.value.body;
214
- const serverInfo = decodeInfo(body);
215
- if (Option.isSome(serverInfo)) {
216
- if (serverInfo.value.pid !== info.pid)
217
- return { service: undefined, timedOut: false };
218
- if (info.version !== undefined && serverInfo.value.version !== info.version)
219
- return { service: undefined, timedOut: false };
220
- return {
221
- service: {
222
- info,
223
- endpoint,
224
- version: serverInfo.value.version,
225
- state: response.ok ? "ready" : response.status === 500 ? "failed" : "waiting",
226
- compatible: true
227
- },
228
- timedOut: false
229
- };
230
- }
231
- return { service: undefined, timedOut: false };
232
- });
233
177
  var registered = Effect.fnUntraced(function* (file, timeout) {
234
178
  const info = yield* read(file);
235
179
  if (info === undefined)
236
180
  return { info: undefined, service: undefined, timedOut: false };
237
- return { info, ...yield* probeResult(info, timeout) };
181
+ return { info, ...yield* Effect.promise(() => probeResult2(info, timeout)) };
238
182
  });
239
183
  var poll = (timing) => Schedule.max([Schedule.spaced(timing.stopPollInterval), Schedule.recurs(timing.stopPollAttempts)]);
240
184
  var signal = (pid, name) => Effect.try({ try: () => process.kill(pid, name), catch: (cause) => cause }).pipe(Effect.ignore);
@@ -244,12 +188,9 @@ var stopped = Effect.fnUntraced(function* (pid) {
244
188
  return true;
245
189
  return yield* Effect.fail(new Error(`Server process ${pid} is still running`));
246
190
  });
247
- function same(left, right) {
248
- return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
249
- }
250
191
  var terminate = Effect.fnUntraced(function* (info, options, timing) {
251
192
  const current = yield* read(options.file);
252
- if (current === undefined || !same(current, info))
193
+ if (current === undefined || !same2(current, info))
253
194
  return;
254
195
  yield* signal(info.pid, "SIGTERM");
255
196
  const done = yield* stopped(info.pid).pipe(Effect.retry(poll(timing)), Effect.option);
@@ -258,18 +199,18 @@ var terminate = Effect.fnUntraced(function* (info, options, timing) {
258
199
  yield* stopped(info.pid).pipe(Effect.retry(poll(timing)));
259
200
  }
260
201
  const latest = yield* read(options.file);
261
- if (latest === undefined || !same(latest, info))
202
+ if (latest === undefined || !same2(latest, info))
262
203
  return;
263
204
  const fs = yield* FileSystem.FileSystem;
264
- yield* fs.remove(options.file ?? fallback()).pipe(Effect.ignore);
205
+ yield* fs.remove(options.file ?? fallback2()).pipe(Effect.ignore);
265
206
  });
266
- var Service = { discover, incumbent, ensure, stop, headers, Info };
207
+ var Service = { discover, incumbent, ensure, stop, headers: headers2, Info };
267
208
  export {
268
209
  Info,
269
210
  Service,
270
211
  discover,
271
212
  ensure,
272
- headers,
213
+ headers2 as headers,
273
214
  incumbent,
274
215
  stop
275
216
  };
@@ -1,5 +1,7 @@
1
1
  import type { DiscoverOptions, Endpoint, EnsureOptions, StopOptions } from "../service.js";
2
+ import { headers } from "../service-probe.js";
2
3
  export * from "../service.js";
4
+ export { headers };
3
5
  /** Discover a healthy, compatible local service without starting one. */
4
6
  export declare function discover(options?: DiscoverOptions): Promise<{
5
7
  url: string;
@@ -13,10 +15,6 @@ export declare function discover(options?: DiscoverOptions): Promise<{
13
15
  export declare function ensure(options?: EnsureOptions): Promise<Endpoint>;
14
16
  /** Stop the registered local service. */
15
17
  export declare function stop(options?: StopOptions): Promise<void>;
16
- /** Create HTTP authentication headers for a service endpoint. */
17
- export declare function headers(endpoint: Endpoint): {
18
- authorization: string;
19
- } | undefined;
20
18
  /** Promise-based local service lifecycle operations. */
21
19
  export declare const Service: {
22
20
  discover: typeof discover;
@@ -16,12 +16,16 @@ import {
16
16
  complete2,
17
17
  clear2
18
18
  } from "../chunks/service-71wkqa80.js";
19
+ import {
20
+ fallback2,
21
+ same2,
22
+ probeResult2,
23
+ headers2
24
+ } from "../chunks/service-ank0d71z.js";
19
25
  import"../chunks/service-qav6mfdz.js";
20
26
 
21
27
  // src/promise/service.ts
22
28
  import { readFile, rm } from "node:fs/promises";
23
- import { homedir } from "node:os";
24
- import { join } from "node:path";
25
29
  async function discover(options = {}) {
26
30
  const found = (await registered(options.file)).service;
27
31
  if (found?.state !== "ready")
@@ -52,7 +56,7 @@ async function ensure(options = {}) {
52
56
  if (command === undefined)
53
57
  throw new Error("Missing service command");
54
58
  try {
55
- return spawnServiceContender2(command, args, await environment2(options.file ?? fallback(), options.env));
59
+ return spawnServiceContender2(command, args, await environment2(options.file ?? fallback2(), options.env));
56
60
  } catch (cause) {
57
61
  throw new Error("Failed to start server", { cause });
58
62
  }
@@ -65,12 +69,12 @@ async function ensure(options = {}) {
65
69
  if (registration.timedOut && registration.info !== undefined) {
66
70
  timeouts = {
67
71
  info: registration.info,
68
- count: timeouts !== undefined && same(timeouts.info, registration.info) ? timeouts.count + 1 : 1
72
+ count: timeouts !== undefined && same2(timeouts.info, registration.info) ? timeouts.count + 1 : 1
69
73
  };
70
74
  if (timeouts.count >= 3) {
71
75
  announce("missing");
72
76
  console.warn("Background service is unresponsive; recovery cannot preserve persistent terminals");
73
- await clear2(options.file ?? fallback());
77
+ await clear2(options.file ?? fallback2());
74
78
  await terminate(registration.info, options, timing);
75
79
  for (const item of contenders) {
76
80
  if (item.child.pid === registration.info.pid || contenderFinished2(item)) {
@@ -92,7 +96,7 @@ async function ensure(options = {}) {
92
96
  if (!service2.compatible && versionMatches)
93
97
  throw new Error("Background service uses an incompatible health protocol. Update this client or explicitly restart the service.");
94
98
  if (compatible && service2.state === "ready") {
95
- await complete2(options.file ?? fallback(), service2.info);
99
+ await complete2(options.file ?? fallback2(), service2.info);
96
100
  return service2.endpoint;
97
101
  }
98
102
  if (compatible && service2.state === "failed")
@@ -141,22 +145,12 @@ async function ensure(options = {}) {
141
145
  }
142
146
  async function stop(options = {}) {
143
147
  const info = await read(options.file);
144
- await (options.pty === "handoff" && info !== undefined ? prepare2(options.file ?? fallback(), info, defaultEnsureTiming2.requestTimeout) : clear2(options.file ?? fallback())).catch((cause) => console.warn("Failed to prepare persistent terminals for replacement", cause));
148
+ await (options.pty === "handoff" && info !== undefined ? prepare2(options.file ?? fallback2(), info, defaultEnsureTiming2.requestTimeout) : clear2(options.file ?? fallback2())).catch((cause) => console.warn("Failed to prepare persistent terminals for replacement", cause));
145
149
  if (info !== undefined)
146
150
  await terminate(info, options, ensureTiming2(options));
147
151
  }
148
- function fallback() {
149
- return join(process.env["XDG_STATE_HOME"] ?? join(homedir(), ".local", "state"), "opencode", "service.json");
150
- }
151
- function headers(endpoint) {
152
- if (endpoint.auth === undefined)
153
- return;
154
- return {
155
- authorization: "Basic " + Buffer.from(endpoint.auth.username + ":" + endpoint.auth.password).toString("base64")
156
- };
157
- }
158
152
  async function read(file) {
159
- const text = await readFile(file ?? fallback(), "utf8").catch(() => {
153
+ const text = await readFile(file ?? fallback2(), "utf8").catch(() => {
160
154
  return;
161
155
  });
162
156
  if (text === undefined)
@@ -167,63 +161,11 @@ async function read(file) {
167
161
  return;
168
162
  }
169
163
  }
170
- async function probeResult(info, timeout = defaultEnsureTiming2.requestTimeout) {
171
- const endpoint = {
172
- url: info.url,
173
- auth: info.password === undefined ? undefined : { type: "basic", username: "opencode", password: info.password }
174
- };
175
- const signal = AbortSignal.timeout(timeout);
176
- const result = await fetch(new URL("/api/info", info.url), { headers: headers(endpoint), signal }).then(async (response) => ({
177
- response,
178
- body: response.status === 404 ? undefined : await response.json()
179
- })).then((value) => ({ value }), (cause) => ({ cause }));
180
- if ("cause" in result)
181
- return { service: undefined, timedOut: signal.aborted };
182
- const response = result.value.response;
183
- if (response.status === 404)
184
- return {
185
- service: {
186
- info,
187
- endpoint,
188
- version: info.version,
189
- state: "ready",
190
- compatible: false
191
- },
192
- timedOut: false
193
- };
194
- const serverInfo = decodeInfo(result.value.body);
195
- if (serverInfo !== undefined) {
196
- if (serverInfo.pid !== info.pid)
197
- return { service: undefined, timedOut: false };
198
- if (info.version !== undefined && serverInfo.version !== info.version)
199
- return { service: undefined, timedOut: false };
200
- return {
201
- service: {
202
- info,
203
- endpoint,
204
- version: serverInfo.version,
205
- state: response.ok ? "ready" : response.status === 500 ? "failed" : "waiting",
206
- compatible: true
207
- },
208
- timedOut: false
209
- };
210
- }
211
- return { service: undefined, timedOut: false };
212
- }
213
- function decodeInfo(input) {
214
- if (typeof input !== "object" || input === null)
215
- return;
216
- if (!("version" in input) || typeof input.version !== "string")
217
- return;
218
- if (!("pid" in input) || typeof input.pid !== "number" || !Number.isInteger(input.pid) || input.pid < 0)
219
- return;
220
- return { version: input.version, pid: input.pid };
221
- }
222
164
  async function registered(file, timeout) {
223
165
  const info = await read(file);
224
166
  if (info === undefined)
225
167
  return { info: undefined, service: undefined, timedOut: false };
226
- return { info, ...await probeResult(info, timeout) };
168
+ return { info, ...await probeResult2(info, timeout) };
227
169
  }
228
170
  function signal(pid, name) {
229
171
  try {
@@ -247,12 +189,9 @@ async function waitUntilStopped(pid, timing) {
247
189
  }
248
190
  return false;
249
191
  }
250
- function same(left, right) {
251
- return left.id === right.id && left.version === right.version && left.url === right.url && left.pid === right.pid;
252
- }
253
192
  async function terminate(info, options, timing) {
254
193
  const current = await read(options.file);
255
- if (current === undefined || !same(current, info))
194
+ if (current === undefined || !same2(current, info))
256
195
  return;
257
196
  signal(info.pid, "SIGTERM");
258
197
  if (!await waitUntilStopped(info.pid, timing)) {
@@ -261,18 +200,18 @@ async function terminate(info, options, timing) {
261
200
  throw new Error(`Server process ${info.pid} is still running`);
262
201
  }
263
202
  const latest = await read(options.file);
264
- if (latest === undefined || !same(latest, info))
203
+ if (latest === undefined || !same2(latest, info))
265
204
  return;
266
- await rm(options.file ?? fallback(), { force: true });
205
+ await rm(options.file ?? fallback2(), { force: true });
267
206
  }
268
207
  function delay(milliseconds) {
269
208
  return new Promise((resolve) => setTimeout(resolve, milliseconds));
270
209
  }
271
- var Service = { discover, ensure, stop, headers };
210
+ var Service = { discover, ensure, stop, headers: headers2 };
272
211
  export {
273
212
  Service,
274
213
  discover,
275
214
  ensure,
276
- headers,
215
+ headers2 as headers,
277
216
  stop
278
217
  };
@@ -0,0 +1,53 @@
1
+ import type { Endpoint, Info } from "./service.js";
2
+ export type LocalService = {
3
+ readonly info: Info;
4
+ readonly endpoint: Endpoint;
5
+ readonly version?: string;
6
+ readonly state: "ready" | "waiting" | "failed";
7
+ readonly compatible: boolean;
8
+ };
9
+ /** The default registration file. */
10
+ export declare function fallback(): string;
11
+ /** Whether two registrations describe the same service instance. */
12
+ export declare function same(left: Info, right: Info): boolean;
13
+ /** Ask a registered owner for its health and classify the answer. */
14
+ export declare function probeResult(info: Info, timeout?: number): Promise<{
15
+ service: undefined;
16
+ timedOut: boolean;
17
+ } | {
18
+ service: {
19
+ info: Info;
20
+ endpoint: {
21
+ url: string;
22
+ auth: {
23
+ type: "basic";
24
+ username: string;
25
+ password: string;
26
+ } | undefined;
27
+ };
28
+ version: string | undefined;
29
+ state: "ready";
30
+ compatible: false;
31
+ };
32
+ timedOut: boolean;
33
+ } | {
34
+ service: {
35
+ info: Info;
36
+ endpoint: {
37
+ url: string;
38
+ auth: {
39
+ type: "basic";
40
+ username: string;
41
+ password: string;
42
+ } | undefined;
43
+ };
44
+ version: string;
45
+ state: "failed" | "ready" | "waiting";
46
+ compatible: true;
47
+ };
48
+ timedOut: boolean;
49
+ }>;
50
+ /** Create HTTP authentication headers for a service endpoint. */
51
+ export declare function headers(endpoint: Endpoint): {
52
+ authorization: string;
53
+ } | undefined;
@@ -0,0 +1,13 @@
1
+ import {
2
+ fallback2,
3
+ same2,
4
+ probeResult2,
5
+ headers2
6
+ } from "./chunks/service-ank0d71z.js";
7
+ import"./chunks/service-xgnj2w4v.js";
8
+ export {
9
+ fallback2 as fallback,
10
+ headers2 as headers,
11
+ probeResult2 as probeResult,
12
+ same2 as same
13
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode/client",
4
- "version": "0.0.0-dev-20558",
4
+ "version": "0.0.0-dev-20560",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -57,8 +57,8 @@
57
57
  "typecheck": "tsgo --noEmit"
58
58
  },
59
59
  "dependencies": {
60
- "@opencode/schema": "0.0.0-dev-20558",
61
- "@opencode/protocol": "0.0.0-dev-20558"
60
+ "@opencode/schema": "0.0.0-dev-20560",
61
+ "@opencode/protocol": "0.0.0-dev-20560"
62
62
  },
63
63
  "peerDependencies": {
64
64
  "effect": "4.0.0-rc.112",
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "devDependencies": {
76
76
  "@effect/platform-node": "4.0.0-rc.112",
77
- "@opencode/httpapi-codegen": "0.0.0-dev-20558",
77
+ "@opencode/httpapi-codegen": "0.0.0-dev-20560",
78
78
  "@tsconfig/bun": "1.0.9",
79
79
  "@types/bun": "1.4.0",
80
80
  "@typescript/native-preview": "7.0.0-dev.20251207.1",