trainfabric 0.1.25 → 0.1.27
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 +139 -38
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8820,12 +8820,16 @@ var import_node_fs = __toESM(require("node:fs"), 1);
|
|
|
8820
8820
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
8821
8821
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
8822
8822
|
var idPatterns = {
|
|
8823
|
+
apiKey: /^key_[A-Za-z0-9_-]+$/,
|
|
8823
8824
|
dataset: /^ds_[A-Za-z0-9_-]+$/,
|
|
8824
8825
|
deployment: /^dep_[A-Za-z0-9_-]+$/,
|
|
8825
8826
|
model: /^mdl_[A-Za-z0-9_-]+$/,
|
|
8826
8827
|
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8828
|
+
pool: /^pool_[A-Za-z0-9_-]+$/,
|
|
8827
8829
|
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8828
|
-
run: /^run_[A-Za-z0-9_-]
|
|
8830
|
+
run: /^run_[A-Za-z0-9_-]+$/,
|
|
8831
|
+
serviceAccount: /^svc_[A-Za-z0-9_-]+$/,
|
|
8832
|
+
supplier: /^[A-Za-z][A-Za-z0-9_-]*$/
|
|
8829
8833
|
};
|
|
8830
8834
|
var allowedBillingUnits = /* @__PURE__ */ new Set(["normalized_tflop_seconds", "reserved_gpu_seconds", "usd"]);
|
|
8831
8835
|
function collectRuntimeFiles(repoPath) {
|
|
@@ -8961,7 +8965,10 @@ function decrypt(payload) {
|
|
|
8961
8965
|
}
|
|
8962
8966
|
function parseApiKeyScopes(input) {
|
|
8963
8967
|
const allowedScopes = new Set(apiKeyScopes);
|
|
8964
|
-
const scopes = input.split(",").map((scope) => scope.trim())
|
|
8968
|
+
const scopes = input.split(",").map((scope) => scope.trim());
|
|
8969
|
+
if (scopes.some((scope) => scope.length === 0)) {
|
|
8970
|
+
throw new Error("API key scopes cannot contain empty entries.");
|
|
8971
|
+
}
|
|
8965
8972
|
const invalidScope = scopes.find((scope) => !allowedScopes.has(scope));
|
|
8966
8973
|
if (invalidScope) {
|
|
8967
8974
|
throw new Error(`Invalid API key scope: ${invalidScope}`);
|
|
@@ -8976,6 +8983,9 @@ function normalizeHumanName(value, label) {
|
|
|
8976
8983
|
if (!/[A-Za-z0-9]/.test(name)) {
|
|
8977
8984
|
throw new Error(`${label} is required.`);
|
|
8978
8985
|
}
|
|
8986
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9 ._-]{0,79}$/.test(name) || name.includes("..")) {
|
|
8987
|
+
throw new Error(`${label} contains invalid characters. Use letters, numbers, spaces, dots, underscores, or hyphens.`);
|
|
8988
|
+
}
|
|
8979
8989
|
return name;
|
|
8980
8990
|
}
|
|
8981
8991
|
function normalizeId(value, kind, label) {
|
|
@@ -9014,36 +9024,105 @@ var BASE_MODEL_ALIASES = {
|
|
|
9014
9024
|
"qwen2.5-7b": "qwen-2.5-7b",
|
|
9015
9025
|
qwen_2_5_7b: "qwen-2.5-7b"
|
|
9016
9026
|
};
|
|
9027
|
+
var ACCELERATOR_CLASSES = /* @__PURE__ */ new Set(["a10g", "l4", "a40", "l40s", "a100", "h100", "h200", "b200"]);
|
|
9028
|
+
var PRECISION_MODES = /* @__PURE__ */ new Set(["fp16_bf16", "fp8", "fp32"]);
|
|
9029
|
+
var INTERCONNECT_TYPES = /* @__PURE__ */ new Set(["pcie", "nvlink"]);
|
|
9030
|
+
var BASE_MODELS = new Set(baseModels);
|
|
9031
|
+
var TRAINING_MODES = new Set(trainingModes);
|
|
9032
|
+
function normalizePositiveInteger(value, label, fallback) {
|
|
9033
|
+
const parsed = Number(value ?? String(fallback));
|
|
9034
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
9035
|
+
throw new Error(`${label} must be a positive integer.`);
|
|
9036
|
+
}
|
|
9037
|
+
return parsed;
|
|
9038
|
+
}
|
|
9039
|
+
function normalizeOptionalPositiveNumber(value, label) {
|
|
9040
|
+
if (value === void 0) {
|
|
9041
|
+
return void 0;
|
|
9042
|
+
}
|
|
9043
|
+
const parsed = Number(value);
|
|
9044
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
9045
|
+
throw new Error(`${label} must be a positive number.`);
|
|
9046
|
+
}
|
|
9047
|
+
return parsed;
|
|
9048
|
+
}
|
|
9049
|
+
function normalizeOptionalChoice(value, allowed, label, examples) {
|
|
9050
|
+
if (value === void 0) {
|
|
9051
|
+
return void 0;
|
|
9052
|
+
}
|
|
9053
|
+
const normalized = String(value).trim().toLowerCase();
|
|
9054
|
+
if (!allowed.has(normalized)) {
|
|
9055
|
+
throw new Error(`${label} is invalid. Use ${examples}.`);
|
|
9056
|
+
}
|
|
9057
|
+
return normalized;
|
|
9058
|
+
}
|
|
9017
9059
|
function normalizeBaseModel(model) {
|
|
9018
|
-
|
|
9060
|
+
const normalized = BASE_MODEL_ALIASES[String(model ?? "").trim().toLowerCase()];
|
|
9061
|
+
if (normalized) {
|
|
9062
|
+
return normalized;
|
|
9063
|
+
}
|
|
9064
|
+
const rawModel = String(model ?? "").trim();
|
|
9065
|
+
if (!BASE_MODELS.has(rawModel)) {
|
|
9066
|
+
throw new Error("Model is invalid. Use llama-3-8b, mistral-7b, or qwen-2.5-7b.");
|
|
9067
|
+
}
|
|
9068
|
+
return rawModel;
|
|
9069
|
+
}
|
|
9070
|
+
function normalizeTrainingMode(mode) {
|
|
9071
|
+
if (mode === void 0) {
|
|
9072
|
+
return void 0;
|
|
9073
|
+
}
|
|
9074
|
+
const normalized = String(mode).trim().toLowerCase();
|
|
9075
|
+
if (!TRAINING_MODES.has(normalized)) {
|
|
9076
|
+
throw new Error("Mode is invalid. Use efficient, balanced, or power.");
|
|
9077
|
+
}
|
|
9078
|
+
return normalized;
|
|
9079
|
+
}
|
|
9080
|
+
function normalizeEpochs(epochs) {
|
|
9081
|
+
return normalizePositiveInteger(epochs, "Epochs", 3);
|
|
9082
|
+
}
|
|
9083
|
+
function normalizeLearningRate(lr) {
|
|
9084
|
+
const parsed = Number(lr ?? "0.0002");
|
|
9085
|
+
if (!Number.isFinite(parsed) || parsed < 1e-5 || parsed > 0.01) {
|
|
9086
|
+
throw new Error("Learning rate must be a number between 0.00001 and 0.01.");
|
|
9087
|
+
}
|
|
9088
|
+
return parsed;
|
|
9019
9089
|
}
|
|
9020
9090
|
function buildComputeSpec(options) {
|
|
9021
|
-
const gpuCount =
|
|
9022
|
-
const nodeCount =
|
|
9091
|
+
const gpuCount = normalizePositiveInteger(options.gpus, "GPU count", 1);
|
|
9092
|
+
const nodeCount = normalizePositiveInteger(options.nodes, "Node count", 1);
|
|
9023
9093
|
const compute = {
|
|
9024
9094
|
gpuCount,
|
|
9025
9095
|
nodeCount,
|
|
9026
9096
|
distributionStrategy: gpuCount > 1 || nodeCount > 1 ? "ddp" : "single_gpu",
|
|
9027
9097
|
target: "local"
|
|
9028
9098
|
};
|
|
9029
|
-
|
|
9030
|
-
|
|
9099
|
+
const accelerator = normalizeOptionalChoice(
|
|
9100
|
+
options.accelerator,
|
|
9101
|
+
ACCELERATOR_CLASSES,
|
|
9102
|
+
"Accelerator",
|
|
9103
|
+
"a10g, l4, a40, l40s, a100, h100, h200, or b200"
|
|
9104
|
+
);
|
|
9105
|
+
const minMemory = normalizeOptionalPositiveNumber(options.minMemory, "Minimum GPU memory");
|
|
9106
|
+
const precision = normalizeOptionalChoice(options.precision, PRECISION_MODES, "Precision", "fp16_bf16, fp8, or fp32");
|
|
9107
|
+
const interconnect = normalizeOptionalChoice(options.interconnect, INTERCONNECT_TYPES, "Interconnect", "pcie or nvlink");
|
|
9108
|
+
if (accelerator) {
|
|
9109
|
+
compute.acceleratorClass = accelerator;
|
|
9031
9110
|
}
|
|
9032
|
-
if (
|
|
9033
|
-
compute.minGpuMemoryGb =
|
|
9111
|
+
if (minMemory !== void 0) {
|
|
9112
|
+
compute.minGpuMemoryGb = minMemory;
|
|
9034
9113
|
}
|
|
9035
|
-
if (
|
|
9036
|
-
compute.precision =
|
|
9114
|
+
if (precision) {
|
|
9115
|
+
compute.precision = precision;
|
|
9037
9116
|
}
|
|
9038
|
-
if (
|
|
9039
|
-
compute.interconnect =
|
|
9117
|
+
if (interconnect) {
|
|
9118
|
+
compute.interconnect = interconnect;
|
|
9040
9119
|
}
|
|
9041
9120
|
return compute;
|
|
9042
9121
|
}
|
|
9043
9122
|
|
|
9044
9123
|
// src/index.ts
|
|
9045
9124
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
9046
|
-
var CLI_VERSION = "0.1.
|
|
9125
|
+
var CLI_VERSION = "0.1.27";
|
|
9047
9126
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
9048
9127
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
9049
9128
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9395,15 +9474,15 @@ function buildRunInput(options, config = loadConfig()) {
|
|
|
9395
9474
|
task: "sft",
|
|
9396
9475
|
method: "lora",
|
|
9397
9476
|
baseModel: normalizeBaseModel(options.model),
|
|
9398
|
-
datasetId: options.dataset,
|
|
9399
|
-
evalDatasetId: options.eval,
|
|
9477
|
+
datasetId: normalizeId(options.dataset, "dataset", "Dataset ID"),
|
|
9478
|
+
evalDatasetId: normalizeOptionalId(options.eval, "dataset", "Eval dataset ID"),
|
|
9400
9479
|
pricingQuoteId: options.quote,
|
|
9401
9480
|
...sourceOptions,
|
|
9402
|
-
mode: options.mode,
|
|
9481
|
+
mode: normalizeTrainingMode(options.mode),
|
|
9403
9482
|
compute: buildComputeSpec(options),
|
|
9404
9483
|
hyperparameters: {
|
|
9405
|
-
epochs:
|
|
9406
|
-
lr:
|
|
9484
|
+
epochs: normalizeEpochs(options.epochs),
|
|
9485
|
+
lr: normalizeLearningRate(options.lr),
|
|
9407
9486
|
batchSize: "auto"
|
|
9408
9487
|
}
|
|
9409
9488
|
};
|
|
@@ -9441,14 +9520,28 @@ function filterQuoteBundleForRequestedMode(bundle, options) {
|
|
|
9441
9520
|
};
|
|
9442
9521
|
}
|
|
9443
9522
|
function redactInlineSource(value) {
|
|
9444
|
-
|
|
9445
|
-
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9523
|
+
const redact = (current) => {
|
|
9524
|
+
if (Array.isArray(current)) {
|
|
9525
|
+
return current.map((item) => redact(item));
|
|
9526
|
+
}
|
|
9527
|
+
if (!current || typeof current !== "object") {
|
|
9449
9528
|
return current;
|
|
9450
|
-
}
|
|
9451
|
-
|
|
9529
|
+
}
|
|
9530
|
+
const object = current;
|
|
9531
|
+
const next = {};
|
|
9532
|
+
for (const [key, child] of Object.entries(object)) {
|
|
9533
|
+
next[key] = key === "content" && typeof child === "string" ? "<redacted>" : redact(child);
|
|
9534
|
+
}
|
|
9535
|
+
return next;
|
|
9536
|
+
};
|
|
9537
|
+
return redact(value);
|
|
9538
|
+
}
|
|
9539
|
+
function normalizePositiveUsd(value, label) {
|
|
9540
|
+
const amount = Number(value);
|
|
9541
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
9542
|
+
throw new Error(`${label} must be a positive USD amount.`);
|
|
9543
|
+
}
|
|
9544
|
+
return amount;
|
|
9452
9545
|
}
|
|
9453
9546
|
async function watchRun(runId, json = false, timeoutMs, pollMs) {
|
|
9454
9547
|
const handle = await createClient(loadConfig()).runs.watch(runId);
|
|
@@ -9687,12 +9780,13 @@ program2.command("datasets:upload").argument("<file>").option("--project <projec
|
|
|
9687
9780
|
printJson(dataset);
|
|
9688
9781
|
});
|
|
9689
9782
|
program2.command("runs:create").option("--project <projectId>").requiredOption("--dataset <datasetId>").requiredOption("--model <baseModel>").option("--eval <evalDatasetId>").option("--epochs <epochs>", "number of epochs", "3").option("--lr <lr>", "learning rate", "0.0002").option("--gpus <gpuCount>", "gpu count", "1").option("--nodes <nodeCount>", "node count", "1").option("--accelerator <acceleratorClass>", "optional hard accelerator constraint (for example: a10g, a100, h100)").option("--min-memory <gigabytes>", "minimum GPU memory in GB").option("--precision <precision>", "fp16_bf16, fp8, or fp32").option("--interconnect <interconnect>", "pcie or nvlink").option("--mode <mode>", "efficient, balanced, or power", "balanced").option("--quote <pricingQuoteId>", "launch the exact accepted quote id from runs:quote").option("--repo <path>", "local repo path for runtime autodetect").option("--git <url>", "git repo URL for runtime metadata").option("--branch <branch>", "git branch for runtime metadata").option("--yes", "confirm that you reviewed pricing with runs:quote --summary and accept fluctuating realized usage").description("Create a training run").action(async (options) => {
|
|
9783
|
+
const config = loadConfig();
|
|
9784
|
+
const runInput = buildRunInput(options, config);
|
|
9690
9785
|
if (!options.yes) {
|
|
9691
9786
|
throw new Error("Refusing to launch without explicit cost acceptance. Run `trainfabric runs:quote --summary ...` first, then rerun `runs:create` with --yes.");
|
|
9692
9787
|
}
|
|
9693
|
-
const config = loadConfig();
|
|
9694
9788
|
const client = createClient(config);
|
|
9695
|
-
const run = await client.runs.create(
|
|
9789
|
+
const run = await client.runs.create(runInput);
|
|
9696
9790
|
printJson(run.snapshot);
|
|
9697
9791
|
});
|
|
9698
9792
|
program2.command("runtime:detect").option("--project <projectId>").option("--repo <path>").option("--git <url>").option("--branch <branch>").description("Detect a supported runtime from a local repo snapshot or git metadata").action(async (options) => {
|
|
@@ -9726,7 +9820,7 @@ program2.command("runs:quote").option("--project <projectId>").requiredOption("-
|
|
|
9726
9820
|
if (options.summary) {
|
|
9727
9821
|
printQuoteSummary(bundle);
|
|
9728
9822
|
} else {
|
|
9729
|
-
printJson(bundle);
|
|
9823
|
+
printJson(redactInlineSource(bundle));
|
|
9730
9824
|
}
|
|
9731
9825
|
});
|
|
9732
9826
|
program2.command("runs:list").option("--project <projectId>").description("List runs").action(async (options) => {
|
|
@@ -9794,7 +9888,7 @@ program2.command("models:list").description("List exported models").action(async
|
|
|
9794
9888
|
printJson(await createClient(loadConfig()).models.list());
|
|
9795
9889
|
});
|
|
9796
9890
|
program2.command("models:export").argument("<modelId>").description("Fetch the export package path for a model").action(async (modelId) => {
|
|
9797
|
-
printJson(await createClient(loadConfig()).models.export(
|
|
9891
|
+
printJson(await createClient(loadConfig()).models.export(normalizeId(modelId, "model", "Model ID")));
|
|
9798
9892
|
});
|
|
9799
9893
|
program2.command("deployments:list").description("List deployments").action(async () => {
|
|
9800
9894
|
printJson(await createClient(loadConfig()).deployments.list());
|
|
@@ -9807,7 +9901,12 @@ program2.command("deployments:create").requiredOption("--model <modelId>").optio
|
|
|
9807
9901
|
await client.projects.get(projectId);
|
|
9808
9902
|
}
|
|
9809
9903
|
const modelId = normalizeId(options.model, "model", "Model ID");
|
|
9810
|
-
|
|
9904
|
+
try {
|
|
9905
|
+
printJson(await client.deployments.create({ modelId }));
|
|
9906
|
+
} catch (error) {
|
|
9907
|
+
printJson({ error: error instanceof Error ? error.message : String(error) });
|
|
9908
|
+
process.exitCode = 1;
|
|
9909
|
+
}
|
|
9811
9910
|
});
|
|
9812
9911
|
program2.command("deployments:get").argument("<deploymentId>").description("Fetch a deployment").action(async (deploymentId) => {
|
|
9813
9912
|
printJson(await createClient(loadConfig()).deployments.get(normalizeId(deploymentId, "deployment", "Deployment ID")));
|
|
@@ -9849,9 +9948,9 @@ program2.command("treasury:funding").description("List supplier funding summarie
|
|
|
9849
9948
|
program2.command("treasury:funding-preview").requiredOption("--estimated-supplier-cost <usd>").option("--supplier <supplierId>").option("--pool <poolId>").description("Preview treasury funding readiness for a supplier-backed launch").action(async (options) => {
|
|
9850
9949
|
printJson(
|
|
9851
9950
|
await createClient(loadConfig()).treasury.previewFunding({
|
|
9852
|
-
estimatedSupplierCostUsd:
|
|
9853
|
-
poolId: options.pool,
|
|
9854
|
-
supplier: options.supplier
|
|
9951
|
+
estimatedSupplierCostUsd: normalizePositiveUsd(options.estimatedSupplierCost, "Estimated supplier cost"),
|
|
9952
|
+
poolId: normalizeOptionalId(options.pool, "pool", "Pool ID"),
|
|
9953
|
+
supplier: normalizeOptionalId(options.supplier, "supplier", "Supplier ID")
|
|
9855
9954
|
})
|
|
9856
9955
|
);
|
|
9857
9956
|
});
|
|
@@ -9870,8 +9969,9 @@ program2.command("api-keys:list").description("List API keys").action(async () =
|
|
|
9870
9969
|
printJson(await createClient(loadConfig()).apiKeys.list());
|
|
9871
9970
|
});
|
|
9872
9971
|
program2.command("api-keys:revoke").argument("<apiKeyId>").description("Revoke an API key").action(async (apiKeyId) => {
|
|
9873
|
-
|
|
9874
|
-
|
|
9972
|
+
const normalizedApiKeyId = normalizeId(apiKeyId, "apiKey", "API key ID");
|
|
9973
|
+
await createClient(loadConfig()).apiKeys.revoke(normalizedApiKeyId);
|
|
9974
|
+
console.log(`Revoked API key ${normalizedApiKeyId}.`);
|
|
9875
9975
|
});
|
|
9876
9976
|
program2.command("service-accounts:create").requiredOption("--name <name>").option("--scopes <scopes>", "comma-separated scopes", "org:read,project:read,project:write,dataset:read,dataset:write,run:read,run:write").description("Create a service account").action(async (options) => {
|
|
9877
9977
|
printJson(
|
|
@@ -9885,8 +9985,9 @@ program2.command("service-accounts:list").description("List service accounts").a
|
|
|
9885
9985
|
printJson(await createClient(loadConfig()).serviceAccounts.list());
|
|
9886
9986
|
});
|
|
9887
9987
|
program2.command("service-accounts:revoke").argument("<serviceAccountId>").description("Revoke a service account").action(async (serviceAccountId) => {
|
|
9888
|
-
|
|
9889
|
-
|
|
9988
|
+
const normalizedServiceAccountId = normalizeId(serviceAccountId, "serviceAccount", "Service account ID");
|
|
9989
|
+
await createClient(loadConfig()).serviceAccounts.revoke(normalizedServiceAccountId);
|
|
9990
|
+
console.log(`Revoked service account ${normalizedServiceAccountId}.`);
|
|
9890
9991
|
});
|
|
9891
9992
|
void program2.parseAsync(process.argv).catch((error) => {
|
|
9892
9993
|
console.error(error instanceof Error ? error.message : String(error));
|