@sealant/sdk 0.5.1 → 0.7.0

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.
@@ -25,10 +25,11 @@ export declare const createWorkspaceOp: (payload: {
25
25
  readonly github?: string | undefined;
26
26
  } | undefined;
27
27
  readonly spec: unknown;
28
+ readonly ttlSeconds?: number | undefined;
28
29
  }, idempotencyKey?: string | undefined) => Effect.Effect<{
29
30
  readonly workspaceId: string;
30
31
  readonly name: string;
31
- readonly status: "cancelled" | "failed" | "queued" | "ready" | "running";
32
+ readonly status: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped";
32
33
  readonly registryId: string;
33
34
  readonly repository: string;
34
35
  readonly tag: string;
@@ -37,7 +38,7 @@ export declare const getWorkspaceOp: (workspaceId: string) => Effect.Effect<{
37
38
  readonly workspaceId: string;
38
39
  readonly name: string;
39
40
  readonly ownerUserId: string;
40
- readonly status: "cancelled" | "failed" | "queued" | "ready" | "running";
41
+ readonly status: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped";
41
42
  readonly registryId?: string | undefined;
42
43
  readonly repository?: string | undefined;
43
44
  readonly tag?: string | undefined;
@@ -61,18 +62,19 @@ export declare const getWorkspaceOp: (workspaceId: string) => Effect.Effect<{
61
62
  readonly updatedAt: string;
62
63
  readonly startedAt?: string | undefined;
63
64
  readonly finishedAt?: string | undefined;
65
+ readonly expiresAt?: string | undefined;
64
66
  readonly spec?: unknown;
65
67
  }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError, SealantApiClient>;
66
68
  export declare const listWorkspacesOp: (query: {
67
69
  readonly ownerUserId: string;
68
- readonly status?: "cancelled" | "failed" | "queued" | "ready" | "running" | undefined;
70
+ readonly status?: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped" | undefined;
69
71
  readonly limit?: string | undefined;
70
72
  }) => Effect.Effect<{
71
73
  readonly items: readonly {
72
74
  readonly workspaceId: string;
73
75
  readonly name: string;
74
76
  readonly ownerUserId: string;
75
- readonly status: "cancelled" | "failed" | "queued" | "ready" | "running";
77
+ readonly status: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped";
76
78
  readonly registryId?: string | undefined;
77
79
  readonly repository?: string | undefined;
78
80
  readonly tag?: string | undefined;
@@ -96,6 +98,7 @@ export declare const listWorkspacesOp: (query: {
96
98
  readonly updatedAt: string;
97
99
  readonly startedAt?: string | undefined;
98
100
  readonly finishedAt?: string | undefined;
101
+ readonly expiresAt?: string | undefined;
99
102
  }[];
100
103
  }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceInternalServerError, SealantApiClient>;
101
104
  export declare const execWorkspaceOp: (workspaceId: string, payload: {
@@ -114,6 +117,14 @@ export declare const execWorkspaceOp: (workspaceId: string, payload: {
114
117
  readonly mode: "interactive" | "one-shot";
115
118
  readonly status: "cancelled" | "completed" | "failed" | "queued" | "running";
116
119
  readonly prompt?: string | undefined;
120
+ readonly command?: {
121
+ readonly executable: string;
122
+ readonly args: readonly string[];
123
+ readonly cwd?: string | undefined;
124
+ } | undefined;
125
+ readonly metadata?: {
126
+ readonly [x: string]: unknown;
127
+ } | undefined;
117
128
  readonly exitCode?: number | undefined;
118
129
  readonly errorMessage?: string | undefined;
119
130
  readonly startedAt?: string | undefined;
@@ -121,6 +132,26 @@ export declare const execWorkspaceOp: (workspaceId: string, payload: {
121
132
  readonly createdAt: string;
122
133
  readonly updatedAt: string;
123
134
  }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceConflictError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError, SealantApiClient>;
135
+ export declare const stopWorkspaceOp: (workspaceId: string, payload: {
136
+ readonly ownerUserId: string;
137
+ }) => Effect.Effect<{
138
+ readonly workspaceId: string;
139
+ readonly status: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped";
140
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadGatewayError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceConflictError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError, SealantApiClient>;
141
+ export declare const restartWorkspaceOp: (workspaceId: string, payload: {
142
+ readonly ownerUserId: string;
143
+ }) => Effect.Effect<{
144
+ readonly workspaceId: string;
145
+ readonly runId: string;
146
+ readonly status: "cancelled" | "failed" | "queued" | "ready" | "running" | "stopped";
147
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadGatewayError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceConflictError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError, SealantApiClient>;
148
+ export declare const expireWorkspaceOp: (workspaceId: string, payload: {
149
+ readonly ownerUserId: string;
150
+ readonly ttlSeconds?: number | null | undefined;
151
+ }) => Effect.Effect<{
152
+ readonly workspaceId: string;
153
+ readonly expiresAt: string | null;
154
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").WorkspaceBadRequestError | import("@sealant/api-contracts").WorkspaceInternalServerError | import("@sealant/api-contracts").WorkspaceNotFoundError, SealantApiClient>;
124
155
  export declare const createRunOp: (payload: {
125
156
  readonly workspaceId: string;
126
157
  readonly harnessId: string;
@@ -133,6 +164,9 @@ export declare const createRunOp: (payload: {
133
164
  readonly args: readonly string[];
134
165
  readonly cwd?: string | undefined;
135
166
  } | undefined;
167
+ readonly metadata?: {
168
+ readonly [x: string]: unknown;
169
+ } | undefined;
136
170
  }) => Effect.Effect<{
137
171
  readonly runId: string;
138
172
  readonly workspaceId: string;
@@ -142,6 +176,14 @@ export declare const createRunOp: (payload: {
142
176
  readonly mode: "interactive" | "one-shot";
143
177
  readonly status: "cancelled" | "completed" | "failed" | "queued" | "running";
144
178
  readonly prompt?: string | undefined;
179
+ readonly command?: {
180
+ readonly executable: string;
181
+ readonly args: readonly string[];
182
+ readonly cwd?: string | undefined;
183
+ } | undefined;
184
+ readonly metadata?: {
185
+ readonly [x: string]: unknown;
186
+ } | undefined;
145
187
  readonly exitCode?: number | undefined;
146
188
  readonly errorMessage?: string | undefined;
147
189
  readonly startedAt?: string | undefined;
@@ -158,6 +200,14 @@ export declare const getRunOp: (runId: string) => Effect.Effect<{
158
200
  readonly mode: "interactive" | "one-shot";
159
201
  readonly status: "cancelled" | "completed" | "failed" | "queued" | "running";
160
202
  readonly prompt?: string | undefined;
203
+ readonly command?: {
204
+ readonly executable: string;
205
+ readonly args: readonly string[];
206
+ readonly cwd?: string | undefined;
207
+ } | undefined;
208
+ readonly metadata?: {
209
+ readonly [x: string]: unknown;
210
+ } | undefined;
161
211
  readonly exitCode?: number | undefined;
162
212
  readonly errorMessage?: string | undefined;
163
213
  readonly startedAt?: string | undefined;
@@ -180,6 +230,14 @@ export declare const listRunsOp: (query: {
180
230
  readonly mode: "interactive" | "one-shot";
181
231
  readonly status: "cancelled" | "completed" | "failed" | "queued" | "running";
182
232
  readonly prompt?: string | undefined;
233
+ readonly command?: {
234
+ readonly executable: string;
235
+ readonly args: readonly string[];
236
+ readonly cwd?: string | undefined;
237
+ } | undefined;
238
+ readonly metadata?: {
239
+ readonly [x: string]: unknown;
240
+ } | undefined;
183
241
  readonly exitCode?: number | undefined;
184
242
  readonly errorMessage?: string | undefined;
185
243
  readonly startedAt?: string | undefined;
@@ -207,6 +265,14 @@ export declare const updateRunOp: (runId: string, payload: {
207
265
  readonly mode: "interactive" | "one-shot";
208
266
  readonly status: "cancelled" | "completed" | "failed" | "queued" | "running";
209
267
  readonly prompt?: string | undefined;
268
+ readonly command?: {
269
+ readonly executable: string;
270
+ readonly args: readonly string[];
271
+ readonly cwd?: string | undefined;
272
+ } | undefined;
273
+ readonly metadata?: {
274
+ readonly [x: string]: unknown;
275
+ } | undefined;
210
276
  readonly exitCode?: number | undefined;
211
277
  readonly errorMessage?: string | undefined;
212
278
  readonly startedAt?: string | undefined;
@@ -232,11 +298,13 @@ export declare const getRunTimelineOp: (runId: string, query: {
232
298
  }[], import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
233
299
  export declare const getRunScrollbackOp: (runId: string, query: {
234
300
  readonly processId: string;
235
- readonly stream: "stderr" | "stdout";
301
+ readonly stream: "pty" | "stderr" | "stdout";
236
302
  readonly atSequence?: string | undefined;
303
+ readonly fromSequence?: string | undefined;
304
+ readonly limit?: string | undefined;
237
305
  }) => Effect.Effect<{
238
306
  readonly processId: string;
239
- readonly stream: "stderr" | "stdout";
307
+ readonly stream: "pty" | "stderr" | "stdout";
240
308
  readonly byteCount: number;
241
309
  readonly contentBase64: string;
242
310
  }, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunBadRequestError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
@@ -263,6 +331,158 @@ export declare const getRunChangesOp: (runId: string) => Effect.Effect<{
263
331
  }[];
264
332
  readonly diff: string;
265
333
  }, import("effect/unstable/http/HttpClientError").HttpClientError | import("@sealant/api-contracts").RunInternalServerError | import("@sealant/api-contracts").RunNotFoundError | import("effect/Schema").SchemaError, SealantApiClient>;
334
+ export declare const createSessionOp: (payload: {
335
+ readonly workspaceId: string;
336
+ readonly ownerUserId: string;
337
+ readonly argv: readonly string[];
338
+ readonly cwd?: string | undefined;
339
+ readonly env?: {
340
+ readonly [x: string]: string;
341
+ } | undefined;
342
+ readonly cols?: number | undefined;
343
+ readonly rows?: number | undefined;
344
+ readonly term?: string | undefined;
345
+ readonly metadata?: {
346
+ readonly [x: string]: unknown;
347
+ } | undefined;
348
+ }) => Effect.Effect<{
349
+ readonly sessionId: string;
350
+ readonly workspaceId: string;
351
+ readonly runId: string;
352
+ readonly ownerUserId: string;
353
+ readonly status: "exited" | "failed" | "running" | "starting";
354
+ readonly argv: readonly string[];
355
+ readonly cwd?: string | undefined;
356
+ readonly cols: number;
357
+ readonly rows: number;
358
+ readonly exitCode?: number | undefined;
359
+ readonly exitSignal?: number | undefined;
360
+ readonly errorMessage?: string | undefined;
361
+ readonly metadata?: {
362
+ readonly [x: string]: unknown;
363
+ } | undefined;
364
+ readonly outputHighWater: string;
365
+ readonly createdAt: string;
366
+ readonly endedAt?: string | undefined;
367
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadGatewayError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionConflictError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
368
+ export declare const getSessionOp: (sessionId: string, ownerUserId?: string | undefined) => Effect.Effect<{
369
+ readonly sessionId: string;
370
+ readonly workspaceId: string;
371
+ readonly runId: string;
372
+ readonly ownerUserId: string;
373
+ readonly status: "exited" | "failed" | "running" | "starting";
374
+ readonly argv: readonly string[];
375
+ readonly cwd?: string | undefined;
376
+ readonly cols: number;
377
+ readonly rows: number;
378
+ readonly exitCode?: number | undefined;
379
+ readonly exitSignal?: number | undefined;
380
+ readonly errorMessage?: string | undefined;
381
+ readonly metadata?: {
382
+ readonly [x: string]: unknown;
383
+ } | undefined;
384
+ readonly outputHighWater: string;
385
+ readonly createdAt: string;
386
+ readonly endedAt?: string | undefined;
387
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
388
+ export declare const listSessionsOp: (query: {
389
+ readonly ownerUserId: string;
390
+ readonly workspaceId?: string | undefined;
391
+ readonly status?: "exited" | "failed" | "running" | "starting" | undefined;
392
+ readonly limit?: string | undefined;
393
+ }) => Effect.Effect<{
394
+ readonly items: readonly {
395
+ readonly sessionId: string;
396
+ readonly workspaceId: string;
397
+ readonly runId: string;
398
+ readonly ownerUserId: string;
399
+ readonly status: "exited" | "failed" | "running" | "starting";
400
+ readonly argv: readonly string[];
401
+ readonly cwd?: string | undefined;
402
+ readonly cols: number;
403
+ readonly rows: number;
404
+ readonly exitCode?: number | undefined;
405
+ readonly exitSignal?: number | undefined;
406
+ readonly errorMessage?: string | undefined;
407
+ readonly metadata?: {
408
+ readonly [x: string]: unknown;
409
+ } | undefined;
410
+ readonly outputHighWater: string;
411
+ readonly createdAt: string;
412
+ readonly endedAt?: string | undefined;
413
+ }[];
414
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
415
+ export declare const getSessionOutputOp: (sessionId: string, query: {
416
+ readonly ownerUserId?: string | undefined;
417
+ readonly from?: string | undefined;
418
+ readonly limit?: string | undefined;
419
+ }) => Effect.Effect<{
420
+ readonly sessionId: string;
421
+ readonly chunks: readonly {
422
+ readonly sequence: string;
423
+ readonly dataBase64: string;
424
+ }[];
425
+ readonly nextFrom: string;
426
+ readonly status: "exited" | "failed" | "running" | "starting";
427
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
428
+ export declare const sendSessionInputOp: (sessionId: string, payload: {
429
+ readonly ownerUserId?: string | undefined;
430
+ readonly dataBase64: string;
431
+ }) => Effect.Effect<{
432
+ readonly ok: boolean;
433
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadGatewayError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionConflictError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
434
+ export declare const resizeSessionOp: (sessionId: string, payload: {
435
+ readonly ownerUserId?: string | undefined;
436
+ readonly cols: number;
437
+ readonly rows: number;
438
+ }) => Effect.Effect<{
439
+ readonly ok: boolean;
440
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadGatewayError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionConflictError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
441
+ export declare const signalSessionOp: (sessionId: string, payload: {
442
+ readonly ownerUserId?: string | undefined;
443
+ readonly signal: number;
444
+ }) => Effect.Effect<{
445
+ readonly ok: boolean;
446
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadGatewayError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionConflictError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
447
+ export declare const closeSessionOp: (sessionId: string, payload: {
448
+ readonly ownerUserId?: string | undefined;
449
+ }) => Effect.Effect<{
450
+ readonly sessionId: string;
451
+ readonly workspaceId: string;
452
+ readonly runId: string;
453
+ readonly ownerUserId: string;
454
+ readonly status: "exited" | "failed" | "running" | "starting";
455
+ readonly argv: readonly string[];
456
+ readonly cwd?: string | undefined;
457
+ readonly cols: number;
458
+ readonly rows: number;
459
+ readonly exitCode?: number | undefined;
460
+ readonly exitSignal?: number | undefined;
461
+ readonly errorMessage?: string | undefined;
462
+ readonly metadata?: {
463
+ readonly [x: string]: unknown;
464
+ } | undefined;
465
+ readonly outputHighWater: string;
466
+ readonly createdAt: string;
467
+ readonly endedAt?: string | undefined;
468
+ }, import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError | import("@sealant/api-contracts").SessionBadGatewayError | import("@sealant/api-contracts").SessionBadRequestError | import("@sealant/api-contracts").SessionForbiddenError | import("@sealant/api-contracts").SessionInternalServerError | import("@sealant/api-contracts").SessionNotFoundError | import("@sealant/api-contracts").SessionUnauthorizedError, SealantApiClient>;
469
+ export declare const createAccessTokenOp: (payload: {
470
+ readonly ownerUserId: string;
471
+ readonly scopes: readonly ("session:input" | "session:read" | "workspace:exec")[];
472
+ readonly name?: string | undefined;
473
+ readonly workspaceId?: string | undefined;
474
+ readonly ttlSeconds?: number | undefined;
475
+ }) => Effect.Effect<{
476
+ readonly tokenId: string;
477
+ readonly ownerUserId: string;
478
+ readonly name?: string | undefined;
479
+ readonly scopes: readonly ("session:input" | "session:read" | "workspace:exec")[];
480
+ readonly workspaceId?: string | undefined;
481
+ readonly expiresAt?: string | undefined;
482
+ readonly revokedAt?: string | undefined;
483
+ readonly createdAt: string;
484
+ readonly token: string;
485
+ }, import("@sealant/api-contracts").AccessTokenBadRequestError | import("@sealant/api-contracts").AccessTokenInternalServerError | import("@sealant/api-contracts").AccessTokenNotFoundError | import("effect/unstable/http/HttpClientError").HttpClientError | import("effect/Schema").SchemaError, SealantApiClient>;
266
486
  export declare const inferenceRespondOp: (payload: {
267
487
  readonly ownerUserId: string;
268
488
  readonly credentials?: {
@@ -8,6 +8,9 @@ export const createWorkspaceOp = (payload, idempotencyKey) => Effect.flatMap(Sea
8
8
  export const getWorkspaceOp = (workspaceId) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.getWorkspace({ params: { workspaceId } }));
9
9
  export const listWorkspacesOp = (query) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.listWorkspaces({ query }));
10
10
  export const execWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.execWorkspace({ params: { workspaceId }, payload }));
11
+ export const stopWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.stopWorkspace({ params: { workspaceId }, payload }));
12
+ export const restartWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.restartWorkspace({ params: { workspaceId }, payload }));
13
+ export const expireWorkspaceOp = (workspaceId, payload) => Effect.flatMap(SealantApiClient, (client) => client.workspaces.expireWorkspace({ params: { workspaceId }, payload }));
11
14
  // ---- runs ----
12
15
  export const createRunOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.runs.createRun({ payload }));
13
16
  export const getRunOp = (runId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRun({ params: { runId } }));
@@ -17,5 +20,20 @@ export const getRunTimelineOp = (runId, query) => Effect.flatMap(SealantApiClien
17
20
  export const getRunScrollbackOp = (runId, query) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunScrollback({ params: { runId }, query }));
18
21
  export const getRunLossOp = (runId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunLoss({ params: { runId } }));
19
22
  export const getRunChangesOp = (runId) => Effect.flatMap(SealantApiClient, (client) => client.runs.getRunChanges({ params: { runId } }));
23
+ // ---- sessions ----
24
+ export const createSessionOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.createSession({ payload, headers: {} }));
25
+ export const getSessionOp = (sessionId, ownerUserId) => Effect.flatMap(SealantApiClient, (client) => client.sessions.getSession({
26
+ params: { sessionId },
27
+ headers: {},
28
+ query: ownerUserId === undefined ? {} : { ownerUserId },
29
+ }));
30
+ export const listSessionsOp = (query) => Effect.flatMap(SealantApiClient, (client) => client.sessions.listSessions({ query, headers: {} }));
31
+ export const getSessionOutputOp = (sessionId, query) => Effect.flatMap(SealantApiClient, (client) => client.sessions.getSessionOutput({ params: { sessionId }, headers: {}, query }));
32
+ export const sendSessionInputOp = (sessionId, payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.sendSessionInput({ params: { sessionId }, headers: {}, payload }));
33
+ export const resizeSessionOp = (sessionId, payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.resizeSession({ params: { sessionId }, headers: {}, payload }));
34
+ export const signalSessionOp = (sessionId, payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.signalSession({ params: { sessionId }, headers: {}, payload }));
35
+ export const closeSessionOp = (sessionId, payload) => Effect.flatMap(SealantApiClient, (client) => client.sessions.closeSession({ params: { sessionId }, headers: {}, payload }));
36
+ // ---- access tokens ----
37
+ export const createAccessTokenOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.accessTokens.createAccessToken({ payload }));
20
38
  // ---- inference ----
21
39
  export const inferenceRespondOp = (payload) => Effect.flatMap(SealantApiClient, (client) => client.inference.respond({ payload }));
@@ -11,31 +11,50 @@
11
11
  import { Effect } from "effect";
12
12
  import { SealantError } from "../errors.js";
13
13
  import { makeRun, toRunChangesData } from "../facade/run.js";
14
- import { createRunOp, getRunChangesOp, getRunOp } from "./operations.js";
14
+ import { createRunOp, getRunChangesOp, getRunOp, getWorkspaceOp } from "./operations.js";
15
15
  const TERMINAL_STATUSES = new Set(["completed", "failed", "cancelled"]);
16
16
  const POLL_INTERVAL = "500 millis";
17
17
  const RUN_TIMEOUT_MS = 30 * 60 * 1_000;
18
18
  /**
19
- * Registers the run WITH the harness command — the control plane executes it server-side (the worker
20
- * docker-execs it and ingests telemetry). The cwd is the workspace repo, which the worker defaults to.
19
+ * Registers the run — the control plane executes it server-side (the worker docker-execs it and
20
+ * ingests telemetry). With a client harness value (the creating handle), the client-built command
21
+ * rides along — the escape hatch that keeps `customHarness()` working. WITHOUT one (a handle from
22
+ * `workspaces.get(id)`), the harness id is read from the workspace's own spec and NO command is
23
+ * sent: the control plane constructs the invocation server-side, so re-fetched handles are fully
24
+ * capable.
21
25
  */
22
- const createHarnessRunEffect = (ctx, init, prompt) => Effect.gen(function* () {
26
+ const createHarnessRunEffect = (ctx, init, prompt, options) => Effect.gen(function* () {
27
+ const metadata = options?.metadata === undefined ? {} : { metadata: { ...options.metadata } };
23
28
  const harness = init.harness;
24
- if (harness === undefined) {
25
- return yield* Effect.fail(new SealantError("This workspace handle has no harness; use the handle returned by workspaces.create().", { code: "harness_required" }));
29
+ if (harness !== undefined) {
30
+ const command = harness.buildRunCommand(prompt);
31
+ return yield* createRunOp({
32
+ workspaceId: init.id,
33
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
34
+ harnessId: harness.id,
35
+ mode: "one-shot",
36
+ prompt,
37
+ command: { executable: command.executable, args: [...command.args] },
38
+ ...metadata,
39
+ });
40
+ }
41
+ const details = yield* getWorkspaceOp(init.id);
42
+ const spec = details.spec;
43
+ const harnessId = spec?.harness?.id;
44
+ if (harnessId === undefined) {
45
+ return yield* Effect.fail(new SealantError(`Workspace ${init.id} has no harness in its spec; pass a harness at workspaces.create() or use workspace.exec().`, { code: "harness_required" }));
26
46
  }
27
- const command = harness.buildRunCommand(prompt);
28
47
  return yield* createRunOp({
29
48
  workspaceId: init.id,
30
49
  ownerUserId: ctx.config.hostLocal.ownerUserId,
31
- harnessId: harness.id,
50
+ harnessId,
32
51
  mode: "one-shot",
33
52
  prompt,
34
- command: { executable: command.executable, args: [...command.args] },
53
+ ...metadata,
35
54
  });
36
55
  });
37
- const runHarnessEffect = (ctx, init, prompt) => Effect.gen(function* () {
38
- const created = yield* createHarnessRunEffect(ctx, init, prompt);
56
+ const runHarnessEffect = (ctx, init, prompt, options) => Effect.gen(function* () {
57
+ const created = yield* createHarnessRunEffect(ctx, init, prompt, options);
39
58
  const runId = created.runId;
40
59
  // Block until the run is terminal, polling the control plane.
41
60
  const deadline = Date.now() + RUN_TIMEOUT_MS;
@@ -54,10 +73,10 @@ const runHarnessEffect = (ctx, init, prompt) => Effect.gen(function* () {
54
73
  return makeRun(ctx, { wire, changes });
55
74
  });
56
75
  /** The BLOCKING `harness.run()` implementation, registered into the Workspace facade by the client. */
57
- export const runHarness = (ctx, init, prompt) => ctx.runtime.run(runHarnessEffect(ctx, init, prompt));
76
+ export const runHarness = (ctx, init, prompt, options) => ctx.runtime.run(runHarnessEffect(ctx, init, prompt, options));
58
77
  /**
59
78
  * The NON-BLOCKING `harness.start()` implementation: register the run and return the live handle
60
79
  * immediately. Callers stream progress via `run.record.stream()` and settle via `run.wait()` (which
61
80
  * fetches the captured changes once terminal).
62
81
  */
63
- export const startHarness = (ctx, init, prompt) => ctx.runtime.run(Effect.map(createHarnessRunEffect(ctx, init, prompt), (created) => makeRun(ctx, { wire: created })));
82
+ export const startHarness = (ctx, init, prompt, options) => ctx.runtime.run(Effect.map(createHarnessRunEffect(ctx, init, prompt, options), (created) => makeRun(ctx, { wire: created })));
@@ -0,0 +1,22 @@
1
+ import type { InteractiveSession } from "../types.js";
2
+ import type { SdkContext } from "./context.js";
3
+ export declare const makeInteractiveSession: (ctx: SdkContext, wire: {
4
+ readonly sessionId: string;
5
+ readonly workspaceId: string;
6
+ readonly runId: string;
7
+ readonly ownerUserId: string;
8
+ readonly status: "exited" | "failed" | "running" | "starting";
9
+ readonly argv: readonly string[];
10
+ readonly cwd?: string | undefined;
11
+ readonly cols: number;
12
+ readonly rows: number;
13
+ readonly exitCode?: number | undefined;
14
+ readonly exitSignal?: number | undefined;
15
+ readonly errorMessage?: string | undefined;
16
+ readonly metadata?: {
17
+ readonly [x: string]: unknown;
18
+ } | undefined;
19
+ readonly outputHighWater: string;
20
+ readonly createdAt: string;
21
+ readonly endedAt?: string | undefined;
22
+ }) => InteractiveSession;
@@ -0,0 +1,171 @@
1
+ import { closeSessionOp, getSessionOp, getSessionOutputOp, resizeSessionOp, sendSessionInputOp, signalSessionOp, } from "../effect/operations.js";
2
+ const OUTPUT_POLL_INTERVAL_MS = 250;
3
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4
+ const decodeBase64 = (value) => {
5
+ const buffer = Buffer.from(value, "base64");
6
+ return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
7
+ };
8
+ /** Parse one SSE frame block ("event: ...\nid: ...\ndata: ...") into its fields. */
9
+ const parseSseFrame = (block) => {
10
+ let event = "message";
11
+ let id;
12
+ const dataLines = [];
13
+ for (const line of block.split("\n")) {
14
+ if (line.startsWith(":")) {
15
+ continue; // comment / keep-alive
16
+ }
17
+ if (line.startsWith("event:")) {
18
+ event = line.slice(6).trim();
19
+ }
20
+ else if (line.startsWith("id:")) {
21
+ id = line.slice(3).trim();
22
+ }
23
+ else if (line.startsWith("data:")) {
24
+ dataLines.push(line.slice(5).trimStart());
25
+ }
26
+ }
27
+ if (dataLines.length === 0 && id === undefined) {
28
+ return undefined;
29
+ }
30
+ return { event, id, data: dataLines.join("\n") };
31
+ };
32
+ /** Stream the session's output over SSE. Throws on transport failure (caller falls back). */
33
+ async function* streamOverSse(ctx, sessionId, from, signal) {
34
+ const config = ctx.config;
35
+ const fetchImpl = config.fetch ?? fetch;
36
+ const url = new URL(`/v1/sessions/${sessionId}/output/stream`, config.baseUrl);
37
+ url.searchParams.set("from", from.toString());
38
+ if (config.apiKey === undefined) {
39
+ url.searchParams.set("ownerUserId", config.hostLocal.ownerUserId);
40
+ }
41
+ const response = await fetchImpl(url, {
42
+ headers: {
43
+ accept: "text/event-stream",
44
+ ...(config.apiKey === undefined ? {} : { authorization: `Bearer ${config.apiKey}` }),
45
+ },
46
+ ...(signal === undefined ? {} : { signal }),
47
+ });
48
+ if (!response.ok || response.body === null) {
49
+ throw new Error(`session output stream failed: HTTP ${response.status}`);
50
+ }
51
+ const reader = response.body.getReader();
52
+ const decoder = new TextDecoder();
53
+ let buffered = "";
54
+ try {
55
+ for (;;) {
56
+ const { done, value } = await reader.read();
57
+ if (done) {
58
+ return;
59
+ }
60
+ buffered += decoder.decode(value, { stream: true });
61
+ for (;;) {
62
+ const boundary = buffered.indexOf("\n\n");
63
+ if (boundary === -1) {
64
+ break;
65
+ }
66
+ const block = buffered.slice(0, boundary);
67
+ buffered = buffered.slice(boundary + 2);
68
+ const frame = parseSseFrame(block);
69
+ if (frame === undefined) {
70
+ continue;
71
+ }
72
+ if (frame.event === "end") {
73
+ return;
74
+ }
75
+ if (frame.event === "output" && frame.id !== undefined) {
76
+ yield { sequence: BigInt(frame.id), data: decodeBase64(frame.data) };
77
+ }
78
+ }
79
+ }
80
+ }
81
+ finally {
82
+ await reader.cancel().catch(() => undefined);
83
+ }
84
+ }
85
+ /** Poll-backed fallback: page the range endpoint until the session settles. */
86
+ async function* streamByPolling(ctx, sessionId, from, signal) {
87
+ let cursor = from;
88
+ for (;;) {
89
+ if (signal?.aborted === true) {
90
+ return;
91
+ }
92
+ const page = await ctx.runtime.run(getSessionOutputOp(sessionId, {
93
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
94
+ from: cursor.toString(),
95
+ }));
96
+ for (const chunk of page.chunks) {
97
+ yield { sequence: BigInt(chunk.sequence), data: decodeBase64(chunk.dataBase64) };
98
+ }
99
+ cursor = BigInt(page.nextFrom);
100
+ if (page.chunks.length === 0) {
101
+ if (page.status === "exited" || page.status === "failed") {
102
+ return;
103
+ }
104
+ await delay(OUTPUT_POLL_INTERVAL_MS);
105
+ }
106
+ }
107
+ }
108
+ export const makeInteractiveSession = (ctx, wire) => {
109
+ const sessionId = wire.sessionId;
110
+ return {
111
+ id: sessionId,
112
+ workspaceId: wire.workspaceId,
113
+ runId: wire.runId,
114
+ send: async (input) => {
115
+ const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
116
+ await ctx.runtime.run(sendSessionInputOp(sessionId, {
117
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
118
+ dataBase64: Buffer.from(bytes).toString("base64"),
119
+ }));
120
+ },
121
+ output: (options) => {
122
+ const from = options?.from ?? 0n;
123
+ const signal = options?.signal;
124
+ async function* iterate() {
125
+ let cursor = from;
126
+ try {
127
+ for await (const chunk of streamOverSse(ctx, sessionId, cursor, signal)) {
128
+ yield chunk;
129
+ cursor = chunk.sequence + 1n;
130
+ }
131
+ return;
132
+ }
133
+ catch (error) {
134
+ if (signal?.aborted === true) {
135
+ return;
136
+ }
137
+ // SSE transport unavailable (proxy, fetch impl, older server) — fall back to polling
138
+ // from the cursor; the sequence keying makes the switch seamless.
139
+ void error;
140
+ }
141
+ yield* streamByPolling(ctx, sessionId, cursor, signal);
142
+ }
143
+ return iterate();
144
+ },
145
+ resize: async (cols, rows) => {
146
+ await ctx.runtime.run(resizeSessionOp(sessionId, {
147
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
148
+ cols,
149
+ rows,
150
+ }));
151
+ },
152
+ signal: async (signalNumber) => {
153
+ await ctx.runtime.run(signalSessionOp(sessionId, {
154
+ ownerUserId: ctx.config.hostLocal.ownerUserId,
155
+ signal: signalNumber,
156
+ }));
157
+ },
158
+ status: async () => {
159
+ const current = await ctx.runtime.run(getSessionOp(sessionId, ctx.config.hostLocal.ownerUserId));
160
+ return {
161
+ status: current.status,
162
+ ...(current.exitCode === undefined ? {} : { exitCode: current.exitCode }),
163
+ ...(current.exitSignal === undefined ? {} : { exitSignal: current.exitSignal }),
164
+ outputHighWater: BigInt(current.outputHighWater),
165
+ };
166
+ },
167
+ close: async () => {
168
+ await ctx.runtime.run(closeSessionOp(sessionId, { ownerUserId: ctx.config.hostLocal.ownerUserId }));
169
+ },
170
+ };
171
+ };