@opencode/server 2.0.8 → 2.0.10

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 (104) hide show
  1. package/dist/api.d.ts +4 -4
  2. package/dist/api.js +10 -9
  3. package/dist/auth.js +12 -17
  4. package/dist/chunks/auth-0ar9gf7q.js +55 -0
  5. package/dist/chunks/auth-0qzy9yxk.js +163 -0
  6. package/dist/chunks/auth-1wj0ectp.js +22 -0
  7. package/dist/chunks/auth-23w8t84j.js +26 -0
  8. package/dist/chunks/auth-34e6wr3b.js +13 -0
  9. package/dist/chunks/auth-3582t7j8.js +139 -0
  10. package/dist/chunks/auth-40g8rvw4.js +59 -0
  11. package/dist/chunks/auth-5p62ed52.js +36 -0
  12. package/dist/chunks/auth-6aya8ky6.js +39 -0
  13. package/dist/chunks/auth-760vtqy2.js +29 -0
  14. package/dist/chunks/auth-7bj707s9.js +99 -0
  15. package/dist/chunks/auth-7qtja2kq.js +15 -0
  16. package/dist/chunks/auth-8e21y1bd.js +20 -0
  17. package/dist/chunks/auth-8nbs1vvt.js +34 -0
  18. package/dist/chunks/auth-8ygjd1s8.js +40 -0
  19. package/dist/chunks/auth-9rqn6x4v.js +16 -0
  20. package/dist/chunks/auth-b4atwt7w.js +37 -0
  21. package/dist/chunks/auth-bgzrbsek.js +13 -0
  22. package/dist/chunks/auth-bkb1xk38.js +42 -0
  23. package/dist/chunks/auth-bkk2wfbz.js +23 -0
  24. package/dist/chunks/auth-byzd2nk7.js +21 -0
  25. package/dist/chunks/auth-cm8xmqjn.js +13 -0
  26. package/dist/chunks/auth-d2g7egyw.js +25 -0
  27. package/dist/chunks/auth-e09mshwt.js +23 -0
  28. package/dist/chunks/auth-erwrh284.js +156 -0
  29. package/dist/chunks/auth-frv6gfy7.js +33 -0
  30. package/dist/chunks/auth-fyjxk8m3.js +22 -0
  31. package/dist/chunks/auth-gsks0nj8.js +149 -0
  32. package/dist/chunks/auth-hezj1jdf.js +7 -0
  33. package/dist/chunks/auth-kv6ycdgm.js +77 -0
  34. package/dist/chunks/auth-m6wvgbfq.js +60 -0
  35. package/dist/chunks/auth-p5hgn93y.js +26 -0
  36. package/dist/chunks/auth-pd1pbg0v.js +17 -0
  37. package/dist/chunks/auth-q4bn6dy8.js +39 -0
  38. package/dist/chunks/auth-r4dhhtgz.js +346 -0
  39. package/dist/chunks/auth-rjpqaa8h.js +83 -0
  40. package/dist/chunks/auth-smaf4a18.js +28 -0
  41. package/dist/chunks/auth-t8yzmczr.js +18 -0
  42. package/dist/chunks/auth-tk6nqkmh.js +22 -0
  43. package/dist/chunks/auth-txhx6f1m.js +13 -0
  44. package/dist/chunks/auth-vgxn80m1.js +61 -0
  45. package/dist/chunks/auth-xpxp1sqb.js +22 -0
  46. package/dist/chunks/auth-xsrtpxyd.js +23 -0
  47. package/dist/chunks/auth-yc7nft6q.js +5 -0
  48. package/dist/chunks/auth-z3nth2hg.js +35 -0
  49. package/dist/chunks/auth-z6kg9h7e.js +19 -0
  50. package/dist/chunks/auth-zw9j8b8g.js +32 -0
  51. package/dist/chunks/auth-zxvt7wd3.js +25 -0
  52. package/dist/chunks/auth-zyv89x5n.js +30 -0
  53. package/dist/cors.js +10 -36
  54. package/dist/event-feed.js +20 -60
  55. package/dist/fetch.js +53 -11
  56. package/dist/handlers/agent.js +11 -17
  57. package/dist/handlers/command.js +11 -5
  58. package/dist/handlers/config.js +11 -16
  59. package/dist/handlers/credential.js +11 -14
  60. package/dist/handlers/debug.js +11 -11
  61. package/dist/handlers/event.js +12 -27
  62. package/dist/handlers/form.js +11 -9
  63. package/dist/handlers/fs.js +11 -22
  64. package/dist/handlers/generate.js +11 -15
  65. package/dist/handlers/integration.js +10 -142
  66. package/dist/handlers/location.js +11 -16
  67. package/dist/handlers/mcp.js +11 -31
  68. package/dist/handlers/message.js +10 -42
  69. package/dist/handlers/migration.js +11 -7
  70. package/dist/handlers/model.js +11 -14
  71. package/dist/handlers/permission.js +11 -57
  72. package/dist/handlers/persistent-pty.js +12 -135
  73. package/dist/handlers/plugin.js +11 -47
  74. package/dist/handlers/project.js +11 -9
  75. package/dist/handlers/provider.js +11 -20
  76. package/dist/handlers/pty-socket.js +6 -4
  77. package/dist/handlers/pty.js +13 -107
  78. package/dist/handlers/reference.js +11 -5
  79. package/dist/handlers/rpc.js +11 -15
  80. package/dist/handlers/server.js +12 -13
  81. package/dist/handlers/session-error.js +12 -26
  82. package/dist/handlers/session.js +10 -311
  83. package/dist/handlers/shell.js +11 -25
  84. package/dist/handlers/skill.js +11 -5
  85. package/dist/handlers/vcs.js +11 -19
  86. package/dist/handlers/websearch.js +11 -31
  87. package/dist/handlers/worktree.js +11 -37
  88. package/dist/handlers.d.ts +1 -1
  89. package/dist/handlers.js +46 -33
  90. package/dist/location.js +15 -49
  91. package/dist/middleware/authorization.js +11 -49
  92. package/dist/middleware/form-location.js +10 -25
  93. package/dist/middleware/schema-error.js +8 -15
  94. package/dist/middleware/session-location.js +10 -19
  95. package/dist/options.js +5 -1
  96. package/dist/process.js +108 -28
  97. package/dist/pty-environment.js +12 -9
  98. package/dist/request-tracing.js +6 -2
  99. package/dist/routes.d.ts +2 -2
  100. package/dist/routes.js +52 -118
  101. package/dist/server-info.js +12 -22
  102. package/dist/service-status.js +8 -11
  103. package/dist/workerd.js +80 -12
  104. package/package.json +8 -8
