@opencode/server 0.0.0-reserved → 2.0.1

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 (108) hide show
  1. package/dist/api.d.ts +8394 -0
  2. package/dist/api.js +9 -0
  3. package/dist/auth.d.ts +17 -0
  4. package/dist/auth.js +17 -0
  5. package/dist/cors.d.ts +7 -0
  6. package/dist/cors.js +36 -0
  7. package/dist/event-feed.d.ts +32 -0
  8. package/dist/event-feed.js +60 -0
  9. package/dist/fetch.d.ts +77 -0
  10. package/dist/fetch.js +11 -0
  11. package/dist/handlers/agent.d.ts +1 -0
  12. package/dist/handlers/agent.js +17 -0
  13. package/dist/handlers/command.d.ts +1 -0
  14. package/dist/handlers/command.js +5 -0
  15. package/dist/handlers/config.d.ts +1 -0
  16. package/dist/handlers/config.js +4 -0
  17. package/dist/handlers/credential.d.ts +1 -0
  18. package/dist/handlers/credential.js +14 -0
  19. package/dist/handlers/debug.d.ts +1 -0
  20. package/dist/handlers/debug.js +11 -0
  21. package/dist/handlers/event.d.ts +2 -0
  22. package/dist/handlers/event.js +27 -0
  23. package/dist/handlers/form.d.ts +4 -0
  24. package/dist/handlers/form.js +61 -0
  25. package/dist/handlers/fs.d.ts +1 -0
  26. package/dist/handlers/fs.js +19 -0
  27. package/dist/handlers/generate.d.ts +3 -0
  28. package/dist/handlers/generate.js +15 -0
  29. package/dist/handlers/health.d.ts +2 -0
  30. package/dist/handlers/health.js +11 -0
  31. package/dist/handlers/integration.d.ts +2 -0
  32. package/dist/handlers/integration.js +83 -0
  33. package/dist/handlers/location.d.ts +1 -0
  34. package/dist/handlers/location.js +12 -0
  35. package/dist/handlers/mcp.d.ts +1 -0
  36. package/dist/handlers/mcp.js +31 -0
  37. package/dist/handlers/message.d.ts +2 -0
  38. package/dist/handlers/message.js +43 -0
  39. package/dist/handlers/migration.d.ts +1 -0
  40. package/dist/handlers/migration.js +7 -0
  41. package/dist/handlers/model.d.ts +1 -0
  42. package/dist/handlers/model.js +14 -0
  43. package/dist/handlers/permission.d.ts +4 -0
  44. package/dist/handlers/permission.js +60 -0
  45. package/dist/handlers/persistent-pty.d.ts +3 -0
  46. package/dist/handlers/persistent-pty.js +136 -0
  47. package/dist/handlers/plugin.d.ts +2 -0
  48. package/dist/handlers/plugin.js +51 -0
  49. package/dist/handlers/project.d.ts +2 -0
  50. package/dist/handlers/project.js +14 -0
  51. package/dist/handlers/provider.d.ts +1 -0
  52. package/dist/handlers/provider.js +20 -0
  53. package/dist/handlers/pty-socket.d.ts +2 -0
  54. package/dist/handlers/pty-socket.js +4 -0
  55. package/dist/handlers/pty.d.ts +3 -0
  56. package/dist/handlers/pty.js +108 -0
  57. package/dist/handlers/reference.d.ts +1 -0
  58. package/dist/handlers/reference.js +5 -0
  59. package/dist/handlers/rpc.d.ts +1 -0
  60. package/dist/handlers/rpc.js +15 -0
  61. package/dist/handlers/server.d.ts +2 -0
  62. package/dist/handlers/server.js +7 -0
  63. package/dist/handlers/session-error.d.ts +5 -0
  64. package/dist/handlers/session-error.js +13 -0
  65. package/dist/handlers/session.d.ts +3 -0
  66. package/dist/handlers/session.js +289 -0
  67. package/dist/handlers/shell.d.ts +1 -0
  68. package/dist/handlers/shell.js +28 -0
  69. package/dist/handlers/skill.d.ts +1 -0
  70. package/dist/handlers/skill.js +5 -0
  71. package/dist/handlers/vcs.d.ts +1 -0
  72. package/dist/handlers/vcs.js +19 -0
  73. package/dist/handlers/websearch.d.ts +1 -0
  74. package/dist/handlers/websearch.js +31 -0
  75. package/dist/handlers/workspace.d.ts +2 -0
  76. package/dist/handlers/workspace.js +20 -0
  77. package/dist/handlers/worktree.d.ts +1 -0
  78. package/dist/handlers/worktree.js +39 -0
  79. package/dist/handlers.d.ts +2 -0
  80. package/dist/handlers.js +35 -0
  81. package/dist/location.d.ts +26 -0
  82. package/dist/location.js +52 -0
  83. package/dist/middleware/authorization.d.ts +7 -0
  84. package/dist/middleware/authorization.js +49 -0
  85. package/dist/middleware/form-location.d.ts +18 -0
  86. package/dist/middleware/form-location.js +25 -0
  87. package/dist/middleware/schema-error.d.ts +3 -0
  88. package/dist/middleware/schema-error.js +15 -0
  89. package/dist/middleware/session-location.d.ts +17 -0
  90. package/dist/middleware/session-location.js +19 -0
  91. package/dist/options.d.ts +47 -0
  92. package/dist/options.js +35 -0
  93. package/dist/process.d.ts +58 -0
  94. package/dist/process.js +131 -0
  95. package/dist/pty-environment.d.ts +13 -0
  96. package/dist/pty-environment.js +9 -0
  97. package/dist/request-tracing.d.ts +2 -0
  98. package/dist/request-tracing.js +2 -0
  99. package/dist/routes.d.ts +29 -0
  100. package/dist/routes.js +116 -0
  101. package/dist/server-info.d.ts +11 -0
  102. package/dist/server-info.js +22 -0
  103. package/dist/service-status.d.ts +25 -0
  104. package/dist/service-status.js +11 -0
  105. package/dist/workerd.d.ts +45 -0
  106. package/dist/workerd.js +61 -0
  107. package/package.json +39 -6
  108. package/README.md +0 -5
