@opencode/server 0.0.0-dev-19653 → 0.0.0-dev-19656

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1 @@
1
- import { Instance } from "@opencode/core/instance/service";
2
- import { LocationServiceMap } from "@opencode/core/location-services";
3
- import { Session } from "@opencode/core/session";
4
- export declare const FormHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.form">, never, LocationServiceMap.Service | Session.Service | Instance.Service | import("../location").LocationMiddleware | (import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../middleware/form-location").FormLocationMiddleware)>;
1
+ export declare const FormHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.form">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -1,61 +1,9 @@
1
1
  import { Form } from "@opencode/core/form";
2
- import { Instance } from "@opencode/core/instance/service";
3
- import { LocationServiceMap } from "@opencode/core/location-services";
4
- import { Session } from "@opencode/core/session";
5
- import {
6
- ConflictError,
7
- FormAlreadySettledError,
8
- FormInvalidAnswerError,
9
- FormNotFoundError,
10
- InvalidRequestError
11
- } from "@opencode/protocol/errors";
12
2
  import { Effect } from "effect";
13
- import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
3
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
14
4
  import { Api } from "../api";
15
- import { requestRef, response, sessionInfo } from "../location";
16
- function missingForm(id) {
17
- return new FormNotFoundError({ id, message: `Form not found: ${id}` });
18
- }
19
- export const FormHandler = HttpApiBuilder.group(Api, "server.form", (handlers) => Effect.gen(function* () {
20
- const locations = yield* LocationServiceMap.Service, instances = yield* Instance.Service, sessions = yield* Session.Service, requireOwnedForm = Effect.fnUntraced(function* (sessionID, formID) {
21
- const form = yield* Form.Service, info = yield* form.get(formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(formID)));
22
- if (info.sessionID !== sessionID)
23
- return yield* missingForm(formID);
24
- return { form, info };
25
- });
26
- return handlers.handle("form.request.list", Effect.fn(function* () {
27
- const form = yield* Form.Service;
28
- return yield* response(form.list());
29
- })).handle("session.form.list", Effect.fn(function* (ctx) {
30
- const session = ctx.params.sessionID === "global" ? void 0 : yield* sessionInfo(sessions, ctx.params.sessionID), read = Form.Service.use((form) => form.list({ sessionID: ctx.params.sessionID }));
31
- return { data: yield* session ? read.pipe(instances.provide(session)) : read.pipe(Effect.provide(locations.get(requestRef(ctx.request)))) };
32
- })).handle("session.form.create", Effect.fn(function* (ctx) {
33
- return { data: yield* (yield* Form.Service).create({
34
- id: ctx.payload.id,
35
- sessionID: ctx.params.sessionID,
36
- title: ctx.payload.title,
37
- metadata: ctx.payload.metadata,
38
- fields: ctx.payload.fields
39
- }).pipe(Effect.catchTags({
40
- "Form.AlreadyExistsError": (error) => new ConflictError({ resource: error.id, message: error.message }),
41
- "Form.InvalidFormError": (error) => new InvalidRequestError({ message: error.message, field: "fields" })
42
- })) };
43
- })).handle("session.form.get", Effect.fn(function* (ctx) {
44
- return { data: (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).info };
45
- })).handle("session.form.state", Effect.fn(function* (ctx) {
46
- return { data: yield* (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).form.state(ctx.params.formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(ctx.params.formID))) };
47
- })).handle("session.form.reply", Effect.fn(function* (ctx) {
48
- yield* (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).form.reply({ id: ctx.params.formID, answer: ctx.payload.answer }).pipe(Effect.catchTags({
49
- "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
50
- "Form.InvalidAnswerError": (error) => new FormInvalidAnswerError({ id: error.id, message: error.message }),
51
- "Form.NotFoundError": () => missingForm(ctx.params.formID)
52
- }));
53
- return HttpApiSchema.NoContent.make();
54
- })).handle("session.form.cancel", Effect.fn(function* (ctx) {
55
- yield* (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).form.cancel(ctx.params.formID).pipe(Effect.catchTags({
56
- "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
57
- "Form.NotFoundError": () => missingForm(ctx.params.formID)
58
- }));
59
- return HttpApiSchema.NoContent.make();
60
- }));
61
- }));
5
+ import { response } from "../location";
6
+ export const FormHandler = HttpApiBuilder.group(Api, "server.form", (handlers) => handlers.handle("form.list", Effect.fn(function* () {
7
+ const form = yield* Form.Service;
8
+ return yield* response(form.list());
9
+ })));
@@ -41,7 +41,7 @@ export const PermissionHandler = HttpApiBuilder.group(Api, "server.permission",
41
41
  })).handle("session.permission.get", Effect.fn(function* (ctx) {
42
42
  return { data: (yield* requireOwnedRequest(ctx.params.sessionID, ctx.params.requestID)).request };
43
43
  })).handle("session.permission.reply", Effect.fn(function* (ctx) {
44
- yield* (yield* requireOwnedRequest(ctx.params.sessionID, ctx.params.requestID)).permission.reply({ requestID: ctx.params.requestID, reply: ctx.payload.reply, message: ctx.payload.message }).pipe(Effect.catchTag("Permission.NotFoundError", () => missingRequest(ctx.params.requestID)));
44
+ yield* (yield* requireOwnedRequest(ctx.params.sessionID, ctx.params.requestID)).permission.reply({ requestID: ctx.params.requestID, reply: ctx.payload.decision, message: ctx.payload.message }).pipe(Effect.catchTag("Permission.NotFoundError", () => missingRequest(ctx.params.requestID)));
45
45
  return HttpApiSchema.NoContent.make();
46
46
  })).handle("session.permission.rules", Effect.fn(function* (ctx) {
47
47
  yield* sessions.setPermissions({ sessionID: ctx.params.sessionID, permissions: ctx.payload.permissions }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
@@ -1,3 +1,3 @@
1
1
  import { Session } from "@opencode/core/session";
2
2
  import { SessionTransfer } from "@opencode/core/session/transfer";
3
- export declare const SessionHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.session">, never, Session.Service | import("../middleware/session-location").SessionLocationMiddleware | SessionTransfer.Service | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/database/database").Service> | (import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization)>;
3
+ export declare const SessionHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.session">, never, Session.Service | import("../middleware/session-location").SessionLocationMiddleware | SessionTransfer.Service | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/database/database").Service> | (import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../middleware/form-location").FormLocationMiddleware)>;
@@ -3,6 +3,7 @@ import { SessionStats } from "@opencode/core/session/stats";
3
3
  import { SessionTitle } from "@opencode/core/session/title";
4
4
  import { SessionTransfer } from "@opencode/core/session/transfer";
5
5
  import { InstructionEntry } from "@opencode/core/session/instruction-entry";
6
+ import { Form } from "@opencode/core/form";
6
7
  import { DateTime, Effect, Stream } from "effect";
7
8
  import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
8
9
  import { Api } from "../api";
@@ -11,6 +12,9 @@ import {
11
12
  ConflictError,
12
13
  CommandExecutionError,
13
14
  CommandNotFoundError,
15
+ FormAlreadySettledError,
16
+ FormInvalidAnswerError,
17
+ FormNotFoundError,
14
18
  InvalidRequestError,
15
19
  InvalidCursorError,
16
20
  MessageNotFoundError,
@@ -21,8 +25,16 @@ import {
21
25
  import { AbsolutePath } from "@opencode/core/schema";
22
26
  import { failedMessageDecode, failedSnapshot, missingMessage, missingSession } from "./session-error";
23
27
  const DefaultSessionsLimit = 50;
28
+ function missingForm(id) {
29
+ return new FormNotFoundError({ id, message: `Form not found: ${id}` });
30
+ }
24
31
  export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) => Effect.gen(function* () {
25
- const session = yield* Session.Service, transfer = yield* SessionTransfer.Service, busySession = (error) => new SessionBusyError({
32
+ const session = yield* Session.Service, transfer = yield* SessionTransfer.Service, requireOwnedForm = Effect.fnUntraced(function* (sessionID, formID) {
33
+ const form = yield* Form.Service, info = yield* form.get(formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(formID)));
34
+ if (info.sessionID !== sessionID)
35
+ return yield* missingForm(formID);
36
+ return { form, info };
37
+ }), busySession = (error) => new SessionBusyError({
26
38
  sessionID: error.sessionID,
27
39
  message: `Session is busy: ${error.sessionID}`
28
40
  }), pendingMutation = (effect, conflict) => effect.pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.InboxConflictError", (error) => new ConflictError({ resource: error.inboxID, message: `${conflict}: ${error.inboxID}` })), Effect.as(HttpApiSchema.NoContent.make()));
@@ -232,11 +244,10 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
232
244
  data: yield* session.inbox(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession))
233
245
  };
234
246
  })).handle("session.inbox.cancel", Effect.fn(function* (ctx) {
235
- return yield* pendingMutation(session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input can no longer be cancelled");
236
- })).handle("session.inbox.steer", Effect.fn(function* (ctx) {
237
- return yield* pendingMutation(session.steerInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input is no longer queued");
238
- })).handle("session.inbox.queue", Effect.fn(function* (ctx) {
239
- return yield* pendingMutation(session.queueInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input is no longer a steer");
247
+ yield* session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.InboxConflictError", () => Effect.void));
248
+ return HttpApiSchema.NoContent.make();
249
+ })).handle("session.inbox.update", Effect.fn(function* (ctx) {
250
+ return yield* pendingMutation(ctx.payload.delivery === "steer" ? session.steerInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }) : session.queueInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), `Pending input cannot change to ${ctx.payload.delivery}`);
240
251
  })).handle("session.instructions.entry.list", Effect.fn(function* (ctx) {
241
252
  return { data: yield* (yield* InstructionEntry.Service).list(ctx.params.sessionID) };
242
253
  })).handle("session.instructions.entry.put", Effect.fn(function* (ctx) {
@@ -265,5 +276,34 @@ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handl
265
276
  messageID: ctx.params.messageID,
266
277
  message: `Message not found: ${ctx.params.messageID}`
267
278
  });
279
+ })).handle("session.form.list", Effect.fn(function* (ctx) {
280
+ return { data: yield* (yield* Form.Service).list({ sessionID: ctx.params.sessionID }) };
281
+ })).handle("session.form.create", Effect.fn(function* (ctx) {
282
+ return { data: yield* (yield* Form.Service).create({
283
+ id: ctx.payload.id,
284
+ sessionID: ctx.params.sessionID,
285
+ title: ctx.payload.title,
286
+ metadata: ctx.payload.metadata,
287
+ fields: ctx.payload.fields
288
+ }).pipe(Effect.catchTags({
289
+ "Form.AlreadyExistsError": (error) => new ConflictError({ resource: error.id, message: error.message }),
290
+ "Form.InvalidFormError": (error) => new InvalidRequestError({ message: error.message, field: "fields" })
291
+ })) };
292
+ })).handle("session.form.get", Effect.fn(function* (ctx) {
293
+ const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID), state = yield* owned.form.state(ctx.params.formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(ctx.params.formID)));
294
+ return { data: { ...owned.info, state } };
295
+ })).handle("session.form.reply", Effect.fn(function* (ctx) {
296
+ yield* (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).form.reply({ id: ctx.params.formID, answer: ctx.payload.answer }).pipe(Effect.catchTags({
297
+ "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
298
+ "Form.InvalidAnswerError": (error) => new FormInvalidAnswerError({ id: error.id, message: error.message }),
299
+ "Form.NotFoundError": () => missingForm(ctx.params.formID)
300
+ }));
301
+ return HttpApiSchema.NoContent.make();
302
+ })).handle("session.form.cancel", Effect.fn(function* (ctx) {
303
+ yield* (yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID)).form.cancel(ctx.params.formID).pipe(Effect.catchTags({
304
+ "Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
305
+ "Form.NotFoundError": () => missingForm(ctx.params.formID)
306
+ }));
307
+ return HttpApiSchema.NoContent.make();
268
308
  }));
