@watasu/sdk 0.1.6 → 0.1.25
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/README.md +122 -8
- package/dist/codeInterpreter.d.ts +100 -0
- package/dist/codeInterpreter.js +241 -0
- package/dist/commands.d.ts +26 -4
- package/dist/commands.js +54 -15
- package/dist/errors.d.ts +3 -0
- package/dist/errors.js +8 -0
- package/dist/filesystem.d.ts +53 -13
- package/dist/filesystem.js +128 -12
- package/dist/git.d.ts +27 -0
- package/dist/git.js +43 -2
- package/dist/index.d.ts +15 -6
- package/dist/index.js +8 -3
- package/dist/process.d.ts +56 -0
- package/dist/process.js +137 -0
- package/dist/processSocket.d.ts +1 -0
- package/dist/processSocket.js +3 -0
- package/dist/pty.d.ts +5 -1
- package/dist/pty.js +9 -7
- package/dist/sandbox.d.ts +121 -20
- package/dist/sandbox.js +254 -43
- package/dist/template.d.ts +232 -0
- package/dist/template.js +624 -0
- package/dist/terminal.d.ts +41 -0
- package/dist/terminal.js +74 -0
- package/dist/transport.d.ts +1 -0
- package/dist/transport.js +3 -0
- package/package.json +5 -1
package/dist/sandbox.js
CHANGED
|
@@ -1,35 +1,96 @@
|
|
|
1
1
|
import { Commands } from './commands.js';
|
|
2
2
|
import { ConnectionConfig, SESSION_OPERATION_REQUEST_TIMEOUT_MS } from './connectionConfig.js';
|
|
3
3
|
import { DataPlaneClient, ControlClient } from './transport.js';
|
|
4
|
-
import { NotFoundError, SandboxError, unsupported } from './errors.js';
|
|
4
|
+
import { ConflictError, FileNotFoundError, NotFoundError, SandboxError, unsupported } from './errors.js';
|
|
5
5
|
import { Filesystem } from './filesystem.js';
|
|
6
6
|
import { Git } from './git.js';
|
|
7
7
|
import { Pty } from './pty.js';
|
|
8
|
+
import { ProcessManager } from './process.js';
|
|
9
|
+
import { TerminalManager } from './terminal.js';
|
|
10
|
+
/** Paginator for listing sandbox snapshots. */
|
|
8
11
|
export class SnapshotPaginator {
|
|
9
|
-
|
|
10
|
-
consumed = false;
|
|
12
|
+
opts;
|
|
11
13
|
hasNext = true;
|
|
12
14
|
nextToken;
|
|
13
|
-
constructor(
|
|
14
|
-
this.
|
|
15
|
+
constructor(opts = {}) {
|
|
16
|
+
this.opts = opts;
|
|
17
|
+
this.nextToken = opts.nextToken;
|
|
15
18
|
}
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
/** Fetch the next page of snapshot metadata. */
|
|
20
|
+
async nextItems(opts = {}) {
|
|
21
|
+
if (!this.hasNext)
|
|
18
22
|
throw new SandboxError('No more snapshots to fetch');
|
|
19
|
-
this.
|
|
20
|
-
|
|
21
|
-
|
|
23
|
+
const config = new ConnectionConfig({ ...this.opts, ...opts });
|
|
24
|
+
const control = new ControlClient(config);
|
|
25
|
+
const payload = await control.get(snapshotListPath(this.opts, this.nextToken), {
|
|
26
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
27
|
+
});
|
|
28
|
+
this.nextToken = stringValue(payload.next_token ?? payload.nextToken);
|
|
29
|
+
this.hasNext = this.nextToken !== undefined;
|
|
30
|
+
const snapshots = Array.isArray(payload.snapshots)
|
|
31
|
+
? payload.snapshots
|
|
32
|
+
: Array.isArray(payload.sandbox_checkpoints) ? payload.sandbox_checkpoints : [];
|
|
33
|
+
return snapshots.map((item) => snapshotInfo(record(item)));
|
|
34
|
+
}
|
|
35
|
+
/** Drain all remaining pages into one list. */
|
|
36
|
+
async listItems(opts = {}) {
|
|
37
|
+
const items = [];
|
|
38
|
+
while (this.hasNext)
|
|
39
|
+
items.push(...await this.nextItems(opts));
|
|
40
|
+
return items;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Paginator for listing sandboxes. */
|
|
44
|
+
export class SandboxPaginator {
|
|
45
|
+
opts;
|
|
46
|
+
hasNext = true;
|
|
47
|
+
nextToken;
|
|
48
|
+
constructor(opts = {}) {
|
|
49
|
+
this.opts = opts;
|
|
50
|
+
this.nextToken = opts.nextToken;
|
|
51
|
+
}
|
|
52
|
+
/** Fetch the next page of sandbox metadata. */
|
|
53
|
+
async nextItems(opts = {}) {
|
|
54
|
+
if (!this.hasNext)
|
|
55
|
+
throw new SandboxError('No more sandboxes to fetch');
|
|
56
|
+
const config = new ConnectionConfig({ ...this.opts, ...opts });
|
|
57
|
+
const control = new ControlClient(config);
|
|
58
|
+
const payload = await control.get(sandboxListPath(this.opts, this.nextToken), {
|
|
59
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
60
|
+
});
|
|
61
|
+
this.nextToken = stringValue(payload.next_token ?? payload.nextToken);
|
|
62
|
+
this.hasNext = this.nextToken !== undefined;
|
|
63
|
+
const sandboxes = Array.isArray(payload.sandboxes) ? payload.sandboxes : [];
|
|
64
|
+
return sandboxes.map((item) => sandboxInfo(record(item)));
|
|
65
|
+
}
|
|
66
|
+
/** Drain all remaining pages into one list. */
|
|
67
|
+
async listItems(opts = {}) {
|
|
68
|
+
const items = [];
|
|
69
|
+
while (this.hasNext)
|
|
70
|
+
items.push(...await this.nextItems(opts));
|
|
71
|
+
return items;
|
|
22
72
|
}
|
|
23
73
|
}
|
|
24
74
|
/** Running Watasu sandbox with ready `files` and `commands` helpers. */
|
|
25
75
|
export class Sandbox {
|
|
26
76
|
/** Default template slug used when create is called without a template. */
|
|
27
77
|
static defaultTemplate = 'base';
|
|
78
|
+
/** Default template slug used by MCP creation once Watasu supports it. */
|
|
79
|
+
static defaultMcpTemplate = 'mcp-gateway';
|
|
80
|
+
/** Default sandbox lifetime in milliseconds. */
|
|
81
|
+
static defaultSandboxTimeoutMs = 300_000;
|
|
28
82
|
files;
|
|
83
|
+
filesystem;
|
|
29
84
|
commands;
|
|
85
|
+
process;
|
|
30
86
|
pty;
|
|
87
|
+
terminal;
|
|
31
88
|
git;
|
|
89
|
+
cwd;
|
|
90
|
+
envVars;
|
|
32
91
|
sandboxId;
|
|
92
|
+
mcpPort = 50005;
|
|
93
|
+
mcpToken;
|
|
33
94
|
config;
|
|
34
95
|
control;
|
|
35
96
|
envs;
|
|
@@ -40,34 +101,42 @@ export class Sandbox {
|
|
|
40
101
|
this.config = opts.connectionConfig;
|
|
41
102
|
this.control = opts.control ?? new ControlClient(this.config);
|
|
42
103
|
this.envs = opts.envs ?? {};
|
|
104
|
+
this.envVars = this.envs;
|
|
43
105
|
this.sandbox = opts.sandbox ?? {};
|
|
44
106
|
const dataPlane = dataPlaneFromSession(opts.session, this.config);
|
|
45
107
|
this.dataPlane = dataPlane;
|
|
46
108
|
this.files = new Filesystem(dataPlane);
|
|
109
|
+
this.filesystem = this.files;
|
|
47
110
|
this.commands = new Commands(dataPlane, this.config, this.envs);
|
|
111
|
+
this.process = new ProcessManager(this.commands);
|
|
48
112
|
this.pty = new Pty(dataPlane, this.config);
|
|
113
|
+
this.terminal = new TerminalManager(this.pty);
|
|
49
114
|
this.git = new Git(dataPlane);
|
|
50
115
|
}
|
|
116
|
+
/** Sandbox id alias used by SDK-compatible code. */
|
|
117
|
+
get id() {
|
|
118
|
+
return this.sandboxId;
|
|
119
|
+
}
|
|
51
120
|
/** Create a sandbox and return it only after the API supplies a data-plane session. */
|
|
52
121
|
static async create(templateOrOpts, opts = {}) {
|
|
122
|
+
const sandboxOpts = typeof templateOrOpts === 'string' ? opts : templateOrOpts ?? {};
|
|
53
123
|
const template = typeof templateOrOpts === 'string'
|
|
54
124
|
? templateOrOpts
|
|
55
|
-
: templateOrOpts?.template ??
|
|
56
|
-
const sandboxOpts = typeof templateOrOpts === 'string' ? opts : templateOrOpts ?? {};
|
|
57
|
-
if (sandboxOpts.mcp !== undefined)
|
|
58
|
-
unsupported('mcp');
|
|
125
|
+
: templateOrOpts?.template ?? (sandboxOpts.mcp === undefined ? this.defaultTemplate : undefined);
|
|
59
126
|
if (sandboxOpts.volumeMounts !== undefined)
|
|
60
127
|
unsupported('volumeMounts');
|
|
61
128
|
const config = new ConnectionConfig(sandboxOpts);
|
|
62
129
|
const control = new ControlClient(config);
|
|
63
130
|
const sandboxPayload = {
|
|
64
|
-
template_id: template,
|
|
65
131
|
timeout: Math.ceil((sandboxOpts.timeoutMs ?? 300_000) / 1000),
|
|
66
132
|
metadata: sandboxOpts.metadata ?? {},
|
|
67
133
|
env_vars: sandboxOpts.envs ?? {},
|
|
68
134
|
secure: sandboxOpts.secure ?? true,
|
|
69
135
|
allow_internet_access: sandboxOpts.allowInternetAccess ?? true,
|
|
70
136
|
};
|
|
137
|
+
putIfPresent(sandboxPayload, 'template_id', template);
|
|
138
|
+
putIfPresent(sandboxPayload, 'mcp', sandboxOpts.mcp);
|
|
139
|
+
Object.assign(sandboxPayload, networkUpdatePayload(sandboxOpts.network));
|
|
71
140
|
putIfPresent(sandboxPayload, 'team', sandboxOpts.team);
|
|
72
141
|
const response = await control.post('/sandboxes', {
|
|
73
142
|
json: sandboxPayload,
|
|
@@ -77,7 +146,7 @@ export class Sandbox {
|
|
|
77
146
|
const sandboxId = sandbox.id ?? sandbox.sandbox_id;
|
|
78
147
|
if (sandboxId === undefined)
|
|
79
148
|
throw new SandboxError('create response did not include sandbox id');
|
|
80
|
-
|
|
149
|
+
const sandboxInstance = new this({
|
|
81
150
|
sandboxId: String(sandboxId),
|
|
82
151
|
connectionConfig: config,
|
|
83
152
|
control,
|
|
@@ -85,17 +154,18 @@ export class Sandbox {
|
|
|
85
154
|
sandbox,
|
|
86
155
|
envs: sandboxOpts.envs,
|
|
87
156
|
});
|
|
157
|
+
return sandboxInstance;
|
|
88
158
|
}
|
|
89
159
|
/** Connect to an existing sandbox and return it with a fresh data-plane session. */
|
|
90
160
|
static async connect(sandboxId, opts = {}) {
|
|
91
161
|
const config = new ConnectionConfig(opts);
|
|
92
162
|
const control = new ControlClient(config);
|
|
93
163
|
const info = await control.get(`/sandboxes/${sandboxId}`);
|
|
94
|
-
const response = await control.post(`/sandboxes/${sandboxId}/
|
|
164
|
+
const response = await control.post(`/sandboxes/${sandboxId}/resume`, {
|
|
95
165
|
json: opts.timeoutMs ? { timeout: Math.ceil(opts.timeoutMs / 1000) } : {},
|
|
96
166
|
requestTimeoutMs: sessionOperationRequestTimeout(config, opts),
|
|
97
167
|
});
|
|
98
|
-
return new
|
|
168
|
+
return new this({
|
|
99
169
|
sandboxId,
|
|
100
170
|
connectionConfig: config,
|
|
101
171
|
control,
|
|
@@ -103,9 +173,14 @@ export class Sandbox {
|
|
|
103
173
|
sandbox: record(response.sandbox ?? info.sandbox ?? {}),
|
|
104
174
|
});
|
|
105
175
|
}
|
|
176
|
+
static async reconnect(sandboxOrOpts, opts = {}) {
|
|
177
|
+
if (typeof sandboxOrOpts === 'string')
|
|
178
|
+
return this.connect(sandboxOrOpts, opts);
|
|
179
|
+
return this.connect(sandboxOrOpts.sandboxID, sandboxOrOpts);
|
|
180
|
+
}
|
|
106
181
|
/** Refresh this sandbox's data-plane session in place. */
|
|
107
182
|
async connect(opts = {}) {
|
|
108
|
-
const response = await this.control.post(`/sandboxes/${this.sandboxId}/
|
|
183
|
+
const response = await this.control.post(`/sandboxes/${this.sandboxId}/resume`, {
|
|
109
184
|
json: opts.timeoutMs ? { timeout: Math.ceil(opts.timeoutMs / 1000) } : {},
|
|
110
185
|
requestTimeoutMs: sessionOperationRequestTimeout(this.config, opts),
|
|
111
186
|
});
|
|
@@ -113,14 +188,41 @@ export class Sandbox {
|
|
|
113
188
|
const dataPlane = dataPlaneFromSession(response.session, this.config);
|
|
114
189
|
this.dataPlane = dataPlane;
|
|
115
190
|
this.files = new Filesystem(dataPlane);
|
|
191
|
+
this.filesystem = this.files;
|
|
116
192
|
this.commands = new Commands(dataPlane, this.config, this.envs);
|
|
193
|
+
this.process = new ProcessManager(this.commands);
|
|
117
194
|
this.pty = new Pty(dataPlane, this.config);
|
|
195
|
+
this.terminal = new TerminalManager(this.pty);
|
|
118
196
|
this.git = new Git(dataPlane);
|
|
119
197
|
return this;
|
|
120
198
|
}
|
|
199
|
+
/** Resume a paused sandbox by id. */
|
|
200
|
+
static async resume(sandboxId, opts = {}) {
|
|
201
|
+
await Sandbox.connect(sandboxId, opts);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
/** Pause a sandbox by id. Returns false when it was already paused. */
|
|
205
|
+
static async betaPause(sandboxId, opts = {}) {
|
|
206
|
+
const control = new ControlClient(new ConnectionConfig(opts));
|
|
207
|
+
try {
|
|
208
|
+
await control.post(`/sandboxes/${sandboxId}/pause`, {
|
|
209
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
210
|
+
});
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
catch (error) {
|
|
214
|
+
if (error instanceof ConflictError)
|
|
215
|
+
return false;
|
|
216
|
+
throw error;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/** Alias for `betaPause`. */
|
|
220
|
+
static async pause(sandboxId, opts = {}) {
|
|
221
|
+
return this.betaPause(sandboxId, opts);
|
|
222
|
+
}
|
|
121
223
|
/** Destroy a sandbox by id. */
|
|
122
224
|
static async kill(sandboxId, opts = {}) {
|
|
123
|
-
const control = new ControlClient(new ConnectionConfig(opts));
|
|
225
|
+
const control = new ControlClient(new ConnectionConfig(typeof opts === 'string' ? { apiKey: opts } : opts));
|
|
124
226
|
await control.delete(`/sandboxes/${sandboxId}`);
|
|
125
227
|
return true;
|
|
126
228
|
}
|
|
@@ -132,6 +234,18 @@ export class Sandbox {
|
|
|
132
234
|
});
|
|
133
235
|
return metricsList(payload.metrics ?? payload);
|
|
134
236
|
}
|
|
237
|
+
/** Atomically replace a sandbox's network egress policy by id. */
|
|
238
|
+
static async updateNetwork(sandboxId, network, opts = {}) {
|
|
239
|
+
await this.putNetwork(sandboxId, network, opts);
|
|
240
|
+
}
|
|
241
|
+
static async putNetwork(sandboxId, network, opts = {}) {
|
|
242
|
+
const control = new ControlClient(new ConnectionConfig(opts));
|
|
243
|
+
const response = await control.put(`/sandboxes/${sandboxId}/network`, {
|
|
244
|
+
json: networkUpdatePayload(network),
|
|
245
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
246
|
+
});
|
|
247
|
+
return response.sandbox === undefined ? undefined : record(response.sandbox);
|
|
248
|
+
}
|
|
135
249
|
/** Deprecated alias for `getInfo`. */
|
|
136
250
|
static async getFullInfo(sandboxId, opts = {}) {
|
|
137
251
|
return this.getInfo(sandboxId, opts);
|
|
@@ -145,16 +259,9 @@ export class Sandbox {
|
|
|
145
259
|
});
|
|
146
260
|
return snapshotInfo(record(payload.sandbox_checkpoint ?? payload.snapshot ?? payload));
|
|
147
261
|
}
|
|
148
|
-
/** List
|
|
149
|
-
static listSnapshots(
|
|
150
|
-
return new SnapshotPaginator(
|
|
151
|
-
const control = new ControlClient(new ConnectionConfig(opts));
|
|
152
|
-
const payload = await control.get(`/sandboxes/${sandboxId}/checkpoints`, {
|
|
153
|
-
requestTimeoutMs: opts.requestTimeoutMs,
|
|
154
|
-
});
|
|
155
|
-
const snapshots = Array.isArray(payload.sandbox_checkpoints) ? payload.sandbox_checkpoints : [];
|
|
156
|
-
return snapshots.map((item) => snapshotInfo(record(item)));
|
|
157
|
-
});
|
|
262
|
+
/** List snapshots visible to the configured API key. */
|
|
263
|
+
static listSnapshots(opts = {}) {
|
|
264
|
+
return new SnapshotPaginator(opts);
|
|
158
265
|
}
|
|
159
266
|
/** Delete a snapshot by id. Returns `false` when the snapshot does not exist. */
|
|
160
267
|
static async deleteSnapshot(snapshotId, opts = {}) {
|
|
@@ -204,6 +311,10 @@ export class Sandbox {
|
|
|
204
311
|
json: { timeout: Math.ceil(timeoutMs / 1000) },
|
|
205
312
|
});
|
|
206
313
|
}
|
|
314
|
+
/** Keep the sandbox alive for `duration` milliseconds. */
|
|
315
|
+
async keepAlive(duration) {
|
|
316
|
+
await this.setTimeout(duration);
|
|
317
|
+
}
|
|
207
318
|
/** Fetch control-plane metadata for a sandbox by id. */
|
|
208
319
|
static async getInfo(sandboxId, opts = {}) {
|
|
209
320
|
const control = new ControlClient(new ConnectionConfig(opts));
|
|
@@ -233,7 +344,7 @@ export class Sandbox {
|
|
|
233
344
|
}
|
|
234
345
|
/** List checkpoints for this sandbox using snapshot naming. */
|
|
235
346
|
listSnapshots(opts = {}) {
|
|
236
|
-
return Sandbox.listSnapshots(
|
|
347
|
+
return Sandbox.listSnapshots({ ...this.configOptions(), ...opts, sandboxId: this.sandboxId });
|
|
237
348
|
}
|
|
238
349
|
/** Restore a checkpoint into a new sandbox and return its control-plane info. */
|
|
239
350
|
async restore(opts = {}) {
|
|
@@ -254,12 +365,10 @@ export class Sandbox {
|
|
|
254
365
|
});
|
|
255
366
|
return sandboxInfo(record(response.sandbox ?? response));
|
|
256
367
|
}
|
|
257
|
-
/**
|
|
258
|
-
static
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const sandboxes = Array.isArray(payload.sandboxes) ? payload.sandboxes : [];
|
|
262
|
-
return sandboxes.map((item) => sandboxInfo(record(item)));
|
|
368
|
+
/** Return a paginator for sandboxes visible to the configured API key. */
|
|
369
|
+
static list(opts = {}) {
|
|
370
|
+
const listOpts = typeof opts === 'string' ? { apiKey: opts } : opts;
|
|
371
|
+
return new SandboxPaginator(listOpts);
|
|
263
372
|
}
|
|
264
373
|
/** Return the public hostname for an exposed sandbox port. */
|
|
265
374
|
getHost(port) {
|
|
@@ -270,8 +379,39 @@ export class Sandbox {
|
|
|
270
379
|
throw new SandboxError('port response did not include host or url');
|
|
271
380
|
return `p${port}-${routeToken}.sandbox.${this.config.dataPlaneDomain}`;
|
|
272
381
|
}
|
|
382
|
+
/** Return the public hostname for the sandbox or an exposed sandbox port. */
|
|
383
|
+
getHostname(port) {
|
|
384
|
+
if (port !== undefined)
|
|
385
|
+
return this.getHost(port);
|
|
386
|
+
return new URL(this.dataPlane.baseUrl).host;
|
|
387
|
+
}
|
|
388
|
+
/** Return the conventional MCP URL for this sandbox. */
|
|
389
|
+
getMcpUrl() {
|
|
390
|
+
return `https://${this.getHost(this.mcpPort)}/mcp`;
|
|
391
|
+
}
|
|
392
|
+
/** Return the MCP gateway token when the sandbox contains one. */
|
|
393
|
+
async getMcpToken() {
|
|
394
|
+
if (this.mcpToken !== undefined)
|
|
395
|
+
return this.mcpToken;
|
|
396
|
+
try {
|
|
397
|
+
const token = await this.files.read('/etc/mcp-gateway/.token', { user: 'root' });
|
|
398
|
+
this.mcpToken = String(token).trim() || undefined;
|
|
399
|
+
return this.mcpToken;
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
if (error instanceof FileNotFoundError || error instanceof NotFoundError)
|
|
403
|
+
return undefined;
|
|
404
|
+
throw error;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/** Return a protocol string for a secure or insecure sandbox URL. */
|
|
408
|
+
getProtocol(baseProtocol = 'http', secure = true) {
|
|
409
|
+
return `${baseProtocol}${secure ? 's' : ''}`;
|
|
410
|
+
}
|
|
411
|
+
/** Close the local SDK attachment. This does not destroy the sandbox. */
|
|
412
|
+
async close() { }
|
|
273
413
|
/** Get a signed URL that accepts a POST upload for a sandbox file path. */
|
|
274
|
-
async uploadUrl(path, opts = {}) {
|
|
414
|
+
async uploadUrl(path = '', opts = {}) {
|
|
275
415
|
const fileUrl = await this.fileUrl('/upload_url', path, opts);
|
|
276
416
|
return fileUrl.url;
|
|
277
417
|
}
|
|
@@ -281,17 +421,31 @@ export class Sandbox {
|
|
|
281
421
|
return fileUrl.url;
|
|
282
422
|
}
|
|
283
423
|
/** Get signed upload URL metadata for a sandbox file path. */
|
|
284
|
-
async uploadUrlInfo(path, opts = {}) {
|
|
424
|
+
async uploadUrlInfo(path = '', opts = {}) {
|
|
285
425
|
return this.fileUrl('/upload_url', path, opts);
|
|
286
426
|
}
|
|
287
427
|
/** Get signed download URL metadata for a sandbox file path. */
|
|
288
428
|
async downloadUrlInfo(path, opts = {}) {
|
|
289
429
|
return this.fileUrl('/download_url', path, opts);
|
|
290
430
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
431
|
+
/** Atomically replace this sandbox's network egress policy. */
|
|
432
|
+
async updateNetwork(network, opts = {}) {
|
|
433
|
+
const sandbox = await Sandbox.putNetwork(this.sandboxId, network, { ...this.configOptions(), ...opts });
|
|
434
|
+
this.sandbox = sandbox ?? this.sandbox;
|
|
435
|
+
}
|
|
436
|
+
/** Pause this sandbox. Returns false when it was already paused. */
|
|
437
|
+
async betaPause(opts = {}) {
|
|
438
|
+
return Sandbox.betaPause(this.sandboxId, { ...this.configOptions(), ...opts });
|
|
439
|
+
}
|
|
440
|
+
/** Alias for `betaPause`. */
|
|
441
|
+
async pause(opts = {}) {
|
|
442
|
+
return this.betaPause(opts);
|
|
443
|
+
}
|
|
444
|
+
/** Resume this sandbox and refresh its data-plane session. */
|
|
445
|
+
async resume(opts = {}) {
|
|
446
|
+
await this.connect(opts);
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
295
449
|
async fileUrl(route, path, opts) {
|
|
296
450
|
const payload = await this.control.post(`/sandboxes/${this.sandboxId}/files${route}`, {
|
|
297
451
|
json: compactRecord({
|
|
@@ -304,6 +458,13 @@ export class Sandbox {
|
|
|
304
458
|
});
|
|
305
459
|
return fileUrlInfo(record(payload.file_url ?? payload));
|
|
306
460
|
}
|
|
461
|
+
/** POST JSON to the sandbox data-plane runtime API. */
|
|
462
|
+
async runtimePostJson(path, json, opts = {}) {
|
|
463
|
+
return this.dataPlane.postJson(path, {
|
|
464
|
+
json,
|
|
465
|
+
requestTimeoutMs: opts.requestTimeoutMs,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
307
468
|
configOptions() {
|
|
308
469
|
return {
|
|
309
470
|
apiKey: this.config.apiKey,
|
|
@@ -324,6 +485,36 @@ function dataPlaneFromSession(session, config) {
|
|
|
324
485
|
}
|
|
325
486
|
return new DataPlaneClient(url, token, config);
|
|
326
487
|
}
|
|
488
|
+
function sandboxListPath(opts, nextToken) {
|
|
489
|
+
const params = new URLSearchParams();
|
|
490
|
+
if (opts.team)
|
|
491
|
+
params.set('team', opts.team);
|
|
492
|
+
if (opts.limit !== undefined)
|
|
493
|
+
params.set('limit', String(opts.limit));
|
|
494
|
+
if (nextToken)
|
|
495
|
+
params.set('next_token', nextToken);
|
|
496
|
+
if (opts.query?.metadata) {
|
|
497
|
+
for (const [key, value] of Object.entries(opts.query.metadata)) {
|
|
498
|
+
params.append(`query[metadata][${key}]`, value);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
for (const state of opts.query?.state ?? []) {
|
|
502
|
+
params.append('query[state][]', state);
|
|
503
|
+
}
|
|
504
|
+
const query = params.toString();
|
|
505
|
+
return query ? `/sandboxes?${query}` : '/sandboxes';
|
|
506
|
+
}
|
|
507
|
+
function snapshotListPath(opts, nextToken) {
|
|
508
|
+
const params = new URLSearchParams();
|
|
509
|
+
if (opts.sandboxId)
|
|
510
|
+
params.set('sandbox_id', opts.sandboxId);
|
|
511
|
+
if (opts.limit !== undefined)
|
|
512
|
+
params.set('limit', String(opts.limit));
|
|
513
|
+
if (nextToken)
|
|
514
|
+
params.set('next_token', nextToken);
|
|
515
|
+
const query = params.toString();
|
|
516
|
+
return query ? `/sandbox_snapshots?${query}` : '/sandbox_snapshots';
|
|
517
|
+
}
|
|
327
518
|
function fileUrlInfo(payload) {
|
|
328
519
|
return {
|
|
329
520
|
method: String(payload.method ?? ''),
|
|
@@ -413,6 +604,26 @@ function putIfPresent(target, key, value) {
|
|
|
413
604
|
if (value !== undefined && value !== null)
|
|
414
605
|
target[key] = value;
|
|
415
606
|
}
|
|
607
|
+
function networkUpdatePayload(network) {
|
|
608
|
+
if (network === undefined)
|
|
609
|
+
return {};
|
|
610
|
+
if (typeof network !== 'object' || network === null)
|
|
611
|
+
unsupported('network callable rules');
|
|
612
|
+
if (network.rules !== undefined)
|
|
613
|
+
unsupported('network rules');
|
|
614
|
+
if (network.maskRequestHost !== undefined)
|
|
615
|
+
unsupported('network request host masking');
|
|
616
|
+
return compactRecord({
|
|
617
|
+
allow_out: network.allowOut,
|
|
618
|
+
deny_out: network.denyOut,
|
|
619
|
+
allow_internet_access: network.allowInternetAccess,
|
|
620
|
+
allow_package_registry_access: network.allowPackageRegistryAccess,
|
|
621
|
+
allow_public_traffic: network.allowPublicTraffic,
|
|
622
|
+
egress_profile: network.egressProfile,
|
|
623
|
+
egress_profiles: network.egressProfiles,
|
|
624
|
+
network_class: network.networkClass,
|
|
625
|
+
});
|
|
626
|
+
}
|
|
416
627
|
function record(value) {
|
|
417
628
|
return value && typeof value === 'object' ? value : {};
|
|
418
629
|
}
|