computesdk 2.5.4 → 2.6.0

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.js CHANGED
@@ -22,34 +22,16 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CommandExitError: () => CommandExitError,
24
24
  FileWatcher: () => FileWatcher,
25
- GatewaySandbox: () => Sandbox,
26
25
  MessageType: () => MessageType,
27
- PROVIDER_AUTH: () => PROVIDER_AUTH,
28
- PROVIDER_DASHBOARD_URLS: () => PROVIDER_DASHBOARD_URLS,
29
- PROVIDER_ENV_MAP: () => PROVIDER_ENV_MAP,
30
- PROVIDER_ENV_VARS: () => PROVIDER_ENV_VARS,
31
- PROVIDER_HEADERS: () => PROVIDER_HEADERS,
32
- PROVIDER_NAMES: () => PROVIDER_NAMES,
33
- PROVIDER_PRIORITY: () => PROVIDER_PRIORITY,
34
26
  Sandbox: () => Sandbox,
35
27
  SignalService: () => SignalService,
36
- TRIBUTARY_URL: () => TRIBUTARY_URL,
37
28
  TerminalInstance: () => TerminalInstance,
38
- autoConfigureCompute: () => autoConfigureCompute,
39
- buildProviderHeaders: () => buildProviderHeaders,
40
29
  buildSetupPayload: () => buildSetupPayload,
41
30
  compute: () => compute,
42
31
  decodeBinaryMessage: () => decodeBinaryMessage,
43
- detectProvider: () => detectProvider,
44
32
  encodeBinaryMessage: () => encodeBinaryMessage,
45
33
  encodeSetupPayload: () => encodeSetupPayload,
46
- getMissingEnvVars: () => getMissingEnvVars,
47
- getProviderConfigFromEnv: () => getProviderConfigFromEnv,
48
- getProviderHeaders: () => getProviderHeaders,
49
- isCommandExitError: () => isCommandExitError,
50
- isGatewayModeEnabled: () => isGatewayModeEnabled,
51
- isProviderAuthComplete: () => isProviderAuthComplete,
52
- isValidProvider: () => isValidProvider
34
+ isCommandExitError: () => isCommandExitError
53
35
  });
54
36
  module.exports = __toCommonJS(index_exports);
55
37
 
@@ -3546,16 +3528,15 @@ API request failed (${response.status}): ${error}`
3546
3528
  const parts = url.hostname.split(".");
3547
3529
  const subdomain = parts[0];
3548
3530
  const baseDomain = parts.slice(1).join(".");
3549
- const previewDomain = baseDomain.replace("sandbox.computesdk.com", "preview.computesdk.com");
3550
- return `${protocol}://${subdomain}-${options.port}.${previewDomain}`;
3531
+ return `${protocol}://${subdomain}-${options.port}.${baseDomain}`;
3551
3532
  }
3552
3533
  /**
3553
3534
  * Get provider instance
3554
- * Note: Not available when using Sandbox directly - only available through gateway provider
3535
+ * Note: Not available on direct Sandbox client instances
3555
3536
  */
3556
3537
  getProvider() {
3557
3538
  throw new Error(
3558
- "getProvider() is not available on Sandbox. This method is only available when using provider sandboxes through the gateway."
3539
+ "getProvider() is not available on Sandbox. This method is only available on provider SDK sandbox wrappers."
3559
3540
  );
3560
3541
  }
3561
3542
  /**
@@ -3568,7 +3549,7 @@ API request failed (${response.status}): ${error}`
3568
3549
  /**
3569
3550
  * Destroy the sandbox (Sandbox interface method)
3570
3551
  *
3571
- * If a destroyHandler was provided (e.g., from gateway), calls it to destroy
3552
+ * If a destroyHandler was provided, calls it to destroy
3572
3553
  * the sandbox on the backend. Otherwise, only disconnects the WebSocket.
3573
3554
  */
