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.
Files changed (203) hide show
  1. package/BaseClient.ts +28 -0
  2. package/Client.ts +1849 -0
  3. package/README.md +339 -0
  4. package/api/client/index.ts +1 -0
  5. package/api/client/requests/CreateSandboxRequest.ts +37 -0
  6. package/api/client/requests/CreateSnapshotRequest.ts +11 -0
  7. package/api/client/requests/EvaluateRequest.ts +9 -0
  8. package/api/client/requests/GetEnvironmentStateRequest.ts +11 -0
  9. package/api/client/requests/GetTestCasesRequest.ts +15 -0
  10. package/api/client/requests/LogRequest.ts +19 -0
  11. package/api/client/requests/MakeEnvironmentRequest.ts +27 -0
  12. package/api/client/requests/PostEvaluationResultRequest.ts +14 -0
  13. package/api/client/requests/ResetEnvironmentRequest.ts +11 -0
  14. package/api/client/requests/SetupRootAccessRequest.ts +14 -0
  15. package/api/client/requests/SetupSandboxRequest.ts +29 -0
  16. package/api/client/requests/StartWorkerRequest.ts +28 -0
  17. package/api/client/requests/index.ts +12 -0
  18. package/api/errors/BadRequestError.ts +17 -0
  19. package/api/errors/index.ts +1 -0
  20. package/api/index.ts +3 -0
  21. package/api/types/ActiveSessionResponse.ts +6 -0
  22. package/api/types/BackupEnvironmentResponse.ts +7 -0
  23. package/api/types/CdpUrlResponse.ts +12 -0
  24. package/api/types/CloseEnvironmentResponse.ts +6 -0
  25. package/api/types/CreateSandboxResponse.ts +14 -0
  26. package/api/types/CreateSnapshotResponse.ts +10 -0
  27. package/api/types/DeleteSandboxResponse.ts +6 -0
  28. package/api/types/Environment.ts +7 -0
  29. package/api/types/EnvironmentStateResponse.ts +11 -0
  30. package/api/types/ErrorResponse.ts +6 -0
  31. package/api/types/EvaluateResponse.ts +21 -0
  32. package/api/types/EvaluationResult.ts +9 -0
  33. package/api/types/HeartbeatResponse.ts +6 -0
  34. package/api/types/JobStatusResponse.ts +7 -0
  35. package/api/types/LogResponse.ts +5 -0
  36. package/api/types/MakeEnvironmentResponse.ts +6 -0
  37. package/api/types/OperationEvent.ts +42 -0
  38. package/api/types/PlatoConfig.ts +8 -0
  39. package/api/types/PlatoTask.ts +21 -0
  40. package/api/types/PlatoTaskMetadata.ts +20 -0
  41. package/api/types/PostEvaluationResultResponse.ts +6 -0
  42. package/api/types/ProxyUrlResponse.ts +12 -0
  43. package/api/types/ResetEnvironmentResponse.ts +13 -0
  44. package/api/types/RunningSessionsCountResponse.ts +6 -0
  45. package/api/types/Sandbox.ts +10 -0
  46. package/api/types/ScoringType.ts +7 -0
  47. package/api/types/SetupRootAccessResponse.ts +7 -0
  48. package/api/types/SetupSandboxResponse.ts +6 -0
  49. package/api/types/SimConfigCompute.ts +9 -0
  50. package/api/types/SimConfigDataset.ts +10 -0
  51. package/api/types/SimConfigListener.ts +27 -0
  52. package/api/types/SimConfigMetadata.ts +14 -0
  53. package/api/types/SimConfigService.ts +8 -0
  54. package/api/types/Simulator.ts +10 -0
  55. package/api/types/SimulatorListItem.ts +7 -0
  56. package/api/types/SshInfo.ts +18 -0
  57. package/api/types/StartWorkerResponse.ts +7 -0
  58. package/api/types/TaskMetadata.ts +20 -0
  59. package/api/types/TestCase.ts +40 -0
  60. package/api/types/TestCasesResponse.ts +10 -0
  61. package/api/types/Variable.ts +6 -0
  62. package/api/types/WorkerReadyResponse.ts +9 -0
  63. package/api/types/index.ts +42 -0
  64. package/core/fetcher/APIResponse.ts +23 -0
  65. package/core/fetcher/BinaryResponse.ts +36 -0
  66. package/core/fetcher/EndpointMetadata.ts +13 -0
  67. package/core/fetcher/EndpointSupplier.ts +14 -0
  68. package/core/fetcher/Fetcher.ts +165 -0
  69. package/core/fetcher/Headers.ts +93 -0
  70. package/core/fetcher/HttpResponsePromise.ts +116 -0
  71. package/core/fetcher/RawResponse.ts +61 -0
  72. package/core/fetcher/ResponseWithBody.ts +7 -0
  73. package/core/fetcher/Supplier.ts +11 -0
  74. package/core/fetcher/createRequestUrl.ts +6 -0
  75. package/core/fetcher/getErrorResponseBody.ts +33 -0
  76. package/core/fetcher/getFetchFn.ts +3 -0
  77. package/core/fetcher/getHeader.ts +8 -0
  78. package/core/fetcher/getRequestBody.ts +16 -0
  79. package/core/fetcher/getResponseBody.ts +43 -0
  80. package/core/fetcher/index.ts +11 -0
  81. package/core/fetcher/makeRequest.ts +44 -0
  82. package/core/fetcher/requestWithRetries.ts +73 -0
  83. package/core/fetcher/signals.ts +38 -0
  84. package/core/headers.ts +33 -0
  85. package/core/index.ts +4 -0
  86. package/core/json.ts +27 -0
  87. package/core/runtime/index.ts +1 -0
  88. package/core/runtime/runtime.ts +133 -0
  89. package/core/stream/Stream.ts +155 -0
  90. package/core/stream/index.ts +1 -0
  91. package/core/url/encodePathParam.ts +18 -0
  92. package/core/url/index.ts +3 -0
  93. package/core/url/join.ts +80 -0
  94. package/core/url/qs.ts +74 -0
  95. package/dist/BaseClient.d.ts +25 -0
  96. package/dist/Client.d.ts +376 -0
  97. package/dist/api/client/index.d.ts +2 -0
  98. package/dist/api/client/requests/CreateSandboxRequest.d.ts +35 -0
  99. package/dist/api/client/requests/CreateSnapshotRequest.d.ts +10 -0
  100. package/dist/api/client/requests/EvaluateRequest.d.ts +8 -0
  101. package/dist/api/client/requests/GetEnvironmentStateRequest.d.ts +10 -0
  102. package/dist/api/client/requests/GetTestCasesRequest.d.ts +14 -0
  103. package/dist/api/client/requests/LogRequest.d.ts +18 -0
  104. package/dist/api/client/requests/MakeEnvironmentRequest.d.ts +26 -0
  105. package/dist/api/client/requests/PostEvaluationResultRequest.d.ts +13 -0
  106. package/dist/api/client/requests/ResetEnvironmentRequest.d.ts +10 -0
  107. package/dist/api/client/requests/SetupRootAccessRequest.d.ts +13 -0
  108. package/dist/api/client/requests/SetupSandboxRequest.d.ts +27 -0
  109. package/dist/api/client/requests/StartWorkerRequest.d.ts +26 -0
  110. package/dist/api/client/requests/index.d.ts +13 -0
  111. package/dist/api/errors/BadRequestError.d.ts +7 -0
  112. package/dist/api/errors/index.d.ts +2 -0
  113. package/dist/api/index.d.ts +4 -0
  114. package/dist/api/types/ActiveSessionResponse.d.ts +5 -0
  115. package/dist/api/types/BackupEnvironmentResponse.d.ts +6 -0
  116. package/dist/api/types/CdpUrlResponse.d.ts +10 -0
  117. package/dist/api/types/CloseEnvironmentResponse.d.ts +5 -0
  118. package/dist/api/types/CreateSandboxResponse.d.ts +13 -0
  119. package/dist/api/types/CreateSnapshotResponse.d.ts +9 -0
  120. package/dist/api/types/DeleteSandboxResponse.d.ts +5 -0
  121. package/dist/api/types/Environment.d.ts +6 -0
  122. package/dist/api/types/EnvironmentStateResponse.d.ts +9 -0
  123. package/dist/api/types/ErrorResponse.d.ts +5 -0
  124. package/dist/api/types/EvaluateResponse.d.ts +18 -0
  125. package/dist/api/types/EvaluationResult.d.ts +8 -0
  126. package/dist/api/types/HeartbeatResponse.d.ts +5 -0
  127. package/dist/api/types/JobStatusResponse.d.ts +6 -0
  128. package/dist/api/types/LogResponse.d.ts +4 -0
  129. package/dist/api/types/MakeEnvironmentResponse.d.ts +5 -0
  130. package/dist/api/types/OperationEvent.d.ts +40 -0
  131. package/dist/api/types/PlatoConfig.d.ts +6 -0
  132. package/dist/api/types/PlatoTask.d.ts +19 -0
  133. package/dist/api/types/PlatoTaskMetadata.d.ts +18 -0
  134. package/dist/api/types/PostEvaluationResultResponse.d.ts +5 -0
  135. package/dist/api/types/ProxyUrlResponse.d.ts +10 -0
  136. package/dist/api/types/ResetEnvironmentResponse.d.ts +11 -0
  137. package/dist/api/types/RunningSessionsCountResponse.d.ts +5 -0
  138. package/dist/api/types/Sandbox.d.ts +9 -0
  139. package/dist/api/types/ScoringType.d.ts +6 -0
  140. package/dist/api/types/SetupRootAccessResponse.d.ts +6 -0
  141. package/dist/api/types/SetupSandboxResponse.d.ts +5 -0
  142. package/dist/api/types/SimConfigCompute.d.ts +8 -0
  143. package/dist/api/types/SimConfigDataset.d.ts +8 -0
  144. package/dist/api/types/SimConfigListener.d.ts +25 -0
  145. package/dist/api/types/SimConfigMetadata.d.ts +12 -0
  146. package/dist/api/types/SimConfigService.d.ts +7 -0
  147. package/dist/api/types/Simulator.d.ts +9 -0
  148. package/dist/api/types/SimulatorListItem.d.ts +6 -0
  149. package/dist/api/types/SshInfo.d.ts +17 -0
  150. package/dist/api/types/StartWorkerResponse.d.ts +6 -0
  151. package/dist/api/types/TaskMetadata.d.ts +18 -0
  152. package/dist/api/types/TestCase.d.ts +34 -0
  153. package/dist/api/types/TestCasesResponse.d.ts +8 -0
  154. package/dist/api/types/Variable.d.ts +5 -0
  155. package/dist/api/types/WorkerReadyResponse.d.ts +8 -0
  156. package/dist/api/types/index.d.ts +43 -0
  157. package/dist/core/fetcher/APIResponse.d.ts +21 -0
  158. package/dist/core/fetcher/BinaryResponse.d.ts +21 -0
  159. package/dist/core/fetcher/EndpointMetadata.d.ts +14 -0
  160. package/dist/core/fetcher/EndpointSupplier.d.ts +13 -0
  161. package/dist/core/fetcher/Fetcher.d.ts +43 -0
  162. package/dist/core/fetcher/Headers.d.ts +3 -0
  163. package/dist/core/fetcher/HttpResponsePromise.d.ts +59 -0
  164. package/dist/core/fetcher/RawResponse.d.ts +30 -0
  165. package/dist/core/fetcher/ResponseWithBody.d.ts +5 -0
  166. package/dist/core/fetcher/Supplier.d.ts +5 -0
  167. package/dist/core/fetcher/createRequestUrl.d.ts +2 -0
  168. package/dist/core/fetcher/getErrorResponseBody.d.ts +2 -0
  169. package/dist/core/fetcher/getFetchFn.d.ts +2 -0
  170. package/dist/core/fetcher/getHeader.d.ts +2 -0
  171. package/dist/core/fetcher/getRequestBody.d.ts +8 -0
  172. package/dist/core/fetcher/getResponseBody.d.ts +2 -0
  173. package/dist/core/fetcher/index.d.ts +12 -0
  174. package/dist/core/fetcher/makeRequest.d.ts +2 -0
  175. package/dist/core/fetcher/requestWithRetries.d.ts +2 -0
  176. package/dist/core/fetcher/signals.d.ts +12 -0
  177. package/dist/core/headers.d.ts +3 -0
  178. package/dist/core/index.d.ts +5 -0
  179. package/dist/core/json.d.ts +16 -0
  180. package/dist/core/runtime/index.d.ts +2 -0
  181. package/dist/core/runtime/runtime.d.ts +10 -0
  182. package/dist/core/stream/Stream.d.ts +48 -0
  183. package/dist/core/stream/index.d.ts +2 -0
  184. package/dist/core/url/encodePathParam.d.ts +2 -0
  185. package/dist/core/url/index.d.ts +4 -0
  186. package/dist/core/url/join.d.ts +2 -0
  187. package/dist/core/url/qs.d.ts +7 -0
  188. package/dist/errors/ApiError.d.ts +13 -0
  189. package/dist/errors/ApiTimeoutError.d.ts +4 -0
  190. package/dist/errors/index.d.ts +3 -0
  191. package/dist/helpers/SandboxHelpers.d.ts +127 -0
  192. package/dist/helpers/SandboxMonitor.d.ts +89 -0
  193. package/dist/helpers/index.d.ts +9 -0
  194. package/dist/index.d.ts +6 -0
  195. package/errors/ApiError.ts +53 -0
  196. package/errors/ApiTimeoutError.ts +8 -0
  197. package/errors/index.ts +2 -0
  198. package/helpers/README.md +229 -0
  199. package/helpers/SandboxHelpers.ts +213 -0
  200. package/helpers/SandboxMonitor.ts +252 -0
  201. package/helpers/index.ts +10 -0
  202. package/index.ts +7 -0
  203. 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
+
@@ -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";