package/dist/api.d.ts CHANGED
@@ -6654,7 +6654,7 @@ export declare const Api: import("@opencode/protocol/api").Api<LocationMiddlewar
6654
6654
  readonly [x: string]: unknown;
6655
6655
  };
6656
6656
  readonly command: string;
6657
- readonly status: "running" | "exited" | "timeout" | "killed";
6657
+ readonly status: "timeout" | "running" | "exited" | "killed";
6658
6658
  readonly time: {
6659
6659
  readonly started: number;
6660
6660
  readonly completed?: number | undefined;
@@ -6679,7 +6679,7 @@ export declare const Api: import("@opencode/protocol/api").Api<LocationMiddlewar
6679
6679
  readonly id: string & import("effect/Brand").Brand<"Event.ID">;
6680
6680
  readonly data: {
6681
6681
  readonly id: string & import("effect/Brand").Brand<"Shell.ID">;
6682
- readonly status: "running" | "exited" | "timeout" | "killed";
6682
+ readonly status: "timeout" | "running" | "exited" | "killed";
6683
6683
  readonly exit?: number | undefined;
6684
6684
  };
6685
6685
  readonly type: "shell.exited";
@@ -7276,7 +7276,7 @@ export declare const Api: import("@opencode/protocol/api").Api<LocationMiddlewar
7276
7276
  readonly [x: string]: unknown;
7277
7277
  };
7278
7278
  readonly command: string;
7279
- readonly status: "running" | "exited" | "timeout" | "killed";
7279
+ readonly status: "timeout" | "running" | "exited" | "killed";
7280
7280
  readonly time: {
7281
7281
  readonly started: number;
7282
7282
  readonly completed?: number | undefined;
@@ -7312,7 +7312,7 @@ export declare const Api: import("@opencode/protocol/api").Api<LocationMiddlewar
7312
7312
  readonly [x: string]: unknown;
7313
7313
  };
7314
7314
  readonly command: string;