269
309
  }));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@opencode/server",
4
- "version": "0.0.0-dev-19653",
4
+ "version": "0.0.0-dev-19656",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -30,17 +30,17 @@
30
30
  "dependencies": {
31
31
  "@effect/platform-node": "4.0.0-rc.112",
32
32
  "@effect/platform-node-shared": "4.0.0-rc.112",
33
- "@opencode/core": "0.0.0-dev-19653",
34
- "@opencode/protocol": "0.0.0-dev-19653",
35
- "@opencode/schema": "0.0.0-dev-19653",
36
- "@opencode/simulation": "0.0.0-dev-19653",
37
- "@opencode/util": "0.0.0-dev-19653",
33
+ "@opencode/core": "0.0.0-dev-19656",
34
+ "@opencode/protocol": "0.0.0-dev-19656",
35
+ "@opencode/schema": "0.0.0-dev-19656",
36
+ "@opencode/simulation": "0.0.0-dev-19656",
37
+ "@opencode/util": "0.0.0-dev-19656",
38
38
  "drizzle-orm": "1.0.0-rc.5-169397b",
39
39
  "effect": "4.0.0-rc.112"
40
40
  },
41
41
  "devDependencies": {
42
- "@opencode/client": "0.0.0-dev-19653",
43
- "@opencode/plugin": "0.0.0-dev-19653",
42
+ "@opencode/client": "0.0.0-dev-19656",
43
+ "@opencode/plugin": "0.0.0-dev-19656",
44
44
  "@tsconfig/bun": "1.0.9",
45
45
  "@types/bun": "1.4.0",
46
46
  "@typescript/native-preview": "7.0.0-dev.20251207.1"