@opencode/protocol 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 (78) hide show
  1. package/dist/api.d.ts +56 -0
  2. package/dist/api.js +79 -0
  3. package/dist/client.d.ts +64 -0
  4. package/dist/client.js +50 -0
  5. package/dist/errors.d.ts +153 -0
  6. package/dist/errors.js +122 -0
  7. package/dist/groups/agent.d.ts +194 -0
  8. package/dist/groups/agent.js +30 -0
  9. package/dist/groups/command.d.ts +15 -0
  10. package/dist/groups/command.js +20 -0
  11. package/dist/groups/config.d.ts +9 -0
  12. package/dist/groups/config.js +16 -0
  13. package/dist/groups/credential.d.ts +32 -0
  14. package/dist/groups/credential.js +40 -0
  15. package/dist/groups/debug.d.ts +17 -0
  16. package/dist/groups/debug.js +23 -0
  17. package/dist/groups/event.d.ts +14643 -0
  18. package/dist/groups/event.js +51 -0
  19. package/dist/groups/form.d.ts +781 -0
  20. package/dist/groups/form.js +95 -0
  21. package/dist/groups/fs.d.ts +35 -0
  22. package/dist/groups/fs.js +53 -0
  23. package/dist/groups/generate.d.ts +33 -0
  24. package/dist/groups/generate.js +23 -0
  25. package/dist/groups/health.d.ts +15 -0
  26. package/dist/groups/health.js +20 -0
  27. package/dist/groups/integration.d.ts +1154 -0
  28. package/dist/groups/integration.js +149 -0
  29. package/dist/groups/location.d.ts +16 -0
  30. package/dist/groups/location.js +34 -0
  31. package/dist/groups/mcp.d.ts +82 -0
  32. package/dist/groups/mcp.js +78 -0
  33. package/dist/groups/message.d.ts +734 -0
  34. package/dist/groups/message.js +51 -0
  35. package/dist/groups/migration.d.ts +28 -0
  36. package/dist/groups/migration.js +21 -0
  37. package/dist/groups/model.d.ts +314 -0
  38. package/dist/groups/model.js +33 -0
  39. package/dist/groups/permission.d.ts +167 -0
  40. package/dist/groups/permission.js +109 -0
  41. package/dist/groups/persistent-pty.d.ts +249 -0
  42. package/dist/groups/persistent-pty.js +91 -0
  43. package/dist/groups/plugin.d.ts +91 -0
  44. package/dist/groups/plugin.js +55 -0
  45. package/dist/groups/project.d.ts +90 -0
  46. package/dist/groups/project.js +39 -0
  47. package/dist/groups/provider.d.ts +156 -0
  48. package/dist/groups/provider.js +34 -0
  49. package/dist/groups/pty.d.ts +147 -0
  50. package/dist/groups/pty.js +113 -0
  51. package/dist/groups/reference.d.ts +29 -0
  52. package/dist/groups/reference.js +20 -0
  53. package/dist/groups/rpc.d.ts +22 -0
  54. package/dist/groups/rpc.js +24 -0
  55. package/dist/groups/server.d.ts +5 -0
  56. package/dist/groups/server.js +11 -0
  57. package/dist/groups/session.d.ts +10794 -0
  58. package/dist/groups/session.js +549 -0
  59. package/dist/groups/shell.d.ts +152 -0
  60. package/dist/groups/shell.js +82 -0
  61. package/dist/groups/skill.d.ts +20 -0
  62. package/dist/groups/skill.js +20 -0
  63. package/dist/groups/vcs.d.ts +70 -0
  64. package/dist/groups/vcs.js +76 -0
  65. package/dist/groups/websearch.d.ts +28 -0
  66. package/dist/groups/websearch.js +34 -0
  67. package/dist/groups/workspace.d.ts +22 -0
  68. package/dist/groups/workspace.js +27 -0
  69. package/dist/groups/worktree.d.ts +47 -0
  70. package/dist/groups/worktree.js +61 -0
  71. package/dist/middleware/authorization.d.ts +13 -0
  72. package/dist/middleware/authorization.js +6 -0
  73. package/dist/middleware/schema-error.d.ts +13 -0
  74. package/dist/middleware/schema-error.js +4 -0
  75. package/dist/simulation.d.ts +1801 -0
  76. package/dist/simulation.js +487 -0
  77. package/package.json +36 -6
  78. package/README.md +0 -5