3574
3555
  async destroy() {
@@ -3629,1030 +3610,331 @@ var encodeSetupPayload = (options) => {
3629
3610
  return encodeBase64(JSON.stringify(payload));
3630
3611
  };
3631
3612
 
3632
- // src/provider-config.ts
3633
- var PROVIDER_AUTH = {
3634
- computesdk: [["COMPUTESDK_API_KEY"]],
3635
- // ComputeSDK native - requires COMPUTESDK_API_KEY
3636
- e2b: [["E2B_API_KEY"]],
3637
- modal: [["MODAL_TOKEN_ID", "MODAL_TOKEN_SECRET"]],
3638
- railway: [["RAILWAY_API_KEY", "RAILWAY_PROJECT_ID", "RAILWAY_ENVIRONMENT_ID"]],
3639
- render: [["RENDER_API_KEY", "RENDER_OWNER_ID"]],
3640
- daytona: [["DAYTONA_API_KEY"]],
3641
- vercel: [
3642
- ["VERCEL_OIDC_TOKEN"],
3643
- ["VERCEL_TOKEN", "VERCEL_TEAM_ID", "VERCEL_PROJECT_ID"]
3644
- ],
3645
- runloop: [["RUNLOOP_API_KEY"]],
3646
- cloudflare: [
3647
- ["CLOUDFLARE_SANDBOX_URL", "CLOUDFLARE_SANDBOX_SECRET"],
3648
- ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"]
3649
- ],
3650
- codesandbox: [["CSB_API_KEY"]],
3651
- blaxel: [["BL_API_KEY", "BL_WORKSPACE"]],
3652
- namespace: [["NSC_TOKEN"], ["NSC_TOKEN_FILE"]],
3653
- hopx: [["HOPX_API_KEY"]],
3654
- beam: [["BEAM_TOKEN", "BEAM_WORKSPACE_ID"]],
3655
- sprites: [["SPRITES_TOKEN"]],
3656
- agentuity: [["AGENTUITY_SDK_KEY"]],
3657
- freestyle: [["FREESTYLE_API_KEY"]],
3658
- upstash: [["UPSTASH_BOX_API_KEY"]],
3659
- "just-bash": [[]],
3660
- "secure-exec": [[]]
3661
- };
3662
- var BROWSER_PROVIDER_AUTH = {
3663
- browserbase: [["BROWSERBASE_API_KEY"]]
3664
- };
3665
- var BROWSER_PROVIDER_NAMES = Object.keys(BROWSER_PROVIDER_AUTH);
3666
- var PROVIDER_NAMES = Object.keys(PROVIDER_AUTH);
3667
- var PROVIDER_HEADERS = {
3668
- e2b: {
3669
- apiKey: "X-E2B-API-Key"
3670
- },
3671
- modal: {
3672
- tokenId: "X-Modal-Token-Id",
3673
- tokenSecret: "X-Modal-Token-Secret"
3674
- },
3675
- railway: {
3676
- apiToken: "X-Railway-API-Key",
3677
- projectId: "X-Railway-Project-ID",
3678
- environmentId: "X-Railway-Environment-ID"
3679
- },
3680
- render: {
3681
- apiKey: "X-Render-API-Key",
3682
- ownerId: "X-Render-Owner-ID"
3683
- },
3684
- daytona: {
3685
- apiKey: "X-Daytona-API-Key"
3686
- },
3687
- vercel: {
3688
- oidcToken: "X-Vercel-OIDC-Token",
3689
- token: "X-Vercel-Token",
3690
- teamId: "X-Vercel-Team-Id",
3691
- projectId: "X-Vercel-Project-Id"
3692
- },
3693
- runloop: {
3694
- apiKey: "X-Runloop-API-Key"
3695
- },
3696
- cloudflare: {
3697
- sandboxUrl: "X-Cloudflare-Sandbox-Url",
3698
- sandboxSecret: "X-Cloudflare-Sandbox-Secret",
3699
- apiToken: "X-Cloudflare-API-Token",
3700
- accountId: "X-Cloudflare-Account-Id"
3701
- },
3702
- codesandbox: {
3703
- apiKey: "X-CODESANDBOX-API-Key"
3704
- },
3705
- blaxel: {
3706
- apiKey: "X-Blaxel-API-Key",
3707
- workspace: "X-Blaxel-Workspace"
3708
- },
3709
- namespace: {
3710
- token: "X-Namespace-Token"
3711
- },
3712
- hopx: {
3713
- apiKey: "X-HOPX-API-Key"
3714
- },
3715
- beam: {
3716
- token: "X-Beam-Token",
3717
- workspaceId: "X-Beam-Workspace-Id"
3718
- },
3719
- sprites: {
3720
- apiKey: "X-Sprites-Token"
3721
- },
3722
- agentuity: {
3723
- apiKey: "X-Agentuity-SDK-Key"
3724
- },
3725
- freestyle: {
3726
- apiKey: "X-Freestyle-API-Key"
3727
- },
3728
- upstash: {
3729
- apiKey: "X-Upstash-Box-API-Key"
3730
- },
3731
- "just-bash": {},
3732
- computesdk: {},
3733
- "secure-exec": {}
3734
- };
3735
- var PROVIDER_ENV_MAP = {
3736
- e2b: {
3737
- E2B_API_KEY: "apiKey"
3738
- },
3739
- modal: {
3740
- MODAL_TOKEN_ID: "tokenId",
3741
- MODAL_TOKEN_SECRET: "tokenSecret"
3742
- },
3743
- railway: {
3744
- RAILWAY_API_KEY: "apiToken",
3745
- RAILWAY_PROJECT_ID: "projectId",
3746
- RAILWAY_ENVIRONMENT_ID: "environmentId"
3747
- },
3748
- render: {
3749
- RENDER_API_KEY: "apiKey",
3750
- RENDER_OWNER_ID: "ownerId"
3751
- },
3752
- daytona: {
3753
- DAYTONA_API_KEY: "apiKey"
3754
- },
3755
- vercel: {
3756
- VERCEL_OIDC_TOKEN: "oidcToken",
3757
- VERCEL_TOKEN: "token",
3758
- VERCEL_TEAM_ID: "teamId",
3759
- VERCEL_PROJECT_ID: "projectId"
3760
- },
3761
- runloop: {
3762
- RUNLOOP_API_KEY: "apiKey"
3763
- },
3764
- cloudflare: {
3765
- CLOUDFLARE_SANDBOX_URL: "sandboxUrl",
3766
- CLOUDFLARE_SANDBOX_SECRET: "sandboxSecret",
3767
- CLOUDFLARE_API_TOKEN: "apiToken",
3768
- CLOUDFLARE_ACCOUNT_ID: "accountId"
3769
- },
3770
- codesandbox: {
3771
- CSB_API_KEY: "apiKey"
3772
- },
3773
- blaxel: {
3774
- BL_API_KEY: "apiKey",
3775
- BL_WORKSPACE: "workspace"
3776
- },
3777
- namespace: {
3778
- NSC_TOKEN: "token",
3779
- NSC_TOKEN_FILE: "tokenFile"
3780
- },
3781
- hopx: {
3782
- HOPX_API_KEY: "apiKey"
3783
- },
3784
- beam: {
3785
- BEAM_TOKEN: "token",
3786
- BEAM_WORKSPACE_ID: "workspaceId"
3787
- },
3788
- sprites: {
3789
- SPRITES_TOKEN: "apiKey"
3790
- },
3791
- agentuity: {
3792
- AGENTUITY_SDK_KEY: "apiKey"
3793
- },
3794
- freestyle: {
3795
- FREESTYLE_API_KEY: "apiKey"
3796
- },
3797
- upstash: {
3798
- UPSTASH_BOX_API_KEY: "apiKey"
3799
- },
3800
- "just-bash": {},
3801
- computesdk: {},
3802
- "secure-exec": {}
3803
- };
3804
- var PROVIDER_DASHBOARD_URLS = {
3805
- e2b: "https://e2b.dev/dashboard",
3806
- modal: "https://modal.com/settings",
3807
- railway: "https://railway.app/account/tokens",
3808
- render: "https://dashboard.render.com/account",
3809
- daytona: "https://daytona.io/dashboard",
3810
- vercel: "https://vercel.com/account/tokens",
3811
- runloop: "https://runloop.ai/dashboard",
3812
- cloudflare: "https://dash.cloudflare.com/profile/api-tokens",
3813
- codesandbox: "https://codesandbox.io/dashboard/settings",
3814
- blaxel: "https://blaxel.ai/dashboard",
3815
- namespace: "https://cloud.namespace.so",
3816
- hopx: "https://hopx.ai/dashboard",
3817
- beam: "https://app.beam.cloud",
3818
- sprites: "https://sprites.dev",
3819
- agentuity: "https://console.agentuity.com",
3820
- freestyle: "https://dash.freestyle.sh",
3821
- upstash: "https://console.upstash.com",
3822
- "just-bash": "https://github.com/vercel-labs/just-bash",
3823
- computesdk: "https://computesdk.com/dashboard",
3824
- "secure-exec": "https://github.com/anomalyco/secure-exec"
3825
- };
3826
- function isValidProvider(name) {
3827
- return name in PROVIDER_AUTH;
3828
- }
3829
- function buildProviderHeaders(provider, config) {
3830
- const headers = {};
3831
- const headerMap = PROVIDER_HEADERS[provider];
3832
- for (const [configKey, headerName] of Object.entries(headerMap)) {
3833
- const value = config[configKey];
3834
- if (value) {
3835
- headers[headerName] = value;
3836
- }
3837
- }
3838
- return headers;
3839
- }
3840
- function getProviderConfigFromEnv(provider) {
3841
- const config = {};
3842
- const envMap = PROVIDER_ENV_MAP[provider];
3843
- for (const [envVar, configKey] of Object.entries(envMap)) {
3844
- const value = process.env[envVar];
3845
- if (value) {
3846
- config[configKey] = value;
3847
- }
3848
- }
3849
- return config;
3850
- }
3851
- function isProviderAuthComplete(provider) {
3852
- const authOptions = PROVIDER_AUTH[provider];
3853
- for (const option of authOptions) {
3854
- const allPresent = option.every((envVar) => !!process.env[envVar]);
3855
- if (allPresent) return true;
3856
- }
3857
- return false;
3858
- }
3859
- function getMissingEnvVars(provider) {
3860
- const authOptions = PROVIDER_AUTH[provider];
3861
- let bestOption = null;
3862
- for (const option of authOptions) {
3863
- const missing = [];
3864
- let presentCount = 0;
3865
- for (const envVar of option) {
3866
- if (process.env[envVar]) {
3867
- presentCount++;
3868
- } else {
3869
- missing.push(envVar);
3870
- }
3871
- }
3872
- if (missing.length === 0) return [];
3873
- if (!bestOption || presentCount > bestOption.presentCount) {
3874
- bestOption = { presentCount, missing };
3875
- }
3876
- }
3877
- return bestOption?.missing ?? [];
3878
- }
3879
-
3880
- // src/constants.ts
3881
- var TRIBUTARY_URL = "https://tributary.edge.computesdk.com";
3882
- var PROVIDER_PRIORITY = [
3883
- "e2b",
3884
- "railway",
3885
- "render",
3886
- "daytona",
3887
- "modal",
3888
- "runloop",
3889
- "vercel",
3890
- "cloudflare",
3891
- "codesandbox",
3892
- "blaxel",
3893
- "namespace",
3894
- "hopx",
3895
- "beam",
3896
- "sprites",
3897
- "agentuity",
3898
- "freestyle",
3899
- "upstash",
3900
- "secure-exec"
3901
- ];
3902
- var PROVIDER_ENV_VARS = {
3903
- computesdk: ["COMPUTESDK_API_KEY"],
3904
- // ComputeSDK native - requires COMPUTESDK_API_KEY
3905
- e2b: ["E2B_API_KEY"],
3906
- railway: ["RAILWAY_API_KEY", "RAILWAY_PROJECT_ID", "RAILWAY_ENVIRONMENT_ID"],
3907
- render: ["RENDER_API_KEY", "RENDER_OWNER_ID"],
3908
- daytona: ["DAYTONA_API_KEY"],
3909
- modal: ["MODAL_TOKEN_ID", "MODAL_TOKEN_SECRET"],
3910
- runloop: ["RUNLOOP_API_KEY"],
3911
- vercel: ["VERCEL_TOKEN", "VERCEL_TEAM_ID", "VERCEL_PROJECT_ID"],
3912
- cloudflare: ["CLOUDFLARE_API_TOKEN", "CLOUDFLARE_ACCOUNT_ID"],
3913
- codesandbox: ["CSB_API_KEY"],
3914
- blaxel: ["BL_API_KEY", "BL_WORKSPACE"],
3915
- namespace: ["NSC_TOKEN"],
3916
- hopx: ["HOPX_API_KEY"],
3917
- beam: ["BEAM_TOKEN", "BEAM_WORKSPACE_ID"],
3918
- sprites: ["SPRITES_TOKEN"],
3919
- agentuity: ["AGENTUITY_SDK_KEY"],
3920
- freestyle: ["FREESTYLE_API_KEY"],
3921
- upstash: ["UPSTASH_BOX_API_KEY"],
3922
- "just-bash": [],
3923
- "secure-exec": []
3924
- };
3925
-
3926
- // src/auto-detect.ts
3927
- function isGatewayModeEnabled() {
3928
- return !!(typeof process !== "undefined" && process.env?.COMPUTESDK_API_KEY);
3929
- }
3930
- function hasProviderEnv(provider) {
3931
- if (typeof process === "undefined") return false;
3932
- const requiredVars = PROVIDER_ENV_VARS[provider];
3933
- if (!requiredVars || requiredVars.length === 0) return false;
3934
- return requiredVars.every((varName) => !!process.env?.[varName]);
3935
- }
3936
- function getProviderEnvStatus(provider) {
3937
- const requiredVars = PROVIDER_ENV_VARS[provider];
3938
- if (typeof process === "undefined" || !requiredVars) {
3939
- return { provider, present: [], missing: requiredVars ? [...requiredVars] : [], isComplete: false };
3940
- }
3941
- const present = requiredVars.filter((varName) => !!process.env?.[varName]);
3942
- const missing = requiredVars.filter((varName) => !process.env?.[varName]);
3943
- return {
3944
- provider,
3945
- present: [...present],
3946
- missing: [...missing],
3947
- isComplete: missing.length === 0
3948
- };
3949
- }
3950
- function detectProvider() {
3951
- if (typeof process === "undefined") return null;
3952
- const explicit = process.env.COMPUTESDK_PROVIDER?.toLowerCase();
3953
- if (explicit && hasProviderEnv(explicit)) {
3954
- return explicit;
3955
- }
3956
- if (explicit && !hasProviderEnv(explicit)) {
3957
- console.warn(
3958
- `\u26A0\uFE0F COMPUTESDK_PROVIDER is set to "${explicit}" but required credentials are missing.
3959
- Required: ${PROVIDER_ENV_VARS[explicit]?.join(", ") || "unknown"}
3960
- Falling back to auto-detection...`
3961
- );
3962
- }
3963
- for (const provider of PROVIDER_PRIORITY) {
3964
- if (hasProviderEnv(provider)) {
3965
- return provider;
3966
- }
3967
- }
3968
- return null;
3969
- }
3970
- function getProviderHeaders(provider) {
3971
- if (typeof process === "undefined") return {};
3972
- const headers = {};
3973
- switch (provider) {
3974
- case "e2b":
3975
- if (process.env.E2B_API_KEY) {
3976
- headers["X-E2B-API-Key"] = process.env.E2B_API_KEY;
3977
- }
3978
- break;
3979
- case "railway":
3980
- if (process.env.RAILWAY_API_KEY) {
3981
- headers["X-Railway-API-Key"] = process.env.RAILWAY_API_KEY;
3982
- }
3983
- if (process.env.RAILWAY_PROJECT_ID) {
3984
- headers["X-Railway-Project-ID"] = process.env.RAILWAY_PROJECT_ID;
3985
- }
3986
- if (process.env.RAILWAY_ENVIRONMENT_ID) {
3987
- headers["X-Railway-Environment-ID"] = process.env.RAILWAY_ENVIRONMENT_ID;
3988
- }
3989
- break;
3990
- case "daytona":
3991
- if (process.env.DAYTONA_API_KEY) {
3992
- headers["X-Daytona-API-Key"] = process.env.DAYTONA_API_KEY;
3993
- }
3994
- break;
3995
- case "modal":
3996
- if (process.env.MODAL_TOKEN_ID) {
3997
- headers["X-Modal-Token-ID"] = process.env.MODAL_TOKEN_ID;
3998
- }
3999
- if (process.env.MODAL_TOKEN_SECRET) {
4000
- headers["X-Modal-Token-Secret"] = process.env.MODAL_TOKEN_SECRET;
4001
- }
4002
- break;
4003
- case "runloop":
4004
- if (process.env.RUNLOOP_API_KEY) {
4005
- headers["X-Runloop-API-Key"] = process.env.RUNLOOP_API_KEY;
4006
- }
4007
- break;
4008
- case "vercel":
4009
- if (process.env.VERCEL_TOKEN) {
4010
- headers["X-Vercel-Token"] = process.env.VERCEL_TOKEN;
4011
- }
4012
- if (process.env.VERCEL_TEAM_ID) {
4013
- headers["X-Vercel-Team-ID"] = process.env.VERCEL_TEAM_ID;
4014
- }
4015
- if (process.env.VERCEL_PROJECT_ID) {
4016
- headers["X-Vercel-Project-ID"] = process.env.VERCEL_PROJECT_ID;
4017
- }
4018
- break;
4019
- case "cloudflare":
4020
- if (process.env.CLOUDFLARE_API_TOKEN) {
4021
- headers["X-Cloudflare-API-Token"] = process.env.CLOUDFLARE_API_TOKEN;
4022
- }
4023
- if (process.env.CLOUDFLARE_ACCOUNT_ID) {
4024
- headers["X-Cloudflare-Account-ID"] = process.env.CLOUDFLARE_ACCOUNT_ID;
4025
- }
4026
- break;
4027
- case "codesandbox":
4028
- if (process.env.CSB_API_KEY) {
4029
- headers["X-CodeSandbox-API-Key"] = process.env.CSB_API_KEY;
4030
- }
4031
- break;
4032
- case "blaxel":
4033
- if (process.env.BL_API_KEY) {
4034
- headers["X-Blaxel-API-Key"] = process.env.BL_API_KEY;
4035
- }
4036
- if (process.env.BL_WORKSPACE) {
4037
- headers["X-Blaxel-Workspace"] = process.env.BL_WORKSPACE;
4038
- }
4039
- break;
4040
- case "namespace":
4041
- if (process.env.NSC_TOKEN) {
4042
- headers["X-Namespace-Token"] = process.env.NSC_TOKEN;
4043
- }
4044
- break;
4045
- case "hopx":
4046
- if (process.env.HOPX_API_KEY) {
4047
- headers["X-HOPX-API-Key"] = process.env.HOPX_API_KEY;
4048
- }
4049
- break;
4050
- case "render":
4051
- if (process.env.RENDER_API_KEY) {
4052
- headers["X-Render-API-Key"] = process.env.RENDER_API_KEY;
4053
- }
4054
- if (process.env.RENDER_OWNER_ID) {
4055
- headers["X-Render-Owner-ID"] = process.env.RENDER_OWNER_ID;
4056
- }
4057
- break;
4058
- case "beam":
4059
- if (process.env.BEAM_TOKEN) {
4060
- headers["X-Beam-Token"] = process.env.BEAM_TOKEN;
4061
- }
4062
- if (process.env.BEAM_WORKSPACE_ID) {
4063
- headers["X-Beam-Workspace-Id"] = process.env.BEAM_WORKSPACE_ID;
4064
- }
4065
- break;
4066
- case "sprites":
4067
- if (process.env.SPRITES_TOKEN) {
4068
- headers["X-Sprites-Token"] = process.env.SPRITES_TOKEN;
4069
- }
4070
- break;
4071
- case "agentuity":
4072
- if (process.env.AGENTUITY_SDK_KEY) {
4073
- headers["X-Agentuity-SDK-Key"] = process.env.AGENTUITY_SDK_KEY;
4074
- }
4075
- break;
4076
- case "just-bash":
4077
- break;
4078
- case "secure-exec":
4079
- break;
4080
- case "upstash":
4081
- if (process.env.UPSTASH_BOX_API_KEY) {
4082
- headers["X-Upstash-Box-API-Key"] = process.env.UPSTASH_BOX_API_KEY;
4083
- }
4084
- break;
4085
- }
4086
- return headers;
3613
+ // src/compute.ts
3614
+ function isProviderLike(value) {
3615
+ if (!value || typeof value !== "object") return false;
3616
+ const candidate = value;
3617
+ const sandbox = candidate.sandbox;
3618
+ return !!(sandbox && typeof sandbox.create === "function" && typeof sandbox.getById === "function" && typeof sandbox.destroy === "function");
4087
3619
  }
4088
- function autoConfigureCompute() {
4089
- if (!isGatewayModeEnabled()) {
4090
- return null;
4091
- }
4092
- const provider = detectProvider();
4093
- if (!provider) {
4094
- const detectionResults = PROVIDER_PRIORITY.map((p) => getProviderEnvStatus(p));
4095
- const statusLines = detectionResults.map((result) => {
4096
- const status = result.isComplete ? "\u2705" : result.present.length > 0 ? "\u26A0\uFE0F " : "\u274C";
4097
- const ratio = `${result.present.length}/${result.present.length + result.missing.length}`;
4098
- let line = ` ${status} ${result.provider.padEnd(12)} ${ratio} credentials`;
4099
- if (result.present.length > 0 && result.missing.length > 0) {
4100
- line += ` (missing: ${result.missing.join(", ")})`;
4101
- }
4102
- return line;
4103
- });
4104
- throw new Error(
4105
- `COMPUTESDK_API_KEY is set but no provider detected.
4106
-
4107
- Provider detection results:
4108
- ` + statusLines.join("\n") + `
4109
-
4110
- To fix this, set one of the following:
4111
-
4112
- E2B: export E2B_API_KEY=xxx
4113
- Railway: export RAILWAY_API_KEY=xxx RAILWAY_PROJECT_ID=xxx RAILWAY_ENVIRONMENT_ID=xxx
4114
- Daytona: export DAYTONA_API_KEY=xxx
4115
- Modal: export MODAL_TOKEN_ID=xxx MODAL_TOKEN_SECRET=xxx
4116
- Runloop: export RUNLOOP_API_KEY=xxx
4117
- Vercel: export VERCEL_TOKEN=xxx VERCEL_TEAM_ID=xxx VERCEL_PROJECT_ID=xxx
4118
- Cloudflare: export CLOUDFLARE_API_TOKEN=xxx CLOUDFLARE_ACCOUNT_ID=xxx
4119
- CodeSandbox: export CSB_API_KEY=xxx
4120
- Blaxel: export BL_API_KEY=xxx BL_WORKSPACE=xxx
4121
- Namespace: export NSC_TOKEN=xxx
4122
- HopX: export HOPX_API_KEY=xxx
4123
- Render: export RENDER_API_KEY=xxx RENDER_OWNER_ID=xxx
4124
- Beam: export BEAM_TOKEN=xxx BEAM_WORKSPACE_ID=xxx
4125
- Sprites: export SPRITES_TOKEN=xxx
4126
- Agentuity: export AGENTUITY_SDK_KEY=xxx
4127
- Upstash: export UPSTASH_BOX_API_KEY=xxx
4128
- just-bash: (no credentials needed - local sandbox)
4129
- secure-exec: (no credentials needed - local sandbox)
4130
-
4131
- Or set COMPUTESDK_PROVIDER to specify explicitly:
4132
- export COMPUTESDK_PROVIDER=<provider>
4133
-
4134
- Docs: https://computesdk.com/docs/quickstart`
4135
- );
4136
- }
4137
- const gatewayUrl = process.env.COMPUTESDK_TRIBUTARY_URL || TRIBUTARY_URL;
4138
- const computesdkApiKey = process.env.COMPUTESDK_API_KEY;
4139
- const providerHeaders = getProviderHeaders(provider);
4140
- try {
4141
- new URL(gatewayUrl);
4142
- } catch (error) {
4143
- throw new Error(
4144
- `Invalid gateway URL: "${gatewayUrl}"
4145
-
4146
- The URL must be a valid HTTP/HTTPS URL.
4147
- Check your COMPUTESDK_TRIBUTARY_URL environment variable.`
4148
- );
4149
- }
4150
- if (process.env.COMPUTESDK_DEBUG) {
4151
- console.log(`\u2728 ComputeSDK: Auto-detected ${provider} provider`);
4152
- console.log(`\u{1F310} Gateway: ${gatewayUrl}`);
4153
- console.log(`\u{1F511} Provider headers:`, Object.keys(providerHeaders).join(", "));
4154
- }
4155
- const config = {
4156
- apiKey: computesdkApiKey,
4157
- gatewayUrl,
4158
- provider,
4159
- providerHeaders
4160
- };
4161
- return config;
3620
+ function getProviderLabel(provider, index) {
3621
+ return provider.name || `provider-${index + 1}`;
4162
3622
  }
4163
-
4164
- // src/explicit-config.ts
4165
- function buildProviderHeaders2(config) {
4166
- const headers = {};
4167
- const provider = config.provider;
4168
- const headerMap = PROVIDER_HEADERS[provider];
4169
- const providerConfig = config[provider];
4170
- if (!providerConfig || !headerMap) return headers;
4171
- for (const [configKey, headerName] of Object.entries(headerMap)) {
4172
- const value = providerConfig[configKey];
4173
- if (value) {
4174
- headers[headerName] = value;
4175
- }
3623
+ function getProviderErrorDetail(error) {
3624
+ if (error instanceof Error) {
3625
+ return error.message;
4176
3626
  }
4177
- return headers;
3627
+ return String(error);
4178
3628
  }
4179
- function validateProviderConfig(config) {
4180
- const provider = config.provider;
4181
- const authOptions = PROVIDER_AUTH[provider];
4182
- const providerConfig = config[provider];
4183
- const dashboardUrl = PROVIDER_DASHBOARD_URLS[provider];
4184
- if (!authOptions) {
4185
- throw new Error(`Unknown provider: ${provider}`);
4186
- }
4187
- for (const option of authOptions) {
4188
- const allPresent = option.every((envVar) => {
4189
- const configField = envVarToConfigField(provider, envVar);
4190
- return providerConfig?.[configField];
4191
- });
4192
- if (allPresent) return;
3629
+ function resolveProviders(config) {
3630
+ const candidates = [];
3631
+ if (config.provider) {
3632
+ candidates.push(config.provider);
3633
+ }
3634
+ if (Array.isArray(config.providers)) {
3635
+ candidates.push(...config.providers);
3636
+ }
3637
+ const providers = [];
3638
+ const seen = /* @__PURE__ */ new Set();
3639
+ const seenNames = /* @__PURE__ */ new Set();
3640
+ for (const candidate of candidates) {
3641
+ if (!isProviderLike(candidate)) continue;
3642
+ if (seen.has(candidate)) continue;
3643
+ const name = candidate.name;
3644
+ if (name && seenNames.has(name)) continue;
3645
+ providers.push(candidate);
3646
+ seen.add(candidate);
3647
+ if (name) {
3648
+ seenNames.add(name);
3649
+ }
3650
+ }
3651
+ if (providers.length > 0) {
3652
+ return providers;
4193
3653
  }
4194
- const configExample = buildConfigExample(provider, authOptions);
4195
3654
  throw new Error(
4196
- `Missing ${provider} configuration. When using provider: '${provider}', you must provide:
4197
- ${configExample}
4198
-
4199
- Get your credentials at: ${dashboardUrl}`
4200
- );
4201
- }
4202
- function envVarToConfigField(provider, envVar) {
4203
- return PROVIDER_ENV_MAP[provider]?.[envVar] ?? envVar.toLowerCase();
4204
- }
4205
- function buildConfigExample(provider, authOptions) {
4206
- if (authOptions.length === 1) {
4207
- const fields = authOptions[0].map((envVar) => {
4208
- const field = envVarToConfigField(provider, envVar);
4209
- return `${field}: '...'`;
4210
- });
4211
- return ` ${provider}: { ${fields.join(", ")} }`;
4212
- }
4213
- const options = authOptions.map((option, i) => {
4214
- const fields = option.map((envVar) => {
4215
- const field = envVarToConfigField(provider, envVar);
4216
- return `${field}: '...'`;
4217
- });
4218
- return ` Option ${i + 1}:
4219
- ${provider}: { ${fields.join(", ")} }`;
4220
- });
4221
- return options.join("\n\n");
4222
- }
4223
- function createConfigFromExplicit(config) {
4224
- const computesdkApiKey = config.computesdkApiKey || config.apiKey;
4225
- if (!computesdkApiKey) {
4226
- throw new Error(
4227
- `Missing ComputeSDK API key. Set 'computesdkApiKey' in your config.
4228
-
4229
- Example:
4230
- compute.setConfig({
4231
- provider: 'e2b',
4232
- computesdkApiKey: process.env.COMPUTESDK_API_KEY,
4233
- e2b: { apiKey: process.env.E2B_API_KEY }
4234
- })
4235
-
4236
- Get your API key at: https://computesdk.com/dashboard`
4237
- );
4238
- }
4239
- validateProviderConfig(config);
4240
- const providerHeaders = buildProviderHeaders2(config);
4241
- return {
4242
- apiKey: computesdkApiKey,
4243
- gatewayUrl: config.gatewayUrl || process.env.COMPUTESDK_TRIBUTARY_URL || TRIBUTARY_URL,
4244
- provider: config.provider,
4245
- providerHeaders,
4246
- requestTimeoutMs: config.requestTimeoutMs,
4247
- WebSocket: config.WebSocket
4248
- };
4249
- }
4250
-
4251
- // src/compute-daemon/lifecycle.ts
4252
- async function waitForComputeReady(client, options = {}) {
4253
- const maxRetries = options.maxRetries ?? 30;
4254
- const initialDelayMs = options.initialDelayMs ?? 500;
4255
- const maxDelayMs = options.maxDelayMs ?? 5e3;
4256
- const backoffFactor = options.backoffFactor ?? 1.5;
4257
- let lastError = null;
4258
- let currentDelay = initialDelayMs;
4259
- for (let i = 0; i < maxRetries; i++) {
4260
- try {
4261
- await client.health();
4262
- if (process.env.COMPUTESDK_DEBUG) {
4263
- console.log(`[Lifecycle] Sandbox ready after ${i + 1} attempt${i === 0 ? "" : "s"}`);
4264
- }
4265
- return;
4266
- } catch (error) {
4267
- lastError = error instanceof Error ? error : new Error(String(error));
4268
- if (i === maxRetries - 1) {
4269
- throw new Error(
4270
- `Sandbox failed to become ready after ${maxRetries} attempts.
4271
- Last error: ${lastError.message}
4272
-
4273
- Possible causes:
4274
- 1. Sandbox failed to start (check provider dashboard for errors)
4275
- 2. Network connectivity issues between your app and the sandbox
4276
- 3. Sandbox is taking longer than expected to initialize
4277
- 4. Invalid sandbox URL or authentication credentials
4278
-
4279
- Troubleshooting:
4280
- - Check sandbox logs in your provider dashboard
4281
- - Verify your network connection
4282
- - Try increasing maxRetries if initialization is slow
4283
- - Enable debug mode: export COMPUTESDK_DEBUG=1`
4284
- );
4285
- }
4286
- await new Promise((resolve) => setTimeout(resolve, currentDelay));
4287
- currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);
4288
- }
4289
- }
4290
- }
4291
-
4292
- // src/compute.ts
4293
- async function tributaryFetch(url, config, options = {}) {
4294
- const timeout = config.requestTimeoutMs ?? 3e4;
4295
- const controller = new AbortController();
4296
- const timeoutId = setTimeout(() => controller.abort(), timeout);
4297
- try {
4298
- const response = await fetch(url, {
4299
- ...options,
4300
- signal: controller.signal,
4301
- headers: {
4302
- "Content-Type": "application/json",
4303
- "X-ComputeSDK-API-Key": config.apiKey,
4304
- "X-Provider": config.provider,
4305
- ...config.providerHeaders,
4306
- ...options.headers
4307
- }
4308
- });
4309
- clearTimeout(timeoutId);
4310
- if (!response.ok) {
4311
- if (response.status === 404) {
4312
- return { success: false };
4313
- }
4314
- const errorText = await response.text().catch(() => response.statusText);
4315
- let errorMessage = `Gateway API error: ${errorText}`;
4316
- if (response.status === 401) {
4317
- errorMessage = `Invalid ComputeSDK API key. Check your COMPUTESDK_API_KEY environment variable.`;
4318
- } else if (response.status === 403) {
4319
- errorMessage = `Access forbidden. Your API key may not have permission to use provider "${config.provider}".`;
4320
- }
4321
- throw new Error(errorMessage);
4322
- }
4323
- return await response.json();
4324
- } catch (error) {
4325
- clearTimeout(timeoutId);
4326
- if (error instanceof Error && error.name === "AbortError") {
4327
- throw new Error(`Request timed out after ${timeout}ms`);
4328
- }
4329
- throw error;
4330
- }
4331
- }
4332
- async function waitForSandboxStatus(config, endpoint, body, options = {}) {
4333
- const maxWaitMs = options.maxWaitMs ?? 6e4;
4334
- const initialDelayMs = 500;
4335
- const maxDelayMs = 2e3;
4336
- const backoffFactor = 1.5;
4337
- const startTime = Date.now();
4338
- let currentDelay = initialDelayMs;
4339
- while (Date.now() - startTime < maxWaitMs) {
4340
- const result = await tributaryFetch(endpoint, config, {
4341
- method: "POST",
4342
- body: JSON.stringify(body)
4343
- });
4344
- if (!result.success || !result.data) {
4345
- return result;
4346
- }
4347
- if (result.data.status !== "creating") {
4348
- return result;
4349
- }
4350
- if (process.env.COMPUTESDK_DEBUG) {
4351
- console.log(`[Compute] Sandbox still creating, waiting ${currentDelay}ms...`);
4352
- }
4353
- await new Promise((resolve) => setTimeout(resolve, currentDelay));
4354
- currentDelay = Math.min(currentDelay * backoffFactor, maxDelayMs);
4355
- }
4356
- throw new Error(
4357
- `Sandbox is still being created after ${maxWaitMs}ms. This may indicate the sandbox failed to start. Check your provider dashboard.`
3655
+ "No provider instance configured.\n\nConfigure compute with provider instances:\n\n compute.setConfig({ providers: [e2b({...}), modal({...})] })\n // or: compute.setConfig({ provider: e2b({...}) })"
4358
3656
  );
4359
3657
  }
4360
3658
  var ComputeManager = class {
4361
3659
  constructor() {
4362
- this.config = null;
4363
- this.autoConfigured = false;
3660
+ this.providers = [];
3661
+ this.providerStrategy = "priority";
3662
+ this.fallbackOnError = true;
3663
+ this.roundRobinCursor = 0;
3664
+ this.sandboxProviders = /* @__PURE__ */ new Map();
3665
+ this.snapshotProviders = /* @__PURE__ */ new Map();
4364
3666
  this.sandbox = {
4365
- /**
4366
- * Create a new sandbox
4367
- *
4368
- * @example
4369
- * ```typescript
4370
- * const sandbox = await compute.sandbox.create({
4371
- * directory: '/custom/path',
4372
- * overlays: [
4373
- * {
4374
- * source: '/templates/nextjs',
4375
- * target: 'app',
4376
- * strategy: 'smart',
4377
- * },
4378
- * ],
4379
- * servers: [
4380
- * {
4381
- * slug: 'web',
4382
- * start: 'npm run dev',
4383
- * path: '/app',
4384
- * },
4385
- * ],
4386
- * });
4387
- * ```
4388
- */
4389
3667
  create: async (options) => {
4390
- const config = this.getGatewayConfig();
4391
- const result = await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes`, config, {
4392
- method: "POST",
4393
- body: JSON.stringify(options || {})
4394
- });
4395
- if (!result.success || !result.data) {
4396
- throw new Error(`Gateway returned invalid response`);
4397
- }
4398
- const { sandboxId, url, token, provider, metadata, name, namespace, overlays, servers } = result.data;
4399
- const sandbox = new Sandbox({
4400
- sandboxUrl: url,
4401
- sandboxId,
4402
- provider,
4403
- token: token || config.apiKey,
4404
- metadata: {
4405
- ...metadata,
4406
- ...name && { name },
4407
- ...namespace && { namespace },
4408
- ...overlays && { overlays },
4409
- ...servers && { servers }
4410
- },
4411
- WebSocket: config.WebSocket || globalThis.WebSocket,
4412
- destroyHandler: async () => {
4413
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
4414
- method: "DELETE"
4415
- });
4416
- }
4417
- });
4418
- await waitForComputeReady(sandbox);
4419
- return sandbox;
3668
+ return this.createWithFallback(options);
4420
3669
  },
4421
- /**
4422
- * Get an existing sandbox by ID
4423
- */
4424
3670
  getById: async (sandboxId) => {
4425
- const config = this.getGatewayConfig();
4426
- const result = await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config);
4427
- if (!result.success || !result.data) {
4428
- return null;
4429
- }
4430
- const { url, token, provider, metadata } = result.data;
4431
- const sandbox = new Sandbox({
4432
- sandboxUrl: url,
4433
- sandboxId,
4434
- provider,
4435
- token: token || config.apiKey,
4436
- metadata,
4437
- WebSocket: config.WebSocket || globalThis.WebSocket,
4438
- destroyHandler: async () => {
4439
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
4440
- method: "DELETE"
4441
- });
3671
+ for (const provider of this.getByIdCandidates(sandboxId)) {
3672
+ const sandbox = await provider.sandbox.getById(sandboxId);
3673
+ if (sandbox) {
3674
+ this.registerSandboxProvider(sandbox, provider);
3675
+ return sandbox;
4442
3676
  }
4443
- });
4444
- await waitForComputeReady(sandbox);
4445
- return sandbox;
3677
+ }
3678
+ this.sandboxProviders.delete(sandboxId);
3679
+ return null;
4446
3680
  },
4447
- /**
4448
- * List all active sandboxes
4449
- */
4450
3681
  list: async () => {
4451
- throw new Error(
4452
- "The gateway does not support listing sandboxes. Use getById() with a known sandbox ID instead."
4453
- );
3682
+ const all = [];
3683
+ for (const provider of this.getProviders()) {
3684
+ if (!provider.sandbox.list) {
3685
+ continue;
3686
+ }
3687
+ const sandboxes = await provider.sandbox.list();
3688
+ for (const sandbox of sandboxes) {
3689
+ this.registerSandboxProvider(sandbox, provider);
3690
+ }
3691
+ all.push(...sandboxes);
3692
+ }
3693
+ return all;
4454
3694
  },
4455
- /**
4456
- * Destroy a sandbox
4457
- */
4458
3695
  destroy: async (sandboxId) => {
4459
- const config = this.getGatewayConfig();
4460
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
4461
- method: "DELETE"
4462
- });
4463
- },
4464
- /**
4465
- * Find existing or create new sandbox by (namespace, name)
4466
- */
4467
- findOrCreate: async (options) => {
4468
- const config = this.getGatewayConfig();
4469
- const { name, namespace, ...restOptions } = options;
4470
- const result = await waitForSandboxStatus(
4471
- config,
4472
- `${config.gatewayUrl}/v1/sandboxes/find-or-create`,
4473
- {
4474
- namespace: namespace || "default",
4475
- name,
4476
- ...restOptions
3696
+ const candidates = this.getByIdCandidates(sandboxId);
3697
+ const errors = [];
3698
+ for (const [index, provider] of candidates.entries()) {
3699
+ try {
3700
+ await provider.sandbox.destroy(sandboxId);
3701
+ this.sandboxProviders.delete(sandboxId);
3702
+ return;
3703
+ } catch (error) {
3704
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
4477
3705
  }
4478
- );
4479
- if (!result.success || !result.data) {
4480
- throw new Error(`Gateway returned invalid response`);
4481
3706
  }
4482
- const { sandboxId, url, token, provider, metadata } = result.data;
4483
- const sandbox = new Sandbox({
4484
- sandboxUrl: url,
4485
- sandboxId,
4486
- provider,
4487
- token: token || config.apiKey,
4488
- metadata: {
4489
- ...metadata,
4490
- name: result.data.name,
4491
- namespace: result.data.namespace
4492
- },
4493
- WebSocket: config.WebSocket || globalThis.WebSocket,
4494
- destroyHandler: async () => {
4495
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
4496
- method: "DELETE"
4497
- });
4498
- }
4499
- });
4500
- await waitForComputeReady(sandbox);
4501
- return sandbox;
3707
+ throw new Error(
3708
+ `Failed to destroy sandbox "${sandboxId}" across ${candidates.length} provider(s).
3709
+ ` + errors.map((error) => `- ${error}`).join("\n")
3710
+ );
3711
+ },
3712
+ findOrCreate: async (options) => {
3713
+ return this.findOrCreateWithFallback(options);
4502
3714
  },
4503
- /**
4504
- * Find existing sandbox by (namespace, name) without creating
4505
- */
4506
3715
  find: async (options) => {
4507
- const config = this.getGatewayConfig();
4508
- const result = await waitForSandboxStatus(
4509
- config,
4510
- `${config.gatewayUrl}/v1/sandboxes/find`,
4511
- {
4512
- namespace: options.namespace || "default",
4513
- name: options.name
3716
+ const preferredProviderName = options.provider;
3717
+ const { provider: _providerName, ...providerOptions } = options;
3718
+ const candidates = this.getCreateCandidates(preferredProviderName);
3719
+ for (const provider of candidates) {
3720
+ if (!provider.sandbox.find) {
3721
+ continue;
4514
3722
  }
4515
- );
4516
- if (!result.success || !result.data) {
4517
- return null;
4518
- }
4519
- const { sandboxId, url, token, provider, metadata, name, namespace } = result.data;
4520
- const sandbox = new Sandbox({
4521
- sandboxUrl: url,
4522
- sandboxId,
4523
- provider,
4524
- token: token || config.apiKey,
4525
- metadata: {
4526
- ...metadata,
4527
- name,
4528
- namespace
4529
- },
4530
- WebSocket: config.WebSocket || globalThis.WebSocket,
4531
- destroyHandler: async () => {
4532
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}`, config, {
4533
- method: "DELETE"
4534
- });
3723
+ const sandbox = await provider.sandbox.find(providerOptions);
3724
+ if (sandbox) {
3725
+ this.registerSandboxProvider(sandbox, provider);
3726
+ return sandbox;
4535
3727
  }
4536
- });
4537
- await waitForComputeReady(sandbox);
4538
- return sandbox;
3728
+ }
3729
+ return null;
4539
3730
  },
4540
- /**
4541
- * Extend sandbox timeout/expiration
4542
- */
4543
3731
  extendTimeout: async (sandboxId, options) => {
4544
- const config = this.getGatewayConfig();
4545
- const duration = options?.duration ?? 9e5;
4546
- await tributaryFetch(`${config.gatewayUrl}/v1/sandboxes/${sandboxId}/extend`, config, {
4547
- method: "POST",
4548
- body: JSON.stringify({ duration })
4549
- });
3732
+ const candidates = this.getByIdCandidates(sandboxId);
3733
+ const errors = [];
3734
+ for (const [index, provider] of candidates.entries()) {
3735
+ if (!provider.sandbox.extendTimeout) {
3736
+ errors.push(`${getProviderLabel(provider, index)}: extendTimeout() not supported`);
3737
+ continue;
3738
+ }
3739
+ try {
3740
+ await provider.sandbox.extendTimeout(sandboxId, options);
3741
+ this.sandboxProviders.set(sandboxId, provider);
3742
+ return;
3743
+ } catch (error) {
3744
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
3745
+ }
3746
+ }
3747
+ throw new Error(
3748
+ `Failed to extend timeout for sandbox "${sandboxId}" across ${candidates.length} provider(s).
3749
+ ` + errors.map((error) => `- ${error}`).join("\n")
3750
+ );
4550
3751
  }
4551
3752
  };
4552
3753
  this.snapshot = {
4553
- /**
4554
- * Create a snapshot from a running sandbox
4555
- *
4556
- * @param sandboxId ID of the sandbox to snapshot
4557
- * @param options Snapshot options (name, metadata)
4558
- */
4559
3754
  create: async (sandboxId, options) => {
4560
- const config = this.getGatewayConfig();
4561
- const result = await tributaryFetch(`${config.gatewayUrl}/v1/snapshots`, config, {
4562
- method: "POST",
4563
- body: JSON.stringify({ sandboxId, ...options })
4564
- });
4565
- if (!result.success || !result.data) {
4566
- throw new Error(`Gateway returned invalid response`);
3755
+ const preferredProviderName = options?.provider;
3756
+ const { provider: _providerName, ...providerOptions } = options || {};
3757
+ const candidates = this.getSnapshotCreateCandidates(sandboxId, preferredProviderName);
3758
+ const errors = [];
3759
+ for (const [index, provider] of candidates.entries()) {
3760
+ if (!provider.snapshot) {
3761
+ errors.push(`${getProviderLabel(provider, index)}: snapshots not supported`);
3762
+ continue;
3763
+ }
3764
+ try {
3765
+ const snapshot = await provider.snapshot.create(sandboxId, providerOptions);
3766
+ this.snapshotProviders.set(snapshot.id, provider);
3767
+ return {
3768
+ ...snapshot,
3769
+ createdAt: new Date(snapshot.createdAt)
3770
+ };
3771
+ } catch (error) {
3772
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
3773
+ }
4567
3774
  }
4568
- return {
4569
- ...result.data,
4570
- createdAt: new Date(result.data.createdAt)
4571
- };
3775
+ throw new Error(
3776
+ `Failed to create snapshot for sandbox "${sandboxId}" across ${candidates.length} provider(s).
3777
+ ` + errors.map((error) => `- ${error}`).join("\n")
3778
+ );
4572
3779
  },
4573
- /**
4574
- * List all snapshots
4575
- */
4576
3780
  list: async () => {
4577
- const config = this.getGatewayConfig();
4578
- const result = await tributaryFetch(`${config.gatewayUrl}/v1/snapshots`, config);
4579
- if (!result.success || !result.data) {
4580
- return [];
3781
+ const snapshots = [];
3782
+ for (const provider of this.getProviders()) {
3783
+ if (!provider.snapshot) continue;
3784
+ const listed = await provider.snapshot.list();
3785
+ for (const snapshot of listed) {
3786
+ this.snapshotProviders.set(snapshot.id, provider);
3787
+ snapshots.push({
3788
+ ...snapshot,
3789
+ createdAt: new Date(snapshot.createdAt)
3790
+ });
3791
+ }
4581
3792
  }
4582
- return result.data.map((s) => ({
4583
- ...s,
4584
- createdAt: new Date(s.createdAt)
4585
- }));
3793
+ return snapshots;
4586
3794
  },
4587
- /**
4588
- * Delete a snapshot
4589
- */
4590
3795
  delete: async (snapshotId) => {
4591
- const config = this.getGatewayConfig();
4592
- await tributaryFetch(`${config.gatewayUrl}/v1/snapshots/${snapshotId}`, config, {
4593
- method: "DELETE"
4594
- });
3796
+ const candidates = this.getSnapshotDeleteCandidates(snapshotId);
3797
+ const errors = [];
3798
+ for (const [index, provider] of candidates.entries()) {
3799
+ if (!provider.snapshot) continue;
3800
+ try {
3801
+ await provider.snapshot.delete(snapshotId);
3802
+ this.snapshotProviders.delete(snapshotId);
3803
+ return;
3804
+ } catch (error) {
3805
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
3806
+ }
3807
+ }
3808
+ throw new Error(
3809
+ `Failed to delete snapshot "${snapshotId}" across ${candidates.length} provider(s).
3810
+ ` + errors.map((error) => `- ${error}`).join("\n")
3811
+ );
4595
3812
  }
4596
3813
  };
4597
3814
  }
4598
- /**
4599
- * Lazy auto-configure from environment if not explicitly configured
4600
- */
4601
- ensureConfigured() {
4602
- if (this.config) return;
4603
- if (this.autoConfigured) return;
4604
- const config = autoConfigureCompute();
4605
- this.autoConfigured = true;
4606
- if (config) {
4607
- this.config = config;
4608
- }
4609
- }
4610
- /**
4611
- * Get gateway config, throwing if not configured
4612
- */
4613
- getGatewayConfig() {
4614
- this.ensureConfigured();
4615
- if (!this.config) {
3815
+ getProviders() {
3816
+ if (this.providers.length === 0) {
4616
3817
  throw new Error(
4617
- `No ComputeSDK configuration found.
4618
-
4619
- Options:
4620
- 1. Zero-config: Set COMPUTESDK_API_KEY and provider credentials (e.g., E2B_API_KEY)
4621
- 2. Explicit: Call compute.setConfig({ provider: "e2b", computesdkApiKey: "...", e2b: { apiKey: "..." } })
4622
- 3. Use provider directly: import { e2b } from '@computesdk/e2b'
4623
-
4624
- Docs: https://computesdk.com/docs/quickstart`
3818
+ "No compute provider configured.\n\nOptions:\n1. Configure providers: compute.setConfig({ providers: [e2b({...}), modal({...})] })\n2. Configure a single provider: compute.setConfig({ provider: e2b({...}) })\n3. Use provider directly: const sdk = e2b({...}); await sdk.sandbox.create()"
4625
3819
  );
4626
3820
  }
4627
- return this.config;
3821
+ return this.providers;
3822
+ }
3823
+ getProviderByName(name) {
3824
+ const provider = this.getProviders().find((p) => p.name === name);
3825
+ if (!provider) {
3826
+ const names = this.getProviders().map((p, i) => getProviderLabel(p, i)).join(", ");
3827
+ throw new Error(`Provider "${name}" is not configured. Configured providers: ${names || "(none)"}.`);
3828
+ }
3829
+ return provider;
3830
+ }
3831
+ registerSandboxProvider(sandbox, provider) {
3832
+ const sandboxId = sandbox?.sandboxId;
3833
+ if (sandboxId) {
3834
+ this.sandboxProviders.set(sandboxId, provider);
3835
+ }
3836
+ }
3837
+ getCreateCandidates(preferredProviderName) {
3838
+ const providers = this.getProviders();
3839
+ if (preferredProviderName) {
3840
+ return [this.getProviderByName(preferredProviderName)];
3841
+ }
3842
+ if (providers.length <= 1 || this.providerStrategy === "priority") {
3843
+ return [...providers];
3844
+ }
3845
+ const start = this.roundRobinCursor % providers.length;
3846
+ this.roundRobinCursor = (this.roundRobinCursor + 1) % providers.length;
3847
+ return [
3848
+ ...providers.slice(start),
3849
+ ...providers.slice(0, start)
3850
+ ];
3851
+ }
3852
+ getByIdCandidates(sandboxId) {
3853
+ const known = this.sandboxProviders.get(sandboxId);
3854
+ if (!known) return this.getProviders();
3855
+ const providers = this.getProviders();
3856
+ return [known, ...providers.filter((p) => p !== known)];
3857
+ }
3858
+ getSnapshotDeleteCandidates(snapshotId) {
3859
+ const known = this.snapshotProviders.get(snapshotId);
3860
+ const providers = this.getProviders().filter((p) => !!p.snapshot);
3861
+ if (!known) return providers;
3862
+ return [known, ...providers.filter((p) => p !== known)];
3863
+ }
3864
+ getSnapshotCreateCandidates(sandboxId, preferredProviderName) {
3865
+ if (preferredProviderName) {
3866
+ return [this.getProviderByName(preferredProviderName)];
3867
+ }
3868
+ const known = this.sandboxProviders.get(sandboxId);
3869
+ const providers = this.getProviders().filter((p) => !!p.snapshot);
3870
+ if (known && known.snapshot) {
3871
+ return [known, ...providers.filter((p) => p !== known)];
3872
+ }
3873
+ return providers;
3874
+ }
3875
+ async createWithFallback(options) {
3876
+ const preferredProviderName = options?.provider;
3877
+ const { provider: _providerName, ...providerOptions } = options || {};
3878
+ const candidates = this.getCreateCandidates(preferredProviderName);
3879
+ const canFallback = this.fallbackOnError && !preferredProviderName;
3880
+ const errors = [];
3881
+ for (const [index, provider] of candidates.entries()) {
3882
+ try {
3883
+ const sandbox = await provider.sandbox.create(providerOptions);
3884
+ this.registerSandboxProvider(sandbox, provider);
3885
+ return sandbox;
3886
+ } catch (error) {
3887
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
3888
+ if (!canFallback) {
3889
+ throw error;
3890
+ }
3891
+ }
3892
+ }
3893
+ throw new Error(
3894
+ `Failed to create sandbox across ${candidates.length} provider(s).
3895
+ ` + errors.map((error) => `- ${error}`).join("\n")
3896
+ );
3897
+ }
3898
+ async findOrCreateWithFallback(options) {
3899
+ const preferredProviderName = options.provider;
3900
+ const { provider: _providerName, ...providerOptions } = options;
3901
+ const candidates = this.getCreateCandidates(preferredProviderName);
3902
+ const canFallback = this.fallbackOnError && !preferredProviderName;
3903
+ const errors = [];
3904
+ for (const [index, provider] of candidates.entries()) {
3905
+ try {
3906
+ if (!provider.sandbox.findOrCreate) {
3907
+ errors.push(`${getProviderLabel(provider, index)}: findOrCreate() not supported`);
3908
+ continue;
3909
+ }
3910
+ const sandbox = await provider.sandbox.findOrCreate(providerOptions);
3911
+ this.registerSandboxProvider(sandbox, provider);
3912
+ return sandbox;
3913
+ } catch (error) {
3914
+ errors.push(`${getProviderLabel(provider, index)}: ${getProviderErrorDetail(error)}`);
3915
+ if (!canFallback) {
3916
+ throw error;
3917
+ }
3918
+ }
3919
+ }
3920
+ throw new Error(
3921
+ `Failed to findOrCreate sandbox across ${candidates.length} provider(s).
3922
+ ` + errors.map((error) => `- ${error}`).join("\n")
3923
+ );
4628
3924
  }
4629
- /**
4630
- * Explicitly configure the compute singleton
4631
- *
4632
- * @example
4633
- * ```typescript
4634
- * import { compute } from 'computesdk';
4635
- *
4636
- * compute.setConfig({
4637
- * provider: 'e2b',
4638
- * apiKey: 'computesdk_xxx',
4639
- * e2b: { apiKey: 'e2b_xxx' }
4640
- * });
4641
- *
4642
- * const sandbox = await compute.sandbox.create();
4643
- * ```
4644
- */
4645
3925
  setConfig(config) {
4646
- const gatewayConfig = createConfigFromExplicit(config);
4647
- this.config = gatewayConfig;
4648
- this.autoConfigured = false;
3926
+ this.providers = resolveProviders(config);
3927
+ this.providerStrategy = config.providerStrategy ?? "priority";
3928
+ this.fallbackOnError = config.fallbackOnError ?? true;
3929
+ this.roundRobinCursor = 0;
3930
+ this.sandboxProviders.clear();
3931
+ this.snapshotProviders.clear();
4649
3932
  }
4650
3933
  };
4651
3934
  var singletonInstance = new ComputeManager();
4652
3935
  function computeFactory(config) {
4653
- const gatewayConfig = createConfigFromExplicit(config);
4654
3936
  const manager = new ComputeManager();
4655
- manager["config"] = gatewayConfig;
3937
+ manager.setConfig(config);
4656
3938
  return manager;
4657
3939
  }
4658
3940
  var compute = new Proxy(
@@ -4675,33 +3957,15 @@ var compute = new Proxy(
4675
3957
  0 && (module.exports = {
4676
3958
  CommandExitError,
4677
3959
  FileWatcher,
4678
- GatewaySandbox,
4679
3960
  MessageType,
4680
- PROVIDER_AUTH,
4681
- PROVIDER_DASHBOARD_URLS,
4682
- PROVIDER_ENV_MAP,
4683
- PROVIDER_ENV_VARS,
4684
- PROVIDER_HEADERS,
4685
- PROVIDER_NAMES,
4686
- PROVIDER_PRIORITY,
4687
3961
  Sandbox,
4688
3962
  SignalService,
4689
- TRIBUTARY_URL,
4690
3963
  TerminalInstance,
4691
- autoConfigureCompute,
4692
- buildProviderHeaders,
4693
3964
  buildSetupPayload,
4694
3965
  compute,
4695
3966
  decodeBinaryMessage,
4696
- detectProvider,
4697
3967
  encodeBinaryMessage,
4698
3968
  encodeSetupPayload,
4699
- getMissingEnvVars,
4700
- getProviderConfigFromEnv,
4701
- getProviderHeaders,
4702
- isCommandExitError,
4703
- isGatewayModeEnabled,
4704
- isProviderAuthComplete,
4705
- isValidProvider
3969
+ isCommandExitError
4706
3970
  });
4707
3971
  //# sourceMappingURL=index.js.map