tensorlake 0.5.17 → 0.5.19
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 +127 -801
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +346 -3
- package/dist/index.d.ts +346 -3
- package/dist/index.js +132 -801
- package/dist/index.js.map +1 -1
- package/dist/native/darwin-arm64/tensorlake-node.node +0 -0
- package/dist/native/linux-x64/tensorlake-node.node +0 -0
- package/dist/native/win32-x64/tensorlake-node.node +0 -0
- package/dist/sandbox-image-CmX-BLMV.d.cts +117 -0
- package/dist/sandbox-image-CmX-BLMV.d.ts +117 -0
- package/dist/sandbox-image.cjs +216 -5098
- 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 +232 -5103
- package/dist/sandbox-image.js.map +1 -1
- package/lib/runtime.cjs +19 -0
- package/package.json +3 -2
- package/dist/sandbox-image-BBcx3bSx.d.cts +0 -477
- package/dist/sandbox-image-BBcx3bSx.d.ts +0 -477
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
3
9
|
var __esm = (fn, res) => function __init() {
|
|
4
10
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
11
|
};
|
|
@@ -13,7 +19,7 @@ var SDK_VERSION, API_URL, API_KEY, NAMESPACE, SANDBOX_PROXY_URL, DEFAULT_HTTP_TI
|
|
|
13
19
|
var init_defaults = __esm({
|
|
14
20
|
"src/defaults.ts"() {
|
|
15
21
|
"use strict";
|
|
16
|
-
SDK_VERSION = "0.5.
|
|
22
|
+
SDK_VERSION = "0.5.19";
|
|
17
23
|
API_URL = process.env.TENSORLAKE_API_URL ?? "https://api.tensorlake.ai";
|
|
18
24
|
API_KEY = process.env.TENSORLAKE_API_KEY ?? void 0;
|
|
19
25
|
NAMESPACE = process.env.INDEXIFY_NAMESPACE ?? "default";
|
|
@@ -4485,11 +4491,6 @@ var init_image = __esm({
|
|
|
4485
4491
|
* and registers the snapshot as a named sandbox template.
|
|
4486
4492
|
*/
|
|
4487
4493
|
async build(options = {}) {
|
|
4488
|
-
if ((options.registeredName ?? this._name) === DEFAULT_IMAGE_NAME) {
|
|
4489
|
-
console.warn(
|
|
4490
|
-
`Building sandbox image with the default name "${DEFAULT_IMAGE_NAME}". Pass \`registeredName\` or \`Image({ name })\` to avoid collisions with other default-named images in this project.`
|
|
4491
|
-
);
|
|
4492
|
-
}
|
|
4493
4494
|
const { createSandboxImage: createSandboxImage2 } = await Promise.resolve().then(() => (init_sandbox_image(), sandbox_image_exports));
|
|
4494
4495
|
return createSandboxImage2(this, options);
|
|
4495
4496
|
}
|
|
@@ -4504,267 +4505,65 @@ var init_image = __esm({
|
|
|
4504
4505
|
// src/sandbox-image.ts
|
|
4505
4506
|
var sandbox_image_exports = {};
|
|
4506
4507
|
__export(sandbox_image_exports, {
|
|
4508
|
+
__setNativeBindingForTest: () => __setNativeBindingForTest,
|
|
4507
4509
|
createSandboxImage: () => createSandboxImage,
|
|
4508
|
-
defaultRegisteredName: () => defaultRegisteredName,
|
|
4509
|
-
loadDockerfilePlan: () => loadDockerfilePlan,
|
|
4510
|
-
loadImagePlan: () => loadImagePlan,
|
|
4511
|
-
logicalDockerfileLines: () => logicalDockerfileLines,
|
|
4512
|
-
registerImage: () => registerImage,
|
|
4513
4510
|
runCreateSandboxImageCli: () => runCreateSandboxImageCli
|
|
4514
4511
|
});
|
|
4515
|
-
import {
|
|
4516
|
-
import { homedir } from "os";
|
|
4512
|
+
import { createRequire } from "module";
|
|
4517
4513
|
import path from "path";
|
|
4514
|
+
import { existsSync } from "fs";
|
|
4518
4515
|
import { parseArgs } from "util";
|
|
4519
|
-
function
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
const parentName = path.basename(path.dirname(dockerfilePath)).trim();
|
|
4523
|
-
return parentName || "sandbox-image";
|
|
4516
|
+
function resolveRequire() {
|
|
4517
|
+
if (typeof module !== "undefined" && module.exports != null && typeof __require !== "undefined") {
|
|
4518
|
+
return __require;
|
|
4524
4519
|
}
|
|
4525
|
-
return
|
|
4520
|
+
return createRequire(import.meta.url);
|
|
4526
4521
|
}
|
|
4527
|
-
function
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
if (startLine == null) {
|
|
4538
|
-
startLine = lineNumber;
|
|
4539
|
-
}
|
|
4540
|
-
let line = rawLine.replace(/\s+$/, "");
|
|
4541
|
-
const continued = line.endsWith("\\");
|
|
4542
|
-
if (continued) {
|
|
4543
|
-
line = line.slice(0, -1);
|
|
4544
|
-
}
|
|
4545
|
-
const normalized = line.trim();
|
|
4546
|
-
if (normalized && !normalized.startsWith("#")) {
|
|
4547
|
-
parts.push(normalized);
|
|
4548
|
-
}
|
|
4549
|
-
if (continued) {
|
|
4550
|
-
continue;
|
|
4551
|
-
}
|
|
4552
|
-
if (parts.length > 0) {
|
|
4553
|
-
logicalLines.push({
|
|
4554
|
-
lineNumber: startLine ?? lineNumber,
|
|
4555
|
-
line: parts.join(" ")
|
|
4556
|
-
});
|
|
4557
|
-
}
|
|
4558
|
-
parts = [];
|
|
4559
|
-
startLine = null;
|
|
4560
|
-
}
|
|
4561
|
-
if (parts.length > 0) {
|
|
4562
|
-
logicalLines.push({
|
|
4563
|
-
lineNumber: startLine ?? 1,
|
|
4564
|
-
line: parts.join(" ")
|
|
4565
|
-
});
|
|
4566
|
-
}
|
|
4567
|
-
return logicalLines;
|
|
4568
|
-
}
|
|
4569
|
-
function splitInstruction(line, lineNumber) {
|
|
4570
|
-
const trimmed = line.trim();
|
|
4571
|
-
if (!trimmed) {
|
|
4572
|
-
throw new Error(`line ${lineNumber}: empty Dockerfile instruction`);
|
|
4573
|
-
}
|
|
4574
|
-
const match = trimmed.match(/^(\S+)(?:\s+(.*))?$/);
|
|
4575
|
-
if (!match) {
|
|
4576
|
-
throw new Error(`line ${lineNumber}: invalid Dockerfile instruction`);
|
|
4577
|
-
}
|
|
4578
|
-
return {
|
|
4579
|
-
keyword: match[1].toUpperCase(),
|
|
4580
|
-
value: (match[2] ?? "").trim()
|
|
4581
|
-
};
|
|
4582
|
-
}
|
|
4583
|
-
function shellSplit(input) {
|
|
4584
|
-
const tokens = [];
|
|
4585
|
-
let current = "";
|
|
4586
|
-
let quote = null;
|
|
4587
|
-
let escape = false;
|
|
4588
|
-
for (let i = 0; i < input.length; i++) {
|
|
4589
|
-
const char = input[i];
|
|
4590
|
-
if (escape) {
|
|
4591
|
-
current += char;
|
|
4592
|
-
escape = false;
|
|
4593
|
-
continue;
|
|
4594
|
-
}
|
|
4595
|
-
if (quote == null) {
|
|
4596
|
-
if (/\s/.test(char)) {
|
|
4597
|
-
if (current) {
|
|
4598
|
-
tokens.push(current);
|
|
4599
|
-
current = "";
|
|
4600
|
-
}
|
|
4601
|
-
continue;
|
|
4602
|
-
}
|
|
4603
|
-
if (char === "'" || char === '"') {
|
|
4604
|
-
quote = char;
|
|
4605
|
-
continue;
|
|
4606
|
-
}
|
|
4607
|
-
if (char === "\\") {
|
|
4608
|
-
escape = true;
|
|
4609
|
-
continue;
|
|
4610
|
-
}
|
|
4611
|
-
current += char;
|
|
4612
|
-
continue;
|
|
4613
|
-
}
|
|
4614
|
-
if (quote === "'") {
|
|
4615
|
-
if (char === "'") {
|
|
4616
|
-
quote = null;
|
|
4617
|
-
} else {
|
|
4618
|
-
current += char;
|
|
4619
|
-
}
|
|
4620
|
-
continue;
|
|
4621
|
-
}
|
|
4622
|
-
if (char === '"') {
|
|
4623
|
-
quote = null;
|
|
4624
|
-
continue;
|
|
4625
|
-
}
|
|
4626
|
-
if (char === "\\") {
|
|
4627
|
-
const next = input[++i];
|
|
4628
|
-
if (next == null) {
|
|
4629
|
-
throw new Error(`unterminated escape sequence in '${input}'`);
|
|
4630
|
-
}
|
|
4631
|
-
current += next;
|
|
4632
|
-
continue;
|
|
4633
|
-
}
|
|
4634
|
-
current += char;
|
|
4635
|
-
}
|
|
4636
|
-
if (escape) {
|
|
4637
|
-
throw new Error(`unterminated escape sequence in '${input}'`);
|
|
4638
|
-
}
|
|
4639
|
-
if (quote != null) {
|
|
4640
|
-
throw new Error(`unterminated quoted string in '${input}'`);
|
|
4641
|
-
}
|
|
4642
|
-
if (current) {
|
|
4643
|
-
tokens.push(current);
|
|
4644
|
-
}
|
|
4645
|
-
return tokens;
|
|
4646
|
-
}
|
|
4647
|
-
function stripLeadingFlags(value) {
|
|
4648
|
-
const flags = {};
|
|
4649
|
-
let remaining = value.trimStart();
|
|
4650
|
-
while (remaining.startsWith("--")) {
|
|
4651
|
-
const firstSpace = remaining.indexOf(" ");
|
|
4652
|
-
if (firstSpace === -1) {
|
|
4653
|
-
throw new Error(`invalid Dockerfile flag syntax: ${value}`);
|
|
4654
|
-
}
|
|
4655
|
-
const token = remaining.slice(0, firstSpace);
|
|
4656
|
-
const rest = remaining.slice(firstSpace + 1).trimStart();
|
|
4657
|
-
const flagBody = token.slice(2);
|
|
4658
|
-
if (flagBody.includes("=")) {
|
|
4659
|
-
const [key, flagValue2] = flagBody.split(/=(.*)/s, 2);
|
|
4660
|
-
flags[key] = flagValue2;
|
|
4661
|
-
remaining = rest;
|
|
4662
|
-
continue;
|
|
4663
|
-
}
|
|
4664
|
-
const [flagValue, ...restTokens] = shellSplit(rest);
|
|
4665
|
-
if (flagValue == null) {
|
|
4666
|
-
throw new Error(`missing value for Dockerfile flag '${token}'`);
|
|
4667
|
-
}
|
|
4668
|
-
flags[flagBody] = flagValue;
|
|
4669
|
-
remaining = restTokens.join(" ");
|
|
4670
|
-
}
|
|
4671
|
-
return { flags, remaining };
|
|
4672
|
-
}
|
|
4673
|
-
function parseFromValue(value, lineNumber) {
|
|
4674
|
-
const { remaining } = stripLeadingFlags(value);
|
|
4675
|
-
const tokens = shellSplit(remaining);
|
|
4676
|
-
if (tokens.length === 0) {
|
|
4677
|
-
throw new Error(`line ${lineNumber}: FROM must include a base image`);
|
|
4678
|
-
}
|
|
4679
|
-
if (tokens.length > 1 && tokens[1].toLowerCase() !== "as") {
|
|
4680
|
-
throw new Error(`line ${lineNumber}: unsupported FROM syntax '${value}'`);
|
|
4522
|
+
function loadNativeBinding() {
|
|
4523
|
+
if (cachedNativeBinding) return cachedNativeBinding;
|
|
4524
|
+
if (cachedNativeBindingError) throw cachedNativeBindingError;
|
|
4525
|
+
try {
|
|
4526
|
+
const { loadNative } = resolveRequire()("../lib/runtime.cjs");
|
|
4527
|
+
cachedNativeBinding = loadNative();
|
|
4528
|
+
return cachedNativeBinding;
|
|
4529
|
+
} catch (error) {
|
|
4530
|
+
cachedNativeBindingError = error instanceof Error ? error : new Error(String(error));
|
|
4531
|
+
throw cachedNativeBindingError;
|
|
4681
4532
|
}
|
|
4682
|
-
return tokens[0];
|
|
4683
4533
|
}
|
|
4684
|
-
function
|
|
4685
|
-
|
|
4686
|
-
|
|
4687
|
-
for (const logicalLine of logicalDockerfileLines(dockerfileText)) {
|
|
4688
|
-
const { keyword, value } = splitInstruction(
|
|
4689
|
-
logicalLine.line,
|
|
4690
|
-
logicalLine.lineNumber
|
|
4691
|
-
);
|
|
4692
|
-
if (keyword === "FROM") {
|
|
4693
|
-
if (baseImage != null) {
|
|
4694
|
-
throw new Error(
|
|
4695
|
-
`line ${logicalLine.lineNumber}: multi-stage Dockerfiles are not supported for sandbox image creation`
|
|
4696
|
-
);
|
|
4697
|
-
}
|
|
4698
|
-
baseImage = parseFromValue(value, logicalLine.lineNumber);
|
|
4699
|
-
continue;
|
|
4700
|
-
}
|
|
4701
|
-
if (UNSUPPORTED_DOCKERFILE_INSTRUCTIONS.has(keyword)) {
|
|
4702
|
-
throw new Error(
|
|
4703
|
-
`line ${logicalLine.lineNumber}: Dockerfile instruction '${keyword}' is not supported for sandbox image creation`
|
|
4704
|
-
);
|
|
4705
|
-
}
|
|
4706
|
-
instructions.push({
|
|
4707
|
-
keyword,
|
|
4708
|
-
value,
|
|
4709
|
-
lineNumber: logicalLine.lineNumber
|
|
4710
|
-
});
|
|
4711
|
-
}
|
|
4712
|
-
if (!baseImage) {
|
|
4713
|
-
throw new Error("Dockerfile must contain a FROM instruction");
|
|
4714
|
-
}
|
|
4715
|
-
return {
|
|
4716
|
-
dockerfilePath,
|
|
4717
|
-
contextDir,
|
|
4718
|
-
registeredName: registeredName ?? defaultRegisteredName(dockerfilePath),
|
|
4719
|
-
dockerfileText,
|
|
4720
|
-
baseImage,
|
|
4721
|
-
instructions
|
|
4722
|
-
};
|
|
4534
|
+
function __setNativeBindingForTest(binding) {
|
|
4535
|
+
cachedNativeBinding = binding;
|
|
4536
|
+
cachedNativeBindingError = void 0;
|
|
4723
4537
|
}
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
const fileStats = await stat(resolvedPath).catch(() => null);
|
|
4727
|
-
if (!fileStats?.isFile()) {
|
|
4728
|
-
throw new Error(`Dockerfile not found: ${dockerfilePath}`);
|
|
4729
|
-
}
|
|
4730
|
-
const dockerfileText = await readFile(resolvedPath, "utf8");
|
|
4731
|
-
return buildPlanFromDockerfileText(
|
|
4732
|
-
dockerfileText,
|
|
4733
|
-
resolvedPath,
|
|
4734
|
-
path.dirname(resolvedPath),
|
|
4735
|
-
registeredName
|
|
4736
|
-
);
|
|
4538
|
+
function nonEmpty(value) {
|
|
4539
|
+
return value && value.length > 0 ? value : void 0;
|
|
4737
4540
|
}
|
|
4738
|
-
function
|
|
4739
|
-
const
|
|
4740
|
-
const
|
|
4741
|
-
const logicalLines = logicalDockerfileLines(dockerfileText);
|
|
4742
|
-
const instructions = image.baseImage == null ? logicalLines : logicalLines.slice(1);
|
|
4541
|
+
function buildContextFromEnv() {
|
|
4542
|
+
const debugRaw = (process.env.TENSORLAKE_DEBUG ?? "").toLowerCase();
|
|
4543
|
+
const debug = ["1", "true", "yes", "on"].includes(debugRaw);
|
|
4743
4544
|
return {
|
|
4744
|
-
|
|
4745
|
-
|
|
4746
|
-
|
|
4747
|
-
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
return {
|
|
4752
|
-
keyword: parsed.keyword,
|
|
4753
|
-
value: parsed.value,
|
|
4754
|
-
lineNumber
|
|
4755
|
-
};
|
|
4756
|
-
})
|
|
4545
|
+
apiUrl: nonEmpty(process.env.TENSORLAKE_API_URL) ?? "https://api.tensorlake.ai",
|
|
4546
|
+
apiKey: nonEmpty(process.env.TENSORLAKE_API_KEY),
|
|
4547
|
+
personalAccessToken: nonEmpty(process.env.TENSORLAKE_PAT),
|
|
4548
|
+
namespace: nonEmpty(process.env.INDEXIFY_NAMESPACE) ?? "default",
|
|
4549
|
+
organizationId: nonEmpty(process.env.TENSORLAKE_ORGANIZATION_ID),
|
|
4550
|
+
projectId: nonEmpty(process.env.TENSORLAKE_PROJECT_ID),
|
|
4551
|
+
debug
|
|
4757
4552
|
};
|
|
4758
4553
|
}
|
|
4759
|
-
function
|
|
4760
|
-
|
|
4761
|
-
|
|
4554
|
+
function defaultRegisteredName(dockerfilePath) {
|
|
4555
|
+
const parsed = path.parse(dockerfilePath);
|
|
4556
|
+
if (parsed.name.toLowerCase() === "dockerfile") {
|
|
4557
|
+
const parentName = path.basename(path.dirname(dockerfilePath)).trim();
|
|
4558
|
+
return parentName || "sandbox-image";
|
|
4559
|
+
}
|
|
4560
|
+
return parsed.name || "sandbox-image";
|
|
4762
4561
|
}
|
|
4763
4562
|
function noopEmit(_event) {
|
|
4764
4563
|
}
|
|
4765
4564
|
function stderrEmit(event) {
|
|
4766
|
-
const type = typeof event.type === "string" ? event.type : "";
|
|
4767
4565
|
const message = typeof event.message === "string" ? event.message : "";
|
|
4566
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
4768
4567
|
if (type === "build_log") {
|
|
4769
4568
|
const stream = typeof event.stream === "string" ? event.stream : "stdout";
|
|
4770
4569
|
process.stderr.write(`[${stream}] ${message}
|
|
@@ -4774,557 +4573,103 @@ function stderrEmit(event) {
|
|
|
4774
4573
|
`);
|
|
4775
4574
|
}
|
|
4776
4575
|
}
|
|
4777
|
-
function
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
);
|
|
4576
|
+
function ndjsonStdoutEmit(event) {
|
|
4577
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
4578
|
+
`);
|
|
4781
4579
|
}
|
|
4782
|
-
function
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
personalAccessToken: process.env.TENSORLAKE_PAT,
|
|
4787
|
-
namespace: process.env.INDEXIFY_NAMESPACE ?? "default",
|
|
4788
|
-
organizationId: process.env.TENSORLAKE_ORGANIZATION_ID,
|
|
4789
|
-
projectId: process.env.TENSORLAKE_PROJECT_ID,
|
|
4790
|
-
debug: debugEnabled()
|
|
4580
|
+
function eventToEmitDict(event) {
|
|
4581
|
+
const out = {
|
|
4582
|
+
type: event.eventType,
|
|
4583
|
+
message: event.message
|
|
4791
4584
|
};
|
|
4585
|
+
if (event.stream != null) out.stream = event.stream;
|
|
4586
|
+
return out;
|
|
4792
4587
|
}
|
|
4793
|
-
function
|
|
4794
|
-
const
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
|
|
4808
|
-
|
|
4809
|
-
|
|
4810
|
-
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4588
|
+
async function createSandboxImage(source, options = {}, deps = {}) {
|
|
4589
|
+
const emit = deps.emit ?? (options.verbose ? stderrEmit : noopEmit);
|
|
4590
|
+
const context = buildContextFromEnv();
|
|
4591
|
+
let dockerfilePath;
|
|
4592
|
+
let dockerfileText;
|
|
4593
|
+
let nativeContextDir;
|
|
4594
|
+
let effectiveName;
|
|
4595
|
+
if (source instanceof Image) {
|
|
4596
|
+
if (!source.baseImage) {
|
|
4597
|
+
throw new Error("Image must have a baseImage to build");
|
|
4598
|
+
}
|
|
4599
|
+
const resolvedContext = path.resolve(options.contextDir ?? process.cwd());
|
|
4600
|
+
dockerfilePath = path.join(resolvedContext, "Dockerfile");
|
|
4601
|
+
let text = dockerfileContent(source);
|
|
4602
|
+
if (!text.endsWith("\n")) text += "\n";
|
|
4603
|
+
dockerfileText = text;
|
|
4604
|
+
nativeContextDir = resolvedContext;
|
|
4605
|
+
effectiveName = options.registeredName ?? source.name;
|
|
4606
|
+
} else if (typeof source === "string") {
|
|
4607
|
+
const resolvedPath = path.resolve(source);
|
|
4608
|
+
if (!existsSync(resolvedPath)) {
|
|
4609
|
+
throw new Error(`Dockerfile not found: ${source}`);
|
|
4610
|
+
}
|
|
4611
|
+
dockerfilePath = resolvedPath;
|
|
4612
|
+
dockerfileText = void 0;
|
|
4613
|
+
nativeContextDir = void 0;
|
|
4614
|
+
effectiveName = options.registeredName ?? defaultRegisteredName(resolvedPath);
|
|
4615
|
+
} else {
|
|
4616
|
+
throw new TypeError(
|
|
4617
|
+
`source must be an Image or a Dockerfile path, got ${typeof source}`
|
|
4618
|
+
);
|
|
4817
4619
|
}
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
const response = await fetch(url, init);
|
|
4822
|
-
if (!response.ok) {
|
|
4823
|
-
throw new Error(
|
|
4824
|
-
`${errorPrefix} (HTTP ${response.status}): ${await response.text()}`
|
|
4620
|
+
if (effectiveName === DEFAULT_IMAGE_NAME2) {
|
|
4621
|
+
console.warn(
|
|
4622
|
+
`Building sandbox image with the default name "${DEFAULT_IMAGE_NAME2}". Pass \`registeredName\` or \`Image({ name })\` to avoid collisions with other default-named images in this project.`
|
|
4825
4623
|
);
|
|
4826
4624
|
}
|
|
4827
|
-
const text = await response.text();
|
|
4828
|
-
return text ? JSON.parse(text) : {};
|
|
4829
|
-
}
|
|
4830
|
-
async function resolveBuildContext(context) {
|
|
4831
4625
|
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
4832
4626
|
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
|
-
);
|
|
4627
|
+
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT credentials.");
|
|
4862
4628
|
}
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
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)}`,
|
|
4629
|
+
emit({ type: "status", message: `Building image '${effectiveName}'...` });
|
|
4630
|
+
const binding = loadNativeBinding();
|
|
4631
|
+
let emitError;
|
|
4632
|
+
const resultJson = await binding.buildSandboxImage(
|
|
4877
4633
|
{
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
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)
|
|
4634
|
+
apiUrl: context.apiUrl,
|
|
4635
|
+
bearerToken,
|
|
4636
|
+
dockerfilePath,
|
|
4637
|
+
registeredName: effectiveName,
|
|
4638
|
+
diskMb: options.diskMb,
|
|
4639
|
+
builderDiskMb: options.builderDiskMb,
|
|
4640
|
+
cpus: options.cpus,
|
|
4641
|
+
memoryMb: options.memoryMb,
|
|
4642
|
+
isPublic: options.isPublic ?? false,
|
|
4643
|
+
organizationId: context.organizationId,
|
|
4644
|
+
projectId: context.projectId,
|
|
4645
|
+
namespace: context.namespace,
|
|
4646
|
+
useScopeHeaders: context.personalAccessToken != null && context.apiKey == null,
|
|
4647
|
+
userAgent: void 0,
|
|
4648
|
+
dockerfileText,
|
|
4649
|
+
contextDir: nativeContextDir
|
|
4926
4650
|
},
|
|
4927
|
-
|
|
4928
|
-
|
|
4929
|
-
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
|
|
4933
|
-
try {
|
|
4934
|
-
const content = await 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
|
-
}
|
|
5080
|
-
async function runChecked(sandbox, command, args, env, workingDir, user) {
|
|
5081
|
-
const options = {
|
|
5082
|
-
args,
|
|
5083
|
-
env,
|
|
5084
|
-
workingDir
|
|
5085
|
-
};
|
|
5086
|
-
if (user != null) {
|
|
5087
|
-
options.user = user;
|
|
5088
|
-
}
|
|
5089
|
-
const result = await sandbox.run(command, options);
|
|
5090
|
-
if (result.exitCode !== 0) {
|
|
5091
|
-
throw new Error(
|
|
5092
|
-
`Command '${command} ${args.join(" ")}' failed with exit code ${result.exitCode}`
|
|
5093
|
-
);
|
|
5094
|
-
}
|
|
5095
|
-
return result;
|
|
5096
|
-
}
|
|
5097
|
-
async function runStreaming(sandbox, emit, sleep3, command, args = [], env, workingDir) {
|
|
5098
|
-
const proc = await sandbox.startProcess(command, {
|
|
5099
|
-
args,
|
|
5100
|
-
env,
|
|
5101
|
-
workingDir,
|
|
5102
|
-
user: ROOTFS_BUILDER_PROCESS_USER
|
|
5103
|
-
});
|
|
5104
|
-
let stdoutSeen = 0;
|
|
5105
|
-
let stderrSeen = 0;
|
|
5106
|
-
let info;
|
|
5107
|
-
while (true) {
|
|
5108
|
-
const stdoutResp = await sandbox.getStdout(proc.pid);
|
|
5109
|
-
emitOutputLines(emit, "stdout", stdoutResp, stdoutSeen);
|
|
5110
|
-
stdoutSeen = stdoutResp.lines.length;
|
|
5111
|
-
const stderrResp = await sandbox.getStderr(proc.pid);
|
|
5112
|
-
emitOutputLines(emit, "stderr", stderrResp, stderrSeen);
|
|
5113
|
-
stderrSeen = stderrResp.lines.length;
|
|
5114
|
-
info = await sandbox.getProcess(proc.pid);
|
|
5115
|
-
if (info.status !== "running" /* RUNNING */) {
|
|
5116
|
-
const finalStdout = await sandbox.getStdout(proc.pid);
|
|
5117
|
-
emitOutputLines(emit, "stdout", finalStdout, stdoutSeen);
|
|
5118
|
-
stdoutSeen = finalStdout.lines.length;
|
|
5119
|
-
const finalStderr = await sandbox.getStderr(proc.pid);
|
|
5120
|
-
emitOutputLines(emit, "stderr", finalStderr, stderrSeen);
|
|
5121
|
-
break;
|
|
5122
|
-
}
|
|
5123
|
-
await sleep3(300);
|
|
5124
|
-
}
|
|
5125
|
-
for (let i = 0; i < 10; i++) {
|
|
5126
|
-
if (info.exitCode != null || info.signal != null) {
|
|
5127
|
-
break;
|
|
4651
|
+
(event) => {
|
|
4652
|
+
try {
|
|
4653
|
+
emit(eventToEmitDict(event));
|
|
4654
|
+
} catch (error) {
|
|
4655
|
+
emitError ??= error;
|
|
4656
|
+
}
|
|
5128
4657
|
}
|
|
5129
|
-
await sleep3(200);
|
|
5130
|
-
info = await sandbox.getProcess(proc.pid);
|
|
5131
|
-
}
|
|
5132
|
-
const exitCode = info.exitCode != null ? info.exitCode : info.signal != null ? -info.signal : 0;
|
|
5133
|
-
if (exitCode !== 0) {
|
|
5134
|
-
throw new Error(
|
|
5135
|
-
`Command '${command} ${args.join(" ")}' failed with exit code ${exitCode}`
|
|
5136
|
-
);
|
|
5137
|
-
}
|
|
5138
|
-
}
|
|
5139
|
-
function emitOutputLines(emit, stream, response, seen) {
|
|
5140
|
-
for (const line of response.lines.slice(seen)) {
|
|
5141
|
-
emit({ type: "build_log", stream, message: line });
|
|
5142
|
-
}
|
|
5143
|
-
}
|
|
5144
|
-
async function ensureRemoteBuildRoot(sandbox) {
|
|
5145
|
-
await runChecked(
|
|
5146
|
-
sandbox,
|
|
5147
|
-
"mkdir",
|
|
5148
|
-
["-p", REMOTE_BUILD_DIR],
|
|
5149
|
-
void 0,
|
|
5150
|
-
void 0,
|
|
5151
|
-
ROOTFS_BUILDER_PROCESS_USER
|
|
5152
4658
|
);
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
"chmod",
|
|
5156
|
-
["0777", REMOTE_BUILD_DIR],
|
|
5157
|
-
void 0,
|
|
5158
|
-
void 0,
|
|
5159
|
-
ROOTFS_BUILDER_PROCESS_USER
|
|
5160
|
-
);
|
|
5161
|
-
}
|
|
5162
|
-
async function ensureRemoteDirectory(sandbox, remotePath) {
|
|
5163
|
-
await runChecked(sandbox, "mkdir", ["-p", remotePath]);
|
|
5164
|
-
}
|
|
5165
|
-
async function ensureRemoteParentDir(sandbox, remotePath) {
|
|
5166
|
-
await ensureRemoteDirectory(sandbox, path.posix.dirname(remotePath));
|
|
5167
|
-
}
|
|
5168
|
-
async function copyLocalPathToSandbox(sandbox, localPath, remotePath) {
|
|
5169
|
-
const fileStats = await stat(localPath).catch(() => null);
|
|
5170
|
-
if (!fileStats) {
|
|
5171
|
-
throw new Error(`Local path not found: ${localPath}`);
|
|
5172
|
-
}
|
|
5173
|
-
if (fileStats.isFile()) {
|
|
5174
|
-
await ensureRemoteParentDir(sandbox, remotePath);
|
|
5175
|
-
await sandbox.writeFile(remotePath, await readFile(localPath));
|
|
5176
|
-
return;
|
|
5177
|
-
}
|
|
5178
|
-
if (!fileStats.isDirectory()) {
|
|
5179
|
-
throw new Error(`Local path not found: ${localPath}`);
|
|
5180
|
-
}
|
|
5181
|
-
const entries = await readdir(localPath, { withFileTypes: true });
|
|
5182
|
-
for (const entry of entries) {
|
|
5183
|
-
const sourcePath = path.join(localPath, entry.name);
|
|
5184
|
-
const destinationPath = path.posix.join(remotePath, entry.name);
|
|
5185
|
-
if (entry.isDirectory()) {
|
|
5186
|
-
await ensureRemoteDirectory(sandbox, destinationPath);
|
|
5187
|
-
await copyLocalPathToSandbox(sandbox, sourcePath, destinationPath);
|
|
5188
|
-
} else if (entry.isFile()) {
|
|
5189
|
-
await ensureRemoteParentDir(sandbox, destinationPath);
|
|
5190
|
-
await sandbox.writeFile(destinationPath, await readFile(sourcePath));
|
|
5191
|
-
}
|
|
5192
|
-
}
|
|
5193
|
-
}
|
|
5194
|
-
async function registerImage(context, name, dockerfile, snapshotId, snapshotSandboxId, snapshotUri, snapshotSizeBytes, rootfsDiskBytes2, isPublic, snapshotFormatVersion) {
|
|
5195
|
-
const bearerToken = context.apiKey ?? context.personalAccessToken;
|
|
5196
|
-
if (!bearerToken) {
|
|
5197
|
-
throw new Error("Missing TENSORLAKE_API_KEY or TENSORLAKE_PAT.");
|
|
4659
|
+
if (emitError != null) {
|
|
4660
|
+
throw emitError;
|
|
5198
4661
|
}
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
"Content-Type": "application/json"
|
|
5203
|
-
};
|
|
5204
|
-
let url;
|
|
5205
|
-
if (context.apiKey) {
|
|
5206
|
-
url = `${baseUrl}/platform/v1/sandbox-templates`;
|
|
5207
|
-
} else {
|
|
5208
|
-
if (!context.organizationId || !context.projectId) {
|
|
5209
|
-
throw new Error(
|
|
5210
|
-
"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."
|
|
5211
|
-
);
|
|
5212
|
-
}
|
|
5213
|
-
url = `${baseUrl}/platform/v1/organizations/${encodeURIComponent(context.organizationId)}/projects/${encodeURIComponent(context.projectId)}/sandbox-templates`;
|
|
5214
|
-
headers["X-Forwarded-Organization-Id"] = context.organizationId;
|
|
5215
|
-
headers["X-Forwarded-Project-Id"] = context.projectId;
|
|
5216
|
-
}
|
|
5217
|
-
const response = await fetch(url, {
|
|
5218
|
-
method: "POST",
|
|
5219
|
-
headers,
|
|
5220
|
-
body: JSON.stringify({
|
|
5221
|
-
name,
|
|
5222
|
-
dockerfile,
|
|
5223
|
-
snapshotId,
|
|
5224
|
-
snapshotSandboxId,
|
|
5225
|
-
snapshotUri,
|
|
5226
|
-
...snapshotFormatVersion ? { snapshotFormatVersion } : {},
|
|
5227
|
-
snapshotSizeBytes,
|
|
5228
|
-
rootfsDiskBytes: rootfsDiskBytes2,
|
|
5229
|
-
public: isPublic
|
|
5230
|
-
})
|
|
5231
|
-
});
|
|
5232
|
-
if (!response.ok) {
|
|
5233
|
-
throw new Error(
|
|
5234
|
-
`${response.status} ${response.statusText}: ${await response.text()}`
|
|
5235
|
-
);
|
|
4662
|
+
let result = {};
|
|
4663
|
+
if (resultJson.trim().length > 0) {
|
|
4664
|
+
result = JSON.parse(resultJson);
|
|
5236
4665
|
}
|
|
5237
|
-
const text = await response.text();
|
|
5238
|
-
return text ? JSON.parse(text) : {};
|
|
5239
|
-
}
|
|
5240
|
-
async function createSandboxImage(source, options = {}, deps = {}) {
|
|
5241
|
-
const emit = deps.emit ?? (options.verbose ? stderrEmit : noopEmit);
|
|
5242
|
-
const sleep3 = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
5243
|
-
const context = buildContextFromEnv();
|
|
5244
|
-
const clientFactory = deps.createClient ?? createDefaultClient;
|
|
5245
|
-
const sourceLabel = typeof source === "string" ? source : `Image(${source.name})`;
|
|
5246
|
-
emit({ type: "status", message: `Loading ${sourceLabel}...` });
|
|
5247
|
-
const plan = typeof source === "string" ? await loadDockerfilePlan(source, options.registeredName) : loadImagePlan(source, options);
|
|
5248
4666
|
emit({
|
|
5249
|
-
type: "
|
|
5250
|
-
|
|
4667
|
+
type: "image_registered",
|
|
4668
|
+
name: effectiveName,
|
|
4669
|
+
image_id: typeof result.id === "string" && result.id || typeof result.templateId === "string" && result.templateId || ""
|
|
5251
4670
|
});
|
|
5252
|
-
emit({ type: "
|
|
5253
|
-
|
|
5254
|
-
const { prepared, spec: preparedSpec } = await prepareRootfsBuild(
|
|
5255
|
-
resolvedContext,
|
|
5256
|
-
plan,
|
|
5257
|
-
options.isPublic ?? false
|
|
5258
|
-
);
|
|
5259
|
-
emit({
|
|
5260
|
-
type: "status",
|
|
5261
|
-
message: prepared.rootfsNodeKind === "diff" ? "Build mode: RootfsDiff" : "Build mode: RootfsBase"
|
|
5262
|
-
});
|
|
5263
|
-
const client = clientFactory(context);
|
|
5264
|
-
let sandbox;
|
|
5265
|
-
try {
|
|
5266
|
-
const outputRootfsDiskBytes = rootfsDiskBytes(options.diskMb, prepared);
|
|
5267
|
-
const builderDiskMb = Math.max(
|
|
5268
|
-
rootfsDiskBytesToMb(outputRootfsDiskBytes),
|
|
5269
|
-
options.builderDiskMb ?? prepared.builder.diskMb
|
|
5270
|
-
);
|
|
5271
|
-
emit({
|
|
5272
|
-
type: "status",
|
|
5273
|
-
message: `Creating rootfs builder sandbox from ${prepared.builder.image}...`
|
|
5274
|
-
});
|
|
5275
|
-
sandbox = await client.createAndConnect({
|
|
5276
|
-
image: prepared.builder.image,
|
|
5277
|
-
cpus: options.cpus ?? prepared.builder.cpus,
|
|
5278
|
-
memoryMb: options.memoryMb ?? prepared.builder.memoryMb,
|
|
5279
|
-
diskMb: builderDiskMb
|
|
5280
|
-
});
|
|
5281
|
-
emit({
|
|
5282
|
-
type: "status",
|
|
5283
|
-
message: `Rootfs builder sandbox ${sandbox.sandboxId} is running`
|
|
5284
|
-
});
|
|
5285
|
-
emit({ type: "status", message: "Uploading build context..." });
|
|
5286
|
-
await ensureRemoteBuildRoot(sandbox);
|
|
5287
|
-
await copyLocalPathToSandbox(sandbox, plan.contextDir, REMOTE_CONTEXT_DIR);
|
|
5288
|
-
const spec = await buildRootfsSpec(
|
|
5289
|
-
preparedSpec,
|
|
5290
|
-
prepared,
|
|
5291
|
-
plan,
|
|
5292
|
-
options.diskMb
|
|
5293
|
-
);
|
|
5294
|
-
await ensureRemoteParentDir(sandbox, REMOTE_SPEC_PATH);
|
|
5295
|
-
await sandbox.writeFile(
|
|
5296
|
-
REMOTE_SPEC_PATH,
|
|
5297
|
-
new TextEncoder().encode(JSON.stringify(spec, null, 2))
|
|
5298
|
-
);
|
|
5299
|
-
emit({ type: "status", message: "Running offline rootfs builder..." });
|
|
5300
|
-
await runRootfsBuilder(sandbox, prepared.builder.command, emit, sleep3);
|
|
5301
|
-
const metadataBytes = await sandbox.readFile(REMOTE_METADATA_PATH);
|
|
5302
|
-
const metadata = JSON.parse(
|
|
5303
|
-
new TextDecoder().decode(metadataBytes)
|
|
5304
|
-
);
|
|
5305
|
-
const completeRequest = completeRequestFromMetadata(prepared, metadata);
|
|
5306
|
-
emit({ type: "status", message: "Completing image registration..." });
|
|
5307
|
-
const result = await completeRootfsBuild(
|
|
5308
|
-
resolvedContext,
|
|
5309
|
-
prepared.buildId,
|
|
5310
|
-
completeRequest
|
|
5311
|
-
);
|
|
5312
|
-
emit({
|
|
5313
|
-
type: "image_registered",
|
|
5314
|
-
name: plan.registeredName,
|
|
5315
|
-
image_id: typeof result.id === "string" && result.id || typeof result.templateId === "string" && result.templateId || ""
|
|
5316
|
-
});
|
|
5317
|
-
emit({ type: "done" });
|
|
5318
|
-
return result;
|
|
5319
|
-
} finally {
|
|
5320
|
-
if (sandbox) {
|
|
5321
|
-
try {
|
|
5322
|
-
await sandbox.terminate();
|
|
5323
|
-
} catch {
|
|
5324
|
-
}
|
|
5325
|
-
}
|
|
5326
|
-
client.close();
|
|
5327
|
-
}
|
|
4671
|
+
emit({ type: "done" });
|
|
4672
|
+
return result;
|
|
5328
4673
|
}
|
|
5329
4674
|
async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
5330
4675
|
const parsed = parseArgs({
|
|
@@ -5341,7 +4686,9 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5341
4686
|
});
|
|
5342
4687
|
const dockerfilePath = parsed.positionals[0];
|
|
5343
4688
|
if (!dockerfilePath) {
|
|
5344
|
-
throw new Error(
|
|
4689
|
+
throw new Error(
|
|
4690
|
+
"Usage: tensorlake-create-sandbox-image <dockerfile_path> [--name NAME] [--cpus N] [--memory MB] [--disk_mb MB] [--builder_disk_mb MB] [--public]"
|
|
4691
|
+
);
|
|
5345
4692
|
}
|
|
5346
4693
|
const cpus = parsed.values.cpus != null ? Number(parsed.values.cpus) : void 0;
|
|
5347
4694
|
const memoryMb = parsed.values.memory != null ? Number(parsed.values.memory) : void 0;
|
|
@@ -5374,28 +4721,12 @@ async function runCreateSandboxImageCli(argv = process.argv.slice(2)) {
|
|
|
5374
4721
|
{ emit: ndjsonStdoutEmit }
|
|
5375
4722
|
);
|
|
5376
4723
|
}
|
|
5377
|
-
var
|
|
4724
|
+
var cachedNativeBinding, cachedNativeBindingError, DEFAULT_IMAGE_NAME2;
|
|
5378
4725
|
var init_sandbox_image = __esm({
|
|
5379
4726
|
"src/sandbox-image.ts"() {
|
|
5380
4727
|
"use strict";
|
|
5381
|
-
init_models();
|
|
5382
|
-
init_client();
|
|
5383
4728
|
init_image();
|
|
5384
|
-
|
|
5385
|
-
REMOTE_BUILD_DIR = "/var/lib/tensorlake/rootfs-builder/build";
|
|
5386
|
-
REMOTE_CONTEXT_DIR = "/var/lib/tensorlake/rootfs-builder/build/context";
|
|
5387
|
-
REMOTE_SPEC_PATH = "/var/lib/tensorlake/rootfs-builder/build/spec.json";
|
|
5388
|
-
REMOTE_METADATA_PATH = "/var/lib/tensorlake/rootfs-builder/build/metadata.json";
|
|
5389
|
-
ROOTFS_BUILDER_BIN_DIR = "/usr/local/bin";
|
|
5390
|
-
ROOTFS_BUILDER_PATH = "/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
5391
|
-
ROOTFS_BUILDER_COMMAND = "tl-rootfs-build";
|
|
5392
|
-
ROOTFS_BUILDER_PROCESS_USER = "root";
|
|
5393
|
-
UNSUPPORTED_DOCKERFILE_INSTRUCTIONS = /* @__PURE__ */ new Set([
|
|
5394
|
-
"ARG",
|
|
5395
|
-
"ONBUILD",
|
|
5396
|
-
"SHELL",
|
|
5397
|
-
"USER"
|
|
5398
|
-
]);
|
|
4729
|
+
DEFAULT_IMAGE_NAME2 = "default";
|
|
5399
4730
|
}
|
|
5400
4731
|
});
|
|
5401
4732
|
|