plato-sandbox-sdk 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/BaseClient.ts +28 -0
- package/Client.ts +1849 -0
- package/README.md +339 -0
- package/api/client/index.ts +1 -0
- package/api/client/requests/CreateSandboxRequest.ts +37 -0
- package/api/client/requests/CreateSnapshotRequest.ts +11 -0
- package/api/client/requests/EvaluateRequest.ts +9 -0
- package/api/client/requests/GetEnvironmentStateRequest.ts +11 -0
- package/api/client/requests/GetTestCasesRequest.ts +15 -0
- package/api/client/requests/LogRequest.ts +19 -0
- package/api/client/requests/MakeEnvironmentRequest.ts +27 -0
- package/api/client/requests/PostEvaluationResultRequest.ts +14 -0
- package/api/client/requests/ResetEnvironmentRequest.ts +11 -0
- package/api/client/requests/SetupRootAccessRequest.ts +14 -0
- package/api/client/requests/SetupSandboxRequest.ts +29 -0
- package/api/client/requests/StartWorkerRequest.ts +28 -0
- package/api/client/requests/index.ts +12 -0
- package/api/errors/BadRequestError.ts +17 -0
- package/api/errors/index.ts +1 -0
- package/api/index.ts +3 -0
- package/api/types/ActiveSessionResponse.ts +6 -0
- package/api/types/BackupEnvironmentResponse.ts +7 -0
- package/api/types/CdpUrlResponse.ts +12 -0
- package/api/types/CloseEnvironmentResponse.ts +6 -0
- package/api/types/CreateSandboxResponse.ts +14 -0
- package/api/types/CreateSnapshotResponse.ts +10 -0
- package/api/types/DeleteSandboxResponse.ts +6 -0
- package/api/types/Environment.ts +7 -0
- package/api/types/EnvironmentStateResponse.ts +11 -0
- package/api/types/ErrorResponse.ts +6 -0
- package/api/types/EvaluateResponse.ts +21 -0
- package/api/types/EvaluationResult.ts +9 -0
- package/api/types/HeartbeatResponse.ts +6 -0
- package/api/types/JobStatusResponse.ts +7 -0
- package/api/types/LogResponse.ts +5 -0
- package/api/types/MakeEnvironmentResponse.ts +6 -0
- package/api/types/OperationEvent.ts +42 -0
- package/api/types/PlatoConfig.ts +8 -0
- package/api/types/PlatoTask.ts +21 -0
- package/api/types/PlatoTaskMetadata.ts +20 -0
- package/api/types/PostEvaluationResultResponse.ts +6 -0
- package/api/types/ProxyUrlResponse.ts +12 -0
- package/api/types/ResetEnvironmentResponse.ts +13 -0
- package/api/types/RunningSessionsCountResponse.ts +6 -0
- package/api/types/Sandbox.ts +10 -0
- package/api/types/ScoringType.ts +7 -0
- package/api/types/SetupRootAccessResponse.ts +7 -0
- package/api/types/SetupSandboxResponse.ts +6 -0
- package/api/types/SimConfigCompute.ts +9 -0
- package/api/types/SimConfigDataset.ts +10 -0
- package/api/types/SimConfigListener.ts +27 -0
- package/api/types/SimConfigMetadata.ts +14 -0
- package/api/types/SimConfigService.ts +8 -0
- package/api/types/Simulator.ts +10 -0
- package/api/types/SimulatorListItem.ts +7 -0
- package/api/types/SshInfo.ts +18 -0
- package/api/types/StartWorkerResponse.ts +7 -0
- package/api/types/TaskMetadata.ts +20 -0
- package/api/types/TestCase.ts +40 -0
- package/api/types/TestCasesResponse.ts +10 -0
- package/api/types/Variable.ts +6 -0
- package/api/types/WorkerReadyResponse.ts +9 -0
- package/api/types/index.ts +42 -0
- package/core/fetcher/APIResponse.ts +23 -0
- package/core/fetcher/BinaryResponse.ts +36 -0
- package/core/fetcher/EndpointMetadata.ts +13 -0
- package/core/fetcher/EndpointSupplier.ts +14 -0
- package/core/fetcher/Fetcher.ts +165 -0
- package/core/fetcher/Headers.ts +93 -0
- package/core/fetcher/HttpResponsePromise.ts +116 -0
- package/core/fetcher/RawResponse.ts +61 -0
- package/core/fetcher/ResponseWithBody.ts +7 -0
- package/core/fetcher/Supplier.ts +11 -0
- package/core/fetcher/createRequestUrl.ts +6 -0
- package/core/fetcher/getErrorResponseBody.ts +33 -0
- package/core/fetcher/getFetchFn.ts +3 -0
- package/core/fetcher/getHeader.ts +8 -0
- package/core/fetcher/getRequestBody.ts +16 -0
- package/core/fetcher/getResponseBody.ts +43 -0
- package/core/fetcher/index.ts +11 -0
- package/core/fetcher/makeRequest.ts +44 -0
- package/core/fetcher/requestWithRetries.ts +73 -0
- package/core/fetcher/signals.ts +38 -0
- package/core/headers.ts +33 -0
- package/core/index.ts +4 -0
- package/core/json.ts +27 -0
- package/core/runtime/index.ts +1 -0
- package/core/runtime/runtime.ts +133 -0
- package/core/stream/Stream.ts +155 -0
- package/core/stream/index.ts +1 -0
- package/core/url/encodePathParam.ts +18 -0
- package/core/url/index.ts +3 -0
- package/core/url/join.ts +80 -0
- package/core/url/qs.ts +74 -0
- package/dist/BaseClient.d.ts +25 -0
- package/dist/Client.d.ts +376 -0
- package/dist/api/client/index.d.ts +2 -0
- package/dist/api/client/requests/CreateSandboxRequest.d.ts +35 -0
- package/dist/api/client/requests/CreateSnapshotRequest.d.ts +10 -0
- package/dist/api/client/requests/EvaluateRequest.d.ts +8 -0
- package/dist/api/client/requests/GetEnvironmentStateRequest.d.ts +10 -0
- package/dist/api/client/requests/GetTestCasesRequest.d.ts +14 -0
- package/dist/api/client/requests/LogRequest.d.ts +18 -0
- package/dist/api/client/requests/MakeEnvironmentRequest.d.ts +26 -0
- package/dist/api/client/requests/PostEvaluationResultRequest.d.ts +13 -0
- package/dist/api/client/requests/ResetEnvironmentRequest.d.ts +10 -0
- package/dist/api/client/requests/SetupRootAccessRequest.d.ts +13 -0
- package/dist/api/client/requests/SetupSandboxRequest.d.ts +27 -0
- package/dist/api/client/requests/StartWorkerRequest.d.ts +26 -0
- package/dist/api/client/requests/index.d.ts +13 -0
- package/dist/api/errors/BadRequestError.d.ts +7 -0
- package/dist/api/errors/index.d.ts +2 -0
- package/dist/api/index.d.ts +4 -0
- package/dist/api/types/ActiveSessionResponse.d.ts +5 -0
- package/dist/api/types/BackupEnvironmentResponse.d.ts +6 -0
- package/dist/api/types/CdpUrlResponse.d.ts +10 -0
- package/dist/api/types/CloseEnvironmentResponse.d.ts +5 -0
- package/dist/api/types/CreateSandboxResponse.d.ts +13 -0
- package/dist/api/types/CreateSnapshotResponse.d.ts +9 -0
- package/dist/api/types/DeleteSandboxResponse.d.ts +5 -0
- package/dist/api/types/Environment.d.ts +6 -0
- package/dist/api/types/EnvironmentStateResponse.d.ts +9 -0
- package/dist/api/types/ErrorResponse.d.ts +5 -0
- package/dist/api/types/EvaluateResponse.d.ts +18 -0
- package/dist/api/types/EvaluationResult.d.ts +8 -0
- package/dist/api/types/HeartbeatResponse.d.ts +5 -0
- package/dist/api/types/JobStatusResponse.d.ts +6 -0
- package/dist/api/types/LogResponse.d.ts +4 -0
- package/dist/api/types/MakeEnvironmentResponse.d.ts +5 -0
- package/dist/api/types/OperationEvent.d.ts +40 -0
- package/dist/api/types/PlatoConfig.d.ts +6 -0
- package/dist/api/types/PlatoTask.d.ts +19 -0
- package/dist/api/types/PlatoTaskMetadata.d.ts +18 -0
- package/dist/api/types/PostEvaluationResultResponse.d.ts +5 -0
- package/dist/api/types/ProxyUrlResponse.d.ts +10 -0
- package/dist/api/types/ResetEnvironmentResponse.d.ts +11 -0
- package/dist/api/types/RunningSessionsCountResponse.d.ts +5 -0
- package/dist/api/types/Sandbox.d.ts +9 -0
- package/dist/api/types/ScoringType.d.ts +6 -0
- package/dist/api/types/SetupRootAccessResponse.d.ts +6 -0
- package/dist/api/types/SetupSandboxResponse.d.ts +5 -0
- package/dist/api/types/SimConfigCompute.d.ts +8 -0
- package/dist/api/types/SimConfigDataset.d.ts +8 -0
- package/dist/api/types/SimConfigListener.d.ts +25 -0
- package/dist/api/types/SimConfigMetadata.d.ts +12 -0
- package/dist/api/types/SimConfigService.d.ts +7 -0
- package/dist/api/types/Simulator.d.ts +9 -0
- package/dist/api/types/SimulatorListItem.d.ts +6 -0
- package/dist/api/types/SshInfo.d.ts +17 -0
- package/dist/api/types/StartWorkerResponse.d.ts +6 -0
- package/dist/api/types/TaskMetadata.d.ts +18 -0
- package/dist/api/types/TestCase.d.ts +34 -0
- package/dist/api/types/TestCasesResponse.d.ts +8 -0
- package/dist/api/types/Variable.d.ts +5 -0
- package/dist/api/types/WorkerReadyResponse.d.ts +8 -0
- package/dist/api/types/index.d.ts +43 -0
- package/dist/core/fetcher/APIResponse.d.ts +21 -0
- package/dist/core/fetcher/BinaryResponse.d.ts +21 -0
- package/dist/core/fetcher/EndpointMetadata.d.ts +14 -0
- package/dist/core/fetcher/EndpointSupplier.d.ts +13 -0
- package/dist/core/fetcher/Fetcher.d.ts +43 -0
- package/dist/core/fetcher/Headers.d.ts +3 -0
- package/dist/core/fetcher/HttpResponsePromise.d.ts +59 -0
- package/dist/core/fetcher/RawResponse.d.ts +30 -0
- package/dist/core/fetcher/ResponseWithBody.d.ts +5 -0
- package/dist/core/fetcher/Supplier.d.ts +5 -0
- package/dist/core/fetcher/createRequestUrl.d.ts +2 -0
- package/dist/core/fetcher/getErrorResponseBody.d.ts +2 -0
- package/dist/core/fetcher/getFetchFn.d.ts +2 -0
- package/dist/core/fetcher/getHeader.d.ts +2 -0
- package/dist/core/fetcher/getRequestBody.d.ts +8 -0
- package/dist/core/fetcher/getResponseBody.d.ts +2 -0
- package/dist/core/fetcher/index.d.ts +12 -0
- package/dist/core/fetcher/makeRequest.d.ts +2 -0
- package/dist/core/fetcher/requestWithRetries.d.ts +2 -0
- package/dist/core/fetcher/signals.d.ts +12 -0
- package/dist/core/headers.d.ts +3 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/json.d.ts +16 -0
- package/dist/core/runtime/index.d.ts +2 -0
- package/dist/core/runtime/runtime.d.ts +10 -0
- package/dist/core/stream/Stream.d.ts +48 -0
- package/dist/core/stream/index.d.ts +2 -0
- package/dist/core/url/encodePathParam.d.ts +2 -0
- package/dist/core/url/index.d.ts +4 -0
- package/dist/core/url/join.d.ts +2 -0
- package/dist/core/url/qs.d.ts +7 -0
- package/dist/errors/ApiError.d.ts +13 -0
- package/dist/errors/ApiTimeoutError.d.ts +4 -0
- package/dist/errors/index.d.ts +3 -0
- package/dist/helpers/SandboxHelpers.d.ts +127 -0
- package/dist/helpers/SandboxMonitor.d.ts +89 -0
- package/dist/helpers/index.d.ts +9 -0
- package/dist/index.d.ts +6 -0
- package/errors/ApiError.ts +53 -0
- package/errors/ApiTimeoutError.ts +8 -0
- package/errors/index.ts +2 -0
- package/helpers/README.md +229 -0
- package/helpers/SandboxHelpers.ts +213 -0
- package/helpers/SandboxMonitor.ts +252 -0
- package/helpers/index.ts +10 -0
- package/index.ts +7 -0
- package/package.json +54 -0
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# Plato SDK Helpers
|
|
2
|
+
|
|
3
|
+
This directory contains **custom helper utilities** that are **NOT generated by Fern**. These files are safe to modify and will not be overwritten when regenerating the SDK.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
The helpers provide high-level convenience functions that combine the low-level Fern-generated API client with business logic for:
|
|
8
|
+
|
|
9
|
+
- Monitoring long-running operations via SSE streams
|
|
10
|
+
- Waiting for sandbox operations to complete
|
|
11
|
+
- Handling operation success/failure states
|
|
12
|
+
|
|
13
|
+
## Files
|
|
14
|
+
|
|
15
|
+
### `SandboxMonitor.ts`
|
|
16
|
+
Core monitoring logic that processes SSE event streams and determines operation success/failure.
|
|
17
|
+
|
|
18
|
+
**Based on:** `sdk/services/sandbox.go` (Go implementation)
|
|
19
|
+
|
|
20
|
+
**Key Features:**
|
|
21
|
+
- Handles SSE event types: `connected`, `progress`, `run_result`, `ssh_result`, `error`
|
|
22
|
+
- Determines when operations are complete
|
|
23
|
+
- Provides progress callbacks
|
|
24
|
+
- Timeout support
|
|
25
|
+
- Abort signal support
|
|
26
|
+
|
|
27
|
+
### `SandboxHelpers.ts`
|
|
28
|
+
High-level wrapper functions that combine API calls with automatic monitoring.
|
|
29
|
+
|
|
30
|
+
**Convenience methods:**
|
|
31
|
+
- `createSandbox()` - Create and wait for sandbox to be ready
|
|
32
|
+
- `setupSandbox()` - Setup sandbox with monitoring
|
|
33
|
+
- `createSnapshot()` - Create snapshot with monitoring
|
|
34
|
+
- `startWorker()` - Start worker with monitoring
|
|
35
|
+
- `setupRootAccess()` - Setup root access with monitoring
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Basic Example
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import { ApiClient, SandboxHelpers } from '@plato-sdk/typescript';
|
|
43
|
+
|
|
44
|
+
// Create client
|
|
45
|
+
const client = new ApiClient({
|
|
46
|
+
baseUrl: 'https://plato.so/api',
|
|
47
|
+
// Add auth headers, etc.
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// Create helpers
|
|
51
|
+
const helpers = new SandboxHelpers(client);
|
|
52
|
+
|
|
53
|
+
// Create sandbox and wait for it to be ready
|
|
54
|
+
const sandbox = await helpers.createSandbox({
|
|
55
|
+
dataset: 'base',
|
|
56
|
+
plato_dataset_config: {
|
|
57
|
+
compute: {
|
|
58
|
+
cpus: 2,
|
|
59
|
+
memory: 4096,
|
|
60
|
+
disk: 20480,
|
|
61
|
+
app_port: 8080,
|
|
62
|
+
plato_messaging_port: 7000
|
|
63
|
+
},
|
|
64
|
+
metadata: {
|
|
65
|
+
name: 'My Sandbox'
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}, {
|
|
69
|
+
onProgress: (message) => {
|
|
70
|
+
console.log('Progress:', message);
|
|
71
|
+
},
|
|
72
|
+
timeoutMs: 600000 // 10 minutes
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log('Sandbox ready!', sandbox);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Advanced: Using SandboxMonitor Directly
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { ApiClient, SandboxMonitor } from '@plato-sdk/typescript';
|
|
82
|
+
|
|
83
|
+
const client = new ApiClient({ /* ... */ });
|
|
84
|
+
const monitor = new SandboxMonitor(client);
|
|
85
|
+
|
|
86
|
+
// Create sandbox without waiting
|
|
87
|
+
const response = await client.createSandbox({ /* ... */ });
|
|
88
|
+
const sandbox = response.data;
|
|
89
|
+
|
|
90
|
+
// Later, monitor it manually
|
|
91
|
+
if (sandbox.correlation_id) {
|
|
92
|
+
await monitor.waitForCompletion(sandbox.correlation_id, {
|
|
93
|
+
onProgress: (msg) => console.log(msg),
|
|
94
|
+
timeoutMs: 600000
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Getting All Events
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
const monitor = new SandboxMonitor(client);
|
|
103
|
+
|
|
104
|
+
// Get all events for debugging
|
|
105
|
+
const events = await monitor.waitForCompletionWithEvents(
|
|
106
|
+
correlationId,
|
|
107
|
+
{
|
|
108
|
+
onProgress: (msg) => console.log(msg)
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
console.log('All events:', events);
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Error Handling
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { SandboxOperationError } from '@plato-sdk/typescript';
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await helpers.createSandbox({ /* ... */ });
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof SandboxOperationError) {
|
|
124
|
+
console.error('Operation failed:', error.message);
|
|
125
|
+
console.error('Last event:', error.event);
|
|
126
|
+
} else {
|
|
127
|
+
console.error('Unexpected error:', error);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Create Without Waiting
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// Don't wait for operation to complete
|
|
136
|
+
const sandbox = await helpers.createSandbox({
|
|
137
|
+
dataset: 'base',
|
|
138
|
+
plato_dataset_config: config
|
|
139
|
+
}, {
|
|
140
|
+
wait: false // Return immediately after creating
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Monitor it later if needed
|
|
144
|
+
if (sandbox.correlation_id) {
|
|
145
|
+
await helpers.getMonitor().waitForCompletion(sandbox.correlation_id);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## How It Works
|
|
150
|
+
|
|
151
|
+
### SSE Event Flow
|
|
152
|
+
|
|
153
|
+
The Plato API sends Server-Sent Events to monitor long-running operations:
|
|
154
|
+
|
|
155
|
+
1. **`connected`** - Initial connection established
|
|
156
|
+
2. **`progress`** - Progress updates (optional)
|
|
157
|
+
3. **Terminal events:**
|
|
158
|
+
- **`run_result`** - Operation completed (check `success` field)
|
|
159
|
+
- **`ssh_result`** - SSH operation completed (check `success` field)
|
|
160
|
+
- **`error`** - Operation failed
|
|
161
|
+
|
|
162
|
+
### Event Structure
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
interface OperationEvent {
|
|
166
|
+
type: 'connected' | 'run_result' | 'ssh_result' | 'error' | 'progress';
|
|
167
|
+
success?: boolean; // Only present in result events
|
|
168
|
+
error?: string; // Error message if failed
|
|
169
|
+
message?: string; // Human-readable status/progress
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Success Detection
|
|
174
|
+
|
|
175
|
+
An operation is **successful** if:
|
|
176
|
+
- You receive a `run_result` or `ssh_result` event with `success: true`
|
|
177
|
+
|
|
178
|
+
An operation **failed** if:
|
|
179
|
+
- You receive an `error` event
|
|
180
|
+
- You receive a result event with `success: false`
|
|
181
|
+
- The stream ends without a terminal event
|
|
182
|
+
- The operation times out
|
|
183
|
+
|
|
184
|
+
## Architecture
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
┌─────────────────────────────────────┐
|
|
188
|
+
│ Your Application Code │
|
|
189
|
+
└─────────────┬───────────────────────┘
|
|
190
|
+
│
|
|
191
|
+
│ Uses
|
|
192
|
+
▼
|
|
193
|
+
┌─────────────────────────────────────┐
|
|
194
|
+
│ SandboxHelpers │ ◄── Custom (not generated)
|
|
195
|
+
│ - High-level convenience methods │
|
|
196
|
+
└─────────────┬───────────────────────┘
|
|
197
|
+
│
|
|
198
|
+
│ Uses
|
|
199
|
+
▼
|
|
200
|
+
┌─────────────────────────────────────┐
|
|
201
|
+
│ SandboxMonitor │ ◄── Custom (not generated)
|
|
202
|
+
│ - SSE event processing │
|
|
203
|
+
│ - Success/failure detection │
|
|
204
|
+
└─────────────┬───────────────────────┘
|
|
205
|
+
│
|
|
206
|
+
│ Uses
|
|
207
|
+
▼
|
|
208
|
+
┌─────────────────────────────────────┐
|
|
209
|
+
│ ApiClient (Fern-generated) │ ◄── Generated by Fern
|
|
210
|
+
│ - Low-level API calls │
|
|
211
|
+
│ - SSE streaming │
|
|
212
|
+
└─────────────────────────────────────┘
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Why Separate Files?
|
|
216
|
+
|
|
217
|
+
- **Generated code** (Client.ts, api/*) is overwritten on each `fern generate`
|
|
218
|
+
- **Custom helpers** (this directory) persist across regenerations
|
|
219
|
+
- **Clean separation** between API client and business logic
|
|
220
|
+
- **Easy to maintain** and extend without modifying generated code
|
|
221
|
+
|
|
222
|
+
## Regenerating the SDK
|
|
223
|
+
|
|
224
|
+
When you regenerate the SDK with `fern generate`:
|
|
225
|
+
- ✅ Files in `helpers/` are **preserved**
|
|
226
|
+
- ❌ Other files are **overwritten**
|
|
227
|
+
|
|
228
|
+
This is the recommended pattern for extending Fern-generated SDKs.
|
|
229
|
+
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* High-level helper functions for sandbox operations.
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT generated by Fern and provides convenient wrappers
|
|
5
|
+
* around the generated API client with built-in operation monitoring.
|
|
6
|
+
*
|
|
7
|
+
* Based on the Go implementation in sdk/services/sandbox.go
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ApiClient } from "../Client.js";
|
|
11
|
+
import type * as Api from "../api/index.js";
|
|
12
|
+
import { SandboxMonitor, type MonitorOptions } from "./SandboxMonitor.js";
|
|
13
|
+
|
|
14
|
+
export interface CreateSandboxOptions extends MonitorOptions {
|
|
15
|
+
/**
|
|
16
|
+
* Whether to wait for the sandbox to be ready before returning.
|
|
17
|
+
* If true, monitors the operation until completion.
|
|
18
|
+
* Default: true
|
|
19
|
+
*/
|
|
20
|
+
wait?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SetupSandboxOptions extends MonitorOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Whether to wait for setup to complete before returning.
|
|
26
|
+
* If true, monitors the operation until completion.
|
|
27
|
+
* Default: true
|
|
28
|
+
*/
|
|
29
|
+
wait?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* High-level helpers for sandbox operations that combine API calls
|
|
34
|
+
* with operation monitoring.
|
|
35
|
+
*/
|
|
36
|
+
export class SandboxHelpers {
|
|
37
|
+
private monitor: SandboxMonitor;
|
|
38
|
+
|
|
39
|
+
constructor(private readonly client: ApiClient) {
|
|
40
|
+
this.monitor = new SandboxMonitor(client);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a sandbox and optionally wait for it to be ready.
|
|
45
|
+
*
|
|
46
|
+
* This is a convenience wrapper that:
|
|
47
|
+
* 1. Calls createSandbox API
|
|
48
|
+
* 2. If wait=true (default), monitors the operation until completion
|
|
49
|
+
* 3. Returns the sandbox info
|
|
50
|
+
*
|
|
51
|
+
* @param request - Sandbox creation request
|
|
52
|
+
* @param options - Creation and monitoring options
|
|
53
|
+
* @returns Sandbox information
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const helpers = new SandboxHelpers(client);
|
|
58
|
+
*
|
|
59
|
+
* // Create and wait for ready (default)
|
|
60
|
+
* const sandbox = await helpers.createSandbox({
|
|
61
|
+
* dataset: "base",
|
|
62
|
+
* plato_dataset_config: config
|
|
63
|
+
* }, {
|
|
64
|
+
* onProgress: (msg) => console.log(msg)
|
|
65
|
+
* });
|
|
66
|
+
*
|
|
67
|
+
* // Create without waiting
|
|
68
|
+
* const sandbox = await helpers.createSandbox({
|
|
69
|
+
* dataset: "base",
|
|
70
|
+
* plato_dataset_config: config
|
|
71
|
+
* }, { wait: false });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
async createSandbox(
|
|
75
|
+
request: Api.CreateSandboxRequest,
|
|
76
|
+
options: CreateSandboxOptions = {}
|
|
77
|
+
): Promise<Api.CreateSandboxResponse> {
|
|
78
|
+
const { wait = true, ...monitorOptions } = options;
|
|
79
|
+
|
|
80
|
+
// Create the sandbox
|
|
81
|
+
// Note: HttpResponsePromise automatically unwraps to the response type
|
|
82
|
+
const sandbox = await this.client.createSandbox(request);
|
|
83
|
+
|
|
84
|
+
// Wait for it to be ready if requested
|
|
85
|
+
if (wait && sandbox.correlation_id) {
|
|
86
|
+
await this.monitor.waitForCompletion(sandbox.correlation_id, monitorOptions);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return sandbox;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Setup a sandbox and optionally wait for completion.
|
|
94
|
+
*
|
|
95
|
+
* This is a convenience wrapper that:
|
|
96
|
+
* 1. Calls setupSandbox API
|
|
97
|
+
* 2. If wait=true (default), monitors the operation until completion
|
|
98
|
+
* 3. Returns the correlation ID
|
|
99
|
+
*
|
|
100
|
+
* @param jobId - Job ID of the sandbox
|
|
101
|
+
* @param request - Setup request with dataset config and optional SSH key
|
|
102
|
+
* @param options - Setup and monitoring options
|
|
103
|
+
* @returns Setup response with correlation ID
|
|
104
|
+
*
|
|
105
|
+
* @example
|
|
106
|
+
* ```typescript
|
|
107
|
+
* const helpers = new SandboxHelpers(client);
|
|
108
|
+
*
|
|
109
|
+
* await helpers.setupSandbox("job_123", {
|
|
110
|
+
* dataset: "base",
|
|
111
|
+
* plato_dataset_config: config,
|
|
112
|
+
* ssh_public_key: publicKey
|
|
113
|
+
* }, {
|
|
114
|
+
* onProgress: (msg) => console.log(msg)
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
async setupSandbox(
|
|
119
|
+
jobId: string,
|
|
120
|
+
request: Api.SetupSandboxRequest,
|
|
121
|
+
options: SetupSandboxOptions = {}
|
|
122
|
+
): Promise<Api.SetupSandboxResponse> {
|
|
123
|
+
const { wait = true, ...monitorOptions } = options;
|
|
124
|
+
|
|
125
|
+
// Setup the sandbox
|
|
126
|
+
const setupResponse = await this.client.setupSandbox(jobId, request);
|
|
127
|
+
|
|
128
|
+
// Wait for setup to complete if requested
|
|
129
|
+
if (wait && setupResponse.correlation_id) {
|
|
130
|
+
await this.monitor.waitForCompletion(setupResponse.correlation_id, monitorOptions);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return setupResponse;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Create a snapshot and optionally wait for completion.
|
|
138
|
+
*
|
|
139
|
+
* @param publicId - Public ID of the sandbox
|
|
140
|
+
* @param request - Snapshot request
|
|
141
|
+
* @param options - Monitoring options
|
|
142
|
+
* @returns Snapshot response
|
|
143
|
+
*/
|
|
144
|
+
async createSnapshot(
|
|
145
|
+
publicId: string,
|
|
146
|
+
request: Api.CreateSnapshotRequest,
|
|
147
|
+
options: MonitorOptions = {}
|
|
148
|
+
): Promise<Api.CreateSnapshotResponse> {
|
|
149
|
+
const snapshot = await this.client.createSnapshot(publicId, request);
|
|
150
|
+
|
|
151
|
+
// Note: Snapshot operations may return a correlation_id for monitoring
|
|
152
|
+
// If it does, we should monitor it
|
|
153
|
+
if ('correlation_id' in snapshot && typeof (snapshot as any).correlation_id === 'string') {
|
|
154
|
+
await this.monitor.waitForCompletion((snapshot as any).correlation_id, options);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return snapshot;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Start a Plato worker and optionally wait for completion.
|
|
162
|
+
*
|
|
163
|
+
* @param publicId - Public ID of the sandbox
|
|
164
|
+
* @param request - Worker start request
|
|
165
|
+
* @param options - Monitoring options
|
|
166
|
+
* @returns Worker start response
|
|
167
|
+
*/
|
|
168
|
+
async startWorker(
|
|
169
|
+
publicId: string,
|
|
170
|
+
request: Api.StartWorkerRequest,
|
|
171
|
+
options: MonitorOptions = {}
|
|
172
|
+
): Promise<Api.StartWorkerResponse> {
|
|
173
|
+
const workerResponse = await this.client.startWorker(publicId, request);
|
|
174
|
+
|
|
175
|
+
// Monitor if correlation_id is provided
|
|
176
|
+
if (workerResponse.correlation_id) {
|
|
177
|
+
await this.monitor.waitForCompletion(workerResponse.correlation_id, options);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return workerResponse;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Setup root access and optionally wait for completion.
|
|
185
|
+
*
|
|
186
|
+
* @param publicId - Public ID of the sandbox
|
|
187
|
+
* @param request - Root access setup request
|
|
188
|
+
* @param options - Monitoring options
|
|
189
|
+
* @returns Setup response
|
|
190
|
+
*/
|
|
191
|
+
async setupRootAccess(
|
|
192
|
+
publicId: string,
|
|
193
|
+
request: Api.SetupRootAccessRequest,
|
|
194
|
+
options: MonitorOptions = {}
|
|
195
|
+
): Promise<Api.SetupRootAccessResponse> {
|
|
196
|
+
const setupResponse = await this.client.setupRootAccess(publicId, request);
|
|
197
|
+
|
|
198
|
+
// Monitor if correlation_id is provided
|
|
199
|
+
if (setupResponse.correlation_id) {
|
|
200
|
+
await this.monitor.waitForCompletion(setupResponse.correlation_id, options);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return setupResponse;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Get the underlying monitor for advanced use cases.
|
|
208
|
+
*/
|
|
209
|
+
getMonitor(): SandboxMonitor {
|
|
210
|
+
return this.monitor;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper utilities for monitoring sandbox operations.
|
|
3
|
+
*
|
|
4
|
+
* This file is NOT generated by Fern and contains custom business logic
|
|
5
|
+
* for handling SSE streams from the Plato API.
|
|
6
|
+
*
|
|
7
|
+
* Based on the Go implementation in sdk/services/sandbox.go
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ApiClient } from "../Client.js";
|
|
11
|
+
import type { OperationEvent } from "../api/types/OperationEvent.js";
|
|
12
|
+
|
|
13
|
+
export interface MonitorOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Timeout in milliseconds for the entire operation.
|
|
16
|
+
* Default: 600000 (10 minutes)
|
|
17
|
+
*/
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Callback for progress updates
|
|
22
|
+
*/
|
|
23
|
+
onProgress?: (message: string) => void;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Abort signal to cancel the operation
|
|
27
|
+
*/
|
|
28
|
+
abortSignal?: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class SandboxOperationError extends Error {
|
|
32
|
+
constructor(message: string, public readonly event?: OperationEvent) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'SandboxOperationError';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export class SandboxMonitor {
|
|
39
|
+
constructor(private readonly client: ApiClient) {}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Monitor a sandbox operation until completion.
|
|
43
|
+
*
|
|
44
|
+
* This implements the business logic from the Go SDK (sandbox.go):
|
|
45
|
+
* - Waits for terminal events (run_result, ssh_result, error)
|
|
46
|
+
* - Reports progress via callback
|
|
47
|
+
* - Throws on failure
|
|
48
|
+
* - Returns on success
|
|
49
|
+
*
|
|
50
|
+
* @param correlationId - Correlation ID from sandbox creation or setup
|
|
51
|
+
* @param options - Monitoring options
|
|
52
|
+
* @throws {SandboxOperationError} If the operation fails
|
|
53
|
+
* @throws {Error} If the stream ends unexpectedly
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const monitor = new SandboxMonitor(client);
|
|
58
|
+
* const sandbox = await client.createSandbox({ ... });
|
|
59
|
+
*
|
|
60
|
+
* // Wait for sandbox to be ready
|
|
61
|
+
* await monitor.waitForCompletion(sandbox.correlation_id, {
|
|
62
|
+
* onProgress: (msg) => console.log('Progress:', msg)
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
async waitForCompletion(
|
|
67
|
+
correlationId: string,
|
|
68
|
+
options: MonitorOptions = {}
|
|
69
|
+
): Promise<void> {
|
|
70
|
+
const { timeoutMs = 600000, onProgress, abortSignal } = options;
|
|
71
|
+
|
|
72
|
+
// Set up timeout
|
|
73
|
+
const timeoutController = new AbortController();
|
|
74
|
+
const timeoutId = setTimeout(() => {
|
|
75
|
+
timeoutController.abort();
|
|
76
|
+
}, timeoutMs);
|
|
77
|
+
|
|
78
|
+
// Combine abort signals
|
|
79
|
+
const combinedSignal = abortSignal
|
|
80
|
+
? this.combineAbortSignals([abortSignal, timeoutController.signal])
|
|
81
|
+
: timeoutController.signal;
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
// Get the SSE stream from the generated client
|
|
85
|
+
// Note: HttpResponsePromise automatically unwraps to the Stream type
|
|
86
|
+
const stream = await this.client.monitorOperation(correlationId, {
|
|
87
|
+
abortSignal: combinedSignal,
|
|
88
|
+
// Increase timeout to match the operation timeout
|
|
89
|
+
timeoutInSeconds: Math.ceil(timeoutMs / 1000),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Process events from the stream
|
|
93
|
+
for await (const event of stream) {
|
|
94
|
+
// NOTE: The Go implementation expects base64-encoded JSON in the SSE data field.
|
|
95
|
+
// Fern's Stream class should handle the SSE parsing, but we may need to handle
|
|
96
|
+
// base64 decoding if the API sends it that way. For now, assuming Fern handles it.
|
|
97
|
+
|
|
98
|
+
const result = this.handleEvent(event, onProgress);
|
|
99
|
+
|
|
100
|
+
if (result.shouldTerminate) {
|
|
101
|
+
if (result.success) {
|
|
102
|
+
return; // Success!
|
|
103
|
+
} else {
|
|
104
|
+
throw new SandboxOperationError(
|
|
105
|
+
result.error || 'Operation failed',
|
|
106
|
+
event
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Stream ended without terminal event
|
|
113
|
+
throw new Error('SSE stream ended without completion event');
|
|
114
|
+
} finally {
|
|
115
|
+
clearTimeout(timeoutId);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Monitor operation and collect all events.
|
|
121
|
+
* Useful for debugging or detailed progress tracking.
|
|
122
|
+
*
|
|
123
|
+
* @param correlationId - Correlation ID from sandbox creation or setup
|
|
124
|
+
* @param options - Monitoring options
|
|
125
|
+
* @returns Array of all events received
|
|
126
|
+
* @throws {SandboxOperationError} If the operation fails
|
|
127
|
+
*/
|
|
128
|
+
async waitForCompletionWithEvents(
|
|
129
|
+
correlationId: string,
|
|
130
|
+
options: MonitorOptions = {}
|
|
131
|
+
): Promise<OperationEvent[]> {
|
|
132
|
+
const events: OperationEvent[] = [];
|
|
133
|
+
|
|
134
|
+
const wrappedOptions: MonitorOptions = {
|
|
135
|
+
...options,
|
|
136
|
+
onProgress: (message) => {
|
|
137
|
+
if (options.onProgress) {
|
|
138
|
+
options.onProgress(message);
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const stream = await this.client.monitorOperation(correlationId, {
|
|
145
|
+
abortSignal: options.abortSignal,
|
|
146
|
+
timeoutInSeconds: Math.ceil((options.timeoutMs || 600000) / 1000),
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
for await (const event of stream) {
|
|
150
|
+
events.push(event);
|
|
151
|
+
|
|
152
|
+
const result = this.handleEvent(event, wrappedOptions.onProgress);
|
|
153
|
+
|
|
154
|
+
if (result.shouldTerminate) {
|
|
155
|
+
if (!result.success) {
|
|
156
|
+
throw new SandboxOperationError(
|
|
157
|
+
result.error || 'Operation failed',
|
|
158
|
+
event
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return events;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
// Still return events even on error for debugging
|
|
168
|
+
if (error instanceof SandboxOperationError) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Handle a single operation event and determine if operation should terminate.
|
|
177
|
+
*
|
|
178
|
+
* This implements the state machine from sandbox.go MonitorOperation():
|
|
179
|
+
* - connected: Continue listening
|
|
180
|
+
* - progress: Report progress, continue listening
|
|
181
|
+
* - run_result/ssh_result: Terminal event, check success
|
|
182
|
+
* - error: Terminal event, always failure
|
|
183
|
+
*
|
|
184
|
+
* @private
|
|
185
|
+
*/
|
|
186
|
+
private handleEvent(
|
|
187
|
+
event: OperationEvent,
|
|
188
|
+
onProgress?: (message: string) => void
|
|
189
|
+
): { shouldTerminate: boolean; success?: boolean; error?: string } {
|
|
190
|
+
switch (event.type) {
|
|
191
|
+
case 'connected':
|
|
192
|
+
// Initial connection, continue listening
|
|
193
|
+
return { shouldTerminate: false };
|
|
194
|
+
|
|
195
|
+
case 'progress':
|
|
196
|
+
// Progress update
|
|
197
|
+
if (onProgress && event.message) {
|
|
198
|
+
onProgress(event.message);
|
|
199
|
+
}
|
|
200
|
+
return { shouldTerminate: false };
|
|
201
|
+
|
|
202
|
+
case 'run_result':
|
|
203
|
+
case 'ssh_result':
|
|
204
|
+
// Terminal event - operation completed
|
|
205
|
+
if (event.success) {
|
|
206
|
+
// Success!
|
|
207
|
+
if (onProgress && event.message) {
|
|
208
|
+
onProgress(event.message);
|
|
209
|
+
}
|
|
210
|
+
return { shouldTerminate: true, success: true };
|
|
211
|
+
} else {
|
|
212
|
+
// Failed
|
|
213
|
+
const errorMsg = event.error || event.message || 'Operation failed';
|
|
214
|
+
return { shouldTerminate: true, success: false, error: errorMsg };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
case 'error':
|
|
218
|
+
// Error event - always terminal
|
|
219
|
+
const errorMsg = event.error || event.message || 'Operation error';
|
|
220
|
+
return { shouldTerminate: true, success: false, error: errorMsg };
|
|
221
|
+
|
|
222
|
+
default:
|
|
223
|
+
// Unknown event type - log and continue
|
|
224
|
+
console.warn('Unknown operation event type:', event.type);
|
|
225
|
+
return { shouldTerminate: false };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Combine multiple abort signals into one.
|
|
231
|
+
* If any signal aborts, the combined signal aborts.
|
|
232
|
+
*
|
|
233
|
+
* @private
|
|
234
|
+
*/
|
|
235
|
+
private combineAbortSignals(signals: AbortSignal[]): AbortSignal {
|
|
236
|
+
const controller = new AbortController();
|
|
237
|
+
|
|
238
|
+
for (const signal of signals) {
|
|
239
|
+
if (signal.aborted) {
|
|
240
|
+
controller.abort();
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
signal.addEventListener('abort', () => {
|
|
245
|
+
controller.abort();
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return controller.signal;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
package/helpers/index.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom helpers for the Plato SDK.
|
|
3
|
+
*
|
|
4
|
+
* These utilities are NOT generated by Fern and provide high-level
|
|
5
|
+
* convenience functions built on top of the generated API client.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { SandboxMonitor, SandboxOperationError, type MonitorOptions } from "./SandboxMonitor.js";
|
|
9
|
+
export { SandboxHelpers, type CreateSandboxOptions, type SetupSandboxOptions } from "./SandboxHelpers.js";
|
|
10
|
+
|
package/index.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export * as Api from "./api/index.js";
|
|
2
|
+
export type { BaseClientOptions, BaseRequestOptions } from "./BaseClient.js";
|
|
3
|
+
export { ApiClient } from "./Client.js";
|
|
4
|
+
export { ApiError, ApiTimeoutError } from "./errors/index.js";
|
|
5
|
+
|
|
6
|
+
// Custom helpers (not generated by Fern)
|
|
7
|
+
export * from "./helpers/index.js";
|