7315
- readonly status: "running" | "exited" | "timeout" | "killed";
7315
+ readonly status: "timeout" | "running" | "exited" | "killed";
7316
7316
  readonly time: {
7317
7317
  readonly started: number;
7318
7318
  readonly completed?: number | undefined;
package/dist/api.js CHANGED
@@ -1,9 +1,10 @@
1
- import { makeDefaultApi } from "@opencode/protocol/api";
2
- import { LocationMiddleware } from "./location";
3
- import { FormLocationMiddleware } from "./middleware/form-location";
4
- import { SessionLocationMiddleware } from "./middleware/session-location";
5
- export const Api = makeDefaultApi({
6
- locationMiddleware: LocationMiddleware,
7
- formLocationMiddleware: FormLocationMiddleware,
8
- sessionLocationMiddleware: SessionLocationMiddleware
9
- });
1
+ import {
2
+ Api2
3
+ } from "./chunks/auth-z6kg9h7e.js";
4
+ import"./chunks/auth-23w8t84j.js";
5
+ import"./chunks/auth-frv6gfy7.js";
6
+ import"./chunks/auth-0ar9gf7q.js";
7
+ import"./chunks/auth-smaf4a18.js";
8
+ export {
9
+ Api2 as Api
10
+ };
package/dist/auth.js CHANGED
@@ -1,17 +1,12 @@
1
- export * as ServerAuth from "./auth";
2
- import { Context, Layer, Option, Redacted } from "effect";
3
-
4
- export class Config extends Context.Service()("@opencode/ServerAuthConfig") {
5
- static configLayer(input) {
6
- return Layer.succeed(this, this.of({ ...input, username: "opencode" }));
7
- }
8
- static get layer() {
9
- return this.configLayer({ password: Option.none() });
10
- }
11
- }
12
- export function required(config) {
13
- return Option.isSome(config.password) && config.password.value !== "";
14
- }
15
- export function authorized(credentials, config) {
16
- return Option.isSome(config.password) && credentials.username === config.username && Redacted.value(credentials.password) === config.password.value;
17
- }
1
+ import {
2
+ Config2,
3
+ required2,
4
+ authorized2,
5
+ exports_auth
6
+ } from "./chunks/auth-zyv89x5n.js";
7
+ export {
8
+ Config2 as Config,
9
+ exports_auth as ServerAuth,
10
+ authorized2 as authorized,
11
+ required2 as required
12
+ };
@@ -0,0 +1,55 @@
1
+ import {
2
+ missingSession2
3
+ } from "./auth-smaf4a18.js";
4
+
5
+ // src/location.ts
6
+ import { Location } from "@opencode/core/location";
7
+ import { LocationServiceMap } from "@opencode/core/location-services";
8
+ import { AbsolutePath } from "@opencode/core/schema";
9
+ import { Session } from "@opencode/core/session";
10
+ import { InvalidRequestError } from "@opencode/protocol/errors";
11
+ import { Effect, Layer, Schema } from "effect";
12
+ import { HttpServerRequest } from "effect/unstable/http";
13
+ import { HttpApiMiddleware } from "effect/unstable/httpapi";
14
+ class LocationMiddleware2 extends HttpApiMiddleware.Service()("@opencode/HttpApiLocation") {
15
+ }
16
+ function response2(data) {
17
+ return Effect.gen(function* () {
18
+ const location = yield* Location.Service;
19
+ return {
20
+ location: new Location.Info({
21
+ directory: location.directory,
22
+ project: location.project
23
+ }),
24
+ data: yield* data
25
+ };
26
+ });
27
+ }
28
+ var decodeSessionID = Schema.decodeUnknownEffect(Session.ID);
29
+ var sessionInfo2 = Effect.fnUntraced(function* (sessions, sessionID) {
30
+ const id = yield* decodeSessionID(sessionID).pipe(Effect.mapError(() => new InvalidRequestError({ message: "Invalid session ID", field: "sessionID" })));
31
+ return yield* sessions.get(id).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
32
+ });
33
+ function requestRef2(request) {
34
+ const query = new URL(request.url, "http://localhost").searchParams;
35
+ const directory = query.get("location[directory]") || (request.headers["x-opencode-directory"] ? decode(request.headers["x-opencode-directory"]) : process.cwd());
36
+ return Location.Ref.make({
37
+ directory: AbsolutePath.make(directory)
38
+ });
39
+ }
40
+ function decode(input) {
41
+ try {
42
+ return decodeURIComponent(input);
43
+ } catch {
44
+ return input;
45
+ }
46
+ }
47
+ var layer5 = Layer.effect(LocationMiddleware2, Effect.gen(function* () {
48
+ const locations = yield* LocationServiceMap.Service;
49
+ return LocationMiddleware2.of((effect) => Effect.gen(function* () {
50
+ const request = yield* HttpServerRequest.HttpServerRequest;
51
+ return yield* effect.pipe(Effect.provide(locations.get(requestRef2(request))));
52
+ }));
53
+ }));
54
+
55
+ export { LocationMiddleware2, response2, sessionInfo2, requestRef2, layer5 };
@@ -0,0 +1,163 @@
1
+ import {
2
+ Api2
3
+ } from "./auth-z6kg9h7e.js";
4
+ import {
5
+ CorsConfig2,
6
+ isAllowedRequestOrigin2
7
+ } from "./auth-q4bn6dy8.js";
8
+ import {
9
+ runPtySocket2
10
+ } from "./auth-hezj1jdf.js";
11
+
12
+ // src/handlers/persistent-pty.ts
13
+ import { PersistentPty } from "@opencode/core/persistent-pty";
14
+ import { PtyTicket } from "@opencode/core/pty/ticket";
15
+ import { ForbiddenError, PtyNotFoundError, ServiceUnavailableError } from "@opencode/protocol/errors";
16
+ import {
17
+ PTY_CONNECT_TICKET_QUERY,
18
+ PTY_CONNECT_TOKEN_HEADER,
19
+ PTY_CONNECT_TOKEN_HEADER_VALUE
20
+ } from "@opencode/protocol/groups/persistent-pty";
21
+ import { Effect, Queue, Semaphore } from "effect";
22
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
23
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
24
+ import { Socket } from "effect/unstable/socket";
25
+ var PersistentPtyHandler2 = HttpApiBuilder.group(Api2, "server.experimental", (handlers) => Effect.gen(function* () {
26
+ const tickets = yield* PtyTicket.Service;
27
+ const cors = yield* CorsConfig2;
28
+ const pty = yield* PersistentPty.Service;
29
+ return handlers.handle("persistentPty.read", Effect.fn(function* (ctx) {
30
+ return { data: yield* pty.read(ctx.params.sessionID, ctx.query.lines).pipe(mapUnavailable) };
31
+ })).handle("persistentPty.list", Effect.fn(function* (ctx) {
32
+ return { data: yield* pty.list(ctx.params.sessionID).pipe(mapUnavailable) };
33
+ })).handle("persistentPty.create", Effect.fn(function* (ctx) {
34
+ return {
35
+ data: yield* pty.create(ctx.params.sessionID, {
36
+ command: ctx.payload.command,
37
+ args: ctx.payload.args,
38
+ cwd: ctx.payload.cwd,
39
+ title: ctx.payload.title,
40
+ env: ctx.payload.env,
41
+ cols: ctx.payload.size?.cols,
42
+ rows: ctx.payload.size?.rows
43
+ }).pipe(Effect.catchTag("PersistentPty.UnavailableError", unavailable))
44
+ };
45
+ })).handle("persistentPty.shutdown", Effect.fn(function* () {
46
+ yield* pty.shutdown().pipe(mapUnavailable);
47
+ return HttpApiSchema.NoContent.make();
48
+ })).handle("persistentPty.handoff", Effect.fn(function* () {
49
+ return { handoff: yield* pty.handoff().pipe(mapUnavailable) };
50
+ })).handle("persistentPty.get", Effect.fn(function* (ctx) {
51
+ return { data: yield* pty.get(ctx.params.ptyID).pipe(mapTerminalError) };
52
+ })).handle("persistentPty.update", Effect.fn(function* (ctx) {
53
+ yield* pty.resize(ctx.params.ptyID, ctx.payload.size.cols, ctx.payload.size.rows, ctx.payload.attachmentID).pipe(mapTerminalError);
54
+ return { data: yield* pty.get(ctx.params.ptyID).pipe(mapTerminalError) };
55
+ })).handle("persistentPty.snapshot", Effect.fn(function* (ctx) {
56
+ return { data: yield* pty.snapshot(ctx.params.ptyID).pipe(mapTerminalError) };
57
+ })).handle("persistentPty.remove", Effect.fn(function* (ctx) {
58
+ yield* pty.remove(ctx.params.ptyID).pipe(mapTerminalError);
59
+ return HttpApiSchema.NoContent.make();
60
+ })).handle("persistentPty.connectToken", Effect.fn(function* (ctx) {
61
+ const request = yield* HttpServerRequest.HttpServerRequest;
62
+ if (request.headers[PTY_CONNECT_TOKEN_HEADER] !== PTY_CONNECT_TOKEN_HEADER_VALUE || !isAllowedRequestOrigin2(request.headers.origin, request.headers.host, cors))
63
+ return yield* new ForbiddenError({ message: "Invalid persistent PTY connect token request" });
64
+ yield* pty.get(ctx.params.ptyID).pipe(mapTerminalError);
65
+ return { data: yield* tickets.issue({ ptyID: ctx.params.ptyID }) };
66
+ })).handleRaw("persistentPty.connect", Effect.fn("PersistentPtyHandler.connect")(function* (ctx) {
67
+ const url = new URL(ctx.request.url, "http://localhost");
68
+ const ticket = url.searchParams.get(PTY_CONNECT_TICKET_QUERY);
69
+ if (ticket) {
70
+ const valid = isAllowedRequestOrigin2(ctx.request.headers.origin, ctx.request.headers.host, cors) ? yield* tickets.consume({ ticket, ptyID: ctx.params.ptyID }) : false;
71
+ if (!valid)
72
+ return HttpServerResponse.empty({ status: 403 });
73
+ }
74
+ const cursor = Number(url.searchParams.get("cursor") ?? "0");
75
+ const role = url.searchParams.get("role") === "observer" ? "observer" : "controller";
76
+ const framedInput = url.searchParams.get("input_protocol") === "1";
77
+ const attachmentID = url.searchParams.get("attachment_id") ?? crypto.randomUUID();
78
+ if (!Number.isSafeInteger(cursor) || cursor < 0)
79
+ return HttpServerResponse.empty({ status: 400 });
80
+ const socket = yield* Effect.orDie(ctx.request.upgrade);
81
+ const write = yield* socket.writer;
82
+ const outbox = yield* Queue.unbounded();
83
+ const input = yield* Semaphore.make(1);
84
+ let attachment;
85
+ const onOpen = Effect.gen(function* () {
86
+ attachment = yield* pty.attach(ctx.params.ptyID, {
87
+ cursor,
88
+ attachmentID,
89
+ role,
90
+ takeover: url.searchParams.get("takeover") === "true",
91
+ onEvent: (event) => {
92
+ if (event.type === "output")
93
+ Queue.offerUnsafe(outbox, event.data);
94
+ if (event.type === "resized")
95
+ Queue.offerUnsafe(outbox, JSON.stringify({ ...event, checkpoint: Buffer.from(event.checkpoint).toString("base64") }));
96
+ if (event.type !== "output" && event.type !== "resized")
97
+ Queue.offerUnsafe(outbox, JSON.stringify(event));
98
+ },
99
+ onEnd: () => Queue.offerUnsafe(outbox, new Socket.CloseEvent(1000))
100
+ }).pipe(Effect.catchTags({
101
+ "PersistentPty.NotFoundError": () => Effect.succeed(undefined),
102
+ "PersistentPty.UnavailableError": () => Effect.succeed(undefined)
103
+ }));
104
+ if (!attachment) {
105
+ Queue.offerUnsafe(outbox, new Socket.CloseEvent(4404, "terminal unavailable"));
106
+ return;
107
+ }
108
+ Queue.offerUnsafe(outbox, JSON.stringify({
109
+ type: "attached",
110
+ attachmentID,
111
+ inputProtocol: framedInput ? 1 : 0,
112
+ info: attachment.info,
113
+ role: attachment.role,
114
+ generation: attachment.generation,
115
+ replay: {
116
+ requestedOffset: attachment.replay.requestedOffset,
117
+ availableOffset: attachment.replay.availableOffset,
118
+ endOffset: attachment.replay.endOffset,
119
+ truncated: attachment.replay.truncated
120
+ }
121
+ }));
122
+ if (attachment.replay.data.length > 0)
123
+ Queue.offerUnsafe(outbox, attachment.replay.data);
124
+ Queue.offerUnsafe(outbox, JSON.stringify({ type: "replay_complete", endOffset: attachment.replay.endOffset }));
125
+ attachment.activate();
126
+ });
127
+ const drain = Effect.gen(function* () {
128
+ while (true) {
129
+ const item = yield* Queue.take(outbox);
130
+ yield* write(item);
131
+ if (item instanceof Socket.CloseEvent)
132
+ return;
133
+ }
134
+ });
135
+ yield* runPtySocket2(drain, socket.runRaw((message) => input.withPermit(Effect.suspend(() => {
136
+ if (!attachment)
137
+ return Effect.void;
138
+ const data = typeof message === "string" ? Buffer.from(message) : message;
139
+ if (!framedInput)
140
+ return pty.input(ctx.params.ptyID, attachmentID, attachment.info.size.cols, attachment.info.size.rows, data).pipe(Effect.ignore);
141
+ if (data.byteLength < 5)
142
+ return Effect.void;
143
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
144
+ const type = data[0];
145
+ const cols = view.getUint16(1);
146
+ const rows = view.getUint16(3);
147
+ if (type !== 0 && type !== 1 || cols === 0 || rows === 0)
148
+ return Effect.void;
149
+ if (type === 0)
150
+ return pty.control(ctx.params.ptyID, attachmentID, cols, rows).pipe(Effect.ignore);
151
+ return pty.input(ctx.params.ptyID, attachmentID, cols, rows, data.subarray(5)).pipe(Effect.ignore);
152
+ })), { onOpen }), () => attachment?.detach()).pipe(Effect.catchReason("SocketError", "SocketCloseError", () => Effect.void), Effect.orDie);
153
+ return HttpServerResponse.empty();
154
+ }));
155
+ }));
156
+ var mapUnavailable = (effect) => effect.pipe(Effect.catchTag("PersistentPty.UnavailableError", unavailable));
157
+ var mapTerminalError = (effect) => effect.pipe(Effect.catchTags({
158
+ "PersistentPty.NotFoundError": (error) => new PtyNotFoundError({ ptyID: error.ptyID, message: `PTY session not found: ${error.ptyID}` }),
159
+ "PersistentPty.UnavailableError": unavailable
160
+ }));
161
+ var unavailable = (error) => new ServiceUnavailableError({ message: error.message, service: "opencode-pty" });
162
+
163
+ export { PersistentPtyHandler2 };
@@ -0,0 +1,22 @@
1
+ import {
2
+ Api2
3
+ } from "./auth-z6kg9h7e.js";
4
+
5
+ // src/handlers/rpc.ts
6
+ import { Rpc } from "@opencode/core/rpc";
7
+ import { Plugin } from "@opencode/core/plugin";
8
+ import { RpcError, RpcInternalError } from "@opencode/protocol/errors";
9
+ import { Effect } from "effect";
10
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
11
+ var RpcHandler2 = HttpApiBuilder.group(Api2, "server.rpc", (handlers) => handlers.handle("rpc.call", ({ params, payload }) => Effect.gen(function* () {
12
+ yield* Plugin.awaitActivation;
13
+ const rpc = yield* Rpc.Service;
14
+ const output = yield* rpc.call(params.rpcID, params.method, payload.input);
15
+ return output === undefined ? {} : { output };
16
+ }).pipe(Effect.mapError((error) => error.type === "rpc.invalid_output" || error.type === "rpc.internal" ? new RpcInternalError({ type: error.type, message: error.message }) : new RpcError({
17
+ type: error.type,
18
+ message: error.message,
19
+ ...error.data === undefined ? {} : { data: error.data }
20
+ })), Effect.catchDefect((defect) => Effect.logError("rpc call failed", { rpc: params.rpcID, method: params.method, defect }).pipe(Effect.andThen(Effect.fail(new RpcInternalError({ type: "rpc.internal", message: "RPC call failed" }))))))));
21
+
22
+ export { RpcHandler2 };
@@ -0,0 +1,26 @@
1
+ import {
2
+ sessionInfo2
3
+ } from "./auth-0ar9gf7q.js";
4
+
5
+ // src/middleware/session-location.ts
6
+ import { Instance } from "@opencode/core/instance/service";
7
+ import { Session } from "@opencode/core/session";
8
+ import { Effect, Layer } from "effect";
9
+ import { HttpRouter } from "effect/unstable/http";
10
+ import { HttpApiMiddleware } from "effect/unstable/httpapi";
11
+ import { InvalidRequestError, SessionNotFoundError } from "@opencode/protocol/errors";
12
+ class SessionLocationMiddleware2 extends HttpApiMiddleware.Service()("@opencode/HttpApiSessionLocation", {
13
+ error: [InvalidRequestError, SessionNotFoundError]
14
+ }) {
15
+ }
16
+ var sessionLocationLayer2 = Layer.effect(SessionLocationMiddleware2, Effect.gen(function* () {
17
+ const sessions = yield* Session.Service;
18
+ const instances = yield* Instance.Service;
19
+ return SessionLocationMiddleware2.of((effect) => Effect.gen(function* () {
20
+ const route = yield* HttpRouter.RouteContext;
21
+ const session = yield* sessionInfo2(sessions, route.params.sessionID);
22
+ return yield* effect.pipe(instances.provide(session));
23
+ }));
24
+ }));
25
+
26
+ export { SessionLocationMiddleware2, sessionLocationLayer2 };
@@ -0,0 +1,13 @@
1
+ import {
2
+ response2
3
+ } from "./auth-0ar9gf7q.js";
4
+ import {
5
+ Api2
6
+ } from "./auth-z6kg9h7e.js";
7
+
8
+ // src/handlers/skill.ts
9
+ import { Skill } from "@opencode/core/skill";
10
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
11
+ var SkillHandler2 = HttpApiBuilder.group(Api2, "server.skill", (handlers) => handlers.handle("skill.list", () => response2(Skill.Service.use((skill) => skill.list()))));
12
+
13
+ export { SkillHandler2 };
@@ -0,0 +1,139 @@
1
+ import {
2
+ response2
3
+ } from "./auth-0ar9gf7q.js";
4
+ import {
5
+ Api2
6
+ } from "./auth-z6kg9h7e.js";
7
+ import {
8
+ CorsConfig2,
9
+ isAllowedRequestOrigin2
10
+ } from "./auth-q4bn6dy8.js";
11
+ import {
12
+ Service2
13
+ } from "./auth-bkk2wfbz.js";
14
+ import {
15
+ runPtySocket2
16
+ } from "./auth-hezj1jdf.js";
17
+
18
+ // src/handlers/pty.ts
19
+ import { Pty } from "@opencode/core/pty";
20
+ import { PtyProtocol } from "@opencode/core/pty/protocol";
21
+ import { PtyTicket } from "@opencode/core/pty/ticket";
22
+ import { Location } from "@opencode/core/location";
23
+ import { Effect, Queue } from "effect";
24
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
25
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
26
+ import { Socket } from "effect/unstable/socket";
27
+ import { ForbiddenError, PtyNotFoundError } from "@opencode/protocol/errors";
28
+ import {
29
+ PTY_CONNECT_TICKET_QUERY,
30
+ PTY_CONNECT_TOKEN_HEADER,
31
+ PTY_CONNECT_TOKEN_HEADER_VALUE
32
+ } from "@opencode/protocol/groups/pty";
33
+ var ticketScope = Effect.gen(function* () {
34
+ const location = yield* Location.Service;
35
+ return { directory: location.directory, workspaceID: location.workspaceID };
36
+ });
37
+ var PtyHandler2 = HttpApiBuilder.group(Api2, "server.pty", (handlers) => Effect.gen(function* () {
38
+ const tickets = yield* PtyTicket.Service;
39
+ const cors = yield* CorsConfig2;
40
+ const environment = yield* Service2;
41
+ return handlers.handle("pty.list", Effect.fn(function* () {
42
+ const pty = yield* Pty.Service;
43
+ return yield* response2(pty.list());
44
+ })).handle("pty.create", Effect.fn(function* (ctx) {
45
+ const pty = yield* Pty.Service;
46
+ const location = yield* Location.Service;
47
+ const cwd = ctx.payload.cwd || location.directory;
48
+ return yield* response2(pty.create({
49
+ ...ctx.payload,
50
+ args: ctx.payload.args ? [...ctx.payload.args] : undefined,
51
+ cwd,
52
+ env: {
53
+ ...ctx.payload.env,
54
+ ...yield* environment.get({ directory: location.directory, cwd })
55
+ }
56
+ }));
57
+ })).handle("pty.get", Effect.fn(function* (ctx) {
58
+ const pty = yield* Pty.Service;
59
+ return yield* response2(pty.get(ctx.params.ptyID).pipe(Effect.catchTag("Pty.NotFoundError", () => new PtyNotFoundError({
60
+ ptyID: ctx.params.ptyID,
61
+ message: `PTY session not found: ${ctx.params.ptyID}`
62
+ }))));
63
+ })).handle("pty.update", Effect.fn(function* (ctx) {
64
+ const pty = yield* Pty.Service;
65
+ return yield* response2(pty.update(ctx.params.ptyID, {
66
+ ...ctx.payload,
67
+ size: ctx.payload.size ? { ...ctx.payload.size } : undefined
68
+ }).pipe(Effect.catchTag("Pty.NotFoundError", () => new PtyNotFoundError({
69
+ ptyID: ctx.params.ptyID,
70
+ message: `PTY session not found: ${ctx.params.ptyID}`
71
+ }))));
72
+ })).handle("pty.remove", Effect.fn(function* (ctx) {
73
+ const pty = yield* Pty.Service;
74
+ yield* pty.remove(ctx.params.ptyID).pipe(Effect.catchTag("Pty.NotFoundError", () => new PtyNotFoundError({
75
+ ptyID: ctx.params.ptyID,
76
+ message: `PTY session not found: ${ctx.params.ptyID}`
77
+ })));
78
+ return HttpApiSchema.NoContent.make();
79
+ })).handle("pty.connectToken", Effect.fn(function* (ctx) {
80
+ const request = yield* HttpServerRequest.HttpServerRequest;
81
+ if (request.headers[PTY_CONNECT_TOKEN_HEADER] !== PTY_CONNECT_TOKEN_HEADER_VALUE || !isAllowedRequestOrigin2(request.headers.origin, request.headers.host, cors))
82
+ return yield* new ForbiddenError({ message: "Invalid PTY connect token request" });
83
+ const pty = yield* Pty.Service;
84
+ yield* pty.get(ctx.params.ptyID).pipe(Effect.catchTag("Pty.NotFoundError", () => new PtyNotFoundError({
85
+ ptyID: ctx.params.ptyID,
86
+ message: `PTY session not found: ${ctx.params.ptyID}`
87
+ })));
88
+ return yield* response2(tickets.issue({ ptyID: ctx.params.ptyID, ...yield* ticketScope }));
89
+ })).handleRaw("pty.connect", Effect.fn("PtyHandler.connect")(function* (ctx) {
90
+ const pty = yield* Pty.Service;
91
+ const exists = yield* pty.get(ctx.params.ptyID).pipe(Effect.as(true), Effect.catchTag("Pty.NotFoundError", () => Effect.succeed(false)));
92
+ if (!exists)
93
+ return HttpServerResponse.empty({ status: 404 });
94
+ const url = new URL(ctx.request.url, "http://localhost");
95
+ const ticket = url.searchParams.get(PTY_CONNECT_TICKET_QUERY);
96
+ if (ticket) {
97
+ const valid = isAllowedRequestOrigin2(ctx.request.headers.origin, ctx.request.headers.host, cors) ? yield* tickets.consume({ ticket, ptyID: ctx.params.ptyID, ...yield* ticketScope }) : false;
98
+ if (!valid)
99
+ return HttpServerResponse.empty({ status: 403 });
100
+ }
101
+ const parsedCursor = url.searchParams.get("cursor");
102
+ const cursorNumber = parsedCursor === null ? undefined : Number(parsedCursor);
103
+ const cursor = cursorNumber !== undefined && Number.isSafeInteger(cursorNumber) && cursorNumber >= -1 ? cursorNumber : undefined;
104
+ const socket = yield* Effect.orDie(ctx.request.upgrade);
105
+ const write = yield* socket.writer;
106
+ const closeAccepted = (event) => socket.runRaw(() => Effect.void, { onOpen: write(event).pipe(Effect.catch(() => Effect.void)) }).pipe(Effect.timeout("1 second"), Effect.catchReason("SocketError", "SocketCloseError", () => Effect.void), Effect.catch(() => Effect.void));
107
+ const outbox = yield* Queue.unbounded();
108
+ const attachment = yield* pty.attach(ctx.params.ptyID, {
109
+ cursor,
110
+ onData: (chunk) => Queue.offerUnsafe(outbox, chunk),
111
+ onEnd: () => Queue.offerUnsafe(outbox, new Socket.CloseEvent(1000))
112
+ }).pipe(Effect.catchTags({
113
+ "Pty.NotFoundError": () => closeAccepted(new Socket.CloseEvent(4404, "session not found")).pipe(Effect.as(undefined)),
114
+ "Pty.ExitedError": () => closeAccepted(new Socket.CloseEvent(4404, "session exited")).pipe(Effect.as(undefined))
115
+ }));
116
+ if (!attachment)
117
+ return HttpServerResponse.empty();
118
+ for (const chunk of PtyProtocol.chunks(attachment.replay))
119
+ Queue.offerUnsafe(outbox, chunk);
120
+ Queue.offerUnsafe(outbox, PtyProtocol.metaFrame(attachment.cursor));
121
+ attachment.activate();
122
+ const drain = Effect.gen(function* () {
123
+ while (true) {
124
+ const item = yield* Queue.take(outbox);
125
+ yield* write(item);
126
+ if (item instanceof Socket.CloseEvent)
127
+ return;
128
+ }
129
+ });
130
+ yield* runPtySocket2(drain, socket.runRaw((message) => {
131
+ const decoded = PtyProtocol.decodeInput(message);
132
+ if (decoded !== undefined)
133
+ attachment.write(decoded);
134
+ }), attachment.detach).pipe(Effect.catchReason("SocketError", "SocketCloseError", () => Effect.void), Effect.orDie);
135
+ return HttpServerResponse.empty();
136
+ }));
137
+ }));
138
+
139
+ export { PtyHandler2 };
@@ -0,0 +1,59 @@
1
+ import {
2
+ missingSession2,
3
+ failedMessageDecode2
4
+ } from "./auth-smaf4a18.js";
5
+ import {
6
+ Api2
7
+ } from "./auth-z6kg9h7e.js";
8
+
9
+ // src/handlers/message.ts
10
+ import { SessionMessage } from "@opencode/core/session/message";
11
+ import { Session } from "@opencode/core/session";
12
+ import { Effect, Schema } from "effect";
13
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
14
+ import { InvalidCursorError } from "@opencode/protocol/errors";
15
+ var DefaultMessagesLimit = 50;
16
+ var Cursor = Schema.Struct({
17
+ id: SessionMessage.ID,
18
+ order: Schema.Union([Schema.Literal("asc"), Schema.Literal("desc")]),
19
+ direction: Schema.Union([Schema.Literal("previous"), Schema.Literal("next")])
20
+ });
21
+ var decodeCursor = Schema.decodeUnknownSync(Cursor);
22
+ var cursor = {
23
+ encode(message, order, direction) {
24
+ return Buffer.from(JSON.stringify({ id: message.id, order, direction })).toString("base64url");
25
+ },
26
+ decode(input) {
27
+ return decodeCursor(JSON.parse(Buffer.from(input, "base64url").toString("utf8")));
28
+ }
29
+ };
30
+ var MessageHandler2 = HttpApiBuilder.group(Api2, "server.message", (handlers) => Effect.gen(function* () {
31
+ const session = yield* Session.Service;
32
+ return handlers.handle("session.messages", Effect.fn(function* (ctx) {
33
+ if (ctx.query.cursor && ctx.query.order !== undefined)
34
+ return yield* new InvalidCursorError({ message: "Cursor cannot be combined with order" });
35
+ const decoded = yield* Effect.try({
36
+ try: () => ctx.query.cursor ? cursor.decode(ctx.query.cursor) : undefined,
37
+ catch: () => new InvalidCursorError({ message: "Invalid cursor" })
38
+ });
39
+ const order = decoded?.order ?? ctx.query.order ?? "desc";
40
+ const messages = yield* session.messages({
41
+ sessionID: ctx.params.sessionID,
42
+ limit: ctx.query.limit ?? DefaultMessagesLimit,
43
+ order,
44
+ type: ctx.query.type,
45
+ cursor: decoded ? { id: decoded.id, direction: decoded.direction } : undefined
46
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode2));
47
+ const first = messages[0];
48
+ const last = messages.at(-1);
49
+ return {
50
+ data: messages,
51
+ cursor: {
52
+ previous: first ? cursor.encode(first, order, "previous") : undefined,
53
+ next: last ? cursor.encode(last, order, "next") : undefined
54
+ }
55
+ };
56
+ }));
57
+ }));
58
+
59
+ export { MessageHandler2 };
@@ -0,0 +1,36 @@
1
+ import {
2
+ __export
3
+ } from "./auth-9rqn6x4v.js";
4
+
5
+ // src/server-info.ts
6
+ var exports_server_info = {};
7
+ __export(exports_server_info, {
8
+ ServerInfo: () => exports_server_info,
9
+ Service: () => Service4,
10
+ connectionURLs: () => connectionURLs2,
11
+ layer: () => layer4
12
+ });
13
+ import { Context, Layer } from "effect";
14
+ import { networkInterfaces } from "node:os";
15
+
16
+ class Service4 extends Context.Service()("@opencode/server/ServerInfo") {
17
+ }
18
+ function layer4(urls, tmp, app = {}) {
19
+ return Layer.succeed(Service4, Service4.of({ urls, app, paths: { tmp } }));
20
+ }
21
+ function connectionURLs2(value, requestedHostname) {
22
+ const url = new URL(value);
23
+ const hostname = requestedHostname ?? url.hostname;
24
+ const family = hostname === "0.0.0.0" ? "IPv4" : hostname === "::" || hostname === "[::]" ? "IPv6" : undefined;
25
+ if (family === undefined)
26
+ return [value];
27
+ return [
28
+ ...new Set(Object.values(networkInterfaces()).flatMap((entries) => entries ?? []).filter((entry) => !entry.internal && entry.family === family).map((entry) => {
29
+ const result = new URL(value);
30
+ result.hostname = family === "IPv6" ? `[${entry.address}]` : entry.address;
31
+ return result.toString().replace(/\/$/, "");
32
+ }))
33
+ ];
34
+ }
35
+
36
+ export { Service4, layer4, connectionURLs2, exports_server_info };
@@ -0,0 +1,39 @@
1
+ import {
2
+ response2
3
+ } from "./auth-0ar9gf7q.js";
4
+ import {
5
+ Api2
6
+ } from "./auth-z6kg9h7e.js";
7
+
8
+ // src/handlers/websearch.ts
9
+ import { WebSearch } from "@opencode/core/websearch";
10
+ import { InvalidRequestError, ServiceUnavailableError } from "@opencode/protocol/errors";
11
+ import { Effect } from "effect";
12
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
13
+ var WebSearchHandler2 = HttpApiBuilder.group(Api2, "server.websearch", (handlers) => Effect.gen(function* () {
14
+ return handlers.handle("websearch.providers", Effect.fn("server.websearch.providers")(function* () {
15
+ const websearch = yield* WebSearch.Service;
16
+ return yield* response2(websearch.providers());
17
+ })).handle("websearch.query", Effect.fn("server.websearch.query")(function* (request) {
18
+ const websearch = yield* WebSearch.Service;
19
+ return yield* response2(websearch.query(request.payload).pipe(Effect.catchTags({
20
+ "WebSearch.ProviderRequired": () => new InvalidRequestError({
21
+ message: "Web search provider is required",
22
+ kind: "websearch_provider_required",
23
+ field: "providerID"
24
+ }),
25
+ "WebSearch.ProviderNotFound": (error) => new InvalidRequestError({
26
+ message: `Web search provider not found: ${error.providerID}`,
27
+ kind: "websearch_provider_not_found",
28
+ field: "providerID"
29
+ }),
30
+ "WebSearch.Disabled": () => new InvalidRequestError({ message: "Web search is disabled", kind: "websearch_disabled" }),
31
+ "WebSearch.Request": (error) => new ServiceUnavailableError({
32
+ message: `Web search request failed: ${error.providerID}`,
33
+ service: error.providerID
34
+ })
35
+ })));
36
+ }));
37
+ }));
38
+
39
+ export { WebSearchHandler2 };