@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.
- package/dist/api.d.ts +4 -4
- package/dist/api.js +10 -9
- package/dist/auth.js +12 -17
- package/dist/chunks/auth-0ar9gf7q.js +55 -0
- package/dist/chunks/auth-0qzy9yxk.js +163 -0
- package/dist/chunks/auth-1wj0ectp.js +22 -0
- package/dist/chunks/auth-23w8t84j.js +26 -0
- package/dist/chunks/auth-34e6wr3b.js +13 -0
- package/dist/chunks/auth-3582t7j8.js +139 -0
- package/dist/chunks/auth-40g8rvw4.js +59 -0
- package/dist/chunks/auth-5p62ed52.js +36 -0
- package/dist/chunks/auth-6aya8ky6.js +39 -0
- package/dist/chunks/auth-760vtqy2.js +29 -0
- package/dist/chunks/auth-7bj707s9.js +99 -0
- package/dist/chunks/auth-7qtja2kq.js +15 -0
- package/dist/chunks/auth-8e21y1bd.js +20 -0
- package/dist/chunks/auth-8nbs1vvt.js +34 -0
- package/dist/chunks/auth-8ygjd1s8.js +40 -0
- package/dist/chunks/auth-9rqn6x4v.js +16 -0
- package/dist/chunks/auth-b4atwt7w.js +37 -0
- package/dist/chunks/auth-bgzrbsek.js +13 -0
- package/dist/chunks/auth-bkb1xk38.js +42 -0
- package/dist/chunks/auth-bkk2wfbz.js +23 -0
- package/dist/chunks/auth-byzd2nk7.js +21 -0
- package/dist/chunks/auth-cm8xmqjn.js +13 -0
- package/dist/chunks/auth-d2g7egyw.js +25 -0
- package/dist/chunks/auth-e09mshwt.js +23 -0
- package/dist/chunks/auth-erwrh284.js +156 -0
- package/dist/chunks/auth-frv6gfy7.js +33 -0
- package/dist/chunks/auth-fyjxk8m3.js +22 -0
- package/dist/chunks/auth-gsks0nj8.js +149 -0
- package/dist/chunks/auth-hezj1jdf.js +7 -0
- package/dist/chunks/auth-kv6ycdgm.js +77 -0
- package/dist/chunks/auth-m6wvgbfq.js +60 -0
- package/dist/chunks/auth-p5hgn93y.js +26 -0
- package/dist/chunks/auth-pd1pbg0v.js +17 -0
- package/dist/chunks/auth-q4bn6dy8.js +39 -0
- package/dist/chunks/auth-r4dhhtgz.js +346 -0
- package/dist/chunks/auth-rjpqaa8h.js +83 -0
- package/dist/chunks/auth-smaf4a18.js +28 -0
- package/dist/chunks/auth-t8yzmczr.js +18 -0
- package/dist/chunks/auth-tk6nqkmh.js +22 -0
- package/dist/chunks/auth-txhx6f1m.js +13 -0
- package/dist/chunks/auth-vgxn80m1.js +61 -0
- package/dist/chunks/auth-xpxp1sqb.js +22 -0
- package/dist/chunks/auth-xsrtpxyd.js +23 -0
- package/dist/chunks/auth-yc7nft6q.js +5 -0
- package/dist/chunks/auth-z3nth2hg.js +35 -0
- package/dist/chunks/auth-z6kg9h7e.js +19 -0
- package/dist/chunks/auth-zw9j8b8g.js +32 -0
- package/dist/chunks/auth-zxvt7wd3.js +25 -0
- package/dist/chunks/auth-zyv89x5n.js +30 -0
- package/dist/cors.js +10 -36
- package/dist/event-feed.js +20 -60
- package/dist/fetch.js +53 -11
- package/dist/handlers/agent.js +11 -17
- package/dist/handlers/command.js +11 -5
- package/dist/handlers/config.js +11 -16
- package/dist/handlers/credential.js +11 -14
- package/dist/handlers/debug.js +11 -11
- package/dist/handlers/event.js +12 -27
- package/dist/handlers/form.js +11 -9
- package/dist/handlers/fs.js +11 -22
- package/dist/handlers/generate.js +11 -15
- package/dist/handlers/integration.js +10 -142
- package/dist/handlers/location.js +11 -16
- package/dist/handlers/mcp.js +11 -31
- package/dist/handlers/message.js +10 -42
- package/dist/handlers/migration.js +11 -7
- package/dist/handlers/model.js +11 -14
- package/dist/handlers/permission.js +11 -57
- package/dist/handlers/persistent-pty.js +12 -135
- package/dist/handlers/plugin.js +11 -47
- package/dist/handlers/project.js +11 -9
- package/dist/handlers/provider.js +11 -20
- package/dist/handlers/pty-socket.js +6 -4
- package/dist/handlers/pty.js +13 -107
- package/dist/handlers/reference.js +11 -5
- package/dist/handlers/rpc.js +11 -15
- package/dist/handlers/server.js +12 -13
- package/dist/handlers/session-error.js +12 -26
- package/dist/handlers/session.js +10 -311
- package/dist/handlers/shell.js +11 -25
- package/dist/handlers/skill.js +11 -5
- package/dist/handlers/vcs.js +11 -19
- package/dist/handlers/websearch.js +11 -31
- package/dist/handlers/worktree.js +11 -37
- package/dist/handlers.d.ts +1 -1
- package/dist/handlers.js +46 -33
- package/dist/location.js +15 -49
- package/dist/middleware/authorization.js +11 -49
- package/dist/middleware/form-location.js +10 -25
- package/dist/middleware/schema-error.js +8 -15
- package/dist/middleware/session-location.js +10 -19
- package/dist/options.js +5 -1
- package/dist/process.js +108 -28
- package/dist/pty-environment.js +12 -9
- package/dist/request-tracing.js +6 -2
- package/dist/routes.d.ts +2 -2
- package/dist/routes.js +52 -118
- package/dist/server-info.js +12 -22
- package/dist/service-status.js +8 -11
- package/dist/workerd.js +80 -12
- package/package.json +8 -8
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
import {
|
|
2
|
+
missingSession2,
|
|
3
|
+
missingMessage2,
|
|
4
|
+
failedMessageDecode2,
|
|
5
|
+
failedSnapshot2
|
|
6
|
+
} from "./auth-smaf4a18.js";
|
|
7
|
+
import {
|
|
8
|
+
Api2
|
|
9
|
+
} from "./auth-z6kg9h7e.js";
|
|
10
|
+
|
|
11
|
+
// src/handlers/session.ts
|
|
12
|
+
import { Session } from "@opencode/core/session";
|
|
13
|
+
import { SessionStats } from "@opencode/core/session/stats";
|
|
14
|
+
import { SessionTitle } from "@opencode/core/session/title";
|
|
15
|
+
import { SessionTransfer } from "@opencode/core/session/transfer";
|
|
16
|
+
import { InstructionEntry } from "@opencode/core/session/instruction-entry";
|
|
17
|
+
import { Form } from "@opencode/core/form";
|
|
18
|
+
import { DateTime, Effect, Stream } from "effect";
|
|
19
|
+
import { HttpApiBuilder, HttpApiSchema } from "effect/unstable/httpapi";
|
|
20
|
+
import { SessionsCursor } from "@opencode/protocol/groups/session";
|
|
21
|
+
import {
|
|
22
|
+
ConflictError,
|
|
23
|
+
CommandExecutionError,
|
|
24
|
+
CommandNotFoundError,
|
|
25
|
+
FormAlreadySettledError,
|
|
26
|
+
FormInvalidAnswerError,
|
|
27
|
+
FormNotFoundError,
|
|
28
|
+
InvalidRequestError,
|
|
29
|
+
InvalidCursorError,
|
|
30
|
+
MessageNotFoundError,
|
|
31
|
+
ServiceUnavailableError,
|
|
32
|
+
SessionBusyError,
|
|
33
|
+
SkillNotFoundError
|
|
34
|
+
} from "@opencode/protocol/errors";
|
|
35
|
+
import { AbsolutePath } from "@opencode/core/schema";
|
|
36
|
+
var DefaultSessionsLimit = 50;
|
|
37
|
+
function missingForm(id) {
|
|
38
|
+
return new FormNotFoundError({ id, message: `Form not found: ${id}` });
|
|
39
|
+
}
|
|
40
|
+
var SessionHandler2 = HttpApiBuilder.group(Api2, "server.session", (handlers) => Effect.gen(function* () {
|
|
41
|
+
const session = yield* Session.Service;
|
|
42
|
+
const transfer = yield* SessionTransfer.Service;
|
|
43
|
+
const requireOwnedForm = Effect.fnUntraced(function* (sessionID, formID) {
|
|
44
|
+
const form = yield* Form.Service;
|
|
45
|
+
const info = yield* form.get(formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(formID)));
|
|
46
|
+
if (info.sessionID !== sessionID)
|
|
47
|
+
return yield* missingForm(formID);
|
|
48
|
+
return { form, info };
|
|
49
|
+
});
|
|
50
|
+
const busySession = (error) => new SessionBusyError({
|
|
51
|
+
sessionID: error.sessionID,
|
|
52
|
+
message: `Session is busy: ${error.sessionID}`
|
|
53
|
+
});
|
|
54
|
+
const pendingMutation = (effect, conflict) => effect.pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.InboxConflictError", (error) => new ConflictError({ resource: error.inboxID, message: `${conflict}: ${error.inboxID}` })), Effect.as(HttpApiSchema.NoContent.make()));
|
|
55
|
+
return handlers.handle("session.list", Effect.fn(function* (ctx) {
|
|
56
|
+
const query = ctx.query.cursor !== undefined ? yield* SessionsCursor.parse(ctx.query.cursor).pipe(Effect.mapError(() => new InvalidCursorError({ message: "Invalid cursor" }))) : ctx.query;
|
|
57
|
+
const page = yield* session.list({
|
|
58
|
+
...query,
|
|
59
|
+
limit: ctx.query.limit ?? DefaultSessionsLimit
|
|
60
|
+
});
|
|
61
|
+
const sessions = page.data;
|
|
62
|
+
const first = sessions[0];
|
|
63
|
+
const last = sessions.at(-1);
|
|
64
|
+
return {
|
|
65
|
+
data: sessions,
|
|
66
|
+
cursor: {
|
|
67
|
+
previous: first ? SessionsCursor.make({
|
|
68
|
+
...query,
|
|
69
|
+
anchor: {
|
|
70
|
+
id: first.id,
|
|
71
|
+
time: DateTime.toEpochMillis(first.time.updated),
|
|
72
|
+
direction: "previous"
|
|
73
|
+
}
|
|
74
|
+
}) : undefined,
|
|
75
|
+
next: last ? SessionsCursor.make({
|
|
76
|
+
...query,
|
|
77
|
+
anchor: {
|
|
78
|
+
id: last.id,
|
|
79
|
+
time: DateTime.toEpochMillis(last.time.updated),
|
|
80
|
+
direction: "next"
|
|
81
|
+
}
|
|
82
|
+
}) : undefined
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
})).handle("session.stats", Effect.fn(function* (ctx) {
|
|
86
|
+
const timezone = ctx.query.timezone ?? "UTC";
|
|
87
|
+
yield* Effect.try({
|
|
88
|
+
try: () => new Intl.DateTimeFormat("en-US", { timeZone: timezone }),
|
|
89
|
+
catch: () => new InvalidRequestError({ message: `Invalid time zone: ${timezone}` })
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
data: yield* SessionStats.get({
|
|
93
|
+
from: ctx.query.from,
|
|
94
|
+
to: ctx.query.to,
|
|
95
|
+
projectID: ctx.query.project,
|
|
96
|
+
timezone,
|
|
97
|
+
tools: ctx.query.tools
|
|
98
|
+
}).pipe(Effect.mapError(() => new InvalidRequestError({ message: "Stats range must end after it starts" })))
|
|
99
|
+
};
|
|
100
|
+
})).handle("session.create", Effect.fn(function* (ctx) {
|
|
101
|
+
return {
|
|
102
|
+
data: yield* session.create({
|
|
103
|
+
id: ctx.payload.id,
|
|
104
|
+
title: ctx.payload.title,
|
|
105
|
+
agent: ctx.payload.agent,
|
|
106
|
+
model: ctx.payload.model,
|
|
107
|
+
metadata: ctx.payload.metadata,
|
|
108
|
+
permissions: ctx.payload.permissions,
|
|
109
|
+
location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
|
|
110
|
+
}).pipe(Effect.orDie)
|
|
111
|
+
};
|
|
112
|
+
})).handle("session.import", Effect.fn(function* (ctx) {
|
|
113
|
+
return {
|
|
114
|
+
data: yield* transfer.import({
|
|
115
|
+
data: { info: ctx.payload.info, messages: ctx.payload.messages },
|
|
116
|
+
location: ctx.payload.location ?? { directory: AbsolutePath.make(process.cwd()) }
|
|
117
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("SessionTransfer.ImportConflictError", (error) => new ConflictError({
|
|
118
|
+
message: `Session already exists: ${error.sessionID}`,
|
|
119
|
+
resource: error.sessionID
|
|
120
|
+
})))
|
|
121
|
+
};
|
|
122
|
+
})).handle("session.export", Effect.fn(function* (ctx) {
|
|
123
|
+
return {
|
|
124
|
+
data: yield* transfer.export({ sessionID: ctx.params.sessionID, sanitize: ctx.query.sanitize }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode2))
|
|
125
|
+
};
|
|
126
|
+
})).handle("session.active", Effect.fn(function* () {
|
|
127
|
+
const active = yield* session.active;
|
|
128
|
+
return {
|
|
129
|
+
data: Object.fromEntries(Array.from(active, (sessionID) => [sessionID, { type: "running" }]))
|
|
130
|
+
};
|
|
131
|
+
})).handle("session.get", Effect.fn(function* (ctx) {
|
|
132
|
+
return {
|
|
133
|
+
data: yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2))
|
|
134
|
+
};
|
|
135
|
+
})).handle("session.view", Effect.fn(function* (ctx) {
|
|
136
|
+
yield* session.view({ sessionID: ctx.params.sessionID, idle: DateTime.toEpochMillis(ctx.payload.idle) }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
137
|
+
return HttpApiSchema.NoContent.make();
|
|
138
|
+
})).handle("session.remove", Effect.fn(function* (ctx) {
|
|
139
|
+
yield* session.remove(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
140
|
+
return HttpApiSchema.NoContent.make();
|
|
141
|
+
})).handle("session.environment", Effect.fn(function* (ctx) {
|
|
142
|
+
yield* session.environment({ sessionID: ctx.params.sessionID, variables: ctx.payload.variables }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
143
|
+
return HttpApiSchema.NoContent.make();
|
|
144
|
+
})).handle("session.fork", Effect.fn(function* (ctx) {
|
|
145
|
+
return {
|
|
146
|
+
data: yield* session.fork({ sessionID: ctx.params.sessionID, before: ctx.payload.before }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.ForkEmptyError", (error) => new InvalidRequestError({ message: error.message, kind: "empty_session" })))
|
|
147
|
+
};
|
|
148
|
+
})).handle("session.switchAgent", Effect.fn(function* (ctx) {
|
|
149
|
+
yield* session.switchAgent({ sessionID: ctx.params.sessionID, agent: ctx.payload.agent }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
150
|
+
return HttpApiSchema.NoContent.make();
|
|
151
|
+
})).handle("session.switchModel", Effect.fn(function* (ctx) {
|
|
152
|
+
yield* session.switchModel({ sessionID: ctx.params.sessionID, model: ctx.payload.model }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
153
|
+
return HttpApiSchema.NoContent.make();
|
|
154
|
+
})).handle("session.update", Effect.fn(function* (ctx) {
|
|
155
|
+
yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
156
|
+
if (ctx.payload.title !== undefined) {
|
|
157
|
+
if (ctx.payload.title) {
|
|
158
|
+
yield* session.rename({ sessionID: ctx.params.sessionID, title: ctx.payload.title }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
159
|
+
} else {
|
|
160
|
+
const title = yield* SessionTitle.Service;
|
|
161
|
+
yield* title.generate(ctx.params.sessionID);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (ctx.payload.permissions !== undefined)
|
|
165
|
+
yield* session.setPermissions({ sessionID: ctx.params.sessionID, permissions: ctx.payload.permissions }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
166
|
+
return HttpApiSchema.NoContent.make();
|
|
167
|
+
})).handle("session.move", Effect.fn(function* (ctx) {
|
|
168
|
+
yield* session.move({
|
|
169
|
+
sessionID: ctx.params.sessionID,
|
|
170
|
+
directory: ctx.payload.directory,
|
|
171
|
+
delivery: ctx.payload.delivery
|
|
172
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), 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}` }))));
|
|
173
|
+
return HttpApiSchema.NoContent.make();
|
|
174
|
+
})).handle("session.prompt", Effect.fn(function* (ctx) {
|
|
175
|
+
return {
|
|
176
|
+
data: yield* session.prompt({
|
|
177
|
+
sessionID: ctx.params.sessionID,
|
|
178
|
+
id: ctx.payload.id,
|
|
179
|
+
text: ctx.payload.text,
|
|
180
|
+
files: ctx.payload.files,
|
|
181
|
+
agents: ctx.payload.agents,
|
|
182
|
+
skills: ctx.payload.skills,
|
|
183
|
+
metadata: ctx.payload.metadata,
|
|
184
|
+
delivery: ctx.payload.delivery,
|
|
185
|
+
resume: ctx.payload.resume
|
|
186
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.PromptConflictError", (error) => Effect.fail(new ConflictError({
|
|
187
|
+
message: `Prompt message ID conflicts with an existing durable record: ${error.messageID}`,
|
|
188
|
+
resource: error.messageID
|
|
189
|
+
}))), 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" }))))
|
|
190
|
+
};
|
|
191
|
+
})).handle("session.command", Effect.fn(function* (ctx) {
|
|
192
|
+
yield* session.command({
|
|
193
|
+
sessionID: ctx.params.sessionID,
|
|
194
|
+
command: ctx.payload.name,
|
|
195
|
+
text: ctx.payload.text,
|
|
196
|
+
files: ctx.payload.files,
|
|
197
|
+
agents: ctx.payload.agents,
|
|
198
|
+
skills: ctx.payload.skills,
|
|
199
|
+
delivery: ctx.payload.delivery
|
|
200
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Command.NotFoundError", (error) => Effect.fail(new CommandNotFoundError({
|
|
201
|
+
command: error.command,
|
|
202
|
+
message: error.message
|
|
203
|
+
}))), Effect.catchTag("Command.ExecutionError", (error) => Effect.fail(new CommandExecutionError({
|
|
204
|
+
command: error.command,
|
|
205
|
+
message: error.message
|
|
206
|
+
}))));
|
|
207
|
+
return HttpApiSchema.NoContent.make();
|
|
208
|
+
})).handle("session.skill", Effect.fn(function* (ctx) {
|
|
209
|
+
yield* session.skill({
|
|
210
|
+
sessionID: ctx.params.sessionID,
|
|
211
|
+
skill: ctx.payload.id,
|
|
212
|
+
resume: ctx.payload.resume
|
|
213
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.SkillNotFoundError", (error) => Effect.fail(new SkillNotFoundError({ skill: error.skill, message: `Skill not found: ${error.skill}` }))));
|
|
214
|
+
return HttpApiSchema.NoContent.make();
|
|
215
|
+
})).handle("session.synthetic", Effect.fn(function* (ctx) {
|
|
216
|
+
const data = yield* session.synthetic({
|
|
217
|
+
id: ctx.payload.id,
|
|
218
|
+
sessionID: ctx.params.sessionID,
|
|
219
|
+
text: ctx.payload.text,
|
|
220
|
+
description: ctx.payload.description,
|
|
221
|
+
metadata: ctx.payload.metadata,
|
|
222
|
+
delivery: ctx.payload.delivery,
|
|
223
|
+
resume: ctx.payload.resume
|
|
224
|
+
}).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.SyntheticConflictError", (error) => Effect.fail(new ConflictError({
|
|
225
|
+
message: `Synthetic input ID conflicts with an existing durable record: ${error.inputID}`,
|
|
226
|
+
resource: error.inputID
|
|
227
|
+
}))));
|
|
228
|
+
return { data };
|
|
229
|
+
})).handle("session.shell", Effect.fn(function* (ctx) {
|
|
230
|
+
yield* session.shell({ sessionID: ctx.params.sessionID, id: ctx.payload.id, command: ctx.payload.command }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
231
|
+
return HttpApiSchema.NoContent.make();
|
|
232
|
+
})).handle("session.compact", Effect.fn(function* (ctx) {
|
|
233
|
+
return {
|
|
234
|
+
data: yield* session.compact({ sessionID: ctx.params.sessionID, id: ctx.payload.id, delivery: ctx.payload.delivery }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.CompactionConflictError", (error) => Effect.fail(new ConflictError({
|
|
235
|
+
message: `Compaction input ID conflicts with an existing durable record: ${error.inputID}`,
|
|
236
|
+
resource: error.inputID
|
|
237
|
+
}))))
|
|
238
|
+
};
|
|
239
|
+
})).handle("session.wait", Effect.fn(function* (ctx) {
|
|
240
|
+
yield* session.wait(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
241
|
+
return HttpApiSchema.NoContent.make();
|
|
242
|
+
})).handle("session.revert.stage", Effect.fn(function* (ctx) {
|
|
243
|
+
yield* Effect.log("session.revert.stage", {
|
|
244
|
+
sessionID: ctx.params.sessionID,
|
|
245
|
+
messageID: ctx.payload.messageID,
|
|
246
|
+
files: ctx.payload.files
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
data: yield* session.revert.stage({ ...ctx.params, ...ctx.payload }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", failedSnapshot2("stage session revert", ctx.params.sessionID)))
|
|
250
|
+
};
|
|
251
|
+
})).handle("session.revert.clear", Effect.fn(function* (ctx) {
|
|
252
|
+
yield* Effect.log("session.revert.clear", { sessionID: ctx.params.sessionID });
|
|
253
|
+
yield* session.revert.clear(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.BusyError", busySession), Effect.catchTag("Snapshot.Error", failedSnapshot2("clear session revert", ctx.params.sessionID)));
|
|
254
|
+
return HttpApiSchema.NoContent.make();
|
|
255
|
+
})).handle("session.revert.commit", Effect.fn(function* (ctx) {
|
|
256
|
+
yield* Effect.log("session.revert.commit", { sessionID: ctx.params.sessionID });
|
|
257
|
+
yield* session.revert.commit(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.BusyError", busySession));
|
|
258
|
+
return HttpApiSchema.NoContent.make();
|
|
259
|
+
})).handle("session.context", Effect.fn(function* (ctx) {
|
|
260
|
+
return {
|
|
261
|
+
data: yield* session.context(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageDecodeError", failedMessageDecode2))
|
|
262
|
+
};
|
|
263
|
+
})).handle("session.diff", Effect.fn(function* (ctx) {
|
|
264
|
+
return {
|
|
265
|
+
data: yield* session.diff({ sessionID: ctx.params.sessionID, ...ctx.query }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.MessageNotFoundError", missingMessage2), Effect.catchTag("Session.TurnRangeError", (error) => new InvalidRequestError({ message: error.message, field: error.field })), Effect.catchTag("Snapshot.Error", failedSnapshot2("diff session turn", ctx.params.sessionID)))
|
|
266
|
+
};
|
|
267
|
+
})).handle("session.inbox.list", Effect.fn(function* (ctx) {
|
|
268
|
+
return {
|
|
269
|
+
data: yield* session.inbox(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2))
|
|
270
|
+
};
|
|
271
|
+
})).handle("session.inbox.cancel", Effect.fn(function* (ctx) {
|
|
272
|
+
yield* session.cancelInbox({ sessionID: ctx.params.sessionID, inboxID: ctx.params.inboxID }).pipe(Effect.catchTag("Session.NotFoundError", missingSession2), Effect.catchTag("Session.InboxConflictError", () => Effect.void));
|
|
273
|
+
return HttpApiSchema.NoContent.make();
|
|
274
|
+
})).handle("session.inbox.update", Effect.fn(function* (ctx) {
|
|
275
|
+
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}`);
|
|
276
|
+
})).handle("session.instructions.entry.list", Effect.fn(function* (ctx) {
|
|
277
|
+
const instructions = yield* InstructionEntry.Service;
|
|
278
|
+
return { data: yield* instructions.list(ctx.params.sessionID) };
|
|
279
|
+
})).handle("session.instructions.entry.put", Effect.fn(function* (ctx) {
|
|
280
|
+
const instructions = yield* InstructionEntry.Service;
|
|
281
|
+
yield* instructions.put({ sessionID: ctx.params.sessionID, key: ctx.params.key, value: ctx.payload.value });
|
|
282
|
+
return HttpApiSchema.NoContent.make();
|
|
283
|
+
})).handle("session.instructions.entry.remove", Effect.fn(function* (ctx) {
|
|
284
|
+
const instructions = yield* InstructionEntry.Service;
|
|
285
|
+
yield* instructions.remove({ sessionID: ctx.params.sessionID, key: ctx.params.key });
|
|
286
|
+
return HttpApiSchema.NoContent.make();
|
|
287
|
+
})).handle("session.generate", Effect.fn(function* (ctx) {
|
|
288
|
+
const text = yield* session.generate({ sessionID: ctx.params.sessionID, prompt: ctx.payload.prompt }).pipe(Effect.mapError((error) => error._tag === "Session.NotFoundError" ? missingSession2(error) : new ServiceUnavailableError({ message: error.message, service: "session generation" })));
|
|
289
|
+
return { data: { text } };
|
|
290
|
+
})).handle("session.log", Effect.fn(function* (ctx) {
|
|
291
|
+
yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
292
|
+
return session.log({ sessionID: ctx.params.sessionID, after: ctx.query.after, follow: ctx.query.follow }).pipe(Stream.orDie);
|
|
293
|
+
})).handle("session.interrupt", Effect.fn(function* (ctx) {
|
|
294
|
+
return { interrupted: yield* session.interrupt(ctx.params.sessionID, { resume: ctx.query.resume }) };
|
|
295
|
+
})).handle("session.background", Effect.fn(function* (ctx) {
|
|
296
|
+
yield* session.background(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
297
|
+
return HttpApiSchema.NoContent.make();
|
|
298
|
+
})).handle("session.message", Effect.fn(function* (ctx) {
|
|
299
|
+
yield* session.get(ctx.params.sessionID).pipe(Effect.catchTag("Session.NotFoundError", missingSession2));
|
|
300
|
+
const message = yield* session.message(ctx.params);
|
|
301
|
+
if (message)
|
|
302
|
+
return { data: message };
|
|
303
|
+
return yield* new MessageNotFoundError({
|
|
304
|
+
sessionID: ctx.params.sessionID,
|
|
305
|
+
messageID: ctx.params.messageID,
|
|
306
|
+
message: `Message not found: ${ctx.params.messageID}`
|
|
307
|
+
});
|
|
308
|
+
})).handle("session.form.list", Effect.fn(function* (ctx) {
|
|
309
|
+
const form = yield* Form.Service;
|
|
310
|
+
return { data: yield* form.list({ sessionID: ctx.params.sessionID }) };
|
|
311
|
+
})).handle("session.form.create", Effect.fn(function* (ctx) {
|
|
312
|
+
const form = yield* Form.Service;
|
|
313
|
+
const created = yield* form.create({
|
|
314
|
+
id: ctx.payload.id,
|
|
315
|
+
sessionID: ctx.params.sessionID,
|
|
316
|
+
title: ctx.payload.title,
|
|
317
|
+
metadata: ctx.payload.metadata,
|
|
318
|
+
fields: ctx.payload.fields
|
|
319
|
+
}).pipe(Effect.catchTags({
|
|
320
|
+
"Form.AlreadyExistsError": (error) => new ConflictError({ resource: error.id, message: error.message }),
|
|
321
|
+
"Form.InvalidFormError": (error) => new InvalidRequestError({ message: error.message, field: "fields" })
|
|
322
|
+
}));
|
|
323
|
+
return { data: created };
|
|
324
|
+
})).handle("session.form.get", Effect.fn(function* (ctx) {
|
|
325
|
+
const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
|
|
326
|
+
const state = yield* owned.form.state(ctx.params.formID).pipe(Effect.catchTag("Form.NotFoundError", () => missingForm(ctx.params.formID)));
|
|
327
|
+
return { data: { ...owned.info, state } };
|
|
328
|
+
})).handle("session.form.reply", Effect.fn(function* (ctx) {
|
|
329
|
+
const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
|
|
330
|
+
yield* owned.form.reply({ id: ctx.params.formID, answer: ctx.payload.answer }).pipe(Effect.catchTags({
|
|
331
|
+
"Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
|
|
332
|
+
"Form.InvalidAnswerError": (error) => new FormInvalidAnswerError({ id: error.id, message: error.message }),
|
|
333
|
+
"Form.NotFoundError": () => missingForm(ctx.params.formID)
|
|
334
|
+
}));
|
|
335
|
+
return HttpApiSchema.NoContent.make();
|
|
336
|
+
})).handle("session.form.cancel", Effect.fn(function* (ctx) {
|
|
337
|
+
const owned = yield* requireOwnedForm(ctx.params.sessionID, ctx.params.formID);
|
|
338
|
+
yield* owned.form.cancel(ctx.params.formID).pipe(Effect.catchTags({
|
|
339
|
+
"Form.AlreadySettledError": (error) => new FormAlreadySettledError({ id: error.id, message: error.message }),
|
|
340
|
+
"Form.NotFoundError": () => missingForm(ctx.params.formID)
|
|
341
|
+
}));
|
|
342
|
+
return HttpApiSchema.NoContent.make();
|
|
343
|
+
}));
|
|
344
|
+
}));
|
|
345
|
+
|
|
346
|
+
export { SessionHandler2 };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./auth-9rqn6x4v.js";
|
|
4
|
+
|
|
5
|
+
// src/event-feed.ts
|
|
6
|
+
var exports_event_feed = {};
|
|
7
|
+
__export(exports_event_feed, {
|
|
8
|
+
EncodingError: () => EncodingError2,
|
|
9
|
+
EventFeed: () => exports_event_feed,
|
|
10
|
+
Service: () => Service3,
|
|
11
|
+
SubscriberCapacity: () => SubscriberCapacity2,
|
|
12
|
+
SubscriberOverflowError: () => SubscriberOverflowError2,
|
|
13
|
+
frame: () => frame2,
|
|
14
|
+
layer: () => layer3,
|
|
15
|
+
make: () => make4
|
|
16
|
+
});
|
|
17
|
+
import { Bus } from "@opencode/core/bus";
|
|
18
|
+
import { Event } from "@opencode/schema/event";
|
|
19
|
+
import { isOpenCodeEvent } from "@opencode/protocol/groups/event";
|
|
20
|
+
import { Cause, Context, Effect, Layer, Queue, Schema, Stream } from "effect";
|
|
21
|
+
var SubscriberCapacity2 = 4096;
|
|
22
|
+
|
|
23
|
+
class SubscriberOverflowError2 extends Schema.TaggedError()("EventFeed.SubscriberOverflow", { capacity: Schema.Int }) {
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class EncodingError2 extends Schema.TaggedError()("EventFeed.EncodingError", {
|
|
27
|
+
eventID: Event.ID,
|
|
28
|
+
eventType: Schema.String,
|
|
29
|
+
cause: Schema.Defect()
|
|
30
|
+
}) {
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
class Service3 extends Context.Service()("@opencode/server/EventFeed") {
|
|
34
|
+
}
|
|
35
|
+
function frame2(event) {
|
|
36
|
+
return `data: ${JSON.stringify(event)}
|
|
37
|
+
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
var make4 = Effect.fn("EventFeed.make")(function* (observe, options) {
|
|
41
|
+
const capacity = options?.capacity ?? SubscriberCapacity2;
|
|
42
|
+
const render = options?.encode ?? frame2;
|
|
43
|
+
const subscribers = new Set;
|
|
44
|
+
const fail = (error) => Effect.sync(() => {
|
|
45
|
+
const current = Array.from(subscribers);
|
|
46
|
+
subscribers.clear();
|
|
47
|
+
for (const subscriber of current)
|
|
48
|
+
Queue.failCauseUnsafe(subscriber, Cause.fail(error));
|
|
49
|
+
});
|
|
50
|
+
const publish = Effect.fnUntraced(function* (event) {
|
|
51
|
+
if (!isOpenCodeEvent(event))
|
|
52
|
+
return;
|
|
53
|
+
if (subscribers.size === 0)
|
|
54
|
+
return;
|
|
55
|
+
const encoded = yield* Effect.try({
|
|
56
|
+
try: () => render(event),
|
|
57
|
+
catch: (cause) => new EncodingError2({ eventID: event.id, eventType: event.type, cause })
|
|
58
|
+
}).pipe(Effect.catch((error) => Effect.logError("Failed to encode public event", {
|
|
59
|
+
eventID: error.eventID,
|
|
60
|
+
eventType: error.eventType,
|
|
61
|
+
cause: error.cause
|
|
62
|
+
}).pipe(Effect.andThen(fail(error)), Effect.as(undefined))));
|
|
63
|
+
if (encoded === undefined)
|
|
64
|
+
return;
|
|
65
|
+
for (const subscriber of subscribers) {
|
|
66
|
+
if (Queue.offerUnsafe(subscriber, encoded))
|
|
67
|
+
continue;
|
|
68
|
+
subscribers.delete(subscriber);
|
|
69
|
+
Queue.failCauseUnsafe(subscriber, Cause.fail(new SubscriberOverflowError2({ capacity })));
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
const unsubscribe = yield* observe(publish);
|
|
73
|
+
yield* Effect.addFinalizer(() => unsubscribe);
|
|
74
|
+
return Service3.of({
|
|
75
|
+
subscribe: Effect.acquireRelease(Queue.dropping(capacity).pipe(Effect.tap((queue) => Effect.sync(() => subscribers.add(queue)))), (queue) => Effect.sync(() => subscribers.delete(queue)).pipe(Effect.andThen(Queue.shutdown(queue)), Effect.asVoid)).pipe(Effect.map(Stream.fromQueue))
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
var layer3 = Layer.effect(Service3, Effect.gen(function* () {
|
|
79
|
+
const bus = yield* Bus.Service;
|
|
80
|
+
return yield* make4(bus.listen);
|
|
81
|
+
}));
|
|
82
|
+
|
|
83
|
+
export { SubscriberCapacity2, SubscriberOverflowError2, EncodingError2, Service3, frame2, make4, layer3, exports_event_feed };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// src/handlers/session-error.ts
|
|
2
|
+
import { MessageNotFoundError, SessionNotFoundError, UnknownError } from "@opencode/protocol/errors";
|
|
3
|
+
import { Effect } from "effect";
|
|
4
|
+
function missingSession2(error) {
|
|
5
|
+
return new SessionNotFoundError({
|
|
6
|
+
sessionID: error.sessionID,
|
|
7
|
+
message: `Session not found: ${error.sessionID}`
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
function missingMessage2(error) {
|
|
11
|
+
return new MessageNotFoundError({
|
|
12
|
+
sessionID: error.sessionID,
|
|
13
|
+
messageID: error.messageID,
|
|
14
|
+
message: `Message not found: ${error.messageID}`
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function failedMessageDecode2(error) {
|
|
18
|
+
const ref = `err_${crypto.randomUUID().slice(0, 8)}`;
|
|
19
|
+
return Effect.logError("failed to decode session message").pipe(Effect.annotateLogs({ ref, sessionID: error.sessionID, messageID: error.messageID }), Effect.andThen(Effect.fail(new UnknownError({ message: "Unexpected server error. Check server logs for details.", ref }))));
|
|
20
|
+
}
|
|
21
|
+
function failedSnapshot2(operation, sessionID) {
|
|
22
|
+
return (error) => {
|
|
23
|
+
const ref = `err_${crypto.randomUUID().slice(0, 8)}`;
|
|
24
|
+
return Effect.logError(`failed to ${operation}`, { cause: error }).pipe(Effect.annotateLogs({ ref, sessionID }), Effect.andThen(Effect.fail(new UnknownError({ message: "Unexpected server error. Check server logs for details.", ref }))));
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export { missingSession2, missingMessage2, failedMessageDecode2, failedSnapshot2 };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// src/middleware/schema-error.ts
|
|
2
|
+
import { Effect } from "effect";
|
|
3
|
+
import { HttpApiMiddleware } from "effect/unstable/httpapi";
|
|
4
|
+
import { InvalidRequestError } from "@opencode/protocol/errors";
|
|
5
|
+
import { SchemaErrorMiddleware } from "@opencode/protocol/middleware/schema-error";
|
|
6
|
+
import { SchemaErrorMiddleware as SchemaErrorMiddleware2 } from "@opencode/protocol/middleware/schema-error";
|
|
7
|
+
var REASON_LIMIT = 1024;
|
|
8
|
+
function truncateReason(reason) {
|
|
9
|
+
if (reason.length <= REASON_LIMIT)
|
|
10
|
+
return reason;
|
|
11
|
+
return reason.slice(0, REASON_LIMIT) + `... (${reason.length - REASON_LIMIT} more chars)`;
|
|
12
|
+
}
|
|
13
|
+
var schemaErrorLayer2 = HttpApiMiddleware.layerSchemaErrorTransform(SchemaErrorMiddleware, (error) => {
|
|
14
|
+
const reason = truncateReason(error.cause.message);
|
|
15
|
+
return Effect.logWarning("schema rejection").pipe(Effect.annotateLogs({ kind: error.kind, reason }), Effect.andThen(Effect.fail(new InvalidRequestError({ message: reason, kind: error.kind }))));
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
export { schemaErrorLayer2, SchemaErrorMiddleware2 };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Api2
|
|
3
|
+
} from "./auth-z6kg9h7e.js";
|
|
4
|
+
|
|
5
|
+
// src/handlers/location.ts
|
|
6
|
+
import { Location } from "@opencode/core/location";
|
|
7
|
+
import { LocationServiceMap } from "@opencode/core/location-service-map";
|
|
8
|
+
import { ServiceUnavailableError } from "@opencode/protocol/errors";
|
|
9
|
+
import { Cause, Effect } from "effect";
|
|
10
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
11
|
+
var LocationHandler2 = HttpApiBuilder.group(Api2, "server.location", (handlers) => Effect.gen(function* () {
|
|
12
|
+
const locations = yield* LocationServiceMap.Service;
|
|
13
|
+
return handlers.handle("location.get", Effect.fn(function* () {
|
|
14
|
+
const location = yield* Location.Service;
|
|
15
|
+
return new Location.Info({
|
|
16
|
+
directory: location.directory,
|
|
17
|
+
project: location.project
|
|
18
|
+
});
|
|
19
|
+
})).handle("location.reload", () => LocationServiceMap.reload().pipe(Effect.provideService(LocationServiceMap.Service, locations), Effect.catchCause((cause) => Cause.hasInterruptsOnly(cause) ? Effect.failCause(cause) : Effect.fail(new ServiceUnavailableError({ message: Cause.pretty(cause), service: "location" })))));
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
export { LocationHandler2 };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Api2
|
|
3
|
+
} from "./auth-z6kg9h7e.js";
|
|
4
|
+
|
|
5
|
+
// src/handlers/migration.ts
|
|
6
|
+
import { V1Migration } from "@opencode/core/database/v1-migration";
|
|
7
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
var MigrationHandler2 = HttpApiBuilder.group(Api2, "server.migration", (handlers) => handlers.handle("migration.v1.status", Effect.fn(function* () {
|
|
10
|
+
return yield* V1Migration.status();
|
|
11
|
+
})));
|
|
12
|
+
|
|
13
|
+
export { MigrationHandler2 };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
response2
|
|
3
|
+
} from "./auth-0ar9gf7q.js";
|
|
4
|
+
import {
|
|
5
|
+
Api2
|
|
6
|
+
} from "./auth-z6kg9h7e.js";
|
|
7
|
+
|
|
8
|
+
// src/handlers/plugin.ts
|
|
9
|
+
import { Plugin } from "@opencode/core/plugin";
|
|
10
|
+
import { PluginUpdate } from "@opencode/core/plugin/update";
|
|
11
|
+
import { InvalidRequestError, ServiceUnavailableError } from "@opencode/protocol/errors";
|
|
12
|
+
import { Cause, Effect, Exit } from "effect";
|
|
13
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
14
|
+
var PluginHandler2 = HttpApiBuilder.group(Api2, "server.plugin", (handlers) => handlers.handle("plugin.list", () => Effect.gen(function* () {
|
|
15
|
+
return yield* response2(Plugin.Service.use((plugin) => plugin.list()));
|
|
16
|
+
})).handle("plugin.check", (ctx) => Effect.gen(function* () {
|
|
17
|
+
const plugins = yield* Plugin.Service;
|
|
18
|
+
const inventory = yield* plugins.list();
|
|
19
|
+
const targets = [
|
|
20
|
+
...new Set(inventory.flatMap((plugin) => plugin.source.type === "package" ? [plugin.source.target] : []))
|
|
21
|
+
].filter((target) => ctx.payload.target === undefined || target === ctx.payload.target);
|
|
22
|
+
if (ctx.payload.target !== undefined && !targets.length)
|
|
23
|
+
return yield* new InvalidRequestError({
|
|
24
|
+
message: `Plugin package is not in the current server inventory: ${ctx.payload.target}`,
|
|
25
|
+
field: "target"
|
|
26
|
+
});
|
|
27
|
+
const updates = yield* PluginUpdate.Service;
|
|
28
|
+
const outdated = new Map(yield* Effect.forEach(targets, (target) => updates.check(target, { refresh: true }).pipe(Effect.map((value) => [target, value])), { concurrency: "unbounded" }));
|
|
29
|
+
return yield* response2(Effect.succeed(inventory.map((plugin) => {
|
|
30
|
+
if (plugin.source.type !== "package" || !outdated.has(plugin.source.target))
|
|
31
|
+
return plugin;
|
|
32
|
+
return {
|
|
33
|
+
...plugin,
|
|
34
|
+
source: {
|
|
35
|
+
type: "package",
|
|
36
|
+
target: plugin.source.target,
|
|
37
|
+
...plugin.source.version ? { version: plugin.source.version } : {},
|
|
38
|
+
...outdated.get(plugin.source.target) ? { outdated: true } : {},
|
|
39
|
+
...plugin.source.updating ? { updating: true } : {}
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
})));
|
|
43
|
+
})).handle("plugin.update", (ctx) => Effect.gen(function* () {
|
|
44
|
+
const plugins = yield* Plugin.Service;
|
|
45
|
+
const inventory = new Set((yield* plugins.list()).flatMap((plugin) => plugin.source.type === "package" ? [plugin.source.target] : []));
|
|
46
|
+
const unknown = ctx.payload.targets.filter((target) => !inventory.has(target));
|
|
47
|
+
if (unknown.length)
|
|
48
|
+
return yield* new InvalidRequestError({
|
|
49
|
+
message: `Plugin packages are not in the current server inventory: ${unknown.join(", ")}`,
|
|
50
|
+
field: "targets"
|
|
51
|
+
});
|
|
52
|
+
const updates = yield* PluginUpdate.Service;
|
|
53
|
+
const failures = yield* Effect.forEach(ctx.payload.targets, (target) => updates.update(target).pipe(Effect.exit, Effect.map((exit) => Exit.isFailure(exit) ? [`${target}: ${Cause.pretty(exit.cause)}`] : [])), { concurrency: "unbounded" }).pipe(Effect.map((results) => results.flat()));
|
|
54
|
+
if (failures.length)
|
|
55
|
+
return yield* new ServiceUnavailableError({
|
|
56
|
+
message: `Failed to update plugin packages: ${failures.join("; ")}`,
|
|
57
|
+
service: "plugin"
|
|
58
|
+
});
|
|
59
|
+
})));
|
|
60
|
+
|
|
61
|
+
export { PluginHandler2 };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Api2
|
|
3
|
+
} from "./auth-z6kg9h7e.js";
|
|
4
|
+
|
|
5
|
+
// src/handlers/config.ts
|
|
6
|
+
import { Config } from "@opencode/core/config";
|
|
7
|
+
import { ShellSelect } from "@opencode/core/shell/select";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
import { HttpApiBuilder } from "effect/unstable/httpapi";
|
|
10
|
+
var ConfigHandler2 = HttpApiBuilder.group(Api2, "server.config", (handlers) => handlers.handle("config.get", () => Config.Service.use((config) => config.entries())).handle("config.update", Effect.fn(function* (ctx) {
|
|
11
|
+
const config = yield* Config.Service;
|
|
12
|
+
if (!config.update)
|
|
13
|
+
return yield* Effect.die(new Error("Config updates are unavailable"));
|
|
14
|
+
return yield* config.update(ctx.payload).pipe(Effect.orDie);
|
|
15
|
+
})).handle("config.shells", Effect.fn(function* () {
|
|
16
|
+
const shell = yield* ShellSelect.Service;
|
|
17
|
+
if (!shell.list)
|
|
18
|
+
return yield* Effect.die(new Error("Shell discovery is unavailable"));
|
|
19
|
+
return yield* shell.list();
|
|
20
|
+
})));
|
|
21
|
+
|
|
22
|
+
export { ConfigHandler2 };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export
|
|
3
|
+
} from "./auth-9rqn6x4v.js";
|
|
4
|
+
|
|
5
|
+
// src/service-status.ts
|
|
6
|
+
var exports_service_status = {};
|
|
7
|
+
__export(exports_service_status, {
|
|
8
|
+
Status: () => exports_service_status,
|
|
9
|
+
make: () => make2
|
|
10
|
+
});
|
|
11
|
+
import { Effect, Ref } from "effect";
|
|
12
|
+
var make2 = Effect.fnUntraced(function* (options = {}) {
|
|
13
|
+
const current = yield* Ref.make(options.initial ?? { type: "starting" });
|
|
14
|
+
const beginStopping = Ref.update(current, (status) => status.type === "stopping" ? status : { type: "stopping" });
|
|
15
|
+
return {
|
|
16
|
+
current: Ref.get(current),
|
|
17
|
+
ready: Ref.update(current, (status) => status.type === "starting" ? { type: "ready" } : status),
|
|
18
|
+
fail: Ref.update(current, (status) => status.type === "starting" ? { type: "failed" } : status),
|
|
19
|
+
beginStopping
|
|
20
|
+
};
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
export { make2, exports_service_status };
|