@kilocode/client 0.0.0-stage → 0.0.0-v2-9

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 (94) hide show
  1. package/README.md +26 -2
  2. package/dist/chunks/contract-15p82y03.js +62 -0
  3. package/dist/chunks/contract-1x65x4hv.js +1701 -0
  4. package/dist/chunks/contract-5bf0yjkp.js +111 -0
  5. package/dist/chunks/contract-6mptkzbc.js +0 -0
  6. package/dist/chunks/contract-8bbb028w.js +145 -0
  7. package/dist/chunks/contract-8s2anffy.js +11 -0
  8. package/dist/chunks/contract-8ye1ye6f.js +92 -0
  9. package/dist/chunks/contract-9rqn6x4v.js +16 -0
  10. package/dist/chunks/contract-c46qzyff.js +37 -0
  11. package/dist/chunks/contract-gne7h7mq.js +50 -0
  12. package/dist/chunks/contract-hjez4bfs.js +52 -0
  13. package/dist/chunks/contract-m2eywc8w.js +0 -0
  14. package/dist/chunks/contract-nwcpakzx.js +12 -0
  15. package/dist/chunks/contract-qav6mfdz.js +0 -0
  16. package/dist/chunks/contract-ryy6kscr.js +39 -0
  17. package/dist/chunks/contract-s2hya144.js +1524 -0
  18. package/dist/chunks/contract-skhp7hxc.js +81 -0
  19. package/dist/chunks/contract-sxt88q69.js +12 -0
  20. package/dist/chunks/contract-ve4aws36.js +103 -0
  21. package/dist/chunks/contract-vmytr4q8.js +4 -0
  22. package/dist/chunks/contract-vwtmm97t.js +30 -0
  23. package/dist/chunks/contract-vyas3vgt.js +0 -0
  24. package/dist/chunks/contract-w10x3kfx.js +658 -0
  25. package/dist/chunks/contract-xgnj2w4v.js +20 -0
  26. package/dist/chunks/contract-y2etg5bs.js +9 -0
  27. package/dist/chunks/contract-ydhz1zrh.js +238 -0
  28. package/dist/contract.d.ts +1 -0
  29. package/dist/contract.js +12 -0
  30. package/dist/effect/api/api.d.ts +2927 -0
  31. package/dist/effect/api/api.js +0 -0
  32. package/dist/effect/api.d.ts +4 -0
  33. package/dist/effect/api.js +0 -0
  34. package/dist/effect/client.d.ts +2569 -0
  35. package/dist/effect/client.js +15 -0
  36. package/dist/effect/generated/client-error.d.ts +7 -0
  37. package/dist/effect/generated/client-error.js +6 -0
  38. package/dist/effect/generated/client.d.ts +2568 -0
  39. package/dist/effect/generated/client.js +8 -0
  40. package/dist/effect/generated/index.d.ts +2 -0
  41. package/dist/effect/generated/index.js +12 -0
  42. package/dist/effect/index.d.ts +35 -0
  43. package/dist/effect/index.js +75 -0
  44. package/dist/effect/rpc.d.ts +20 -0
  45. package/dist/effect/rpc.js +11 -0
  46. package/dist/effect/service.d.ts +90 -0
  47. package/dist/effect/service.js +187 -0
  48. package/dist/mcp.d.ts +44 -0
  49. package/dist/mcp.js +110 -0
  50. package/dist/promise/api.d.ts +21 -0
  51. package/dist/promise/api.js +0 -0
  52. package/dist/promise/client.d.ts +270 -0
  53. package/dist/promise/client.js +14 -0
  54. package/dist/promise/generated/client-error.d.ts +8 -0
  55. package/dist/promise/generated/client-error.js +6 -0
  56. package/dist/promise/generated/client.d.ts +277 -0
  57. package/dist/promise/generated/client.js +7 -0
  58. package/dist/promise/generated/index.d.ts +3 -0
  59. package/dist/promise/generated/index.js +81 -0
  60. package/dist/promise/generated/types.d.ts +7877 -0
  61. package/dist/promise/generated/types.js +72 -0
  62. package/dist/promise/index.d.ts +6 -0
  63. package/dist/promise/index.js +85 -0
  64. package/dist/promise/rpc.d.ts +32 -0
  65. package/dist/promise/rpc.js +7 -0
  66. package/dist/promise/service.d.ts +24 -0
  67. package/dist/promise/service.js +187 -0
  68. package/dist/pty-handoff.d.ts +12 -0
  69. package/dist/pty-handoff.js +17 -0
  70. package/dist/rpc-runtime.d.ts +20 -0
  71. package/dist/rpc-runtime.js +14 -0
  72. package/dist/service-contender.d.ts +37 -0
  73. package/dist/service-contender.js +10 -0
  74. package/dist/service-probe.d.ts +70 -0
  75. package/dist/service-probe.js +16 -0
  76. package/dist/service-timing.d.ts +12 -0
  77. package/dist/service-timing.js +10 -0
  78. package/dist/service-version.d.ts +2 -0
  79. package/dist/service-version.js +6 -0
  80. package/dist/service.d.ts +52 -0
  81. package/dist/service.js +1 -0
  82. package/dist/shared-events.d.ts +11 -0
  83. package/dist/shared-events.js +8 -0
  84. package/dist/solid/connection.d.ts +37 -0
  85. package/dist/solid/connection.js +10 -0
  86. package/dist/solid/data.d.ts +211 -0
  87. package/dist/solid/data.js +18 -0
  88. package/dist/solid/index.d.ts +4 -0
  89. package/dist/solid/index.js +36 -0
  90. package/dist/solid/pty.d.ts +22 -0
  91. package/dist/solid/pty.js +8 -0
  92. package/dist/solid/usage.d.ts +9 -0
  93. package/dist/solid/usage.js +6 -0
  94. package/package.json +88 -4
