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