@agentuity/core 2.0.11 → 2.0.13
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/services/api.d.ts +1 -1
- package/dist/services/api.d.ts.map +1 -1
- package/dist/services/api.js +4 -6
- package/dist/services/api.js.map +1 -1
- package/dist/services/coder/agents.d.ts +6 -4
- package/dist/services/coder/agents.d.ts.map +1 -1
- package/dist/services/coder/api-reference.d.ts.map +1 -1
- package/dist/services/coder/api-reference.js +78 -10
- package/dist/services/coder/api-reference.js.map +1 -1
- package/dist/services/coder/client.d.ts +17 -1
- package/dist/services/coder/client.d.ts.map +1 -1
- package/dist/services/coder/client.js +30 -2
- package/dist/services/coder/client.js.map +1 -1
- package/dist/services/coder/index.d.ts +2 -2
- package/dist/services/coder/index.d.ts.map +1 -1
- package/dist/services/coder/index.js +1 -1
- package/dist/services/coder/index.js.map +1 -1
- package/dist/services/coder/protocol.d.ts +385 -15
- package/dist/services/coder/protocol.d.ts.map +1 -1
- package/dist/services/coder/protocol.js +148 -2
- package/dist/services/coder/protocol.js.map +1 -1
- package/dist/services/coder/sessions.d.ts +24 -2
- package/dist/services/coder/sessions.d.ts.map +1 -1
- package/dist/services/coder/sessions.js +10 -1
- package/dist/services/coder/sessions.js.map +1 -1
- package/dist/services/coder/sse.d.ts +2 -2
- package/dist/services/coder/sse.d.ts.map +1 -1
- package/dist/services/coder/sse.js +290 -178
- package/dist/services/coder/sse.js.map +1 -1
- package/dist/services/coder/types.d.ts +680 -42
- package/dist/services/coder/types.d.ts.map +1 -1
- package/dist/services/coder/types.js +284 -40
- package/dist/services/coder/types.js.map +1 -1
- package/dist/services/coder/websocket.d.ts +13 -1
- package/dist/services/coder/websocket.d.ts.map +1 -1
- package/dist/services/coder/websocket.js +91 -19
- package/dist/services/coder/websocket.js.map +1 -1
- package/dist/services/coder/workspaces.d.ts +11 -1
- package/dist/services/coder/workspaces.d.ts.map +1 -1
- package/dist/services/coder/workspaces.js +34 -1
- package/dist/services/coder/workspaces.js.map +1 -1
- package/dist/services/sandbox/api-reference.js +8 -8
- package/dist/services/sandbox/api-reference.js.map +1 -1
- package/dist/services/sandbox/client.d.ts +3 -2
- package/dist/services/sandbox/client.d.ts.map +1 -1
- package/dist/services/sandbox/client.js.map +1 -1
- package/dist/services/sandbox/create.d.ts +5 -0
- package/dist/services/sandbox/create.d.ts.map +1 -1
- package/dist/services/sandbox/create.js +8 -0
- package/dist/services/sandbox/create.js.map +1 -1
- package/dist/services/sandbox/get.d.ts +8 -4
- package/dist/services/sandbox/get.d.ts.map +1 -1
- package/dist/services/sandbox/get.js +28 -3
- package/dist/services/sandbox/get.js.map +1 -1
- package/dist/services/sandbox/getStatus.d.ts +2 -0
- package/dist/services/sandbox/getStatus.d.ts.map +1 -1
- package/dist/services/sandbox/getStatus.js +17 -1
- package/dist/services/sandbox/getStatus.js.map +1 -1
- package/dist/services/sandbox/index.d.ts +1 -1
- package/dist/services/sandbox/index.d.ts.map +1 -1
- package/dist/services/sandbox/list.d.ts +3 -0
- package/dist/services/sandbox/list.d.ts.map +1 -1
- package/dist/services/sandbox/list.js +5 -0
- package/dist/services/sandbox/list.js.map +1 -1
- package/dist/services/sandbox/pause.d.ts +17 -1
- package/dist/services/sandbox/pause.d.ts.map +1 -1
- package/dist/services/sandbox/pause.js +21 -3
- package/dist/services/sandbox/pause.js.map +1 -1
- package/dist/services/sandbox/run.d.ts +3 -2
- package/dist/services/sandbox/run.d.ts.map +1 -1
- package/dist/services/sandbox/run.js +145 -85
- package/dist/services/sandbox/run.js.map +1 -1
- package/dist/services/sandbox/types.d.ts +11 -4
- package/dist/services/sandbox/types.d.ts.map +1 -1
- package/dist/services/sandbox/types.js +12 -0
- package/dist/services/sandbox/types.js.map +1 -1
- package/dist/services/stream/namespaces.d.ts +2 -2
- package/dist/services/stream/namespaces.js +2 -2
- package/dist/services/stream/namespaces.js.map +1 -1
- package/package.json +2 -2
- package/src/services/api.ts +6 -7
- package/src/services/coder/api-reference.ts +79 -9
- package/src/services/coder/client.ts +46 -0
- package/src/services/coder/index.ts +3 -0
- package/src/services/coder/protocol.ts +166 -2
- package/src/services/coder/sessions.ts +26 -0
- package/src/services/coder/sse.ts +343 -184
- package/src/services/coder/types.ts +350 -44
- package/src/services/coder/websocket.ts +120 -21
- package/src/services/coder/workspaces.ts +74 -0
- package/src/services/sandbox/api-reference.ts +8 -8
- package/src/services/sandbox/client.ts +4 -4
- package/src/services/sandbox/create.ts +10 -0
- package/src/services/sandbox/get.ts +32 -3
- package/src/services/sandbox/getStatus.ts +20 -1
- package/src/services/sandbox/index.ts +1 -1
- package/src/services/sandbox/list.ts +5 -0
- package/src/services/sandbox/pause.ts +38 -4
- package/src/services/sandbox/run.ts +202 -108
- package/src/services/sandbox/types.ts +17 -2
- package/src/services/stream/namespaces.ts +2 -2
|
@@ -70,7 +70,7 @@ import type {
|
|
|
70
70
|
ConnectionParams,
|
|
71
71
|
ServerMessage,
|
|
72
72
|
} from './protocol.ts';
|
|
73
|
-
import { CoderHubInitMessageSchema } from './protocol.ts';
|
|
73
|
+
import { CoderHubInitMessageSchema, parseServerMessage } from './protocol.ts';
|
|
74
74
|
import { normalizeCoderUrl } from './util.ts';
|
|
75
75
|
|
|
76
76
|
/**
|
|
@@ -118,6 +118,11 @@ export const CoderHubWebSocketOptionsSchema = z.object({
|
|
|
118
118
|
task: z.string().optional().describe('Initial task for driver mode'),
|
|
119
119
|
/** Human-readable session label */
|
|
120
120
|
label: z.string().optional().describe('Session label'),
|
|
121
|
+
/** Observer event filters to request during the initial connection. */
|
|
122
|
+
subscribe: z
|
|
123
|
+
.array(z.string())
|
|
124
|
+
.optional()
|
|
125
|
+
.describe('Observer event filters to request during connection setup'),
|
|
121
126
|
/** Client origin (web, desktop, tui, sdk) */
|
|
122
127
|
origin: z.enum(['web', 'desktop', 'tui', 'sdk']).optional().describe('Client origin'),
|
|
123
128
|
/** Driver mode: 'rpc' for RPC bridge driver */
|
|
@@ -198,7 +203,13 @@ export const CoderHubWebSocketError = StructuredError('CoderHubWebSocketError')<
|
|
|
198
203
|
| 'response_timeout'
|
|
199
204
|
| 'invalid_response';
|
|
200
205
|
sessionId?: string;
|
|
206
|
+
serverCode?: string;
|
|
207
|
+
serverMessage?: string;
|
|
208
|
+
serverMessageType?: 'connection_rejected' | 'protocol_error';
|
|
209
|
+
closeCode?: number;
|
|
210
|
+
closeReason?: string;
|
|
201
211
|
}>();
|
|
212
|
+
export type CoderHubWebSocketErrorInstance = InstanceType<typeof CoderHubWebSocketError>;
|
|
202
213
|
|
|
203
214
|
interface PendingRequest {
|
|
204
215
|
resolve: (response: CoderHubResponse) => void;
|
|
@@ -270,6 +281,7 @@ export class CoderHubWebSocketClient {
|
|
|
270
281
|
parentSessionId: string;
|
|
271
282
|
task: string;
|
|
272
283
|
label: string;
|
|
284
|
+
subscribe: string[];
|
|
273
285
|
origin: 'web' | 'desktop' | 'tui' | 'sdk';
|
|
274
286
|
driverMode: 'rpc' | undefined;
|
|
275
287
|
driverInstanceId: string;
|
|
@@ -317,6 +329,7 @@ export class CoderHubWebSocketClient {
|
|
|
317
329
|
parentSessionId: options.parentSessionId ?? '',
|
|
318
330
|
task: options.task ?? '',
|
|
319
331
|
label: options.label ?? '',
|
|
332
|
+
subscribe: options.subscribe ?? [],
|
|
320
333
|
origin: options.origin ?? 'sdk',
|
|
321
334
|
driverMode: options.driverMode,
|
|
322
335
|
driverInstanceId: options.driverInstanceId ?? '',
|
|
@@ -522,6 +535,57 @@ export class CoderHubWebSocketClient {
|
|
|
522
535
|
return `${Date.now()}-${++this.#messageId}`;
|
|
523
536
|
}
|
|
524
537
|
|
|
538
|
+
#buildHandshakeError(input: {
|
|
539
|
+
code: 'auth_failed' | 'connection_error';
|
|
540
|
+
message: string;
|
|
541
|
+
serverCode?: string;
|
|
542
|
+
serverMessage?: string;
|
|
543
|
+
serverMessageType?: 'connection_rejected' | 'protocol_error';
|
|
544
|
+
closeCode?: number;
|
|
545
|
+
closeReason?: string;
|
|
546
|
+
}): CoderHubWebSocketErrorInstance {
|
|
547
|
+
return new CoderHubWebSocketError({
|
|
548
|
+
code: input.code,
|
|
549
|
+
message: input.message,
|
|
550
|
+
sessionId: this.sessionId,
|
|
551
|
+
serverCode: input.serverCode,
|
|
552
|
+
serverMessage: input.serverMessage,
|
|
553
|
+
serverMessageType: input.serverMessageType,
|
|
554
|
+
closeCode: input.closeCode,
|
|
555
|
+
closeReason: input.closeReason,
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
#markReady(input?: {
|
|
560
|
+
initMessage?: CoderHubInitMessage;
|
|
561
|
+
firstMessage?: ServerMessage;
|
|
562
|
+
sendBootstrapReady?: boolean;
|
|
563
|
+
}): void {
|
|
564
|
+
this.#authenticated = true;
|
|
565
|
+
this.#initMessage = input?.initMessage ?? null;
|
|
566
|
+
this.#sessionId =
|
|
567
|
+
input?.initMessage?.sessionId ??
|
|
568
|
+
(input?.firstMessage && 'sessionId' in input.firstMessage
|
|
569
|
+
? input.firstMessage.sessionId
|
|
570
|
+
: undefined) ??
|
|
571
|
+
this.#options.sessionId ??
|
|
572
|
+
null;
|
|
573
|
+
this.#reconnectAttempts = 0;
|
|
574
|
+
this.#setState('connected');
|
|
575
|
+
this.#startHeartbeat();
|
|
576
|
+
if (input?.initMessage) {
|
|
577
|
+
this.#options.onInit(input.initMessage);
|
|
578
|
+
}
|
|
579
|
+
if (input?.sendBootstrapReady && this.#ws?.readyState === WebSocket.OPEN) {
|
|
580
|
+
this.#ws.send(JSON.stringify({ type: 'bootstrap_ready' }));
|
|
581
|
+
}
|
|
582
|
+
this.#flushMessageQueue();
|
|
583
|
+
this.#options.onOpen();
|
|
584
|
+
if (input?.firstMessage) {
|
|
585
|
+
this.#options.onMessage(input.firstMessage);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
525
589
|
#setState(state: CoderHubWebSocketState): void {
|
|
526
590
|
if (this.#state !== state) {
|
|
527
591
|
this.#state = state;
|
|
@@ -589,6 +653,8 @@ export class CoderHubWebSocketClient {
|
|
|
589
653
|
parent: this.#options.parentSessionId || undefined,
|
|
590
654
|
task: this.#options.task || undefined,
|
|
591
655
|
label: this.#options.label || undefined,
|
|
656
|
+
subscribe:
|
|
657
|
+
this.#options.subscribe.length > 0 ? this.#options.subscribe.join(',') : undefined,
|
|
592
658
|
orgId: this.#options.orgId || undefined,
|
|
593
659
|
origin: this.#options.origin || undefined,
|
|
594
660
|
driverMode: this.#options.driverMode || undefined,
|
|
@@ -601,6 +667,9 @@ export class CoderHubWebSocketClient {
|
|
|
601
667
|
params.set(key, String(value));
|
|
602
668
|
}
|
|
603
669
|
}
|
|
670
|
+
if (this.#options.apiKey) {
|
|
671
|
+
params.set('api_key', this.#options.apiKey);
|
|
672
|
+
}
|
|
604
673
|
|
|
605
674
|
const queryString = params.toString();
|
|
606
675
|
return queryString ? `${wsUrl}?${queryString}` : wsUrl;
|
|
@@ -642,12 +711,16 @@ export class CoderHubWebSocketClient {
|
|
|
642
711
|
ws.onopen = () => {
|
|
643
712
|
if (ws !== this.#ws) return;
|
|
644
713
|
this.#setState('authenticating');
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
}
|
|
650
|
-
|
|
714
|
+
if (this.#options.apiKey || this.#options.orgId) {
|
|
715
|
+
const bootstrapPayload: { authorization?: string; org_id?: string } = {};
|
|
716
|
+
if (this.#options.apiKey) {
|
|
717
|
+
bootstrapPayload.authorization = this.#options.apiKey;
|
|
718
|
+
}
|
|
719
|
+
if (this.#options.orgId) {
|
|
720
|
+
bootstrapPayload.org_id = this.#options.orgId;
|
|
721
|
+
}
|
|
722
|
+
ws.send(JSON.stringify(bootstrapPayload));
|
|
723
|
+
}
|
|
651
724
|
};
|
|
652
725
|
|
|
653
726
|
ws.onmessage = (event: MessageEvent) => {
|
|
@@ -685,10 +758,12 @@ export class CoderHubWebSocketClient {
|
|
|
685
758
|
if (msg.type === 'connection_rejected' || msg.type === 'protocol_error') {
|
|
686
759
|
this.#setState('closed');
|
|
687
760
|
this.#options.onError(
|
|
688
|
-
|
|
689
|
-
message: `Connection rejected: ${msg.message ?? msg.code ?? 'Unknown error'}`,
|
|
761
|
+
this.#buildHandshakeError({
|
|
690
762
|
code: 'auth_failed',
|
|
691
|
-
|
|
763
|
+
message: `Connection rejected: ${msg.message ?? msg.code ?? 'Unknown error'}`,
|
|
764
|
+
serverCode: msg.code,
|
|
765
|
+
serverMessage: msg.message,
|
|
766
|
+
serverMessageType: msg.type,
|
|
692
767
|
})
|
|
693
768
|
);
|
|
694
769
|
this.#intentionallyClosed = true;
|
|
@@ -700,15 +775,18 @@ export class CoderHubWebSocketClient {
|
|
|
700
775
|
const initResult = CoderHubInitMessageSchema.safeParse(parsed);
|
|
701
776
|
if (initResult.success) {
|
|
702
777
|
const initMsg = initResult.data;
|
|
703
|
-
this.#
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
778
|
+
this.#markReady({
|
|
779
|
+
initMessage: initMsg,
|
|
780
|
+
sendBootstrapReady: this.#options.role === 'controller',
|
|
781
|
+
});
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
if (this.#options.role === 'observer') {
|
|
786
|
+
const firstObserverMessage = parseServerMessage(parsed);
|
|
787
|
+
if (firstObserverMessage) {
|
|
788
|
+
this.#markReady({ firstMessage: firstObserverMessage });
|
|
789
|
+
}
|
|
712
790
|
}
|
|
713
791
|
return;
|
|
714
792
|
}
|
|
@@ -759,14 +837,31 @@ export class CoderHubWebSocketClient {
|
|
|
759
837
|
this.#clearTimers();
|
|
760
838
|
this.#setState('closed');
|
|
761
839
|
|
|
840
|
+
const wasAuthenticated = this.#authenticated;
|
|
841
|
+
const hadTerminalError = this.#intentionallyClosed;
|
|
842
|
+
const terminalClose = isTerminalCloseCode(event.code);
|
|
843
|
+
|
|
762
844
|
// Clear auth state for clean reconnect
|
|
763
845
|
this.#authenticated = false;
|
|
764
846
|
this.#initMessage = null;
|
|
765
847
|
|
|
766
|
-
if (
|
|
848
|
+
if (terminalClose) {
|
|
767
849
|
this.#intentionallyClosed = true;
|
|
768
850
|
}
|
|
769
851
|
|
|
852
|
+
if (!wasAuthenticated && terminalClose && !hadTerminalError) {
|
|
853
|
+
this.#options.onError(
|
|
854
|
+
this.#buildHandshakeError({
|
|
855
|
+
code: 'connection_error',
|
|
856
|
+
message: `WebSocket closed before connection was ready (code ${event.code})${
|
|
857
|
+
event.reason ? `: ${event.reason}` : ''
|
|
858
|
+
}`,
|
|
859
|
+
closeCode: event.code,
|
|
860
|
+
closeReason: event.reason || undefined,
|
|
861
|
+
})
|
|
862
|
+
);
|
|
863
|
+
}
|
|
864
|
+
|
|
770
865
|
this.#options.onClose(event.code, event.reason);
|
|
771
866
|
|
|
772
867
|
if (!this.#intentionallyClosed) {
|
|
@@ -896,7 +991,11 @@ export async function* subscribeToCoderHub(
|
|
|
896
991
|
onError: (error) => {
|
|
897
992
|
if (
|
|
898
993
|
error instanceof CoderHubWebSocketError &&
|
|
899
|
-
(error.code === 'max_reconnects_exceeded' ||
|
|
994
|
+
(error.code === 'max_reconnects_exceeded' ||
|
|
995
|
+
error.code === 'auth_failed' ||
|
|
996
|
+
(error.code === 'connection_error' &&
|
|
997
|
+
typeof error.closeCode === 'number' &&
|
|
998
|
+
isTerminalCloseCode(error.closeCode)))
|
|
900
999
|
) {
|
|
901
1000
|
terminalError = error;
|
|
902
1001
|
done = true;
|
|
@@ -2,9 +2,13 @@ import { z } from 'zod/v4';
|
|
|
2
2
|
import { type APIClient } from '../api.ts';
|
|
3
3
|
import {
|
|
4
4
|
CoderCreateWorkspaceRequestSchema,
|
|
5
|
+
CoderUpdateWorkspaceRequestSchema,
|
|
6
|
+
CoderWorkspaceDependencyValidationResponseSchema,
|
|
5
7
|
CoderWorkspaceDetailSchema,
|
|
6
8
|
CoderWorkspaceListResponseSchema,
|
|
7
9
|
type CoderCreateWorkspaceRequest,
|
|
10
|
+
type CoderUpdateWorkspaceRequest,
|
|
11
|
+
type CoderWorkspaceDependencyValidationResponse,
|
|
8
12
|
type CoderWorkspaceDetail,
|
|
9
13
|
type CoderWorkspaceListResponse,
|
|
10
14
|
} from './types.ts';
|
|
@@ -25,6 +29,34 @@ const WorkspaceCreateResponseSchema = z
|
|
|
25
29
|
.passthrough()
|
|
26
30
|
.describe('Wrapped workspace create response from coder hub');
|
|
27
31
|
|
|
32
|
+
const WorkspaceUpdateResponseSchema = z
|
|
33
|
+
.object({
|
|
34
|
+
workspace: CoderWorkspaceDetailSchema.describe(
|
|
35
|
+
'Updated workspace payload returned by coder hub'
|
|
36
|
+
),
|
|
37
|
+
})
|
|
38
|
+
.passthrough()
|
|
39
|
+
.describe('Wrapped workspace update response from coder hub');
|
|
40
|
+
|
|
41
|
+
const WorkspaceSnapshotRefreshResponseSchema = z
|
|
42
|
+
.object({
|
|
43
|
+
workspace: CoderWorkspaceDetailSchema.describe(
|
|
44
|
+
'Workspace payload returned after refreshing its snapshot'
|
|
45
|
+
),
|
|
46
|
+
})
|
|
47
|
+
.passthrough()
|
|
48
|
+
.describe('Wrapped workspace snapshot refresh response from coder hub');
|
|
49
|
+
|
|
50
|
+
const WorkspaceDependencyValidationWrappedResponseSchema = z
|
|
51
|
+
.object({
|
|
52
|
+
success: z.boolean().describe('Validation request success indicator'),
|
|
53
|
+
data: CoderWorkspaceDependencyValidationResponseSchema.describe(
|
|
54
|
+
'Dependency validation result'
|
|
55
|
+
),
|
|
56
|
+
})
|
|
57
|
+
.passthrough()
|
|
58
|
+
.describe('Wrapped workspace dependency validation response from coder hub');
|
|
59
|
+
|
|
28
60
|
const OkResponseSchema = z
|
|
29
61
|
.object({
|
|
30
62
|
ok: z.boolean().describe('Operation success indicator'),
|
|
@@ -68,6 +100,48 @@ export async function coderCreateWorkspace(
|
|
|
68
100
|
return resp.workspace;
|
|
69
101
|
}
|
|
70
102
|
|
|
103
|
+
export async function coderUpdateWorkspace(
|
|
104
|
+
client: APIClient,
|
|
105
|
+
params: { workspaceId: string; body: CoderUpdateWorkspaceRequest }
|
|
106
|
+
): Promise<CoderWorkspaceDetail> {
|
|
107
|
+
const path = `/hub/workspaces/${encodeURIComponent(params.workspaceId)}`;
|
|
108
|
+
const resp = await client.patch<
|
|
109
|
+
z.infer<typeof WorkspaceUpdateResponseSchema>,
|
|
110
|
+
CoderUpdateWorkspaceRequest
|
|
111
|
+
>(path, params.body, WorkspaceUpdateResponseSchema, CoderUpdateWorkspaceRequestSchema);
|
|
112
|
+
|
|
113
|
+
return resp.workspace;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function coderRefreshWorkspaceSnapshot(
|
|
117
|
+
client: APIClient,
|
|
118
|
+
params: { workspaceId: string }
|
|
119
|
+
): Promise<CoderWorkspaceDetail> {
|
|
120
|
+
const path = `/hub/workspaces/${encodeURIComponent(params.workspaceId)}/snapshot/refresh`;
|
|
121
|
+
const resp = await client.post<z.infer<typeof WorkspaceSnapshotRefreshResponseSchema>>(
|
|
122
|
+
path,
|
|
123
|
+
undefined,
|
|
124
|
+
WorkspaceSnapshotRefreshResponseSchema
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
return resp.workspace;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function coderValidateWorkspaceDependencies(
|
|
131
|
+
client: APIClient,
|
|
132
|
+
params: { dependencies: string[] }
|
|
133
|
+
): Promise<CoderWorkspaceDependencyValidationResponse> {
|
|
134
|
+
const resp = await client.post<
|
|
135
|
+
z.infer<typeof WorkspaceDependencyValidationWrappedResponseSchema>
|
|
136
|
+
>(
|
|
137
|
+
'/hub/workspaces/dependencies/validate',
|
|
138
|
+
{ dependencies: params.dependencies },
|
|
139
|
+
WorkspaceDependencyValidationWrappedResponseSchema
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return resp.data;
|
|
143
|
+
}
|
|
144
|
+
|
|
71
145
|
export async function coderDeleteWorkspace(
|
|
72
146
|
client: APIClient,
|
|
73
147
|
params: { workspaceId: string }
|
|
@@ -613,7 +613,7 @@ const service: Service = {
|
|
|
613
613
|
],
|
|
614
614
|
requestBody: {
|
|
615
615
|
description: 'Snapshot creation payload.',
|
|
616
|
-
fields: { schema: SnapshotCreateOptionsSchema },
|
|
616
|
+
fields: { schema: SnapshotCreateOptionsSchema.omit({ orgId: true }) },
|
|
617
617
|
},
|
|
618
618
|
responseDescription: 'Returns the created snapshot.',
|
|
619
619
|
statuses: [
|
|
@@ -887,15 +887,15 @@ const service: Service = {
|
|
|
887
887
|
exampleHeaders: { 'Content-Type': 'application/gzip' },
|
|
888
888
|
exampleBody: '<binary gzip data>',
|
|
889
889
|
},
|
|
890
|
-
// ──
|
|
890
|
+
// ── Sandbox Checkpoints ───────────────────────────────────────────
|
|
891
891
|
{
|
|
892
892
|
id: 'create-checkpoint',
|
|
893
893
|
title: 'Create Checkpoint',
|
|
894
|
-
sectionTitle: '
|
|
894
|
+
sectionTitle: 'Sandbox Checkpoints',
|
|
895
895
|
method: 'POST',
|
|
896
896
|
path: '/sandbox/{sandboxId}/checkpoint',
|
|
897
897
|
description:
|
|
898
|
-
'Create a named checkpoint of the sandbox filesystem. Checkpoint names must be unique — creating a checkpoint with a name that already exists returns 409 Conflict.
|
|
898
|
+
'Create a named checkpoint of the sandbox filesystem. Checkpoint names must be unique — creating a checkpoint with a name that already exists returns 409 Conflict. Checkpoints are persisted across pause/resume cycles.',
|
|
899
899
|
pathParams: [
|
|
900
900
|
{ name: 'sandboxId', type: 'string', description: 'Sandbox ID', required: true },
|
|
901
901
|
],
|
|
@@ -923,11 +923,11 @@ const service: Service = {
|
|
|
923
923
|
{
|
|
924
924
|
id: 'list-checkpoints',
|
|
925
925
|
title: 'List Checkpoints',
|
|
926
|
-
sectionTitle: '
|
|
926
|
+
sectionTitle: 'Sandbox Checkpoints',
|
|
927
927
|
method: 'GET',
|
|
928
928
|
path: '/sandbox/checkpoints/{sandboxId}',
|
|
929
929
|
description:
|
|
930
|
-
'List checkpoints for a specific sandbox.
|
|
930
|
+
'List checkpoints for a specific sandbox. Checkpoints are persisted across pause/resume cycles.',
|
|
931
931
|
pathParams: [
|
|
932
932
|
{ name: 'sandboxId', type: 'string', description: 'Sandbox ID', required: true },
|
|
933
933
|
],
|
|
@@ -947,7 +947,7 @@ const service: Service = {
|
|
|
947
947
|
{
|
|
948
948
|
id: 'restore-checkpoint',
|
|
949
949
|
title: 'Restore Checkpoint',
|
|
950
|
-
sectionTitle: '
|
|
950
|
+
sectionTitle: 'Sandbox Checkpoints',
|
|
951
951
|
method: 'POST',
|
|
952
952
|
path: '/sandbox/{sandboxId}/checkpoint/{checkpointId}/restore',
|
|
953
953
|
description: 'Restore the sandbox filesystem to a checkpoint state.',
|
|
@@ -971,7 +971,7 @@ const service: Service = {
|
|
|
971
971
|
{
|
|
972
972
|
id: 'delete-checkpoint',
|
|
973
973
|
title: 'Delete Checkpoint',
|
|
974
|
-
sectionTitle: '
|
|
974
|
+
sectionTitle: 'Sandbox Checkpoints',
|
|
975
975
|
method: 'DELETE',
|
|
976
976
|
path: '/sandbox/{sandboxId}/checkpoint/{checkpointId}',
|
|
977
977
|
description: 'Delete a checkpoint.',
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
sandboxRmDir,
|
|
35
35
|
sandboxSetEnv,
|
|
36
36
|
} from './files.ts';
|
|
37
|
-
import { sandboxPause } from './pause.ts';
|
|
37
|
+
import { sandboxPause, type SandboxPauseResult } from './pause.ts';
|
|
38
38
|
import { sandboxResume } from './resume.ts';
|
|
39
39
|
import { sandboxRun } from './run.ts';
|
|
40
40
|
import {
|
|
@@ -281,7 +281,7 @@ export interface SandboxInstance {
|
|
|
281
281
|
/**
|
|
282
282
|
* Pause the sandbox, creating a checkpoint of its current state
|
|
283
283
|
*/
|
|
284
|
-
pause(): Promise<
|
|
284
|
+
pause(): Promise<SandboxPauseResult>;
|
|
285
285
|
|
|
286
286
|
/**
|
|
287
287
|
* Resume the sandbox from a paused or evacuated state
|
|
@@ -399,7 +399,7 @@ function createSandboxInstanceMethods(
|
|
|
399
399
|
return sandboxGet(client, { sandboxId, orgId });
|
|
400
400
|
},
|
|
401
401
|
|
|
402
|
-
async pause(): Promise<
|
|
402
|
+
async pause(): Promise<SandboxPauseResult> {
|
|
403
403
|
return sandboxPause(client, { sandboxId, orgId });
|
|
404
404
|
},
|
|
405
405
|
|
|
@@ -725,7 +725,7 @@ export class SandboxClient {
|
|
|
725
725
|
*
|
|
726
726
|
* @param sandboxId - The sandbox ID to pause
|
|
727
727
|
*/
|
|
728
|
-
async pause(sandboxId: string): Promise<
|
|
728
|
+
async pause(sandboxId: string): Promise<SandboxPauseResult> {
|
|
729
729
|
return sandboxPause(this.#client, { sandboxId, orgId: this.#orgId });
|
|
730
730
|
}
|
|
731
731
|
|
|
@@ -49,6 +49,12 @@ export const SandboxCreateRequestSchema = z
|
|
|
49
49
|
.object({
|
|
50
50
|
idle: z.string().optional().describe('Idle timeout duration (e.g., "5m", "1h")'),
|
|
51
51
|
execution: z.string().optional().describe('Maximum execution time (e.g., "30m", "2h")'),
|
|
52
|
+
paused: z
|
|
53
|
+
.string()
|
|
54
|
+
.optional()
|
|
55
|
+
.describe(
|
|
56
|
+
'Maximum time sandbox can remain paused before termination (e.g., "24h", "0s" for infinite)'
|
|
57
|
+
),
|
|
52
58
|
})
|
|
53
59
|
.optional()
|
|
54
60
|
.describe('Timeout settings for the sandbox'),
|
|
@@ -139,6 +145,10 @@ export const SandboxCreateDataSchema = z
|
|
|
139
145
|
'failed',
|
|
140
146
|
])
|
|
141
147
|
.describe('Current status of the sandbox'),
|
|
148
|
+
executionId: z
|
|
149
|
+
.string()
|
|
150
|
+
.optional()
|
|
151
|
+
.describe('Initial execution identifier for oneshot sandbox creation'),
|
|
142
152
|
url: z
|
|
143
153
|
.string()
|
|
144
154
|
.optional()
|
|
@@ -156,7 +156,7 @@ export const SandboxInfoDataSchema = z
|
|
|
156
156
|
user: SandboxUserInfoSchema.optional().describe('User who created the sandbox'),
|
|
157
157
|
agent: SandboxAgentInfoSchema.optional().describe('Agent associated with the sandbox'),
|
|
158
158
|
project: SandboxProjectInfoSchema.optional().describe('Project associated with the sandbox'),
|
|
159
|
-
org: SandboxOrgInfoSchema.describe('Organization associated with the sandbox'),
|
|
159
|
+
org: SandboxOrgInfoSchema.nullish().describe('Organization associated with the sandbox'),
|
|
160
160
|
timeout: z
|
|
161
161
|
.object({
|
|
162
162
|
idle: z.string().optional().describe('Idle timeout duration (e.g., "5m", "1h").'),
|
|
@@ -164,6 +164,10 @@ export const SandboxInfoDataSchema = z
|
|
|
164
164
|
.string()
|
|
165
165
|
.optional()
|
|
166
166
|
.describe('Execution timeout duration (e.g., "30m", "2h").'),
|
|
167
|
+
paused: z
|
|
168
|
+
.string()
|
|
169
|
+
.optional()
|
|
170
|
+
.describe('Paused timeout duration (e.g., "24h", "0s" for infinite).'),
|
|
167
171
|
})
|
|
168
172
|
.optional()
|
|
169
173
|
.describe('Timeout configuration for the sandbox.'),
|
|
@@ -189,6 +193,16 @@ export const SandboxGetParamsSchema = z.object({
|
|
|
189
193
|
.boolean()
|
|
190
194
|
.optional()
|
|
191
195
|
.describe('Whether deleted sandboxes should be included in lookup'),
|
|
196
|
+
waitForStatus: z
|
|
197
|
+
.union([z.string(), z.array(z.string())])
|
|
198
|
+
.optional()
|
|
199
|
+
.describe('Optional desired status or statuses to wait for before responding'),
|
|
200
|
+
waitMs: z
|
|
201
|
+
.number()
|
|
202
|
+
.int()
|
|
203
|
+
.nonnegative()
|
|
204
|
+
.optional()
|
|
205
|
+
.describe('Maximum time in milliseconds to wait for the desired status'),
|
|
192
206
|
});
|
|
193
207
|
|
|
194
208
|
export type SandboxGetParams = z.infer<typeof SandboxGetParamsSchema>;
|
|
@@ -205,7 +219,7 @@ export async function sandboxGet(
|
|
|
205
219
|
client: APIClient,
|
|
206
220
|
params: SandboxGetParams
|
|
207
221
|
): Promise<SandboxInfo> {
|
|
208
|
-
const { sandboxId, orgId, includeDeleted } = params;
|
|
222
|
+
const { sandboxId, orgId, includeDeleted, waitForStatus, waitMs } = params;
|
|
209
223
|
const queryParams = new URLSearchParams();
|
|
210
224
|
if (orgId) {
|
|
211
225
|
queryParams.set('orgId', orgId);
|
|
@@ -213,6 +227,15 @@ export async function sandboxGet(
|
|
|
213
227
|
if (includeDeleted) {
|
|
214
228
|
queryParams.set('includeDeleted', 'true');
|
|
215
229
|
}
|
|
230
|
+
if (waitForStatus) {
|
|
231
|
+
queryParams.set(
|
|
232
|
+
'waitForStatus',
|
|
233
|
+
Array.isArray(waitForStatus) ? waitForStatus.join(',') : waitForStatus
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
if (waitMs != null) {
|
|
237
|
+
queryParams.set('waitMs', String(waitMs));
|
|
238
|
+
}
|
|
216
239
|
const queryString = queryParams.toString();
|
|
217
240
|
const url = `/sandbox/${encodeURIComponent(sandboxId)}${queryString ? `?${queryString}` : ''}`;
|
|
218
241
|
|
|
@@ -222,6 +245,12 @@ export async function sandboxGet(
|
|
|
222
245
|
);
|
|
223
246
|
|
|
224
247
|
if (resp.success) {
|
|
248
|
+
// Newly created sandboxes can be served from the server's pending cache before
|
|
249
|
+
// the backing row and related org hydration are fully available. That response
|
|
250
|
+
// serializes `org: null`, so normalize it to a placeholder object instead of
|
|
251
|
+
// failing response validation for an otherwise valid sandbox status poll.
|
|
252
|
+
const org = resp.data.org ?? { id: orgId ?? '', name: '' };
|
|
253
|
+
|
|
225
254
|
return {
|
|
226
255
|
sandboxId: resp.data.sandboxId,
|
|
227
256
|
identifier: resp.data.identifier,
|
|
@@ -252,7 +281,7 @@ export async function sandboxGet(
|
|
|
252
281
|
user: resp.data.user,
|
|
253
282
|
agent: resp.data.agent,
|
|
254
283
|
project: resp.data.project,
|
|
255
|
-
org
|
|
284
|
+
org,
|
|
256
285
|
timeout: resp.data.timeout,
|
|
257
286
|
command: resp.data.command,
|
|
258
287
|
};
|
|
@@ -13,6 +13,16 @@ const SandboxStatusResponseSchema = APIResponseSchema(SandboxStatusDataSchema);
|
|
|
13
13
|
export const SandboxGetStatusParamsSchema = z.object({
|
|
14
14
|
sandboxId: z.string().describe('Sandbox ID to retrieve status for'),
|
|
15
15
|
orgId: z.string().optional().describe('Optional org id for CLI auth context'),
|
|
16
|
+
waitForStatus: z
|
|
17
|
+
.union([z.string(), z.array(z.string())])
|
|
18
|
+
.optional()
|
|
19
|
+
.describe('Optional desired status or statuses to wait for before responding'),
|
|
20
|
+
waitMs: z
|
|
21
|
+
.number()
|
|
22
|
+
.int()
|
|
23
|
+
.nonnegative()
|
|
24
|
+
.optional()
|
|
25
|
+
.describe('Maximum time in milliseconds to wait for the desired status'),
|
|
16
26
|
});
|
|
17
27
|
|
|
18
28
|
export type SandboxGetStatusParams = z.infer<typeof SandboxGetStatusParamsSchema>;
|
|
@@ -26,11 +36,20 @@ export async function sandboxGetStatus(
|
|
|
26
36
|
client: APIClient,
|
|
27
37
|
params: SandboxGetStatusParams
|
|
28
38
|
): Promise<SandboxStatusResult> {
|
|
29
|
-
const { sandboxId, orgId } = params;
|
|
39
|
+
const { sandboxId, orgId, waitForStatus, waitMs } = params;
|
|
30
40
|
const queryParams = new URLSearchParams();
|
|
31
41
|
if (orgId) {
|
|
32
42
|
queryParams.set('orgId', orgId);
|
|
33
43
|
}
|
|
44
|
+
if (waitForStatus) {
|
|
45
|
+
queryParams.set(
|
|
46
|
+
'waitForStatus',
|
|
47
|
+
Array.isArray(waitForStatus) ? waitForStatus.join(',') : waitForStatus
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
if (waitMs != null) {
|
|
51
|
+
queryParams.set('waitMs', String(waitMs));
|
|
52
|
+
}
|
|
34
53
|
const queryString = queryParams.toString();
|
|
35
54
|
const url = `/sandbox/status/${sandboxId}${queryString ? `?${queryString}` : ''}`;
|
|
36
55
|
|
|
@@ -52,7 +52,7 @@ export {
|
|
|
52
52
|
diskCheckpointRestore,
|
|
53
53
|
diskCheckpointDelete,
|
|
54
54
|
} from './disk-checkpoint.ts';
|
|
55
|
-
export type { SandboxPauseParams } from './pause.ts';
|
|
55
|
+
export type { SandboxPauseParams, SandboxPauseResult } from './pause.ts';
|
|
56
56
|
export { PauseResponseSchema, SandboxPauseParamsSchema, sandboxPause } from './pause.ts';
|
|
57
57
|
export type { SandboxResumeParams } from './resume.ts';
|
|
58
58
|
export { ResumeResponseSchema, SandboxResumeParamsSchema, sandboxResume } from './resume.ts';
|
|
@@ -112,6 +112,10 @@ export const SandboxInfoSchema = z
|
|
|
112
112
|
.string()
|
|
113
113
|
.optional()
|
|
114
114
|
.describe('Execution timeout duration (e.g., "30m", "2h").'),
|
|
115
|
+
paused: z
|
|
116
|
+
.string()
|
|
117
|
+
.optional()
|
|
118
|
+
.describe('Paused timeout duration (e.g., "24h", "0s" for infinite).'),
|
|
115
119
|
})
|
|
116
120
|
.optional()
|
|
117
121
|
.describe('Timeout configuration for the sandbox.'),
|
|
@@ -225,6 +229,7 @@ export async function sandboxList(
|
|
|
225
229
|
networkPort: s.networkPort,
|
|
226
230
|
url: s.url,
|
|
227
231
|
org: s.org,
|
|
232
|
+
timeout: s.timeout,
|
|
228
233
|
})),
|
|
229
234
|
total: resp.data.total,
|
|
230
235
|
};
|
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
import { type APIClient
|
|
2
|
+
import { type APIClient } from '../api.ts';
|
|
3
3
|
import { throwSandboxError } from './util.ts';
|
|
4
4
|
|
|
5
|
-
export const PauseResponseSchema =
|
|
5
|
+
export const PauseResponseSchema = z.discriminatedUnion('success', [
|
|
6
|
+
z.object({
|
|
7
|
+
success: z.literal<false>(false),
|
|
8
|
+
message: z.string(),
|
|
9
|
+
code: z.string().optional(),
|
|
10
|
+
}),
|
|
11
|
+
z.object({
|
|
12
|
+
success: z.literal<true>(true),
|
|
13
|
+
sandboxId: z.string(),
|
|
14
|
+
status: z.string(),
|
|
15
|
+
checkpointId: z.string().optional(),
|
|
16
|
+
terminatesAt: z.string().optional(),
|
|
17
|
+
}),
|
|
18
|
+
]);
|
|
6
19
|
|
|
7
20
|
export const SandboxPauseParamsSchema = z.object({
|
|
8
21
|
sandboxId: z.string().describe('Sandbox ID to pause'),
|
|
@@ -11,14 +24,30 @@ export const SandboxPauseParamsSchema = z.object({
|
|
|
11
24
|
|
|
12
25
|
export type SandboxPauseParams = z.infer<typeof SandboxPauseParamsSchema>;
|
|
13
26
|
|
|
27
|
+
/** Result returned from pausing a sandbox */
|
|
28
|
+
export interface SandboxPauseResult {
|
|
29
|
+
/** The sandbox ID that was paused */
|
|
30
|
+
sandboxId: string;
|
|
31
|
+
/** New status (typically "suspended") */
|
|
32
|
+
status: string;
|
|
33
|
+
/** Checkpoint ID created during pause */
|
|
34
|
+
checkpointId?: string;
|
|
35
|
+
/** ISO 8601 timestamp when sandbox will auto-terminate if not resumed (omitted if no paused timeout) */
|
|
36
|
+
terminatesAt?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
14
39
|
/**
|
|
15
40
|
* Pauses a running sandbox, creating a checkpoint of its current state.
|
|
16
41
|
*
|
|
17
42
|
* @param client - The API client to use for the request
|
|
18
43
|
* @param params - Parameters including the sandbox ID to pause
|
|
44
|
+
* @returns Pause result including terminatesAt if a paused timeout is configured
|
|
19
45
|
* @throws {SandboxResponseError} If the sandbox is not found or pause fails
|
|
20
46
|
*/
|
|
21
|
-
export async function sandboxPause(
|
|
47
|
+
export async function sandboxPause(
|
|
48
|
+
client: APIClient,
|
|
49
|
+
params: SandboxPauseParams
|
|
50
|
+
): Promise<SandboxPauseResult> {
|
|
22
51
|
const { sandboxId, orgId } = params;
|
|
23
52
|
const queryParams = new URLSearchParams();
|
|
24
53
|
if (orgId) {
|
|
@@ -34,7 +63,12 @@ export async function sandboxPause(client: APIClient, params: SandboxPauseParams
|
|
|
34
63
|
);
|
|
35
64
|
|
|
36
65
|
if (resp.success) {
|
|
37
|
-
return
|
|
66
|
+
return {
|
|
67
|
+
sandboxId: resp.sandboxId,
|
|
68
|
+
status: resp.status,
|
|
69
|
+
checkpointId: resp.checkpointId,
|
|
70
|
+
terminatesAt: resp.terminatesAt,
|
|
71
|
+
};
|
|
38
72
|
}
|
|
39
73
|
|
|
40
74
|
throwSandboxError(resp, { sandboxId });
|