@@ -0,0 +1,8 @@
1
+ import {
2
+ make4
3
+ } from "../../chunks/contract-w10x3kfx.js";
4
+ import"../../chunks/contract-y2etg5bs.js";
5
+ import"../../chunks/contract-vmytr4q8.js";
6
+ export {
7
+ make4 as make
8
+ };
@@ -0,0 +1,2 @@
1
+ export { ClientError } from "./client-error.js";
2
+ export * as OpenCode from "./client.js";
@@ -0,0 +1,12 @@
1
+ import"../../chunks/contract-6mptkzbc.js";
2
+ import {
3
+ exports_client2
4
+ } from "../../chunks/contract-w10x3kfx.js";
5
+ import {
6
+ ClientError2
7
+ } from "../../chunks/contract-y2etg5bs.js";
8
+ import"../../chunks/contract-vmytr4q8.js";
9
+ export {
10
+ ClientError2 as ClientError,
11
+ exports_client2 as OpenCode
12
+ };
@@ -0,0 +1,35 @@
1
+ import type { Effect } from "effect";
2
+ import type { OpenCode } from "./client.js";
3
+ export * from "./generated/index";
4
+ export { OpenCode } from "./client.js";
5
+ export type { AgentApi, AppApi, CommandApi, ConfigApi, EventApi, IntegrationApi, ModelApi, PluginApi, ProviderApi, ReferenceApi, RpcApi, RpcClient, WebSearchApi, SessionApi, SkillApi, } from "./api.js";
6
+ export { Agent } from "@opencode/schema/agent";
7
+ export { Command } from "@opencode/schema/command";
8
+ export { Config } from "@opencode/schema/config";
9
+ export { Credential } from "@opencode/schema/credential";
10
+ export { Event } from "@opencode/schema/event";
11
+ export { EventLog } from "@opencode/schema/event-log";
12
+ export { FileSystem } from "@opencode/schema/filesystem";
13
+ export { Form } from "@opencode/schema/form";
14
+ export { Integration } from "@opencode/schema/integration";
15
+ export { Location } from "@opencode/schema/location";
16
+ export { Model } from "@opencode/schema/model";
17
+ export { Permission } from "@opencode/schema/permission";
18
+ export { PermissionSaved } from "@opencode/schema/permission-saved";
19
+ export { Project } from "@opencode/schema/project";
20
+ export { Worktree } from "@opencode/schema/worktree";
21
+ export { Vcs } from "@opencode/schema/vcs";
22
+ export { Provider } from "@opencode/schema/provider";
23
+ export { Pty } from "@opencode/schema/pty";
24
+ export { Question } from "@opencode/schema/question";
25
+ export { Reference } from "@opencode/schema/reference";
26
+ export { WebSearch } from "@opencode/schema/websearch";
27
+ export { AbsolutePath, RelativePath } from "@opencode/schema/schema";
28
+ export { Session } from "@opencode/schema/session";
29
+ export { SessionInbox } from "@opencode/schema/session-inbox";
30
+ export { SessionMessage } from "@opencode/schema/session-message";
31
+ export { Skill } from "@opencode/schema/skill";
32
+ export { Prompt } from "@opencode/schema/prompt";
33
+ export { PromptInput } from "@opencode/schema/prompt-input";
34
+ export type { OpenCodeEvent } from "@opencode/protocol/groups/event";
35
+ export type OpenCodeClient = Effect.Success<ReturnType<typeof OpenCode.make>>;
@@ -0,0 +1,75 @@
1
+ import {
2
+ ClientError2
3
+ } from "../chunks/contract-y2etg5bs.js";
4
+ import"../chunks/contract-vmytr4q8.js";
5
+ import"../chunks/contract-w10x3kfx.js";
6
+ import"../chunks/contract-6mptkzbc.js";
7
+ import"../chunks/contract-8bbb028w.js";
8
+ import"../chunks/contract-ryy6kscr.js";
9
+ import"../chunks/contract-15p82y03.js";
10
+ import {
11
+ exports_client
12
+ } from "../chunks/contract-hjez4bfs.js";
13
+
14
+ // src/effect/index.ts
15
+ import { Agent } from "@opencode/schema/agent";
16
+ import { Command } from "@opencode/schema/command";
17
+ import { Config } from "@opencode/schema/config";
18
+ import { Credential } from "@opencode/schema/credential";
19
+ import { Event } from "@opencode/schema/event";
20
+ import { EventLog } from "@opencode/schema/event-log";
21
+ import { FileSystem } from "@opencode/schema/filesystem";
22
+ import { Form } from "@opencode/schema/form";
23
+ import { Integration } from "@opencode/schema/integration";
24
+ import { Location } from "@opencode/schema/location";
25
+ import { Model } from "@opencode/schema/model";
26
+ import { Permission } from "@opencode/schema/permission";
27
+ import { PermissionSaved } from "@opencode/schema/permission-saved";
28
+ import { Project } from "@opencode/schema/project";
29
+ import { Worktree } from "@opencode/schema/worktree";
30
+ import { Vcs } from "@opencode/schema/vcs";
31
+ import { Provider } from "@opencode/schema/provider";
32
+ import { Pty } from "@opencode/schema/pty";
33
+ import { Question } from "@opencode/schema/question";
34
+ import { Reference } from "@opencode/schema/reference";
35
+ import { WebSearch } from "@opencode/schema/websearch";
36
+ import { AbsolutePath, RelativePath } from "@opencode/schema/schema";
37
+ import { Session } from "@opencode/schema/session";
38
+ import { SessionInbox } from "@opencode/schema/session-inbox";
39
+ import { SessionMessage } from "@opencode/schema/session-message";
40
+ import { Skill } from "@opencode/schema/skill";
41
+ import { Prompt } from "@opencode/schema/prompt";
42
+ import { PromptInput } from "@opencode/schema/prompt-input";
43
+ export {
44
+ AbsolutePath,
45
+ Agent,
46
+ ClientError2 as ClientError,
47
+ Command,
48
+ Config,
49
+ Credential,
50
+ Event,
51
+ EventLog,
52
+ FileSystem,
53
+ Form,
54
+ Integration,
55
+ Location,
56
+ Model,
57
+ exports_client as OpenCode,
58
+ Permission,
59
+ PermissionSaved,
60
+ Project,
61
+ Prompt,
62
+ PromptInput,
63
+ Provider,
64
+ Pty,
65
+ Question,
66
+ Reference,
67
+ RelativePath,
68
+ Session,
69
+ SessionInbox,
70
+ SessionMessage,
71
+ Skill,
72
+ Vcs,
73
+ WebSearch,
74
+ Worktree
75
+ };
@@ -0,0 +1,20 @@
1
+ export * as RpcClientRuntime from "./rpc.js";
2
+ import type { Rpc } from "@opencode/schema/rpc";
3
+ import type { RpcError, RpcInternalError } from "@opencode/protocol/errors";
4
+ import type { OpenCodeEvent } from "@opencode/protocol/groups/event";
5
+ import { Effect, Schema, Stream } from "effect";
6
+ import type { RpcArguments, RpcCallOptions } from "../promise/rpc.js";
7
+ import type { RpcCallInput, RpcCallOutput } from "./api/api.js";
8
+ type DecodeError<S> = S extends Schema.Top ? Schema.SchemaError : never;
9
+ export type RpcClient<D extends Rpc.Definition, E = never, Options = RpcCallOptions, EventError = E> = {
10
+ readonly [Name in keyof D["methods"]]: (...args: RpcArguments<Rpc.Input<D["methods"][Name]["input"]>, Options>) => Effect.Effect<Rpc.Output<D["methods"][Name]["output"]>, Rpc.MethodError<D["methods"][Name]> | DecodeError<D["methods"][Name]["output"]> | E>;
11
+ } & {
12
+ readonly events: {
13
+ readonly subscribe: <Name extends keyof D["events"] & string>(name: Name) => Stream.Stream<Rpc.EventPayload<D, Name>, DecodeError<D["events"][Name]["schema"]> | EventError>;
14
+ };
15
+ };
16
+ export interface RpcApi<E = never, Options = RpcCallOptions, EventError = E> {
17
+ <D extends Rpc.Definition>(definition: D): RpcClient<D, E, Options, EventError>;
18
+ }
19
+ export declare function make<CallError, EventError>(call: (input: RpcCallInput, options?: RpcCallOptions) => Effect.Effect<RpcCallOutput, CallError>, subscribe: () => Stream.Stream<OpenCodeEvent, EventError>): RpcApi<Exclude<CallError, RpcError | RpcInternalError> | Rpc.SystemError, RpcCallOptions, EventError>;
20
+ export declare function aborted(signal: AbortSignal): Effect.Effect<void, never, never>;
@@ -0,0 +1,11 @@
1
+ import {
2
+ make3,
3
+ aborted2,
4
+ exports_rpc
5
+ } from "../chunks/contract-15p82y03.js";
6
+ import"../chunks/contract-ryy6kscr.js";
7
+ export {
8
+ exports_rpc as RpcClientRuntime,
9
+ aborted2 as aborted,
10
+ make3 as make
11
+ };
@@ -0,0 +1,90 @@
1
+ import { Effect, FileSystem, Schema } from "effect";
2
+ import type { DiscoverOptions, EnsureOptions, StopOptions } from "../service.js";
3
+ import { headers } from "../service-probe.js";
4
+ export * from "../service.js";
5
+ export { headers };
6
+ /** Contents of the local service registration file. */
7
+ export type Info = typeof Info.Type;
8
+ /** Discover a healthy, compatible local service without starting one. */
9
+ export declare const discover: (options?: DiscoverOptions | undefined) => Effect.Effect<{
10
+ url: string;
11
+ auth: {
12
+ type: "basic";
13
+ username: string;
14
+ password: string;
15
+ } | undefined;
16
+ } | undefined, never, FileSystem.FileSystem>;
17
+ /** Recognize an authenticated compatible service bound to an expected URL, including while it starts or fails. */
18
+ export declare const incumbent: (options: DiscoverOptions & {
19
+ readonly url: string;
20
+ }) => Effect.Effect<{
21
+ endpoint: {
22
+ url: string;
23
+ auth: {
24
+ type: "basic";
25
+ username: string;
26
+ password: string;
27
+ } | undefined;
28
+ };
29
+ state: "failed" | "ready" | "waiting";
30
+ } | undefined, never, FileSystem.FileSystem>;
31
+ /** Ensure a healthy, compatible local service is running. */
32
+ export declare const ensure: (options?: EnsureOptions | undefined) => Effect.Effect<{
33
+ url: string;
34
+ auth: {
35
+ type: "basic";
36
+ username: string;
37
+ password: string;
38
+ } | undefined;
39
+ }, Error, FileSystem.FileSystem>;
40
+ /** Stop the registered local service. */
41
+ export declare const stop: (options?: StopOptions | undefined) => Effect.Effect<void, Error, FileSystem.FileSystem>;
42
+ /** Schema for the local service registration file. */
43
+ export declare const Info: Schema.Struct<{
44
+ readonly id: Schema.optional<Schema.String>;
45
+ readonly version: Schema.optional<Schema.String>;
46
+ readonly url: Schema.String;
47
+ readonly pid: Schema.Int;
48
+ readonly password: Schema.optional<Schema.String>;
49
+ }>;
50
+ /** Effect-based local service lifecycle operations. */
51
+ export declare const Service: {
52
+ discover: (options?: DiscoverOptions | undefined) => Effect.Effect<{
53
+ url: string;
54
+ auth: {
55
+ type: "basic";
56
+ username: string;
57
+ password: string;
58
+ } | undefined;
59
+ } | undefined, never, FileSystem.FileSystem>;
60
+ incumbent: (options: DiscoverOptions & {
61
+ readonly url: string;
62
+ }) => Effect.Effect<{
63
+ endpoint: {
64
+ url: string;
65
+ auth: {
66
+ type: "basic";
67
+ username: string;
68
+ password: string;
69
+ } | undefined;
70
+ };
71
+ state: "failed" | "ready" | "waiting";
72
+ } | undefined, never, FileSystem.FileSystem>;
73
+ ensure: (options?: EnsureOptions | undefined) => Effect.Effect<{
74
+ url: string;
75
+ auth: {
76
+ type: "basic";
77
+ username: string;
78
+ password: string;
79
+ } | undefined;
80
+ }, Error, FileSystem.FileSystem>;
81
+ stop: (options?: StopOptions | undefined) => Effect.Effect<void, Error, FileSystem.FileSystem>;
82
+ headers: typeof headers;
83
+ Info: Schema.Struct<{
84
+ readonly id: Schema.optional<Schema.String>;
85
+ readonly version: Schema.optional<Schema.String>;
86
+ readonly url: Schema.String;
87
+ readonly pid: Schema.Int;
88
+ readonly password: Schema.optional<Schema.String>;
89
+ }>;
90
+ };
@@ -0,0 +1,187 @@
1
+ import {
2
+ spawnServiceContender2,
3
+ contenderPool2
4
+ } from "../chunks/contract-ve4aws36.js";
5
+ import {
6
+ defaultEnsureTiming2,
7
+ ensureTiming2
8
+ } from "../chunks/contract-xgnj2w4v.js";
9
+ import {
10
+ matchesVersion2
11
+ } from "../chunks/contract-sxt88q69.js";
12
+ import {
13
+ decide2,
14
+ fallback2,
15
+ same2,
16
+ probeResult2,
17
+ headers2
18
+ } from "../chunks/contract-8ye1ye6f.js";
19
+ import {
20
+ prepare2,
21
+ environment2,
22
+ complete2,
23
+ clear2
24
+ } from "../chunks/contract-5bf0yjkp.js";
25
+ import"../chunks/contract-qav6mfdz.js";
26
+
27
+ // src/effect/service.ts
28
+ import { Effect, FileSystem, Option, Schedule, Schema } from "effect";
29
+ var discover = Effect.fn("service.discover")(function* (options = {}) {
30
+ const found = (yield* registered(options.file)).service;
31
+ if (found?.state !== "ready")
32
+ return;
33
+ if (!found.compatible)
34
+ return;
35
+ if (!matchesVersion2(found.version, options))
36
+ return;
37
+ return found.endpoint;
38
+ });
39
+ var incumbent = Effect.fn("service.incumbent")(function* (options) {
40
+ const info = yield* read(options.file);
41
+ if (info === undefined)
42
+ return;
43
+ const found = (yield* Effect.promise(() => probeResult2({ ...info, url: options.url }))).service;
44
+ if (!found?.compatible)
45
+ return;
46
+ if (!matchesVersion2(found.version, options))
47
+ return;
48
+ return { endpoint: found.endpoint, state: found.state };
49
+ });
50
+ var ensure = Effect.fn("service.ensure")(function* (options = {}) {
51
+ const timing = ensureTiming2(options);
52
+ const pool = contenderPool2(timing);
53
+ let timeouts;
54
+ let announced = false;
55
+ const announce = (reason, previousVersion) => Effect.sync(() => {
56
+ if (announced)
57
+ return;
58
+ announced = true;
59
+ options.onStart?.(reason, previousVersion);
60
+ });
61
+ const spawnContender = Effect.gen(function* () {
62
+ const [command, ...args] = options.command ?? ["opencode", "serve", "--service"];
63
+ if (command === undefined)
64
+ return yield* Effect.fail(new Error("Missing service command"));
65
+ const env = yield* Effect.tryPromise(() => environment2(options.file ?? fallback2(), options.env));
66
+ return yield* Effect.try({
67
+ try: () => {
68
+ return spawnServiceContender2(command, args, env);
69
+ },
70
+ catch: (cause) => new Error("Failed to start server", { cause })
71
+ });
72
+ });
73
+ const found = yield* Effect.gen(function* () {
74
+ const registration = yield* registered(options.file, timing.requestTimeout);
75
+ const info = registration.info;
76
+ const service2 = registration.service;
77
+ if (registration.timedOut && info !== undefined) {
78
+ timeouts = {
79
+ info,
80
+ count: timeouts !== undefined && same2(timeouts.info, info) ? timeouts.count + 1 : 1
81
+ };
82
+ if (timeouts.count >= 3) {
83
+ yield* announce("missing");
84
+ yield* Effect.logWarning("Background service is unresponsive; recovery cannot preserve persistent terminals");
85
+ yield* Effect.tryPromise(() => clear2(options.file ?? fallback2()));
86
+ yield* terminate(info, options, timing);
87
+ pool.evict(info.pid);
88
+ pool.recruitNow();
89
+ timeouts = undefined;
90
+ }
91
+ } else
92
+ timeouts = undefined;
93
+ if (service2 !== undefined) {
94
+ pool.serviceAnswered();
95
+ const decision = decide2(service2, options);
96
+ if (decision._tag === "fail")
97
+ return yield* Effect.fail(decision.error);
98
+ if (decision._tag === "reuse") {
99
+ yield* Effect.tryPromise(() => complete2(options.file ?? fallback2(), service2.info));
100
+ return Option.some(service2);
101
+ }
102
+ if (decision._tag === "replace") {
103
+ yield* announce("version-mismatch", service2.version);
104
+ if (service2.state !== "ready")
105
+ yield* Effect.logWarning("Background service is not ready; replacement cannot preserve persistent terminals");
106
+ yield* stop({ file: options.file, pty: decision.pty }).pipe(Effect.ignore);
107
+ pool.evict(service2.info.pid);
108
+ }
109
+ return Option.none();
110
+ }
111
+ const failed = pool.reap();
112
+ if (failed !== undefined)
113
+ return yield* Effect.fail(failed);
114
+ if (pool.shouldRecruit(info !== undefined)) {
115
+ yield* announce("missing");
116
+ pool.add(yield* spawnContender);
117
+ }
118
+ return Option.none();
119
+ }).pipe(Effect.repeat({
120
+ until: Option.isSome,
121
+ schedule: Schedule.spaced(timing.pollInterval).pipe(Schedule.upTo({ duration: timing.promiseTimeout }))
122
+ }), Effect.ensuring(Effect.sync(() => pool.releaseAll())));
123
+ if (Option.isNone(found))
124
+ return yield* Effect.fail(pool.failure() ?? new Error("Timed out waiting for the background service to start"));
125
+ return found.value.endpoint;
126
+ });
127
+ var stop = Effect.fn("service.stop")(function* (options = {}) {
128
+ const info = yield* read(options.file);
129
+ 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)));
130
+ if (info !== undefined)
131
+ yield* terminate(info, options, ensureTiming2(options));
132
+ });
133
+ var Info = Schema.Struct({
134
+ id: Schema.optional(Schema.String),
135
+ version: Schema.optional(Schema.String),
136
+ url: Schema.String,
137
+ pid: Schema.Int.check(Schema.isGreaterThan(0)),
138
+ password: Schema.optional(Schema.String)
139
+ });
140
+ var decode = Schema.decodeUnknownEffect(Schema.fromJsonString(Info));
141
+ var read = Effect.fnUntraced(function* (file) {
142
+ const fs = yield* FileSystem.FileSystem;
143
+ const text = yield* fs.readFileString(file ?? fallback2()).pipe(Effect.option);
144
+ if (Option.isNone(text))
145
+ return;
146
+ return yield* decode(text.value).pipe(Effect.option, Effect.map(Option.getOrUndefined));
147
+ });
148
+ var registered = Effect.fnUntraced(function* (file, timeout) {
149
+ const info = yield* read(file);
150
+ if (info === undefined)
151
+ return { info: undefined, service: undefined, timedOut: false };
152
+ return { info, ...yield* Effect.promise(() => probeResult2(info, timeout)) };
153
+ });
154
+ var poll = (timing) => Schedule.max([Schedule.spaced(timing.stopPollInterval), Schedule.recurs(timing.stopPollAttempts)]);
155
+ var signal = (pid, name) => Effect.try({ try: () => process.kill(pid, name), catch: (cause) => cause }).pipe(Effect.ignore);
156
+ var stopped = Effect.fnUntraced(function* (pid) {
157
+ const running = yield* Effect.try({ try: () => process.kill(pid, 0), catch: () => false }).pipe(Effect.orElseSucceed(() => false));
158
+ if (!running)
159
+ return true;
160
+ return yield* Effect.fail(new Error(`Server process ${pid} is still running`));
161
+ });
162
+ var terminate = Effect.fnUntraced(function* (info, options, timing) {
163
+ const current = yield* read(options.file);
164
+ if (current === undefined || !same2(current, info))
165
+ return;
166
+ yield* signal(info.pid, "SIGTERM");
167
+ const done = yield* stopped(info.pid).pipe(Effect.retry(poll(timing)), Effect.option);
168
+ if (Option.isNone(done)) {
169
+ yield* signal(info.pid, "SIGKILL");
170
+ yield* stopped(info.pid).pipe(Effect.retry(poll(timing)));
171
+ }
172
+ const latest = yield* read(options.file);
173
+ if (latest === undefined || !same2(latest, info))
174
+ return;
175
+ const fs = yield* FileSystem.FileSystem;
176
+ yield* fs.remove(options.file ?? fallback2()).pipe(Effect.ignore);
177
+ });
178
+ var Service = { discover, incumbent, ensure, stop, headers: headers2, Info };
179
+ export {
180
+ Info,
181
+ Service,
182
+ discover,
183
+ ensure,
184
+ headers2 as headers,
185
+ incumbent,
186
+ stop
187
+ };
package/dist/mcp.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ export type ClientMcpOptions = {
2
+ /** Base URL of the opencode server, for example `http://127.0.0.1:4096`. Its query params are kept. */
3
+ readonly url: string;
4
+ /** Client name, `[a-zA-Z0-9_-]+`. Tools are exposed to the model as `<name>_<tool>`. */
5
+ readonly name: string;
6
+ /** Sent as `location[directory]`. */
7
+ readonly directory?: string;
8
+ /** Handshake headers, such as `authorization`. Browsers cannot send them; use a ticket in `url` instead. */
9
+ readonly headers?: Record<string, string>;
10
+ readonly tools: ReadonlyArray<ClientMcpTool>;
11
+ readonly WebSocket?: typeof WebSocket;
12
+ };
13
+ export type ClientMcpTool = {
14
+ /** `[a-zA-Z0-9_-]+` */
15
+ readonly name: string;
16
+ readonly description?: string;
17
+ /** JSON Schema for the arguments; `type: "object"` is always set. Arguments are not validated here. */
18
+ readonly inputSchema?: Record<string, unknown>;
19
+ readonly handler: (args: Record<string, unknown>, ctx: ClientMcpToolContext) => ClientMcpToolResult | Promise<ClientMcpToolResult>;
20
+ };
21
+ export type ClientMcpToolContext = {
22
+ /** The opencode session that issued the call. */
23
+ readonly sessionID?: string;
24
+ };
25
+ export type ClientMcpToolResult = {
26
+ readonly content: ReadonlyArray<{
27
+ readonly type: "text";
28
+ readonly text: string;
29
+ } | {
30
+ readonly type: "image";
31
+ readonly data: string;
32
+ readonly mimeType: string;
33
+ }>;
34
+ readonly isError?: boolean;
35
+ };
36
+ export type ClientMcpConnection = {
37
+ close(): Promise<void>;
38
+ };
39
+ /**
40
+ * Exposes tools to the opencode server: opens a WebSocket to `/api/experimental/client-mcp` and answers MCP
41
+ * JSON-RPC over it (one message per text frame), so the opencode server is the MCP client. Reconnects with
42
+ * backoff until `close()`.
43
+ */
44
+ export declare function connectClientMcp(options: ClientMcpOptions): ClientMcpConnection;
package/dist/mcp.js ADDED
@@ -0,0 +1,110 @@
1
+ // src/mcp.ts
2
+ var SESSION_ID_META_KEY = "ai.opencode/sessionID";
3
+ var PROTOCOL_VERSION = "2025-11-25";
4
+ var RECONNECT_MIN = 500;
5
+ var RECONNECT_MAX = 1e4;
6
+ function connectClientMcp(options) {
7
+ const state = {
8
+ disposed: false,
9
+ delay: RECONNECT_MIN
10
+ };
11
+ const connect = () => {
12
+ if (state.disposed)
13
+ return;
14
+ const Socket = options.WebSocket ?? WebSocket;
15
+ const socket = options.headers ? new Socket(endpoint(options), { headers: options.headers }) : new Socket(endpoint(options));
16
+ state.socket = socket;
17
+ socket.addEventListener("message", (event) => {
18
+ if (state.disposed || typeof event.data !== "string")
19
+ return;
20
+ const message = parse(event.data);
21
+ if (!message)
22
+ return;
23
+ if (message.method === "notifications/initialized")
24
+ state.delay = RECONNECT_MIN;
25
+ if (typeof message.id !== "string" && typeof message.id !== "number" || typeof message.method !== "string")
26
+ return;
27
+ respond(options, message.method, message.params).then((reply) => send(socket, { jsonrpc: "2.0", id: message.id, ...reply }));
28
+ });
29
+ socket.addEventListener("close", () => {
30
+ if (state.socket === socket)
31
+ state.socket = undefined;
32
+ if (state.disposed)
33
+ return;
34
+ state.timer = setTimeout(connect, state.delay);
35
+ state.delay = Math.min(state.delay * 2, RECONNECT_MAX);
36
+ });
37
+ };
38
+ connect();
39
+ return {
40
+ async close() {
41
+ state.disposed = true;
42
+ clearTimeout(state.timer);
43
+ state.socket?.close(1000);
44
+ }
45
+ };
46
+ }
47
+ async function respond(options, method, params) {
48
+ switch (method) {
49
+ case "initialize":
50
+ return {
51
+ result: {
52
+ protocolVersion: typeof params?.protocolVersion === "string" ? params.protocolVersion : PROTOCOL_VERSION,
53
+ capabilities: { tools: {} },
54
+ serverInfo: { name: options.name, version: "1.0.0" }
55
+ }
56
+ };
57
+ case "ping":
58
+ return { result: {} };
59
+ case "tools/list":
60
+ return {
61
+ result: {
62
+ tools: options.tools.map((tool) => ({
63
+ name: tool.name,
64
+ description: tool.description,
65
+ inputSchema: { type: "object", ...tool.inputSchema }
66
+ }))
67
+ }
68
+ };
69
+ case "tools/call": {
70
+ const tool = options.tools.find((tool) => tool.name === params?.name);
71
+ if (!tool)
72
+ return { error: { code: -32602, message: `Unknown tool: ${String(params?.name)}` } };
73
+ const meta = params?._meta?.[SESSION_ID_META_KEY];
74
+ return Promise.resolve().then(() => tool.handler(params?.arguments ?? {}, { sessionID: typeof meta === "string" ? meta : undefined })).then((result) => ({ result }), (error) => ({
75
+ result: {
76
+ content: [{ type: "text", text: error instanceof Error ? error.message : String(error) }],
77
+ isError: true
78
+ }
79
+ }));
80
+ }
81
+ }
82
+ return { error: { code: -32601, message: `Method not found: ${method}` } };
83
+ }
84
+ function send(socket, message) {
85
+ if (socket.readyState === socket.OPEN)
86
+ socket.send(JSON.stringify(message));
87
+ }
88
+ function parse(text) {
89
+ try {
90
+ const value = JSON.parse(text);
91
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
92
+ } catch {
93
+ return;
94
+ }
95
+ }
96
+ function endpoint(options) {
97
+ const base = new URL(options.url);
98
+ if (!base.pathname.endsWith("/"))
99
+ base.pathname += "/";
100
+ const url = new URL("api/experimental/client-mcp", base);
101
+ url.search = base.search;
102
+ if (options.directory)
103
+ url.searchParams.set("location[directory]", options.directory);
104
+ url.searchParams.set("name", options.name);
105
+ url.protocol = url.protocol === "https:" || url.protocol === "wss:" ? "wss:" : "ws:";
106
+ return url;
107
+ }
108
+ export {
109
+ connectClientMcp
110
+ };
@@ -0,0 +1,21 @@
1
+ import type { OpenCode } from "./client.js";
2
+ type Client = ReturnType<typeof OpenCode.make>;
3
+ export type { RpcApi, RpcCallOptions, RpcClient, RpcEventPayload } from "./rpc.js";
4
+ export type AgentApi = Client["agent"];
5
+ export type CommandApi = Client["command"];
6
+ export type ConfigApi = Client["config"];
7
+ export type CredentialApi = Client["credential"];
8
+ export type EventApi = Client["event"];
9
+ export type GenerateApi = Client["generate"];
10
+ export type IntegrationApi = Client["integration"];
11
+ export type McpApi = Client["mcp"];
12
+ export type ModelApi = Client["model"];
13
+ export type PluginApi = Client["plugin"];
14
+ export type PermissionApi = Client["permission"];
15
+ export type ProviderApi = Client["provider"];
16
+ export type ReferenceApi = Client["reference"];
17
+ export type WebSearchApi = Client["websearch"];
18
+ export type SessionApi = Client["session"];
19
+ export type SkillApi = Client["skill"];
20
+ export type VcsApi = Client["vcs"];
21
+ export type WorktreeApi = Client["worktree"];
File without changes