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/index.js
CHANGED
|
@@ -13,7 +13,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
|
|
|
13
13
|
var init_defaults = __esm({
|
|
14
14
|
"src/defaults.ts"() {
|
|
15
15
|
"use strict";
|
|
16
|
-
SDK_VERSION = "0.5.
|
|
16
|
+
SDK_VERSION = "0.5.13";
|
|
17
17
|
API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
|
|
18
18
|
API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
|
|
19
19
|
NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
|
|
@@ -3955,6 +3955,46 @@ var init_client = __esm({
|
|
|
3955
3955
|
);
|
|
3956
3956
|
return Object.assign(sandboxes, { traceId: raw.traceId });
|
|
3957
3957
|
}
|
|
3958
|
+
/**
|
|
3959
|
+
* List archived (terminated) sandboxes in the namespace.
|
|
3960
|
+
*
|
|
3961
|
+
* Archived sandboxes are terminated sandboxes parked in the server's
|
|
3962
|
+
* archived sandboxes store until the server-configured TTL expires.
|
|
3963
|
+
*/
|
|
3964
|
+
async listArchived(options) {
|
|
3965
|
+
const query = [];
|
|
3966
|
+
if (options?.limit != null) {
|
|
3967
|
+
query.push(`limit=${encodeURIComponent(String(options.limit))}`);
|
|
3968
|
+
}
|
|
3969
|
+
if (options?.cursor != null) {
|
|
3970
|
+
query.push(`cursor=${encodeURIComponent(options.cursor)}`);
|
|
3971
|
+
}
|
|
3972
|
+
if (options?.direction != null) {
|
|
3973
|
+
query.push(`direction=${encodeURIComponent(options.direction)}`);
|
|
3974
|
+
}
|
|
3975
|
+
const suffix = query.length ? `?${query.join("&")}` : "";
|
|
3976
|
+
const raw = await this.http.requestJson("GET", this.path(`archived-sandboxes${suffix}`));
|
|
3977
|
+
const sandboxes = (raw.sandboxes ?? []).map(
|
|
3978
|
+
(s) => fromSnakeKeys(s, "sandboxId")
|
|
3979
|
+
);
|
|
3980
|
+
const response = {
|
|
3981
|
+
sandboxes,
|
|
3982
|
+
prevCursor: raw.prev_cursor,
|
|
3983
|
+
nextCursor: raw.next_cursor
|
|
3984
|
+
};
|
|
3985
|
+
return Object.assign(response, { traceId: raw.traceId });
|
|
3986
|
+
}
|
|
3987
|
+
/** Get a single archived sandbox by id. */
|
|
3988
|
+
async getArchived(sandboxId) {
|
|
3989
|
+
const raw = await this.http.requestJson(
|
|
3990
|
+
"GET",
|
|
3991
|
+
this.path(`archived-sandboxes/${encodeURIComponent(sandboxId)}`)
|
|
3992
|
+
);
|
|
3993
|
+
return Object.assign(
|
|
3994
|
+
fromSnakeKeys(raw, "sandboxId"),
|
|
3995
|
+
{ traceId: raw.traceId }
|
|
3996
|
+
);
|
|
3997
|
+
}
|
|
3958
3998
|
/** Update sandbox properties such as name, exposed ports, and proxy auth settings. */
|
|
3959
3999
|
async update(sandboxId, options) {
|
|
3960
4000
|
const body = {};
|
|
@@ -4459,6 +4499,7 @@ __export(sandbox_image_exports, {
|
|
|
4459
4499
|
runCreateSandboxImageCli: () => runCreateSandboxImageCli
|
|
4460
4500
|
});
|
|
4461
4501
|
import { readFile, readdir, stat } from "fs/promises";
|
|
4502
|
+
import { homedir } from "os";
|
|
4462
4503
|
import path from "path";
|
|
4463
4504
|
import { parseArgs } from "util";
|
|
4464
4505
|
function defaultRegisteredName(dockerfilePath) {
|
|
@@ -4589,12 +4630,6 @@ function shellSplit(input) {
|
|
|
4589
4630
|
}
|
|
4590
4631
|
return tokens;
|
|
4591
4632
|
}
|
|
4592
|
-
function shellQuote(value) {
|
|
4593
|
-
if (!value) {
|
|
4594
|
-
return "''";
|
|
4595
|
-
}
|
|
4596
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
4597
|
-
}
|
|
4598
4633
|
function stripLeadingFlags(value) {
|
|
4599
4634
|
const flags = {};
|
|
4600
4635
|
let remaining = value.trimStart();
|
|
@@ -4632,75 +4667,6 @@ function parseFromValue(value, lineNumber) {
|
|
|
4632
4667
|
}
|
|
4633
4668
|
return tokens[0];
|
|
4634
4669
|
}
|
|
4635
|
-
function parseCopyLikeValues(value, lineNumber, keyword) {
|
|
4636
|
-
const { flags, remaining } = stripLeadingFlags(value);
|
|
4637
|
-
if ("from" in flags) {
|
|
4638
|
-
throw new Error(
|
|
4639
|
-
`line ${lineNumber}: ${keyword} --from is not supported for sandbox image creation`
|
|
4640
|
-
);
|
|
4641
|
-
}
|
|
4642
|
-
const payload = remaining.trim();
|
|
4643
|
-
if (!payload) {
|
|
4644
|
-
throw new Error(
|
|
4645
|
-
`line ${lineNumber}: ${keyword} must include source and destination`
|
|
4646
|
-
);
|
|
4647
|
-
}
|
|
4648
|
-
let parts;
|
|
4649
|
-
if (payload.startsWith("[")) {
|
|
4650
|
-
let parsed;
|
|
4651
|
-
try {
|
|
4652
|
-
parsed = JSON.parse(payload);
|
|
4653
|
-
} catch (error) {
|
|
4654
|
-
throw new Error(
|
|
4655
|
-
`line ${lineNumber}: invalid JSON array syntax for ${keyword}: ${error.message}`
|
|
4656
|
-
);
|
|
4657
|
-
}
|
|
4658
|
-
if (!Array.isArray(parsed) || parsed.length < 2 || parsed.some((item) => typeof item !== "string")) {
|
|
4659
|
-
throw new Error(
|
|
4660
|
-
`line ${lineNumber}: ${keyword} JSON array form requires at least two string values`
|
|
4661
|
-
);
|
|
4662
|
-
}
|
|
4663
|
-
parts = parsed;
|
|
4664
|
-
} else {
|
|
4665
|
-
parts = shellSplit(payload);
|
|
4666
|
-
if (parts.length < 2) {
|
|
4667
|
-
throw new Error(
|
|
4668
|
-
`line ${lineNumber}: ${keyword} must include at least one source and one destination`
|
|
4669
|
-
);
|
|
4670
|
-
}
|
|
4671
|
-
}
|
|
4672
|
-
return {
|
|
4673
|
-
flags,
|
|
4674
|
-
sources: parts.slice(0, -1),
|
|
4675
|
-
destination: parts[parts.length - 1]
|
|
4676
|
-
};
|
|
4677
|
-
}
|
|
4678
|
-
function parseEnvPairs(value, lineNumber) {
|
|
4679
|
-
const tokens = shellSplit(value);
|
|
4680
|
-
if (tokens.length === 0) {
|
|
4681
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4682
|
-
}
|
|
4683
|
-
if (tokens.every((token) => token.includes("="))) {
|
|
4684
|
-
return tokens.map((token) => {
|
|
4685
|
-
const [key, envValue] = token.split(/=(.*)/s, 2);
|
|
4686
|
-
if (!key) {
|
|
4687
|
-
throw new Error(`line ${lineNumber}: invalid ENV token '${token}'`);
|
|
4688
|
-
}
|
|
4689
|
-
return [key, envValue];
|
|
4690
|
-
});
|
|
4691
|
-
}
|
|
4692
|
-
if (tokens.length < 2) {
|
|
4693
|
-
throw new Error(`line ${lineNumber}: ENV must include a key and value`);
|
|
4694
|
-
}
|
|
4695
|
-
return [[tokens[0], tokens.slice(1).join(" ")]];
|
|
4696
|
-
}
|
|
4697
|
-
function resolveContainerPath(containerPath, workingDir) {
|
|
4698
|
-
if (!containerPath) {
|
|
4699
|
-
return workingDir;
|
|
4700
|
-
}
|
|
4701
|
-
const normalized = containerPath.startsWith("/") ? path.posix.normalize(containerPath) : path.posix.normalize(path.posix.join(workingDir, containerPath));
|
|
4702
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
4703
|
-
}
|
|
4704
4670
|
function buildPlanFromDockerfileText(dockerfileText, dockerfilePath, contextDir, registeredName) {
|
|
4705
4671
|
let baseImage;
|
|
4706
4672
|
const instructions = [];
|
|
@@ -4811,14 +4777,292 @@ function buildContextFromEnv() {
|
|
|
4811
4777
|
};
|
|
4812
4778
|
}
|
|
4813
4779
|
function createDefaultClient(context) {
|
|
4780
|
+
const useScopeHeaders = context.personalAccessToken != null && context.apiKey == null;
|
|
4814
4781
|
return new SandboxClient({
|
|
4815
4782
|
apiUrl: context.apiUrl,
|
|
4816
4783
|
apiKey: context.apiKey ?? context.personalAccessToken,
|
|
4817
|
-
organizationId: context.organizationId,
|
|
4818
|
-
projectId: context.projectId,
|
|
4784
|
+
organizationId: useScopeHeaders ? context.organizationId : void 0,
|
|
4785
|
+
projectId: useScopeHeaders ? context.projectId : void 0,
|
|
4819
4786
|
namespace: context.namespace
|
|
4820
4787
|
});
|
|
4821
4788
|
}
|
|
4789
|
+
function baseApiUrl(context) {
|
|
4790
|
+
return context.apiUrl.replace(/\/+$/, "");
|
|
4791
|
+
}
|
|
4792
|
+
function scopedBuildsPath(context) {
|
|
4793
|
+
return `/platform/v1/organizations/${encodeURIComponent(context.organizationId)}/projects/${encodeURIComponent(context.projectId)}/sandbox-template-builds`;
|
|
4794
|
+
}
|
|
4795
|
+
function platformHeaders(context) {
|
|
4796
|
+
const headers = {
|
|
4797
|
+
Authorization: `Bearer ${context.bearerToken}`,
|
|
4798
|
+
"Content-Type": "application/json"
|
|
4799
|
+
};
|
|
4800
|
+
if (context.useScopeHeaders) {
|
|
4801
|
+
headers["X-Forwarded-Organization-Id"] = context.organizationId;
|
|
4802
|
+
headers["X-Forwarded-Project-Id"] = context.projectId;
|
|
4803
|
+
}
|
|
4804
|
+
return headers;
|
|
4805
|
+
}
|
|
4806
|
+
async function requestJson(url, init, errorPrefix) {
|
|
4807
|
+
const response = await fetch(url, init);
|
|
4808
|
+
if (!response.ok) {
|
|
4809
|
+
throw new Error(
|
|
4810
|
+
`${errorPrefix} (HTTP ${response.status}): ${await response.text()}`
|
|
4811
|
+
);
|
|
4812
|
+
}
|
|
4813
|
+
const text = await response.text();
|
|
4814
|
+
return text ? JSON.parse(text) : {};
|
|
4815
|
+
}
|
|
4816
|
+
async function resolveBuildContext(context) {
|
|
4817
|
+
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4818
|
+
if (!bearerToken) {
|
|
4819
|
+
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
4820
|
+
}
|
|
4821
|
+
if (context.apiKey) {
|
|
4822
|
+
const scope = await requestJson(
|
|
4823
|
+
`${baseApiUrl(context)}/platform/v1/keys/introspect`,
|
|
4824
|
+
{
|
|
4825
|
+
method: "POST",
|
|
4826
|
+
headers: {
|
|
4827
|
+
Authorization: `Bearer ${bearerToken}`,
|
|
4828
|
+
"Content-Type": "application/json"
|
|
4829
|
+
}
|
|
4830
|
+
},
|
|
4831
|
+
"API key introspection failed"
|
|
4832
|
+
);
|
|
4833
|
+
if (!scope.organizationId || !scope.projectId) {
|
|
4834
|
+
throw new Error("API key introspection response is missing organizationId or projectId");
|
|
4835
|
+
}
|
|
4836
|
+
return {
|
|
4837
|
+
...context,
|
|
4838
|
+
bearerToken,
|
|
4839
|
+
organizationId: scope.organizationId,
|
|
4840
|
+
projectId: scope.projectId,
|
|
4841
|
+
useScopeHeaders: false
|
|
4842
|
+
};
|
|
4843
|
+
}
|
|
4844
|
+
if (!context.organizationId || !context.projectId) {
|
|
4845
|
+
throw new Error(
|
|
4846
|
+
"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."
|
|
4847
|
+
);
|
|
4848
|
+
}
|
|
4849
|
+
return {
|
|
4850
|
+
...context,
|
|
4851
|
+
bearerToken,
|
|
4852
|
+
organizationId: context.organizationId,
|
|
4853
|
+
projectId: context.projectId,
|
|
4854
|
+
useScopeHeaders: true
|
|
4855
|
+
};
|
|
4856
|
+
}
|
|
4857
|
+
async function prepareRootfsBuild(context, plan, isPublic) {
|
|
4858
|
+
if (!plan.baseImage) {
|
|
4859
|
+
throw new Error("Sandbox image builds require a Dockerfile FROM image or Image baseImage");
|
|
4860
|
+
}
|
|
4861
|
+
const spec = await requestJson(
|
|
4862
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}`,
|
|
4863
|
+
{
|
|
4864
|
+
method: "POST",
|
|
4865
|
+
headers: platformHeaders(context),
|
|
4866
|
+
body: JSON.stringify({
|
|
4867
|
+
name: plan.registeredName,
|
|
4868
|
+
dockerfile: plan.dockerfileText,
|
|
4869
|
+
baseImage: plan.baseImage,
|
|
4870
|
+
public: isPublic
|
|
4871
|
+
})
|
|
4872
|
+
},
|
|
4873
|
+
"failed to prepare sandbox image build"
|
|
4874
|
+
);
|
|
4875
|
+
return { prepared: parsePreparedBuild(spec), spec };
|
|
4876
|
+
}
|
|
4877
|
+
function parsePreparedBuild(raw) {
|
|
4878
|
+
const builder = raw.builder;
|
|
4879
|
+
if (!builder) {
|
|
4880
|
+
throw new Error("platform API response is missing rootfs builder configuration");
|
|
4881
|
+
}
|
|
4882
|
+
const prepared = {
|
|
4883
|
+
...raw,
|
|
4884
|
+
buildId: requiredString(raw, "buildId"),
|
|
4885
|
+
snapshotId: requiredString(raw, "snapshotId"),
|
|
4886
|
+
snapshotUri: requiredString(raw, "snapshotUri"),
|
|
4887
|
+
rootfsNodeKind: requiredString(raw, "rootfsNodeKind"),
|
|
4888
|
+
builder: {
|
|
4889
|
+
image: requiredString(builder, "image"),
|
|
4890
|
+
command: requiredString(builder, "command"),
|
|
4891
|
+
cpus: requiredNumber(builder, "cpus"),
|
|
4892
|
+
memoryMb: requiredNumber(builder, "memoryMb"),
|
|
4893
|
+
diskMb: requiredNumber(builder, "diskMb")
|
|
4894
|
+
}
|
|
4895
|
+
};
|
|
4896
|
+
const parent = raw.parent;
|
|
4897
|
+
if (parent != null) {
|
|
4898
|
+
prepared.parent = {
|
|
4899
|
+
parentManifestUri: requiredString(parent, "parentManifestUri"),
|
|
4900
|
+
rootfsDiskBytes: optionalNumber(parent, "rootfsDiskBytes")
|
|
4901
|
+
};
|
|
4902
|
+
}
|
|
4903
|
+
return prepared;
|
|
4904
|
+
}
|
|
4905
|
+
async function completeRootfsBuild(context, buildId, request) {
|
|
4906
|
+
return requestJson(
|
|
4907
|
+
`${baseApiUrl(context)}${scopedBuildsPath(context)}/${encodeURIComponent(buildId)}/complete`,
|
|
4908
|
+
{
|
|
4909
|
+
method: "POST",
|
|
4910
|
+
headers: platformHeaders(context),
|
|
4911
|
+
body: JSON.stringify(request)
|
|
4912
|
+
},
|
|
4913
|
+
"failed to complete sandbox image build"
|
|
4914
|
+
);
|
|
4915
|
+
}
|
|
4916
|
+
async function resolvedDockerConfigJson() {
|
|
4917
|
+
const configDir = process.env.DOCKER_CONFIG ?? path.join(homedir(), ".docker");
|
|
4918
|
+
const configPath = path.join(configDir, "config.json");
|
|
4919
|
+
try {
|
|
4920
|
+
const content = await readFile(configPath, "utf8");
|
|
4921
|
+
const parsed = JSON.parse(content);
|
|
4922
|
+
const auths = parsed.auths;
|
|
4923
|
+
if (auths != null && Object.keys(auths).length > 0) {
|
|
4924
|
+
return JSON.stringify({ auths });
|
|
4925
|
+
}
|
|
4926
|
+
} catch (error) {
|
|
4927
|
+
if (error.code === "ENOENT") {
|
|
4928
|
+
return void 0;
|
|
4929
|
+
}
|
|
4930
|
+
throw error;
|
|
4931
|
+
}
|
|
4932
|
+
return void 0;
|
|
4933
|
+
}
|
|
4934
|
+
function rootfsDiskBytes(diskMb, prepared) {
|
|
4935
|
+
if (diskMb != null) {
|
|
4936
|
+
return diskMb * 1024 * 1024;
|
|
4937
|
+
}
|
|
4938
|
+
if (prepared.parent != null) {
|
|
4939
|
+
if (prepared.parent.rootfsDiskBytes == null) {
|
|
4940
|
+
throw new Error(
|
|
4941
|
+
"platform API did not return parent rootfsDiskBytes for diff build; pass diskMb explicitly or update Platform API"
|
|
4942
|
+
);
|
|
4943
|
+
}
|
|
4944
|
+
return prepared.parent.rootfsDiskBytes;
|
|
4945
|
+
}
|
|
4946
|
+
return DEFAULT_ROOTFS_DISK_MB * 1024 * 1024;
|
|
4947
|
+
}
|
|
4948
|
+
function rootfsDiskBytesToMb(bytes) {
|
|
4949
|
+
return Math.ceil(bytes / (1024 * 1024));
|
|
4950
|
+
}
|
|
4951
|
+
async function buildRootfsSpec(preparedSpec, prepared, plan, diskMb) {
|
|
4952
|
+
const spec = {
|
|
4953
|
+
...preparedSpec,
|
|
4954
|
+
dockerfile: plan.dockerfileText,
|
|
4955
|
+
contextDir: REMOTE_CONTEXT_DIR,
|
|
4956
|
+
baseImage: plan.baseImage,
|
|
4957
|
+
rootfsDiskBytes: rootfsDiskBytes(diskMb, prepared)
|
|
4958
|
+
};
|
|
4959
|
+
const dockerConfigJson = await resolvedDockerConfigJson();
|
|
4960
|
+
if (dockerConfigJson != null) {
|
|
4961
|
+
spec.dockerConfigJson = dockerConfigJson;
|
|
4962
|
+
}
|
|
4963
|
+
return spec;
|
|
4964
|
+
}
|
|
4965
|
+
function rootfsBuilderExecutable(executable) {
|
|
4966
|
+
return executable === ROOTFS_BUILDER_COMMAND ? `${ROOTFS_BUILDER_BIN_DIR}/${ROOTFS_BUILDER_COMMAND}` : executable;
|
|
4967
|
+
}
|
|
4968
|
+
function rootfsBuilderEnv() {
|
|
4969
|
+
return { PATH: ROOTFS_BUILDER_PATH };
|
|
4970
|
+
}
|
|
4971
|
+
async function runRootfsBuilder(sandbox, command, emit, sleep3) {
|
|
4972
|
+
const parts = shellSplit(command);
|
|
4973
|
+
const [executable, ...commandArgs] = parts;
|
|
4974
|
+
if (!executable) {
|
|
4975
|
+
throw new Error("empty rootfs builder command returned by platform API");
|
|
4976
|
+
}
|
|
4977
|
+
await runStreaming(
|
|
4978
|
+
sandbox,
|
|
4979
|
+
emit,
|
|
4980
|
+
sleep3,
|
|
4981
|
+
rootfsBuilderExecutable(executable),
|
|
4982
|
+
[...commandArgs, "--spec", REMOTE_SPEC_PATH, "--metadata-out", REMOTE_METADATA_PATH],
|
|
4983
|
+
rootfsBuilderEnv(),
|
|
4984
|
+
REMOTE_BUILD_DIR
|
|
4985
|
+
);
|
|
4986
|
+
}
|
|
4987
|
+
function metadataString(metadata, snakeKey, camelKey) {
|
|
4988
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4989
|
+
return typeof value === "string" ? value : void 0;
|
|
4990
|
+
}
|
|
4991
|
+
function metadataNumber(metadata, snakeKey, camelKey) {
|
|
4992
|
+
const value = metadata[snakeKey] ?? metadata[camelKey];
|
|
4993
|
+
if (typeof value === "number") {
|
|
4994
|
+
return value;
|
|
4995
|
+
}
|
|
4996
|
+
if (typeof value === "string" && value.trim()) {
|
|
4997
|
+
const parsed = Number(value);
|
|
4998
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
4999
|
+
}
|
|
5000
|
+
return void 0;
|
|
5001
|
+
}
|
|
5002
|
+
function completeRequestFromMetadata(prepared, metadata) {
|
|
5003
|
+
const rootfsNodeKind = metadataString(metadata, "rootfs_node_kind", "rootfsNodeKind") ?? prepared.rootfsNodeKind;
|
|
5004
|
+
const parentManifestUri = metadataString(metadata, "parent_manifest_uri", "parentManifestUri") ?? (rootfsNodeKind === "diff" ? prepared.parent?.parentManifestUri : void 0);
|
|
5005
|
+
if (rootfsNodeKind === "diff" && parentManifestUri == null) {
|
|
5006
|
+
throw new Error("rootfs diff build completed without parent_manifest_uri");
|
|
5007
|
+
}
|
|
5008
|
+
const snapshotFormatVersion = metadataString(
|
|
5009
|
+
metadata,
|
|
5010
|
+
"snapshot_format_version",
|
|
5011
|
+
"snapshotFormatVersion"
|
|
5012
|
+
);
|
|
5013
|
+
const snapshotSizeBytes = metadataNumber(
|
|
5014
|
+
metadata,
|
|
5015
|
+
"snapshot_size_bytes",
|
|
5016
|
+
"snapshotSizeBytes"
|
|
5017
|
+
);
|
|
5018
|
+
const rootfsDiskBytesValue = metadataNumber(
|
|
5019
|
+
metadata,
|
|
5020
|
+
"rootfs_disk_bytes",
|
|
5021
|
+
"rootfsDiskBytes"
|
|
5022
|
+
);
|
|
5023
|
+
if (!snapshotFormatVersion) {
|
|
5024
|
+
throw new Error("rootfs builder metadata is missing snapshot_format_version");
|
|
5025
|
+
}
|
|
5026
|
+
if (snapshotSizeBytes == null) {
|
|
5027
|
+
throw new Error("rootfs builder metadata is missing numeric snapshot_size_bytes");
|
|
5028
|
+
}
|
|
5029
|
+
if (rootfsDiskBytesValue == null) {
|
|
5030
|
+
throw new Error("rootfs builder metadata is missing numeric rootfs_disk_bytes");
|
|
5031
|
+
}
|
|
5032
|
+
return {
|
|
5033
|
+
snapshotId: metadataString(metadata, "snapshot_id", "snapshotId") ?? prepared.snapshotId,
|
|
5034
|
+
snapshotUri: metadataString(metadata, "snapshot_uri", "snapshotUri") ?? prepared.snapshotUri,
|
|
5035
|
+
snapshotFormatVersion,
|
|
5036
|
+
snapshotSizeBytes,
|
|
5037
|
+
rootfsDiskBytes: rootfsDiskBytesValue,
|
|
5038
|
+
rootfsNodeKind,
|
|
5039
|
+
...parentManifestUri ? { parentManifestUri } : {}
|
|
5040
|
+
};
|
|
5041
|
+
}
|
|
5042
|
+
function requiredString(object, key) {
|
|
5043
|
+
const value = object[key];
|
|
5044
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
5045
|
+
throw new Error(`expected '${key}' to be a non-empty string`);
|
|
5046
|
+
}
|
|
5047
|
+
return value;
|
|
5048
|
+
}
|
|
5049
|
+
function requiredNumber(object, key) {
|
|
5050
|
+
const value = object[key];
|
|
5051
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
5052
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
5053
|
+
}
|
|
5054
|
+
return value;
|
|
5055
|
+
}
|
|
5056
|
+
function optionalNumber(object, key) {
|
|
5057
|
+
const value = object[key];
|
|
5058
|
+
if (value == null) {
|
|
5059
|
+
return void 0;
|
|
5060
|
+
}
|
|
5061
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
5062
|
+
throw new Error(`expected '${key}' to be a finite number`);
|
|
5063
|
+
}
|
|
5064
|
+
return value;
|
|
5065
|
+
}
|
|
4822
5066
|
async function runChecked(sandbox, command, args, env, workingDir) {
|
|
4823
5067
|
const result = await sandbox.run(command, {
|
|
4824
5068
|
args,
|
|
@@ -4878,18 +5122,6 @@ function emitOutputLines(emit, stream, response, seen) {
|
|
|
4878
5122
|
emit({ type: "build_log", stream, message: line });
|
|
4879
5123
|
}
|
|
4880
5124
|
}
|
|
4881
|
-
function isPathWithinContext(contextDir, localPath) {
|
|
4882
|
-
const relative = path.relative(contextDir, localPath);
|
|
4883
|
-
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
4884
|
-
}
|
|
4885
|
-
function resolveContextSourcePath(contextDir, source) {
|
|
4886
|
-
const resolvedContextDir = path.resolve(contextDir);
|
|
4887
|
-
const resolvedSource = path.resolve(resolvedContextDir, source);
|
|
4888
|
-
if (!isPathWithinContext(resolvedContextDir, resolvedSource)) {
|
|
4889
|
-
throw new Error(`Local path escapes the build context: ${source}`);
|
|
4890
|
-
}
|
|
4891
|
-
return resolvedSource;
|
|
4892
|
-
}
|
|
4893
5125
|
async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
4894
5126
|
const fileStats = await stat(localPath).catch(() => null);
|
|
4895
5127
|
if (!fileStats) {
|
|
@@ -4920,168 +5152,7 @@ async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
|
4920
5152
|
}
|
|
4921
5153
|
}
|
|
4922
5154
|
}
|
|
4923
|
-
async function
|
|
4924
|
-
const exportLine = `export ${key}=${shellQuote(value)}`;
|
|
4925
|
-
await runChecked(
|
|
4926
|
-
sandbox,
|
|
4927
|
-
"sh",
|
|
4928
|
-
["-c", `printf '%s\\n' ${shellQuote(exportLine)} >> /etc/environment`],
|
|
4929
|
-
processEnv
|
|
4930
|
-
);
|
|
4931
|
-
}
|
|
4932
|
-
async function copyFromContext(sandbox, emit, contextDir, sources, destination, workingDir, keyword) {
|
|
4933
|
-
const destinationPath = resolveContainerPath(destination, workingDir);
|
|
4934
|
-
if (sources.length > 1 && !destinationPath.endsWith("/")) {
|
|
4935
|
-
throw new Error(
|
|
4936
|
-
`${keyword} with multiple sources requires a directory destination ending in '/'`
|
|
4937
|
-
);
|
|
4938
|
-
}
|
|
4939
|
-
for (const source of sources) {
|
|
4940
|
-
const localSource = resolveContextSourcePath(contextDir, source);
|
|
4941
|
-
const localStats = await stat(localSource).catch(() => null);
|
|
4942
|
-
if (!localStats) {
|
|
4943
|
-
throw new Error(`Local path not found: ${localSource}`);
|
|
4944
|
-
}
|
|
4945
|
-
let remoteDestination = destinationPath;
|
|
4946
|
-
if (sources.length > 1) {
|
|
4947
|
-
remoteDestination = path.posix.join(
|
|
4948
|
-
destinationPath.replace(/\/$/, ""),
|
|
4949
|
-
path.posix.basename(source.replace(/\/$/, ""))
|
|
4950
|
-
);
|
|
4951
|
-
} else if (localStats.isFile() && destinationPath.endsWith("/")) {
|
|
4952
|
-
remoteDestination = path.posix.join(
|
|
4953
|
-
destinationPath.replace(/\/$/, ""),
|
|
4954
|
-
path.basename(source)
|
|
4955
|
-
);
|
|
4956
|
-
}
|
|
4957
|
-
emit({
|
|
4958
|
-
type: "status",
|
|
4959
|
-
message: `${keyword} ${source} -> ${remoteDestination}`
|
|
4960
|
-
});
|
|
4961
|
-
await copyLocalPathToSandbox(sandbox, localSource, remoteDestination);
|
|
4962
|
-
}
|
|
4963
|
-
}
|
|
4964
|
-
async function addUrlToSandbox(sandbox, emit, url, destination, workingDir, processEnv, sleep3) {
|
|
4965
|
-
let destinationPath = resolveContainerPath(destination, workingDir);
|
|
4966
|
-
const parsedUrl = new URL(url);
|
|
4967
|
-
const fileName = path.posix.basename(parsedUrl.pathname.replace(/\/$/, "")) || "downloaded";
|
|
4968
|
-
if (destinationPath.endsWith("/")) {
|
|
4969
|
-
destinationPath = path.posix.join(destinationPath.replace(/\/$/, ""), fileName);
|
|
4970
|
-
}
|
|
4971
|
-
const parentDir = path.posix.dirname(destinationPath) || "/";
|
|
4972
|
-
emit({
|
|
4973
|
-
type: "status",
|
|
4974
|
-
message: `ADD ${url} -> ${destinationPath}`
|
|
4975
|
-
});
|
|
4976
|
-
await runChecked(sandbox, "mkdir", ["-p", parentDir], processEnv);
|
|
4977
|
-
await runStreaming(
|
|
4978
|
-
sandbox,
|
|
4979
|
-
emit,
|
|
4980
|
-
sleep3,
|
|
4981
|
-
"sh",
|
|
4982
|
-
[
|
|
4983
|
-
"-c",
|
|
4984
|
-
`curl -fsSL --location ${shellQuote(url)} -o ${shellQuote(destinationPath)}`
|
|
4985
|
-
],
|
|
4986
|
-
processEnv,
|
|
4987
|
-
workingDir
|
|
4988
|
-
);
|
|
4989
|
-
}
|
|
4990
|
-
async function executeDockerfilePlan(sandbox, plan, emit, sleep3) {
|
|
4991
|
-
const processEnv = { ...BUILD_SANDBOX_PIP_ENV };
|
|
4992
|
-
let workingDir = "/";
|
|
4993
|
-
for (const instruction of plan.instructions) {
|
|
4994
|
-
const { keyword, value, lineNumber } = instruction;
|
|
4995
|
-
if (keyword === "RUN") {
|
|
4996
|
-
emit({ type: "status", message: `RUN ${value}` });
|
|
4997
|
-
await runStreaming(
|
|
4998
|
-
sandbox,
|
|
4999
|
-
emit,
|
|
5000
|
-
sleep3,
|
|
5001
|
-
"sh",
|
|
5002
|
-
["-c", value],
|
|
5003
|
-
processEnv,
|
|
5004
|
-
workingDir
|
|
5005
|
-
);
|
|
5006
|
-
continue;
|
|
5007
|
-
}
|
|
5008
|
-
if (keyword === "WORKDIR") {
|
|
5009
|
-
const tokens = shellSplit(value);
|
|
5010
|
-
if (tokens.length !== 1) {
|
|
5011
|
-
throw new Error(`line ${lineNumber}: WORKDIR must include exactly one path`);
|
|
5012
|
-
}
|
|
5013
|
-
workingDir = resolveContainerPath(tokens[0], workingDir);
|
|
5014
|
-
emit({ type: "status", message: `WORKDIR ${workingDir}` });
|
|
5015
|
-
await runChecked(sandbox, "mkdir", ["-p", workingDir], processEnv);
|
|
5016
|
-
continue;
|
|
5017
|
-
}
|
|
5018
|
-
if (keyword === "ENV") {
|
|
5019
|
-
for (const [key, envValue] of parseEnvPairs(value, lineNumber)) {
|
|
5020
|
-
emit({ type: "status", message: `ENV ${key}=${envValue}` });
|
|
5021
|
-
processEnv[key] = envValue;
|
|
5022
|
-
await persistEnvVar(sandbox, processEnv, key, envValue);
|
|
5023
|
-
}
|
|
5024
|
-
continue;
|
|
5025
|
-
}
|
|
5026
|
-
if (keyword === "COPY") {
|
|
5027
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
5028
|
-
value,
|
|
5029
|
-
lineNumber,
|
|
5030
|
-
keyword
|
|
5031
|
-
);
|
|
5032
|
-
await copyFromContext(
|
|
5033
|
-
sandbox,
|
|
5034
|
-
emit,
|
|
5035
|
-
plan.contextDir,
|
|
5036
|
-
sources,
|
|
5037
|
-
destination,
|
|
5038
|
-
workingDir,
|
|
5039
|
-
keyword
|
|
5040
|
-
);
|
|
5041
|
-
continue;
|
|
5042
|
-
}
|
|
5043
|
-
if (keyword === "ADD") {
|
|
5044
|
-
const { sources, destination } = parseCopyLikeValues(
|
|
5045
|
-
value,
|
|
5046
|
-
lineNumber,
|
|
5047
|
-
keyword
|
|
5048
|
-
);
|
|
5049
|
-
if (sources.length === 1 && /^https?:\/\//.test(sources[0])) {
|
|
5050
|
-
await addUrlToSandbox(
|
|
5051
|
-
sandbox,
|
|
5052
|
-
emit,
|
|
5053
|
-
sources[0],
|
|
5054
|
-
destination,
|
|
5055
|
-
workingDir,
|
|
5056
|
-
processEnv,
|
|
5057
|
-
sleep3
|
|
5058
|
-
);
|
|
5059
|
-
} else {
|
|
5060
|
-
await copyFromContext(
|
|
5061
|
-
sandbox,
|
|
5062
|
-
emit,
|
|
5063
|
-
plan.contextDir,
|
|
5064
|
-
sources,
|
|
5065
|
-
destination,
|
|
5066
|
-
workingDir,
|
|
5067
|
-
keyword
|
|
5068
|
-
);
|
|
5069
|
-
}
|
|
5070
|
-
continue;
|
|
5071
|
-
}
|
|
5072
|
-
if (IGNORED_DOCKERFILE_INSTRUCTIONS.has(keyword)) {
|
|
5073
|
-
emit({
|
|
5074
|
-
type: "warning",
|
|
5075
|
-
message: `Skipping Dockerfile instruction '${keyword}' during snapshot materialization. It is still preserved in the registered Dockerfile.`
|
|
5076
|
-
});
|
|
5077
|
-
continue;
|
|
5078
|
-
}
|
|
5079
|
-
throw new Error(
|
|
5080
|
-
`line ${lineNumber}: Dockerfile instruction '${keyword}' is not supported for sandbox image creation`
|
|
5081
|
-
);
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5084
|
-
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes, isPublic, snapshotFormatVersion) {
|
|
5155
|
+
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes2, isPublic, snapshotFormatVersion) {
|
|
5085
5156
|
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
5086
5157
|
if (!bearerToken) {
|
|
5087
5158
|
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
@@ -5115,7 +5186,7 @@ async function registerImage(context, name, dockerfile, snapshotId, snapshotSand
|
|
|
5115
5186
|
snapshotUri,
|
|
5116
5187
|
...snapshotFormatVersion ? { snapshotFormatVersion } : {},
|
|
5117
5188
|
snapshotSizeBytes,
|
|
5118
|
-
rootfsDiskBytes,
|
|
5189
|
+
rootfsDiskBytes: rootfsDiskBytes2,
|
|
5119
5190
|
public: isPublic
|
|
5120
5191
|
})
|
|
5121
5192
|
});
|
|
@@ -5132,67 +5203,71 @@ async function createSandboxImage(source, options = {}, deps = {}) {
|
|
|
5132
5203
|
const sleep3 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5133
5204
|
const context = buildContextFromEnv();
|
|
5134
5205
|
const clientFactory = deps.createClient ?? createDefaultClient;
|
|
5135
|
-
const register = deps.registerImage ?? ((...args) => registerImage(...args));
|
|
5136
5206
|
const sourceLabel = typeof source === "string" ? source : `Image(${source.name})`;
|
|
5137
5207
|
emit({ type: "status", message: `Loading ${sourceLabel}...` });
|
|
5138
5208
|
const plan = typeof source === "string" ? await loadDockerfilePlan(source, options.registeredName) : loadImagePlan(source, options);
|
|
5139
5209
|
emit({
|
|
5140
5210
|
type: "status",
|
|
5141
|
-
message:
|
|
5211
|
+
message: `Selected image name: ${plan.registeredName}`
|
|
5212
|
+
});
|
|
5213
|
+
emit({ type: "status", message: "Preparing rootfs build..." });
|
|
5214
|
+
const resolvedContext = await resolveBuildContext(context);
|
|
5215
|
+
const { prepared, spec: preparedSpec } = await prepareRootfsBuild(
|
|
5216
|
+
resolvedContext,
|
|
5217
|
+
plan,
|
|
5218
|
+
options.isPublic ?? false
|
|
5219
|
+
);
|
|
5220
|
+
emit({
|
|
5221
|
+
type: "status",
|
|
5222
|
+
message: prepared.rootfsNodeKind === "diff" ? "Build mode: RootfsDiff" : "Build mode: RootfsBase"
|
|
5142
5223
|
});
|
|
5143
5224
|
const client = clientFactory(context);
|
|
5144
5225
|
let sandbox;
|
|
5145
5226
|
try {
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
});
|
|
5227
|
+
const outputRootfsDiskBytes = rootfsDiskBytes(options.diskMb, prepared);
|
|
5228
|
+
const builderDiskMb = Math.max(
|
|
5229
|
+
rootfsDiskBytesToMb(outputRootfsDiskBytes),
|
|
5230
|
+
options.builderDiskMb ?? prepared.builder.diskMb
|
|
5231
|
+
);
|
|
5152
5232
|
emit({
|
|
5153
5233
|
type: "status",
|
|
5154
|
-
message: `
|
|
5234
|
+
message: `Creating rootfs builder sandbox from ${prepared.builder.image}...`
|
|
5155
5235
|
});
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5236
|
+
sandbox = await client.createAndConnect({
|
|
5237
|
+
image: prepared.builder.image,
|
|
5238
|
+
cpus: options.cpus ?? prepared.builder.cpus,
|
|
5239
|
+
memoryMb: options.memoryMb ?? prepared.builder.memoryMb,
|
|
5240
|
+
diskMb: builderDiskMb
|
|
5161
5241
|
});
|
|
5162
|
-
emit({
|
|
5163
|
-
type: "snapshot_created",
|
|
5164
|
-
snapshot_id: snapshot.snapshotId
|
|
5165
|
-
});
|
|
5166
|
-
if (!snapshot.snapshotUri) {
|
|
5167
|
-
throw new Error(
|
|
5168
|
-
`Snapshot ${snapshot.snapshotId} is missing snapshotUri and cannot be registered as a sandbox image.`
|
|
5169
|
-
);
|
|
5170
|
-
}
|
|
5171
|
-
if (snapshot.sizeBytes == null) {
|
|
5172
|
-
throw new Error(
|
|
5173
|
-
`Snapshot ${snapshot.snapshotId} is missing sizeBytes and cannot be registered as a sandbox image.`
|
|
5174
|
-
);
|
|
5175
|
-
}
|
|
5176
|
-
if (snapshot.rootfsDiskBytes == null) {
|
|
5177
|
-
throw new Error(
|
|
5178
|
-
`Snapshot ${snapshot.snapshotId} is missing rootfsDiskBytes and cannot be registered as a sandbox image.`
|
|
5179
|
-
);
|
|
5180
|
-
}
|
|
5181
5242
|
emit({
|
|
5182
5243
|
type: "status",
|
|
5183
|
-
message: `
|
|
5244
|
+
message: `Rootfs builder sandbox ${sandbox.sandboxId} is running`
|
|
5184
5245
|
});
|
|
5185
|
-
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5246
|
+
emit({ type: "status", message: "Uploading build context..." });
|
|
5247
|
+
await copyLocalPathToSandbox(sandbox, plan.contextDir, REMOTE_CONTEXT_DIR);
|
|
5248
|
+
const spec = await buildRootfsSpec(
|
|
5249
|
+
preparedSpec,
|
|
5250
|
+
prepared,
|
|
5251
|
+
plan,
|
|
5252
|
+
options.diskMb
|
|
5253
|
+
);
|
|
5254
|
+
await runChecked(sandbox, "mkdir", ["-p", path.posix.dirname(REMOTE_SPEC_PATH)]);
|
|
5255
|
+
await sandbox.writeFile(
|
|
5256
|
+
REMOTE_SPEC_PATH,
|
|
5257
|
+
new TextEncoder().encode(JSON.stringify(spec, null, 2))
|
|
5258
|
+
);
|
|
5259
|
+
emit({ type: "status", message: "Running offline rootfs builder..." });
|
|
5260
|
+
await runRootfsBuilder(sandbox, prepared.builder.command, emit, sleep3);
|
|
5261
|
+
const metadataBytes = await sandbox.readFile(REMOTE_METADATA_PATH);
|
|
5262
|
+
const metadata = JSON.parse(
|
|
5263
|
+
new TextDecoder().decode(metadataBytes)
|
|
5264
|
+
);
|
|
5265
|
+
const completeRequest = completeRequestFromMetadata(prepared, metadata);
|
|
5266
|
+
emit({ type: "status", message: "Completing image registration..." });
|
|
5267
|
+
const result = await completeRootfsBuild(
|
|
5268
|
+
resolvedContext,
|
|
5269
|
+
prepared.buildId,
|
|
5270
|
+
completeRequest
|
|
5196
5271
|
);
|
|
5197
5272
|
emit({
|
|
5198
5273
|
type: "image_registered",
|
|
@@ -5220,16 +5295,18 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5220
5295
|
cpus: { type: "string" },
|
|
5221
5296
|
memory: { type: "string" },
|
|
5222
5297
|
disk_mb: { type: "string" },
|
|
5298
|
+
builder_disk_mb: { type: "string" },
|
|
5223
5299
|
public: { type: "boolean", default: false }
|
|
5224
5300
|
}
|
|
5225
5301
|
});
|
|
5226
5302
|
const dockerfilePath = parsed.positionals[0];
|
|
5227
5303
|
if (!dockerfilePath) {
|
|
5228
|
-
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--public]");
|
|
5304
|
+
throw new Error("Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--builder_disk_mb MB] [--public]");
|
|
5229
5305
|
}
|
|
5230
5306
|
const cpus = parsed.values.cpus != null ? Number(parsed.values.cpus) : void 0;
|
|
5231
5307
|
const memoryMb = parsed.values.memory != null ? Number(parsed.values.memory) : void 0;
|
|
5232
5308
|
const diskMb = parsed.values.disk_mb != null ? Number(parsed.values.disk_mb) : void 0;
|
|
5309
|
+
const builderDiskMb = parsed.values.builder_disk_mb != null ? Number(parsed.values.builder_disk_mb) : void 0;
|
|
5233
5310
|
if (cpus != null && !Number.isFinite(cpus)) {
|
|
5234
5311
|
throw new Error(`Invalid --cpus value: ${parsed.values.cpus}`);
|
|
5235
5312
|
}
|
|
@@ -5239,6 +5316,11 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5239
5316
|
if (diskMb != null && !Number.isInteger(diskMb)) {
|
|
5240
5317
|
throw new Error(`Invalid --disk_mb value: ${parsed.values.disk_mb}`);
|
|
5241
5318
|
}
|
|
5319
|
+
if (builderDiskMb != null && !Number.isInteger(builderDiskMb)) {
|
|
5320
|
+
throw new Error(
|
|
5321
|
+
`Invalid --builder_disk_mb value: ${parsed.values.builder_disk_mb}`
|
|
5322
|
+
);
|
|
5323
|
+
}
|
|
5242
5324
|
await createSandboxImage(
|
|
5243
5325
|
dockerfilePath,
|
|
5244
5326
|
{
|
|
@@ -5246,28 +5328,27 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5246
5328
|
cpus,
|
|
5247
5329
|
memoryMb,
|
|
5248
5330
|
diskMb,
|
|
5331
|
+
builderDiskMb,
|
|
5249
5332
|
isPublic: parsed.values.public
|
|
5250
5333
|
},
|
|
5251
5334
|
{ emit: ndjsonStdoutEmit }
|
|
5252
5335
|
);
|
|
5253
5336
|
}
|
|
5254
|
-
var
|
|
5337
|
+
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;
|
|
5255
5338
|
var init_sandbox_image = __esm({
|
|
5256
5339
|
"src/sandbox-image.ts"() {
|
|
5257
5340
|
"use strict";
|
|
5258
5341
|
init_models();
|
|
5259
5342
|
init_client();
|
|
5260
5343
|
init_image();
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
"VOLUME"
|
|
5270
|
-
]);
|
|
5344
|
+
DEFAULT_ROOTFS_DISK_MB = 10 * 1024;
|
|
5345
|
+
REMOTE_BUILD_DIR = "/var/lib/tensorlake/rootfs-builder/build";
|
|
5346
|
+
REMOTE_CONTEXT_DIR = "/var/lib/tensorlake/rootfs-builder/build/context";
|
|
5347
|
+
REMOTE_SPEC_PATH = "/var/lib/tensorlake/rootfs-builder/build/spec.json";
|
|
5348
|
+
REMOTE_METADATA_PATH = "/var/lib/tensorlake/rootfs-builder/build/metadata.json";
|
|
5349
|
+
ROOTFS_BUILDER_BIN_DIR = "/usr/local/bin";
|
|
5350
|
+
ROOTFS_BUILDER_PATH = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
5351
|
+
ROOTFS_BUILDER_COMMAND = "tl-rootfs-build";
|
|
5271
5352
|
UNSUPPORTED_DOCKERFILE_INSTRUCTIONS = /* @__PURE__ */ new Set([
|
|
5272
5353
|
"ARG",
|
|
5273
5354
|
"ONBUILD",
|