@@ -0,0 +1,289 @@
1
+ import { Session } from "@opencode/core/session";
2
+ import { SessionStats } from "@opencode/core/session/stats";
3
+ import { SessionTitle } from "@opencode/core/session/title";
4
+ import { SessionTransfer } from "@opencode/core/session/transfer";
5
+ import { InstructionEntry } from "@opencode/core/session/instruction-entry";
6
+ import { DateTime, Effect, Stream } from "effect";
7
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
8
+ import { Api } from "../api";
9
+ import { SessionsCursor } from "@opencode/protocol/groups/session";
10
+ import {
11
+ ConflictError,
12
+ CommandExecutionError,
13
+ CommandNotFoundError,
14
+ InvalidRequestError,
15
+ InvalidCursorError,
16
+ MessageNotFoundError,
17
+ ServiceUnavailableError,
18
+ SessionBusyError,
19
+ SkillNotFoundError,
20
+ UnknownError
21
+ } from "@opencode/protocol/errors";
22
+ import { AbsolutePath } from "@opencode/core/schema";
23
+ import { failedMessageDecode, missingSession } from "./session-error";
24
+ const DefaultSessionsLimit = 50;
25
+ export const SessionHandler = HttpApiBuilder.group(Api, "server.session", (handlers) => Effect.gen(function* () {
26
+ const session = yield* Session.Service, transfer = yield* SessionTransfer.Service, busySession = (error) => new SessionBusyError({
27
+ sessionID: error.sessionID,
28
+ message: `Session is busy: ${error.sessionID}`
29
+ }), 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()));
30
+ return handlers.handle("session.list", Effect.fn(function* (ctx) {
31
+ const query = ctx.query.cursor !== void 0 ? yield* SessionsCursor.parse(ctx.query.cursor).pipe(Effect.mapError(() => new InvalidCursorError({ message: "Invalid cursor" }))) : ctx.query, sessions = (yield* session.list({
32
+ ...query,
33
+ workspaceID: query.workspace,
34
+ limit: ctx.query.limit ?? DefaultSessionsLimit
35
+ })).data, first = sessions[0], last = sessions.at(-1);
36
+ return {
37
+ data: sessions,
38
+ cursor: {
39
+ previous: first ? SessionsCursor.make({
40
+ ...query,
41
+ anchor: {
42
+ id: first.id,
43
+ time: DateTime.toEpochMillis(first.time.updated),
44
+ direction: "previous"
45
+ }
46
+ }) : void 0,
47
+ next: last ? SessionsCursor.make({
48
+ ...query,
49
+ anchor: {
50
+ id: last.id,
51
+ time: DateTime.toEpochMillis(last.time.updated),
52
+ direction: "next"
53
+ }
54
+ }) : void 0
55
+ }
56
+ };
57
+ })).handle("session.stats", Effect.fn(function* (ctx) {
58
+ const timezone = ctx.query.timezone ?? "UTC";
59
+ yield* Effect.try({
60
+ try: () => new Intl.DateTimeFormat("en-US", { timeZone: timezone }),
61
+ catch: () => new InvalidRequestError({ message: `Invalid time zone: ${timezone}` })
62
+ });
63
+ return {
64
+ data: yield* SessionStats.get({
65
+ from: ctx.query.from,
66
+ to: ctx.query.to,
67
+ projectID: ctx.query.project,
68
+ timezone,
69
+ tools: ctx.query.tools
70
+ }).pipe(Effect.mapError(() => new InvalidRequestError({ message: "Stats range must end after it starts" })))
71
+ };
72
+ })).handle("session.create", Effect.fn(function* (ctx) {
73
+ return {
74
+ data: yield* session.create({
75
+ id: ctx.payload.id,
76
+ title: ctx.payload.title,
77
+ agent: ctx.payload.agent,
78
+ model: ctx.payload.model,
79
+ metadata: ctx.payload.metadata,
80
+ permissions: ctx.payload.permissions,
81
+ location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
82
+ }).pipe(Effect.orDie)
83
+ };
84
+ })).handle("session.import", Effect.fn(function* (ctx) {
85
+ return {
86
+ data: yield* transfer.import({
87
+ data: { info: ctx.payload.info, messages: ctx.payload.messages },
88
+ location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
89
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("SessionTransfer.ImportConflictError", (error) => new ConflictError({
90
+ message: `Session already exists: ${error.sessionID}`,
91
+ resource: error.sessionID
92
+ })))
93
+ };
94
+ })).handle("session.export", Effect.fn(function* (ctx) {
95
+ return {
96
+ data: yield* transfer.export({ sessionID: ctx.params.sessionID, sanitize: ctx.query.sanitize }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode))
97
+ };
98
+ })).handle("session.active", Effect.fn(function* () {
99
+ const active = yield* session.active;
100
+ return {
101
+ data: Object.fromEntries(Array.from(active, (sessionID) => [sessionID, { type: "running" }]))
102
+ };
103
+ })).handle("session.get", Effect.fn(function* (ctx) {
104
+ return {
105
+ data: yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession))
106
+ };
107
+ })).handle("session.view", Effect.fn(function* (ctx) {
108
+ yield* session.view({ sessionID: ctx.params.sessionID, idle: ctx.payload.idle }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
109
+ return HttpApiSchema.NoContent.make();
110
+ })).handle("session.remove", Effect.fn(function* (ctx) {
111
+ yield* session.remove(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
112
+ return HttpApiSchema.NoContent.make();
113
+ })).handle("session.environment", Effect.fn(function* (ctx) {
114
+ yield* session.environment({ sessionID: ctx.params.sessionID, variables: ctx.payload.variables }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
115
+ return HttpApiSchema.NoContent.make();
116
+ })).handle("session.fork", Effect.fn(function* (ctx) {
117
+ return {
118
+ data: yield* session.fork({ sessionID: ctx.params.sessionID, boundary: ctx.payload.boundary }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.MessageNotFoundError", (error) => new MessageNotFoundError({
119
+ sessionID: error.sessionID,
120
+ messageID: error.messageID,
121
+ message: `Message not found: ${error.messageID}`
122
+ })), Effect.catchTag("Session.ForkEmptyError", (error) => new InvalidRequestError({ message: error.message, kind: "empty_session" })))
123
+ };
124
+ })).handle("session.switchAgent", Effect.fn(function* (ctx) {
125
+ yield* session.switchAgent({ sessionID: ctx.params.sessionID, agent: ctx.payload.agent }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
126
+ return HttpApiSchema.NoContent.make();
127
+ })).handle("session.switchModel", Effect.fn(function* (ctx) {
128
+ yield* session.switchModel({ sessionID: ctx.params.sessionID, model: ctx.payload.model }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
129
+ return HttpApiSchema.NoContent.make();
130
+ })).handle("session.rename", Effect.fn(function* (ctx) {
131
+ if (ctx.payload.title) {
132
+ yield* session.rename({ sessionID: ctx.params.sessionID, title: ctx.payload.title }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
133
+ return HttpApiSchema.NoContent.make();
134
+ }
135
+ yield* (yield* SessionTitle.Service).generate(ctx.params.sessionID);
136
+ return HttpApiSchema.NoContent.make();
137
+ })).handle("session.move", Effect.fn(function* (ctx) {
138
+ yield* session.move({
139
+ sessionID: ctx.params.sessionID,
140
+ directory: ctx.payload.directory,
141
+ workspaceID: ctx.payload.workspaceID,
142
+ delivery: ctx.payload.delivery
143
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.DestinationNotFoundError", (error) => Effect.fail(new InvalidRequestError({ message: `Directory does not exist: ${error.directory}` }))), Effect.catchTag("Session.DestinationNotDirectoryError", (error) => Effect.fail(new InvalidRequestError({ message: `Not a directory: ${error.directory}` }))), Effect.catchTag("Session.DestinationUnavailableError", (error) => Effect.fail(new InvalidRequestError({ message: `Directory is unavailable: ${error.directory}` }))));
144
+ return HttpApiSchema.NoContent.make();
145
+ })).handle("session.prompt", Effect.fn(function* (ctx) {
146
+ return {
147
+ data: yield* session.prompt({
148
+ sessionID: ctx.params.sessionID,
149
+ id: ctx.payload.id,
150
+ text: ctx.payload.text,
151
+ files: ctx.payload.files,
152
+ agents: ctx.payload.agents,
153
+ skills: ctx.payload.skills,
154
+ metadata: ctx.payload.metadata,
155
+ delivery: ctx.payload.delivery,
156
+ resume: ctx.payload.resume
157
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.PromptConflictError", (error) => Effect.fail(new ConflictError({
158
+ message: `Prompt message ID conflicts with an existing durable record: ${error.messageID}`,
159
+ resource: error.messageID
160
+ }))), Effect.catchTag("Session.AttachmentError", (error) => Effect.fail(new InvalidRequestError({ message: error.message, field: "files" }))), Effect.catchTag("Session.SkillNotFoundError", (error) => Effect.fail(new InvalidRequestError({ message: `Skill not found: ${error.skill}`, field: "skills" }))))
161
+ };
162
+ })).handle("session.command", Effect.fn(function* (ctx) {
163
+ yield* session.command({
164
+ sessionID: ctx.params.sessionID,
165
+ command: ctx.payload.command,
166
+ text: ctx.payload.text,
167
+ files: ctx.payload.files,
168
+ agents: ctx.payload.agents,
169
+ skills: ctx.payload.skills,
170
+ delivery: ctx.payload.delivery
171
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Command.NotFoundError", (error) => Effect.fail(new CommandNotFoundError({
172
+ command: error.command,
173
+ message: error.message
174
+ }))), Effect.catchTag("Command.ExecutionError", (error) => Effect.fail(new CommandExecutionError({
175
+ command: error.command,
176
+ message: error.message
177
+ }))));
178
+ return HttpApiSchema.NoContent.make();
179
+ })).handle("session.skill", Effect.fn(function* (ctx) {
180
+ yield* session.skill({
181
+ sessionID: ctx.params.sessionID,
182
+ id: ctx.payload.id,
183
+ skill: ctx.payload.skill,
184
+ resume: ctx.payload.resume
185
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.SkillNotFoundError", (error) => Effect.fail(new SkillNotFoundError({ skill: error.skill, message: `Skill not found: ${error.skill}` }))));
186
+ return HttpApiSchema.NoContent.make();
187
+ })).handle("session.synthetic", Effect.fn(function* (ctx) {
188
+ return { data: yield* session.synthetic({
189
+ id: ctx.payload.id,
190
+ sessionID: ctx.params.sessionID,
191
+ text: ctx.payload.text,
192
+ description: ctx.payload.description,
193
+ metadata: ctx.payload.metadata,
194
+ delivery: ctx.payload.delivery,
195
+ resume: ctx.payload.resume
196
+ }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.SyntheticConflictError", (error) => Effect.fail(new ConflictError({
197
+ message: `Synthetic input ID conflicts with an existing durable record: ${error.inputID}`,
198
+ resource: error.inputID
199
+ })))) };
200
+ })).handle("session.shell", Effect.fn(function* (ctx) {
201
+ yield* session.shell({ sessionID: ctx.params.sessionID, id: ctx.payload.id, command: ctx.payload.command }).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
202
+ return HttpApiSchema.NoContent.make();
203
+ })).handle("session.compact", Effect.fn(function* (ctx) {
204
+ return {
205
+ data: yield* session.compact({ sessionID: ctx.params.sessionID, id: ctx.payload.id, delivery: ctx.payload.delivery }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.CompactionConflictError", (error) => Effect.fail(new ConflictError({
206
+ message: `Compaction input ID conflicts with an existing durable record: ${error.inputID}`,
207
+ resource: error.inputID
208
+ }))))
209
+ };
210
+ })).handle("session.wait", Effect.fn(function* (ctx) {
211
+ yield* session.wait(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
212
+ return HttpApiSchema.NoContent.make();
213
+ })).handle("session.revert.stage", Effect.fn(function* (ctx) {
214
+ yield* Effect.log("session.revert.stage", {
215
+ sessionID: ctx.params.sessionID,
216
+ messageID: ctx.payload.messageID,
217
+ files: ctx.payload.files
218
+ });
219
+ return {
220
+ data: yield* session.revert.stage({ ...ctx.params, ...ctx.payload }).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.MessageNotFoundError", (error) => new MessageNotFoundError({
221
+ sessionID: error.sessionID,
222
+ messageID: error.messageID,
223
+ message: `Message not found: ${error.messageID}`
224
+ })), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", (error) => {
225
+ const ref = `err_${crypto.randomUUID().slice(0, 8)}`;
226
+ return Effect.logError("failed to stage session revert", { cause: error }).pipe(Effect.andThen(Effect.fail(new UnknownError({
227
+ message: "Unexpected server error. Check server logs for details.",
228
+ ref
229
+ }))));
230
+ }))
231
+ };
232
+ })).handle("session.revert.clear", Effect.fn(function* (ctx) {
233
+ yield* Effect.log("session.revert.clear", { sessionID: ctx.params.sessionID });
234
+ yield* session.revert.clear(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", (error) => {
235
+ const ref = `err_${crypto.randomUUID().slice(0, 8)}`;
236
+ return Effect.logError("failed to clear session revert", { cause: error }).pipe(Effect.andThen(Effect.fail(new UnknownError({
237
+ message: "Unexpected server error. Check server logs for details.",
238
+ ref
239
+ }))));
240
+ }));
241
+ return HttpApiSchema.NoContent.make();
242
+ })).handle("session.revert.commit", Effect.fn(function* (ctx) {
243
+ yield* Effect.log("session.revert.commit", { sessionID: ctx.params.sessionID });
244
+ yield* session.revert.commit(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.BusyError", busySession));
245
+ return HttpApiSchema.NoContent.make();
246
+ })).handle("session.context", Effect.fn(function* (ctx) {
247
+ return {
248
+ data: yield* session.context(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode))
249
+ };
250
+ })).handle("session.inbox.list", Effect.fn(function* (ctx) {
251
+ return {
252
+ data: yield* session.inbox(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession))
253
+ };
254
+ })).handle("session.inbox.cancel", Effect.fn(function* (ctx) {
255
+ return yield* pendingMutation(session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input can no longer be cancelled");
256
+ })).handle("session.inbox.steer", Effect.fn(function* (ctx) {
257
+ return yield* pendingMutation(session.steerInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input is no longer queued");
258
+ })).handle("session.inbox.queue", Effect.fn(function* (ctx) {
259
+ return yield* pendingMutation(session.queueInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }), "Pending input is no longer a steer");
260
+ })).handle("session.instructions.entry.list", Effect.fn(function* (ctx) {
261
+ return { data: yield* (yield* InstructionEntry.Service).list(ctx.params.sessionID) };
262
+ })).handle("session.instructions.entry.put", Effect.fn(function* (ctx) {
263
+ yield* (yield* InstructionEntry.Service).put({ sessionID: ctx.params.sessionID, key: ctx.params.key, value: ctx.payload.value });
264
+ return HttpApiSchema.NoContent.make();
265
+ })).handle("session.instructions.entry.remove", Effect.fn(function* (ctx) {
266
+ yield* (yield* InstructionEntry.Service).remove({ sessionID: ctx.params.sessionID, key: ctx.params.key });
267
+ return HttpApiSchema.NoContent.make();
268
+ })).handle("session.generate", Effect.fn(function* (ctx) {
269
+ return { data: { text: yield* session.generate({ sessionID: ctx.params.sessionID, prompt: ctx.payload.prompt }).pipe(Effect.mapError((error) => error._tag === "Session.NotFoundError" ? missingSession(error) : new ServiceUnavailableError({ message: error.message, service: "session generation" }))) } };
270
+ })).handle("session.log", Effect.fn(function* (ctx) {
271
+ yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
272
+ return session.log({ sessionID: ctx.params.sessionID, after: ctx.query.after, follow: ctx.query.follow }).pipe(Stream.orDie);
273
+ })).handle("session.interrupt", Effect.fn(function* (ctx) {
274
+ return { interrupted: yield* session.interrupt(ctx.params.sessionID, { continue: ctx.query.continue }) };
275
+ })).handle("session.background", Effect.fn(function* (ctx) {
276
+ yield* session.background(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
277
+ return HttpApiSchema.NoContent.make();
278
+ })).handle("session.message", Effect.fn(function* (ctx) {
279
+ yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
280
+ const message = yield* session.message(ctx.params);
281
+ if (message)
282
+ return { data: message };
283
+ return yield* new MessageNotFoundError({
284
+ sessionID: ctx.params.sessionID,
285
+ messageID: ctx.params.messageID,
286
+ message: `Message not found: ${ctx.params.messageID}`
287
+ });
288
+ }));
289
+ }));
@@ -0,0 +1 @@
1
+ export declare const ShellHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.shell">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -0,0 +1,28 @@
1
+ import { Shell } from "@opencode/core/shell";
2
+ import { Location } from "@opencode/core/location";
3
+ import { Effect } from "effect";
4
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
5
+ import { ShellNotFoundError } from "@opencode/protocol/errors";
6
+ import { Api } from "../api";
7
+ import { response } from "../location";
8
+ export const ShellHandler = HttpApiBuilder.group(Api, "server.shell", (handlers) => Effect.gen(function* () {
9
+ return handlers.handle("shell.list", Effect.fn(function* () {
10
+ const shell = yield* Shell.Service;
11
+ return yield* response(shell.list());
12
+ })).handle("shell.create", Effect.fn(function* (ctx) {
13
+ const shell = yield* Shell.Service, location = yield* Location.Service;
14
+ return yield* response(shell.create({ ...ctx.payload, cwd: ctx.payload.cwd || location.directory }).pipe(Effect.orDie));
15
+ })).handle("shell.get", Effect.fn(function* (ctx) {
16
+ const shell = yield* Shell.Service;
17
+ return yield* response(shell.get(ctx.params.id).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }))));
18
+ })).handle("shell.timeout", Effect.fn(function* (ctx) {
19
+ const shell = yield* Shell.Service;
20
+ return yield* response(shell.timeout(ctx.params.id, ctx.payload.timeout).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }))));
21
+ })).handle("shell.output", Effect.fn(function* (ctx) {
22
+ const shell = yield* Shell.Service;
23
+ return yield* response(shell.output(ctx.params.id, { cursor: ctx.query.cursor, limit: ctx.query.limit }).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` }))));
24
+ })).handle("shell.remove", Effect.fn(function* (ctx) {
25
+ yield* (yield* Shell.Service).remove(ctx.params.id).pipe(Effect.catchTag("Shell.NotFoundError", () => new ShellNotFoundError({ id: ctx.params.id, message: `Shell command not found: ${ctx.params.id}` })));
26
+ return HttpApiSchema.NoContent.make();
27
+ }));
28
+ }));
@@ -0,0 +1 @@
1
+ export declare const SkillHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.skill">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -0,0 +1,5 @@
1
+ import { Skill } from "@opencode/core/skill";
2
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
3
+ import { Api } from "../api";
4
+ import { response } from "../location";
5
+ export const SkillHandler = HttpApiBuilder.group(Api, "server.skill", (handlers) => handlers.handle("skill.list", () => response(Skill.Service.use((skill) => skill.list()))));
@@ -0,0 +1 @@
1
+ export declare const VcsHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.vcs">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -0,0 +1,19 @@
1
+ import { Vcs } from "@opencode/core/vcs";
2
+ import { ServiceUnavailableError } from "@opencode/protocol/errors";
3
+ import { Effect } from "effect";
4
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
5
+ import { Api } from "../api";
6
+ import { response } from "../location";
7
+ export const VcsHandler = HttpApiBuilder.group(Api, "server.vcs", (handlers) => Effect.gen(function* () {
8
+ return handlers.handle("vcs.get", () => response(Effect.gen(function* () {
9
+ return yield* (yield* Vcs.Service).info();
10
+ }))).handle("vcs.base", () => response(Effect.gen(function* () {
11
+ return yield* (yield* Vcs.Service).base().pipe(Effect.mapError((error) => new ServiceUnavailableError({ service: "vcs", message: error.message })));
12
+ }))).handle("vcs.status", () => response(Effect.gen(function* () {
13
+ return yield* (yield* Vcs.Service).status();
14
+ }))).handle("vcs.branches", (ctx) => response(Effect.gen(function* () {
15
+ return yield* (yield* Vcs.Service).branches({ search: ctx.query.search, limit: Math.min(ctx.query.limit ?? 50, 100) });
16
+ }))).handle("vcs.diff", (ctx) => response(Effect.gen(function* () {
17
+ return yield* (yield* Vcs.Service).diff(ctx.query.mode, { context: ctx.query.context, base: ctx.query.base }).pipe(Effect.mapError((error) => new ServiceUnavailableError({ service: "vcs", message: error.message })));
18
+ })));
19
+ }));
@@ -0,0 +1 @@
1
+ export declare const WebSearchHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.websearch">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -0,0 +1,31 @@
1
+ import { WebSearch } from "@opencode/core/websearch";
2
+ import { InvalidRequestError, ServiceUnavailableError } from "@opencode/protocol/errors";
3
+ import { Effect } from "effect";
4
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
5
+ import { Api } from "../api";
6
+ import { response } from "../location";
7
+ export const WebSearchHandler = HttpApiBuilder.group(Api, "server.websearch", (handlers) => Effect.gen(function* () {
8
+ return handlers.handle("websearch.providers", Effect.fn("server.websearch.providers")(function* () {
9
+ const websearch = yield* WebSearch.Service;
10
+ return yield* response(websearch.providers());
11
+ })).handle("websearch.query", Effect.fn("server.websearch.query")(function* (request) {
12
+ const websearch = yield* WebSearch.Service;
13
+ return yield* response(websearch.query(request.payload).pipe(Effect.catchTags({
14
+ "WebSearch.ProviderRequired": () => new InvalidRequestError({
15
+ message: "Web search provider is required",
16
+ kind: "websearch_provider_required",
17
+ field: "providerID"
18
+ }),
19
+ "WebSearch.ProviderNotFound": (error) => new InvalidRequestError({
20
+ message: `Web search provider not found: ${error.providerID}`,
21
+ kind: "websearch_provider_not_found",
22
+ field: "providerID"
23
+ }),
24
+ "WebSearch.Disabled": () => new InvalidRequestError({ message: "Web search is disabled", kind: "websearch_disabled" }),
25
+ "WebSearch.Request": (error) => new ServiceUnavailableError({
26
+ message: `Web search request failed: ${error.providerID}`,
27
+ service: error.providerID
28
+ })
29
+ })));
30
+ }));
31
+ }));
@@ -0,0 +1,2 @@
1
+ import { Workspace } from "@opencode/core/workspace";
2
+ export declare const WorkspaceHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.workspace">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | Workspace.Service>;
@@ -0,0 +1,20 @@
1
+ import { Workspace } from "@opencode/core/workspace";
2
+ import { ConflictError, ProviderNotFoundError, UnknownError } from "@opencode/protocol/errors";
3
+ import { Effect } from "effect";
4
+ import { HttpApiBuilder } from "effect/unstable/httpapi";
5
+ import { Api } from "../api";
6
+ export const WorkspaceHandler = HttpApiBuilder.group(Api, "server.workspace", (handlers) => Effect.gen(function* () {
7
+ const workspace = yield* Workspace.Service;
8
+ return handlers.handle("workspace.create", (ctx) => workspace.create(ctx.payload).pipe(Effect.map((workspaceID) => ({ data: workspaceID })), Effect.catchTags({
9
+ "Workspace.CreateConflict": (error) => new ConflictError({
10
+ resource: error.workspaceID,
11
+ message: `Workspace ${error.workspaceID} already uses provider ${error.existingProvider}, not ${error.provider}`
12
+ }),
13
+ "WorkspaceDriver.ProviderNotFound": (error) => new ProviderNotFoundError({
14
+ providerID: error.provider,
15
+ message: `Workspace provider not found: ${error.provider}`
16
+ })
17
+ }))).handle("workspace.destroy", (ctx) => workspace.destroy(ctx.params.workspaceID).pipe(Effect.mapError((error) => new UnknownError({
18
+ message: error._tag === "WorkspaceDriver.ProviderNotFound" ? `Workspace provider not found: ${error.provider}` : error.message ?? "Workspace provider failed to destroy the workspace"
19
+ }))));
20
+ }));
@@ -0,0 +1 @@
1
+ export declare const WorktreeHandler: import("effect/Layer").Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.worktree">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("../location").LocationMiddleware>;
@@ -0,0 +1,39 @@
1
+ import { Git } from "@opencode/core/git";
2
+ import { Worktree } from "@opencode/core/worktree";
3
+ import { Plugin } from "@opencode/core/plugin";
4
+ import { WorktreeError } from "@opencode/protocol/groups/worktree";
5
+ import { Effect } from "effect";
6
+ import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
7
+ import { Api } from "../api";
8
+ export const WorktreeHandler = HttpApiBuilder.group(Api, "server.worktree", (handlers) => handlers.handle("worktree.list", () => run((worktrees) => worktrees.list())).handle("worktree.create", (ctx) => run((worktrees) => worktrees.create(ctx.payload))).handle("worktree.remove", (ctx) => run((worktrees) => worktrees.remove(ctx.payload)).pipe(Effect.as(HttpApiSchema.NoContent.make()))).handle("worktree.refresh", () => run((worktrees) => worktrees.refresh()).pipe(Effect.as(HttpApiSchema.NoContent.make()))));
9
+ function run(action) {
10
+ return Effect.gen(function* () {
11
+ const plugins = yield* Plugin.Service, worktrees = yield* Worktree.Service;
12
+ yield* plugins.awaitActivation;
13
+ return yield* action(worktrees);
14
+ }).pipe(badRequest);
15
+ }
16
+ function badRequest(effect) {
17
+ return effect.pipe(Effect.mapError((error) => new WorktreeError({
18
+ name: "WorktreeError",
19
+ data: {
20
+ message: message(error),
21
+ forceRequired: error instanceof Git.WorktreeError || error instanceof Worktree.OperationError ? error.forceRequired : void 0
22
+ }
23
+ })));
24
+ }
25
+ function message(error) {
26
+ if (error instanceof Worktree.SourceDirectoryNotFoundError)
27
+ return error.directory ? `Worktree source not found: ${error.directory}` : `Worktree source not found for project: ${error.projectID}`;
28
+ if (error instanceof Worktree.DestinationExistsError)
29
+ return `Worktree destination already exists: ${error.directory}`;
30
+ if (error instanceof Worktree.DirectoryUnavailableError)
31
+ return `Worktree directory unavailable: ${error.directory}`;
32
+ if (error instanceof Worktree.InvalidDirectoryError)
33
+ return `Invalid worktree directory: ${error.directory}`;
34
+ if (error instanceof Worktree.StrategyUnavailableError)
35
+ return `Worktree strategy unavailable: ${error.strategy}`;
36
+ if (error instanceof Worktree.UnsupportedLocationError)
37
+ return "Worktree operations only support local locations";
38
+ return error.message;
39
+ }
@@ -0,0 +1,2 @@
1
+ import { Layer } from "effect";
2
+ export declare const handlers: Layer.Layer<import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.generate"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.message"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.model"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.provider"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.session"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.permission"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.fs"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.form"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.command"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.skill"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.rpc"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.event"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.agent"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.plugin"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.health"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.server"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.debug"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.pty"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.experimental"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.shell"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.reference"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.location"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.integration"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.websearch"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.mcp"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.credential"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.project"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.worktree"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.vcs"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.migration"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.config"> | import("effect/unstable/httpapi/HttpApiGroup").Service<"server", "server.workspace">, never, import("@opencode/protocol/middleware/schema-error").SchemaErrorMiddleware | import("@opencode/protocol/middleware/authorization").Authorization | import("@opencode/util/global").Service | import("@opencode/core/bus").Service | import("@opencode/core/workspace").Service | import("@opencode/core/persistent-pty/index").Service | import("@opencode/core/location-service-map").Service | import("@opencode/core/session").Service | import("@opencode/core/instance").Service | import("./location").LocationMiddleware | import("./middleware/form-location").FormLocationMiddleware | import("./middleware/session-location").SessionLocationMiddleware | import("@opencode/core/pty/ticket").Service | import("@opencode/core/session/transfer").Service | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/database/database").Service> | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/permission/saved").Service> | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/plugin/update").Service> | import("effect/unstable/http/HttpRouter").Request<"Requires", import("./server-info").Service> | import("./pty-environment").Service | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/wellknown").Service> | import("effect/unstable/http/HttpRouter").Request<"Requires", import("@opencode/core/project").Service>>;
@@ -0,0 +1,35 @@
1
+ import { Layer } from "effect";
2
+ import { GenerateHandler } from "./handlers/generate";
3
+ import { MessageHandler } from "./handlers/message";
4
+ import { ModelHandler } from "./handlers/model";
5
+ import { ProviderHandler } from "./handlers/provider";
6
+ import { SessionHandler } from "./handlers/session";
7
+ import { PermissionHandler } from "./handlers/permission";
8
+ import { FileSystemHandler } from "./handlers/fs";
9
+ import { FormHandler } from "./handlers/form";
10
+ import { CommandHandler } from "./handlers/command";
11
+ import { SkillHandler } from "./handlers/skill";
12
+ import { RpcHandler } from "./handlers/rpc";
13
+ import { EventHandler } from "./handlers/event";
14
+ import { AgentHandler } from "./handlers/agent";
15
+ import { PluginHandler } from "./handlers/plugin";
16
+ import { HealthHandler } from "./handlers/health";
17
+ import { ServerHandler } from "./handlers/server";
18
+ import { DebugHandler } from "./handlers/debug";
19
+ import { PtyHandler } from "./handlers/pty";
20
+ import { PersistentPtyHandler } from "./handlers/persistent-pty";
21
+ import { ShellHandler } from "./handlers/shell";
22
+ import { ReferenceHandler } from "./handlers/reference";
23
+ import { LocationHandler } from "./handlers/location";
24
+ import { IntegrationHandler } from "./handlers/integration";
25
+ import { WebSearchHandler } from "./handlers/websearch";
26
+ import { McpHandler } from "./handlers/mcp";
27
+ import { CredentialHandler } from "./handlers/credential";
28
+ import { ProjectHandler } from "./handlers/project";
29
+ import { WorktreeHandler } from "./handlers/worktree";
30
+ import { VcsHandler } from "./handlers/vcs";
31
+ import { EventFeed } from "./event-feed";
32
+ import { MigrationHandler } from "./handlers/migration";
33
+ import { ConfigHandler } from "./handlers/config";
34
+ import { WorkspaceHandler } from "./handlers/workspace";
35
+ export const handlers = Layer.mergeAll(HealthHandler, ServerHandler, DebugHandler, MigrationHandler, LocationHandler, AgentHandler, PluginHandler, SessionHandler, MessageHandler, ModelHandler, GenerateHandler, ProviderHandler, IntegrationHandler, WebSearchHandler, McpHandler, CredentialHandler, ProjectHandler, FormHandler, PermissionHandler, FileSystemHandler, CommandHandler, SkillHandler, RpcHandler, EventHandler.pipe(Layer.provide(EventFeed.layer)), PtyHandler, PersistentPtyHandler, ShellHandler, ReferenceHandler, WorktreeHandler, WorkspaceHandler, VcsHandler, ConfigHandler);
@@ -0,0 +1,26 @@
1
+ import { Location } from "@opencode/core/location";
2
+ import { LocationServiceMap } from "@opencode/core/location-services";
3
+ import { Session } from "@opencode/core/session";
4
+ import { InvalidRequestError } from "@opencode/protocol/errors";
5
+ import { Effect, Layer } from "effect";
6
+ import { HttpServerRequest } from "effect/unstable/http";
7
+ import { HttpApiMiddleware } from "effect/unstable/httpapi";
8
+ export type LocationServices = Layer.Success<ReturnType<(typeof LocationServiceMap.Service)["get"]>>;
9
+ declare const LocationMiddleware_base: HttpApiMiddleware.ServiceClass<LocationMiddleware, "@opencode/HttpApiLocation", {
10
+ requires: never;
11
+ provides: import("@opencode/core/instance").Services;
12
+ error: never;
13
+ clientError: never;
14
+ requiredForClient: false;
15
+ security: never;
16
+ }, HttpApiMiddleware.HttpApiMiddleware<import("@opencode/core/instance").Services, never, never>>;
17
+ export declare class LocationMiddleware extends LocationMiddleware_base {
18
+ }
19
+ export declare function response<A, E, R>(data: Effect.Effect<A, E, R>): Effect.Effect<{
20
+ location: Location.Info;
21
+ data: A;
22
+ }, E, Location.Service | R>;
23
+ export declare const sessionInfo: (sessions: Session.Interface, sessionID: unknown) => Effect.Effect<Session.SessionSchema.Info, InvalidRequestError | import("@opencode/protocol/errors").SessionNotFoundError, never>;
24
+ export declare function requestRef(request: HttpServerRequest.HttpServerRequest): Location.Ref;
25
+ export declare const layer: Layer.Layer<LocationMiddleware, never, LocationServiceMap.Service>;
26
+ export {};
@@ -0,0 +1,52 @@
1
+ import { Location } from "@opencode/core/location";
2
+ import { LocationServiceMap } from "@opencode/core/location-services";
3
+ import { AbsolutePath } from "@opencode/core/schema";
4
+ import { Session } from "@opencode/core/session";
5
+ import { Workspace } from "@opencode/core/workspace";
6
+ import { InvalidRequestError } from "@opencode/protocol/errors";
7
+ import { Effect, Layer, Schema } from "effect";
8
+ import { HttpServerRequest } from "effect/unstable/http";
9
+ import { HttpApiMiddleware } from "effect/unstable/httpapi";
10
+ import { missingSession } from "./handlers/session-error";
11
+
12
+ export class LocationMiddleware extends HttpApiMiddleware.Service()("@opencode/HttpApiLocation") {
13
+ }
14
+ export function response(data) {
15
+ return Effect.gen(function* () {
16
+ const location = yield* Location.Service;
17
+ return {
18
+ location: new Location.Info({
19
+ directory: location.directory,
20
+ workspaceID: location.workspaceID,
21
+ project: location.project
22
+ }),
23
+ data: yield* data
24
+ };
25
+ });
26
+ }
27
+ const decodeSessionID = Schema.decodeUnknownEffect(Session.ID);
28
+ export const sessionInfo = Effect.fnUntraced(function* (sessions, sessionID) {
29
+ const id = yield* decodeSessionID(sessionID).pipe(Effect.mapError(() => new InvalidRequestError({ message: "Invalid session ID", field: "sessionID" })));
30
+ return yield* sessions.get(id).pipe(Effect.catchTag("Session.NotFoundError", missingSession));
31
+ });
32
+ export function requestRef(request) {
33
+ const query = new URL(request.url, "http://localhost").searchParams, workspaceID = query.get("location[workspace]") || request.headers["x-opencode-workspace"], directory = query.get("location[directory]") || (request.headers["x-opencode-directory"] ? decode(request.headers["x-opencode-directory"]) : process.cwd());
34
+ return Location.Ref.make({
35
+ directory: AbsolutePath.make(directory),
36
+ workspaceID: workspaceID ? Workspace.ID.make(workspaceID) : void 0
37
+ });
38
+ }
39
+ function decode(input) {
40
+ try {
41
+ return decodeURIComponent(input);
42
+ } catch {
43
+ return input;
44
+ }
45
+ }
46
+ export const layer = Layer.effect(LocationMiddleware, Effect.gen(function* () {
47
+ const locations = yield* LocationServiceMap.Service;
48
+ return LocationMiddleware.of((effect) => Effect.gen(function* () {
49
+ const request = yield* HttpServerRequest.HttpServerRequest;
50
+ return yield* effect.pipe(Effect.provide(locations.get(requestRef(request))));
51
+ }));
52
+ }));
@@ -0,0 +1,7 @@
1
+ import { ServerAuth } from "../auth";
2
+ import { Authorization } from "@opencode/protocol/middleware/authorization";
3
+ export { Authorization } from "@opencode/protocol/middleware/authorization";
4
+ import { Effect, Layer } from "effect";
5
+ import { HttpServerRequest } from "effect/unstable/http";
6
+ export declare function authorizedRequest(request: HttpServerRequest.HttpServerRequest, config: ServerAuth.Info): Effect.Effect<boolean, never, never>;
7
+ export declare const authorizationLayer: Layer.Layer<Authorization, never, ServerAuth.Config>;