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.js
CHANGED
|
@@ -61,7 +61,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
|
|
|
61
61
|
var init_defaults = __esm({
|
|
62
62
|
"src/defaults.ts"() {
|
|
63
63
|
"use strict";
|
|
64
|
-
SDK_VERSION = "0.5.
|
|
64
|
+
SDK_VERSION = "0.5.13";
|
|
65
65
|
API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
|
|
66
66
|
API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
|
|
67
67
|
NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
|
|
@@ -3893,6 +3893,46 @@ var init_client = __esm({
|
|
|
3893
3893
|
);
|
|
3894
3894
|
return Object.assign(sandboxes, { traceId: raw.traceId });
|
|
3895
3895
|
}
|
|
3896
|
+
/**
|
|
3897
|
+
* List archived (terminated) sandboxes in the namespace.
|
|
3898
|
+
*
|
|
3899
|
+
* Archived sandboxes are terminated sandboxes parked in the server's
|
|
3900
|
+
* archived sandboxes store until the server-configured TTL expires.
|
|
3901
|
+
*/
|
|
3902
|
+
async listArchived(options) {
|
|
3903
|
+
const query = [];
|
|
3904
|
+
if (options?.limit != null) {
|
|
3905
|
+
query.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
3906
|
+
}
|
|
3907
|
+
if (options?.cursor != null) {
|
|
3908
|
+
query.push(`cursor=${encodeURIComponent(options.cursor)}`);
|
|
3909
|
+
}
|
|
3910
|
+
if (options?.direction != null) {
|
|
3911
|
+
query.push(`direction=${encodeURIComponent(options.direction)}`);
|
|
3912
|
+
}
|
|
3913
|
+
const suffix = query.length ? `?${query.join("&")}` : "";
|
|
3914
|
+
const raw = await this.http.requestJson("GET", this.path(`archived-sandboxes${suffix}`));
|
|
3915
|
+
const sandboxes = (raw.sandboxes ?? []).map(
|
|
3916
|
+
(s) => fromSnakeKeys(s, "sandboxId")
|
|
3917
|
+
);
|
|
3918
|
+
const response = {
|
|
3919
|
+
sandboxes,
|
|
3920
|
+
prevCursor: raw.prev_cursor,
|
|
3921
|
+
nextCursor: raw.next_cursor
|
|
3922
|
+
};
|
|
3923
|
+
return Object.assign(response, { traceId: raw.traceId });
|
|
3924
|
+
}
|
|
3925
|
+
/** Get a single archived sandbox by id. */
|
|
3926
|
+
async getArchived(sandboxId) {
|
|
3927
|
+
const raw = await this.http.requestJson(
|
|
3928
|
+
"GET",
|
|
3929
|
+
this.path(`archived-sandboxes/${encodeURIComponent(sandboxId)}`)
|
|
3930
|
+
);
|
|
3931
|
+
return Object.assign(
|
|
3932
|
+
fromSnakeKeys(raw, "sandboxId"),
|
|
3933
|
+
{ traceId: raw.traceId }
|
|
3934
|
+
);
|
|
3935
|
+
}
|
|
3896
3936
|
/** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
|
|
3897
3937
|
async update(sandboxId, options) {
|
|
3898
3938
|
const body = {};
|
|
@@ -4293,6 +4333,7 @@ var init_image = __esm({
|
|
|
4293
4333
|
|
|
4294
4334
|
// src/sandbox-image.ts
|
|
4295
4335
|
import { readFile, readdir, stat } from "fs/promises";
|
|
4336
|
+
import { homedir } from "os";
|
|
4296
4337
|
import path from "path";
|
|
4297
4338
|
import { parseArgs } from "util";
|
|
4298
4339
|
function defaultRegisteredName(dockerfilePath) {
|
|
@@ -4423,12 +4464,6 @@ function shellSplit(input) {
|
|
|
4423
4464
|
}
|
|
4424
4465
|
return tokens;
|
|
4425
4466
|
}
|
|
4426
|
-
function shellQuote(value) {
|
|
4427
|
-
if (!value) {
|
|
4428
|
-
return "''";
|
|
4429
|
-
}
|
|
4430
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
4431
|
-
}
|
|
4432
4467
|
function stripLeadingFlags(value) {
|
|
4433
4468
|
const flags = {};
|
|
4434
4469
|
let remaining = value.trimStart();
|
|
@@ -4466,75 +4501,6 @@ function parseFromValue(value, lineNumber) {
|
|
|
4466
4501
|
}
|
|
4467
4502
|
return tokens[0];
|
|
4468
4503
|
}
|
|
4469
|
-
function parseCopyLikeValues(value, lineNumber, keyword) {
|
|
4470
|
-
const { flags, remaining } = stripLeadingFlags(value);
|
|
4471
|
-
if ("from" in flags) {
|
|
4472
|
-
throw new Error(
|
|
4473
|
-
`line ${lineNumber}: ${keyword} --from is not supported for sandbox image creation`
|
|
4474
|
-
);
|
|
4475
|
-
}
|
|
4476
|
-
const payload = remaining.trim();
|
|
4477
|
-
if (!payload) {
|
|
4478
|
-
throw new Error(
|
|
4479
|
-
`line ${lineNumber}: ${keyword} must include source and destination`
|
|
4480
|
-
);
|
|
4481
|
-
}
|
|
4482
|
-
let parts;
|
|
4483
|
-
if (payload.startsWith("[")) {
|
|
4484
|
-
let parsed;
|
|
4485
|
-
try {
|
|
4486
|
-
parsed = JSON.parse(payload);
|
|
4487
|
-
} catch (error) {
|
|
4488
|
-
throw new Error(
|
|
4489
|
-
`line ${lineNumber}: invalid JSON array syntax for ${keyword}: ${error.message}`
|
|
4490
|
-
);
|
|
4491
|
-
}
|
|
4492
|
-
if (!Array.isArray(parsed) || parsed.length < 2 || parsed.some((item) => typeof item !== "string")) {
|
|
4493
|
-
throw new Error(
|
|
4494
|
-
`line ${lineNumber}: ${keyword} JSON array form requires at least two string values`
|
|
4495
|
-
);
|
|
4496
|
-
}
|
|
4497
|
-
parts = parsed;
|
|
4498
|
-
} else {
|
|
4499
|
-
parts = shellSplit(payload);
|
|
4500
|
-
if (parts.length < 2) {
|
|
4501
|
-
throw new Error(
|
|
4502
|
-
`line ${lineNumber}: ${keyword} must include at least one source and one destination`
|
|
4503
|
-
);
|
|
4504
|
-
}
|
|
4505
|
-
}
|
|
4506
|
-
return {
|
|
4507
|
-
flags,
|
|
4508
|
-
sources: parts.slice(0, -1),
|
|
4509
|
-
destination: parts[parts.length - 1]
|
|
4510
|
-
};
|
|
4511
|
-
}
|
|
4512
|
-
function parseEnvPairs(value, lineNumber) {
|
|
4513
|
-
const tokens = shellSplit(value);
|
|
4514
|
-
if (tokens.length === 0) {
|
|
4515
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4516
|
-
}
|
|
4517
|
-
if (tokens.every((token) => token.includes("="))) {
|
|
4518
|
-
return tokens.map((token) => {
|
|
4519
|
-
const [key, envValue] = token.split(/=(.*)/s, 2);
|
|
4520
|
-
if (!key) {
|
|
4521
|
-
throw new Error(`line ${lineNumber}: invalid ENV token '${token}'`);
|
|
4522
|
-
}
|
|
4523
|
-
return [key, envValue];
|
|
4524
|
-
});
|
|
4525
|
-
}
|
|
4526
|
-
if (tokens.length < 2) {
|
|
4527
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4528
|
-
}
|
|
4529
|
-
return [[tokens[0], tokens.slice(1).join(" ")]];
|
|
4530
|
-
}
|
|
4531
|
-
function resolveContainerPath(containerPath, workingDir) {
|
|
4532
|
-
if (!containerPath) {
|
|
4533
|
-
return workingDir;
|
|
4534
|
-
}
|
|
4535
|
-
const normalized = containerPath.startsWith("/") ? path.posix.normalize(containerPath) : path.posix.normalize(path.posix.join(workingDir, containerPath));
|
|
4536
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
4537
|
-
}
|
|
4538
4504
|
function buildPlanFromDockerfileText(dockerfileText, dockerfilePath, contextDir, registeredName) {
|
|
4539
4505
|
let baseImage;
|
|
4540
4506
|
const instructions = [];
|
|
@@ -4645,14 +4611,292 @@ function buildContextFromEnv() {
|
|
|
4645
4611
|
};
|
|
4646
4612
|
}
|
|
4647
4613
|
function createDefaultClient(context) {
|
|
4614
|
+
const useScopeHeaders = context.personalAccessToken != null && context.apiKey == null;
|
|
4648
4615
|
return new SandboxClient({
|
|
4649
4616
|
apiUrl: context.apiUrl,
|
|
4650
4617
|
apiKey: context.apiKey ?? context.personalAccessToken,
|
|
4651
|
-
organizationId: context.organizationId,
|
|
4652
|
-
projectId: context.projectId,
|
|
4618
|
+
organizationId: useScopeHeaders ? context.organizationId : void 0,
|
|
4619
|
+
projectId: useScopeHeaders ? context.projectId : void 0,
|
|
4653
4620
|
namespace: context.namespace
|
|
4654
4621
|
});
|
|
4655
4622
|
}
|
|
4623
|
+
function baseApiUrl(context) {
|
|
4624
|
+
return context.apiUrl.replace(/\/+$/, "");
|
|
4625
|
+
}
|
|
4626
|
+
function scopedBuildsPath(context) {
|
|
4627
|
+
return `/platform/v1/organizations/${encodeURIComponent(context.organizationId)}/projects/${encodeURIComponent(context.projectId)}/sandbox-template-builds`;
|
|
4628
|
+
}
|
|
4629
|
+
function platformHeaders(context) {
|
|
4630
|
+
const headers = {
|
|
4631
|
+
Authorization: `Bearer ${context.bearerToken}`,
|
|
4632
|
+
"Content-Type": "application/json"
|
|
4633
|
+
};
|
|
4634
|
+
if (context.useScopeHeaders) {
|
|
4635
|
+
headers["X-Forwarded-Organization-Id"] = context.organizationId;
|
|
4636
|
+
headers["X-Forwarded-Project-Id"] = context.projectId;
|
|
4637
|
+
}
|
|
4638
|
+
return headers;
|
|
4639
|
+
}
|
|
4640
|
+
async function requestJson(url, init, errorPrefix) {
|
|
4641
|
+
const response = await fetch(url, init);
|
|
4642
|
+
if (!response.ok) {
|
|
4643
|
+
throw new Error(
|
|
4644
|
+
`${errorPrefix} (HTTP ${response.status}): ${await response.text()}`
|
|
4645
|
+
);
|
|
4646
|
+
}
|
|
4647
|
+
const text = await response.text();
|
|
4648
|
+
return text ? JSON.parse(text) : {};
|
|
4649
|
+
}
|
|
4650
|
+
async function resolveBuildContext(context) {
|
|
4651
|
+
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4652
|
+
if (!bearerToken) {
|
|
4653
|
+
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
4654
|
+
}
|
|
4655
|
+
if (context.apiKey) {
|
|
4656
|
+
const scope = await requestJson(
|
|
4657
|
+
`${baseApiUrl(context)}/platform/v1/keys/introspect`,
|
|
4658
|
+
{
|
|
4659
|
+
method: "POST",
|
|
4660
|
+
headers: {
|
|
4661
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
4662
|
+
"Content-Type": "application/json"
|
|
4663
|
+
}
|
|
4664
|
+
},
|
|
4665
|
+
"API key introspection failed"
|
|
4666
|
+
);
|
|
4667
|
+
if (!scope.organizationId || !scope.projectId) {
|
|
4668
|
+
throw new Error("API key introspection response is missing organizationId or projectId");
|
|
4669
|
+
}
|
|
4670
|
+
return {
|
|
4671
|
+
...context,
|
|
4672
|
+
bearerToken,
|
|
4673
|
+
organizationId: scope.organizationId,
|
|
4674
|
+
projectId: scope.projectId,
|
|
4675
|
+
useScopeHeaders: false
|
|
4676
|
+
};
|
|
4677
|
+
}
|
|
4678
|
+
if (!context.organizationId || !context.projectId) {
|
|
4679
|
+
throw new Error(
|
|
4680
|
+
"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."
|
|
4681
|
+
);
|
|
4682
|
+
}
|
|
4683
|
+
return {
|
|
4684
|
+
...context,
|
|
4685
|
+
bearerToken,
|
|
4686
|
+
organizationId: context.organizationId,
|
|
4687
|
+
projectId: context.projectId,
|
|
4688
|
+
useScopeHeaders: true
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4691
|
+
async function prepareRootfsBuild(context, plan, isPublic) {
|
|
4692
|
+
if (!plan.baseImage) {
|
|
4693
|
+
throw new Error("Sandbox image builds require a Dockerfile FROM image or Image baseImage");
|
|
4694
|
+
}
|
|
4695
|
+
const spec = await requestJson(
|
|
4696
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}`,
|
|
4697
|
+
{
|
|
4698
|
+
method: "POST",
|
|
4699
|
+
headers: platformHeaders(context),
|
|
4700
|
+
body: JSON.stringify({
|
|
4701
|
+
name: plan.registeredName,
|
|
4702
|
+
dockerfile: plan.dockerfileText,
|
|
4703
|
+
baseImage: plan.baseImage,
|
|
4704
|
+
public: isPublic
|
|
4705
|
+
})
|
|
4706
|
+
},
|
|
4707
|
+
"failed to prepare sandbox image build"
|
|
4708
|
+
);
|
|
4709
|
+
return { prepared: parsePreparedBuild(spec), spec };
|
|
4710
|
+
}
|
|
4711
|
+
function parsePreparedBuild(raw) {
|
|
4712
|
+
const builder = raw.builder;
|
|
4713
|
+
if (!builder) {
|
|
4714
|
+
throw new Error("platform API response is missing rootfs builder configuration");
|
|
4715
|
+
}
|
|
4716
|
+
const prepared = {
|
|
4717
|
+
...raw,
|
|
4718
|
+
buildId: requiredString(raw, "buildId"),
|
|
4719
|
+
snapshotId: requiredString(raw, "snapshotId"),
|
|
4720
|
+
snapshotUri: requiredString(raw, "snapshotUri"),
|
|
4721
|
+
rootfsNodeKind: requiredString(raw, "rootfsNodeKind"),
|
|
4722
|
+
builder: {
|
|
4723
|
+
image: requiredString(builder, "image"),
|
|
4724
|
+
command: requiredString(builder, "command"),
|
|
4725
|
+
cpus: requiredNumber(builder, "cpus"),
|
|
4726
|
+
memoryMb: requiredNumber(builder, "memoryMb"),
|
|
4727
|
+
diskMb: requiredNumber(builder, "diskMb")
|
|
4728
|
+
}
|
|
4729
|
+
};
|
|
4730
|
+
const parent = raw.parent;
|
|
4731
|
+
if (parent != null) {
|
|
4732
|
+
prepared.parent = {
|
|
4733
|
+
parentManifestUri: requiredString(parent, "parentManifestUri"),
|
|
4734
|
+
rootfsDiskBytes: optionalNumber(parent, "rootfsDiskBytes")
|
|
4735
|
+
};
|
|
4736
|
+
}
|
|
4737
|
+
return prepared;
|
|
4738
|
+
}
|
|
4739
|
+
async function completeRootfsBuild(context, buildId, request) {
|
|
4740
|
+
return requestJson(
|
|
4741
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}/${encodeURIComponent(buildId)}/complete`,
|
|
4742
|
+
{
|
|
4743
|
+
method: "POST",
|
|
4744
|
+
headers: platformHeaders(context),
|
|
4745
|
+
body: JSON.stringify(request)
|
|
4746
|
+
},
|
|
4747
|
+
"failed to complete sandbox image build"
|
|
4748
|
+
);
|
|
4749
|
+
}
|
|
4750
|
+
async function resolvedDockerConfigJson() {
|
|
4751
|
+
const configDir = process.env.DOCKER_CONFIG ?? path.join(homedir(), ".docker");
|
|
4752
|
+
const configPath = path.join(configDir, "config.json");
|
|
4753
|
+
try {
|
|
4754
|
+
const content = await readFile(configPath, "utf8");
|
|
4755
|
+
const parsed = JSON.parse(content);
|
|
4756
|
+
const auths = parsed.auths;
|
|
4757
|
+
if (auths != null && Object.keys(auths).length > 0) {
|
|
4758
|
+
return JSON.stringify({ auths });
|
|
4759
|
+
}
|
|
4760
|
+
} catch (error) {
|
|
4761
|
+
if (error.code === "ENOENT") {
|
|
4762
|
+
return void 0;
|
|
4763
|
+
}
|
|
4764
|
+
throw error;
|
|
4765
|
+
}
|
|
4766
|
+
return void 0;
|
|
4767
|
+
}
|
|
4768
|
+
function rootfsDiskBytes(diskMb, prepared) {
|
|
4769
|
+
if (diskMb != null) {
|
|
4770
|
+
return diskMb * 1024 * 1024;
|
|
4771
|
+
}
|
|
4772
|
+
if (prepared.parent != null) {
|
|
4773
|
+
if (prepared.parent.rootfsDiskBytes == null) {
|
|
4774
|
+
throw new Error(
|
|
4775
|
+
"platform API did not return parent rootfsDiskBytes for diff build; pass diskMb explicitly or update Platform API"
|
|
4776
|
+
);
|
|
4777
|
+
}
|
|
4778
|
+
return prepared.parent.rootfsDiskBytes;
|
|
4779
|
+
}
|
|
4780
|
+
return DEFAULT_ROOTFS_DISK_MB * 1024 * 1024;
|
|
4781
|
+
}
|
|
4782
|
+
function rootfsDiskBytesToMb(bytes) {
|
|
4783
|
+
return Math.ceil(bytes / (1024 * 1024));
|
|
4784
|
+
}
|
|
4785
|
+
async function buildRootfsSpec(preparedSpec, prepared, plan, diskMb) {
|
|
4786
|
+
const spec = {
|
|
4787
|
+
...preparedSpec,
|
|
4788
|
+
dockerfile: plan.dockerfileText,
|
|
4789
|
+
contextDir: REMOTE_CONTEXT_DIR,
|
|
4790
|
+
baseImage: plan.baseImage,
|
|
4791
|
+
rootfsDiskBytes: rootfsDiskBytes(diskMb, prepared)
|
|
4792
|
+
};
|
|
4793
|
+
const dockerConfigJson = await resolvedDockerConfigJson();
|
|
4794
|
+
if (dockerConfigJson != null) {
|
|
4795
|
+
spec.dockerConfigJson = dockerConfigJson;
|
|
4796
|
+
}
|
|
4797
|
+
return spec;
|
|
4798
|
+
}
|
|
4799
|
+
function rootfsBuilderExecutable(executable) {
|
|
4800
|
+
return executable === ROOTFS_BUILDER_COMMAND ? `${ROOTFS_BUILDER_BIN_DIR}/${ROOTFS_BUILDER_COMMAND}` : executable;
|
|
4801
|
+
}
|
|
4802
|
+
function rootfsBuilderEnv() {
|
|
4803
|
+
return { PATH: ROOTFS_BUILDER_PATH };
|
|
4804
|
+
}
|
|
4805
|
+
async function runRootfsBuilder(sandbox, command, emit, sleep3) {
|
|
4806
|
+
const parts = shellSplit(command);
|
|
4807
|
+
const [executable, ...commandArgs] = parts;
|
|
4808
|
+
if (!executable) {
|
|
4809
|
+
throw new Error("empty rootfs builder command returned by platform API");
|
|
4810
|
+
}
|
|
4811
|
+
await runStreaming(
|
|
4812
|
+
sandbox,
|
|
4813
|
+
emit,
|
|
4814
|
+
sleep3,
|
|
4815
|
+
rootfsBuilderExecutable(executable),
|
|
4816
|
+
[...commandArgs, "--spec", REMOTE_SPEC_PATH, "--metadata-out", REMOTE_METADATA_PATH],
|
|
4817
|
+
rootfsBuilderEnv(),
|
|
4818
|
+
REMOTE_BUILD_DIR
|
|
4819
|
+
);
|
|
4820
|
+
}
|
|
4821
|
+
function metadataString(metadata, snakeKey, camelKey) {
|
|
4822
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4823
|
+
return typeof value === "string" ? value : void 0;
|
|
4824
|
+
}
|
|
4825
|
+
function metadataNumber(metadata, snakeKey, camelKey) {
|
|
4826
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4827
|
+
if (typeof value === "number") {
|
|
4828
|
+
return value;
|
|
4829
|
+
}
|
|
4830
|
+
if (typeof value === "string" && value.trim()) {
|
|
4831
|
+
const parsed = Number(value);
|
|
4832
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
4833
|
+
}
|
|
4834
|
+
return void 0;
|
|
4835
|
+
}
|
|
4836
|
+
function completeRequestFromMetadata(prepared, metadata) {
|
|
4837
|
+
const rootfsNodeKind = metadataString(metadata, "rootfs_node_kind", "rootfsNodeKind") ?? prepared.rootfsNodeKind;
|
|
4838
|
+
const parentManifestUri = metadataString(metadata, "parent_manifest_uri", "parentManifestUri") ?? (rootfsNodeKind === "diff" ? prepared.parent?.parentManifestUri : void 0);
|
|
4839
|
+
if (rootfsNodeKind === "diff" && parentManifestUri == null) {
|
|
4840
|
+
throw new Error("rootfs diff build completed without parent_manifest_uri");
|
|
4841
|
+
}
|
|
4842
|
+
const snapshotFormatVersion = metadataString(
|
|
4843
|
+
metadata,
|
|
4844
|
+
"snapshot_format_version",
|
|
4845
|
+
"snapshotFormatVersion"
|
|
4846
|
+
);
|
|
4847
|
+
const snapshotSizeBytes = metadataNumber(
|
|
4848
|
+
metadata,
|
|
4849
|
+
"snapshot_size_bytes",
|
|
4850
|
+
"snapshotSizeBytes"
|
|
4851
|
+
);
|
|
4852
|
+
const rootfsDiskBytesValue = metadataNumber(
|
|
4853
|
+
metadata,
|
|
4854
|
+
"rootfs_disk_bytes",
|
|
4855
|
+
"rootfsDiskBytes"
|
|
4856
|
+
);
|
|
4857
|
+
if (!snapshotFormatVersion) {
|
|
4858
|
+
throw new Error("rootfs builder metadata is missing snapshot_format_version");
|
|
4859
|
+
}
|
|
4860
|
+
if (snapshotSizeBytes == null) {
|
|
4861
|
+
throw new Error("rootfs builder metadata is missing numeric snapshot_size_bytes");
|
|
4862
|
+
}
|
|
4863
|
+
if (rootfsDiskBytesValue == null) {
|
|
4864
|
+
throw new Error("rootfs builder metadata is missing numeric rootfs_disk_bytes");
|
|
4865
|
+
}
|
|
4866
|
+
return {
|
|
4867
|
+
snapshotId: metadataString(metadata, "snapshot_id", "snapshotId") ?? prepared.snapshotId,
|
|
4868
|
+
snapshotUri: metadataString(metadata, "snapshot_uri", "snapshotUri") ?? prepared.snapshotUri,
|
|
4869
|
+
snapshotFormatVersion,
|
|
4870
|
+
snapshotSizeBytes,
|
|
4871
|
+
rootfsDiskBytes: rootfsDiskBytesValue,
|
|
4872
|
+
rootfsNodeKind,
|
|
4873
|
+
...parentManifestUri ? { parentManifestUri } : {}
|
|
4874
|
+
};
|
|
4875
|
+
}
|
|
4876
|
+
function requiredString(object, key) {
|
|
4877
|
+
const value = object[key];
|
|
4878
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
4879
|
+
throw new Error(`expected '${key}' to be a non-empty string`);
|
|
4880
|
+
}
|
|
4881
|
+
return value;
|
|
4882
|
+
}
|
|
4883
|
+
function requiredNumber(object, key) {
|
|
4884
|
+
const value = object[key];
|
|
4885
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4886
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
4887
|
+
}
|
|
4888
|
+
return value;
|
|
4889
|
+
}
|
|
4890
|
+
function optionalNumber(object, key) {
|
|
4891
|
+
const value = object[key];
|
|
4892
|
+
if (value == null) {
|
|
4893
|
+
return void 0;
|
|
4894
|
+
}
|
|
4895
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
4896
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
4897
|
+
}
|
|
4898
|
+
return value;
|
|
4899
|
+
}
|
|
4656
4900
|
async function runChecked(sandbox, command, args, env, workingDir) {
|
|
4657
4901
|
const result = await sandbox.run(command, {
|
|
4658
4902
|
args,
|
|
@@ -4712,18 +4956,6 @@ function emitOutputLines(emit, stream, response, seen) {
|
|
|
4712
4956
|
emit({ type: "build_log", stream, message: line });
|
|
4713
4957
|
}
|
|
4714
4958
|
}
|
|
4715
|
-
function isPathWithinContext(contextDir, localPath) {
|
|
4716
|
-
const relative = path.relative(contextDir, localPath);
|
|
4717
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
4718
|
-
}
|
|
4719
|
-
function resolveContextSourcePath(contextDir, source) {
|
|
4720
|
-
const resolvedContextDir = path.resolve(contextDir);
|
|
4721
|
-
const resolvedSource = path.resolve(resolvedContextDir, source);
|
|
4722
|
-
if (!isPathWithinContext(resolvedContextDir, resolvedSource)) {
|
|
4723
|
-
throw new Error(`Local path escapes the build context: ${source}`);
|
|
4724
|
-
}
|
|
4725
|
-
return resolvedSource;
|
|
4726
|
-
}
|
|
4727
4959
|
async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
4728
4960
|
const fileStats = await stat(localPath).catch(() => null);
|
|
4729
4961
|
if (!fileStats) {
|
|
@@ -4754,168 +4986,7 @@ async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
|
4754
4986
|
}
|
|
4755
4987
|
}
|
|
4756
4988
|
}
|
|
4757
|
-
async function
|
|
4758
|
-
const exportLine = `export ${key}=${shellQuote(value)}`;
|
|
4759
|
-
await runChecked(
|
|
4760
|
-
sandbox,
|
|
4761
|
-
"sh",
|
|
4762
|
-
["-c", `printf '%s\\n' ${shellQuote(exportLine)} >> /etc/environment`],
|
|
4763
|
-
processEnv
|
|
4764
|
-
);
|
|
4765
|
-
}
|
|
4766
|
-
async function copyFromContext(sandbox, emit, contextDir, sources, destination, workingDir, keyword) {
|
|
4767
|
-
const destinationPath = resolveContainerPath(destination, workingDir);
|
|
4768
|
-
if (sources.length > 1 && !destinationPath.endsWith("/")) {
|
|
4769
|
-
throw new Error(
|
|
4770
|
-
`${keyword} with multiple sources requires a directory destination ending in '/'`
|
|
4771
|
-
);
|
|
4772
|
-
}
|
|
4773
|
-
for (const source of sources) {
|
|
4774
|
-
const localSource = resolveContextSourcePath(contextDir, source);
|
|
4775
|
-
const localStats = await stat(localSource).catch(() => null);
|
|
4776
|
-
if (!localStats) {
|
|
4777
|
-
throw new Error(`Local path not found: ${localSource}`);
|
|
4778
|
-
}
|
|
4779
|
-
let remoteDestination = destinationPath;
|
|
4780
|
-
if (sources.length > 1) {
|
|
4781
|
-
remoteDestination = path.posix.join(
|
|
4782
|
-
destinationPath.replace(/\/$/, ""),
|
|
4783
|
-
path.posix.basename(source.replace(/\/$/, ""))
|
|
4784
|
-
);
|
|
4785
|
-
} else if (localStats.isFile() && destinationPath.endsWith("/")) {
|
|
4786
|
-
remoteDestination = path.posix.join(
|
|
4787
|
-
destinationPath.replace(/\/$/, ""),
|
|
4788
|
-
path.basename(source)
|
|
4789
|
-
);
|
|
4790
|
-
}
|
|
4791
|
-
emit({
|
|
4792
|
-
type: "status",
|
|
4793
|
-
message: `${keyword} ${source} -> ${remoteDestination}`
|
|
4794
|
-
});
|
|
4795
|
-
await copyLocalPathToSandbox(sandbox, localSource, remoteDestination);
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
async function addUrlToSandbox(sandbox, emit, url, destination, workingDir, processEnv, sleep3) {
|
|
4799
|
-
let destinationPath = resolveContainerPath(destination, workingDir);
|
|
4800
|
-
const parsedUrl = new URL(url);
|
|
4801
|
-
const fileName = path.posix.basename(parsedUrl.pathname.replace(/\/$/, "")) || "downloaded";
|
|
4802
|
-
if (destinationPath.endsWith("/")) {
|
|
4803
|
-
destinationPath = path.posix.join(destinationPath.replace(/\/$/, ""), fileName);
|
|
4804
|
-
}
|
|
4805
|
-
const parentDir = path.posix.dirname(destinationPath) || "/";
|
|
4806
|
-
emit({
|
|
4807
|
-
type: "status",
|
|
4808
|
-
message: `ADD ${url} -> ${destinationPath}`
|
|
4809
|
-
});
|
|
4810
|
-
await runChecked(sandbox, "mkdir", ["-p", parentDir], processEnv);
|
|
4811
|
-
await runStreaming(
|
|
4812
|
-
sandbox,
|
|
4813
|
-
emit,
|
|
4814
|
-
sleep3,
|
|
4815
|
-
"sh",
|
|
4816
|
-
[
|
|
4817
|
-
"-c",
|
|
4818
|
-
`curl -fsSL --location ${shellQuote(url)} -o ${shellQuote(destinationPath)}`
|
|
4819
|
-
],
|
|
4820
|
-
processEnv,
|
|
4821
|
-
workingDir
|
|
4822
|
-
);
|
|
4823
|
-
}
|
|
4824
|
-
async function executeDockerfilePlan(sandbox, plan, emit, sleep3) {
|
|
4825
|
-
const processEnv = { ...BUILD_SANDBOX_PIP_ENV };
|
|
4826
|
-
let workingDir = "/";
|
|
4827
|
-
for (const instruction of plan.instructions) {
|
|
4828
|
-
const { keyword, value, lineNumber } = instruction;
|
|
4829
|
-
if (keyword === "RUN") {
|
|
4830
|
-
emit({ type: "status", message: `RUN ${value}` });
|
|
4831
|
-
await runStreaming(
|
|
4832
|
-
sandbox,
|
|
4833
|
-
emit,
|
|
4834
|
-
sleep3,
|
|
4835
|
-
"sh",
|
|
4836
|
-
["-c", value],
|
|
4837
|
-
processEnv,
|
|
4838
|
-
workingDir
|
|
4839
|
-
);
|
|
4840
|
-
continue;
|
|
4841
|
-
}
|
|
4842
|
-
if (keyword === "WORKDIR") {
|
|
4843
|
-
const tokens = shellSplit(value);
|
|
4844
|
-
if (tokens.length !== 1) {
|
|
4845
|
-
throw new Error(`line ${lineNumber}: WORKDIR must include exactly one path`);
|
|
4846
|
-
}
|
|
4847
|
-
workingDir = resolveContainerPath(tokens[0], workingDir);
|
|
4848
|
-
emit({ type: "status", message: `WORKDIR ${workingDir}` });
|
|
4849
|
-
await runChecked(sandbox, "mkdir", ["-p", workingDir], processEnv);
|
|
4850
|
-
continue;
|
|
4851
|
-
}
|
|
4852
|
-
if (keyword === "ENV") {
|
|
4853
|
-
for (const [key, envValue] of parseEnvPairs(value, lineNumber)) {
|
|
4854
|
-
emit({ type: "status", message: `ENV ${key}=${envValue}` });
|
|
4855
|
-
processEnv[key] = envValue;
|
|
4856
|
-
await persistEnvVar(sandbox, processEnv, key, envValue);
|
|
4857
|
-
}
|
|
4858
|
-
continue;
|
|
4859
|
-
}
|
|
4860
|
-
if (keyword === "COPY") {
|
|
4861
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
4862
|
-
value,
|
|
4863
|
-
lineNumber,
|
|
4864
|
-
keyword
|
|
4865
|
-
);
|
|
4866
|
-
await copyFromContext(
|
|
4867
|
-
sandbox,
|
|
4868
|
-
emit,
|
|
4869
|
-
plan.contextDir,
|
|
4870
|
-
sources,
|
|
4871
|
-
destination,
|
|
4872
|
-
workingDir,
|
|
4873
|
-
keyword
|
|
4874
|
-
);
|
|
4875
|
-
continue;
|
|
4876
|
-
}
|
|
4877
|
-
if (keyword === "ADD") {
|
|
4878
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
4879
|
-
value,
|
|
4880
|
-
lineNumber,
|
|
4881
|
-
keyword
|
|
4882
|
-
);
|
|
4883
|
-
if (sources.length === 1 && /^https?:\/\//.test(sources[0])) {
|
|
4884
|
-
await addUrlToSandbox(
|
|
4885
|
-
sandbox,
|
|
4886
|
-
emit,
|
|
4887
|
-
sources[0],
|
|
4888
|
-
destination,
|
|
4889
|
-
workingDir,
|
|
4890
|
-
processEnv,
|
|
4891
|
-
sleep3
|
|
4892
|
-
);
|
|
4893
|
-
} else {
|
|
4894
|
-
await copyFromContext(
|
|
4895
|
-
sandbox,
|
|
4896
|
-
emit,
|
|
4897
|
-
plan.contextDir,
|
|
4898
|
-
sources,
|
|
4899
|
-
destination,
|
|
4900
|
-
workingDir,
|
|
4901
|
-
keyword
|
|
4902
|
-
);
|
|
4903
|
-
}
|
|
4904
|
-
continue;
|
|
4905
|
-
}
|
|
4906
|
-
if (IGNORED_DOCKERFILE_INSTRUCTIONS.has(keyword)) {
|
|
4907
|
-
emit({
|
|
4908
|
-
type: "warning",
|
|
4909
|
-
message: `Skipping Dockerfile instruction '${keyword}' during snapshot materialization. It is still preserved in the registered Dockerfile.`
|
|
4910
|
-
});
|
|
4911
|
-
continue;
|
|
4912
|
-
}
|
|
4913
|
-
throw new Error(
|
|
4914
|
-
`line ${lineNumber}: Dockerfile instruction '${keyword}' is not supported for sandbox image creation`
|
|
4915
|
-
);
|
|
4916
|
-
}
|
|
4917
|
-
}
|
|
4918
|
-
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes, isPublic, snapshotFormatVersion) {
|
|
4989
|
+
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes2, isPublic, snapshotFormatVersion) {
|
|
4919
4990
|
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4920
4991
|
if (!bearerToken) {
|
|
4921
4992
|
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
@@ -4949,7 +5020,7 @@ async function registerImage(context, name, dockerfile, snapshotId, snapshotSand
|
|
|
4949
5020
|
snapshotUri,
|
|
4950
5021
|
...snapshotFormatVersion ? { snapshotFormatVersion } : {},
|
|
4951
5022
|
snapshotSizeBytes,
|
|
4952
|
-
rootfsDiskBytes,
|
|
5023
|
+
rootfsDiskBytes: rootfsDiskBytes2,
|
|
4953
5024
|
public: isPublic
|
|
4954
5025
|
})
|
|
4955
5026
|
});
|
|
@@ -4966,67 +5037,71 @@ async function createSandboxImage(source, options = {}, deps = {}) {
|
|
|
4966
5037
|
const sleep3 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
4967
5038
|
const context = buildContextFromEnv();
|
|
4968
5039
|
const clientFactory = deps.createClient ?? createDefaultClient;
|
|
4969
|
-
const register = deps.registerImage ?? ((...args) => registerImage(...args));
|
|
4970
5040
|
const sourceLabel = typeof source === "string" ? source : `Image(${source.name})`;
|
|
4971
5041
|
emit({ type: "status", message: `Loading ${sourceLabel}...` });
|
|
4972
5042
|
const plan = typeof source === "string" ? await loadDockerfilePlan(source, options.registeredName) : loadImagePlan(source, options);
|
|
4973
5043
|
emit({
|
|
4974
5044
|
type: "status",
|
|
4975
|
-
message:
|
|
5045
|
+
message: `Selected image name: ${plan.registeredName}`
|
|
5046
|
+
});
|
|
5047
|
+
emit({ type: "status", message: "Preparing rootfs build..." });
|
|
5048
|
+
const resolvedContext = await resolveBuildContext(context);
|
|
5049
|
+
const { prepared, spec: preparedSpec } = await prepareRootfsBuild(
|
|
5050
|
+
resolvedContext,
|
|
5051
|
+
plan,
|
|
5052
|
+
options.isPublic ?? false
|
|
5053
|
+
);
|
|
5054
|
+
emit({
|
|
5055
|
+
type: "status",
|
|
5056
|
+
message: prepared.rootfsNodeKind === "diff" ? "Build mode: RootfsDiff" : "Build mode: RootfsBase"
|
|
4976
5057
|
});
|
|
4977
5058
|
const client = clientFactory(context);
|
|
4978
5059
|
let sandbox;
|
|
4979
5060
|
try {
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
});
|
|
5061
|
+
const outputRootfsDiskBytes = rootfsDiskBytes(options.diskMb, prepared);
|
|
5062
|
+
const builderDiskMb = Math.max(
|
|
5063
|
+
rootfsDiskBytesToMb(outputRootfsDiskBytes),
|
|
5064
|
+
options.builderDiskMb ?? prepared.builder.diskMb
|
|
5065
|
+
);
|
|
4986
5066
|
emit({
|
|
4987
5067
|
type: "status",
|
|
4988
|
-
message: `
|
|
5068
|
+
message: `Creating rootfs builder sandbox from ${prepared.builder.image}...`
|
|
4989
5069
|
});
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
5070
|
+
sandbox = await client.createAndConnect({
|
|
5071
|
+
image: prepared.builder.image,
|
|
5072
|
+
cpus: options.cpus ?? prepared.builder.cpus,
|
|
5073
|
+
memoryMb: options.memoryMb ?? prepared.builder.memoryMb,
|
|
5074
|
+
diskMb: builderDiskMb
|
|
4995
5075
|
});
|
|
4996
|
-
emit({
|
|
4997
|
-
type: "snapshot_created",
|
|
4998
|
-
snapshot_id: snapshot.snapshotId
|
|
4999
|
-
});
|
|
5000
|
-
if (!snapshot.snapshotUri) {
|
|
5001
|
-
throw new Error(
|
|
5002
|
-
`Snapshot ${snapshot.snapshotId} is missing snapshotUri and cannot be registered as a sandbox image.`
|
|
5003
|
-
);
|
|
5004
|
-
}
|
|
5005
|
-
if (snapshot.sizeBytes == null) {
|
|
5006
|
-
throw new Error(
|
|
5007
|
-
`Snapshot ${snapshot.snapshotId} is missing sizeBytes and cannot be registered as a sandbox image.`
|
|
5008
|
-
);
|
|
5009
|
-
}
|
|
5010
|
-
if (snapshot.rootfsDiskBytes == null) {
|
|
5011
|
-
throw new Error(
|
|
5012
|
-
`Snapshot ${snapshot.snapshotId} is missing rootfsDiskBytes and cannot be registered as a sandbox image.`
|
|
5013
|
-
);
|
|
5014
|
-
}
|
|
5015
5076
|
emit({
|
|
5016
5077
|
type: "status",
|
|
5017
|
-
message: `
|
|
5078
|
+
message: `Rootfs builder sandbox ${sandbox.sandboxId} is running`
|
|
5018
5079
|
});
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5080
|
+
emit({ type: "status", message: "Uploading build context..." });
|
|
5081
|
+
await copyLocalPathToSandbox(sandbox, plan.contextDir, REMOTE_CONTEXT_DIR);
|
|
5082
|
+
const spec = await buildRootfsSpec(
|
|
5083
|
+
preparedSpec,
|
|
5084
|
+
prepared,
|
|
5085
|
+
plan,
|
|
5086
|
+
options.diskMb
|
|
5087
|
+
);
|
|
5088
|
+
await runChecked(sandbox, "mkdir", ["-p", path.posix.dirname(REMOTE_SPEC_PATH)]);
|
|
5089
|
+
await sandbox.writeFile(
|
|
5090
|
+
REMOTE_SPEC_PATH,
|
|
5091
|
+
new TextEncoder().encode(JSON.stringify(spec, null, 2))
|
|
5092
|
+
);
|
|
5093
|
+
emit({ type: "status", message: "Running offline rootfs builder..." });
|
|
5094
|
+
await runRootfsBuilder(sandbox, prepared.builder.command, emit, sleep3);
|
|
5095
|
+
const metadataBytes = await sandbox.readFile(REMOTE_METADATA_PATH);
|
|
5096
|
+
const metadata = JSON.parse(
|
|
5097
|
+
new TextDecoder().decode(metadataBytes)
|
|
5098
|
+
);
|
|
5099
|
+
const completeRequest = completeRequestFromMetadata(prepared, metadata);
|
|
5100
|
+
emit({ type: "status", message: "Completing image registration..." });
|
|
5101
|
+
const result = await completeRootfsBuild(
|
|
5102
|
+
resolvedContext,
|
|
5103
|
+
prepared.buildId,
|
|
5104
|
+
completeRequest
|
|
5030
5105
|
);
|
|
5031
5106
|
emit({
|
|
5032
5107
|
type: "image_registered",
|
|
@@ -5054,16 +5129,18 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5054
5129
|
cpus: { type: "string" },
|
|
5055
5130
|
memory: { type: "string" },
|
|
5056
5131
|
disk_mb: { type: "string" },
|
|
5132
|
+
builder_disk_mb: { type: "string" },
|
|
5057
5133
|
public: { type: "boolean", default: false }
|
|
5058
5134
|
}
|
|
5059
5135
|
});
|
|
5060
5136
|
const dockerfilePath = parsed.positionals[0];
|
|
5061
5137
|
if (!dockerfilePath) {
|
|
5062
|
-
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--public]");
|
|
5138
|
+
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--builder_disk_mb MB] [--public]");
|
|
5063
5139
|
}
|
|
5064
5140
|
const cpus = parsed.values.cpus != null ? Number(parsed.values.cpus) : void 0;
|
|
5065
5141
|
const memoryMb = parsed.values.memory != null ? Number(parsed.values.memory) : void 0;
|
|
5066
5142
|
const diskMb = parsed.values.disk_mb != null ? Number(parsed.values.disk_mb) : void 0;
|
|
5143
|
+
const builderDiskMb = parsed.values.builder_disk_mb != null ? Number(parsed.values.builder_disk_mb) : void 0;
|
|
5067
5144
|
if (cpus != null && !Number.isFinite(cpus)) {
|
|
5068
5145
|
throw new Error(`Invalid --cpus value: ${parsed.values.cpus}`);
|
|
5069
5146
|
}
|
|
@@ -5073,6 +5150,11 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5073
5150
|
if (diskMb != null && !Number.isInteger(diskMb)) {
|
|
5074
5151
|
throw new Error(`Invalid --disk_mb value: ${parsed.values.disk_mb}`);
|
|
5075
5152
|
}
|
|
5153
|
+
if (builderDiskMb != null && !Number.isInteger(builderDiskMb)) {
|
|
5154
|
+
throw new Error(
|
|
5155
|
+
`Invalid --builder_disk_mb value: ${parsed.values.builder_disk_mb}`
|
|
5156
|
+
);
|
|
5157
|
+
}
|
|
5076
5158
|
await createSandboxImage(
|
|
5077
5159
|
dockerfilePath,
|
|
5078
5160
|
{
|
|
@@ -5080,27 +5162,26 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5080
5162
|
cpus,
|
|
5081
5163
|
memoryMb,
|
|
5082
5164
|
diskMb,
|
|
5165
|
+
builderDiskMb,
|
|
5083
5166
|
isPublic: parsed.values.public
|
|
5084
5167
|
},
|
|
5085
5168
|
{ emit: ndjsonStdoutEmit }
|
|
5086
5169
|
);
|
|
5087
5170
|
}
|
|
5088
|
-
var
|
|
5171
|
+
var 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;
|
|
5089
5172
|
var init_sandbox_image = __esm({
|
|
5090
5173
|
"src/sandbox-image.ts"() {
|
|
5091
5174
|
init_models();
|
|
5092
5175
|
init_client();
|
|
5093
5176
|
init_image();
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
"VOLUME"
|
|
5103
|
-
]);
|
|
5177
|
+
DEFAULT_ROOTFS_DISK_MB = 10 * 1024;
|
|
5178
|
+
REMOTE_BUILD_DIR = "/var/lib/tensorlake/rootfs-builder/build";
|
|
5179
|
+
REMOTE_CONTEXT_DIR = "/var/lib/tensorlake/rootfs-builder/build/context";
|
|
5180
|
+
REMOTE_SPEC_PATH = "/var/lib/tensorlake/rootfs-builder/build/spec.json";
|
|
5181
|
+
REMOTE_METADATA_PATH = "/var/lib/tensorlake/rootfs-builder/build/metadata.json";
|
|
5182
|
+
ROOTFS_BUILDER_BIN_DIR = "/usr/local/bin";
|
|
5183
|
+
ROOTFS_BUILDER_PATH = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
5184
|
+
ROOTFS_BUILDER_COMMAND = "tl-rootfs-build";
|
|
5104
5185
|
UNSUPPORTED_DOCKERFILE_INSTRUCTIONS = /* @__PURE__ */ new Set([
|
|
5105
5186
|
"ARG",
|
|
5106
5187
|
"ONBUILD",
|