@opencode-ai/server 0.0.0-dev-18303 → 0.0.0-dev-18308

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.
@@ -48,8 +48,6 @@ export const PersistentPtyHandler = HttpApiBuilder.group(Api, "server.experiment
48
48
  yield* pty.get(ctx.params.ptyID).pipe(mapTerminalError);
49
49
  return { data: yield* tickets.issue({ ptyID: ctx.params.ptyID }) };
50
50
  })).handleRaw("persistentPty.connect", Effect.fn("PersistentPtyHandler.connect")(function* (ctx) {
51
- if (!(yield* pty.get(ctx.params.ptyID).pipe(Effect.as(!0), Effect.catchTag("PersistentPty.NotFoundError", () => Effect.succeed(!1)), Effect.catchTag("PersistentPty.UnavailableError", () => Effect.succeed(!1)))))
52
- return HttpServerResponse.empty({ status: 404 });
53
51
  const url = new URL(ctx.request.url, "http://localhost"), ticket = url.searchParams.get(PTY_CONNECT_TICKET_QUERY);
54
52
  if (ticket) {
55
53
  if (!(isAllowedRequestOrigin(ctx.request.headers.origin, ctx.request.headers.host, cors) ? yield* tickets.consume({ ticket, ptyID: ctx.params.ptyID }) : !1))
@@ -58,45 +56,50 @@ export const PersistentPtyHandler = HttpApiBuilder.group(Api, "server.experiment
58
56
  const cursor = Number(url.searchParams.get("cursor") ?? "0"), role = url.searchParams.get("role") === "observer" ? "observer" : "controller", framedInput = url.searchParams.get("input_protocol") === "1", attachmentID = url.searchParams.get("attachment_id") ?? crypto.randomUUID();
59
57
  if (!Number.isSafeInteger(cursor) || cursor < 0)
60
58
  return HttpServerResponse.empty({ status: 400 });
61
- const socket = yield* Effect.orDie(ctx.request.upgrade), write = yield* socket.writer, outbox = yield* Queue.unbounded(), input = yield* Semaphore.make(1), attachment = yield* pty.attach(ctx.params.ptyID, {
62
- cursor,
63
- attachmentID,
64
- role,
65
- takeover: url.searchParams.get("takeover") === "true",
66
- onEvent: (event) => {
67
- if (event.type === "output")
68
- Queue.offerUnsafe(outbox, event.data);
69
- if (event.type === "resized")
70
- Queue.offerUnsafe(outbox, JSON.stringify({ ...event, checkpoint: Buffer.from(event.checkpoint).toString("base64") }));
71
- if (event.type !== "output" && event.type !== "resized")
72
- Queue.offerUnsafe(outbox, JSON.stringify(event));
73
- },
74
- onEnd: () => Queue.offerUnsafe(outbox, new Socket.CloseEvent(1000))
75
- }).pipe(Effect.catchTags({
76
- "PersistentPty.NotFoundError": () => Effect.succeed(void 0),
77
- "PersistentPty.UnavailableError": () => Effect.succeed(void 0)
78
- }));
79
- if (!attachment)
80
- return HttpServerResponse.empty({ status: 404 });
81
- Queue.offerUnsafe(outbox, JSON.stringify({
82
- type: "attached",
83
- attachmentID,
84
- inputProtocol: framedInput ? 1 : 0,
85
- info: attachment.info,
86
- role: attachment.role,
87
- generation: attachment.generation,
88
- replay: {
89
- requestedOffset: attachment.replay.requestedOffset,
90
- availableOffset: attachment.replay.availableOffset,
91
- endOffset: attachment.replay.endOffset,
92
- truncated: attachment.replay.truncated
59
+ const socket = yield* Effect.orDie(ctx.request.upgrade), write = yield* socket.writer, outbox = yield* Queue.unbounded(), input = yield* Semaphore.make(1);
60
+ let attachment;
61
+ const onOpen = Effect.gen(function* () {
62
+ attachment = yield* pty.attach(ctx.params.ptyID, {
63
+ cursor,
64
+ attachmentID,
65
+ role,
66
+ takeover: url.searchParams.get("takeover") === "true",
67
+ onEvent: (event) => {
68
+ if (event.type === "output")
69
+ Queue.offerUnsafe(outbox, event.data);
70
+ if (event.type === "resized")
71
+ Queue.offerUnsafe(outbox, JSON.stringify({ ...event, checkpoint: Buffer.from(event.checkpoint).toString("base64") }));
72
+ if (event.type !== "output" && event.type !== "resized")
73
+ Queue.offerUnsafe(outbox, JSON.stringify(event));
74
+ },
75
+ onEnd: () => Queue.offerUnsafe(outbox, new Socket.CloseEvent(1000))
76
+ }).pipe(Effect.catchTags({
77
+ "PersistentPty.NotFoundError": () => Effect.succeed(void 0),
78
+ "PersistentPty.UnavailableError": () => Effect.succeed(void 0)
79
+ }));
80
+ if (!attachment) {
81
+ Queue.offerUnsafe(outbox, new Socket.CloseEvent(4404, "terminal unavailable"));
82
+ return;
93
83
  }
94
- }));
95
- if (attachment.replay.data.length > 0)
96
- Queue.offerUnsafe(outbox, attachment.replay.data);
97
- Queue.offerUnsafe(outbox, JSON.stringify({ type: "replay_complete", endOffset: attachment.replay.endOffset }));
98
- attachment.activate();
99
- const drain = Effect.gen(function* () {
84
+ Queue.offerUnsafe(outbox, JSON.stringify({
85
+ type: "attached",
86
+ attachmentID,
87
+ inputProtocol: framedInput ? 1 : 0,
88
+ info: attachment.info,
89
+ role: attachment.role,
90
+ generation: attachment.generation,
91
+ replay: {
92
+ requestedOffset: attachment.replay.requestedOffset,
93
+ availableOffset: attachment.replay.availableOffset,
94
+ endOffset: attachment.replay.endOffset,
95
+ truncated: attachment.replay.truncated
96
+ }
97
+ }));
98
+ if (attachment.replay.data.length > 0)
99
+ Queue.offerUnsafe(outbox, attachment.replay.data);
100
+ Queue.offerUnsafe(outbox, JSON.stringify({ type: "replay_complete", endOffset: attachment.replay.endOffset }));
101
+ attachment.activate();
102
+ }), drain = Effect.gen(function* () {
100
103
  while (!0) {
101
104
  const item = yield* Queue.take(outbox);
102
105
  yield* write(item);
@@ -105,6 +108,8 @@ export const PersistentPtyHandler = HttpApiBuilder.group(Api, "server.experiment
105
108
  }
106
109
  });
107
110
  yield* Effect.race(drain, socket.runRaw((message) => input.withPermit(Effect.suspend(() => {
111
+ if (!attachment)
112
+ return Effect.void;
108
113
  const data = typeof message === "string" ? Buffer.from(message) : message;
109
114
  if (!framedInput)
110
115
  return pty.input(ctx.params.ptyID, attachmentID, attachment.info.size.cols, attachment.info.size.rows, data).pipe(Effect.ignore);
@@ -116,7 +121,7 @@ export const PersistentPtyHandler = HttpApiBuilder.group(Api, "server.experiment
116
121
  if (type === 0)
117
122
  return pty.control(ctx.params.ptyID, attachmentID, cols, rows).pipe(Effect.ignore);
118
123
  return pty.input(ctx.params.ptyID, attachmentID, cols, rows, data.subarray(5)).pipe(Effect.ignore);
119
- })))).pipe(Effect.catchReason("SocketError", "SocketCloseError", () => Effect.void), Effect.ensuring(Effect.sync(() => attachment.detach())), Effect.orDie);
124
+ })), { onOpen })).pipe(Effect.catchReason("SocketError", "SocketCloseError", () => Effect.void), Effect.ensuring(Effect.sync(() => attachment?.detach())), Effect.orDie);
120
125
  return HttpServerResponse.empty();
121
126
  }));
122
127
  }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode-ai/server",
4
- "version": "0.0.0-dev-18303",
4
+ "version": "0.0.0-dev-18308",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -30,11 +30,11 @@
30
30
  "dependencies": {
31
31
  "@effect/platform-node": "4.0.0-rc.111",
32
32
  "@effect/platform-node-shared": "4.0.0-rc.111",
33
- "@opencode-ai/core": "0.0.0-dev-18303",
34
- "@opencode-ai/protocol": "0.0.0-dev-18303",
35
- "@opencode-ai/schema": "0.0.0-dev-18303",
36
- "@opencode-ai/simulation": "0.0.0-dev-18303",
37
- "@opencode-ai/util": "0.0.0-dev-18303",
33
+ "@opencode-ai/core": "0.0.0-dev-18308",
34
+ "@opencode-ai/protocol": "0.0.0-dev-18308",
35
+ "@opencode-ai/schema": "0.0.0-dev-18308",
36
+ "@opencode-ai/simulation": "0.0.0-dev-18308",
37
+ "@opencode-ai/util": "0.0.0-dev-18308",
38
38
  "drizzle-orm": "1.0.0-rc.5-169397b",
39
39
  "effect": "4.0.0-rc.111"
40
40
  },