@hyperbrowser/sdk 0.93.0 → 1.0.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/README.md +190 -0
- package/dist/client.d.ts +2 -20
- package/dist/client.js +5 -17
- package/dist/error.d.ts +27 -0
- package/dist/error.js +32 -0
- package/dist/image-builds.d.ts +6 -0
- package/dist/image-builds.js +12 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +12 -1
- package/dist/retry.d.ts +14 -0
- package/dist/retry.js +49 -0
- package/dist/sandbox/base.d.ts +27 -0
- package/dist/sandbox/base.js +298 -99
- package/dist/sandbox/files.d.ts +24 -25
- package/dist/sandbox/files.js +146 -37
- package/dist/sandbox/image-build/artifacts.d.ts +16 -0
- package/dist/sandbox/image-build/artifacts.js +16 -0
- package/dist/sandbox/image-build/blake2b.d.ts +2 -0
- package/dist/sandbox/image-build/blake2b.js +89 -0
- package/dist/sandbox/image-build/common.d.ts +19 -0
- package/dist/sandbox/image-build/common.js +109 -0
- package/dist/sandbox/image-build/context.d.ts +43 -0
- package/dist/sandbox/image-build/context.js +599 -0
- package/dist/sandbox/image-build/docker-image.d.ts +34 -0
- package/dist/sandbox/image-build/docker-image.js +472 -0
- package/dist/sandbox/image-build/dockerfile-analysis.d.ts +17 -0
- package/dist/sandbox/image-build/dockerfile-analysis.js +409 -0
- package/dist/sandbox/image-build/dockerignore.d.ts +18 -0
- package/dist/sandbox/image-build/dockerignore.js +275 -0
- package/dist/sandbox/image-build/gzip.d.ts +11 -0
- package/dist/sandbox/image-build/gzip.js +91 -0
- package/dist/sandbox/image-build/image-init.d.ts +5 -0
- package/dist/sandbox/image-build/image-init.js +97 -0
- package/dist/sandbox/image-build/index.d.ts +13 -0
- package/dist/sandbox/image-build/index.js +36 -0
- package/dist/sandbox/image-build/local-docker.d.ts +11 -0
- package/dist/sandbox/image-build/local-docker.js +72 -0
- package/dist/sandbox/image-build/resolution.d.ts +27 -0
- package/dist/sandbox/image-build/resolution.js +137 -0
- package/dist/sandbox/image-build/tar.d.ts +41 -0
- package/dist/sandbox/image-build/tar.js +378 -0
- package/dist/sandbox/image-build/upload.d.ts +10 -0
- package/dist/sandbox/image-build/upload.js +143 -0
- package/dist/sandbox/index.d.ts +2 -1
- package/dist/sandbox/index.js +4 -1
- package/dist/sandbox/process-output.d.ts +36 -0
- package/dist/sandbox/process-output.js +117 -0
- package/dist/sandbox/process.d.ts +25 -1
- package/dist/sandbox/process.js +197 -29
- package/dist/sandbox/terminal.js +9 -5
- package/dist/sandbox/ws.d.ts +4 -1
- package/dist/sandbox/ws.js +33 -7
- package/dist/services/base.d.ts +2 -0
- package/dist/services/base.js +46 -30
- package/dist/services/normalize.d.ts +3 -0
- package/dist/services/normalize.js +21 -0
- package/dist/services/sandboxes.d.ts +40 -3
- package/dist/services/sandboxes.js +569 -53
- package/dist/services/volumes.js +29 -14
- package/dist/types/constants.d.ts +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/sandbox.d.ts +246 -30
- package/dist/types/volume.d.ts +7 -7
- package/package.json +24 -5
package/README.md
CHANGED
|
@@ -241,6 +241,70 @@ await sandbox.stop();
|
|
|
241
241
|
|
|
242
242
|
`connect()` refreshes runtime auth and throws if the sandbox is no longer running.
|
|
243
243
|
|
|
244
|
+
Run commands and stream complete output. `exec()` and `processes.start()` open a
|
|
245
|
+
single streamed request and collect stdout/stderr from process start, so output is
|
|
246
|
+
complete even beyond the receiver's replay window. `wait()` timeouts are local and
|
|
247
|
+
keep collecting; `disconnect()` stops collecting without killing the process.
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const result = await sandbox.exec("npm test", {
|
|
251
|
+
cwd: "/workspace",
|
|
252
|
+
maxOutputBytes: 128 * 1024 * 1024, // default 64 MiB; exceeding it fails, never truncates
|
|
253
|
+
});
|
|
254
|
+
console.log(result.exitCode, result.stdout);
|
|
255
|
+
|
|
256
|
+
const proc = await sandbox.processes.start("tail -f /var/log/app.log");
|
|
257
|
+
try {
|
|
258
|
+
await proc.wait({ timeoutSec: 5 });
|
|
259
|
+
} catch (error) {
|
|
260
|
+
// Local wait timeout: the process is still running and output is still collected.
|
|
261
|
+
}
|
|
262
|
+
for await (const event of proc.stream()) {
|
|
263
|
+
if (event.type === "stdout") process.stdout.write(event.data);
|
|
264
|
+
if (event.type === "exit") console.log(event.result.exitCode);
|
|
265
|
+
}
|
|
266
|
+
proc.disconnect();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Build a custom sandbox image from a Dockerfile or a local Docker image. `getOrBuildImage()`
|
|
270
|
+
derives a content-based image name, reuses a ready image with the same identity, joins a
|
|
271
|
+
matching in-progress build, or creates a new one. Dockerfile builds package the effective
|
|
272
|
+
build context (Dockerfile sources, `.dockerignore`) and build remotely; no local Docker is
|
|
273
|
+
required. `dockerImage` imports a local `linux/amd64` image via the Docker CLI.
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
const resolved = await client.sandboxes.getOrBuildImage({
|
|
277
|
+
contextPath: "./services/api",
|
|
278
|
+
dockerfile: "Dockerfile", // relative to contextPath
|
|
279
|
+
imageInit: { env: { NODE_ENV: "production" }, workingDir: "/app" },
|
|
280
|
+
builderCpus: 4,
|
|
281
|
+
builderMemoryMiB: 8192,
|
|
282
|
+
builderScratchMiB: 20480,
|
|
283
|
+
});
|
|
284
|
+
console.log(resolved.outcome, resolved.imageName, resolved.imageId); // "reused" | "joined" | "created"
|
|
285
|
+
|
|
286
|
+
const sandbox = await client.sandboxes.create({ imageName: resolved.imageName });
|
|
287
|
+
|
|
288
|
+
// Import a local Docker image instead (requires docker CLI, linux/amd64 image):
|
|
289
|
+
const imported = await client.sandboxes.getOrBuildImage({ dockerImage: "myorg/app:1.2.3" });
|
|
290
|
+
|
|
291
|
+
// Detached build: return immediately and poll later.
|
|
292
|
+
const pending = await client.sandboxes.getOrBuildImage({ contextPath: ".", wait: false });
|
|
293
|
+
if (pending.build) {
|
|
294
|
+
const build = await client.sandboxes.waitForImageBuild(pending.build.id, {
|
|
295
|
+
pollInterval: 3,
|
|
296
|
+
timeout: 35 * 60,
|
|
297
|
+
});
|
|
298
|
+
console.log(build.status, build.imageId);
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Lower-level helpers are also available: `buildImageFromDockerfile()`,
|
|
303
|
+
`buildImageFromDockerImage()`, `findReadyImage()`, `reuseDockerImage()`, plus the raw
|
|
304
|
+
`createImageBuild()` / `completeImageBuild()` / `getImageBuild()` / `listImageBuilds()` /
|
|
305
|
+
`cancelImageBuild()` APIs. Control-plane `GET` requests retry transient failures
|
|
306
|
+
(429/502/503/504 and network errors) up to three times with jittered backoff.
|
|
307
|
+
|
|
244
308
|
Create a sandbox with pre-exposed ports:
|
|
245
309
|
|
|
246
310
|
```typescript
|
|
@@ -339,3 +403,129 @@ const terminal = await sandbox.terminal.create({
|
|
|
339
403
|
|
|
340
404
|
const connection = await terminal.attach(10);
|
|
341
405
|
```
|
|
406
|
+
|
|
407
|
+
### Streaming file transfers and watches
|
|
408
|
+
|
|
409
|
+
`read({ format: "stream" })` retains its buffered behavior. Use `uploadStream()`
|
|
410
|
+
and `downloadStream()` for large transfers with backpressure and bounded memory:
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
import { createReadStream, createWriteStream } from "node:fs";
|
|
414
|
+
import { pipeline } from "node:stream/promises";
|
|
415
|
+
|
|
416
|
+
await sandbox.files.withRunAs("root").uploadStream(
|
|
417
|
+
"/tmp/archive.tar", createReadStream("./archive.tar"),
|
|
418
|
+
);
|
|
419
|
+
await pipeline(sandbox.files.downloadStream("/tmp/archive.tar"), createWriteStream("./copy.tar"));
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Uploads accept a Node readable or iterable of string/byte chunks and optional
|
|
423
|
+
`{ contentLength, signal }`. Downloads return an async generator of buffers and
|
|
424
|
+
accept `{ signal }`. Breaking download iteration closes the request. After an
|
|
425
|
+
authentication failure, a consumed upload is rejected with `stream_not_replayable`;
|
|
426
|
+
open a fresh source to retry it. `files.stat`, `mkdir`, `move`, and `delete` are
|
|
427
|
+
aliases; `rename(oldPath, newPath, { overwrite: false })` forwards overwrite policy.
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
const watch = await sandbox.files.watch("/workspace", { recursive: true });
|
|
431
|
+
try {
|
|
432
|
+
for await (const message of watch.events({ cursor: 0, route: "ws" })) {
|
|
433
|
+
if (message.type === "done") break;
|
|
434
|
+
console.log(message.event.seq, message.event.path, message.event.op);
|
|
435
|
+
break; // Close this connection; the remote watch remains active.
|
|
436
|
+
}
|
|
437
|
+
// Persist watch.id and the last sequence to resume an active watch:
|
|
438
|
+
const resumed = await sandbox.files.getWatch(watch.id, true);
|
|
439
|
+
await resumed.refresh(true);
|
|
440
|
+
console.log(resumed.current, resumed.toJSON());
|
|
441
|
+
} finally {
|
|
442
|
+
await watch.stop();
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Both watch routes (`ws` and `stream`) use WebSocket transport. Watch events include
|
|
447
|
+
an explicit `done` envelope. `watchDir()` remains the callback convenience API.
|
|
448
|
+
Stopping a watcher stops the remote watch; breaking event iteration only closes
|
|
449
|
+
that connection. Watch timestamps remain milliseconds; existing file metadata
|
|
450
|
+
continues to expose `modifiedTime` as a `Date`.
|
|
451
|
+
|
|
452
|
+
### Image identities, snapshots, and runtime sessions
|
|
453
|
+
|
|
454
|
+
Offline identity helpers are available from the package root and
|
|
455
|
+
`@hyperbrowser/sdk/image-builds`:
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
import {
|
|
459
|
+
dockerBuildContextFingerprint, imageBuildName, DockerBuildContextChangedError,
|
|
460
|
+
} from "@hyperbrowser/sdk/image-builds";
|
|
461
|
+
|
|
462
|
+
const fingerprint = await dockerBuildContextFingerprint("./app");
|
|
463
|
+
const imageName = imageBuildName({ source: "dockerfile", fingerprint });
|
|
464
|
+
try {
|
|
465
|
+
await client.sandboxes.buildImageFromDockerfile({
|
|
466
|
+
contextPath: "./app", imageName, expectedContextFingerprint: fingerprint,
|
|
467
|
+
builderCpus: 4, builderMemoryMiB: 8192, builderScratchMiB: 20480,
|
|
468
|
+
});
|
|
469
|
+
} catch (error) {
|
|
470
|
+
if (error instanceof DockerBuildContextChangedError) {
|
|
471
|
+
// Recompute identity after a local edit, then retry explicitly.
|
|
472
|
+
} else throw error;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const restored = await client.sandboxes.startFromSnapshot({ snapshotName: "saved" });
|
|
476
|
+
const session = await restored.createRuntimeSession({ forceRefresh: true });
|
|
477
|
+
// Also available without keeping a handle:
|
|
478
|
+
await client.sandboxes.getRuntimeSession(restored.id);
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
Remote Dockerfile builds need no local Docker. Local builds/imports require Docker;
|
|
482
|
+
platform-specific digest lookup requires Docker API 1.49+ (Docker 28.1+). Imports
|
|
483
|
+
preserve image `PATH`, entrypoint/CMD, working directory, and supported environment
|
|
484
|
+
variables; explicit image initialization overrides take precedence. Names include
|
|
485
|
+
context/digest, platform, and initialization overrides. Mutable base tags and
|
|
486
|
+
network downloads are not resolved by fingerprinting; use `forceBuild` when needed.
|
|
487
|
+
|
|
488
|
+
Only `linux/amd64` image builds are supported. Sandbox creation preserves
|
|
489
|
+
`runtimeClass: "firecracker" | "gvisor-cpu"`; use response capabilities when choosing
|
|
490
|
+
snapshot, volume, or exposure operations. Snapshot launches cannot override image
|
|
491
|
+
resources. Invalid or conflicting launch sources fail before a network request.
|
|
492
|
+
|
|
493
|
+
### Timeouts, cancellation, and compatibility
|
|
494
|
+
|
|
495
|
+
- Client `timeout` is milliseconds. Normal runtime requests have separate header
|
|
496
|
+
and body budgets. Streaming transfers reset inactivity budgets as data moves.
|
|
497
|
+
- Process `timeoutMs` / `timeoutSec` limit remote execution. `wait({ timeoutMs })`
|
|
498
|
+
only limits local waiting and leaves collection running.
|
|
499
|
+
- `exec()` / `processes.start()` accept an `AbortSignal`. Aborting stops local
|
|
500
|
+
collection and closes its connection; the detached remote process continues.
|
|
501
|
+
Use `signal()` or `kill()` when you intend to stop the command.
|
|
502
|
+
- Process stream inactivity is limited to 60 seconds; receiver keepalives reset it.
|
|
503
|
+
Incomplete output, exceeded output limits, and unsupported receivers produce
|
|
504
|
+
structured errors. A failed streaming start is never automatically re-executed.
|
|
505
|
+
- Image polling `pollInterval`, `waitTimeout`, and `uploadTimeout` are seconds.
|
|
506
|
+
`getOrBuildImage()` defaults uploads to 600 seconds of inactivity and polling to
|
|
507
|
+
35 minutes. Explicit `null` disables the corresponding timeout. Polling deadlines
|
|
508
|
+
include control GET retries. A polling `signal` or timeout leaves accepted builds
|
|
509
|
+
running for other callers. Lower-level build helpers leave upload timeouts unset
|
|
510
|
+
unless explicitly supplied.
|
|
511
|
+
- `HYPERBROWSER_BASE_URL` supplies the API base URL when `baseUrl` is not provided.
|
|
512
|
+
|
|
513
|
+
Public handle classes are available from `@hyperbrowser/sdk/sandbox`; request and
|
|
514
|
+
response types remain under `@hyperbrowser/sdk/types`.
|
|
515
|
+
|
|
516
|
+
### Development checks
|
|
517
|
+
|
|
518
|
+
The supported Node baseline is 20.20.2. Run `yarn build`, `yarn typecheck`,
|
|
519
|
+
`yarn lint`, and `yarn test` for local verification. Tests share `vitest.config.ts`:
|
|
520
|
+
|
|
521
|
+
- `tests/unit`: isolated logic and mocked contracts (`yarn test:unit`).
|
|
522
|
+
- `tests/integration`: local HTTP, WebSocket, filesystem, and subprocess tests
|
|
523
|
+
(`yarn test:integration`).
|
|
524
|
+
- `tests/e2e`: live Hyperbrowser API tests (`yarn test:e2e`).
|
|
525
|
+
|
|
526
|
+
`yarn test` and `yarn test:watch` select unit and integration tests by default;
|
|
527
|
+
neither requires API credentials or a Docker daemon.
|
|
528
|
+
|
|
529
|
+
Live tests are opt-in: set `HYPERBROWSER_API_KEY` and `HYPERBROWSER_BASE_URL`, then
|
|
530
|
+
run `yarn test:e2e`. They create remote resources and require a receiver supporting
|
|
531
|
+
streamed process starts.
|
package/dist/client.d.ts
CHANGED
|
@@ -18,26 +18,8 @@ import { JevComputerUseService } from "./services/agents/jev-computer-use";
|
|
|
18
18
|
import { WebService } from "./services/web";
|
|
19
19
|
import { SandboxesService } from "./services/sandboxes";
|
|
20
20
|
import { VolumesService } from "./services/volumes";
|
|
21
|
-
export
|
|
22
|
-
export
|
|
23
|
-
statusCode?: number;
|
|
24
|
-
code?: string;
|
|
25
|
-
requestId?: string;
|
|
26
|
-
retryable?: boolean;
|
|
27
|
-
service?: HyperbrowserService;
|
|
28
|
-
details?: unknown;
|
|
29
|
-
cause?: unknown;
|
|
30
|
-
}
|
|
31
|
-
export declare class HyperbrowserError extends Error {
|
|
32
|
-
readonly statusCode?: number;
|
|
33
|
-
readonly code?: string;
|
|
34
|
-
readonly requestId?: string;
|
|
35
|
-
readonly retryable: boolean;
|
|
36
|
-
readonly service?: HyperbrowserService;
|
|
37
|
-
readonly details?: unknown;
|
|
38
|
-
readonly cause?: unknown;
|
|
39
|
-
constructor(message: string, options?: number | HyperbrowserErrorOptions);
|
|
40
|
-
}
|
|
21
|
+
export { HyperbrowserError } from "./error";
|
|
22
|
+
export type { HyperbrowserErrorOptions, HyperbrowserService } from "./error";
|
|
41
23
|
export declare class HyperbrowserClient {
|
|
42
24
|
readonly sessions: SessionsService;
|
|
43
25
|
readonly scrape: ScrapeService;
|
package/dist/client.js
CHANGED
|
@@ -20,29 +20,17 @@ const jev_computer_use_1 = require("./services/agents/jev-computer-use");
|
|
|
20
20
|
const web_1 = require("./services/web");
|
|
21
21
|
const sandboxes_1 = require("./services/sandboxes");
|
|
22
22
|
const volumes_1 = require("./services/volumes");
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.name = "HyperbrowserError";
|
|
27
|
-
const normalized = typeof options === "number" ? { statusCode: options } : options;
|
|
28
|
-
this.statusCode = normalized.statusCode;
|
|
29
|
-
this.code = normalized.code;
|
|
30
|
-
this.requestId = normalized.requestId;
|
|
31
|
-
this.retryable = normalized.retryable ?? false;
|
|
32
|
-
this.service = normalized.service;
|
|
33
|
-
this.details = normalized.details;
|
|
34
|
-
this.cause = normalized.cause;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
exports.HyperbrowserError = HyperbrowserError;
|
|
23
|
+
const error_1 = require("./error");
|
|
24
|
+
var error_2 = require("./error");
|
|
25
|
+
Object.defineProperty(exports, "HyperbrowserError", { enumerable: true, get: function () { return error_2.HyperbrowserError; } });
|
|
38
26
|
class HyperbrowserClient {
|
|
39
27
|
constructor(config = {}) {
|
|
40
28
|
const apiKey = config.apiKey || process.env["HYPERBROWSER_API_KEY"];
|
|
41
|
-
const baseUrl = config.baseUrl || "https://api.hyperbrowser.ai";
|
|
29
|
+
const baseUrl = config.baseUrl || process.env["HYPERBROWSER_BASE_URL"] || "https://api.hyperbrowser.ai";
|
|
42
30
|
const timeout = config.timeout || 30000;
|
|
43
31
|
const runtimeProxyOverride = config.runtimeProxyOverride?.trim() || undefined;
|
|
44
32
|
if (!apiKey) {
|
|
45
|
-
throw new HyperbrowserError("API key is required - either pass it in config or set HYPERBROWSER_API_KEY environment variable");
|
|
33
|
+
throw new error_1.HyperbrowserError("API key is required - either pass it in config or set HYPERBROWSER_API_KEY environment variable");
|
|
46
34
|
}
|
|
47
35
|
this.sessions = new sessions_1.SessionsService(apiKey, baseUrl, timeout);
|
|
48
36
|
this.scrape = new scrape_1.ScrapeService(apiKey, baseUrl, timeout);
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type HyperbrowserService = "control" | "runtime";
|
|
2
|
+
/** Keep diagnostics useful without retaining query credentials or URL hosts. */
|
|
3
|
+
export declare const errorRequestPath: (target: string) => string;
|
|
4
|
+
export declare const networkErrorMessage: (error: unknown, fallback: string) => string;
|
|
5
|
+
export interface HyperbrowserErrorOptions {
|
|
6
|
+
statusCode?: number;
|
|
7
|
+
code?: string;
|
|
8
|
+
requestId?: string;
|
|
9
|
+
retryable?: boolean;
|
|
10
|
+
service?: HyperbrowserService;
|
|
11
|
+
details?: unknown;
|
|
12
|
+
cause?: unknown;
|
|
13
|
+
method?: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare class HyperbrowserError extends Error {
|
|
17
|
+
readonly statusCode?: number;
|
|
18
|
+
readonly code?: string;
|
|
19
|
+
readonly requestId?: string;
|
|
20
|
+
readonly retryable: boolean;
|
|
21
|
+
readonly service?: HyperbrowserService;
|
|
22
|
+
readonly details?: unknown;
|
|
23
|
+
readonly cause?: unknown;
|
|
24
|
+
readonly method?: string;
|
|
25
|
+
readonly path?: string;
|
|
26
|
+
constructor(message: string, options?: number | HyperbrowserErrorOptions);
|
|
27
|
+
}
|
package/dist/error.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HyperbrowserError = exports.networkErrorMessage = exports.errorRequestPath = void 0;
|
|
4
|
+
/** Keep diagnostics useful without retaining query credentials or URL hosts. */
|
|
5
|
+
const errorRequestPath = (target) => {
|
|
6
|
+
try {
|
|
7
|
+
return new URL(target, "http://sdk.invalid").pathname;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return target.split("?", 1)[0];
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
exports.errorRequestPath = errorRequestPath;
|
|
14
|
+
const networkErrorMessage = (error, fallback) => error instanceof Error ? error.message || `${fallback} (${error.name || "Error"})` : fallback;
|
|
15
|
+
exports.networkErrorMessage = networkErrorMessage;
|
|
16
|
+
class HyperbrowserError extends Error {
|
|
17
|
+
constructor(message, options = {}) {
|
|
18
|
+
super(`[Hyperbrowser]: ${message}`);
|
|
19
|
+
this.name = "HyperbrowserError";
|
|
20
|
+
const normalized = typeof options === "number" ? { statusCode: options } : options;
|
|
21
|
+
this.statusCode = normalized.statusCode;
|
|
22
|
+
this.code = normalized.code;
|
|
23
|
+
this.requestId = normalized.requestId;
|
|
24
|
+
this.retryable = normalized.retryable ?? false;
|
|
25
|
+
this.service = normalized.service;
|
|
26
|
+
this.details = normalized.details;
|
|
27
|
+
this.cause = normalized.cause;
|
|
28
|
+
this.method = normalized.method;
|
|
29
|
+
this.path = normalized.path;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.HyperbrowserError = HyperbrowserError;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Public, offline image identity helpers. */
|
|
2
|
+
export { dockerBuildContextFingerprint, DockerBuildContextChangedError, } from "./sandbox/image-build/context";
|
|
3
|
+
export type { DockerBuildContextOptions } from "./sandbox/image-build/context";
|
|
4
|
+
export { imageBuildName } from "./sandbox/image-build/resolution";
|
|
5
|
+
export type { ImageBuildNameOptions, ImageBuildSource } from "./sandbox/image-build/resolution";
|
|
6
|
+
export { dockerImageDigest, DockerCommandError } from "./sandbox/image-build/docker-image";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DockerCommandError = exports.dockerImageDigest = exports.imageBuildName = exports.DockerBuildContextChangedError = exports.dockerBuildContextFingerprint = void 0;
|
|
4
|
+
/** Public, offline image identity helpers. */
|
|
5
|
+
var context_1 = require("./sandbox/image-build/context");
|
|
6
|
+
Object.defineProperty(exports, "dockerBuildContextFingerprint", { enumerable: true, get: function () { return context_1.dockerBuildContextFingerprint; } });
|
|
7
|
+
Object.defineProperty(exports, "DockerBuildContextChangedError", { enumerable: true, get: function () { return context_1.DockerBuildContextChangedError; } });
|
|
8
|
+
var resolution_1 = require("./sandbox/image-build/resolution");
|
|
9
|
+
Object.defineProperty(exports, "imageBuildName", { enumerable: true, get: function () { return resolution_1.imageBuildName; } });
|
|
10
|
+
var docker_image_1 = require("./sandbox/image-build/docker-image");
|
|
11
|
+
Object.defineProperty(exports, "dockerImageDigest", { enumerable: true, get: function () { return docker_image_1.dockerImageDigest; } });
|
|
12
|
+
Object.defineProperty(exports, "DockerCommandError", { enumerable: true, get: function () { return docker_image_1.DockerCommandError; } });
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { dockerBuildContextFingerprint, DockerBuildContextChangedError, imageBuildName, dockerImageDigest, DockerCommandError } from "./image-builds";
|
|
2
|
+
export { dockerBuildContextFingerprint, DockerBuildContextChangedError, imageBuildName, dockerImageDigest, DockerCommandError, };
|
|
3
|
+
export type { DockerBuildContextOptions, ImageBuildNameOptions, ImageBuildSource, } from "./image-builds";
|
|
1
4
|
import { HyperbrowserClient, HyperbrowserError } from "./client";
|
|
2
5
|
export declare const Hyperbrowser: typeof HyperbrowserClient;
|
|
3
6
|
export type Hyperbrowser = HyperbrowserClient;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HyperbrowserError = exports.HyperbrowserClient = exports.Hyperbrowser = void 0;
|
|
3
|
+
exports.HyperbrowserError = exports.HyperbrowserClient = exports.Hyperbrowser = exports.DockerCommandError = exports.dockerImageDigest = exports.imageBuildName = exports.DockerBuildContextChangedError = exports.dockerBuildContextFingerprint = void 0;
|
|
4
|
+
const image_builds_1 = require("./image-builds");
|
|
5
|
+
Object.defineProperty(exports, "dockerBuildContextFingerprint", { enumerable: true, get: function () { return image_builds_1.dockerBuildContextFingerprint; } });
|
|
6
|
+
Object.defineProperty(exports, "DockerBuildContextChangedError", { enumerable: true, get: function () { return image_builds_1.DockerBuildContextChangedError; } });
|
|
7
|
+
Object.defineProperty(exports, "imageBuildName", { enumerable: true, get: function () { return image_builds_1.imageBuildName; } });
|
|
8
|
+
Object.defineProperty(exports, "dockerImageDigest", { enumerable: true, get: function () { return image_builds_1.dockerImageDigest; } });
|
|
9
|
+
Object.defineProperty(exports, "DockerCommandError", { enumerable: true, get: function () { return image_builds_1.DockerCommandError; } });
|
|
4
10
|
const client_1 = require("./client");
|
|
5
11
|
Object.defineProperty(exports, "HyperbrowserClient", { enumerable: true, get: function () { return client_1.HyperbrowserClient; } });
|
|
6
12
|
Object.defineProperty(exports, "HyperbrowserError", { enumerable: true, get: function () { return client_1.HyperbrowserError; } });
|
|
@@ -13,5 +19,10 @@ if (typeof module !== "undefined" && module.exports) {
|
|
|
13
19
|
module.exports.Hyperbrowser = client_1.HyperbrowserClient;
|
|
14
20
|
module.exports.HyperbrowserClient = client_1.HyperbrowserClient;
|
|
15
21
|
module.exports.HyperbrowserError = client_1.HyperbrowserError;
|
|
22
|
+
module.exports.dockerBuildContextFingerprint = image_builds_1.dockerBuildContextFingerprint;
|
|
23
|
+
module.exports.DockerBuildContextChangedError = image_builds_1.DockerBuildContextChangedError;
|
|
24
|
+
module.exports.imageBuildName = image_builds_1.imageBuildName;
|
|
25
|
+
module.exports.dockerImageDigest = image_builds_1.dockerImageDigest;
|
|
26
|
+
module.exports.DockerCommandError = image_builds_1.DockerCommandError;
|
|
16
27
|
module.exports.default = client_1.HyperbrowserClient;
|
|
17
28
|
}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HyperbrowserError } from "./error";
|
|
2
|
+
export declare const RETRYABLE_STATUS_CODES: Set<number>;
|
|
3
|
+
export declare const GET_RETRY_MAX_ATTEMPTS = 3;
|
|
4
|
+
export declare const GET_RETRY_INITIAL_DELAY_MS = 250;
|
|
5
|
+
export declare const GET_RETRY_MAX_DELAY_MS = 1000;
|
|
6
|
+
export declare const isRetryableNetworkError: (error: unknown) => boolean;
|
|
7
|
+
export declare const shouldRetryGet: (method: string, error: HyperbrowserError, failedAttempt: number) => boolean;
|
|
8
|
+
/** Exponential backoff with jitter, capped at one second. */
|
|
9
|
+
export declare const getRetryDelayMs: (failedAttempt: number) => number;
|
|
10
|
+
export declare const retryDelay: (ms: number, signal?: {
|
|
11
|
+
readonly aborted: boolean;
|
|
12
|
+
addEventListener: AbortSignal["addEventListener"];
|
|
13
|
+
removeEventListener: AbortSignal["removeEventListener"];
|
|
14
|
+
}) => Promise<void>;
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.retryDelay = exports.getRetryDelayMs = exports.shouldRetryGet = exports.isRetryableNetworkError = exports.GET_RETRY_MAX_DELAY_MS = exports.GET_RETRY_INITIAL_DELAY_MS = exports.GET_RETRY_MAX_ATTEMPTS = exports.RETRYABLE_STATUS_CODES = void 0;
|
|
4
|
+
const error_1 = require("./error");
|
|
5
|
+
exports.RETRYABLE_STATUS_CODES = new Set([429, 502, 503, 504]);
|
|
6
|
+
exports.GET_RETRY_MAX_ATTEMPTS = 3;
|
|
7
|
+
exports.GET_RETRY_INITIAL_DELAY_MS = 250;
|
|
8
|
+
exports.GET_RETRY_MAX_DELAY_MS = 1000;
|
|
9
|
+
const RETRYABLE_NETWORK_CODES = new Set([
|
|
10
|
+
"ECONNRESET",
|
|
11
|
+
"ECONNREFUSED",
|
|
12
|
+
"EAI_AGAIN",
|
|
13
|
+
"ETIMEDOUT",
|
|
14
|
+
"ESOCKETTIMEDOUT",
|
|
15
|
+
]);
|
|
16
|
+
const isRetryableNetworkError = (error) => {
|
|
17
|
+
if (!(error instanceof Error)) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const networkError = error;
|
|
21
|
+
return (networkError.name === "AbortError" ||
|
|
22
|
+
networkError.type === "request-timeout" ||
|
|
23
|
+
(networkError.code ? RETRYABLE_NETWORK_CODES.has(networkError.code) : false));
|
|
24
|
+
};
|
|
25
|
+
exports.isRetryableNetworkError = isRetryableNetworkError;
|
|
26
|
+
const shouldRetryGet = (method, error, failedAttempt) => method.toUpperCase() === "GET" && error.retryable && failedAttempt < exports.GET_RETRY_MAX_ATTEMPTS;
|
|
27
|
+
exports.shouldRetryGet = shouldRetryGet;
|
|
28
|
+
/** Exponential backoff with jitter, capped at one second. */
|
|
29
|
+
const getRetryDelayMs = (failedAttempt) => {
|
|
30
|
+
const maximumDelay = Math.min(exports.GET_RETRY_INITIAL_DELAY_MS * 2 ** (failedAttempt - 1), exports.GET_RETRY_MAX_DELAY_MS);
|
|
31
|
+
return maximumDelay / 2 + Math.random() * (maximumDelay / 2);
|
|
32
|
+
};
|
|
33
|
+
exports.getRetryDelayMs = getRetryDelayMs;
|
|
34
|
+
const retryDelay = (ms, signal) => new Promise((resolve, reject) => {
|
|
35
|
+
const aborted = () => {
|
|
36
|
+
clearTimeout(timer);
|
|
37
|
+
signal?.removeEventListener("abort", aborted);
|
|
38
|
+
reject(new error_1.HyperbrowserError("Request canceled", { code: "request_aborted", service: "control" }));
|
|
39
|
+
};
|
|
40
|
+
const timer = setTimeout(() => {
|
|
41
|
+
signal?.removeEventListener("abort", aborted);
|
|
42
|
+
resolve();
|
|
43
|
+
}, ms);
|
|
44
|
+
if (signal?.aborted)
|
|
45
|
+
aborted();
|
|
46
|
+
else
|
|
47
|
+
signal?.addEventListener("abort", aborted, { once: true });
|
|
48
|
+
});
|
|
49
|
+
exports.retryDelay = retryDelay;
|
package/dist/sandbox/base.d.ts
CHANGED
|
@@ -11,14 +11,41 @@ export interface RuntimeSSEEvent {
|
|
|
11
11
|
id?: string;
|
|
12
12
|
}
|
|
13
13
|
type RuntimeParams = Record<string, string | number | boolean | undefined>;
|
|
14
|
+
export declare const PROCESS_STREAM_IDLE_TIMEOUT_MS = 60000;
|
|
15
|
+
export interface RuntimeSSEInit {
|
|
16
|
+
method?: "GET" | "POST";
|
|
17
|
+
body?: string;
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
/** Milliseconds without any bytes before the stream fails. Defaults to 60s. */
|
|
20
|
+
idleTimeoutMs?: number;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
}
|
|
23
|
+
export interface RuntimeSSEStream {
|
|
24
|
+
events: AsyncGenerator<RuntimeSSEEvent>;
|
|
25
|
+
/** Close the underlying connection; the remote process keeps running. */
|
|
26
|
+
close(): void;
|
|
27
|
+
}
|
|
14
28
|
export declare class RuntimeTransport {
|
|
15
29
|
private readonly resolveConnection;
|
|
16
30
|
private readonly timeout;
|
|
17
31
|
private readonly runtimeProxyOverride?;
|
|
32
|
+
private readonly responseCleanup;
|
|
33
|
+
private closeResponse;
|
|
34
|
+
private failure;
|
|
18
35
|
constructor(resolveConnection: (forceRefresh?: boolean) => Promise<RuntimeConnection>, timeout?: number, runtimeProxyOverride?: string | undefined);
|
|
19
36
|
requestJSON<T>(path: string, init?: RequestInit, params?: RuntimeParams): Promise<T>;
|
|
20
37
|
requestBuffer(path: string, init?: RequestInit, params?: RuntimeParams): Promise<Buffer>;
|
|
38
|
+
/** Pull-based binary transfer. Breaking iteration closes the HTTP connection. */
|
|
39
|
+
streamBytes(path: string, init?: RequestInit, params?: RuntimeParams): AsyncGenerator<Buffer>;
|
|
40
|
+
private readChunk;
|
|
21
41
|
streamSSE(path: string, params?: RuntimeParams): AsyncGenerator<RuntimeSSEEvent>;
|
|
42
|
+
/**
|
|
43
|
+
* Open a server-sent event stream. POST streams carry a JSON body and
|
|
44
|
+
* require an event-stream response, since the request may have side effects
|
|
45
|
+
* that must not be retried blindly.
|
|
46
|
+
*/
|
|
47
|
+
openSSE(path: string, params?: RuntimeParams, init?: RuntimeSSEInit): Promise<RuntimeSSEStream>;
|
|
48
|
+
private parseSSE;
|
|
22
49
|
private fetchWithAuth;
|
|
23
50
|
private fetchForConnection;
|
|
24
51
|
private assertResponse;
|