@@ -0,0 +1,109 @@
1
+ import { Agent } from "@opencode/schema/agent";
2
+ import { Location } from "@opencode/schema/location";
3
+ import { Permission } from "@opencode/schema/permission";
4
+ import { PermissionSaved } from "@opencode/schema/permission-saved";
5
+ import { Project } from "@opencode/schema/project";
6
+ import { Session } from "@opencode/schema/session";
7
+ import { Context, Schema } from "effect";
8
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
9
+ import { PermissionNotFoundError, SessionNotFoundError } from "../errors.js";
10
+ import { LocationQuery, locationQueryOpenApi } from "./location.js";
11
+ export const makePermissionGroup = (locationMiddleware, sessionLocationMiddleware) => HttpApiGroup.make("server.permission")
12
+ .add(HttpApiEndpoint.get("permission.request.list", "/api/permission/request", {
13
+ query: LocationQuery,
14
+ success: Location.response(Schema.Array(Permission.Request)),
15
+ })
16
+ .annotateMerge(locationQueryOpenApi)
17
+ .annotateMerge(OpenApi.annotations({
18
+ identifier: "v2.permission.request.list",
19
+ summary: "List pending permission requests",
20
+ description: "Retrieve pending permission requests for a location.",
21
+ })))
22
+ .add(HttpApiEndpoint.get("permission.saved.list", "/api/permission/saved", {
23
+ query: Schema.Struct({ projectID: Project.ID.pipe(Schema.optional) }),
24
+ success: Schema.Struct({ data: Schema.Array(PermissionSaved.Info) }),
25
+ }).annotateMerge(OpenApi.annotations({
26
+ identifier: "v2.permission.saved.list",
27
+ summary: "List saved permissions",
28
+ description: "Retrieve saved permissions, optionally filtered by project.",
29
+ })))
30
+ .add(HttpApiEndpoint.delete("permission.saved.remove", "/api/permission/saved/:id", {
31
+ params: { id: PermissionSaved.ID },
32
+ success: HttpApiSchema.NoContent,
33
+ }).annotateMerge(OpenApi.annotations({
34
+ identifier: "v2.permission.saved.remove",
35
+ summary: "Remove saved permission",
36
+ description: "Remove a saved permission by ID.",
37
+ })))
38
+ // Effect applies group middleware only to endpoints already added; session endpoints use session placement below.
39
+ .middleware(locationMiddleware)
40
+ .add(HttpApiEndpoint.post("session.permission.create", "/api/session/:sessionID/permission", {
41
+ params: { sessionID: Session.ID },
42
+ payload: Schema.Struct({
43
+ id: Permission.ID.pipe(Schema.optional),
44
+ action: Permission.Request.fields.action,
45
+ resources: Permission.Request.fields.resources,
46
+ save: Permission.Request.fields.save,
47
+ metadata: Permission.Request.fields.metadata,
48
+ source: Permission.Request.fields.source,
49
+ agent: Agent.ID.pipe(Schema.optional),
50
+ }),
51
+ success: Schema.Struct({
52
+ data: Schema.Struct({ id: Permission.ID, effect: Permission.Effect }),
53
+ }),
54
+ error: SessionNotFoundError,
55
+ })
56
+ .middleware(sessionLocationMiddleware)
57
+ .annotateMerge(OpenApi.annotations({
58
+ identifier: "v2.session.permission.create",
59
+ summary: "Create permission request",
60
+ description: "Evaluate and, when approval is required, create a permission request for a session.",
61
+ })))
62
+ .add(HttpApiEndpoint.get("session.permission.list", "/api/session/:sessionID/permission", {
63
+ params: { sessionID: Session.ID },
64
+ success: Schema.Struct({ data: Schema.Array(Permission.Request) }),
65
+ error: SessionNotFoundError,
66
+ }).annotateMerge(OpenApi.annotations({
67
+ identifier: "v2.session.permission.list",
68
+ summary: "List session permission requests",
69
+ description: "Retrieve pending permission requests owned by a session.",
70
+ })))
71
+ .add(HttpApiEndpoint.get("session.permission.get", "/api/session/:sessionID/permission/:requestID", {
72
+ params: { sessionID: Session.ID, requestID: Permission.ID },
73
+ success: Schema.Struct({ data: Permission.Request }),
74
+ error: [SessionNotFoundError, PermissionNotFoundError],
75
+ })
76
+ .middleware(sessionLocationMiddleware)
77
+ .annotateMerge(OpenApi.annotations({
78
+ identifier: "v2.session.permission.get",
79
+ summary: "Get permission request",
80
+ description: "Retrieve a pending permission request owned by a session.",
81
+ })))
82
+ .add(HttpApiEndpoint.post("session.permission.reply", "/api/session/:sessionID/permission/:requestID/reply", {
83
+ params: { sessionID: Session.ID, requestID: Permission.ID },
84
+ payload: Schema.Struct({
85
+ reply: Permission.Reply,
86
+ message: Schema.String.pipe(Schema.optional),
87
+ }),
88
+ success: HttpApiSchema.NoContent,
89
+ error: [SessionNotFoundError, PermissionNotFoundError],
90
+ })
91
+ .middleware(sessionLocationMiddleware)
92
+ .annotateMerge(OpenApi.annotations({
93
+ identifier: "v2.session.permission.reply",
94
+ summary: "Reply to pending permission request",
95
+ description: "Respond to a pending permission request owned by a session.",
96
+ })))
97
+ .add(HttpApiEndpoint.put("session.permission.rules", "/api/session/:sessionID/permission/rules", {
98
+ params: { sessionID: Session.ID },
99
+ payload: Schema.Struct({ permissions: Permission.Ruleset }),
100
+ success: HttpApiSchema.NoContent,
101
+ error: SessionNotFoundError,
102
+ })
103
+ .middleware(sessionLocationMiddleware)
104
+ .annotateMerge(OpenApi.annotations({
105
+ identifier: "v2.session.permission.rules",
106
+ summary: "Replace session permission rules",
107
+ description: "Replace the session-scoped permission rules. Rules are evaluated after the agent's rules, and the last matching rule wins.",
108
+ })))
109
+ .annotateMerge(OpenApi.annotations({ title: "permission", description: "Experimental permission routes." }));
@@ -0,0 +1,249 @@
1
+ import { Schema } from "effect";
2
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi";
3
+ import { ForbiddenError, InvalidRequestError, PtyNotFoundError, ServiceUnavailableError } from "../errors.js";
4
+ import { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE } from "./pty.js";
5
+ export { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE };
6
+ export declare function hasPersistentPtyConnectTicketURL(url: URL): boolean;
7
+ export declare const PersistentPtyGroup: HttpApiGroup.HttpApiGroup<"server.experimental", HttpApiEndpoint.HttpApiEndpoint<"persistentPty.read", "GET", "/api/experimental/session/:sessionID/terminal/read", Schema.toCodecStringTree<Schema.Struct<{
8
+ sessionID: Schema.brand<Schema.String, "SessionID"> & {
9
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
10
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
11
+ };
12
+ }>>, Schema.toCodecStringTree<Schema.Struct<{
13
+ lines: Schema.optional<Schema.compose<Schema.Int, Schema.NumberFromString>>;
14
+ }>>, never, never, Schema.toCodecJson<Schema.Struct<{
15
+ readonly data: Schema.NullOr<Schema.Struct<{
16
+ readonly ptyID: Schema.brand<Schema.String, "PtyID"> & {
17
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
18
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
19
+ };
20
+ readonly title: Schema.String;
21
+ readonly cwd: Schema.String;
22
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
23
+ readonly screen: Schema.Struct<{
24
+ readonly text: Schema.String;
25
+ readonly cols: Schema.Int;
26
+ readonly rows: Schema.Int;
27
+ readonly cursor: Schema.Struct<{
28
+ readonly x: Schema.Int;
29
+ readonly y: Schema.Int;
30
+ }>;
31
+ }>;
32
+ }>>;
33
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.list", "GET", "/api/experimental/session/:sessionID/terminal", Schema.toCodecStringTree<Schema.Struct<{
34
+ sessionID: Schema.brand<Schema.String, "SessionID"> & {
35
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
36
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
37
+ };
38
+ }>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
39
+ readonly data: Schema.$Array<Schema.Struct<{
40
+ readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
41
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
42
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
43
+ };
44
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
45
+ readonly size: Schema.Struct<{
46
+ readonly cols: Schema.Int;
47
+ readonly rows: Schema.Int;
48
+ }>;
49
+ readonly output: Schema.Struct<{
50
+ readonly head: Schema.Int;
51
+ readonly tail: Schema.Int;
52
+ }>;
53
+ readonly id: Schema.brand<Schema.String, "PtyID"> & {
54
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
55
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
56
+ };
57
+ readonly title: Schema.String;
58
+ readonly command: Schema.String;
59
+ readonly args: Schema.$Array<Schema.String>;
60
+ readonly cwd: Schema.String;
61
+ readonly status: Schema.Literals<readonly ["running", "exited"]>;
62
+ readonly pid: Schema.Int;
63
+ readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
64
+ }>>;
65
+ }>>, Schema.toCodecJson<typeof InvalidRequestError | typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.create", "POST", "/api/experimental/session/:sessionID/terminal", Schema.toCodecStringTree<Schema.Struct<{
66
+ sessionID: Schema.brand<Schema.String, "SessionID"> & {
67
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
68
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
69
+ };
70
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
71
+ readonly command: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
72
+ readonly args: Schema.$Array<Schema.String>;
73
+ readonly cwd: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
74
+ readonly title: Schema.String;
75
+ readonly env: Schema.$Record<Schema.String, Schema.String>;
76
+ readonly size: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Struct<{
77
+ readonly cols: Schema.Int;
78
+ readonly rows: Schema.Int;
79
+ }>>>, Schema.optionalKey<Schema.Struct<{
80
+ readonly cols: Schema.Int;
81
+ readonly rows: Schema.Int;
82
+ }>>, never, never>;
83
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
84
+ readonly data: Schema.Struct<{
85
+ readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
86
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
87
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
88
+ };
89
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
90
+ readonly size: Schema.Struct<{
91
+ readonly cols: Schema.Int;
92
+ readonly rows: Schema.Int;
93
+ }>;
94
+ readonly output: Schema.Struct<{
95
+ readonly head: Schema.Int;
96
+ readonly tail: Schema.Int;
97
+ }>;
98
+ readonly id: Schema.brand<Schema.String, "PtyID"> & {
99
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
100
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
101
+ };
102
+ readonly title: Schema.String;
103
+ readonly command: Schema.String;
104
+ readonly args: Schema.$Array<Schema.String>;
105
+ readonly cwd: Schema.String;
106
+ readonly status: Schema.Literals<readonly ["running", "exited"]>;
107
+ readonly pid: Schema.Int;
108
+ readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
109
+ }>;
110
+ }>>, Schema.toCodecJson<typeof InvalidRequestError | typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.shutdown", "POST", "/api/experimental/persistent-pty/shutdown", never, never, never, never, Schema.toCodecJson<HttpApiSchema.NoContent>, Schema.toCodecJson<typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.handoff", "POST", "/api/experimental/persistent-pty/handoff", never, never, never, never, Schema.toCodecJson<Schema.Struct<{
111
+ readonly handoff: Schema.NullOr<Schema.Struct<{
112
+ readonly directory: Schema.String;
113
+ readonly instanceID: Schema.String;
114
+ readonly ticket: Schema.String;
115
+ readonly expiresAt: Schema.Number;
116
+ }>>;
117
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.get", "GET", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
118
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
119
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
120
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
121
+ };
122
+ }>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
123
+ readonly data: Schema.Struct<{
124
+ readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
125
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
126
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
127
+ };
128
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
129
+ readonly size: Schema.Struct<{
130
+ readonly cols: Schema.Int;
131
+ readonly rows: Schema.Int;
132
+ }>;
133
+ readonly output: Schema.Struct<{
134
+ readonly head: Schema.Int;
135
+ readonly tail: Schema.Int;
136
+ }>;
137
+ readonly id: Schema.brand<Schema.String, "PtyID"> & {
138
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
139
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
140
+ };
141
+ readonly title: Schema.String;
142
+ readonly command: Schema.String;
143
+ readonly args: Schema.$Array<Schema.String>;
144
+ readonly cwd: Schema.String;
145
+ readonly status: Schema.Literals<readonly ["running", "exited"]>;
146
+ readonly pid: Schema.Int;
147
+ readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
148
+ }>;
149
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.update", "PUT", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
150
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
151
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
152
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
153
+ };
154
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
155
+ readonly attachmentID: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
156
+ readonly size: Schema.Struct<{
157
+ readonly cols: Schema.Int;
158
+ readonly rows: Schema.Int;
159
+ }>;
160
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
161
+ readonly data: Schema.Struct<{
162
+ readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
163
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
164
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
165
+ };
166
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
167
+ readonly size: Schema.Struct<{
168
+ readonly cols: Schema.Int;
169
+ readonly rows: Schema.Int;
170
+ }>;
171
+ readonly output: Schema.Struct<{
172
+ readonly head: Schema.Int;
173
+ readonly tail: Schema.Int;
174
+ }>;
175
+ readonly id: Schema.brand<Schema.String, "PtyID"> & {
176
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
177
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
178
+ };
179
+ readonly title: Schema.String;
180
+ readonly command: Schema.String;
181
+ readonly args: Schema.$Array<Schema.String>;
182
+ readonly cwd: Schema.String;
183
+ readonly status: Schema.Literals<readonly ["running", "exited"]>;
184
+ readonly pid: Schema.Int;
185
+ readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
186
+ }>;
187
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.snapshot", "GET", "/api/experimental/persistent-pty/:ptyID/snapshot", Schema.toCodecStringTree<Schema.Struct<{
188
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
189
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
190
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
191
+ };
192
+ }>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
193
+ readonly data: Schema.Struct<{
194
+ readonly info: Schema.Struct<{
195
+ readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
196
+ create: () => string & import("effect/Brand").Brand<"SessionID">;
197
+ descending: (id?: string) => string & import("effect/Brand").Brand<"SessionID">;
198
+ };
199
+ readonly foregroundProcess: Schema.NullOr<Schema.String>;
200
+ readonly size: Schema.Struct<{
201
+ readonly cols: Schema.Int;
202
+ readonly rows: Schema.Int;
203
+ }>;
204
+ readonly output: Schema.Struct<{
205
+ readonly head: Schema.Int;
206
+ readonly tail: Schema.Int;
207
+ }>;
208
+ readonly id: Schema.brand<Schema.String, "PtyID"> & {
209
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
210
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
211
+ };
212
+ readonly title: Schema.String;
213
+ readonly command: Schema.String;
214
+ readonly args: Schema.$Array<Schema.String>;
215
+ readonly cwd: Schema.String;
216
+ readonly status: Schema.Literals<readonly ["running", "exited"]>;
217
+ readonly pid: Schema.Int;
218
+ readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
219
+ }>;
220
+ readonly text: Schema.String;
221
+ readonly checkpoint: Schema.Uint8Array;
222
+ readonly cursor: Schema.Struct<{
223
+ readonly x: Schema.Int;
224
+ readonly y: Schema.Int;
225
+ }>;
226
+ }>;
227
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.remove", "DELETE", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
228
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
229
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
230
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
231
+ };
232
+ }>>, never, never, never, Schema.toCodecJson<HttpApiSchema.NoContent>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.connectToken", "POST", "/api/experimental/persistent-pty/:ptyID/connect-token", Schema.toCodecStringTree<Schema.Struct<{
233
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
234
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
235
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
236
+ };
237
+ }>>, never, never, Schema.toCodecStringTree<Schema.Struct<{
238
+ readonly "x-opencode-ticket": Schema.optional<Schema.String>;
239
+ }>>, Schema.toCodecJson<Schema.Struct<{
240
+ readonly data: Schema.Struct<{
241
+ readonly ticket: Schema.String;
242
+ readonly expires_in: Schema.Int;
243
+ }>;
244
+ }>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof ForbiddenError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.connect", "GET", "/api/experimental/persistent-pty/:ptyID/connect", Schema.toCodecStringTree<Schema.Struct<{
245
+ ptyID: Schema.brand<Schema.String, "PtyID"> & {
246
+ create: () => string & import("effect/Brand").Brand<"PtyID">;
247
+ ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
248
+ };
249
+ }>>, never, never, never, Schema.toCodecJson<Schema.Boolean>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof ForbiddenError | typeof PtyNotFoundError>, never, never>, false>;
@@ -0,0 +1,91 @@
1
+ import { PersistentPty } from "@opencode/schema/persistent-pty";
2
+ import { Pty } from "@opencode/schema/pty";
3
+ import { PtyTicket } from "@opencode/schema/pty-ticket";
4
+ import { Session } from "@opencode/schema/session";
5
+ import { Schema } from "effect";
6
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
7
+ import { ForbiddenError, InvalidRequestError, PtyNotFoundError, ServiceUnavailableError } from "../errors.js";
8
+ import { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE } from "./pty.js";
9
+ export { PTY_CONNECT_TICKET_QUERY, PTY_CONNECT_TOKEN_HEADER, PTY_CONNECT_TOKEN_HEADER_VALUE };
10
+ const CONNECT_PATH = /^\/api\/experimental\/persistent-pty\/[^/]+\/connect$/;
11
+ export function hasPersistentPtyConnectTicketURL(url) {
12
+ return CONNECT_PATH.test(url.pathname) && !!url.searchParams.get(PTY_CONNECT_TICKET_QUERY);
13
+ }
14
+ const errors = [InvalidRequestError, ServiceUnavailableError];
15
+ const terminalErrors = [PtyNotFoundError, ServiceUnavailableError];
16
+ export const PersistentPtyGroup = HttpApiGroup.make("server.experimental")
17
+ .add(HttpApiEndpoint.get("persistentPty.read", "/api/experimental/session/:sessionID/terminal/read", {
18
+ params: { sessionID: Session.ID },
19
+ query: {
20
+ lines: Schema.NumberFromString.pipe(Schema.decodeTo(PersistentPty.ReadLines), Schema.optional),
21
+ },
22
+ success: Schema.Struct({ data: Schema.NullOr(PersistentPty.ReadResult) }),
23
+ error: [ServiceUnavailableError],
24
+ }).annotateMerge(OpenApi.annotations({
25
+ summary: "Read the session's most recently controlled terminal",
26
+ description: "Read the last physical rows without changing selection or taking control. Omitted lines uses the live terminal height; larger counts include retained history. Blank rows are preserved. Screen dimensions and cursor remain relative to the live screen. Returns null when no current terminal exists. Selection is server-local and resets on restart. Experimental: may change without compatibility guarantees.",
27
+ })))
28
+ .add(HttpApiEndpoint.get("persistentPty.list", "/api/experimental/session/:sessionID/terminal", {
29
+ params: { sessionID: Session.ID },
30
+ success: Schema.Struct({ data: Schema.Array(PersistentPty.Info) }),
31
+ error: errors,
32
+ }))
33
+ .add(HttpApiEndpoint.post("persistentPty.create", "/api/experimental/session/:sessionID/terminal", {
34
+ params: { sessionID: Session.ID },
35
+ payload: PersistentPty.CreateInput,
36
+ success: Schema.Struct({ data: PersistentPty.Info }),
37
+ error: errors,
38
+ }))
39
+ .add(HttpApiEndpoint.post("persistentPty.shutdown", "/api/experimental/persistent-pty/shutdown", {
40
+ success: HttpApiSchema.NoContent,
41
+ error: [ServiceUnavailableError],
42
+ }))
43
+ .add(HttpApiEndpoint.post("persistentPty.handoff", "/api/experimental/persistent-pty/handoff", {
44
+ success: Schema.Struct({ handoff: Schema.NullOr(PersistentPty.Handoff) }),
45
+ error: [ServiceUnavailableError],
46
+ }))
47
+ .add(HttpApiEndpoint.get("persistentPty.get", "/api/experimental/persistent-pty/:ptyID", {
48
+ params: { ptyID: Pty.ID },
49
+ success: Schema.Struct({ data: PersistentPty.Info }),
50
+ error: terminalErrors,
51
+ }))
52
+ .add(HttpApiEndpoint.put("persistentPty.update", "/api/experimental/persistent-pty/:ptyID", {
53
+ params: { ptyID: Pty.ID },
54
+ payload: PersistentPty.UpdateInput,
55
+ success: Schema.Struct({ data: PersistentPty.Info }),
56
+ error: terminalErrors,
57
+ }))
58
+ .add(HttpApiEndpoint.get("persistentPty.snapshot", "/api/experimental/persistent-pty/:ptyID/snapshot", {
59
+ params: { ptyID: Pty.ID },
60
+ success: Schema.Struct({ data: PersistentPty.Snapshot }),
61
+ error: terminalErrors,
62
+ }))
63
+ .add(HttpApiEndpoint.delete("persistentPty.remove", "/api/experimental/persistent-pty/:ptyID", {
64
+ params: { ptyID: Pty.ID },
65
+ success: HttpApiSchema.NoContent,
66
+ error: terminalErrors,
67
+ }))
68
+ .add(HttpApiEndpoint.post("persistentPty.connectToken", "/api/experimental/persistent-pty/:ptyID/connect-token", {
69
+ params: { ptyID: Pty.ID },
70
+ headers: Schema.Struct({ [PTY_CONNECT_TOKEN_HEADER]: Schema.optional(Schema.String) }),
71
+ success: Schema.Struct({ data: PtyTicket.ConnectToken }),
72
+ error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError],
73
+ }))
74
+ .add(HttpApiEndpoint.get("persistentPty.connect", "/api/experimental/persistent-pty/:ptyID/connect", {
75
+ params: { ptyID: Pty.ID },
76
+ success: Schema.Boolean,
77
+ error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError],
78
+ }).annotateMerge(OpenApi.annotations({
79
+ identifier: "v2.persistentPty.connect",
80
+ summary: "Connect to a persistent PTY",
81
+ description: "Stream persistent PTY output through the OpenCode server.",
82
+ transform: (operation) => ({
83
+ ...operation,
84
+ "x-websocket": true,
85
+ parameters: [
86
+ ...(operation.parameters ?? []),
87
+ ...["cursor", "role", "attachment_id", "takeover", "input_protocol", PTY_CONNECT_TICKET_QUERY].map((name) => ({ in: "query", name, schema: { type: "string" } })),
88
+ ],
89
+ }),
90
+ })))
91
+ .annotateMerge(OpenApi.annotations({ title: "persistentPty", description: "Prototype persistent PTY routes." }));
@@ -0,0 +1,91 @@
1
+ import { Location } from "@opencode/schema/location";
2
+ import { Schema } from "effect";
3
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/unstable/httpapi";
4
+ import { InvalidRequestError, ServiceUnavailableError } from "../errors.js";
5
+ export declare const PluginGroup: HttpApiGroup.HttpApiGroup<"server.plugin", HttpApiEndpoint.HttpApiEndpoint<"plugin.list", "GET", "/api/plugin", never, Schema.toCodecStringTree<Schema.Struct<{
6
+ readonly location: Schema.optional<Schema.Struct<{
7
+ readonly directory: Schema.optional<Schema.String>;
8
+ readonly workspace: Schema.optional<Schema.String>;
9
+ }>>;
10
+ }>>, never, never, Schema.toCodecJson<Schema.Struct<{
11
+ readonly location: typeof Location.Info;
12
+ readonly data: Schema.$Array<Schema.Struct<{
13
+ readonly id: Schema.decodeTo<Schema.optional<Schema.toType<Schema.brand<Schema.String, "Plugin.ID">>>, Schema.optionalKey<Schema.brand<Schema.String, "Plugin.ID">>, never, never>;
14
+ readonly source: Schema.Union<readonly [Schema.Struct<{
15
+ readonly type: Schema.Literal<"builtin">;
16
+ }>, Schema.Struct<{
17
+ readonly type: Schema.Literal<"package">;
18
+ readonly target: Schema.String;
19
+ readonly version: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
20
+ readonly outdated: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
21
+ readonly updating: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
22
+ }>, Schema.Struct<{
23
+ readonly type: Schema.Literal<"local">;
24
+ readonly path: Schema.String;
25
+ }>, Schema.Struct<{
26
+ readonly type: Schema.Literal<"sdk">;
27
+ }>]>;
28
+ readonly features: Schema.Struct<{
29
+ readonly server: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
30
+ readonly tui: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
31
+ readonly rpc: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
32
+ }>;
33
+ readonly state: Schema.Union<readonly [Schema.Struct<{
34
+ readonly status: Schema.Literal<"active">;
35
+ }>, Schema.Struct<{
36
+ readonly status: Schema.Literal<"failed">;
37
+ readonly error: Schema.String;
38
+ readonly ref: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
39
+ }>]>;
40
+ }>>;
41
+ }>>, never, never, never> | HttpApiEndpoint.HttpApiEndpoint<"plugin.awaitActivation", "POST", "/api/plugin/await-activation", never, Schema.toCodecStringTree<Schema.Struct<{
42
+ readonly location: Schema.optional<Schema.Struct<{
43
+ readonly directory: Schema.optional<Schema.String>;
44
+ readonly workspace: Schema.optional<Schema.String>;
45
+ }>>;
46
+ }>>, never, never, Schema.toCodecJson<HttpApiSchema.NoContent>, never, never, never> | HttpApiEndpoint.HttpApiEndpoint<"plugin.check", "POST", "/api/plugin/check", never, Schema.toCodecStringTree<Schema.Struct<{
47
+ readonly location: Schema.optional<Schema.Struct<{
48
+ readonly directory: Schema.optional<Schema.String>;
49
+ readonly workspace: Schema.optional<Schema.String>;
50
+ }>>;
51
+ }>>, Schema.toCodecJson<Schema.Struct<{
52
+ readonly target: Schema.optional<Schema.String>;
53
+ }>>, never, Schema.toCodecJson<Schema.Struct<{
54
+ readonly location: typeof Location.Info;
55
+ readonly data: Schema.$Array<Schema.Struct<{
56
+ readonly id: Schema.decodeTo<Schema.optional<Schema.toType<Schema.brand<Schema.String, "Plugin.ID">>>, Schema.optionalKey<Schema.brand<Schema.String, "Plugin.ID">>, never, never>;
57
+ readonly source: Schema.Union<readonly [Schema.Struct<{
58
+ readonly type: Schema.Literal<"builtin">;
59
+ }>, Schema.Struct<{
60
+ readonly type: Schema.Literal<"package">;
61
+ readonly target: Schema.String;
62
+ readonly version: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
63
+ readonly outdated: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
64
+ readonly updating: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
65
+ }>, Schema.Struct<{
66
+ readonly type: Schema.Literal<"local">;
67
+ readonly path: Schema.String;
68
+ }>, Schema.Struct<{
69
+ readonly type: Schema.Literal<"sdk">;
70
+ }>]>;
71
+ readonly features: Schema.Struct<{
72
+ readonly server: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
73
+ readonly tui: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
74
+ readonly rpc: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Literal<true>>>, Schema.optionalKey<Schema.Literal<true>>, never, never>;
75
+ }>;
76
+ readonly state: Schema.Union<readonly [Schema.Struct<{
77
+ readonly status: Schema.Literal<"active">;
78
+ }>, Schema.Struct<{
79
+ readonly status: Schema.Literal<"failed">;
80
+ readonly error: Schema.String;
81
+ readonly ref: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
82
+ }>]>;
83
+ }>>;
84
+ }>>, Schema.toCodecJson<typeof InvalidRequestError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"plugin.update", "POST", "/api/plugin/update", never, Schema.toCodecStringTree<Schema.Struct<{
85
+ readonly location: Schema.optional<Schema.Struct<{
86
+ readonly directory: Schema.optional<Schema.String>;
87
+ readonly workspace: Schema.optional<Schema.String>;
88
+ }>>;
89
+ }>>, Schema.toCodecJson<Schema.Struct<{
90
+ readonly targets: Schema.$Array<Schema.String>;
91
+ }>>, never, Schema.toCodecJson<HttpApiSchema.NoContent>, Schema.toCodecJson<typeof InvalidRequestError | typeof ServiceUnavailableError>, never, never>, false>;
@@ -0,0 +1,55 @@
1
+ import { Location } from "@opencode/schema/location";
2
+ import { Plugin } from "@opencode/schema/plugin";
3
+ import { Schema } from "effect";
4
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
5
+ import { InvalidRequestError, ServiceUnavailableError } from "../errors.js";
6
+ import { LocationQuery, locationQueryOpenApi } from "./location.js";
7
+ export const PluginGroup = HttpApiGroup.make("server.plugin")
8
+ .add(HttpApiEndpoint.get("plugin.list", "/api/plugin", {
9
+ query: LocationQuery,
10
+ success: Location.response(Schema.Array(Plugin.Info)),
11
+ })
12
+ .annotateMerge(locationQueryOpenApi)
13
+ .annotateMerge(OpenApi.annotations({
14
+ identifier: "v2.plugin.list",
15
+ summary: "List plugins",
16
+ description: "Retrieve enabled server plugins and their current status.",
17
+ })))
18
+ .add(HttpApiEndpoint.post("plugin.awaitActivation", "/api/plugin/await-activation", {
19
+ query: LocationQuery,
20
+ success: HttpApiSchema.NoContent,
21
+ })
22
+ .annotateMerge(locationQueryOpenApi)
23
+ .annotateMerge(OpenApi.annotations({
24
+ identifier: "v2.plugin.awaitActivation",
25
+ summary: "Wait for plugin activation",
26
+ description: "Wait for configured plugin activation at a Location to settle, including missing-package installs. Completion does not imply every plugin succeeded or background resource discovery finished. Cancelling this wait does not cancel activation.",
27
+ })))
28
+ .add(HttpApiEndpoint.post("plugin.check", "/api/plugin/check", {
29
+ query: LocationQuery,
30
+ payload: Schema.Struct({ target: Schema.String.pipe(Schema.optional) }),
31
+ success: Location.response(Schema.Array(Plugin.Info)),
32
+ error: InvalidRequestError,
33
+ })
34
+ .annotateMerge(locationQueryOpenApi)
35
+ .annotateMerge(OpenApi.annotations({
36
+ identifier: "v2.plugin.check",
37
+ summary: "Check plugin updates",
38
+ description: "Check one or all package plugins for available updates.",
39
+ })))
40
+ .add(HttpApiEndpoint.post("plugin.update", "/api/plugin/update", {
41
+ query: LocationQuery,
42
+ payload: Schema.Struct({ targets: Schema.Array(Schema.String) }),
43
+ success: HttpApiSchema.NoContent,
44
+ error: [InvalidRequestError, ServiceUnavailableError],
45
+ })
46
+ .annotateMerge(locationQueryOpenApi)
47
+ .annotateMerge(OpenApi.annotations({
48
+ identifier: "v2.plugin.update",
49
+ summary: "Update plugins",
50
+ description: "Update package plugins concurrently and notify active locations to reload them. Responds once every update has finished; fails when any update fails.",
51
+ })))
52
+ .annotateMerge(OpenApi.annotations({
53
+ title: "plugin",
54
+ description: "Experimental plugin routes.",
55
+ }));