trainfabric 0.1.24 → 0.1.25
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/index.cjs +115 -30
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -7613,6 +7613,7 @@ var runCreateSchema = external_exports.object({
|
|
|
7613
7613
|
repoUrl: external_exports.string().min(1).optional(),
|
|
7614
7614
|
branch: external_exports.string().min(1).optional(),
|
|
7615
7615
|
commitSha: external_exports.string().min(1).optional(),
|
|
7616
|
+
entrypoint: external_exports.string().min(1).optional(),
|
|
7616
7617
|
rootDirName: external_exports.string().min(1).optional(),
|
|
7617
7618
|
files: external_exports.array(external_exports.object({
|
|
7618
7619
|
path: external_exports.string().min(1),
|
|
@@ -7638,6 +7639,7 @@ var runtimeDetectSchema = external_exports.object({
|
|
|
7638
7639
|
repoUrl: external_exports.string().min(1).optional(),
|
|
7639
7640
|
branch: external_exports.string().min(1).optional(),
|
|
7640
7641
|
commitSha: external_exports.string().min(1).optional(),
|
|
7642
|
+
entrypoint: external_exports.string().min(1).optional(),
|
|
7641
7643
|
rootDirName: external_exports.string().min(1).optional(),
|
|
7642
7644
|
files: external_exports.array(external_exports.object({
|
|
7643
7645
|
path: external_exports.string().min(1),
|
|
@@ -7661,6 +7663,7 @@ var runQuoteSchema = external_exports.object({
|
|
|
7661
7663
|
repoUrl: external_exports.string().min(1).optional(),
|
|
7662
7664
|
branch: external_exports.string().min(1).optional(),
|
|
7663
7665
|
commitSha: external_exports.string().min(1).optional(),
|
|
7666
|
+
entrypoint: external_exports.string().min(1).optional(),
|
|
7664
7667
|
rootDirName: external_exports.string().min(1).optional(),
|
|
7665
7668
|
files: external_exports.array(external_exports.object({
|
|
7666
7669
|
path: external_exports.string().min(1),
|
|
@@ -8817,10 +8820,14 @@ var import_node_fs = __toESM(require("node:fs"), 1);
|
|
|
8817
8820
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
8818
8821
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
8819
8822
|
var idPatterns = {
|
|
8823
|
+
dataset: /^ds_[A-Za-z0-9_-]+$/,
|
|
8824
|
+
deployment: /^dep_[A-Za-z0-9_-]+$/,
|
|
8825
|
+
model: /^mdl_[A-Za-z0-9_-]+$/,
|
|
8820
8826
|
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8821
8827
|
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8822
8828
|
run: /^run_[A-Za-z0-9_-]+$/
|
|
8823
8829
|
};
|
|
8830
|
+
var allowedBillingUnits = /* @__PURE__ */ new Set(["normalized_tflop_seconds", "reserved_gpu_seconds", "usd"]);
|
|
8824
8831
|
function collectRuntimeFiles(repoPath) {
|
|
8825
8832
|
const manifestFiles = /* @__PURE__ */ new Set([
|
|
8826
8833
|
"training.yaml",
|
|
@@ -8868,12 +8875,56 @@ function collectRuntimeFiles(repoPath) {
|
|
|
8868
8875
|
walk(repoPath);
|
|
8869
8876
|
return found.sort((left, right) => left.path.localeCompare(right.path));
|
|
8870
8877
|
}
|
|
8878
|
+
function inferEntrypoint(files) {
|
|
8879
|
+
const trainingYaml = files.find((file) => file.path === "training.yaml" || file.path === "train.runtime.yaml");
|
|
8880
|
+
if (trainingYaml) {
|
|
8881
|
+
const match = trainingYaml.content.match(/entrypoint\s*[:=]\s*["']?([A-Za-z0-9_./-]+)["']?/i);
|
|
8882
|
+
if (match?.[1]) {
|
|
8883
|
+
const entry = match[1];
|
|
8884
|
+
const exact = files.find((file) => file.path === entry);
|
|
8885
|
+
if (exact) {
|
|
8886
|
+
return exact.path;
|
|
8887
|
+
}
|
|
8888
|
+
const nested = files.find((file) => file.path.endsWith(`/${entry}`));
|
|
8889
|
+
if (nested) {
|
|
8890
|
+
return nested.path;
|
|
8891
|
+
}
|
|
8892
|
+
return entry;
|
|
8893
|
+
}
|
|
8894
|
+
}
|
|
8895
|
+
return files.find((file) => /(^|\/)train\.(py|sh)$/i.test(file.path))?.path;
|
|
8896
|
+
}
|
|
8897
|
+
function validateGitUrl(value) {
|
|
8898
|
+
const url = new URL(value);
|
|
8899
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "ssh:" && url.protocol !== "git:") {
|
|
8900
|
+
throw new Error(`Invalid git URL "${value}". Use an absolute repository URL.`);
|
|
8901
|
+
}
|
|
8902
|
+
return value;
|
|
8903
|
+
}
|
|
8904
|
+
function validateRepoPath(repoPath) {
|
|
8905
|
+
const absolute = import_node_path2.default.resolve(repoPath);
|
|
8906
|
+
const parsed = import_node_path2.default.parse(absolute);
|
|
8907
|
+
if (absolute === parsed.root || absolute === import_node_os.default.homedir()) {
|
|
8908
|
+
throw new Error(`Refusing to package unsafe repository path: ${repoPath}`);
|
|
8909
|
+
}
|
|
8910
|
+
const stat = import_node_fs.default.statSync(absolute);
|
|
8911
|
+
if (!stat.isDirectory()) {
|
|
8912
|
+
throw new Error(`Repository path must be a directory: ${repoPath}`);
|
|
8913
|
+
}
|
|
8914
|
+
return absolute;
|
|
8915
|
+
}
|
|
8871
8916
|
function buildSourceOptions(options) {
|
|
8917
|
+
if (options.repo && options.git) {
|
|
8918
|
+
throw new Error("Use either --repo or --git, not both.");
|
|
8919
|
+
}
|
|
8872
8920
|
if (options.repo) {
|
|
8921
|
+
const repoPath = validateRepoPath(options.repo);
|
|
8922
|
+
const files = collectRuntimeFiles(repoPath);
|
|
8873
8923
|
return {
|
|
8874
8924
|
source: {
|
|
8875
8925
|
kind: "inline",
|
|
8876
|
-
|
|
8926
|
+
entrypoint: inferEntrypoint(files),
|
|
8927
|
+
files
|
|
8877
8928
|
}
|
|
8878
8929
|
};
|
|
8879
8930
|
}
|
|
@@ -8881,7 +8932,7 @@ function buildSourceOptions(options) {
|
|
|
8881
8932
|
return {
|
|
8882
8933
|
source: {
|
|
8883
8934
|
kind: "git",
|
|
8884
|
-
repoUrl: options.git,
|
|
8935
|
+
repoUrl: validateGitUrl(options.git),
|
|
8885
8936
|
branch: options.branch
|
|
8886
8937
|
}
|
|
8887
8938
|
};
|
|
@@ -8915,7 +8966,7 @@ function parseApiKeyScopes(input) {
|
|
|
8915
8966
|
if (invalidScope) {
|
|
8916
8967
|
throw new Error(`Invalid API key scope: ${invalidScope}`);
|
|
8917
8968
|
}
|
|
8918
|
-
return scopes;
|
|
8969
|
+
return [...new Set(scopes)];
|
|
8919
8970
|
}
|
|
8920
8971
|
function normalizeHumanName(value, label) {
|
|
8921
8972
|
let name = String(value ?? "").trim();
|
|
@@ -8940,6 +8991,16 @@ function normalizeOptionalId(value, kind, label) {
|
|
|
8940
8991
|
}
|
|
8941
8992
|
return normalizeId(value, kind, label);
|
|
8942
8993
|
}
|
|
8994
|
+
function normalizeOptionalBillingUnit(value) {
|
|
8995
|
+
if (value === void 0) {
|
|
8996
|
+
return void 0;
|
|
8997
|
+
}
|
|
8998
|
+
const unit = String(value).trim();
|
|
8999
|
+
if (!allowedBillingUnits.has(unit)) {
|
|
9000
|
+
throw new Error("Billing unit is invalid. Use normalized_tflop_seconds, reserved_gpu_seconds, or usd.");
|
|
9001
|
+
}
|
|
9002
|
+
return unit;
|
|
9003
|
+
}
|
|
8943
9004
|
|
|
8944
9005
|
// src/run_input.ts
|
|
8945
9006
|
var BASE_MODEL_ALIASES = {
|
|
@@ -8982,7 +9043,7 @@ function buildComputeSpec(options) {
|
|
|
8982
9043
|
|
|
8983
9044
|
// src/index.ts
|
|
8984
9045
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
8985
|
-
var CLI_VERSION = "0.1.
|
|
9046
|
+
var CLI_VERSION = "0.1.25";
|
|
8986
9047
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
8987
9048
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
8988
9049
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9379,6 +9440,16 @@ function filterQuoteBundleForRequestedMode(bundle, options) {
|
|
|
9379
9440
|
quotes: bundle.quotes.filter((item) => item.mode === options.mode || item.quote.mode === options.mode)
|
|
9380
9441
|
};
|
|
9381
9442
|
}
|
|
9443
|
+
function redactInlineSource(value) {
|
|
9444
|
+
return JSON.parse(
|
|
9445
|
+
JSON.stringify(value, (_key, current) => {
|
|
9446
|
+
if (current && typeof current === "object" && typeof current.path === "string" && typeof current.content === "string") {
|
|
9447
|
+
return { ...current, content: "<redacted>" };
|
|
9448
|
+
}
|
|
9449
|
+
return current;
|
|
9450
|
+
})
|
|
9451
|
+
);
|
|
9452
|
+
}
|
|
9382
9453
|
async function watchRun(runId, json = false, timeoutMs, pollMs) {
|
|
9383
9454
|
const handle = await createClient(loadConfig()).runs.watch(runId);
|
|
9384
9455
|
if (json) {
|
|
@@ -9595,11 +9666,12 @@ program2.command("datasets:list").option("--project <projectId>").description("L
|
|
|
9595
9666
|
printJson(await createClient(loadConfig()).datasets.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9596
9667
|
});
|
|
9597
9668
|
program2.command("datasets:get").argument("<datasetId>").description("Fetch dataset detail").action(async (datasetId) => {
|
|
9598
|
-
printJson(await createClient(loadConfig()).datasets.get(
|
|
9669
|
+
printJson(await createClient(loadConfig()).datasets.get(normalizeId(datasetId, "dataset", "Dataset ID")));
|
|
9599
9670
|
});
|
|
9600
9671
|
program2.command("datasets:delete").argument("<datasetId>").description("Delete a dataset").action(async (datasetId) => {
|
|
9601
|
-
|
|
9602
|
-
|
|
9672
|
+
const normalizedDatasetId = normalizeId(datasetId, "dataset", "Dataset ID");
|
|
9673
|
+
await createClient(loadConfig()).datasets.delete(normalizedDatasetId);
|
|
9674
|
+
printJson({ deleted: true, id: normalizedDatasetId });
|
|
9603
9675
|
});
|
|
9604
9676
|
program2.command("datasets:upload").argument("<file>").option("--project <projectId>").option("--name <name>").description("Upload a dataset through upload sessions").action(async (file, options) => {
|
|
9605
9677
|
assertReadableFile(file, "Dataset path");
|
|
@@ -9629,12 +9701,11 @@ program2.command("runtime:detect").option("--project <projectId>").option("--rep
|
|
|
9629
9701
|
throw new Error("Provide --repo or --git.");
|
|
9630
9702
|
}
|
|
9631
9703
|
const config = loadConfig();
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
);
|
|
9704
|
+
const detection = await createClient(config).runtime.detect({
|
|
9705
|
+
projectId: requireProjectId(options, config),
|
|
9706
|
+
source: sourceOptions.source
|
|
9707
|
+
});
|
|
9708
|
+
printJson(redactInlineSource(detection));
|
|
9638
9709
|
});
|
|
9639
9710
|
program2.command("runtime:build").option("--project <projectId>").requiredOption("--detection <detectionId>").description("Build or reuse a cached runtime image from a runtime detection").action(async (options) => {
|
|
9640
9711
|
const config = loadConfig();
|
|
@@ -9662,24 +9733,25 @@ program2.command("runs:list").option("--project <projectId>").description("List
|
|
|
9662
9733
|
printJson(await createClient(loadConfig()).runs.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9663
9734
|
});
|
|
9664
9735
|
program2.command("runs:status").argument("<runId>").description("Fetch full run detail").action(async (runId) => {
|
|
9665
|
-
printJson(await createClient(loadConfig()).runs.get(
|
|
9736
|
+
printJson(await createClient(loadConfig()).runs.get(normalizeId(runId, "run", "Run ID")));
|
|
9666
9737
|
});
|
|
9667
9738
|
program2.command("runs:logs").argument("<runId>").description("Fetch run logs").action(async (runId) => {
|
|
9668
|
-
printJson(await createClient(loadConfig()).runs.logs(
|
|
9739
|
+
printJson(await createClient(loadConfig()).runs.logs(normalizeId(runId, "run", "Run ID")));
|
|
9669
9740
|
});
|
|
9670
9741
|
program2.command("runs:wait").argument("<runId>").option("--timeout <duration>", "maximum wait duration, for example 30s, 10m, or 1h").option("--poll <duration>", "poll interval, for example 1s or 500ms").description("Wait for a run to reach a terminal state").action(async (runId, options) => {
|
|
9742
|
+
const normalizedRunId = normalizeId(runId, "run", "Run ID");
|
|
9671
9743
|
printJson(
|
|
9672
|
-
await createClient(loadConfig()).runs.wait(
|
|
9744
|
+
await createClient(loadConfig()).runs.wait(normalizedRunId, {
|
|
9673
9745
|
timeoutMs: parseDurationMs(options.timeout),
|
|
9674
9746
|
pollMs: parsePositiveMs(options.poll, 1e3)
|
|
9675
9747
|
})
|
|
9676
9748
|
);
|
|
9677
9749
|
});
|
|
9678
9750
|
program2.command("runs:watch").argument("<runId>").option("--json", "emit event output as JSON").option("--timeout <duration>", "maximum watch duration, for example 30s, 10m, or 1h").option("--poll <duration>", "poll interval, for example 1s or 500ms").description("Stream a run until it completes").action(async (runId, options) => {
|
|
9679
|
-
await watchRun(
|
|
9751
|
+
await watchRun(normalizeId(runId, "run", "Run ID"), Boolean(options.json), parseDurationMs(options.timeout), parsePositiveMs(options.poll, 1e3));
|
|
9680
9752
|
});
|
|
9681
9753
|
program2.command("runs:usage").argument("<runId>").option("--summary", "print a human-readable cost summary instead of raw JSON").description("Fetch run usage summary").action(async (runId, options) => {
|
|
9682
|
-
const usage = await createClient(loadConfig()).runs.usage(
|
|
9754
|
+
const usage = await createClient(loadConfig()).runs.usage(normalizeId(runId, "run", "Run ID"));
|
|
9683
9755
|
if (options.summary) {
|
|
9684
9756
|
printUsageSummary(usage);
|
|
9685
9757
|
} else {
|
|
@@ -9687,7 +9759,7 @@ program2.command("runs:usage").argument("<runId>").option("--summary", "print a
|
|
|
9687
9759
|
}
|
|
9688
9760
|
});
|
|
9689
9761
|
program2.command("runs:cost-breakdown").argument("<runId>").option("--summary", "print a human-readable cost summary instead of raw JSON").description("Fetch realized run cost and profitability").action(async (runId, options) => {
|
|
9690
|
-
const breakdown = await createClient(loadConfig()).runs.costBreakdown(
|
|
9762
|
+
const breakdown = await createClient(loadConfig()).runs.costBreakdown(normalizeId(runId, "run", "Run ID"));
|
|
9691
9763
|
if (options.summary) {
|
|
9692
9764
|
printUsageSummary(breakdown);
|
|
9693
9765
|
} else {
|
|
@@ -9695,22 +9767,22 @@ program2.command("runs:cost-breakdown").argument("<runId>").option("--summary",
|
|
|
9695
9767
|
}
|
|
9696
9768
|
});
|
|
9697
9769
|
program2.command("runs:allocation").argument("<runId>").description("Fetch run allocation").action(async (runId) => {
|
|
9698
|
-
printJson(await createClient(loadConfig()).runs.allocation(
|
|
9770
|
+
printJson(await createClient(loadConfig()).runs.allocation(normalizeId(runId, "run", "Run ID")));
|
|
9699
9771
|
});
|
|
9700
9772
|
program2.command("runs:configuration").argument("<runId>").description("Fetch run placement configuration").action(async (runId) => {
|
|
9701
|
-
printJson(await createClient(loadConfig()).runs.configuration(
|
|
9773
|
+
printJson(await createClient(loadConfig()).runs.configuration(normalizeId(runId, "run", "Run ID")));
|
|
9702
9774
|
});
|
|
9703
9775
|
program2.command("runs:explanation").argument("<runId>").description("Fetch run planning explanation").action(async (runId) => {
|
|
9704
|
-
printJson(await createClient(loadConfig()).runs.explanation(
|
|
9776
|
+
printJson(await createClient(loadConfig()).runs.explanation(normalizeId(runId, "run", "Run ID")));
|
|
9705
9777
|
});
|
|
9706
9778
|
program2.command("runs:cancel").argument("<runId>").description("Cancel a run").action(async (runId) => {
|
|
9707
|
-
printJson(await createClient(loadConfig()).runs.cancel(
|
|
9779
|
+
printJson(await createClient(loadConfig()).runs.cancel(normalizeId(runId, "run", "Run ID")));
|
|
9708
9780
|
});
|
|
9709
9781
|
program2.command("runs:resume").argument("<runId>").description("Resume a run from the latest checkpoint").action(async (runId) => {
|
|
9710
|
-
printJson(await createClient(loadConfig()).runs.resume(
|
|
9782
|
+
printJson(await createClient(loadConfig()).runs.resume(normalizeId(runId, "run", "Run ID")));
|
|
9711
9783
|
});
|
|
9712
9784
|
program2.command("runs:terminate").argument("<runId>").description("Terminate a queued, launchable, or active run").action(async (runId) => {
|
|
9713
|
-
printJson(await createClient(loadConfig()).runs.terminate(
|
|
9785
|
+
printJson(await createClient(loadConfig()).runs.terminate(normalizeId(runId, "run", "Run ID")));
|
|
9714
9786
|
});
|
|
9715
9787
|
program2.command("cluster:capacity").description("Fetch cluster capacity view").action(async () => {
|
|
9716
9788
|
printJson(await createClient(loadConfig()).cluster.getCapacity());
|
|
@@ -9728,20 +9800,33 @@ program2.command("deployments:list").description("List deployments").action(asyn
|
|
|
9728
9800
|
printJson(await createClient(loadConfig()).deployments.list());
|
|
9729
9801
|
});
|
|
9730
9802
|
program2.command("deployments:create").requiredOption("--model <modelId>").option("--project <projectId>", "accepted for consistency; deployment project is inferred from the model").description("Create a deployment for a model").action(async (options) => {
|
|
9731
|
-
|
|
9803
|
+
const config = loadConfig();
|
|
9804
|
+
const client = createClient(config);
|
|
9805
|
+
const projectId = normalizeOptionalId(options.project, "project", "Project ID");
|
|
9806
|
+
if (projectId) {
|
|
9807
|
+
await client.projects.get(projectId);
|
|
9808
|
+
}
|
|
9809
|
+
const modelId = normalizeId(options.model, "model", "Model ID");
|
|
9810
|
+
printJson(await client.deployments.create({ modelId }));
|
|
9732
9811
|
});
|
|
9733
9812
|
program2.command("deployments:get").argument("<deploymentId>").description("Fetch a deployment").action(async (deploymentId) => {
|
|
9734
|
-
printJson(await createClient(loadConfig()).deployments.get(
|
|
9813
|
+
printJson(await createClient(loadConfig()).deployments.get(normalizeId(deploymentId, "deployment", "Deployment ID")));
|
|
9735
9814
|
});
|
|
9736
9815
|
program2.command("deployments:delete").argument("<deploymentId>").description("Delete a pending or cataloged deployment").action(async (deploymentId) => {
|
|
9737
|
-
|
|
9738
|
-
|
|
9816
|
+
const normalizedDeploymentId = normalizeId(deploymentId, "deployment", "Deployment ID");
|
|
9817
|
+
await createClient(loadConfig()).deployments.delete(normalizedDeploymentId);
|
|
9818
|
+
printJson({ deleted: true, id: normalizedDeploymentId });
|
|
9739
9819
|
});
|
|
9740
9820
|
program2.command("billing:summary").description("Fetch billing summary").action(async () => {
|
|
9741
9821
|
printJson(await createClient(loadConfig()).billing.getSummary());
|
|
9742
9822
|
});
|
|
9743
9823
|
program2.command("billing:usage").option("--unit <unit>", "normalized_tflop_seconds, reserved_gpu_seconds, or usd").option("--run <runId>", "filter usage records to one run").description("List billing usage records").action(async (options) => {
|
|
9744
|
-
printJson(
|
|
9824
|
+
printJson(
|
|
9825
|
+
await createClient(loadConfig()).billing.getUsage(
|
|
9826
|
+
normalizeOptionalBillingUnit(options.unit),
|
|
9827
|
+
normalizeOptionalId(options.run, "run", "Run ID")
|
|
9828
|
+
)
|
|
9829
|
+
);
|
|
9745
9830
|
});
|
|
9746
9831
|
program2.command("billing:invoices").description("List invoices").action(async () => {
|
|
9747
9832
|
printJson(await createClient(loadConfig()).billing.listInvoices());
|