@adobe/aio-lib-sandbox 0.1.0-alpha.4 → 0.1.0-alpha.5

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 CHANGED
@@ -1,7 +1,10 @@
1
1
  # App Builder Sandbox SDK
2
2
 
3
3
  [![Version](https://img.shields.io/npm/v/@adobe/aio-lib-sandbox.svg)](https://npmjs.org/package/@adobe/aio-lib-sandbox)
4
+ [![Downloads/week](https://img.shields.io/npm/dw/@adobe/aio-lib-sandbox.svg)](https://npmjs.org/package/@adobe/aio-lib-sandbox)
5
+ ![Node.js CI](https://github.com/adobe/aio-lib-sandbox/workflows/Node.js%20CI/badge.svg)
4
6
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
7
+ [![Codecov Coverage](https://img.shields.io/codecov/c/github/adobe/aio-lib-sandbox/main.svg?style=flat-square)](https://codecov.io/gh/adobe/aio-lib-sandbox/)
5
8
  ![Status](https://img.shields.io/badge/status-alpha-orange.svg)
6
9
 
7
10
  JavaScript SDK for Adobe Runtime Sandboxes.
@@ -23,7 +26,7 @@ npm install @adobe/aio-lib-sandbox@alpha
23
26
 
24
27
  ## Quickstart
25
28
 
26
- Inside a Runtime action, credentials are read automatically from the environment.
29
+ Inside a Runtime action, no configuration is needed to use the SDK as credentials are read automatically from the environment.
27
30
 
28
31
  ```js
29
32
  const { Sandbox } = require('@adobe/aio-lib-sandbox')
@@ -72,6 +75,7 @@ const sandbox = await Sandbox.create({
72
75
  name: 'my-sandbox',
73
76
  type: 'cpu:default',
74
77
  maxLifetime: 3600,
78
+ ports: [3000, 8080],
75
79
  envs: { API_KEY: 'your-api-key' }
76
80
  })
77
81
  ```
@@ -93,6 +97,31 @@ console.log('exit code:', result.exitCode)
93
97
 
94
98
  > Note: Commands run in the `/workspace` directory by default, this is not configurable
95
99
 
100
+ ### Detached Commands
101
+
102
+ Pass `detached: true` to run a long-lived background process.
103
+
104
+ ```js
105
+ // Start a background server
106
+ const command = await sandbox.exec('node server.js', { detached: true })
107
+
108
+ // Wait for it to exit (e.g. after you stop it)
109
+ const result = await command.wait()
110
+ console.log('exit code:', result.exitCode)
111
+
112
+ // Send a signal to stop it
113
+ await command.kill()
114
+ ```
115
+
116
+ If the process is still running and you need a handle to it from a different context, use `getCommand()` to re-attach by `execId`:
117
+
118
+ ```js
119
+ const command = await sandbox.getCommand(execId, { onOutput: (data, stream) => process.stdout.write(data) })
120
+ await command.wait()
121
+ ```
122
+
123
+ > Note: Only 5 background processes are allowed to run at once currently.
124
+
96
125
  ### File Management
97
126
 
98
127
  ```js
@@ -146,11 +175,20 @@ await sandbox.destroy()
146
175
 
147
176
  ### Preview URLs
148
177
 
149
- Use preview URLs to get access to servers or web services running in a sandbox on a particular port:
178
+ Ports that should be publicly accessible must be declared at creation time via the `ports` array.
150
179
 
151
180
  ```js
152
- const url = await sandbox.getUrl({port: 3000})
153
- console.log("preview:", url)
181
+ const sandbox = await Sandbox.create({
182
+ name: 'web-sandbox',
183
+ ports: [3000, 8080]
184
+ })
185
+
186
+ // Start a server inside the sandbox on the declared port
187
+ await sandbox.exec('node server.js &', { timeout: 50_000 })
188
+
189
+ // Retrieve the pre-provisioned preview URL — synchronous, no network call
190
+ const url = sandbox.getUrl(3000)
191
+ console.log('preview:', url)
154
192
  // https://sb-abc123-va6-0-xK3mPq2nAeB-3000.sandbox-adobeioruntime.net
155
193
  ```
156
194
 
package/RELEASING.md ADDED
@@ -0,0 +1,36 @@
1
+ # Releasing
2
+
3
+ Releases are managed with [`np`](https://github.com/sindresorhus/np)
4
+
5
+ ## Prerequisites
6
+
7
+ Install `np`:
8
+
9
+ ```bash
10
+ npm install --global np
11
+ ```
12
+
13
+ Ensure your working tree is clean and you are on the `main` branch with the latest changes pulled:
14
+
15
+ ```bash
16
+ git checkout main && git pull
17
+ ```
18
+
19
+ ## Steps
20
+
21
+ 1. **Run `np` with `--no-publish`** and the target version to bump the version, generate the changelog, create the git tag, and push without publishing to npm:
22
+
23
+ ```bash
24
+ np 0.1.0-alpha.5 --no-publish
25
+ ```
26
+
27
+ Replace `0.1.0-alpha.5` with the appropriate next version.
28
+
29
+ 2. In the popup GitHub browser window, add the release version as the title and create the new release.
30
+
31
+ 3. Actual publishing should be handled by the `on-push-publish-to-npm.yml` workflow automatically.
32
+
33
+ ## Notes
34
+
35
+ - This package is in **alpha**. Prefer pre-release version increments (`alpha.x`) until the API is stable.
36
+ - If `np` complains about a dirty working tree or unpushed commits, resolve those before proceeding.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/aio-lib-sandbox",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "JavaScript SDK for Adobe Runtime Sandboxes",
5
5
  "main": "src/index.js",
6
6
  "license": "Apache-2.0",
package/src/Sandbox.js CHANGED
@@ -13,7 +13,9 @@ const crypto = require('node:crypto')
13
13
  const {
14
14
  SandboxClientError,
15
15
  SandboxTimeoutError,
16
- SandboxWebSocketError
16
+ SandboxWebSocketError,
17
+ SandboxPortNotProvisionedError,
18
+ SandboxInvalidPortError
17
19
  } = require('./errors')
18
20
  const {
19
21
  buildWebSocketEndpoint,
@@ -34,7 +36,7 @@ class Sandbox {
34
36
  * @param {object} options sandbox options
35
37
  * @private
36
38
  */
37
- constructor (options) {
39
+ constructor(options) {
38
40
  this.id = options.id
39
41
  this.endpoint = options.endpoint
40
42
  this.status = options.status
@@ -46,7 +48,8 @@ class Sandbox {
46
48
  this.apiHost = options.apiHost
47
49
  this.apiKey = options.apiKey
48
50
  this.token = options.token
49
- this.publicUrlTemplate = options.publicUrlTemplate || null
51
+ // previewUrls is a Map<number, string> of (port → URL) returned by the server.
52
+ this.previewUrls = options.previewUrls || new Map()
50
53
  this.managementEndpoint = options.managementEndpoint || null
51
54
  this.ws = null
52
55
  }
@@ -68,11 +71,12 @@ class Sandbox {
68
71
  * @param {string} [options.type] sandbox type (default: `'cpu:default'`)
69
72
  * @param {string|object} [options.size] sandbox size tier (name or spec object)
70
73
  * @param {number} [options.maxLifetime] maximum lifetime in seconds
74
+ * @param {number[]} [options.ports] TCP ports to expose via preview URLs (default: `[]`)
71
75
  * @param {object} [options.envs] environment variables to inject into the sandbox
72
76
  * @param {object} [options.policy] network policy (e.g. egress allowlist)
73
77
  * @returns {Promise<Sandbox>} connected sandbox instance
74
78
  */
75
- static async create (options = {}) {
79
+ static async create(options = {}) {
76
80
  console.warn('[aio-lib-sandbox] alpha — APIs may change without notice')
77
81
  const creds = resolveCredentials(options)
78
82
 
@@ -87,6 +91,7 @@ class Sandbox {
87
91
  if (options.region !== undefined) body.region = options.region
88
92
  if (options.envs !== undefined) body.envs = options.envs
89
93
  if (options.policy !== undefined) body.policy = options.policy
94
+ if (options.ports !== undefined) body.ports = options.ports
90
95
 
91
96
  const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandbox`
92
97
  const payload = await apiRequest('POST', url, creds.apiKey, body)
@@ -101,7 +106,7 @@ class Sandbox {
101
106
  cluster: payload.cluster,
102
107
  region: payload.region,
103
108
  maxLifetime: payload.maxLifetime,
104
- publicUrlTemplate: payload.publicUrlTemplate || null,
109
+ previewUrls: parsePreviewUrls(payload.previewUrls),
105
110
  managementEndpoint: payload.managementEndpoint || null,
106
111
  namespace: creds.namespace,
107
112
  apiHost: creds.apiHost,
@@ -126,7 +131,7 @@ class Sandbox {
126
131
  * @param {string} [options.auth] Runtime API key
127
132
  * @returns {Promise<Sandbox>} sandbox instance with `status` populated (not WebSocket-connected)
128
133
  */
129
- static async get (sandboxId, options = {}) {
134
+ static async get(sandboxId, options = {}) {
130
135
  console.warn('[aio-lib-sandbox] alpha — APIs may change without notice')
131
136
  const creds = resolveCredentials(options)
132
137
  const url = `${creds.apiHost}/api/v1/namespaces/${creds.namespace}/sandbox/${sandboxId}`
@@ -139,6 +144,7 @@ class Sandbox {
139
144
  cluster: payload.cluster,
140
145
  region: payload.region,
141
146
  maxLifetime: payload.maxLifetime,
147
+ previewUrls: parsePreviewUrls(payload.previewUrls),
142
148
  namespace: creds.namespace,
143
149
  apiHost: creds.apiHost,
144
150
  apiKey: creds.apiKey,
@@ -151,7 +157,7 @@ class Sandbox {
151
157
  *
152
158
  * @type {object}
153
159
  */
154
- static get sizes () {
160
+ static get sizes() {
155
161
  return SANDBOX_SIZES
156
162
  }
157
163
 
@@ -161,7 +167,7 @@ class Sandbox {
161
167
  * @param {object} overrides credential overrides
162
168
  * @returns {{ apiHost: string, namespace: string, apiKey: string }}
163
169
  */
164
- static resolveCredentials (overrides = {}) {
170
+ static resolveCredentials(overrides = {}) {
165
171
  return resolveCredentials(overrides)
166
172
  }
167
173
 
@@ -171,7 +177,7 @@ class Sandbox {
171
177
  * @param {string|object|undefined} size
172
178
  * @returns {string}
173
179
  */
174
- static normalizeSize (size) {
180
+ static normalizeSize(size) {
175
181
  return normalizeSize(size)
176
182
  }
177
183
 
@@ -184,7 +190,7 @@ class Sandbox {
184
190
  *
185
191
  * @returns {Promise<void>}
186
192
  */
187
- connect () {
193
+ connect() {
188
194
  if (!this.ws) {
189
195
  this.ws = new SandboxSocket({
190
196
  id: this.id,
@@ -202,62 +208,110 @@ class Sandbox {
202
208
  /**
203
209
  * Executes a command inside the sandbox.
204
210
  *
205
- * Returns a Promise (with an `execId` property) that resolves to
206
- * `{ execId, stdout, stderr, exitCode }` when the command completes.
211
+ * `options.timeout` is not supported with `options.detached: true`.
207
212
  *
208
213
  * @param {string} command command to run
209
214
  * @param {object} [options] execution options
210
- * @param {number} [options.timeout] timeout in milliseconds
215
+ * @param {number} [options.timeout] timeout in milliseconds (foreground only)
216
+ * @param {boolean} [options.detached] when true, run as a detached background process
211
217
  * @param {string|Buffer} [options.stdin] data to send to stdin at startup
212
218
  * @param {function} [options.onOutput] callback called with `(data, stream)` for each output chunk
213
- * @returns {Promise<{execId: string, stdout: string, stderr: string, exitCode: number}>}
219
+ * @returns {Promise}
214
220
  */
215
- exec (command, options = {}) {
221
+ exec(command, options = {}) {
216
222
  try {
217
223
  this.ensureOpen()
218
224
  } catch (error) {
219
225
  return Promise.reject(error)
220
226
  }
221
227
 
228
+ if (options.detached && options.timeout) {
229
+ return Promise.reject(new SandboxClientError(
230
+ 'cannot set a timeout for a detached command'
231
+ ))
232
+ }
233
+
222
234
  const execId = `exec-${crypto.randomBytes(12).toString('hex')}`
223
- let timeoutHandle
235
+ const promise = this.sendExecFrameAndAwaitResponse(execId, command, options)
236
+ promise.execId = execId
237
+ return promise
238
+ }
239
+
240
+ /**
241
+ * @param {string} execId
242
+ * @param {string} command
243
+ * @param {object} options
244
+ * @private
245
+ */
246
+ async sendExecFrameAndAwaitResponse(execId, command, options) {
247
+ const detached = !!options.detached
248
+ const frame = { type: 'exec.run', execId, command, ...(detached && { detached: true }) }
249
+
250
+ const { ackPromise, waitPromise } = this.ws.sendExec(execId, frame, {
251
+ detached,
252
+ onOutput: options.onOutput
253
+ })
254
+
255
+ if (options.timeout) {
256
+ this.ws.setExecTimeout(execId, this.scheduleTimeout(execId, command, options.timeout))
257
+ }
258
+
259
+ if (options.stdin !== undefined) {
260
+ this.writeStdin(execId, options.stdin)
261
+ this.closeStdin(execId)
262
+ }
263
+
264
+ const result = await ackPromise
224
265
 
225
- const execPromise = new Promise((resolve, reject) => {
226
- this.ws.pendingExecs.set(execId, {
266
+ if (!detached) return result
267
+
268
+ const { pid, startedAt } = result
269
+ return {
270
+ execId,
271
+ pid,
272
+ startedAt,
273
+ detached: true,
274
+ wait: () => waitPromise,
275
+ writeStdin: (data) => this.writeStdin(execId, data),
276
+ closeStdin: () => this.closeStdin(execId),
277
+ kill: (signal) => this.kill(execId, signal)
278
+ }
279
+ }
280
+
281
+ /**
282
+ * Re-attaches to a detached command that is still running in the sandbox.
283
+ *
284
+ * @param {string} execId the execId returned by the original `exec()` call
285
+ * @param {object} [options] re-attach options
286
+ * @param {function} [options.onOutput] callback called with `(data, stream)` for live output
287
+ * @returns {Promise<{execId, command, pid, startedAt, detached, wait, kill, writeStdin, closeStdin}>}
288
+ */
289
+ getCommand(execId, options = {}) {
290
+ try {
291
+ this.ensureOpen()
292
+ } catch (error) {
293
+ return Promise.reject(error)
294
+ }
295
+
296
+ const getPromise = new Promise((resolve, reject) => {
297
+ this.ws.pendingGetOps.set(execId, {
227
298
  resolve,
228
299
  reject,
229
- stdout: '',
230
- stderr: '',
231
- onOutput: options.onOutput,
232
- timeout: undefined
300
+ onOutput: options.onOutput || null,
301
+ sandbox: this
233
302
  })
234
-
235
- if (options.timeout) {
236
- timeoutHandle = setTimeout(() => {
237
- try { this.kill(execId) } catch (_) {}
238
- this.ws.rejectExec(execId, new SandboxTimeoutError(
239
- `Command '${command}' exceeded timeout of ${options.timeout}ms`
240
- ))
241
- }, options.timeout)
242
- this.ws.pendingExecs.get(execId).timeout = timeoutHandle
243
- }
244
303
  })
245
304
 
246
- execPromise.execId = execId
247
-
248
305
  try {
249
- this.sendFrame({ type: 'exec.run', execId, command })
250
- if (options.stdin !== undefined) {
251
- this.writeStdin(execId, options.stdin)
252
- this.closeStdin(execId)
253
- }
306
+ this.sendFrame({ type: 'exec.get', execId })
254
307
  } catch (error) {
255
- this.ws.rejectExec(execId, new SandboxWebSocketError(
256
- `Could not send exec frame: ${error.message}`
308
+ this.ws.pendingGetOps.delete(execId)
309
+ return Promise.reject(new SandboxWebSocketError(
310
+ `Could not send exec.get frame: ${error.message}`
257
311
  ))
258
312
  }
259
313
 
260
- return execPromise
314
+ return getPromise
261
315
  }
262
316
 
263
317
  /**
@@ -266,7 +320,7 @@ class Sandbox {
266
320
  * @param {string} execId execution id
267
321
  * @param {string} [signal] signal to deliver (default: `'SIGTERM'`)
268
322
  */
269
- kill (execId, signal = 'SIGTERM') {
323
+ kill(execId, signal = 'SIGTERM') {
270
324
  this.ensureOpen()
271
325
  this.sendFrame({ type: 'exec.kill', execId, signal })
272
326
  }
@@ -278,7 +332,7 @@ class Sandbox {
278
332
  * @param {string} execId execution id from `exec()`
279
333
  * @param {string|Buffer} data data to write
280
334
  */
281
- writeStdin (execId, data) {
335
+ writeStdin(execId, data) {
282
336
  this.ensureOpen()
283
337
  const frame = { type: 'exec.input', execId }
284
338
  if (Buffer.isBuffer(data)) {
@@ -296,7 +350,7 @@ class Sandbox {
296
350
  *
297
351
  * @param {string} execId execution id from `exec()`
298
352
  */
299
- closeStdin (execId) {
353
+ closeStdin(execId) {
300
354
  this.ensureOpen()
301
355
  this.sendFrame({ type: 'exec.endInput', execId })
302
356
  }
@@ -311,7 +365,7 @@ class Sandbox {
311
365
  * @param {string} path path inside the sandbox
312
366
  * @returns {Promise<string>} file contents as a UTF-8 string
313
367
  */
314
- readFile (path) {
368
+ readFile(path) {
315
369
  try {
316
370
  this.ensureOpen()
317
371
  } catch (error) {
@@ -341,7 +395,7 @@ class Sandbox {
341
395
  * @param {string|Buffer} content file contents
342
396
  * @returns {Promise<{path: string, size: number, ok: boolean}>} write confirmation
343
397
  */
344
- writeFile (path, content) {
398
+ writeFile(path, content) {
345
399
  try {
346
400
  this.ensureOpen()
347
401
  } catch (error) {
@@ -374,7 +428,7 @@ class Sandbox {
374
428
  * @param {string} path directory path inside the sandbox
375
429
  * @returns {Promise<Array<{name: string, type: string, size?: number}>>} directory entries
376
430
  */
377
- listFiles (path) {
431
+ listFiles(path) {
378
432
  try {
379
433
  this.ensureOpen()
380
434
  } catch (error) {
@@ -404,30 +458,29 @@ class Sandbox {
404
458
  /**
405
459
  * Returns the public preview URL for a given port on this sandbox.
406
460
  *
407
- * @param {object} options URL options
408
- * @param {number} options.port port number (1–65535)
409
- * @param {string} [options.protocol] override the URL scheme (e.g. `'wss'`)
410
- * @returns {Promise<string>} public preview URL
461
+ * This is a synchronous local lookup against the `previewUrls` map returned
462
+ * by the server at create time. The URL is opaque — do not parse or reconstruct it.
463
+ *
464
+ * @param {number} port port number (1–65535)
465
+ * @returns {string} public preview URL
466
+ * @throws {SandboxInvalidPortError} when `port` is not an integer in the
467
+ * range 1–65535
468
+ * @throws {SandboxPortNotProvisionedError} when `port` is valid but was not
469
+ * declared in `create({ ports })`
411
470
  */
412
- async getUrl ({ port, protocol } = {}) {
413
- if (!this.publicUrlTemplate) {
414
- throw new SandboxClientError(
415
- `Cannot get URL for sandbox '${this.id}': publicUrlTemplate is not available`
416
- )
417
- }
418
-
471
+ getUrl (port) {
419
472
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
420
- throw new SandboxClientError(
473
+ throw new SandboxInvalidPortError(
421
474
  `Invalid port '${port}': must be an integer between 1 and 65535`
422
475
  )
423
476
  }
424
477
 
425
- let url = this.publicUrlTemplate
426
- .replace('{sandboxId}', this.id)
427
- .replace('{port}', String(port))
428
-
429
- if (protocol) {
430
- url = url.replace(/^https?:\/\//, `${protocol}://`)
478
+ const url = this.previewUrls.get(port)
479
+ if (url === undefined) {
480
+ throw new SandboxPortNotProvisionedError(
481
+ `Port ${port} was not provisioned for sandbox '${this.id}'. ` +
482
+ "Declare it in create({ ports: [...] }) to get a preview URL."
483
+ )
431
484
  }
432
485
 
433
486
  return url
@@ -438,10 +491,18 @@ class Sandbox {
438
491
  *
439
492
  * @returns {Promise<object>} destroy response payload
440
493
  */
441
- async destroy () {
494
+ async destroy() {
442
495
  const base = this.managementEndpoint || this.apiHost
443
496
  const url = `${base}/api/v1/namespaces/${this.namespace}/sandbox/${this.id}`
444
- const payload = await apiRequest('DELETE', url, this.apiKey)
497
+ this.ws?.beginIntentionalClose()
498
+
499
+ let payload
500
+ try {
501
+ payload = await apiRequest('DELETE', url, this.apiKey)
502
+ } catch (error) {
503
+ this.ws?.cancelIntentionalClose()
504
+ throw error
505
+ }
445
506
 
446
507
  this.status = payload.status || this.status
447
508
  this.ws?.close()
@@ -452,16 +513,63 @@ class Sandbox {
452
513
  // Private helpers
453
514
  // ------------------------------------------------------------------
454
515
 
455
- ensureOpen () {
516
+ /**
517
+ * Schedules a timeout that kills `execId` and rejects its pending entry.
518
+ *
519
+ * @param {string} execId
520
+ * @param {string} command human-readable command string (for the error message)
521
+ * @param {number} ms timeout in milliseconds
522
+ * @returns {ReturnType<setTimeout>} the timer handle (stored on the entry for cancellation)
523
+ */
524
+ scheduleTimeout(execId, command, ms) {
525
+ return setTimeout(() => {
526
+ try {
527
+ this.kill(execId)
528
+ } catch (_) {
529
+ // ignore errors
530
+ }
531
+
532
+ this.ws.rejectExec(execId, new SandboxTimeoutError(
533
+ `Command '${command}' exceeded timeout of ${ms}ms`
534
+ ))
535
+ }, ms)
536
+ }
537
+
538
+ ensureOpen() {
456
539
  if (!this.ws) {
457
540
  throw new SandboxWebSocketError(`Sandbox '${this.id}' is not connected`)
458
541
  }
459
542
  this.ws.ensureOpen()
460
543
  }
461
544
 
462
- sendFrame (frame) {
545
+ sendFrame(frame) {
463
546
  this.ws.send(frame)
464
547
  }
465
548
  }
466
549
 
550
+ /**
551
+ * Parses the `previewUrls` JSON object returned by the server into a
552
+ * `Map<number, string>`. String keys (port numbers) are converted to integers.
553
+ * The URL values are treated as opaque — not parsed or reconstructed.
554
+ *
555
+ * Returns an empty Map when the server response omits `previewUrls` (fail-closed:
556
+ * every `getUrl()` call will throw `SandboxPortNotProvisionedError`).
557
+ *
558
+ * @param {object|null|undefined} raw the `previewUrls` field from the API response
559
+ * @returns {Map<number, string>}
560
+ */
561
+ function parsePreviewUrls (raw) {
562
+ if (!raw || typeof raw !== 'object') {
563
+ return new Map()
564
+ }
565
+ const map = new Map()
566
+ for (const [key, value] of Object.entries(raw)) {
567
+ const port = Number(key)
568
+ if (Number.isInteger(port) && port >= 1 && port <= 65535 && typeof value === 'string') {
569
+ map.set(port, value)
570
+ }
571
+ }
572
+ return map
573
+ }
574
+
467
575
  module.exports = Sandbox
package/src/errors.js CHANGED
@@ -25,6 +25,9 @@ class SandboxNotFoundError extends SandboxSDKError {}
25
25
  class SandboxUnauthorizedError extends SandboxSDKError {}
26
26
  class SandboxTimeoutError extends SandboxSDKError {}
27
27
  class SandboxWebSocketError extends SandboxSDKError {}
28
+ class SandboxCommandNotFoundError extends SandboxSDKError {}
29
+ class SandboxPortNotProvisionedError extends SandboxSDKError {}
30
+ class SandboxInvalidPortError extends SandboxClientError {}
28
31
 
29
32
  module.exports = {
30
33
  SandboxSDKError,
@@ -33,5 +36,8 @@ module.exports = {
33
36
  SandboxNotFoundError,
34
37
  SandboxUnauthorizedError,
35
38
  SandboxTimeoutError,
36
- SandboxWebSocketError
39
+ SandboxWebSocketError,
40
+ SandboxCommandNotFoundError,
41
+ SandboxPortNotProvisionedError,
42
+ SandboxInvalidPortError
37
43
  }
package/src/index.js CHANGED
@@ -17,7 +17,10 @@ const {
17
17
  SandboxNotFoundError,
18
18
  SandboxUnauthorizedError,
19
19
  SandboxTimeoutError,
20
- SandboxWebSocketError
20
+ SandboxWebSocketError,
21
+ SandboxCommandNotFoundError,
22
+ SandboxPortNotProvisionedError,
23
+ SandboxInvalidPortError
21
24
  } = require('./errors')
22
25
 
23
26
  module.exports = {
@@ -28,5 +31,8 @@ module.exports = {
28
31
  SandboxNotFoundError,
29
32
  SandboxUnauthorizedError,
30
33
  SandboxTimeoutError,
31
- SandboxWebSocketError
34
+ SandboxWebSocketError,
35
+ SandboxCommandNotFoundError,
36
+ SandboxPortNotProvisionedError,
37
+ SandboxInvalidPortError
32
38
  }