@banata-boxes/sdk 0.2.1 → 0.2.3
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/dist/index.d.ts +79 -165
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +88 -217
- package/package.json +5 -6
- package/src/index.ts +304 -142
package/src/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// sdk/src/index.ts
|
|
2
|
-
// Customer-facing TypeScript SDK for
|
|
2
|
+
// Customer-facing TypeScript SDK for Banata sandboxes
|
|
3
3
|
//
|
|
4
4
|
// M7 FIX: Added retry with exponential backoff on transient errors (5xx, network).
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
interface BrowserConfig {
|
|
7
7
|
/** Session weight: light (512MB, default), medium (1GB), heavy (2GB — Pro+) */
|
|
8
8
|
weight?: 'light' | 'medium' | 'heavy';
|
|
9
9
|
/** Isolation mode: shared (default) or dedicated (Pro+) */
|
|
@@ -43,7 +43,7 @@ export interface BrowserConfig {
|
|
|
43
43
|
timeout?: number;
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
interface BrowserSession {
|
|
47
47
|
id: string;
|
|
48
48
|
status: 'queued' | 'assigning' | 'ready' | 'active' | 'ending' | 'ended' | 'failed';
|
|
49
49
|
waitTimedOut?: boolean;
|
|
@@ -64,6 +64,11 @@ export interface BrowserSession {
|
|
|
64
64
|
export interface UsageInfo {
|
|
65
65
|
totalSessions: number;
|
|
66
66
|
totalBrowserHours: number;
|
|
67
|
+
totalSandboxSessions?: number;
|
|
68
|
+
totalSandboxHours?: number;
|
|
69
|
+
sandboxSizeBreakdown?: {
|
|
70
|
+
standard: number;
|
|
71
|
+
};
|
|
67
72
|
weightBreakdown: {
|
|
68
73
|
light: number;
|
|
69
74
|
medium: number;
|
|
@@ -78,6 +83,8 @@ export interface BillingInfo {
|
|
|
78
83
|
currentPeriod: {
|
|
79
84
|
totalSessions: number;
|
|
80
85
|
totalBrowserHours: number;
|
|
86
|
+
totalSandboxSessions?: number;
|
|
87
|
+
totalSandboxHours?: number;
|
|
81
88
|
weightBreakdown: {
|
|
82
89
|
light: number;
|
|
83
90
|
medium: number;
|
|
@@ -86,7 +93,7 @@ export interface BillingInfo {
|
|
|
86
93
|
};
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
interface LaunchedBrowserSession {
|
|
90
97
|
cdpUrl: string;
|
|
91
98
|
sessionId: string;
|
|
92
99
|
previewViewerUrl: string | null;
|
|
@@ -263,6 +270,15 @@ export class BanataError extends Error {
|
|
|
263
270
|
/** @deprecated Use BanataError instead */
|
|
264
271
|
export const BrowserServiceError = BanataError;
|
|
265
272
|
|
|
273
|
+
const BROWSER_SESSIONS_UNSUPPORTED_MESSAGE =
|
|
274
|
+
"Browser-only sessions are not currently supported. Use BanataSandbox with browser capabilities instead.";
|
|
275
|
+
|
|
276
|
+
function throwBrowserSessionsUnsupported(): never {
|
|
277
|
+
throw new BanataError(BROWSER_SESSIONS_UNSUPPORTED_MESSAGE, 501, {
|
|
278
|
+
code: "NOT_SUPPORTED",
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
266
282
|
/** Check if an HTTP status is retryable (5xx or 429) */
|
|
267
283
|
function isRetryableStatus(status: number): boolean {
|
|
268
284
|
return status >= 500 || status === 429;
|
|
@@ -284,10 +300,8 @@ function derivePreviewConnection(
|
|
|
284
300
|
const parsed = new URL(rawUrl);
|
|
285
301
|
const token = parsed.searchParams.get("token");
|
|
286
302
|
const sessionId = parsed.searchParams.get("session");
|
|
303
|
+
const key = parsed.searchParams.get("key");
|
|
287
304
|
const machine = parsed.searchParams.get("machine");
|
|
288
|
-
if (!token || !sessionId) {
|
|
289
|
-
return null;
|
|
290
|
-
}
|
|
291
305
|
|
|
292
306
|
const httpProtocol =
|
|
293
307
|
parsed.protocol === "https:" || parsed.protocol === "wss:"
|
|
@@ -299,6 +313,27 @@ function derivePreviewConnection(
|
|
|
299
313
|
: parsed.protocol === "http:"
|
|
300
314
|
? "ws:"
|
|
301
315
|
: parsed.protocol;
|
|
316
|
+
const isDirectPreviewUrl =
|
|
317
|
+
parsed.pathname.endsWith("/ws") && parsed.pathname.includes("/preview/");
|
|
318
|
+
|
|
319
|
+
if (isDirectPreviewUrl && key) {
|
|
320
|
+
const basePath = parsed.pathname.slice(0, -3);
|
|
321
|
+
const search = new URLSearchParams({ key }).toString();
|
|
322
|
+
return {
|
|
323
|
+
rawUrl,
|
|
324
|
+
token: key,
|
|
325
|
+
sessionId: sessionId ?? "preview",
|
|
326
|
+
startUrl: `${httpProtocol}//${parsed.host}${basePath}/start?${search}`,
|
|
327
|
+
navigateUrl: `${httpProtocol}//${parsed.host}${basePath}/navigate?${search}`,
|
|
328
|
+
resizeUrl: `${httpProtocol}//${parsed.host}${basePath}/resize?${search}`,
|
|
329
|
+
wsUrl: `${wsProtocol}//${parsed.host}${parsed.pathname}?${search}`,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
if (!token || !sessionId) {
|
|
334
|
+
return null;
|
|
335
|
+
}
|
|
336
|
+
|
|
302
337
|
const searchParams = new URLSearchParams({
|
|
303
338
|
token,
|
|
304
339
|
session: sessionId,
|
|
@@ -307,8 +342,6 @@ function derivePreviewConnection(
|
|
|
307
342
|
searchParams.set("machine", machine);
|
|
308
343
|
}
|
|
309
344
|
const search = searchParams.toString();
|
|
310
|
-
const isDirectPreviewUrl =
|
|
311
|
-
parsed.pathname.endsWith("/ws") && parsed.pathname.includes("/preview/");
|
|
312
345
|
|
|
313
346
|
if (isDirectPreviewUrl) {
|
|
314
347
|
const basePath = parsed.pathname.slice(0, -3);
|
|
@@ -337,25 +370,6 @@ function derivePreviewConnection(
|
|
|
337
370
|
}
|
|
338
371
|
}
|
|
339
372
|
|
|
340
|
-
function withMachineQuery(
|
|
341
|
-
rawUrl: string | null | undefined,
|
|
342
|
-
machineId: string | null | undefined,
|
|
343
|
-
): string | null | undefined {
|
|
344
|
-
if (!rawUrl || !machineId) {
|
|
345
|
-
return rawUrl;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
try {
|
|
349
|
-
const parsed = new URL(rawUrl);
|
|
350
|
-
if (!parsed.searchParams.get("machine")) {
|
|
351
|
-
parsed.searchParams.set("machine", machineId);
|
|
352
|
-
}
|
|
353
|
-
return parsed.toString();
|
|
354
|
-
} catch {
|
|
355
|
-
return rawUrl;
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
373
|
function deriveOpenCodeConnection(
|
|
360
374
|
rawUrl: string | null | undefined,
|
|
361
375
|
): OpenCodeConnectionInfo | null {
|
|
@@ -391,6 +405,25 @@ function deriveOpenCodeConnection(
|
|
|
391
405
|
}
|
|
392
406
|
}
|
|
393
407
|
|
|
408
|
+
function withMachineQuery(
|
|
409
|
+
rawUrl: string | null | undefined,
|
|
410
|
+
machineId: string | null | undefined,
|
|
411
|
+
): string | null | undefined {
|
|
412
|
+
if (!rawUrl || !machineId) {
|
|
413
|
+
return rawUrl;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
try {
|
|
417
|
+
const parsed = new URL(rawUrl);
|
|
418
|
+
if (!parsed.searchParams.get("machine")) {
|
|
419
|
+
parsed.searchParams.set("machine", machineId);
|
|
420
|
+
}
|
|
421
|
+
return parsed.toString();
|
|
422
|
+
} catch {
|
|
423
|
+
return rawUrl;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
394
427
|
async function parseJsonResponse(response: Response): Promise<unknown> {
|
|
395
428
|
const text = await response.text();
|
|
396
429
|
if (!text) {
|
|
@@ -508,8 +541,7 @@ function isSandboxSessionOperationallyReady(
|
|
|
508
541
|
return false;
|
|
509
542
|
}
|
|
510
543
|
|
|
511
|
-
|
|
512
|
-
if (browserMode !== "none") {
|
|
544
|
+
if (session.capabilities?.browser) {
|
|
513
545
|
if (!session.pairedBrowser?.cdpUrl) {
|
|
514
546
|
return false;
|
|
515
547
|
}
|
|
@@ -528,7 +560,7 @@ function isSandboxSessionOperationallyReady(
|
|
|
528
560
|
return true;
|
|
529
561
|
}
|
|
530
562
|
|
|
531
|
-
|
|
563
|
+
class BrowserCloud {
|
|
532
564
|
private apiKey: string;
|
|
533
565
|
private baseUrl: string;
|
|
534
566
|
private appUrl: string;
|
|
@@ -634,56 +666,37 @@ export class BrowserCloud {
|
|
|
634
666
|
|
|
635
667
|
/** Create a new browser session */
|
|
636
668
|
async createBrowser(config: BrowserConfig = {}): Promise<BrowserSession> {
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
if (waitTimeoutMs !== undefined) {
|
|
640
|
-
requestBody.waitTimeoutMs = waitTimeoutMs;
|
|
641
|
-
}
|
|
642
|
-
return this.request<BrowserSession>('/v1/browsers', {
|
|
643
|
-
method: 'POST',
|
|
644
|
-
body: JSON.stringify(requestBody),
|
|
645
|
-
});
|
|
669
|
+
void config;
|
|
670
|
+
throwBrowserSessionsUnsupported();
|
|
646
671
|
}
|
|
647
672
|
|
|
648
673
|
/** Get session status */
|
|
649
674
|
async getBrowser(sessionId: string): Promise<BrowserSession> {
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
);
|
|
675
|
+
void sessionId;
|
|
676
|
+
throwBrowserSessionsUnsupported();
|
|
653
677
|
}
|
|
654
678
|
|
|
655
679
|
/** End a session */
|
|
656
680
|
async closeBrowser(sessionId: string): Promise<void> {
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
});
|
|
681
|
+
void sessionId;
|
|
682
|
+
throwBrowserSessionsUnsupported();
|
|
660
683
|
}
|
|
661
684
|
|
|
662
685
|
/** Derive the preview websocket/start endpoints for a live browser session. */
|
|
663
686
|
async getPreviewConnection(sessionId: string): Promise<PreviewConnectionInfo | null> {
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
session.previewUrl ?? withMachineQuery(session.cdpUrl, session.machineId),
|
|
667
|
-
);
|
|
687
|
+
void sessionId;
|
|
688
|
+
throwBrowserSessionsUnsupported();
|
|
668
689
|
}
|
|
669
690
|
|
|
670
691
|
async getPreviewViewerUrl(sessionId: string): Promise<string | null> {
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
session.previewUrl ?? withMachineQuery(session.cdpUrl, session.machineId),
|
|
674
|
-
this.appUrl,
|
|
675
|
-
);
|
|
692
|
+
void sessionId;
|
|
693
|
+
throwBrowserSessionsUnsupported();
|
|
676
694
|
}
|
|
677
695
|
|
|
678
696
|
/** Start the remote browser preview backend for a live browser session. */
|
|
679
697
|
async startPreview(sessionId: string): Promise<PreviewCommandResponse> {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
throw new BanataError("Browser preview is not available for this session", 409);
|
|
683
|
-
}
|
|
684
|
-
return requestPreviewEndpoint<PreviewCommandResponse>(connection.startUrl, {
|
|
685
|
-
method: "POST",
|
|
686
|
-
});
|
|
698
|
+
void sessionId;
|
|
699
|
+
throwBrowserSessionsUnsupported();
|
|
687
700
|
}
|
|
688
701
|
|
|
689
702
|
/** Navigate the live browser preview session. */
|
|
@@ -691,15 +704,9 @@ export class BrowserCloud {
|
|
|
691
704
|
sessionId: string,
|
|
692
705
|
url: string,
|
|
693
706
|
): Promise<PreviewCommandResponse> {
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
}
|
|
698
|
-
return requestPreviewEndpoint<PreviewCommandResponse>(connection.navigateUrl, {
|
|
699
|
-
method: "POST",
|
|
700
|
-
headers: { "Content-Type": "application/json" },
|
|
701
|
-
body: JSON.stringify({ url }),
|
|
702
|
-
});
|
|
707
|
+
void sessionId;
|
|
708
|
+
void url;
|
|
709
|
+
throwBrowserSessionsUnsupported();
|
|
703
710
|
}
|
|
704
711
|
|
|
705
712
|
/** Resize the live browser preview session if the preview backend supports it. */
|
|
@@ -707,15 +714,9 @@ export class BrowserCloud {
|
|
|
707
714
|
sessionId: string,
|
|
708
715
|
size: { width?: number; height?: number },
|
|
709
716
|
): Promise<PreviewCommandResponse> {
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
return requestPreviewEndpoint<PreviewCommandResponse>(connection.resizeUrl, {
|
|
715
|
-
method: "POST",
|
|
716
|
-
headers: { "Content-Type": "application/json" },
|
|
717
|
-
body: JSON.stringify(size),
|
|
718
|
-
});
|
|
717
|
+
void sessionId;
|
|
718
|
+
void size;
|
|
719
|
+
throwBrowserSessionsUnsupported();
|
|
719
720
|
}
|
|
720
721
|
|
|
721
722
|
/** Wait until session is ready and return CDP URL */
|
|
@@ -723,28 +724,9 @@ export class BrowserCloud {
|
|
|
723
724
|
sessionId: string,
|
|
724
725
|
timeoutMs: number = 30_000
|
|
725
726
|
): Promise<string> {
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
const session = await this.getBrowser(sessionId);
|
|
730
|
-
|
|
731
|
-
if (isBrowserSessionOperationallyReady(session)) {
|
|
732
|
-
return session.cdpUrl;
|
|
733
|
-
}
|
|
734
|
-
if (session.status === 'failed') {
|
|
735
|
-
throw new BanataError('Session failed to start', 500);
|
|
736
|
-
}
|
|
737
|
-
if (session.status === 'ended' || session.status === 'ending') {
|
|
738
|
-
throw new BanataError('Session ended before becoming ready', 410);
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
await sleep(500);
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
throw new BanataError(
|
|
745
|
-
`Session ${sessionId} not ready within ${timeoutMs}ms`,
|
|
746
|
-
408
|
|
747
|
-
);
|
|
727
|
+
void sessionId;
|
|
728
|
+
void timeoutMs;
|
|
729
|
+
throwBrowserSessionsUnsupported();
|
|
748
730
|
}
|
|
749
731
|
|
|
750
732
|
/**
|
|
@@ -763,10 +745,12 @@ export class BrowserCloud {
|
|
|
763
745
|
async launch(
|
|
764
746
|
config: BrowserConfig = {}
|
|
765
747
|
): Promise<LaunchedBrowserSession> {
|
|
748
|
+
void config;
|
|
749
|
+
throwBrowserSessionsUnsupported();
|
|
766
750
|
const session = await this.createBrowser(config);
|
|
767
751
|
let cdpUrl: string;
|
|
768
752
|
if (isBrowserSessionOperationallyReady(session)) {
|
|
769
|
-
cdpUrl = session.cdpUrl
|
|
753
|
+
cdpUrl = session.cdpUrl!;
|
|
770
754
|
} else {
|
|
771
755
|
try {
|
|
772
756
|
cdpUrl = await this.waitForReady(
|
|
@@ -864,19 +848,11 @@ export class BrowserCloud {
|
|
|
864
848
|
// ── Sandbox types ──
|
|
865
849
|
|
|
866
850
|
export interface SandboxConfig {
|
|
867
|
-
/** Sandbox runtime: bun, python, or base shell */
|
|
868
|
-
runtime?: 'bun' | 'python' | 'base';
|
|
869
|
-
/** Sandbox compute shape. */
|
|
870
|
-
size?: 'standard';
|
|
871
851
|
/** Environment variables to inject into the sandbox */
|
|
872
852
|
env?: Record<string, string>;
|
|
873
|
-
/** Whether sandbox is ephemeral (destroyed on kill). Default: true */
|
|
874
|
-
ephemeral?: boolean;
|
|
875
|
-
/** Max session duration in ms */
|
|
876
|
-
maxDurationMs?: number;
|
|
877
853
|
/** Preferred region for sandbox placement */
|
|
878
854
|
region?: string;
|
|
879
|
-
/**
|
|
855
|
+
/** Supported capabilities to prelaunch inside the sandbox */
|
|
880
856
|
capabilities?: SandboxCapabilities;
|
|
881
857
|
/** Ask the API to wait briefly for the sandbox to become ready before returning */
|
|
882
858
|
waitUntilReady?: boolean;
|
|
@@ -890,14 +866,14 @@ export interface SandboxCapabilities {
|
|
|
890
866
|
opencode?: {
|
|
891
867
|
enabled: boolean;
|
|
892
868
|
defaultAgent?: "build" | "plan";
|
|
893
|
-
allowPromptApi?: boolean;
|
|
894
869
|
};
|
|
895
870
|
browser?: {
|
|
896
|
-
|
|
871
|
+
/** Save a low-resolution MP4 recording as an artifact. Team plan only. */
|
|
897
872
|
recording?: boolean;
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
873
|
+
/** Route outbound traffic through your own proxy. Team plan only. */
|
|
874
|
+
byoProxyUrl?: string;
|
|
875
|
+
/** True when a BYO proxy is configured for this sandbox. */
|
|
876
|
+
byoProxyConfigured?: boolean;
|
|
901
877
|
viewport?: {
|
|
902
878
|
width?: number;
|
|
903
879
|
height?: number;
|
|
@@ -907,18 +883,10 @@ export interface SandboxCapabilities {
|
|
|
907
883
|
libreofficeHeadless?: boolean;
|
|
908
884
|
};
|
|
909
885
|
storage?: {
|
|
910
|
-
workspace?: "ephemeral" | "checkpointed";
|
|
911
886
|
artifactPrefix?: string;
|
|
912
887
|
};
|
|
913
888
|
}
|
|
914
889
|
|
|
915
|
-
export interface SandboxCredentialDescriptor {
|
|
916
|
-
name: string;
|
|
917
|
-
kind: string;
|
|
918
|
-
target: string;
|
|
919
|
-
lastInjectedAt?: number;
|
|
920
|
-
}
|
|
921
|
-
|
|
922
890
|
export interface SandboxArtifactItem {
|
|
923
891
|
key: string;
|
|
924
892
|
path: string;
|
|
@@ -941,8 +909,6 @@ export interface SandboxPairedBrowser {
|
|
|
941
909
|
sessionId?: string;
|
|
942
910
|
cdpUrl?: string;
|
|
943
911
|
previewUrl?: string;
|
|
944
|
-
recording?: boolean;
|
|
945
|
-
persistentProfile?: boolean;
|
|
946
912
|
controlMode?: "ai" | "human" | "shared";
|
|
947
913
|
controller?: string;
|
|
948
914
|
handoffRequestedAt?: number;
|
|
@@ -1028,7 +994,6 @@ export interface SandboxSession {
|
|
|
1028
994
|
terminalUrl: string | null;
|
|
1029
995
|
previewBaseUrl?: string | null;
|
|
1030
996
|
capabilities?: SandboxCapabilities | null;
|
|
1031
|
-
credentialDescriptors?: SandboxCredentialDescriptor[] | null;
|
|
1032
997
|
pairedBrowser?: SandboxPairedBrowser | null;
|
|
1033
998
|
opencode?: SandboxOpencodeState | null;
|
|
1034
999
|
browserPreview?: SandboxBrowserPreviewState | null;
|
|
@@ -1062,9 +1027,18 @@ export interface SandboxOpencodePromptResponse {
|
|
|
1062
1027
|
|
|
1063
1028
|
export interface SandboxCheckpointResponse {
|
|
1064
1029
|
ok: boolean;
|
|
1030
|
+
status?: number;
|
|
1031
|
+
checkpointId?: string | null;
|
|
1065
1032
|
artifacts?: SandboxArtifacts;
|
|
1066
1033
|
}
|
|
1067
1034
|
|
|
1035
|
+
export interface SandboxCheckpointInfo {
|
|
1036
|
+
id: string;
|
|
1037
|
+
createTimeMs: number;
|
|
1038
|
+
comment?: string;
|
|
1039
|
+
history?: string[];
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1068
1042
|
export interface SandboxArtifactDownload {
|
|
1069
1043
|
id: string;
|
|
1070
1044
|
key: string;
|
|
@@ -1072,6 +1046,15 @@ export interface SandboxArtifactDownload {
|
|
|
1072
1046
|
expiresInSeconds: number;
|
|
1073
1047
|
}
|
|
1074
1048
|
|
|
1049
|
+
export interface SandboxDocumentConversionResult {
|
|
1050
|
+
ok: boolean;
|
|
1051
|
+
inputPath: string;
|
|
1052
|
+
outputPath: string;
|
|
1053
|
+
format: string;
|
|
1054
|
+
artifact: SandboxArtifactItem | null;
|
|
1055
|
+
artifacts: SandboxArtifacts | null;
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1075
1058
|
export interface SandboxBrowserControlResponse {
|
|
1076
1059
|
ok: boolean;
|
|
1077
1060
|
preview: SandboxBrowserPreviewState;
|
|
@@ -1156,6 +1139,14 @@ export interface LaunchedSandbox {
|
|
|
1156
1139
|
sessionId?: string;
|
|
1157
1140
|
}) => AsyncGenerator<SandboxOpencodeStreamEvent, void, void>;
|
|
1158
1141
|
checkpoint: () => Promise<SandboxCheckpointResponse>;
|
|
1142
|
+
listCheckpoints: () => Promise<SandboxCheckpointInfo[]>;
|
|
1143
|
+
restoreCheckpoint: (checkpointId: string) => Promise<{ ok: boolean; status: number; checkpointId: string }>;
|
|
1144
|
+
getArtifacts: () => Promise<{ id: string; artifacts: SandboxArtifacts | null }>;
|
|
1145
|
+
getArtifactDownloadUrl: (key: string, expiresInSeconds?: number) => Promise<SandboxArtifactDownload>;
|
|
1146
|
+
convertDocument: (
|
|
1147
|
+
inputPath: string,
|
|
1148
|
+
options?: { format?: string; outputDir?: string },
|
|
1149
|
+
) => Promise<SandboxDocumentConversionResult>;
|
|
1159
1150
|
getRuntime: () => Promise<SandboxRuntimeState>;
|
|
1160
1151
|
getPreview: () => Promise<SandboxBrowserPreviewInfo>;
|
|
1161
1152
|
getPreviewConnection: () => Promise<PreviewConnectionInfo | null>;
|
|
@@ -1347,10 +1338,111 @@ export class BanataSandbox {
|
|
|
1347
1338
|
|
|
1348
1339
|
// ── Sandbox lifecycle ──
|
|
1349
1340
|
|
|
1341
|
+
async getUsage(): Promise<UsageInfo> {
|
|
1342
|
+
return this.request<UsageInfo>('/v1/usage');
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
async getBilling(): Promise<BillingInfo> {
|
|
1346
|
+
return this.request<BillingInfo>('/v1/billing');
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
async createCheckout(params: {
|
|
1350
|
+
plan: 'builder' | 'pro' | 'scale';
|
|
1351
|
+
successUrl?: string;
|
|
1352
|
+
}): Promise<{ checkoutUrl: string; checkoutId: string }> {
|
|
1353
|
+
return this.request<{ checkoutUrl: string; checkoutId: string }>('/v1/billing/checkout', {
|
|
1354
|
+
method: 'POST',
|
|
1355
|
+
body: JSON.stringify(params),
|
|
1356
|
+
});
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
async listWebhooks(): Promise<WebhookEndpoint[]> {
|
|
1360
|
+
const response = await this.request<{ data: WebhookEndpoint[] }>('/v1/webhooks');
|
|
1361
|
+
return response.data;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
async createWebhook(params: {
|
|
1365
|
+
url: string;
|
|
1366
|
+
description?: string;
|
|
1367
|
+
eventTypes?: WebhookEventType[];
|
|
1368
|
+
signingSecret?: string;
|
|
1369
|
+
}): Promise<WebhookEndpoint & { signingSecret: string }> {
|
|
1370
|
+
return this.request<WebhookEndpoint & { signingSecret: string }>('/v1/webhooks', {
|
|
1371
|
+
method: 'POST',
|
|
1372
|
+
body: JSON.stringify(params),
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
async deleteWebhook(id: string): Promise<void> {
|
|
1377
|
+
await this.request(`/v1/webhooks?id=${encodeURIComponent(id)}`, {
|
|
1378
|
+
method: 'DELETE',
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
async listWebhookDeliveries(limit = 50): Promise<WebhookDelivery[]> {
|
|
1383
|
+
const response = await this.request<{ data: WebhookDelivery[] }>(
|
|
1384
|
+
`/v1/webhooks/deliveries?limit=${encodeURIComponent(String(limit))}`
|
|
1385
|
+
);
|
|
1386
|
+
return response.data;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
async testWebhook(id: string): Promise<{ eventId: string }> {
|
|
1390
|
+
return this.request<{ eventId: string }>('/v1/webhooks/test', {
|
|
1391
|
+
method: 'POST',
|
|
1392
|
+
body: JSON.stringify({ id }),
|
|
1393
|
+
});
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1350
1396
|
/** Create a new sandbox session */
|
|
1351
1397
|
async create(config: SandboxConfig = {}): Promise<SandboxSession> {
|
|
1352
|
-
const { timeout, waitTimeoutMs, ...rest } = config;
|
|
1398
|
+
const { timeout, waitTimeoutMs, capabilities, ...rest } = config;
|
|
1353
1399
|
const requestBody: Record<string, unknown> = { ...rest };
|
|
1400
|
+
if (capabilities) {
|
|
1401
|
+
requestBody.capabilities = {
|
|
1402
|
+
...(capabilities.opencode
|
|
1403
|
+
? {
|
|
1404
|
+
opencode: {
|
|
1405
|
+
enabled: capabilities.opencode.enabled,
|
|
1406
|
+
...(capabilities.opencode.defaultAgent
|
|
1407
|
+
? { defaultAgent: capabilities.opencode.defaultAgent }
|
|
1408
|
+
: {}),
|
|
1409
|
+
},
|
|
1410
|
+
}
|
|
1411
|
+
: {}),
|
|
1412
|
+
...(capabilities.browser?.viewport
|
|
1413
|
+
|| capabilities.browser?.recording !== undefined
|
|
1414
|
+
|| capabilities.browser?.byoProxyUrl
|
|
1415
|
+
? {
|
|
1416
|
+
browser: {
|
|
1417
|
+
...(capabilities.browser?.viewport
|
|
1418
|
+
? { viewport: capabilities.browser.viewport }
|
|
1419
|
+
: {}),
|
|
1420
|
+
...(capabilities.browser?.recording !== undefined
|
|
1421
|
+
? { recording: capabilities.browser.recording }
|
|
1422
|
+
: {}),
|
|
1423
|
+
...(capabilities.browser?.byoProxyUrl
|
|
1424
|
+
? { byoProxyUrl: capabilities.browser.byoProxyUrl }
|
|
1425
|
+
: {}),
|
|
1426
|
+
},
|
|
1427
|
+
}
|
|
1428
|
+
: {}),
|
|
1429
|
+
...(capabilities.documents
|
|
1430
|
+
? {
|
|
1431
|
+
documents: {
|
|
1432
|
+
libreofficeHeadless:
|
|
1433
|
+
capabilities.documents.libreofficeHeadless ?? true,
|
|
1434
|
+
},
|
|
1435
|
+
}
|
|
1436
|
+
: {}),
|
|
1437
|
+
...(capabilities.storage?.artifactPrefix
|
|
1438
|
+
? {
|
|
1439
|
+
storage: {
|
|
1440
|
+
artifactPrefix: capabilities.storage.artifactPrefix,
|
|
1441
|
+
},
|
|
1442
|
+
}
|
|
1443
|
+
: {}),
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1354
1446
|
if (waitTimeoutMs !== undefined) {
|
|
1355
1447
|
requestBody.waitTimeoutMs = waitTimeoutMs;
|
|
1356
1448
|
}
|
|
@@ -1451,7 +1543,11 @@ export class BanataSandbox {
|
|
|
1451
1543
|
/** Read a file from the sandbox */
|
|
1452
1544
|
read: async (id: string, path: string): Promise<string> => {
|
|
1453
1545
|
const result = await this.request<{ content: string }>(
|
|
1454
|
-
|
|
1546
|
+
'/v1/sandboxes/fs/read',
|
|
1547
|
+
{
|
|
1548
|
+
method: 'POST',
|
|
1549
|
+
body: JSON.stringify({ id, path }),
|
|
1550
|
+
},
|
|
1455
1551
|
);
|
|
1456
1552
|
return result.content;
|
|
1457
1553
|
},
|
|
@@ -1466,9 +1562,10 @@ export class BanataSandbox {
|
|
|
1466
1562
|
|
|
1467
1563
|
/** List files in a directory in the sandbox */
|
|
1468
1564
|
list: async (id: string, path?: string): Promise<FsEntry[]> => {
|
|
1469
|
-
return this.request<FsEntry[]>(
|
|
1470
|
-
|
|
1471
|
-
|
|
1565
|
+
return this.request<FsEntry[]>('/v1/sandboxes/fs/list', {
|
|
1566
|
+
method: 'POST',
|
|
1567
|
+
body: JSON.stringify({ id, path: path ?? '/workspace' }),
|
|
1568
|
+
});
|
|
1472
1569
|
},
|
|
1473
1570
|
};
|
|
1474
1571
|
|
|
@@ -1527,7 +1624,9 @@ export class BanataSandbox {
|
|
|
1527
1624
|
async getOpencodeConnection(id: string): Promise<OpenCodeConnectionInfo | null> {
|
|
1528
1625
|
const preview = await this.getPreview(id);
|
|
1529
1626
|
return deriveOpenCodeConnection(
|
|
1530
|
-
preview.
|
|
1627
|
+
preview.browserPreview?.websocketUrl ??
|
|
1628
|
+
preview.runtime?.websocketUrl ??
|
|
1629
|
+
preview.browserPreviewUrl ??
|
|
1531
1630
|
preview.browserPreview?.publicUrl ??
|
|
1532
1631
|
preview.pairedBrowser?.previewUrl ??
|
|
1533
1632
|
null,
|
|
@@ -1744,6 +1843,24 @@ export class BanataSandbox {
|
|
|
1744
1843
|
});
|
|
1745
1844
|
}
|
|
1746
1845
|
|
|
1846
|
+
async listCheckpoints(id: string): Promise<SandboxCheckpointInfo[]> {
|
|
1847
|
+
const response = await this.request<{
|
|
1848
|
+
id: string;
|
|
1849
|
+
checkpoints: SandboxCheckpointInfo[];
|
|
1850
|
+
currentCheckpointId?: string | null;
|
|
1851
|
+
}>(
|
|
1852
|
+
`/v1/sandboxes/checkpoints?id=${encodeURIComponent(id)}`
|
|
1853
|
+
);
|
|
1854
|
+
return response.checkpoints;
|
|
1855
|
+
}
|
|
1856
|
+
|
|
1857
|
+
async restoreCheckpoint(id: string, checkpointId: string): Promise<{ ok: boolean; status: number; checkpointId: string }> {
|
|
1858
|
+
return this.request<{ ok: boolean; status: number; checkpointId: string }>('/v1/sandboxes/checkpoints/restore', {
|
|
1859
|
+
method: 'POST',
|
|
1860
|
+
body: JSON.stringify({ id, checkpointId }),
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1747
1864
|
async getArtifacts(id: string): Promise<{ id: string; artifacts: SandboxArtifacts | null }> {
|
|
1748
1865
|
return this.request(
|
|
1749
1866
|
`/v1/sandboxes/artifacts?id=${encodeURIComponent(id)}`
|
|
@@ -1760,29 +1877,64 @@ export class BanataSandbox {
|
|
|
1760
1877
|
);
|
|
1761
1878
|
}
|
|
1762
1879
|
|
|
1880
|
+
async convertDocument(
|
|
1881
|
+
id: string,
|
|
1882
|
+
inputPath: string,
|
|
1883
|
+
options: {
|
|
1884
|
+
format?: string;
|
|
1885
|
+
outputDir?: string;
|
|
1886
|
+
} = {},
|
|
1887
|
+
): Promise<SandboxDocumentConversionResult> {
|
|
1888
|
+
return this.request<SandboxDocumentConversionResult>('/v1/sandboxes/documents/convert', {
|
|
1889
|
+
method: 'POST',
|
|
1890
|
+
body: JSON.stringify({
|
|
1891
|
+
id,
|
|
1892
|
+
inputPath,
|
|
1893
|
+
format: options.format,
|
|
1894
|
+
outputDir: options.outputDir,
|
|
1895
|
+
}),
|
|
1896
|
+
});
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1763
1899
|
async getPreview(id: string): Promise<SandboxBrowserPreviewInfo> {
|
|
1764
1900
|
const preview = await this.request<SandboxBrowserPreviewInfo>(
|
|
1765
1901
|
`/v1/sandboxes/browser-preview?id=${encodeURIComponent(id)}`
|
|
1766
1902
|
);
|
|
1767
1903
|
return {
|
|
1768
1904
|
...preview,
|
|
1769
|
-
browserPreviewViewerUrl:
|
|
1770
|
-
preview.
|
|
1771
|
-
|
|
1772
|
-
|
|
1905
|
+
browserPreviewViewerUrl:
|
|
1906
|
+
preview.browserPreviewViewerUrl ??
|
|
1907
|
+
preview.browserPreview?.publicUrl ??
|
|
1908
|
+
preview.pairedBrowser?.previewUrl ??
|
|
1909
|
+
buildPreviewViewerUrl(
|
|
1910
|
+
preview.browserPreview?.websocketUrl ??
|
|
1911
|
+
preview.runtime?.websocketUrl ??
|
|
1912
|
+
preview.browserPreviewUrl,
|
|
1913
|
+
this.appUrl,
|
|
1914
|
+
),
|
|
1773
1915
|
};
|
|
1774
1916
|
}
|
|
1775
1917
|
|
|
1776
1918
|
async getPreviewConnection(id: string): Promise<PreviewConnectionInfo | null> {
|
|
1777
1919
|
const preview = await this.getPreview(id);
|
|
1778
|
-
return derivePreviewConnection(
|
|
1920
|
+
return derivePreviewConnection(
|
|
1921
|
+
preview.browserPreview?.websocketUrl ??
|
|
1922
|
+
preview.runtime?.websocketUrl ??
|
|
1923
|
+
preview.browserPreviewUrl,
|
|
1924
|
+
);
|
|
1779
1925
|
}
|
|
1780
1926
|
|
|
1781
1927
|
async getPreviewViewerUrl(id: string): Promise<string | null> {
|
|
1782
1928
|
const preview = await this.getPreview(id);
|
|
1783
1929
|
return (
|
|
1784
1930
|
preview.browserPreviewViewerUrl ??
|
|
1785
|
-
|
|
1931
|
+
preview.browserPreview?.publicUrl ??
|
|
1932
|
+
buildPreviewViewerUrl(
|
|
1933
|
+
preview.browserPreview?.websocketUrl ??
|
|
1934
|
+
preview.runtime?.websocketUrl ??
|
|
1935
|
+
preview.browserPreviewUrl,
|
|
1936
|
+
this.appUrl,
|
|
1937
|
+
)
|
|
1786
1938
|
);
|
|
1787
1939
|
}
|
|
1788
1940
|
|
|
@@ -1911,7 +2063,7 @@ export class BanataSandbox {
|
|
|
1911
2063
|
* Usage:
|
|
1912
2064
|
* ```ts
|
|
1913
2065
|
* const sandbox = new BanataSandbox({ apiKey: '...' });
|
|
1914
|
-
* const session = await sandbox.launch(
|
|
2066
|
+
* const session = await sandbox.launch();
|
|
1915
2067
|
*
|
|
1916
2068
|
* const result = await session.exec('echo', ['Hello!']);
|
|
1917
2069
|
* console.log(result.stdout);
|
|
@@ -1974,6 +2126,16 @@ export class BanataSandbox {
|
|
|
1974
2126
|
streamOpencodeEvents: (options) =>
|
|
1975
2127
|
this.streamOpencodeEvents(sessionId, options),
|
|
1976
2128
|
checkpoint: () => this.checkpoint(sessionId),
|
|
2129
|
+
listCheckpoints: () => this.listCheckpoints(sessionId),
|
|
2130
|
+
restoreCheckpoint: (checkpointId: string) =>
|
|
2131
|
+
this.restoreCheckpoint(sessionId, checkpointId),
|
|
2132
|
+
getArtifacts: () => this.getArtifacts(sessionId),
|
|
2133
|
+
getArtifactDownloadUrl: (key: string, expiresInSeconds?: number) =>
|
|
2134
|
+
this.getArtifactDownloadUrl(sessionId, key, expiresInSeconds),
|
|
2135
|
+
convertDocument: (
|
|
2136
|
+
inputPath: string,
|
|
2137
|
+
options?: { format?: string; outputDir?: string },
|
|
2138
|
+
) => this.convertDocument(sessionId, inputPath, options),
|
|
1977
2139
|
getRuntime: () => this.getRuntime(sessionId),
|
|
1978
2140
|
getPreview: () => this.getPreview(sessionId),
|
|
1979
2141
|
getPreviewConnection: () => this.getPreviewConnection(sessionId),
|
|
@@ -2004,4 +2166,4 @@ export class BanataSandbox {
|
|
|
2004
2166
|
}
|
|
2005
2167
|
|
|
2006
2168
|
// Default export for convenience
|
|
2007
|
-
export default
|
|
2169
|
+
export default BanataSandbox;
|