tensorlake 0.5.10 → 0.5.13
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/bin/darwin-arm64/tensorlake +0 -0
- package/dist/bin/darwin-arm64/tl +0 -0
- package/dist/bin/linux-x64/tensorlake +0 -0
- package/dist/bin/linux-x64/tl +0 -0
- package/dist/bin/win32-x64/tensorlake.exe +0 -0
- package/dist/bin/win32-x64/tl.exe +0 -0
- package/dist/index.cjs +391 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -3
- package/dist/index.d.ts +12 -3
- package/dist/index.js +391 -310
- package/dist/index.js.map +1 -1
- package/dist/{sandbox-image-CUEKMhZ1.d.cts → sandbox-image-BepgJl5u.d.cts} +20 -8
- package/dist/{sandbox-image-CUEKMhZ1.d.ts → sandbox-image-BepgJl5u.d.ts} +20 -8
- package/dist/sandbox-image.cjs +391 -310
- package/dist/sandbox-image.cjs.map +1 -1
- package/dist/sandbox-image.d.cts +1 -1
- package/dist/sandbox-image.d.ts +1 -1
- package/dist/sandbox-image.js +391 -310
- package/dist/sandbox-image.js.map +1 -1
- package/package.json +1 -1
package/dist/sandbox-image.cjs
CHANGED
|
@@ -83,7 +83,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
|
|
|
83
83
|
var init_defaults = __esm({
|
|
84
84
|
"src/defaults.ts"() {
|
|
85
85
|
"use strict";
|
|
86
|
-
SDK_VERSION = "0.5.
|
|
86
|
+
SDK_VERSION = "0.5.13";
|
|
87
87
|
API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
|
|
88
88
|
API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
|
|
89
89
|
NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
|
|
@@ -3911,6 +3911,46 @@ var init_client = __esm({
|
|
|
3911
3911
|
);
|
|
3912
3912
|
return Object.assign(sandboxes, { traceId: raw.traceId });
|
|
3913
3913
|
}
|
|
3914
|
+
/**
|
|
3915
|
+
* List archived (terminated) sandboxes in the namespace.
|
|
3916
|
+
*
|
|
3917
|
+
* Archived sandboxes are terminated sandboxes parked in the server's
|
|
3918
|
+
* archived sandboxes store until the server-configured TTL expires.
|
|
3919
|
+
*/
|
|
3920
|
+
async listArchived(options) {
|
|
3921
|
+
const query = [];
|
|
3922
|
+
if (options?.limit != null) {
|
|
3923
|
+
query.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
3924
|
+
}
|
|
3925
|
+
if (options?.cursor != null) {
|
|
3926
|
+
query.push(`cursor=${encodeURIComponent(options.cursor)}`);
|
|
3927
|
+
}
|
|
3928
|
+
if (options?.direction != null) {
|
|
3929
|
+
query.push(`direction=${encodeURIComponent(options.direction)}`);
|
|
3930
|
+
}
|
|
3931
|
+
const suffix = query.length ? `?${query.join("&")}` : "";
|
|
3932
|
+
const raw = await this.http.requestJson("GET", this.path(`archived-sandboxes${suffix}`));
|
|
3933
|
+
const sandboxes = (raw.sandboxes ?? []).map(
|
|
3934
|
+
(s) => fromSnakeKeys(s, "sandboxId")
|
|
3935
|
+
);
|
|
3936
|
+
const response = {
|
|
3937
|
+
sandboxes,
|
|
3938
|
+
prevCursor: raw.prev_cursor,
|
|
3939
|
+
nextCursor: raw.next_cursor
|
|
3940
|
+
};
|
|
3941
|
+
return Object.assign(response, { traceId: raw.traceId });
|
|
3942
|
+
}
|
|
3943
|
+
/** Get a single archived sandbox by id. */
|
|
3944
|
+
async getArchived(sandboxId) {
|
|
3945
|
+
const raw = await this.http.requestJson(
|
|
3946
|
+
"GET",
|
|
3947
|
+
this.path(`archived-sandboxes/${encodeURIComponent(sandboxId)}`)
|
|
3948
|
+
);
|
|
3949
|
+
return Object.assign(
|
|
3950
|
+
fromSnakeKeys(raw, "sandboxId"),
|
|
3951
|
+
{ traceId: raw.traceId }
|
|
3952
|
+
);
|
|
3953
|
+
}
|
|
3914
3954
|
/** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
|
|
3915
3955
|
async update(sandboxId, options) {
|
|
3916
3956
|
const body = {};
|
|
@@ -4449,12 +4489,6 @@ function shellSplit(input) {
|
|
|
4449
4489
|
}
|
|
4450
4490
|
return tokens;
|
|
4451
4491
|
}
|
|
4452
|
-
function shellQuote(value) {
|
|
4453
|
-
if (!value) {
|
|
4454
|
-
return "''";
|
|
4455
|
-
}
|
|
4456
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
4457
|
-
}
|
|
4458
4492
|
function stripLeadingFlags(value) {
|
|
4459
4493
|
const flags = {};
|
|
4460
4494
|
let remaining = value.trimStart();
|
|
@@ -4492,75 +4526,6 @@ function parseFromValue(value, lineNumber) {
|
|
|
4492
4526
|
}
|
|
4493
4527
|
return tokens[0];
|
|
4494
4528
|
}
|
|
4495
|
-
function parseCopyLikeValues(value, lineNumber, keyword) {
|
|
4496
|
-
const { flags, remaining } = stripLeadingFlags(value);
|
|
4497
|
-
if ("from" in flags) {
|
|
4498
|
-
throw new Error(
|
|
4499
|
-
`line ${lineNumber}: ${keyword} --from is not supported for sandbox image creation`
|
|
4500
|
-
);
|
|
4501
|
-
}
|
|
4502
|
-
const payload = remaining.trim();
|
|
4503
|
-
if (!payload) {
|
|
4504
|
-
throw new Error(
|
|
4505
|
-
`line ${lineNumber}: ${keyword} must include source and destination`
|
|
4506
|
-
);
|
|
4507
|
-
}
|
|
4508
|
-
let parts;
|
|
4509
|
-
if (payload.startsWith("[")) {
|
|
4510
|
-
let parsed;
|
|
4511
|
-
try {
|
|
4512
|
-
parsed = JSON.parse(payload);
|
|
4513
|
-
} catch (error) {
|
|
4514
|
-
throw new Error(
|
|
4515
|
-
`line ${lineNumber}: invalid JSON array syntax for ${keyword}: ${error.message}`
|
|
4516
|
-
);
|
|
4517
|
-
}
|
|
4518
|
-
if (!Array.isArray(parsed) || parsed.length < 2 || parsed.some((item) => typeof item !== "string")) {
|
|
4519
|
-
throw new Error(
|
|
4520
|
-
`line ${lineNumber}: ${keyword} JSON array form requires at least two string values`
|
|
4521
|
-
);
|
|
4522
|
-
}
|
|
4523
|
-
parts = parsed;
|
|
4524
|
-
} else {
|
|
4525
|
-
parts = shellSplit(payload);
|
|
4526
|
-
if (parts.length < 2) {
|
|
4527
|
-
throw new Error(
|
|
4528
|
-
`line ${lineNumber}: ${keyword} must include at least one source and one destination`
|
|
4529
|
-
);
|
|
4530
|
-
}
|
|
4531
|
-
}
|
|
4532
|
-
return {
|
|
4533
|
-
flags,
|
|
4534
|
-
sources: parts.slice(0, -1),
|
|
4535
|
-
destination: parts[parts.length - 1]
|
|
4536
|
-
};
|
|
4537
|
-
}
|
|
4538
|
-
function parseEnvPairs(value, lineNumber) {
|
|
4539
|
-
const tokens = shellSplit(value);
|
|
4540
|
-
if (tokens.length === 0) {
|
|
4541
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4542
|
-
}
|
|
4543
|
-
if (tokens.every((token) => token.includes("="))) {
|
|
4544
|
-
return tokens.map((token) => {
|
|
4545
|
-
const [key, envValue] = token.split(/=(.*)/s, 2);
|
|
4546
|
-
if (!key) {
|
|
4547
|
-
throw new Error(`line ${lineNumber}: invalid ENV token '${token}'`);
|
|
4548
|
-
}
|
|
4549
|
-
return [key, envValue];
|
|
4550
|
-
});
|
|
4551
|
-
}
|
|
4552
|
-
if (tokens.length < 2) {
|
|
4553
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4554
|
-
}
|
|
4555
|
-
return [[tokens[0], tokens.slice(1).join(" ")]];
|
|
4556
|
-
}
|
|
4557
|
-
function resolveContainerPath(containerPath, workingDir) {
|
|
4558
|
-
if (!containerPath) {
|
|
4559
|
-
return workingDir;
|
|
4560
|
-
}
|
|
4561
|
-
const normalized = containerPath.startsWith("/") ? import_node_path.default.posix.normalize(containerPath) : import_node_path.default.posix.normalize(import_node_path.default.posix.join(workingDir, containerPath));
|
|
4562
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
4563
|
-
}
|
|
4564
4529
|
function buildPlanFromDockerfileText(dockerfileText, dockerfilePath, contextDir, registeredName) {
|
|
4565
4530
|
let baseImage;
|
|
4566
4531
|
const instructions = [];
|
|
@@ -4671,14 +4636,292 @@ function buildContextFromEnv() {
|
|
|
4671
4636
|
};
|
|
4672
4637
|
}
|
|
4673
4638
|
function createDefaultClient(context) {
|
|
4639
|
+
const useScopeHeaders = context.personalAccessToken != null && context.apiKey == null;
|
|
4674
4640
|
return new SandboxClient({
|
|
4675
4641
|
apiUrl: context.apiUrl,
|
|
4676
4642
|
apiKey: context.apiKey ?? context.personalAccessToken,
|
|
4677
|
-
organizationId: context.organizationId,
|
|
4678
|
-
projectId: context.projectId,
|
|
4643
|
+
organizationId: useScopeHeaders ? context.organizationId : void 0,
|
|
4644
|
+
projectId: useScopeHeaders ? context.projectId : void 0,
|
|
4679
4645
|
namespace: context.namespace
|
|
4680
4646
|
});
|
|
4681
4647
|
}
|
|
4648
|
+
function baseApiUrl(context) {
|
|
4649
|
+
return context.apiUrl.replace(/\/+$/, "");
|
|
4650
|
+
}
|
|
4651
|
+
function scopedBuildsPath(context) {
|
|
4652
|
+
return `/platform/v1/organizations/${encodeURIComponent(context.organizationId)}/projects/${encodeURIComponent(context.projectId)}/sandbox-template-builds`;
|
|
4653
|
+
}
|
|
4654
|
+
function platformHeaders(context) {
|
|
4655
|
+
const headers = {
|
|
4656
|
+
Authorization: `Bearer ${context.bearerToken}`,
|
|
4657
|
+
"Content-Type": "application/json"
|
|
4658
|
+
};
|
|
4659
|
+
if (context.useScopeHeaders) {
|
|
4660
|
+
headers["X-Forwarded-Organization-Id"] = context.organizationId;
|
|
4661
|
+
headers["X-Forwarded-Project-Id"] = context.projectId;
|
|
4662
|
+
}
|
|
4663
|
+
return headers;
|
|
4664
|
+
}
|
|
4665
|
+
async function requestJson(url, init, errorPrefix) {
|
|
4666
|
+
const response = await fetch(url, init);
|
|
4667
|
+
if (!response.ok) {
|
|
4668
|
+
throw new Error(
|
|
4669
|
+
`${errorPrefix} (HTTP ${response.status}): ${await response.text()}`
|
|
4670
|
+
);
|
|
4671
|
+
}
|
|
4672
|
+
const text = await response.text();
|
|
4673
|
+
return text ? JSON.parse(text) : {};
|
|
4674
|
+
}
|
|
4675
|
+
async function resolveBuildContext(context) {
|
|
4676
|
+
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4677
|
+
if (!bearerToken) {
|
|
4678
|
+
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
4679
|
+
}
|
|
4680
|
+
if (context.apiKey) {
|
|
4681
|
+
const scope = await requestJson(
|
|
4682
|
+
`${baseApiUrl(context)}/platform/v1/keys/introspect`,
|
|
4683
|
+
{
|
|
4684
|
+
method: "POST",
|
|
4685
|
+
headers: {
|
|
4686
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
4687
|
+
"Content-Type": "application/json"
|
|
4688
|
+
}
|
|
4689
|
+
},
|
|
4690
|
+
"API key introspection failed"
|
|
4691
|
+
);
|
|
4692
|
+
if (!scope.organizationId || !scope.projectId) {
|
|
4693
|
+
throw new Error("API key introspection response is missing organizationId or projectId");
|
|
4694
|
+
}
|
|
4695
|
+
return {
|
|
4696
|
+
...context,
|
|
4697
|
+
bearerToken,
|
|
4698
|
+
organizationId: scope.organizationId,
|
|
4699
|
+
projectId: scope.projectId,
|
|
4700
|
+
useScopeHeaders: false
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
if (!context.organizationId || !context.projectId) {
|
|
4704
|
+
throw new Error(
|
|
4705
|
+
"Personal Access Token authentication requires TENSORLAKE_ORGANIZATION_ID and TENSORLAKE_PROJECT_ID to be set (e.g. via 'tl login && tl init'). To skip this requirement, authenticate with TENSORLAKE_API_KEY instead \u2014 API keys are bound to a single project at creation."
|
|
4706
|
+
);
|
|
4707
|
+
}
|
|
4708
|
+
return {
|
|
4709
|
+
...context,
|
|
4710
|
+
bearerToken,
|
|
4711
|
+
organizationId: context.organizationId,
|
|
4712
|
+
projectId: context.projectId,
|
|
4713
|
+
useScopeHeaders: true
|
|
4714
|
+
};
|
|
4715
|
+
}
|
|
4716
|
+
async function prepareRootfsBuild(context, plan, isPublic) {
|
|
4717
|
+
if (!plan.baseImage) {
|
|
4718
|
+
throw new Error("Sandbox image builds require a Dockerfile FROM image or Image baseImage");
|
|
4719
|
+
}
|
|
4720
|
+
const spec = await requestJson(
|
|
4721
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}`,
|
|
4722
|
+
{
|
|
4723
|
+
method: "POST",
|
|
4724
|
+
headers: platformHeaders(context),
|
|
4725
|
+
body: JSON.stringify({
|
|
4726
|
+
name: plan.registeredName,
|
|
4727
|
+
dockerfile: plan.dockerfileText,
|
|
4728
|
+
baseImage: plan.baseImage,
|
|
4729
|
+
public: isPublic
|
|
4730
|
+
})
|
|
4731
|
+
},
|
|
4732
|
+
"failed to prepare sandbox image build"
|
|
4733
|
+
);
|
|
4734
|
+
return { prepared: parsePreparedBuild(spec), spec };
|
|
4735
|
+
}
|
|
4736
|
+
function parsePreparedBuild(raw) {
|
|
4737
|
+
const builder = raw.builder;
|
|
4738
|
+
if (!builder) {
|
|
4739
|
+
throw new Error("platform API response is missing rootfs builder configuration");
|
|
4740
|
+
}
|
|
4741
|
+
const prepared = {
|
|
4742
|
+
...raw,
|
|
4743
|
+
buildId: requiredString(raw, "buildId"),
|
|
4744
|
+
snapshotId: requiredString(raw, "snapshotId"),
|
|
4745
|
+
snapshotUri: requiredString(raw, "snapshotUri"),
|
|
4746
|
+
rootfsNodeKind: requiredString(raw, "rootfsNodeKind"),
|
|
4747
|
+
builder: {
|
|
4748
|
+
image: requiredString(builder, "image"),
|
|
4749
|
+
command: requiredString(builder, "command"),
|
|
4750
|
+
cpus: requiredNumber(builder, "cpus"),
|
|
4751
|
+
memoryMb: requiredNumber(builder, "memoryMb"),
|
|
4752
|
+
diskMb: requiredNumber(builder, "diskMb")
|
|
4753
|
+
}
|
|
4754
|
+
};
|
|
4755
|
+
const parent = raw.parent;
|
|
4756
|
+
if (parent != null) {
|
|
4757
|
+
prepared.parent = {
|
|
4758
|
+
parentManifestUri: requiredString(parent, "parentManifestUri"),
|
|
4759
|
+
rootfsDiskBytes: optionalNumber(parent, "rootfsDiskBytes")
|
|
4760
|
+
};
|
|
4761
|
+
}
|
|
4762
|
+
return prepared;
|
|
4763
|
+
}
|
|
4764
|
+
async function completeRootfsBuild(context, buildId, request) {
|
|
4765
|
+
return requestJson(
|
|
4766
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}/${encodeURIComponent(buildId)}/complete`,
|
|
4767
|
+
{
|
|
4768
|
+
method: "POST",
|
|
4769
|
+
headers: platformHeaders(context),
|
|
4770
|
+
body: JSON.stringify(request)
|
|
4771
|
+
},
|
|
4772
|
+
"failed to complete sandbox image build"
|
|
4773
|
+
);
|
|
4774
|
+
}
|
|
4775
|
+
async function resolvedDockerConfigJson() {
|
|
4776
|
+
const configDir = process.env.DOCKER_CONFIG ?? import_node_path.default.join((0, import_node_os.homedir)(), ".docker");
|
|
4777
|
+
const configPath = import_node_path.default.join(configDir, "config.json");
|
|
4778
|
+
try {
|
|
4779
|
+
const content = await (0, import_promises.readFile)(configPath, "utf8");
|
|
4780
|
+
const parsed = JSON.parse(content);
|
|
4781
|
+
const auths = parsed.auths;
|
|
4782
|
+
if (auths != null && Object.keys(auths).length > 0) {
|
|
4783
|
+
return JSON.stringify({ auths });
|
|
4784
|
+
}
|
|
4785
|
+
} catch (error) {
|
|
4786
|
+
if (error.code === "ENOENT") {
|
|
4787
|
+
return void 0;
|
|
4788
|
+
}
|
|
4789
|
+
throw error;
|
|
4790
|
+
}
|
|
4791
|
+
return void 0;
|
|
4792
|
+
}
|
|
4793
|
+
function rootfsDiskBytes(diskMb, prepared) {
|
|
4794
|
+
if (diskMb != null) {
|
|
4795
|
+
return diskMb * 1024 * 1024;
|
|
4796
|
+
}
|
|
4797
|
+
if (prepared.parent != null) {
|
|
4798
|
+
if (prepared.parent.rootfsDiskBytes == null) {
|
|
4799
|
+
throw new Error(
|
|
4800
|
+
"platform API did not return parent rootfsDiskBytes for diff build; pass diskMb explicitly or update Platform API"
|
|
4801
|
+
);
|
|
4802
|
+
}
|
|
4803
|
+
return prepared.parent.rootfsDiskBytes;
|
|
4804
|
+
}
|
|
4805
|
+
return DEFAULT_ROOTFS_DISK_MB * 1024 * 1024;
|
|
4806
|
+
}
|
|
4807
|
+
function rootfsDiskBytesToMb(bytes) {
|
|
4808
|
+
return Math.ceil(bytes / (1024 * 1024));
|
|
4809
|
+
}
|
|
4810
|
+
async function buildRootfsSpec(preparedSpec, prepared, plan, diskMb) {
|
|
4811
|
+
const spec = {
|
|
4812
|
+
...preparedSpec,
|
|
4813
|
+
dockerfile: plan.dockerfileText,
|
|
4814
|
+
contextDir: REMOTE_CONTEXT_DIR,
|
|
4815
|
+
baseImage: plan.baseImage,
|
|
4816
|
+
rootfsDiskBytes: rootfsDiskBytes(diskMb, prepared)
|
|
4817
|
+
};
|
|
4818
|
+
const dockerConfigJson = await resolvedDockerConfigJson();
|
|
4819
|
+
if (dockerConfigJson != null) {
|
|
4820
|
+
spec.dockerConfigJson = dockerConfigJson;
|
|
4821
|
+
}
|
|
4822
|
+
return spec;
|
|
4823
|
+
}
|
|
4824
|
+
function rootfsBuilderExecutable(executable) {
|
|
4825
|
+
return executable === ROOTFS_BUILDER_COMMAND ? `${ROOTFS_BUILDER_BIN_DIR}/${ROOTFS_BUILDER_COMMAND}` : executable;
|
|
4826
|
+
}
|
|
4827
|
+
function rootfsBuilderEnv() {
|
|
4828
|
+
return { PATH: ROOTFS_BUILDER_PATH };
|
|
4829
|
+
}
|
|
4830
|
+
async function runRootfsBuilder(sandbox, command, emit, sleep3) {
|
|
4831
|
+
const parts = shellSplit(command);
|
|
4832
|
+
const [executable, ...commandArgs] = parts;
|
|
4833
|
+
if (!executable) {
|
|
4834
|
+
throw new Error("empty rootfs builder command returned by platform API");
|
|
4835
|
+
}
|
|
4836
|
+
await runStreaming(
|
|
4837
|
+
sandbox,
|
|
4838
|
+
emit,
|
|
4839
|
+
sleep3,
|
|
4840
|
+
rootfsBuilderExecutable(executable),
|
|
4841
|
+
[...commandArgs, "--spec", REMOTE_SPEC_PATH, "--metadata-out", REMOTE_METADATA_PATH],
|
|
4842
|
+
rootfsBuilderEnv(),
|
|
4843
|
+
REMOTE_BUILD_DIR
|
|
4844
|
+
);
|
|
4845
|
+
}
|
|
4846
|
+
function metadataString(metadata, snakeKey, camelKey) {
|
|
4847
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4848
|
+
return typeof value === "string" ? value : void 0;
|
|
4849
|
+
}
|
|
4850
|
+
function metadataNumber(metadata, snakeKey, camelKey) {
|
|
4851
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4852
|
+
if (typeof value === "number") {
|
|
4853
|
+
return value;
|
|
4854
|
+
}
|
|
4855
|
+
if (typeof value === "string" && value.trim()) {
|
|
4856
|
+
const parsed = Number(value);
|
|
4857
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
4858
|
+
}
|
|
4859
|
+
return void 0;
|
|
4860
|
+
}
|
|
4861
|
+
function completeRequestFromMetadata(prepared, metadata) {
|
|
4862
|
+
const rootfsNodeKind = metadataString(metadata, "rootfs_node_kind", "rootfsNodeKind") ?? prepared.rootfsNodeKind;
|
|
4863
|
+
const parentManifestUri = metadataString(metadata, "parent_manifest_uri", "parentManifestUri") ?? (rootfsNodeKind === "diff" ? prepared.parent?.parentManifestUri : void 0);
|
|
4864
|
+
if (rootfsNodeKind === "diff" && parentManifestUri == null) {
|
|
4865
|
+
throw new Error("rootfs diff build completed without parent_manifest_uri");
|
|
4866
|
+
}
|
|
4867
|
+
const snapshotFormatVersion = metadataString(
|
|
4868
|
+
metadata,
|
|
4869
|
+
"snapshot_format_version",
|
|
4870
|
+
"snapshotFormatVersion"
|
|
4871
|
+
);
|
|
4872
|
+
const snapshotSizeBytes = metadataNumber(
|
|
4873
|
+
metadata,
|
|
4874
|
+
"snapshot_size_bytes",
|
|
4875
|
+
"snapshotSizeBytes"
|
|
4876
|
+
);
|
|
4877
|
+
const rootfsDiskBytesValue = metadataNumber(
|
|
4878
|
+
metadata,
|
|
4879
|
+
"rootfs_disk_bytes",
|
|
4880
|
+
"rootfsDiskBytes"
|
|
4881
|
+
);
|
|
4882
|
+
if (!snapshotFormatVersion) {
|
|
4883
|
+
throw new Error("rootfs builder metadata is missing snapshot_format_version");
|
|
4884
|
+
}
|
|
4885
|
+
if (snapshotSizeBytes == null) {
|
|
4886
|
+
throw new Error("rootfs builder metadata is missing numeric snapshot_size_bytes");
|
|
4887
|
+
}
|
|
4888
|
+
if (rootfsDiskBytesValue == null) {
|
|
4889
|
+
throw new Error("rootfs builder metadata is missing numeric rootfs_disk_bytes");
|
|
4890
|
+
}
|
|
4891
|
+
return {
|
|
4892
|
+
snapshotId: metadataString(metadata, "snapshot_id", "snapshotId") ?? prepared.snapshotId,
|
|
4893
|
+
snapshotUri: metadataString(metadata, "snapshot_uri", "snapshotUri") ?? prepared.snapshotUri,
|
|
4894
|
+
snapshotFormatVersion,
|
|
4895
|
+
snapshotSizeBytes,
|
|
4896
|
+
rootfsDiskBytes: rootfsDiskBytesValue,
|
|
4897
|
+
rootfsNodeKind,
|
|
4898
|
+
...parentManifestUri ? { parentManifestUri } : {}
|
|
4899
|
+
};
|
|
4900
|
+
}
|
|
4901
|
+
function requiredString(object, key) {
|
|
4902
|
+
const value = object[key];
|
|
4903
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
4904
|
+
throw new Error(`expected '${key}' to be a non-empty string`);
|
|
4905
|
+
}
|
|
4906
|
+
return value;
|
|
4907
|
+
}
|
|
4908
|
+
function requiredNumber(object, key) {
|
|
4909
|
+
const value = object[key];
|
|
4910
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4911
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
4912
|
+
}
|
|
4913
|
+
return value;
|
|
4914
|
+
}
|
|
4915
|
+
function optionalNumber(object, key) {
|
|
4916
|
+
const value = object[key];
|
|
4917
|
+
if (value == null) {
|
|
4918
|
+
return void 0;
|
|
4919
|
+
}
|
|
4920
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4921
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
4922
|
+
}
|
|
4923
|
+
return value;
|
|
4924
|
+
}
|
|
4682
4925
|
async function runChecked(sandbox, command, args, env, workingDir) {
|
|
4683
4926
|
const result = await sandbox.run(command, {
|
|
4684
4927
|
args,
|
|
@@ -4738,18 +4981,6 @@ function emitOutputLines(emit, stream, response, seen) {
|
|
|
4738
4981
|
emit({ type: "build_log", stream, message: line });
|
|
4739
4982
|
}
|
|
4740
4983
|
}
|
|
4741
|
-
function isPathWithinContext(contextDir, localPath) {
|
|
4742
|
-
const relative = import_node_path.default.relative(contextDir, localPath);
|
|
4743
|
-
return relative === "" || !relative.startsWith("..") && !import_node_path.default.isAbsolute(relative);
|
|
4744
|
-
}
|
|
4745
|
-
function resolveContextSourcePath(contextDir, source) {
|
|
4746
|
-
const resolvedContextDir = import_node_path.default.resolve(contextDir);
|
|
4747
|
-
const resolvedSource = import_node_path.default.resolve(resolvedContextDir, source);
|
|
4748
|
-
if (!isPathWithinContext(resolvedContextDir, resolvedSource)) {
|
|
4749
|
-
throw new Error(`Local path escapes the build context: ${source}`);
|
|
4750
|
-
}
|
|
4751
|
-
return resolvedSource;
|
|
4752
|
-
}
|
|
4753
4984
|
async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
4754
4985
|
const fileStats = await (0, import_promises.stat)(localPath).catch(() => null);
|
|
4755
4986
|
if (!fileStats) {
|
|
@@ -4780,168 +5011,7 @@ async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
|
4780
5011
|
}
|
|
4781
5012
|
}
|
|
4782
5013
|
}
|
|
4783
|
-
async function
|
|
4784
|
-
const exportLine = `export ${key}=${shellQuote(value)}`;
|
|
4785
|
-
await runChecked(
|
|
4786
|
-
sandbox,
|
|
4787
|
-
"sh",
|
|
4788
|
-
["-c", `printf '%s\\n' ${shellQuote(exportLine)} >> /etc/environment`],
|
|
4789
|
-
processEnv
|
|
4790
|
-
);
|
|
4791
|
-
}
|
|
4792
|
-
async function copyFromContext(sandbox, emit, contextDir, sources, destination, workingDir, keyword) {
|
|
4793
|
-
const destinationPath = resolveContainerPath(destination, workingDir);
|
|
4794
|
-
if (sources.length > 1 && !destinationPath.endsWith("/")) {
|
|
4795
|
-
throw new Error(
|
|
4796
|
-
`${keyword} with multiple sources requires a directory destination ending in '/'`
|
|
4797
|
-
);
|
|
4798
|
-
}
|
|
4799
|
-
for (const source of sources) {
|
|
4800
|
-
const localSource = resolveContextSourcePath(contextDir, source);
|
|
4801
|
-
const localStats = await (0, import_promises.stat)(localSource).catch(() => null);
|
|
4802
|
-
if (!localStats) {
|
|
4803
|
-
throw new Error(`Local path not found: ${localSource}`);
|
|
4804
|
-
}
|
|
4805
|
-
let remoteDestination = destinationPath;
|
|
4806
|
-
if (sources.length > 1) {
|
|
4807
|
-
remoteDestination = import_node_path.default.posix.join(
|
|
4808
|
-
destinationPath.replace(/\/$/, ""),
|
|
4809
|
-
import_node_path.default.posix.basename(source.replace(/\/$/, ""))
|
|
4810
|
-
);
|
|
4811
|
-
} else if (localStats.isFile() && destinationPath.endsWith("/")) {
|
|
4812
|
-
remoteDestination = import_node_path.default.posix.join(
|
|
4813
|
-
destinationPath.replace(/\/$/, ""),
|
|
4814
|
-
import_node_path.default.basename(source)
|
|
4815
|
-
);
|
|
4816
|
-
}
|
|
4817
|
-
emit({
|
|
4818
|
-
type: "status",
|
|
4819
|
-
message: `${keyword} ${source} -> ${remoteDestination}`
|
|
4820
|
-
});
|
|
4821
|
-
await copyLocalPathToSandbox(sandbox, localSource, remoteDestination);
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
async function addUrlToSandbox(sandbox, emit, url, destination, workingDir, processEnv, sleep3) {
|
|
4825
|
-
let destinationPath = resolveContainerPath(destination, workingDir);
|
|
4826
|
-
const parsedUrl = new URL(url);
|
|
4827
|
-
const fileName = import_node_path.default.posix.basename(parsedUrl.pathname.replace(/\/$/, "")) || "downloaded";
|
|
4828
|
-
if (destinationPath.endsWith("/")) {
|
|
4829
|
-
destinationPath = import_node_path.default.posix.join(destinationPath.replace(/\/$/, ""), fileName);
|
|
4830
|
-
}
|
|
4831
|
-
const parentDir = import_node_path.default.posix.dirname(destinationPath) || "/";
|
|
4832
|
-
emit({
|
|
4833
|
-
type: "status",
|
|
4834
|
-
message: `ADD ${url} -> ${destinationPath}`
|
|
4835
|
-
});
|
|
4836
|
-
await runChecked(sandbox, "mkdir", ["-p", parentDir], processEnv);
|
|
4837
|
-
await runStreaming(
|
|
4838
|
-
sandbox,
|
|
4839
|
-
emit,
|
|
4840
|
-
sleep3,
|
|
4841
|
-
"sh",
|
|
4842
|
-
[
|
|
4843
|
-
"-c",
|
|
4844
|
-
`curl -fsSL --location ${shellQuote(url)} -o ${shellQuote(destinationPath)}`
|
|
4845
|
-
],
|
|
4846
|
-
processEnv,
|
|
4847
|
-
workingDir
|
|
4848
|
-
);
|
|
4849
|
-
}
|
|
4850
|
-
async function executeDockerfilePlan(sandbox, plan, emit, sleep3) {
|
|
4851
|
-
const processEnv = { ...BUILD_SANDBOX_PIP_ENV };
|
|
4852
|
-
let workingDir = "/";
|
|
4853
|
-
for (const instruction of plan.instructions) {
|
|
4854
|
-
const { keyword, value, lineNumber } = instruction;
|
|
4855
|
-
if (keyword === "RUN") {
|
|
4856
|
-
emit({ type: "status", message: `RUN ${value}` });
|
|
4857
|
-
await runStreaming(
|
|
4858
|
-
sandbox,
|
|
4859
|
-
emit,
|
|
4860
|
-
sleep3,
|
|
4861
|
-
"sh",
|
|
4862
|
-
["-c", value],
|
|
4863
|
-
processEnv,
|
|
4864
|
-
workingDir
|
|
4865
|
-
);
|
|
4866
|
-
continue;
|
|
4867
|
-
}
|
|
4868
|
-
if (keyword === "WORKDIR") {
|
|
4869
|
-
const tokens = shellSplit(value);
|
|
4870
|
-
if (tokens.length !== 1) {
|
|
4871
|
-
throw new Error(`line ${lineNumber}: WORKDIR must include exactly one path`);
|
|
4872
|
-
}
|
|
4873
|
-
workingDir = resolveContainerPath(tokens[0], workingDir);
|
|
4874
|
-
emit({ type: "status", message: `WORKDIR ${workingDir}` });
|
|
4875
|
-
await runChecked(sandbox, "mkdir", ["-p", workingDir], processEnv);
|
|
4876
|
-
continue;
|
|
4877
|
-
}
|
|
4878
|
-
if (keyword === "ENV") {
|
|
4879
|
-
for (const [key, envValue] of parseEnvPairs(value, lineNumber)) {
|
|
4880
|
-
emit({ type: "status", message: `ENV ${key}=${envValue}` });
|
|
4881
|
-
processEnv[key] = envValue;
|
|
4882
|
-
await persistEnvVar(sandbox, processEnv, key, envValue);
|
|
4883
|
-
}
|
|
4884
|
-
continue;
|
|
4885
|
-
}
|
|
4886
|
-
if (keyword === "COPY") {
|
|
4887
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
4888
|
-
value,
|
|
4889
|
-
lineNumber,
|
|
4890
|
-
keyword
|
|
4891
|
-
);
|
|
4892
|
-
await copyFromContext(
|
|
4893
|
-
sandbox,
|
|
4894
|
-
emit,
|
|
4895
|
-
plan.contextDir,
|
|
4896
|
-
sources,
|
|
4897
|
-
destination,
|
|
4898
|
-
workingDir,
|
|
4899
|
-
keyword
|
|
4900
|
-
);
|
|
4901
|
-
continue;
|
|
4902
|
-
}
|
|
4903
|
-
if (keyword === "ADD") {
|
|
4904
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
4905
|
-
value,
|
|
4906
|
-
lineNumber,
|
|
4907
|
-
keyword
|
|
4908
|
-
);
|
|
4909
|
-
if (sources.length === 1 && /^https?:\/\//.test(sources[0])) {
|
|
4910
|
-
await addUrlToSandbox(
|
|
4911
|
-
sandbox,
|
|
4912
|
-
emit,
|
|
4913
|
-
sources[0],
|
|
4914
|
-
destination,
|
|
4915
|
-
workingDir,
|
|
4916
|
-
processEnv,
|
|
4917
|
-
sleep3
|
|
4918
|
-
);
|
|
4919
|
-
} else {
|
|
4920
|
-
await copyFromContext(
|
|
4921
|
-
sandbox,
|
|
4922
|
-
emit,
|
|
4923
|
-
plan.contextDir,
|
|
4924
|
-
sources,
|
|
4925
|
-
destination,
|
|
4926
|
-
workingDir,
|
|
4927
|
-
keyword
|
|
4928
|
-
);
|
|
4929
|
-
}
|
|
4930
|
-
continue;
|
|
4931
|
-
}
|
|
4932
|
-
if (IGNORED_DOCKERFILE_INSTRUCTIONS.has(keyword)) {
|
|
4933
|
-
emit({
|
|
4934
|
-
type: "warning",
|
|
4935
|
-
message: `Skipping Dockerfile instruction '${keyword}' during snapshot materialization. It is still preserved in the registered Dockerfile.`
|
|
4936
|
-
});
|
|
4937
|
-
continue;
|
|
4938
|
-
}
|
|
4939
|
-
throw new Error(
|
|
4940
|
-
`line ${lineNumber}: Dockerfile instruction '${keyword}' is not supported for sandbox image creation`
|
|
4941
|
-
);
|
|
4942
|
-
}
|
|
4943
|
-
}
|
|
4944
|
-
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes, isPublic, snapshotFormatVersion) {
|
|
5014
|
+
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes2, isPublic, snapshotFormatVersion) {
|
|
4945
5015
|
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4946
5016
|
if (!bearerToken) {
|
|
4947
5017
|
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
@@ -4975,7 +5045,7 @@ async function registerImage(context, name, dockerfile, snapshotId, snapshotSand
|
|
|
4975
5045
|
snapshotUri,
|
|
4976
5046
|
...snapshotFormatVersion ? { snapshotFormatVersion } : {},
|
|
4977
5047
|
snapshotSizeBytes,
|
|
4978
|
-
rootfsDiskBytes,
|
|
5048
|
+
rootfsDiskBytes: rootfsDiskBytes2,
|
|
4979
5049
|
public: isPublic
|
|
4980
5050
|
})
|
|
4981
5051
|
});
|
|
@@ -4992,67 +5062,71 @@ async function createSandboxImage(source, options = {}, deps = {}) {
|
|
|
4992
5062
|
const sleep3 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
4993
5063
|
const context = buildContextFromEnv();
|
|
4994
5064
|
const clientFactory = deps.createClient ?? createDefaultClient;
|
|
4995
|
-
const register = deps.registerImage ?? ((...args) => registerImage(...args));
|
|
4996
5065
|
const sourceLabel = typeof source === "string" ? source : `Image(${source.name})`;
|
|
4997
5066
|
emit({ type: "status", message: `Loading ${sourceLabel}...` });
|
|
4998
5067
|
const plan = typeof source === "string" ? await loadDockerfilePlan(source, options.registeredName) : loadImagePlan(source, options);
|
|
4999
5068
|
emit({
|
|
5000
5069
|
type: "status",
|
|
5001
|
-
message:
|
|
5070
|
+
message: `Selected image name: ${plan.registeredName}`
|
|
5071
|
+
});
|
|
5072
|
+
emit({ type: "status", message: "Preparing rootfs build..." });
|
|
5073
|
+
const resolvedContext = await resolveBuildContext(context);
|
|
5074
|
+
const { prepared, spec: preparedSpec } = await prepareRootfsBuild(
|
|
5075
|
+
resolvedContext,
|
|
5076
|
+
plan,
|
|
5077
|
+
options.isPublic ?? false
|
|
5078
|
+
);
|
|
5079
|
+
emit({
|
|
5080
|
+
type: "status",
|
|
5081
|
+
message: prepared.rootfsNodeKind === "diff" ? "Build mode: RootfsDiff" : "Build mode: RootfsBase"
|
|
5002
5082
|
});
|
|
5003
5083
|
const client = clientFactory(context);
|
|
5004
5084
|
let sandbox;
|
|
5005
5085
|
try {
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
});
|
|
5086
|
+
const outputRootfsDiskBytes = rootfsDiskBytes(options.diskMb, prepared);
|
|
5087
|
+
const builderDiskMb = Math.max(
|
|
5088
|
+
rootfsDiskBytesToMb(outputRootfsDiskBytes),
|
|
5089
|
+
options.builderDiskMb ?? prepared.builder.diskMb
|
|
5090
|
+
);
|
|
5012
5091
|
emit({
|
|
5013
5092
|
type: "status",
|
|
5014
|
-
message: `
|
|
5093
|
+
message: `Creating rootfs builder sandbox from ${prepared.builder.image}...`
|
|
5015
5094
|
});
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5095
|
+
sandbox = await client.createAndConnect({
|
|
5096
|
+
image: prepared.builder.image,
|
|
5097
|
+
cpus: options.cpus ?? prepared.builder.cpus,
|
|
5098
|
+
memoryMb: options.memoryMb ?? prepared.builder.memoryMb,
|
|
5099
|
+
diskMb: builderDiskMb
|
|
5021
5100
|
});
|
|
5022
|
-
emit({
|
|
5023
|
-
type: "snapshot_created",
|
|
5024
|
-
snapshot_id: snapshot.snapshotId
|
|
5025
|
-
});
|
|
5026
|
-
if (!snapshot.snapshotUri) {
|
|
5027
|
-
throw new Error(
|
|
5028
|
-
`Snapshot ${snapshot.snapshotId} is missing snapshotUri and cannot be registered as a sandbox image.`
|
|
5029
|
-
);
|
|
5030
|
-
}
|
|
5031
|
-
if (snapshot.sizeBytes == null) {
|
|
5032
|
-
throw new Error(
|
|
5033
|
-
`Snapshot ${snapshot.snapshotId} is missing sizeBytes and cannot be registered as a sandbox image.`
|
|
5034
|
-
);
|
|
5035
|
-
}
|
|
5036
|
-
if (snapshot.rootfsDiskBytes == null) {
|
|
5037
|
-
throw new Error(
|
|
5038
|
-
`Snapshot ${snapshot.snapshotId} is missing rootfsDiskBytes and cannot be registered as a sandbox image.`
|
|
5039
|
-
);
|
|
5040
|
-
}
|
|
5041
5101
|
emit({
|
|
5042
5102
|
type: "status",
|
|
5043
|
-
message: `
|
|
5103
|
+
message: `Rootfs builder sandbox ${sandbox.sandboxId} is running`
|
|
5044
5104
|
});
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5105
|
+
emit({ type: "status", message: "Uploading build context..." });
|
|
5106
|
+
await copyLocalPathToSandbox(sandbox, plan.contextDir, REMOTE_CONTEXT_DIR);
|
|
5107
|
+
const spec = await buildRootfsSpec(
|
|
5108
|
+
preparedSpec,
|
|
5109
|
+
prepared,
|
|
5110
|
+
plan,
|
|
5111
|
+
options.diskMb
|
|
5112
|
+
);
|
|
5113
|
+
await runChecked(sandbox, "mkdir", ["-p", import_node_path.default.posix.dirname(REMOTE_SPEC_PATH)]);
|
|
5114
|
+
await sandbox.writeFile(
|
|
5115
|
+
REMOTE_SPEC_PATH,
|
|
5116
|
+
new TextEncoder().encode(JSON.stringify(spec, null, 2))
|
|
5117
|
+
);
|
|
5118
|
+
emit({ type: "status", message: "Running offline rootfs builder..." });
|
|
5119
|
+
await runRootfsBuilder(sandbox, prepared.builder.command, emit, sleep3);
|
|
5120
|
+
const metadataBytes = await sandbox.readFile(REMOTE_METADATA_PATH);
|
|
5121
|
+
const metadata = JSON.parse(
|
|
5122
|
+
new TextDecoder().decode(metadataBytes)
|
|
5123
|
+
);
|
|
5124
|
+
const completeRequest = completeRequestFromMetadata(prepared, metadata);
|
|
5125
|
+
emit({ type: "status", message: "Completing image registration..." });
|
|
5126
|
+
const result = await completeRootfsBuild(
|
|
5127
|
+
resolvedContext,
|
|
5128
|
+
prepared.buildId,
|
|
5129
|
+
completeRequest
|
|
5056
5130
|
);
|
|
5057
5131
|
emit({
|
|
5058
5132
|
type: "image_registered",
|
|
@@ -5080,16 +5154,18 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5080
5154
|
cpus: { type: "string" },
|
|
5081
5155
|
memory: { type: "string" },
|
|
5082
5156
|
disk_mb: { type: "string" },
|
|
5157
|
+
builder_disk_mb: { type: "string" },
|
|
5083
5158
|
public: { type: "boolean", default: false }
|
|
5084
5159
|
}
|
|
5085
5160
|
});
|
|
5086
5161
|
const dockerfilePath = parsed.positionals[0];
|
|
5087
5162
|
if (!dockerfilePath) {
|
|
5088
|
-
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--public]");
|
|
5163
|
+
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--builder_disk_mb MB] [--public]");
|
|
5089
5164
|
}
|
|
5090
5165
|
const cpus = parsed.values.cpus != null ? Number(parsed.values.cpus) : void 0;
|
|
5091
5166
|
const memoryMb = parsed.values.memory != null ? Number(parsed.values.memory) : void 0;
|
|
5092
5167
|
const diskMb = parsed.values.disk_mb != null ? Number(parsed.values.disk_mb) : void 0;
|
|
5168
|
+
const builderDiskMb = parsed.values.builder_disk_mb != null ? Number(parsed.values.builder_disk_mb) : void 0;
|
|
5093
5169
|
if (cpus != null && !Number.isFinite(cpus)) {
|
|
5094
5170
|
throw new Error(`Invalid --cpus value: ${parsed.values.cpus}`);
|
|
5095
5171
|
}
|
|
@@ -5099,6 +5175,11 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5099
5175
|
if (diskMb != null && !Number.isInteger(diskMb)) {
|
|
5100
5176
|
throw new Error(`Invalid --disk_mb value: ${parsed.values.disk_mb}`);
|
|
5101
5177
|
}
|
|
5178
|
+
if (builderDiskMb != null && !Number.isInteger(builderDiskMb)) {
|
|
5179
|
+
throw new Error(
|
|
5180
|
+
`Invalid --builder_disk_mb value: ${parsed.values.builder_disk_mb}`
|
|
5181
|
+
);
|
|
5182
|
+
}
|
|
5102
5183
|
await createSandboxImage(
|
|
5103
5184
|
dockerfilePath,
|
|
5104
5185
|
{
|
|
@@ -5106,30 +5187,30 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5106
5187
|
cpus,
|
|
5107
5188
|
memoryMb,
|
|
5108
5189
|
diskMb,
|
|
5190
|
+
builderDiskMb,
|
|
5109
5191
|
isPublic: parsed.values.public
|
|
5110
5192
|
},
|
|
5111
5193
|
{ emit: ndjsonStdoutEmit }
|
|
5112
5194
|
);
|
|
5113
5195
|
}
|
|
5114
|
-
var import_promises, import_node_path, import_node_util,
|
|
5196
|
+
var import_promises, import_node_os, import_node_path, import_node_util, DEFAULT_ROOTFS_DISK_MB, REMOTE_BUILD_DIR, REMOTE_CONTEXT_DIR, REMOTE_SPEC_PATH, REMOTE_METADATA_PATH, ROOTFS_BUILDER_BIN_DIR, ROOTFS_BUILDER_PATH, ROOTFS_BUILDER_COMMAND, UNSUPPORTED_DOCKERFILE_INSTRUCTIONS;
|
|
5115
5197
|
var init_sandbox_image = __esm({
|
|
5116
5198
|
"src/sandbox-image.ts"() {
|
|
5117
5199
|
import_promises = require("fs/promises");
|
|
5200
|
+
import_node_os = require("os");
|
|
5118
5201
|
import_node_path = __toESM(require("path"), 1);
|
|
5119
5202
|
import_node_util = require("util");
|
|
5120
5203
|
init_models();
|
|
5121
5204
|
init_client();
|
|
5122
5205
|
init_image();
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
"VOLUME"
|
|
5132
|
-
]);
|
|
5206
|
+
DEFAULT_ROOTFS_DISK_MB = 10 * 1024;
|
|
5207
|
+
REMOTE_BUILD_DIR = "/var/lib/tensorlake/rootfs-builder/build";
|
|
5208
|
+
REMOTE_CONTEXT_DIR = "/var/lib/tensorlake/rootfs-builder/build/context";
|
|
5209
|
+
REMOTE_SPEC_PATH = "/var/lib/tensorlake/rootfs-builder/build/spec.json";
|
|
5210
|
+
REMOTE_METADATA_PATH = "/var/lib/tensorlake/rootfs-builder/build/metadata.json";
|
|
5211
|
+
ROOTFS_BUILDER_BIN_DIR = "/usr/local/bin";
|
|
5212
|
+
ROOTFS_BUILDER_PATH = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
5213
|
+
ROOTFS_BUILDER_COMMAND = "tl-rootfs-build";
|
|
5133
5214
|
UNSUPPORTED_DOCKERFILE_INSTRUCTIONS = /* @__PURE__ */ new Set([
|
|
5134
5215
|
"ARG",
|
|
5135
5216
|
"ONBUILD",
|