@tangle-network/sandbox 0.9.7 → 0.10.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/README.md +28 -0
- package/dist/agent/index.d.ts +2 -2
- package/dist/auth/index.js +66 -25
- package/dist/{client-BZSkXIEp.js → client-InX98Zxk.js} +98 -83
- package/dist/{client-DWkQOg1k.d.ts → client-cfdZckhU.d.ts} +5 -1
- package/dist/collaboration/index.js +1 -1
- package/dist/{collaboration-BxlfZ2Uh.js → collaboration-D17lnOJX.js} +1 -1
- package/dist/core.d.ts +3 -3
- package/dist/core.js +3 -3
- package/dist/{errors-DZsfJUuc.js → errors-DSz87Rkk.js} +10 -1
- package/dist/{errors-1Se5ATyZ.d.ts → errors-H9268M_g.d.ts} +5 -1
- package/dist/{index-DLH5MBrq.d.ts → index-DS4SOkKG.d.ts} +9 -4
- package/dist/index.d.ts +470 -7
- package/dist/index.js +562 -10
- package/dist/intelligence/index.js +1 -1
- package/dist/{sandbox-B6qbvsur.d.ts → sandbox-Bm2C9Phd.d.ts} +843 -164
- package/dist/{sandbox-bDWrUu8i.js → sandbox-CeimsfC8.js} +682 -164
- package/dist/tangle/index.d.ts +1 -1
- package/dist/tangle/index.js +1 -1
- package/dist/{tangle-CMGJEZit.js → tangle-DayyZe-i.js} +115 -37
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { a as DEFAULT_SANDBOX_SIZE, c as resolveSandboxResources, i as SandboxFleetClient, l as sandboxResourcesForSize, n as SandboxClient, o as SANDBOX_SIZE_PRESETS, r as SandboxFleet, s as SANDBOX_SIZE_PRESET_NAMES, t as IntelligenceClient } from "./client-
|
|
4
|
-
import { a as CollaborationClient, i as parseCollaborationDocumentId, n as buildCollaborationDocumentId, r as normalizeCollaborationPath, t as CollaborationFileBridge } from "./collaboration-
|
|
5
|
-
import { t as TangleSandboxClient } from "./tangle-
|
|
1
|
+
import { _ as normalizeRuntimeBackendConfig, a as InteractiveSessionHandle, c as getSandboxEventText, d as exportTraceBundle, f as otelTraceIdForTangleTrace, g as parseSSEStream, h as encodeTextForWire, i as SandboxSession, o as applySandboxEventText, p as toOtelJson, r as SandboxTaskSession, s as collectAgentResponseText, t as SandboxInstance, u as buildTraceExportPayload, v as serializeForSidecar } from "./sandbox-CeimsfC8.js";
|
|
2
|
+
import { a as NotFoundError, c as SandboxError, d as TimeoutError, f as ValidationError, i as NetworkError, l as ServerError, n as CapabilityError, o as PartialFailureError, r as EgressProxyRecoveryError, s as QuotaError, t as AuthError, u as StateError } from "./errors-DSz87Rkk.js";
|
|
3
|
+
import { a as DEFAULT_SANDBOX_SIZE, c as resolveSandboxResources, i as SandboxFleetClient, l as sandboxResourcesForSize, n as SandboxClient, o as SANDBOX_SIZE_PRESETS, r as SandboxFleet, s as SANDBOX_SIZE_PRESET_NAMES, t as IntelligenceClient } from "./client-InX98Zxk.js";
|
|
4
|
+
import { a as CollaborationClient, i as parseCollaborationDocumentId, n as buildCollaborationDocumentId, r as normalizeCollaborationPath, t as CollaborationFileBridge } from "./collaboration-D17lnOJX.js";
|
|
5
|
+
import { t as TangleSandboxClient } from "./tangle-DayyZe-i.js";
|
|
6
6
|
import { defineAgentProfile, defineGitHubResource, defineInlineResource, mergeAgentProfiles } from "@tangle-network/agent-interface";
|
|
7
7
|
//#region src/confidential.ts
|
|
8
8
|
function generateAttestationNonce(bytes = 32) {
|
|
@@ -595,6 +595,20 @@ function generateDockerfile(spec) {
|
|
|
595
595
|
return lines.join("\n");
|
|
596
596
|
}
|
|
597
597
|
//#endregion
|
|
598
|
+
//#region src/types.ts
|
|
599
|
+
const GPU_LEASE_PROVIDER_NAMES = [
|
|
600
|
+
"runpod",
|
|
601
|
+
"vast-ai",
|
|
602
|
+
"lambda-labs",
|
|
603
|
+
"tensordock",
|
|
604
|
+
"paperspace",
|
|
605
|
+
"aws",
|
|
606
|
+
"gcp",
|
|
607
|
+
"azure",
|
|
608
|
+
"crusoe",
|
|
609
|
+
"fluidstack"
|
|
610
|
+
];
|
|
611
|
+
//#endregion
|
|
598
612
|
//#region src/manage-sandboxes.ts
|
|
599
613
|
const MANAGE_SANDBOXES_TOOL_NAME = "manageSandboxes";
|
|
600
614
|
function createManageSandboxesTool(client, options = {}) {
|
|
@@ -605,8 +619,8 @@ function createManageSandboxesTool(client, options = {}) {
|
|
|
605
619
|
async execute(input) {
|
|
606
620
|
switch (input.action) {
|
|
607
621
|
case "capabilities": return client.fleets.capabilities();
|
|
608
|
-
case "create": return client.fleets.create(applyToolPolicy(requireCreateOptions(input), options.policy));
|
|
609
|
-
case "createWithCoordinator": return client.fleets.createWithCoordinator(applyToolPolicy(requireCreateWithCoordinatorOptions(input), options.policy));
|
|
622
|
+
case "create": return client.fleets.create(applyFleetGpuPolicy(applyToolPolicy(requireCreateOptions(input), options.policy), options.gpuPolicy));
|
|
623
|
+
case "createWithCoordinator": return client.fleets.createWithCoordinator(applyFleetGpuPolicyToCoordinator(applyToolPolicy(requireCreateWithCoordinatorOptions(input), options.policy), options.gpuPolicy));
|
|
610
624
|
case "list": return client.fleets.list({ fleetId: requireString(input, "fleetId") });
|
|
611
625
|
case "delete": return client.fleets.delete(requireString(input, "fleetId"));
|
|
612
626
|
case "attachMachine": return client.fleets.attachMachine(requireString(input, "fleetId"), requireMachine(input));
|
|
@@ -619,6 +633,13 @@ function createManageSandboxesTool(client, options = {}) {
|
|
|
619
633
|
case "createWorkspaceSnapshot": return client.fleets.createWorkspaceSnapshot(requireString(input, "fleetId"));
|
|
620
634
|
case "restoreWorkspaceSnapshot": return client.fleets.restoreWorkspaceSnapshot(requireString(input, "fleetId"), requireString(input, "snapshotId"));
|
|
621
635
|
case "reconcileWorkspace": return client.fleets.reconcileWorkspace(requireString(input, "fleetId"));
|
|
636
|
+
case "attachGpuLease": {
|
|
637
|
+
const attachOptions = applyGpuToolPolicy(requireGpuAttachOptions(input), options.gpuPolicy);
|
|
638
|
+
return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.attach(attachOptions);
|
|
639
|
+
}
|
|
640
|
+
case "listGpuLeases": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.list();
|
|
641
|
+
case "execGpuLease": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.exec(requireString(input, "leaseId"), requireGpuCommand(input));
|
|
642
|
+
case "detachGpuLease": return (await requireSandbox(client, requireString(input, "sandboxId"))).gpu.detach(requireString(input, "leaseId"));
|
|
622
643
|
default: throw new Error(`Unsupported manageSandboxes action: ${String(input.action)}`);
|
|
623
644
|
}
|
|
624
645
|
}
|
|
@@ -650,6 +671,77 @@ function applyToolPolicy(options, policy) {
|
|
|
650
671
|
policy
|
|
651
672
|
};
|
|
652
673
|
}
|
|
674
|
+
function applyFleetGpuPolicy(options, gpuPolicy) {
|
|
675
|
+
return {
|
|
676
|
+
...options,
|
|
677
|
+
defaults: options.defaults ? applyGpuPolicyToCreateFragment(options.defaults, gpuPolicy, "fleet defaults") : void 0,
|
|
678
|
+
machines: options.machines.map((machine) => applyGpuPolicyToCreateFragment(machine, gpuPolicy, `machine ${machine.machineId}`))
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function applyFleetGpuPolicyToCoordinator(options, gpuPolicy) {
|
|
682
|
+
return {
|
|
683
|
+
...options,
|
|
684
|
+
coordinator: options.coordinator ? applyGpuPolicyToCreateFragment(options.coordinator, gpuPolicy, "coordinator") : void 0,
|
|
685
|
+
workers: options.workers.map((worker) => applyGpuPolicyToCreateFragment(worker, gpuPolicy, `worker ${worker.machineId}`))
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function applyGpuPolicyToCreateFragment(fragment, gpuPolicy, label) {
|
|
689
|
+
if (fragment.gpuLease !== void 0) throw new Error("manageSandboxes GPU lease budget fields must be supplied by the tool host");
|
|
690
|
+
if (!fragment.resources?.accelerator) return fragment;
|
|
691
|
+
if (!gpuPolicy) throw new Error(`manageSandboxes ${label} requested accelerators but no host-owned GPU policy was supplied`);
|
|
692
|
+
return {
|
|
693
|
+
...fragment,
|
|
694
|
+
gpuLease: gpuLeaseFromPolicy(gpuPolicy)
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
function gpuLeaseFromPolicy(policy) {
|
|
698
|
+
return {
|
|
699
|
+
...policy.provider ? { provider: policy.provider } : {},
|
|
700
|
+
maxSpendUsd: policy.maxSpendUsd,
|
|
701
|
+
maxLifetimeSeconds: policy.maxLifetimeSeconds,
|
|
702
|
+
...policy.idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds: policy.idleTimeoutSeconds } : {}
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
async function requireSandbox(client, sandboxId) {
|
|
706
|
+
const box = await client.get(sandboxId);
|
|
707
|
+
if (!box) throw new Error(`Sandbox not found: ${sandboxId}`);
|
|
708
|
+
return box;
|
|
709
|
+
}
|
|
710
|
+
function requireGpuAttachOptions(input) {
|
|
711
|
+
const value = input.options;
|
|
712
|
+
if (!value || typeof value !== "object") throw new Error("manageSandboxes requires options");
|
|
713
|
+
const options = value;
|
|
714
|
+
for (const key of [
|
|
715
|
+
"maxSpendUsd",
|
|
716
|
+
"maxLifetimeSeconds",
|
|
717
|
+
"idleTimeoutSeconds"
|
|
718
|
+
]) if (key in options && options[key] !== void 0) throw new Error("manageSandboxes GPU lease budget fields must be supplied by the tool host");
|
|
719
|
+
const acceleratorValue = options.accelerator;
|
|
720
|
+
if (!acceleratorValue || typeof acceleratorValue !== "object") throw new Error("manageSandboxes GPU lease attach requires accelerator");
|
|
721
|
+
requireString(acceleratorValue, "kind");
|
|
722
|
+
return value;
|
|
723
|
+
}
|
|
724
|
+
function applyGpuToolPolicy(options, policy) {
|
|
725
|
+
if (!policy) throw new Error("manageSandboxes GPU lease attach requires a host-owned policy");
|
|
726
|
+
const requestedProvider = options.provider;
|
|
727
|
+
if (requestedProvider && policy.allowProviders && !policy.allowProviders.includes(requestedProvider)) throw new Error(`GPU provider ${requestedProvider} is not allowed by policy`);
|
|
728
|
+
const acceleratorCount = options.accelerator.count ?? 1;
|
|
729
|
+
if (policy.maxAcceleratorCount !== void 0 && acceleratorCount > policy.maxAcceleratorCount) throw new Error(`GPU lease requested ${acceleratorCount} accelerator(s) but policy.maxAcceleratorCount is ${policy.maxAcceleratorCount}`);
|
|
730
|
+
const provider = policy.provider ?? requestedProvider;
|
|
731
|
+
return {
|
|
732
|
+
...options,
|
|
733
|
+
...provider ? { provider } : {},
|
|
734
|
+
maxSpendUsd: policy.maxSpendUsd,
|
|
735
|
+
maxLifetimeSeconds: policy.maxLifetimeSeconds,
|
|
736
|
+
...policy.idleTimeoutSeconds !== void 0 ? { idleTimeoutSeconds: policy.idleTimeoutSeconds } : {}
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
function requireGpuCommand(input) {
|
|
740
|
+
const value = input.command;
|
|
741
|
+
if (!value || typeof value !== "object") throw new Error("manageSandboxes requires command");
|
|
742
|
+
requireString(value, "command");
|
|
743
|
+
return value;
|
|
744
|
+
}
|
|
653
745
|
function requireMachine(input) {
|
|
654
746
|
const value = input.machine;
|
|
655
747
|
if (!value || typeof value !== "object") throw new Error("manageSandboxes requires machine");
|
|
@@ -662,6 +754,135 @@ const fleetId = {
|
|
|
662
754
|
type: "string",
|
|
663
755
|
pattern: "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$"
|
|
664
756
|
};
|
|
757
|
+
const accelerator = {
|
|
758
|
+
type: "object",
|
|
759
|
+
additionalProperties: false,
|
|
760
|
+
required: ["kind"],
|
|
761
|
+
properties: {
|
|
762
|
+
kind: {
|
|
763
|
+
type: "string",
|
|
764
|
+
pattern: "^[A-Za-z0-9][A-Za-z0-9._-]*$",
|
|
765
|
+
description: "Accelerator kind such as gpu, nvidia, nvidia-l4, or nvidia-h100."
|
|
766
|
+
},
|
|
767
|
+
count: {
|
|
768
|
+
type: "integer",
|
|
769
|
+
minimum: 1,
|
|
770
|
+
maximum: 16
|
|
771
|
+
},
|
|
772
|
+
memoryMB: {
|
|
773
|
+
type: "integer",
|
|
774
|
+
minimum: 1
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
const gpuLeaseAttachOptions = {
|
|
779
|
+
type: "object",
|
|
780
|
+
additionalProperties: false,
|
|
781
|
+
required: ["accelerator"],
|
|
782
|
+
properties: {
|
|
783
|
+
provider: {
|
|
784
|
+
type: "string",
|
|
785
|
+
enum: [...GPU_LEASE_PROVIDER_NAMES]
|
|
786
|
+
},
|
|
787
|
+
accelerator,
|
|
788
|
+
image: {
|
|
789
|
+
type: "string",
|
|
790
|
+
minLength: 1
|
|
791
|
+
},
|
|
792
|
+
env: {
|
|
793
|
+
type: "object",
|
|
794
|
+
additionalProperties: { type: "string" }
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
const gpuLeaseCommand = {
|
|
799
|
+
type: "object",
|
|
800
|
+
additionalProperties: false,
|
|
801
|
+
required: ["command"],
|
|
802
|
+
properties: {
|
|
803
|
+
command: {
|
|
804
|
+
type: "string",
|
|
805
|
+
minLength: 1
|
|
806
|
+
},
|
|
807
|
+
timeoutMs: {
|
|
808
|
+
type: "integer",
|
|
809
|
+
minimum: 1,
|
|
810
|
+
maximum: 36e5
|
|
811
|
+
},
|
|
812
|
+
cwd: {
|
|
813
|
+
type: "string",
|
|
814
|
+
minLength: 1
|
|
815
|
+
},
|
|
816
|
+
env: {
|
|
817
|
+
type: "object",
|
|
818
|
+
additionalProperties: { type: "string" }
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
};
|
|
822
|
+
const resources = {
|
|
823
|
+
type: "object",
|
|
824
|
+
additionalProperties: false,
|
|
825
|
+
properties: {
|
|
826
|
+
cpuCores: {
|
|
827
|
+
type: "number",
|
|
828
|
+
exclusiveMinimum: 0
|
|
829
|
+
},
|
|
830
|
+
memoryMB: {
|
|
831
|
+
type: "integer",
|
|
832
|
+
minimum: 1
|
|
833
|
+
},
|
|
834
|
+
diskGB: {
|
|
835
|
+
type: "integer",
|
|
836
|
+
minimum: 1
|
|
837
|
+
},
|
|
838
|
+
accelerator
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
const driver = {
|
|
842
|
+
type: "object",
|
|
843
|
+
additionalProperties: true,
|
|
844
|
+
properties: {
|
|
845
|
+
type: {
|
|
846
|
+
type: "string",
|
|
847
|
+
enum: [
|
|
848
|
+
"docker",
|
|
849
|
+
"firecracker",
|
|
850
|
+
"host-agent",
|
|
851
|
+
"tangle"
|
|
852
|
+
]
|
|
853
|
+
},
|
|
854
|
+
runtimeBackend: {
|
|
855
|
+
type: "string",
|
|
856
|
+
enum: ["docker", "firecracker"]
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
const fleetMachineSpec = {
|
|
861
|
+
type: "object",
|
|
862
|
+
additionalProperties: true,
|
|
863
|
+
required: ["machineId"],
|
|
864
|
+
properties: {
|
|
865
|
+
machineId: fleetId,
|
|
866
|
+
name: {
|
|
867
|
+
type: "string",
|
|
868
|
+
minLength: 1
|
|
869
|
+
},
|
|
870
|
+
role: {
|
|
871
|
+
type: "string",
|
|
872
|
+
enum: ["coordinator", "worker"]
|
|
873
|
+
},
|
|
874
|
+
image: {
|
|
875
|
+
type: "string",
|
|
876
|
+
minLength: 1
|
|
877
|
+
},
|
|
878
|
+
environment: {
|
|
879
|
+
type: "string",
|
|
880
|
+
minLength: 1
|
|
881
|
+
},
|
|
882
|
+
resources,
|
|
883
|
+
driver
|
|
884
|
+
}
|
|
885
|
+
};
|
|
665
886
|
const MANAGE_SANDBOXES_PARAMETERS = {
|
|
666
887
|
type: "object",
|
|
667
888
|
additionalProperties: false,
|
|
@@ -684,16 +905,72 @@ const MANAGE_SANDBOXES_PARAMETERS = {
|
|
|
684
905
|
"cost",
|
|
685
906
|
"createWorkspaceSnapshot",
|
|
686
907
|
"restoreWorkspaceSnapshot",
|
|
687
|
-
"reconcileWorkspace"
|
|
908
|
+
"reconcileWorkspace",
|
|
909
|
+
"attachGpuLease",
|
|
910
|
+
"listGpuLeases",
|
|
911
|
+
"execGpuLease",
|
|
912
|
+
"detachGpuLease"
|
|
688
913
|
]
|
|
689
914
|
},
|
|
690
915
|
fleetId,
|
|
691
916
|
machineId: fleetId,
|
|
917
|
+
sandboxId: {
|
|
918
|
+
type: "string",
|
|
919
|
+
minLength: 1,
|
|
920
|
+
maxLength: 128
|
|
921
|
+
},
|
|
922
|
+
leaseId: {
|
|
923
|
+
type: "string",
|
|
924
|
+
minLength: 1,
|
|
925
|
+
maxLength: 128
|
|
926
|
+
},
|
|
692
927
|
snapshotId: {
|
|
693
928
|
type: "string",
|
|
694
929
|
minLength: 1
|
|
695
930
|
},
|
|
696
|
-
options: {
|
|
931
|
+
options: { anyOf: [{
|
|
932
|
+
type: "object",
|
|
933
|
+
additionalProperties: true,
|
|
934
|
+
properties: {
|
|
935
|
+
fleetId,
|
|
936
|
+
defaults: {
|
|
937
|
+
type: "object",
|
|
938
|
+
additionalProperties: true,
|
|
939
|
+
properties: {
|
|
940
|
+
image: {
|
|
941
|
+
type: "string",
|
|
942
|
+
minLength: 1
|
|
943
|
+
},
|
|
944
|
+
environment: {
|
|
945
|
+
type: "string",
|
|
946
|
+
minLength: 1
|
|
947
|
+
},
|
|
948
|
+
resources,
|
|
949
|
+
driver
|
|
950
|
+
}
|
|
951
|
+
},
|
|
952
|
+
machines: {
|
|
953
|
+
type: "array",
|
|
954
|
+
items: fleetMachineSpec,
|
|
955
|
+
minItems: 1
|
|
956
|
+
},
|
|
957
|
+
workers: {
|
|
958
|
+
type: "array",
|
|
959
|
+
items: fleetMachineSpec,
|
|
960
|
+
minItems: 1
|
|
961
|
+
},
|
|
962
|
+
coordinator: fleetMachineSpec,
|
|
963
|
+
workspace: {
|
|
964
|
+
type: "object",
|
|
965
|
+
additionalProperties: true,
|
|
966
|
+
properties: { mode: {
|
|
967
|
+
type: "string",
|
|
968
|
+
enum: ["isolated", "shared"]
|
|
969
|
+
} }
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
}, gpuLeaseAttachOptions] },
|
|
973
|
+
command: gpuLeaseCommand,
|
|
697
974
|
machine: {
|
|
698
975
|
type: "object",
|
|
699
976
|
required: ["machineId", "sandboxId"],
|
|
@@ -784,6 +1061,281 @@ function buildControlPlaneMcpConfig(options) {
|
|
|
784
1061
|
};
|
|
785
1062
|
}
|
|
786
1063
|
//#endregion
|
|
1064
|
+
//#region src/profile-file-mounts.ts
|
|
1065
|
+
const PROFILE_FILE_MOUNT_B64_CHUNK_CHARS = 3e3;
|
|
1066
|
+
const PROFILE_FILE_MOUNT_MAX_RETRIES = 4;
|
|
1067
|
+
const PROFILE_FILE_MOUNT_RETRY_BASE_MS = 250;
|
|
1068
|
+
const PROFILE_FILE_MOUNT_RETRY_MAX_MS = 2e3;
|
|
1069
|
+
const PROFILE_FILE_MOUNT_EXEC_TIMEOUT_MS = 3e4;
|
|
1070
|
+
const PROFILE_FILE_MOUNT_PACE_MS = 150;
|
|
1071
|
+
const PROFILE_BIN_DIR_RE = /(^|\/)(s?bin)\//;
|
|
1072
|
+
const DEFAULT_PROFILE_FILE_API_ABSOLUTE_PREFIXES = [
|
|
1073
|
+
["/home", "agent"].join("/"),
|
|
1074
|
+
["/home", "user"].join("/"),
|
|
1075
|
+
"/tmp",
|
|
1076
|
+
"/output"
|
|
1077
|
+
];
|
|
1078
|
+
function inlineContent(mount) {
|
|
1079
|
+
const resource = mount.resource;
|
|
1080
|
+
if (resource.kind !== "inline") throw new Error(`materializeProfileFileMounts: ${mount.path} requires an inline resource`);
|
|
1081
|
+
if (typeof resource.content !== "string") throw new Error(`materializeProfileFileMounts: ${mount.path} inline resource requires string content`);
|
|
1082
|
+
return resource.content;
|
|
1083
|
+
}
|
|
1084
|
+
function splitInlineProfileFileMounts(profile) {
|
|
1085
|
+
const files = profile.resources?.files ?? [];
|
|
1086
|
+
const deferredFiles = [];
|
|
1087
|
+
const keptFiles = [];
|
|
1088
|
+
for (const mount of files) if (mount.resource.kind === "inline") deferredFiles.push(mount);
|
|
1089
|
+
else keptFiles.push(mount);
|
|
1090
|
+
if (deferredFiles.length === 0) return {
|
|
1091
|
+
leanProfile: profile,
|
|
1092
|
+
deferredFiles
|
|
1093
|
+
};
|
|
1094
|
+
return {
|
|
1095
|
+
leanProfile: {
|
|
1096
|
+
...profile,
|
|
1097
|
+
resources: {
|
|
1098
|
+
...profile.resources ?? {},
|
|
1099
|
+
files: keptFiles
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
deferredFiles
|
|
1103
|
+
};
|
|
1104
|
+
}
|
|
1105
|
+
function shellSingleQuote(value) {
|
|
1106
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
1107
|
+
}
|
|
1108
|
+
function shellPath(path) {
|
|
1109
|
+
if (path === "~") return "\"$HOME\"";
|
|
1110
|
+
if (path.startsWith("~/")) {
|
|
1111
|
+
const rest = path.slice(2);
|
|
1112
|
+
return rest ? `"$HOME"/${shellSingleQuote(rest)}` : "\"$HOME\"";
|
|
1113
|
+
}
|
|
1114
|
+
return shellSingleQuote(path);
|
|
1115
|
+
}
|
|
1116
|
+
function validateProfileFilePath(path) {
|
|
1117
|
+
if (!path) throw new Error("materializeProfileFileMounts: file path is required");
|
|
1118
|
+
if (path.endsWith("/")) throw new Error(`materializeProfileFileMounts: refusing directory-shaped file path ${path}`);
|
|
1119
|
+
if (hasControlCharacter(path)) throw new Error(`materializeProfileFileMounts: refusing unsafe file path ${JSON.stringify(path)}`);
|
|
1120
|
+
if (path === "~" || /^~[^/]/.test(path)) throw new Error(`materializeProfileFileMounts: refusing unsupported home path ${path}`);
|
|
1121
|
+
if (hasUnsafeFileApiSegment(path)) throw new Error(`materializeProfileFileMounts: refusing unsafe file path ${path}`);
|
|
1122
|
+
}
|
|
1123
|
+
function hasUnsafeFileApiSegment(path) {
|
|
1124
|
+
return path.split("/").some((segment) => segment === "." || segment === ".." || segment === ".sidecar");
|
|
1125
|
+
}
|
|
1126
|
+
function hasControlCharacter(value) {
|
|
1127
|
+
for (let i = 0; i < value.length; i++) {
|
|
1128
|
+
const code = value.charCodeAt(i);
|
|
1129
|
+
if (code < 32 || code === 127) return true;
|
|
1130
|
+
}
|
|
1131
|
+
return false;
|
|
1132
|
+
}
|
|
1133
|
+
function normalizeAbsolutePrefixes(prefixes) {
|
|
1134
|
+
return prefixes.map((prefix) => {
|
|
1135
|
+
const normalized = prefix.trim().replace(/\/+$/, "");
|
|
1136
|
+
if (!normalized.startsWith("/") || hasControlCharacter(normalized)) throw new Error(`materializeProfileFileMounts: fileApiAbsolutePrefixes must contain absolute paths; received ${JSON.stringify(prefix)}`);
|
|
1137
|
+
if (hasUnsafeFileApiSegment(normalized)) throw new Error(`materializeProfileFileMounts: fileApiAbsolutePrefixes contains unsafe path ${JSON.stringify(prefix)}`);
|
|
1138
|
+
return normalized;
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
function isAllowedAbsoluteFileApiPath(path, prefixes) {
|
|
1142
|
+
for (const prefix of prefixes) if (path === prefix || path.startsWith(`${prefix}/`)) return true;
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
function fileApiTarget(mount, absolutePrefixes) {
|
|
1146
|
+
if (mount.path.startsWith("~/")) return null;
|
|
1147
|
+
if (mount.path.startsWith("/")) return isAllowedAbsoluteFileApiPath(mount.path, absolutePrefixes) ? mount.path : null;
|
|
1148
|
+
if (mount.path.startsWith("~")) return null;
|
|
1149
|
+
if (mount.path.length === 0) return null;
|
|
1150
|
+
return mount.path;
|
|
1151
|
+
}
|
|
1152
|
+
function isExecutableProfileFile(mount) {
|
|
1153
|
+
return mount.executable ?? PROFILE_BIN_DIR_RE.test(mount.path);
|
|
1154
|
+
}
|
|
1155
|
+
function profileFileMode(mount) {
|
|
1156
|
+
return isExecutableProfileFile(mount) ? 493 : void 0;
|
|
1157
|
+
}
|
|
1158
|
+
function fileApiSupportsMode(box) {
|
|
1159
|
+
return box.fs?.supportsWriteMode === true;
|
|
1160
|
+
}
|
|
1161
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1162
|
+
var ProfileFileMountExecTimeoutError = class extends Error {
|
|
1163
|
+
constructor(timeoutMs) {
|
|
1164
|
+
super(`exec exceeded ${timeoutMs}ms`);
|
|
1165
|
+
this.name = "ProfileFileMountExecTimeoutError";
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
function execWithTimeout(box, cmd, timeoutMs) {
|
|
1169
|
+
return new Promise((resolve, reject) => {
|
|
1170
|
+
let settled = false;
|
|
1171
|
+
const timer = setTimeout(() => {
|
|
1172
|
+
if (settled) return;
|
|
1173
|
+
settled = true;
|
|
1174
|
+
reject(new ProfileFileMountExecTimeoutError(timeoutMs));
|
|
1175
|
+
}, timeoutMs);
|
|
1176
|
+
box.exec(cmd, { timeoutMs }).then((res) => {
|
|
1177
|
+
if (settled) return;
|
|
1178
|
+
settled = true;
|
|
1179
|
+
clearTimeout(timer);
|
|
1180
|
+
resolve(res);
|
|
1181
|
+
}, (err) => {
|
|
1182
|
+
if (settled) return;
|
|
1183
|
+
settled = true;
|
|
1184
|
+
clearTimeout(timer);
|
|
1185
|
+
reject(err);
|
|
1186
|
+
});
|
|
1187
|
+
});
|
|
1188
|
+
}
|
|
1189
|
+
const TRANSIENT_EXEC_STATUS_CODES = new Set([
|
|
1190
|
+
408,
|
|
1191
|
+
409,
|
|
1192
|
+
425,
|
|
1193
|
+
429,
|
|
1194
|
+
500,
|
|
1195
|
+
502,
|
|
1196
|
+
503,
|
|
1197
|
+
504
|
|
1198
|
+
]);
|
|
1199
|
+
const TRANSIENT_EXEC_CODE_RE = /^(ECONNRESET|ECONNREFUSED|ETIMEDOUT|EPIPE|ECONNABORTED)$/i;
|
|
1200
|
+
const TRANSIENT_EXEC_MESSAGE_RE = /\b(408|409|425|429|500|502|503|504)\b|rate.?limit|too many requests|\bfetch failed\b|network error|connection reset|socket hang up|timed? out|service unavailable|bad gateway|gateway timeout|internal server error|\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b.{0,80}\bnot ready\b|\bnot ready\b.{0,80}\b(?:sidecar|runtime|exec(?:ution)?|terminal|sandbox|service|command(?:s)?|proxy)\b/i;
|
|
1201
|
+
function errorStatus(err) {
|
|
1202
|
+
const rawStatus = err.status ?? err.statusCode ?? (err.response && typeof err.response === "object" ? err.response.status : void 0);
|
|
1203
|
+
if (typeof rawStatus === "number") return rawStatus;
|
|
1204
|
+
if (typeof rawStatus === "string" && /^\d+$/.test(rawStatus)) return Number(rawStatus);
|
|
1205
|
+
}
|
|
1206
|
+
function retryAfterMs(err, seen = /* @__PURE__ */ new Set()) {
|
|
1207
|
+
if (!err || typeof err !== "object") return void 0;
|
|
1208
|
+
if (seen.has(err)) return void 0;
|
|
1209
|
+
seen.add(err);
|
|
1210
|
+
const e = err;
|
|
1211
|
+
if (typeof e.retryAfterMs === "number") return e.retryAfterMs;
|
|
1212
|
+
return retryAfterMs(e.cause, seen);
|
|
1213
|
+
}
|
|
1214
|
+
function isTransientExecError(err, seen = /* @__PURE__ */ new Set()) {
|
|
1215
|
+
if (!err || typeof err !== "object") return false;
|
|
1216
|
+
if (seen.has(err)) return false;
|
|
1217
|
+
seen.add(err);
|
|
1218
|
+
const e = err;
|
|
1219
|
+
const status = errorStatus(e);
|
|
1220
|
+
if (status !== void 0 && TRANSIENT_EXEC_STATUS_CODES.has(status)) return true;
|
|
1221
|
+
if (typeof e.code === "string") {
|
|
1222
|
+
if (TRANSIENT_EXEC_CODE_RE.test(e.code)) return true;
|
|
1223
|
+
if (/rate.?limit|too.?many.?requests|429|server.?error|service.?unavailable/i.test(e.code)) return true;
|
|
1224
|
+
}
|
|
1225
|
+
if (typeof e.message === "string" && TRANSIENT_EXEC_MESSAGE_RE.test(e.message)) return true;
|
|
1226
|
+
return isTransientExecError(e.cause, seen);
|
|
1227
|
+
}
|
|
1228
|
+
function transientExecError(err) {
|
|
1229
|
+
if (err instanceof ProfileFileMountExecTimeoutError) return { retryable: false };
|
|
1230
|
+
if (isTransientExecError(err)) return {
|
|
1231
|
+
retryable: true,
|
|
1232
|
+
retryAfterMs: retryAfterMs(err)
|
|
1233
|
+
};
|
|
1234
|
+
return { retryable: false };
|
|
1235
|
+
}
|
|
1236
|
+
async function sha256Hex(content) {
|
|
1237
|
+
const bytes = new TextEncoder().encode(content);
|
|
1238
|
+
const webSubtle = globalThis.crypto?.subtle;
|
|
1239
|
+
if (webSubtle) {
|
|
1240
|
+
const digest = await webSubtle.digest("SHA-256", bytes);
|
|
1241
|
+
return Array.from(new Uint8Array(digest)).map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
1242
|
+
}
|
|
1243
|
+
const { createHash } = await import("node:crypto");
|
|
1244
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
1245
|
+
}
|
|
1246
|
+
async function execProfileFileMount(box, mount, content, options, execState) {
|
|
1247
|
+
const maxRetries = options.maxRetries ?? PROFILE_FILE_MOUNT_MAX_RETRIES;
|
|
1248
|
+
const path = mount.path;
|
|
1249
|
+
const b64 = encodeTextForWire(content);
|
|
1250
|
+
const b64Chunks = [];
|
|
1251
|
+
for (let i = 0; i < b64.length; i += PROFILE_FILE_MOUNT_B64_CHUNK_CHARS) b64Chunks.push(b64.slice(i, i + PROFILE_FILE_MOUNT_B64_CHUNK_CHARS));
|
|
1252
|
+
const expectedSha256 = await sha256Hex(content);
|
|
1253
|
+
const dir = path.replace(/\/[^/]*$/, "");
|
|
1254
|
+
const executable = isExecutableProfileFile(mount);
|
|
1255
|
+
const q = shellPath(path);
|
|
1256
|
+
const qb64 = shellPath(`${path}.b64`);
|
|
1257
|
+
const qtmp = shellPath(`${path}.tmp`);
|
|
1258
|
+
const qpartPrefix = shellPath(`${path}.b64.part.`);
|
|
1259
|
+
const homeGuard = path.startsWith("~/") ? `[ -n "$HOME" ] || { echo ${shellSingleQuote(`materializeProfileFileMounts: HOME is unset for ${path}`)} >&2; exit 1; }; ` : "";
|
|
1260
|
+
const run = async (cmd) => {
|
|
1261
|
+
for (let attempt = 0;; attempt++) {
|
|
1262
|
+
if (execState.started && options.paceMs > 0) await sleep(options.paceMs);
|
|
1263
|
+
execState.started = true;
|
|
1264
|
+
try {
|
|
1265
|
+
return await execWithTimeout(box, cmd, options.execTimeoutMs);
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
const { retryable, retryAfterMs: retryAfter } = transientExecError(err);
|
|
1268
|
+
if (retryable && attempt < maxRetries) {
|
|
1269
|
+
const backoff = Math.min(PROFILE_FILE_MOUNT_RETRY_BASE_MS * 2 ** attempt, PROFILE_FILE_MOUNT_RETRY_MAX_MS);
|
|
1270
|
+
await sleep(Math.min(retryAfter ?? backoff, PROFILE_FILE_MOUNT_RETRY_MAX_MS));
|
|
1271
|
+
continue;
|
|
1272
|
+
}
|
|
1273
|
+
throw new Error(`materializeProfileFileMounts: exec failed for ${path}`, { cause: err });
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
const step = async (cmd) => {
|
|
1278
|
+
const result = await run(`${homeGuard}${cmd}`);
|
|
1279
|
+
if (result.exitCode !== 0) throw new Error(`materializeProfileFileMounts: failed to write ${path} (exit ${result.exitCode}): ${result.stderr.slice(0, 500)}`);
|
|
1280
|
+
};
|
|
1281
|
+
await step(dir ? `mkdir -p ${shellPath(dir)}` : ":");
|
|
1282
|
+
for (let i = 0; i < b64Chunks.length; i++) {
|
|
1283
|
+
const slice = b64Chunks[i];
|
|
1284
|
+
if (slice === void 0) continue;
|
|
1285
|
+
await step(`printf '%s' '${slice}' > ${shellPath(`${path}.b64.part.${i}`)}`);
|
|
1286
|
+
}
|
|
1287
|
+
const chmod = executable ? `chmod +x ${q} || exit 1; ` : "";
|
|
1288
|
+
const checksumMismatch = shellSingleQuote(`materializeProfileFileMounts: checksum mismatch for ${path}`);
|
|
1289
|
+
await step(`cleanup() { ${`rm -f ${qb64} ${qtmp}; i=0; while [ "$i" -lt ${b64Chunks.length} ]; do rm -f ${qpartPrefix}$i; i=$((i+1)); done`}; }; trap cleanup EXIT; expected='${expectedSha256}'; if [ -f ${q} ] && [ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ]; then ${chmod}exit 0; fi; : > ${qb64} && i=0; while [ "$i" -lt ${b64Chunks.length} ]; do cat ${qpartPrefix}$i >> ${qb64} || exit 1; i=$((i+1)); done && base64 -d ${qb64} > ${qtmp} && [ "$(sha256sum ${qtmp} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }; mv ${qtmp} ${q} && ${executable ? `chmod +x ${q} && ` : ""}[ "$(sha256sum ${q} | awk '{print $1}')" = "$expected" ] || { echo ${checksumMismatch} >&2; exit 1; }`);
|
|
1290
|
+
}
|
|
1291
|
+
async function materializeProfileFileMounts(box, files, options = {}) {
|
|
1292
|
+
const execTimeoutMs = options.execTimeoutMs ?? PROFILE_FILE_MOUNT_EXEC_TIMEOUT_MS;
|
|
1293
|
+
const paceMs = options.paceMs ?? PROFILE_FILE_MOUNT_PACE_MS;
|
|
1294
|
+
const maxRetries = options.maxRetries ?? PROFILE_FILE_MOUNT_MAX_RETRIES;
|
|
1295
|
+
const absolutePrefixes = normalizeAbsolutePrefixes(options.fileApiAbsolutePrefixes ?? DEFAULT_PROFILE_FILE_API_ABSOLUTE_PREFIXES);
|
|
1296
|
+
const fs = box.fs;
|
|
1297
|
+
const fileApiAvailable = typeof fs?.writeMany === "function";
|
|
1298
|
+
const modeAwareFileApi = fileApiSupportsMode(box);
|
|
1299
|
+
const viaFileApi = [];
|
|
1300
|
+
const viaExec = [];
|
|
1301
|
+
for (const mount of files) {
|
|
1302
|
+
const content = inlineContent(mount);
|
|
1303
|
+
validateProfileFilePath(mount.path);
|
|
1304
|
+
const target = fileApiAvailable ? fileApiTarget(mount, absolutePrefixes) : null;
|
|
1305
|
+
const executable = isExecutableProfileFile(mount);
|
|
1306
|
+
if (target !== null && (!executable || modeAwareFileApi)) {
|
|
1307
|
+
const mode = profileFileMode(mount);
|
|
1308
|
+
viaFileApi.push({
|
|
1309
|
+
path: target,
|
|
1310
|
+
content,
|
|
1311
|
+
...mode !== void 0 ? { mode } : {}
|
|
1312
|
+
});
|
|
1313
|
+
} else viaExec.push({
|
|
1314
|
+
mount,
|
|
1315
|
+
content
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
if (viaFileApi.length > 0) try {
|
|
1319
|
+
await fs?.writeMany(viaFileApi, {
|
|
1320
|
+
paceMs,
|
|
1321
|
+
maxRetries
|
|
1322
|
+
});
|
|
1323
|
+
} catch (err) {
|
|
1324
|
+
throw new Error("materializeProfileFileMounts: file API batch write failed", { cause: err });
|
|
1325
|
+
}
|
|
1326
|
+
const execState = { started: false };
|
|
1327
|
+
for (const item of viaExec) await execProfileFileMount(box, item.mount, item.content, {
|
|
1328
|
+
execTimeoutMs,
|
|
1329
|
+
paceMs,
|
|
1330
|
+
maxRetries
|
|
1331
|
+
}, execState);
|
|
1332
|
+
return {
|
|
1333
|
+
written: viaFileApi.length + viaExec.length,
|
|
1334
|
+
viaFileApi: viaFileApi.length,
|
|
1335
|
+
viaExec: viaExec.length
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
//#endregion
|
|
787
1339
|
//#region src/router-search.ts
|
|
788
1340
|
const DEFAULT_ROUTER_URL = "https://router.tangle.tools";
|
|
789
1341
|
async function fetchTangleRouterSearchConfig(options = {}) {
|
|
@@ -1177,4 +1729,4 @@ function stripUndefined(value) {
|
|
|
1177
1729
|
return Object.fromEntries(Object.entries(value).filter(([, v]) => v !== void 0).map(([k, v]) => [k, stripUndefined(v)]));
|
|
1178
1730
|
}
|
|
1179
1731
|
//#endregion
|
|
1180
|
-
export { AuthError, CONTROL_PLANE_MCP_SERVER_NAME, CapabilityError, CollaborationClient, CollaborationFileBridge, DEFAULT_SANDBOX_SIZE, Image, ImageBuilder, IntelligenceClient, InteractiveSessionHandle, MANAGE_SANDBOXES_PARAMETERS, MANAGE_SANDBOXES_TOOL_NAME, NetworkError, NotFoundError, PartialFailureError, QuotaError, SANDBOX_MCP_SERVER_NAME, SANDBOX_SIZE_PRESETS, SANDBOX_SIZE_PRESET_NAMES, SandboxClient as Sandbox, SandboxClient, SandboxError, SandboxFleet, SandboxFleetClient, SandboxInstance, SandboxSession, ServerError, StateError, TangleSandboxClient, TimeoutError, ValidationError, applySandboxEventText, buildCollaborationDocumentId, buildControlPlaneMcpConfig, buildSandboxMcpConfig, buildTangleRouterEvalMatrixRequest, buildTangleRouterResponsesWebSearchRequest, buildTangleRouterSearchBackend, buildTangleRouterSearchProfile, buildTraceExportPayload, collectAgentResponseText, createConfidentialSandbox, createManageSandboxesTool, defineAgentProfile, defineGitHubResource, defineInlineResource, exportTraceBundle, fetchTangleRouterSearchConfig, generateAttestationNonce, generateDockerfile, getSandboxEventText, mergeAgentProfiles, normalizeCollaborationPath, normalizeRuntimeBackendConfig, otelTraceIdForTangleTrace, parseCollaborationDocumentId, parseSSEStream, resolveSandboxResources, runTangleRouterEvalMatrixInSandbox, sandboxResourcesForSize, serializeForSidecar, startTeeAttestationHeartbeat, toOtelJson };
|
|
1732
|
+
export { AuthError, CONTROL_PLANE_MCP_SERVER_NAME, CapabilityError, CollaborationClient, CollaborationFileBridge, DEFAULT_SANDBOX_SIZE, EgressProxyRecoveryError, GPU_LEASE_PROVIDER_NAMES, Image, ImageBuilder, IntelligenceClient, InteractiveSessionHandle, MANAGE_SANDBOXES_PARAMETERS, MANAGE_SANDBOXES_TOOL_NAME, NetworkError, NotFoundError, PartialFailureError, QuotaError, SANDBOX_MCP_SERVER_NAME, SANDBOX_SIZE_PRESETS, SANDBOX_SIZE_PRESET_NAMES, SandboxClient as Sandbox, SandboxClient, SandboxError, SandboxFleet, SandboxFleetClient, SandboxInstance, SandboxSession, SandboxTaskSession, ServerError, StateError, TangleSandboxClient, TimeoutError, ValidationError, applySandboxEventText, buildCollaborationDocumentId, buildControlPlaneMcpConfig, buildSandboxMcpConfig, buildTangleRouterEvalMatrixRequest, buildTangleRouterResponsesWebSearchRequest, buildTangleRouterSearchBackend, buildTangleRouterSearchProfile, buildTraceExportPayload, collectAgentResponseText, createConfidentialSandbox, createManageSandboxesTool, defineAgentProfile, defineGitHubResource, defineInlineResource, exportTraceBundle, fetchTangleRouterSearchConfig, generateAttestationNonce, generateDockerfile, getSandboxEventText, materializeProfileFileMounts, mergeAgentProfiles, normalizeCollaborationPath, normalizeRuntimeBackendConfig, otelTraceIdForTangleTrace, parseCollaborationDocumentId, parseSSEStream, resolveSandboxResources, runTangleRouterEvalMatrixInSandbox, sandboxResourcesForSize, serializeForSidecar, splitInlineProfileFileMounts, startTeeAttestationHeartbeat, toOtelJson };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as TimeoutError, i as NetworkError, p as parseErrorResponse } from "../errors-DSz87Rkk.js";
|
|
2
2
|
//#region src/intelligence/index.ts
|
|
3
3
|
/**
|
|
4
4
|
* `@tangle-network/sandbox/intelligence` — browser-safe typed client for the
|