@kilocode/protocol 0.0.0-stage → 0.0.0-v2-9
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 +58 -0
- package/dist/api.js +77 -0
- package/dist/client.d.ts +63 -0
- package/dist/client.js +55 -0
- package/dist/errors.d.ts +193 -0
- package/dist/errors.js +152 -0
- package/dist/groups/agent.d.ts +225 -0
- package/dist/groups/agent.js +30 -0
- package/dist/groups/command.d.ts +15 -0
- package/dist/groups/command.js +20 -0
- package/dist/groups/config.d.ts +14 -0
- package/dist/groups/config.js +32 -0
- package/dist/groups/credential.d.ts +93 -0
- package/dist/groups/credential.js +50 -0
- package/dist/groups/debug.d.ts +22 -0
- package/dist/groups/debug.js +49 -0
- package/dist/groups/event.d.ts +14853 -0
- package/dist/groups/event.js +51 -0
- package/dist/groups/form.d.ts +138 -0
- package/dist/groups/form.js +18 -0
- package/dist/groups/fs.d.ts +48 -0
- package/dist/groups/fs.js +73 -0
- package/dist/groups/generate.d.ts +33 -0
- package/dist/groups/generate.js +23 -0
- package/dist/groups/integration.d.ts +1724 -0
- package/dist/groups/integration.js +169 -0
- package/dist/groups/location.d.ts +23 -0
- package/dist/groups/location.js +45 -0
- package/dist/groups/mcp.d.ts +103 -0
- package/dist/groups/mcp.js +114 -0
- package/dist/groups/message.d.ts +1416 -0
- package/dist/groups/message.js +77 -0
- package/dist/groups/migration.d.ts +28 -0
- package/dist/groups/migration.js +21 -0
- package/dist/groups/model.d.ts +399 -0
- package/dist/groups/model.js +33 -0
- package/dist/groups/permission.d.ts +175 -0
- package/dist/groups/permission.js +110 -0
- package/dist/groups/persistent-pty.d.ts +242 -0
- package/dist/groups/persistent-pty.js +92 -0
- package/dist/groups/plugin.d.ts +86 -0
- package/dist/groups/plugin.js +45 -0
- package/dist/groups/project.d.ts +83 -0
- package/dist/groups/project.js +33 -0
- package/dist/groups/provider.d.ts +231 -0
- package/dist/groups/provider.js +34 -0
- package/dist/groups/pty.d.ts +151 -0
- package/dist/groups/pty.js +112 -0
- package/dist/groups/reference.d.ts +25 -0
- package/dist/groups/reference.js +33 -0
- package/dist/groups/rpc.d.ts +21 -0
- package/dist/groups/rpc.js +24 -0
- package/dist/groups/server.d.ts +44 -0
- package/dist/groups/server.js +53 -0
- package/dist/groups/session.d.ts +11380 -0
- package/dist/groups/session.js +611 -0
- package/dist/groups/shell.d.ts +125 -0
- package/dist/groups/shell.js +64 -0
- package/dist/groups/skill.d.ts +19 -0
- package/dist/groups/skill.js +20 -0
- package/dist/groups/vcs.d.ts +87 -0
- package/dist/groups/vcs.js +93 -0
- package/dist/groups/voice.d.ts +93 -0
- package/dist/groups/voice.js +89 -0
- package/dist/groups/websearch.d.ts +29 -0
- package/dist/groups/websearch.js +34 -0
- package/dist/groups/worktree.d.ts +41 -0
- package/dist/groups/worktree.js +52 -0
- package/dist/middleware/authorization.d.ts +13 -0
- package/dist/middleware/authorization.js +6 -0
- package/dist/middleware/schema-error.d.ts +13 -0
- package/dist/middleware/schema-error.js +4 -0
- package/dist/simulation.d.ts +1830 -0
- package/dist/simulation.js +497 -0
- package/package.json +45 -4
- package/README.md +0 -3
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { Context, Schema } from "effect";
|
|
2
|
+
import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema } from "effect/http-api";
|
|
3
|
+
import { ConflictError, LocationNotFoundError, PermissionNotFoundError, PermissionSavedNotFoundError, SessionNotFoundError } from "../errors.js";
|
|
4
|
+
export declare const makePermissionGroup: <LocationId extends HttpApiMiddleware.AnyId, LocationService, SessionLocationId extends HttpApiMiddleware.AnyId, SessionLocationService>(locationMiddleware: Context.Key<LocationId, LocationService>, sessionLocationMiddleware: Context.Key<SessionLocationId, SessionLocationService>) => HttpApiGroup.HttpApiGroup<"server.permission", HttpApiEndpoint.HttpApiEndpoint<"permission.request.list", "GET", "/api/permission/request", never, Schema.toCodecStringTree<Schema.Struct<{
|
|
5
|
+
readonly location: Schema.optional<Schema.Struct<{
|
|
6
|
+
readonly directory: Schema.optional<Schema.String>;
|
|
7
|
+
}>>;
|
|
8
|
+
}>>, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
9
|
+
readonly location: Schema.Struct<{
|
|
10
|
+
readonly directory: Schema.brand<Schema.String, "AbsolutePath">;
|
|
11
|
+
}>;
|
|
12
|
+
readonly data: Schema.$Array<Schema.Struct<{
|
|
13
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
14
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
15
|
+
};
|
|
16
|
+
readonly action: Schema.String;
|
|
17
|
+
readonly resources: Schema.$Array<Schema.String>;
|
|
18
|
+
readonly save: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Array<Schema.String>>>, Schema.optionalKey<Schema.$Array<Schema.String>>, never, never>;
|
|
19
|
+
readonly metadata: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Record<Schema.String, Schema.Unknown>>>, Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>, never, never>;
|
|
20
|
+
readonly source: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Union<readonly [Schema.Struct<{
|
|
21
|
+
readonly type: Schema.Literal<"tool">;
|
|
22
|
+
readonly messageID: Schema.String;
|
|
23
|
+
readonly id: Schema.String;
|
|
24
|
+
}>]>>>, Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
25
|
+
readonly type: Schema.Literal<"tool">;
|
|
26
|
+
readonly messageID: Schema.String;
|
|
27
|
+
readonly id: Schema.String;
|
|
28
|
+
}>]>>, never, never>;
|
|
29
|
+
readonly message: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
30
|
+
readonly id: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
31
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
32
|
+
};
|
|
33
|
+
}>>;
|
|
34
|
+
}>>, never, LocationId, HttpApiMiddleware.Requires<LocationId>> | HttpApiEndpoint.HttpApiEndpoint<"permission.saved.list", "GET", "/api/permission/saved", never, Schema.toCodecStringTree<Schema.Struct<{
|
|
35
|
+
readonly projectID: Schema.optional<Schema.brand<Schema.String, "Project.ID"> & {
|
|
36
|
+
global: string & import("effect/Brand").Brand<"Project.ID">;
|
|
37
|
+
}>;
|
|
38
|
+
}>>, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
39
|
+
readonly data: Schema.$Array<Schema.Struct<{
|
|
40
|
+
readonly id: Schema.brand<Schema.String, "PermissionSaved.ID"> & {
|
|
41
|
+
create: () => string & import("effect/Brand").Brand<"PermissionSaved.ID">;
|
|
42
|
+
};
|
|
43
|
+
readonly projectID: Schema.brand<Schema.String, "Project.ID"> & {
|
|
44
|
+
global: string & import("effect/Brand").Brand<"Project.ID">;
|
|
45
|
+
};
|
|
46
|
+
readonly action: Schema.String;
|
|
47
|
+
readonly resource: Schema.String;
|
|
48
|
+
readonly time: Schema.Struct<{
|
|
49
|
+
readonly created: Schema.decodeTo<Schema.DateTimeUtc, Schema.Finite, never, never>;
|
|
50
|
+
readonly updated: Schema.decodeTo<Schema.DateTimeUtc, Schema.Finite, never, never>;
|
|
51
|
+
}>;
|
|
52
|
+
}>>;
|
|
53
|
+
}>>, never, LocationId, HttpApiMiddleware.Requires<LocationId>> | HttpApiEndpoint.HttpApiEndpoint<"permission.saved.remove", "DELETE", "/api/permission/saved/:id", Schema.toCodecStringTree<Schema.Struct<{
|
|
54
|
+
id: Schema.brand<Schema.String, "PermissionSaved.ID"> & {
|
|
55
|
+
create: () => string & import("effect/Brand").Brand<"PermissionSaved.ID">;
|
|
56
|
+
};
|
|
57
|
+
}>>, never, never, never, Schema.toCodecJson<HttpApiSchema.NoContent>, never, LocationId, HttpApiMiddleware.Requires<LocationId>> | HttpApiEndpoint.HttpApiEndpoint<"permission.saved.update", "PATCH", "/api/permission/saved/:id", Schema.toCodecStringTree<Schema.Struct<{
|
|
58
|
+
id: Schema.brand<Schema.String, "PermissionSaved.ID"> & {
|
|
59
|
+
create: () => string & import("effect/Brand").Brand<"PermissionSaved.ID">;
|
|
60
|
+
};
|
|
61
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
62
|
+
readonly action: Schema.optional<Schema.String>;
|
|
63
|
+
readonly resource: Schema.optional<Schema.String>;
|
|
64
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
65
|
+
readonly data: Schema.Struct<{
|
|
66
|
+
readonly id: Schema.brand<Schema.String, "PermissionSaved.ID"> & {
|
|
67
|
+
create: () => string & import("effect/Brand").Brand<"PermissionSaved.ID">;
|
|
68
|
+
};
|
|
69
|
+
readonly projectID: Schema.brand<Schema.String, "Project.ID"> & {
|
|
70
|
+
global: string & import("effect/Brand").Brand<"Project.ID">;
|
|
71
|
+
};
|
|
72
|
+
readonly action: Schema.String;
|
|
73
|
+
readonly resource: Schema.String;
|
|
74
|
+
readonly time: Schema.Struct<{
|
|
75
|
+
readonly created: Schema.decodeTo<Schema.DateTimeUtc, Schema.Finite, never, never>;
|
|
76
|
+
readonly updated: Schema.decodeTo<Schema.DateTimeUtc, Schema.Finite, never, never>;
|
|
77
|
+
}>;
|
|
78
|
+
}>;
|
|
79
|
+
}>>, Schema.toCodecJson<typeof ConflictError | typeof PermissionSavedNotFoundError>, LocationId, HttpApiMiddleware.Requires<LocationId>> | HttpApiEndpoint.HttpApiEndpoint<"session.permission.create", "POST", "/api/session/:sessionID/permission", Schema.toCodecStringTree<Schema.Struct<{
|
|
80
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
81
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
82
|
+
};
|
|
83
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
84
|
+
readonly id: Schema.optional<Schema.brand<Schema.String, "Permission.ID"> & {
|
|
85
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
86
|
+
}>;
|
|
87
|
+
readonly action: Schema.String;
|
|
88
|
+
readonly resources: Schema.$Array<Schema.String>;
|
|
89
|
+
readonly save: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Array<Schema.String>>>, Schema.optionalKey<Schema.$Array<Schema.String>>, never, never>;
|
|
90
|
+
readonly metadata: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Record<Schema.String, Schema.Unknown>>>, Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>, never, never>;
|
|
91
|
+
readonly source: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Union<readonly [Schema.Struct<{
|
|
92
|
+
readonly type: Schema.Literal<"tool">;
|
|
93
|
+
readonly messageID: Schema.String;
|
|
94
|
+
readonly id: Schema.String;
|
|
95
|
+
}>]>>>, Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
96
|
+
readonly type: Schema.Literal<"tool">;
|
|
97
|
+
readonly messageID: Schema.String;
|
|
98
|
+
readonly id: Schema.String;
|
|
99
|
+
}>]>>, never, never>;
|
|
100
|
+
readonly agent: Schema.optional<Schema.brand<Schema.String, "Agent.ID">>;
|
|
101
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
102
|
+
readonly data: Schema.Struct<{
|
|
103
|
+
readonly id: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
104
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
105
|
+
};
|
|
106
|
+
readonly effect: Schema.Literals<readonly ["allow", "deny", "ask"]>;
|
|
107
|
+
}>;
|
|
108
|
+
}>>, Schema.toCodecJson<typeof SessionNotFoundError>, SessionLocationId, HttpApiMiddleware.Requires<SessionLocationId>> | HttpApiEndpoint.HttpApiEndpoint<"session.permission.get", "GET", "/api/session/:sessionID/permission/:requestID", Schema.toCodecStringTree<Schema.Struct<{
|
|
109
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
110
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
111
|
+
};
|
|
112
|
+
requestID: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
113
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
114
|
+
};
|
|
115
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
116
|
+
readonly data: Schema.Struct<{
|
|
117
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
118
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
119
|
+
};
|
|
120
|
+
readonly action: Schema.String;
|
|
121
|
+
readonly resources: Schema.$Array<Schema.String>;
|
|
122
|
+
readonly save: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Array<Schema.String>>>, Schema.optionalKey<Schema.$Array<Schema.String>>, never, never>;
|
|
123
|
+
readonly metadata: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Record<Schema.String, Schema.Unknown>>>, Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>, never, never>;
|
|
124
|
+
readonly source: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Union<readonly [Schema.Struct<{
|
|
125
|
+
readonly type: Schema.Literal<"tool">;
|
|
126
|
+
readonly messageID: Schema.String;
|
|
127
|
+
readonly id: Schema.String;
|
|
128
|
+
}>]>>>, Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
129
|
+
readonly type: Schema.Literal<"tool">;
|
|
130
|
+
readonly messageID: Schema.String;
|
|
131
|
+
readonly id: Schema.String;
|
|
132
|
+
}>]>>, never, never>;
|
|
133
|
+
readonly message: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
134
|
+
readonly id: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
135
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
136
|
+
};
|
|
137
|
+
}>;
|
|
138
|
+
}>>, Schema.toCodecJson<typeof SessionNotFoundError | typeof PermissionNotFoundError>, SessionLocationId, HttpApiMiddleware.Requires<SessionLocationId>> | HttpApiEndpoint.HttpApiEndpoint<"session.permission.list", "GET", "/api/session/:sessionID/permission", Schema.toCodecStringTree<Schema.Struct<{
|
|
139
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
140
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
141
|
+
};
|
|
142
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
143
|
+
readonly data: Schema.$Array<Schema.Struct<{
|
|
144
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
145
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
146
|
+
};
|
|
147
|
+
readonly action: Schema.String;
|
|
148
|
+
readonly resources: Schema.$Array<Schema.String>;
|
|
149
|
+
readonly save: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Array<Schema.String>>>, Schema.optionalKey<Schema.$Array<Schema.String>>, never, never>;
|
|
150
|
+
readonly metadata: Schema.decodeTo<Schema.optional<Schema.toType<Schema.$Record<Schema.String, Schema.Unknown>>>, Schema.optionalKey<Schema.$Record<Schema.String, Schema.Unknown>>, never, never>;
|
|
151
|
+
readonly source: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Union<readonly [Schema.Struct<{
|
|
152
|
+
readonly type: Schema.Literal<"tool">;
|
|
153
|
+
readonly messageID: Schema.String;
|
|
154
|
+
readonly id: Schema.String;
|
|
155
|
+
}>]>>>, Schema.optionalKey<Schema.Union<readonly [Schema.Struct<{
|
|
156
|
+
readonly type: Schema.Literal<"tool">;
|
|
157
|
+
readonly messageID: Schema.String;
|
|
158
|
+
readonly id: Schema.String;
|
|
159
|
+
}>]>>, never, never>;
|
|
160
|
+
readonly message: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
161
|
+
readonly id: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
162
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
163
|
+
};
|
|
164
|
+
}>>;
|
|
165
|
+
}>>, Schema.toCodecJson<typeof LocationNotFoundError | typeof SessionNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"session.permission.reply", "POST", "/api/session/:sessionID/permission/:requestID/reply", Schema.toCodecStringTree<Schema.Struct<{
|
|
166
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
167
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
168
|
+
};
|
|
169
|
+
requestID: Schema.brand<Schema.String, "Permission.ID"> & {
|
|
170
|
+
create: (id?: string) => string & import("effect/Brand").Brand<"Permission.ID">;
|
|
171
|
+
};
|
|
172
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
173
|
+
readonly decision: Schema.Literals<readonly ["once", "always", "reject"]>;
|
|
174
|
+
readonly message: Schema.optional<Schema.String>;
|
|
175
|
+
}>>, never, Schema.toCodecJson<HttpApiSchema.NoContent>, Schema.toCodecJson<typeof SessionNotFoundError | typeof PermissionNotFoundError>, SessionLocationId, HttpApiMiddleware.Requires<SessionLocationId>>, false>;
|
|
@@ -0,0 +1,110 @@
|
|
|
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/http-api";
|
|
9
|
+
import { ConflictError, LocationNotFoundError, PermissionNotFoundError, PermissionSavedNotFoundError, 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: "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: "permission.saved.list",
|
|
27
|
+
summary: "List saved permissions",
|
|
28
|
+
description: "Retrieve saved permissions, optionally filtered by project.",
|
|
29
|
+
})))
|
|
30
|
+
.add(HttpApiEndpoint.patch("permission.saved.update", "/api/permission/saved/:id", {
|
|
31
|
+
params: { id: PermissionSaved.ID },
|
|
32
|
+
payload: Schema.Struct({
|
|
33
|
+
action: Schema.String.pipe(Schema.optional),
|
|
34
|
+
resource: Schema.String.pipe(Schema.optional),
|
|
35
|
+
}),
|
|
36
|
+
success: Schema.Struct({ data: PermissionSaved.Info }),
|
|
37
|
+
error: [PermissionSavedNotFoundError, ConflictError],
|
|
38
|
+
}).annotateMerge(OpenApi.annotations({
|
|
39
|
+
identifier: "permission.saved.update",
|
|
40
|
+
summary: "Update saved permission",
|
|
41
|
+
description: "Change the action or resource of a saved permission. Fails with a conflict when the project already has a saved permission with the same action and resource.",
|
|
42
|
+
})))
|
|
43
|
+
.add(HttpApiEndpoint.delete("permission.saved.remove", "/api/permission/saved/:id", {
|
|
44
|
+
params: { id: PermissionSaved.ID },
|
|
45
|
+
success: HttpApiSchema.NoContent,
|
|
46
|
+
}).annotateMerge(OpenApi.annotations({
|
|
47
|
+
identifier: "permission.saved.remove",
|
|
48
|
+
summary: "Remove saved permission",
|
|
49
|
+
description: "Remove a saved permission by ID.",
|
|
50
|
+
})))
|
|
51
|
+
// Effect applies group middleware only to endpoints already added; session endpoints use session placement below.
|
|
52
|
+
.middleware(locationMiddleware)
|
|
53
|
+
.add(HttpApiEndpoint.post("session.permission.create", "/api/session/:sessionID/permission", {
|
|
54
|
+
params: { sessionID: Session.ID },
|
|
55
|
+
payload: Schema.Struct({
|
|
56
|
+
id: Permission.ID.pipe(Schema.optional),
|
|
57
|
+
action: Permission.Request.fields.action,
|
|
58
|
+
resources: Permission.Request.fields.resources,
|
|
59
|
+
save: Permission.Request.fields.save,
|
|
60
|
+
metadata: Permission.Request.fields.metadata,
|
|
61
|
+
source: Permission.Request.fields.source,
|
|
62
|
+
agent: Agent.ID.pipe(Schema.optional),
|
|
63
|
+
}),
|
|
64
|
+
success: Schema.Struct({
|
|
65
|
+
data: Schema.Struct({ id: Permission.ID, effect: Permission.Effect }),
|
|
66
|
+
}),
|
|
67
|
+
error: SessionNotFoundError,
|
|
68
|
+
})
|
|
69
|
+
.middleware(sessionLocationMiddleware)
|
|
70
|
+
.annotateMerge(OpenApi.annotations({
|
|
71
|
+
identifier: "session.permission.create",
|
|
72
|
+
summary: "Create permission request",
|
|
73
|
+
description: "Evaluate and, when approval is required, create a permission request for a session.",
|
|
74
|
+
})))
|
|
75
|
+
.add(HttpApiEndpoint.get("session.permission.list", "/api/session/:sessionID/permission", {
|
|
76
|
+
params: { sessionID: Session.ID },
|
|
77
|
+
success: Schema.Struct({ data: Schema.Array(Permission.Request) }),
|
|
78
|
+
error: [SessionNotFoundError, LocationNotFoundError],
|
|
79
|
+
}).annotateMerge(OpenApi.annotations({
|
|
80
|
+
identifier: "session.permission.list",
|
|
81
|
+
summary: "List session permission requests",
|
|
82
|
+
description: "Retrieve pending permission requests owned by a session.",
|
|
83
|
+
})))
|
|
84
|
+
.add(HttpApiEndpoint.get("session.permission.get", "/api/session/:sessionID/permission/:requestID", {
|
|
85
|
+
params: { sessionID: Session.ID, requestID: Permission.ID },
|
|
86
|
+
success: Schema.Struct({ data: Permission.Request }),
|
|
87
|
+
error: [SessionNotFoundError, PermissionNotFoundError],
|
|
88
|
+
})
|
|
89
|
+
.middleware(sessionLocationMiddleware)
|
|
90
|
+
.annotateMerge(OpenApi.annotations({
|
|
91
|
+
identifier: "session.permission.get",
|
|
92
|
+
summary: "Get permission request",
|
|
93
|
+
description: "Retrieve a pending permission request owned by a session.",
|
|
94
|
+
})))
|
|
95
|
+
.add(HttpApiEndpoint.post("session.permission.reply", "/api/session/:sessionID/permission/:requestID/reply", {
|
|
96
|
+
params: { sessionID: Session.ID, requestID: Permission.ID },
|
|
97
|
+
payload: Schema.Struct({
|
|
98
|
+
decision: Permission.Reply,
|
|
99
|
+
message: Schema.String.pipe(Schema.optional),
|
|
100
|
+
}),
|
|
101
|
+
success: HttpApiSchema.NoContent,
|
|
102
|
+
error: [SessionNotFoundError, PermissionNotFoundError],
|
|
103
|
+
})
|
|
104
|
+
.middleware(sessionLocationMiddleware)
|
|
105
|
+
.annotateMerge(OpenApi.annotations({
|
|
106
|
+
identifier: "session.permission.reply",
|
|
107
|
+
summary: "Reply to pending permission request",
|
|
108
|
+
description: "Respond to a pending permission request owned by a session.",
|
|
109
|
+
})))
|
|
110
|
+
.annotateMerge(OpenApi.annotations({ title: "permission", description: "Experimental permission routes." }));
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api";
|
|
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 const PERSISTENT_PTY_CONNECT_PATH = "/api/experimental/persistent-pty/:ptyID/connect";
|
|
7
|
+
export declare function hasPersistentPtyConnectTicketURL(url: URL): boolean;
|
|
8
|
+
export declare const PersistentPtyGroup: HttpApiGroup.HttpApiGroup<"server.experimental", HttpApiEndpoint.HttpApiEndpoint<"persistentPty.connect", "GET", "/api/experimental/persistent-pty/:ptyID/connect", Schema.toCodecStringTree<Schema.Struct<{
|
|
9
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
10
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
11
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
12
|
+
};
|
|
13
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Boolean>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof ForbiddenError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.connectToken", "POST", "/api/experimental/persistent-pty/:ptyID/connect-token", Schema.toCodecStringTree<Schema.Struct<{
|
|
14
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
15
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
16
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
17
|
+
};
|
|
18
|
+
}>>, never, never, Schema.toCodecStringTree<Schema.Struct<{
|
|
19
|
+
readonly "x-opencode-ticket": Schema.optional<Schema.String>;
|
|
20
|
+
}>>, Schema.toCodecJson<Schema.Struct<{
|
|
21
|
+
readonly data: Schema.Struct<{
|
|
22
|
+
readonly ticket: Schema.String;
|
|
23
|
+
readonly expires_in: Schema.Int;
|
|
24
|
+
}>;
|
|
25
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof ForbiddenError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.create", "POST", "/api/experimental/session/:sessionID/terminal", Schema.toCodecStringTree<Schema.Struct<{
|
|
26
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
27
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
28
|
+
};
|
|
29
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
30
|
+
readonly command: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
31
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
32
|
+
readonly cwd: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
33
|
+
readonly title: Schema.String;
|
|
34
|
+
readonly env: Schema.$Record<Schema.String, Schema.String>;
|
|
35
|
+
readonly size: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Struct<{
|
|
36
|
+
readonly cols: Schema.Int;
|
|
37
|
+
readonly rows: Schema.Int;
|
|
38
|
+
}>>>, Schema.optionalKey<Schema.Struct<{
|
|
39
|
+
readonly cols: Schema.Int;
|
|
40
|
+
readonly rows: Schema.Int;
|
|
41
|
+
}>>, never, never>;
|
|
42
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
43
|
+
readonly data: Schema.Struct<{
|
|
44
|
+
readonly id: Schema.brand<Schema.String, "PtyID"> & {
|
|
45
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
46
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
47
|
+
};
|
|
48
|
+
readonly title: Schema.String;
|
|
49
|
+
readonly command: Schema.String;
|
|
50
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
51
|
+
readonly cwd: Schema.String;
|
|
52
|
+
readonly status: Schema.Literals<readonly ["running", "exited"]>;
|
|
53
|
+
readonly pid: Schema.Int;
|
|
54
|
+
readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
|
|
55
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
56
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
57
|
+
};
|
|
58
|
+
readonly foregroundProcess: Schema.NullOr<Schema.String>;
|
|
59
|
+
readonly size: Schema.Struct<{
|
|
60
|
+
readonly cols: Schema.Int;
|
|
61
|
+
readonly rows: Schema.Int;
|
|
62
|
+
}>;
|
|
63
|
+
readonly output: Schema.Struct<{
|
|
64
|
+
readonly head: Schema.Int;
|
|
65
|
+
readonly tail: Schema.Int;
|
|
66
|
+
}>;
|
|
67
|
+
}>;
|
|
68
|
+
}>>, Schema.toCodecJson<typeof InvalidRequestError | typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.get", "GET", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
|
|
69
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
70
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
71
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
72
|
+
};
|
|
73
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
74
|
+
readonly data: Schema.Struct<{
|
|
75
|
+
readonly id: Schema.brand<Schema.String, "PtyID"> & {
|
|
76
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
77
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
78
|
+
};
|
|
79
|
+
readonly title: Schema.String;
|
|
80
|
+
readonly command: Schema.String;
|
|
81
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
82
|
+
readonly cwd: Schema.String;
|
|
83
|
+
readonly status: Schema.Literals<readonly ["running", "exited"]>;
|
|
84
|
+
readonly pid: Schema.Int;
|
|
85
|
+
readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
|
|
86
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
87
|
+
create: () => 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
|
+
}>;
|
|
99
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.handoff", "POST", "/api/experimental/persistent-pty/handoff", never, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
100
|
+
readonly handoff: Schema.NullOr<Schema.Struct<{
|
|
101
|
+
readonly directory: Schema.String;
|
|
102
|
+
readonly instanceID: Schema.String;
|
|
103
|
+
readonly ticket: Schema.String;
|
|
104
|
+
readonly expiresAt: Schema.Number;
|
|
105
|
+
}>>;
|
|
106
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.list", "GET", "/api/experimental/session/:sessionID/terminal", Schema.toCodecStringTree<Schema.Struct<{
|
|
107
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
108
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
109
|
+
};
|
|
110
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
111
|
+
readonly data: Schema.$Array<Schema.Struct<{
|
|
112
|
+
readonly id: Schema.brand<Schema.String, "PtyID"> & {
|
|
113
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
114
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
115
|
+
};
|
|
116
|
+
readonly title: Schema.String;
|
|
117
|
+
readonly command: Schema.String;
|
|
118
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
119
|
+
readonly cwd: Schema.String;
|
|
120
|
+
readonly status: Schema.Literals<readonly ["running", "exited"]>;
|
|
121
|
+
readonly pid: Schema.Int;
|
|
122
|
+
readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
|
|
123
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
124
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
125
|
+
};
|
|
126
|
+
readonly foregroundProcess: Schema.NullOr<Schema.String>;
|
|
127
|
+
readonly size: Schema.Struct<{
|
|
128
|
+
readonly cols: Schema.Int;
|
|
129
|
+
readonly rows: Schema.Int;
|
|
130
|
+
}>;
|
|
131
|
+
readonly output: Schema.Struct<{
|
|
132
|
+
readonly head: Schema.Int;
|
|
133
|
+
readonly tail: Schema.Int;
|
|
134
|
+
}>;
|
|
135
|
+
}>>;
|
|
136
|
+
}>>, Schema.toCodecJson<typeof InvalidRequestError | typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.read", "GET", "/api/experimental/session/:sessionID/terminal/read", Schema.toCodecStringTree<Schema.Struct<{
|
|
137
|
+
sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
138
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
139
|
+
};
|
|
140
|
+
}>>, Schema.toCodecStringTree<Schema.Struct<{
|
|
141
|
+
lines: Schema.optional<Schema.compose<Schema.Int, Schema.NumberFromString>>;
|
|
142
|
+
}>>, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
143
|
+
readonly data: Schema.NullOr<Schema.Struct<{
|
|
144
|
+
readonly ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
145
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
146
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
147
|
+
};
|
|
148
|
+
readonly title: Schema.String;
|
|
149
|
+
readonly cwd: Schema.String;
|
|
150
|
+
readonly foregroundProcess: Schema.NullOr<Schema.String>;
|
|
151
|
+
readonly screen: Schema.Struct<{
|
|
152
|
+
readonly text: Schema.String;
|
|
153
|
+
readonly cols: Schema.Int;
|
|
154
|
+
readonly rows: Schema.Int;
|
|
155
|
+
readonly cursor: Schema.Struct<{
|
|
156
|
+
readonly x: Schema.Int;
|
|
157
|
+
readonly y: Schema.Int;
|
|
158
|
+
}>;
|
|
159
|
+
}>;
|
|
160
|
+
}>>;
|
|
161
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.remove", "DELETE", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
|
|
162
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
163
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
164
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
165
|
+
};
|
|
166
|
+
}>>, never, never, never, Schema.toCodecJson<HttpApiSchema.NoContent>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, 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.snapshot", "GET", "/api/experimental/persistent-pty/:ptyID/snapshot", Schema.toCodecStringTree<Schema.Struct<{
|
|
167
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
168
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
169
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
170
|
+
};
|
|
171
|
+
}>>, never, never, never, Schema.toCodecJson<Schema.Struct<{
|
|
172
|
+
readonly data: Schema.Struct<{
|
|
173
|
+
readonly info: Schema.Struct<{
|
|
174
|
+
readonly id: Schema.brand<Schema.String, "PtyID"> & {
|
|
175
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
176
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
177
|
+
};
|
|
178
|
+
readonly title: Schema.String;
|
|
179
|
+
readonly command: Schema.String;
|
|
180
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
181
|
+
readonly cwd: Schema.String;
|
|
182
|
+
readonly status: Schema.Literals<readonly ["running", "exited"]>;
|
|
183
|
+
readonly pid: Schema.Int;
|
|
184
|
+
readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
|
|
185
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
186
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
187
|
+
};
|
|
188
|
+
readonly foregroundProcess: Schema.NullOr<Schema.String>;
|
|
189
|
+
readonly size: Schema.Struct<{
|
|
190
|
+
readonly cols: Schema.Int;
|
|
191
|
+
readonly rows: Schema.Int;
|
|
192
|
+
}>;
|
|
193
|
+
readonly output: Schema.Struct<{
|
|
194
|
+
readonly head: Schema.Int;
|
|
195
|
+
readonly tail: Schema.Int;
|
|
196
|
+
}>;
|
|
197
|
+
}>;
|
|
198
|
+
readonly text: Schema.String;
|
|
199
|
+
readonly checkpoint: Schema.Uint8Array;
|
|
200
|
+
readonly cursor: Schema.Struct<{
|
|
201
|
+
readonly x: Schema.Int;
|
|
202
|
+
readonly y: Schema.Int;
|
|
203
|
+
}>;
|
|
204
|
+
}>;
|
|
205
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"persistentPty.update", "PUT", "/api/experimental/persistent-pty/:ptyID", Schema.toCodecStringTree<Schema.Struct<{
|
|
206
|
+
ptyID: Schema.brand<Schema.String, "PtyID"> & {
|
|
207
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
208
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
209
|
+
};
|
|
210
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
211
|
+
readonly attachmentID: Schema.decodeTo<Schema.optional<Schema.toType<Schema.String>>, Schema.optionalKey<Schema.String>, never, never>;
|
|
212
|
+
readonly size: Schema.Struct<{
|
|
213
|
+
readonly cols: Schema.Int;
|
|
214
|
+
readonly rows: Schema.Int;
|
|
215
|
+
}>;
|
|
216
|
+
}>>, never, Schema.toCodecJson<Schema.Struct<{
|
|
217
|
+
readonly data: Schema.Struct<{
|
|
218
|
+
readonly id: Schema.brand<Schema.String, "PtyID"> & {
|
|
219
|
+
create: () => string & import("effect/Brand").Brand<"PtyID">;
|
|
220
|
+
ascending: (id?: string) => string & import("effect/Brand").Brand<"PtyID">;
|
|
221
|
+
};
|
|
222
|
+
readonly title: Schema.String;
|
|
223
|
+
readonly command: Schema.String;
|
|
224
|
+
readonly args: Schema.$Array<Schema.String>;
|
|
225
|
+
readonly cwd: Schema.String;
|
|
226
|
+
readonly status: Schema.Literals<readonly ["running", "exited"]>;
|
|
227
|
+
readonly pid: Schema.Int;
|
|
228
|
+
readonly exitCode: Schema.decodeTo<Schema.optional<Schema.toType<Schema.Int>>, Schema.optionalKey<Schema.Int>, never, never>;
|
|
229
|
+
readonly sessionID: Schema.brand<Schema.String, "SessionID"> & {
|
|
230
|
+
create: () => string & import("effect/Brand").Brand<"SessionID">;
|
|
231
|
+
};
|
|
232
|
+
readonly foregroundProcess: Schema.NullOr<Schema.String>;
|
|
233
|
+
readonly size: Schema.Struct<{
|
|
234
|
+
readonly cols: Schema.Int;
|
|
235
|
+
readonly rows: Schema.Int;
|
|
236
|
+
}>;
|
|
237
|
+
readonly output: Schema.Struct<{
|
|
238
|
+
readonly head: Schema.Int;
|
|
239
|
+
readonly tail: Schema.Int;
|
|
240
|
+
}>;
|
|
241
|
+
}>;
|
|
242
|
+
}>>, Schema.toCodecJson<typeof ServiceUnavailableError | typeof PtyNotFoundError>, never, never>, false>;
|
|
@@ -0,0 +1,92 @@
|
|
|
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/http-api";
|
|
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
|
+
export const PERSISTENT_PTY_CONNECT_PATH = "/api/experimental/persistent-pty/:ptyID/connect";
|
|
11
|
+
const PERSISTENT_PTY_CONNECT_URL = /^\/api\/experimental\/persistent-pty\/[^/]+\/connect$/;
|
|
12
|
+
export function hasPersistentPtyConnectTicketURL(url) {
|
|
13
|
+
return PERSISTENT_PTY_CONNECT_URL.test(url.pathname) && !!url.searchParams.get(PTY_CONNECT_TICKET_QUERY);
|
|
14
|
+
}
|
|
15
|
+
const errors = [InvalidRequestError, ServiceUnavailableError];
|
|
16
|
+
const terminalErrors = [PtyNotFoundError, ServiceUnavailableError];
|
|
17
|
+
export const PersistentPtyGroup = HttpApiGroup.make("server.experimental")
|
|
18
|
+
.add(HttpApiEndpoint.get("persistentPty.read", "/api/experimental/session/:sessionID/terminal/read", {
|
|
19
|
+
params: { sessionID: Session.ID },
|
|
20
|
+
query: {
|
|
21
|
+
lines: Schema.NumberFromString.pipe(Schema.decodeTo(PersistentPty.ReadLines), Schema.optional),
|
|
22
|
+
},
|
|
23
|
+
success: Schema.Struct({ data: Schema.NullOr(PersistentPty.ReadResult) }),
|
|
24
|
+
error: [ServiceUnavailableError],
|
|
25
|
+
}).annotateMerge(OpenApi.annotations({
|
|
26
|
+
summary: "Read the session's most recently controlled terminal",
|
|
27
|
+
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.",
|
|
28
|
+
})))
|
|
29
|
+
.add(HttpApiEndpoint.get("persistentPty.list", "/api/experimental/session/:sessionID/terminal", {
|
|
30
|
+
params: { sessionID: Session.ID },
|
|
31
|
+
success: Schema.Struct({ data: Schema.Array(PersistentPty.Info) }),
|
|
32
|
+
error: errors,
|
|
33
|
+
}))
|
|
34
|
+
.add(HttpApiEndpoint.post("persistentPty.create", "/api/experimental/session/:sessionID/terminal", {
|
|
35
|
+
params: { sessionID: Session.ID },
|
|
36
|
+
payload: PersistentPty.CreateInput,
|
|
37
|
+
success: Schema.Struct({ data: PersistentPty.Info }),
|
|
38
|
+
error: errors,
|
|
39
|
+
}))
|
|
40
|
+
.add(HttpApiEndpoint.post("persistentPty.shutdown", "/api/experimental/persistent-pty/shutdown", {
|
|
41
|
+
success: HttpApiSchema.NoContent,
|
|
42
|
+
error: [ServiceUnavailableError],
|
|
43
|
+
}))
|
|
44
|
+
.add(HttpApiEndpoint.post("persistentPty.handoff", "/api/experimental/persistent-pty/handoff", {
|
|
45
|
+
success: Schema.Struct({ handoff: Schema.NullOr(PersistentPty.Handoff) }),
|
|
46
|
+
error: [ServiceUnavailableError],
|
|
47
|
+
}))
|
|
48
|
+
.add(HttpApiEndpoint.get("persistentPty.get", "/api/experimental/persistent-pty/:ptyID", {
|
|
49
|
+
params: { ptyID: Pty.ID },
|
|
50
|
+
success: Schema.Struct({ data: PersistentPty.Info }),
|
|
51
|
+
error: terminalErrors,
|
|
52
|
+
}))
|
|
53
|
+
.add(HttpApiEndpoint.put("persistentPty.update", "/api/experimental/persistent-pty/:ptyID", {
|
|
54
|
+
params: { ptyID: Pty.ID },
|
|
55
|
+
payload: PersistentPty.UpdateInput,
|
|
56
|
+
success: Schema.Struct({ data: PersistentPty.Info }),
|
|
57
|
+
error: terminalErrors,
|
|
58
|
+
}))
|
|
59
|
+
.add(HttpApiEndpoint.get("persistentPty.snapshot", "/api/experimental/persistent-pty/:ptyID/snapshot", {
|
|
60
|
+
params: { ptyID: Pty.ID },
|
|
61
|
+
success: Schema.Struct({ data: PersistentPty.Snapshot }),
|
|
62
|
+
error: terminalErrors,
|
|
63
|
+
}))
|
|
64
|
+
.add(HttpApiEndpoint.delete("persistentPty.remove", "/api/experimental/persistent-pty/:ptyID", {
|
|
65
|
+
params: { ptyID: Pty.ID },
|
|
66
|
+
success: HttpApiSchema.NoContent,
|
|
67
|
+
error: terminalErrors,
|
|
68
|
+
}))
|
|
69
|
+
.add(HttpApiEndpoint.post("persistentPty.connectToken", "/api/experimental/persistent-pty/:ptyID/connect-token", {
|
|
70
|
+
params: { ptyID: Pty.ID },
|
|
71
|
+
headers: Schema.Struct({ [PTY_CONNECT_TOKEN_HEADER]: Schema.optional(Schema.String) }),
|
|
72
|
+
success: Schema.Struct({ data: PtyTicket.ConnectToken }),
|
|
73
|
+
error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError],
|
|
74
|
+
}))
|
|
75
|
+
.add(HttpApiEndpoint.get("persistentPty.connect", PERSISTENT_PTY_CONNECT_PATH, {
|
|
76
|
+
params: { ptyID: Pty.ID },
|
|
77
|
+
success: Schema.Boolean,
|
|
78
|
+
error: [ForbiddenError, PtyNotFoundError, ServiceUnavailableError],
|
|
79
|
+
}).annotateMerge(OpenApi.annotations({
|
|
80
|
+
identifier: "persistentPty.connect",
|
|
81
|
+
summary: "Connect to a persistent PTY",
|
|
82
|
+
description: "Stream persistent PTY output through the OpenCode server.",
|
|
83
|
+
transform: (operation) => ({
|
|
84
|
+
...operation,
|
|
85
|
+
"x-websocket": true,
|
|
86
|
+
parameters: [
|
|
87
|
+
...(operation.parameters ?? []),
|
|
88
|
+
...["cursor", "role", "attachment_id", "takeover", "input_protocol", PTY_CONNECT_TICKET_QUERY].map((name) => ({ in: "query", name, schema: { type: "string" } })),
|
|
89
|
+
],
|
|
90
|
+
}),
|
|
91
|
+
})))
|
|
92
|
+
.annotateMerge(OpenApi.annotations({ title: "persistentPty", description: "Prototype persistent PTY routes." }));
|