trainfabric 0.1.24 → 0.1.26
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 +201 -53
- 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,18 @@ 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
|
+
apiKey: /^key_[A-Za-z0-9_-]+$/,
|
|
8824
|
+
dataset: /^ds_[A-Za-z0-9_-]+$/,
|
|
8825
|
+
deployment: /^dep_[A-Za-z0-9_-]+$/,
|
|
8826
|
+
model: /^mdl_[A-Za-z0-9_-]+$/,
|
|
8820
8827
|
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8828
|
+
pool: /^pool_[A-Za-z0-9_-]+$/,
|
|
8821
8829
|
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8822
|
-
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_-]*$/
|
|
8823
8833
|
};
|
|
8834
|
+
var allowedBillingUnits = /* @__PURE__ */ new Set(["normalized_tflop_seconds", "reserved_gpu_seconds", "usd"]);
|
|
8824
8835
|
function collectRuntimeFiles(repoPath) {
|
|
8825
8836
|
const manifestFiles = /* @__PURE__ */ new Set([
|
|
8826
8837
|
"training.yaml",
|
|
@@ -8868,12 +8879,56 @@ function collectRuntimeFiles(repoPath) {
|
|
|
8868
8879
|
walk(repoPath);
|
|
8869
8880
|
return found.sort((left, right) => left.path.localeCompare(right.path));
|
|
8870
8881
|
}
|
|
8882
|
+
function inferEntrypoint(files) {
|
|
8883
|
+
const trainingYaml = files.find((file) => file.path === "training.yaml" || file.path === "train.runtime.yaml");
|
|
8884
|
+
if (trainingYaml) {
|
|
8885
|
+
const match = trainingYaml.content.match(/entrypoint\s*[:=]\s*["']?([A-Za-z0-9_./-]+)["']?/i);
|
|
8886
|
+
if (match?.[1]) {
|
|
8887
|
+
const entry = match[1];
|
|
8888
|
+
const exact = files.find((file) => file.path === entry);
|
|
8889
|
+
if (exact) {
|
|
8890
|
+
return exact.path;
|
|
8891
|
+
}
|
|
8892
|
+
const nested = files.find((file) => file.path.endsWith(`/${entry}`));
|
|
8893
|
+
if (nested) {
|
|
8894
|
+
return nested.path;
|
|
8895
|
+
}
|
|
8896
|
+
return entry;
|
|
8897
|
+
}
|
|
8898
|
+
}
|
|
8899
|
+
return files.find((file) => /(^|\/)train\.(py|sh)$/i.test(file.path))?.path;
|
|
8900
|
+
}
|
|
8901
|
+
function validateGitUrl(value) {
|
|
8902
|
+
const url = new URL(value);
|
|
8903
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" && url.protocol !== "ssh:" && url.protocol !== "git:") {
|
|
8904
|
+
throw new Error(`Invalid git URL "${value}". Use an absolute repository URL.`);
|
|
8905
|
+
}
|
|
8906
|
+
return value;
|
|
8907
|
+
}
|
|
8908
|
+
function validateRepoPath(repoPath) {
|
|
8909
|
+
const absolute = import_node_path2.default.resolve(repoPath);
|
|
8910
|
+
const parsed = import_node_path2.default.parse(absolute);
|
|
8911
|
+
if (absolute === parsed.root || absolute === import_node_os.default.homedir()) {
|
|
8912
|
+
throw new Error(`Refusing to package unsafe repository path: ${repoPath}`);
|
|
8913
|
+
}
|
|
8914
|
+
const stat = import_node_fs.default.statSync(absolute);
|
|
8915
|
+
if (!stat.isDirectory()) {
|
|
8916
|
+
throw new Error(`Repository path must be a directory: ${repoPath}`);
|
|
8917
|
+
}
|
|
8918
|
+
return absolute;
|
|
8919
|
+
}
|
|
8871
8920
|
function buildSourceOptions(options) {
|
|
8921
|
+
if (options.repo && options.git) {
|
|
8922
|
+
throw new Error("Use either --repo or --git, not both.");
|
|
8923
|
+
}
|
|
8872
8924
|
if (options.repo) {
|
|
8925
|
+
const repoPath = validateRepoPath(options.repo);
|
|
8926
|
+
const files = collectRuntimeFiles(repoPath);
|
|
8873
8927
|
return {
|
|
8874
8928
|
source: {
|
|
8875
8929
|
kind: "inline",
|
|
8876
|
-
|
|
8930
|
+
entrypoint: inferEntrypoint(files),
|
|
8931
|
+
files
|
|
8877
8932
|
}
|
|
8878
8933
|
};
|
|
8879
8934
|
}
|
|
@@ -8881,7 +8936,7 @@ function buildSourceOptions(options) {
|
|
|
8881
8936
|
return {
|
|
8882
8937
|
source: {
|
|
8883
8938
|
kind: "git",
|
|
8884
|
-
repoUrl: options.git,
|
|
8939
|
+
repoUrl: validateGitUrl(options.git),
|
|
8885
8940
|
branch: options.branch
|
|
8886
8941
|
}
|
|
8887
8942
|
};
|
|
@@ -8910,12 +8965,15 @@ function decrypt(payload) {
|
|
|
8910
8965
|
}
|
|
8911
8966
|
function parseApiKeyScopes(input) {
|
|
8912
8967
|
const allowedScopes = new Set(apiKeyScopes);
|
|
8913
|
-
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
|
+
}
|
|
8914
8972
|
const invalidScope = scopes.find((scope) => !allowedScopes.has(scope));
|
|
8915
8973
|
if (invalidScope) {
|
|
8916
8974
|
throw new Error(`Invalid API key scope: ${invalidScope}`);
|
|
8917
8975
|
}
|
|
8918
|
-
return scopes;
|
|
8976
|
+
return [...new Set(scopes)];
|
|
8919
8977
|
}
|
|
8920
8978
|
function normalizeHumanName(value, label) {
|
|
8921
8979
|
let name = String(value ?? "").trim();
|
|
@@ -8940,6 +8998,16 @@ function normalizeOptionalId(value, kind, label) {
|
|
|
8940
8998
|
}
|
|
8941
8999
|
return normalizeId(value, kind, label);
|
|
8942
9000
|
}
|
|
9001
|
+
function normalizeOptionalBillingUnit(value) {
|
|
9002
|
+
if (value === void 0) {
|
|
9003
|
+
return void 0;
|
|
9004
|
+
}
|
|
9005
|
+
const unit = String(value).trim();
|
|
9006
|
+
if (!allowedBillingUnits.has(unit)) {
|
|
9007
|
+
throw new Error("Billing unit is invalid. Use normalized_tflop_seconds, reserved_gpu_seconds, or usd.");
|
|
9008
|
+
}
|
|
9009
|
+
return unit;
|
|
9010
|
+
}
|
|
8943
9011
|
|
|
8944
9012
|
// src/run_input.ts
|
|
8945
9013
|
var BASE_MODEL_ALIASES = {
|
|
@@ -8953,36 +9021,75 @@ var BASE_MODEL_ALIASES = {
|
|
|
8953
9021
|
"qwen2.5-7b": "qwen-2.5-7b",
|
|
8954
9022
|
qwen_2_5_7b: "qwen-2.5-7b"
|
|
8955
9023
|
};
|
|
9024
|
+
var ACCELERATOR_CLASSES = /* @__PURE__ */ new Set(["a10g", "l4", "a40", "l40s", "a100", "h100", "h200", "b200"]);
|
|
9025
|
+
var PRECISION_MODES = /* @__PURE__ */ new Set(["fp16_bf16", "fp8", "fp32"]);
|
|
9026
|
+
var INTERCONNECT_TYPES = /* @__PURE__ */ new Set(["pcie", "nvlink"]);
|
|
9027
|
+
function normalizePositiveInteger(value, label, fallback) {
|
|
9028
|
+
const parsed = Number(value ?? String(fallback));
|
|
9029
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
9030
|
+
throw new Error(`${label} must be a positive integer.`);
|
|
9031
|
+
}
|
|
9032
|
+
return parsed;
|
|
9033
|
+
}
|
|
9034
|
+
function normalizeOptionalPositiveNumber(value, label) {
|
|
9035
|
+
if (value === void 0) {
|
|
9036
|
+
return void 0;
|
|
9037
|
+
}
|
|
9038
|
+
const parsed = Number(value);
|
|
9039
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
9040
|
+
throw new Error(`${label} must be a positive number.`);
|
|
9041
|
+
}
|
|
9042
|
+
return parsed;
|
|
9043
|
+
}
|
|
9044
|
+
function normalizeOptionalChoice(value, allowed, label, examples) {
|
|
9045
|
+
if (value === void 0) {
|
|
9046
|
+
return void 0;
|
|
9047
|
+
}
|
|
9048
|
+
const normalized = String(value).trim().toLowerCase();
|
|
9049
|
+
if (!allowed.has(normalized)) {
|
|
9050
|
+
throw new Error(`${label} is invalid. Use ${examples}.`);
|
|
9051
|
+
}
|
|
9052
|
+
return normalized;
|
|
9053
|
+
}
|
|
8956
9054
|
function normalizeBaseModel(model) {
|
|
8957
9055
|
return BASE_MODEL_ALIASES[model] ?? model;
|
|
8958
9056
|
}
|
|
8959
9057
|
function buildComputeSpec(options) {
|
|
8960
|
-
const gpuCount =
|
|
8961
|
-
const nodeCount =
|
|
9058
|
+
const gpuCount = normalizePositiveInteger(options.gpus, "GPU count", 1);
|
|
9059
|
+
const nodeCount = normalizePositiveInteger(options.nodes, "Node count", 1);
|
|
8962
9060
|
const compute = {
|
|
8963
9061
|
gpuCount,
|
|
8964
9062
|
nodeCount,
|
|
8965
9063
|
distributionStrategy: gpuCount > 1 || nodeCount > 1 ? "ddp" : "single_gpu",
|
|
8966
9064
|
target: "local"
|
|
8967
9065
|
};
|
|
8968
|
-
|
|
8969
|
-
|
|
9066
|
+
const accelerator = normalizeOptionalChoice(
|
|
9067
|
+
options.accelerator,
|
|
9068
|
+
ACCELERATOR_CLASSES,
|
|
9069
|
+
"Accelerator",
|
|
9070
|
+
"a10g, l4, a40, l40s, a100, h100, h200, or b200"
|
|
9071
|
+
);
|
|
9072
|
+
const minMemory = normalizeOptionalPositiveNumber(options.minMemory, "Minimum GPU memory");
|
|
9073
|
+
const precision = normalizeOptionalChoice(options.precision, PRECISION_MODES, "Precision", "fp16_bf16, fp8, or fp32");
|
|
9074
|
+
const interconnect = normalizeOptionalChoice(options.interconnect, INTERCONNECT_TYPES, "Interconnect", "pcie or nvlink");
|
|
9075
|
+
if (accelerator) {
|
|
9076
|
+
compute.acceleratorClass = accelerator;
|
|
8970
9077
|
}
|
|
8971
|
-
if (
|
|
8972
|
-
compute.minGpuMemoryGb =
|
|
9078
|
+
if (minMemory !== void 0) {
|
|
9079
|
+
compute.minGpuMemoryGb = minMemory;
|
|
8973
9080
|
}
|
|
8974
|
-
if (
|
|
8975
|
-
compute.precision =
|
|
9081
|
+
if (precision) {
|
|
9082
|
+
compute.precision = precision;
|
|
8976
9083
|
}
|
|
8977
|
-
if (
|
|
8978
|
-
compute.interconnect =
|
|
9084
|
+
if (interconnect) {
|
|
9085
|
+
compute.interconnect = interconnect;
|
|
8979
9086
|
}
|
|
8980
9087
|
return compute;
|
|
8981
9088
|
}
|
|
8982
9089
|
|
|
8983
9090
|
// src/index.ts
|
|
8984
9091
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
8985
|
-
var CLI_VERSION = "0.1.
|
|
9092
|
+
var CLI_VERSION = "0.1.26";
|
|
8986
9093
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
8987
9094
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
8988
9095
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9379,6 +9486,30 @@ function filterQuoteBundleForRequestedMode(bundle, options) {
|
|
|
9379
9486
|
quotes: bundle.quotes.filter((item) => item.mode === options.mode || item.quote.mode === options.mode)
|
|
9380
9487
|
};
|
|
9381
9488
|
}
|
|
9489
|
+
function redactInlineSource(value) {
|
|
9490
|
+
const redact = (current) => {
|
|
9491
|
+
if (Array.isArray(current)) {
|
|
9492
|
+
return current.map((item) => redact(item));
|
|
9493
|
+
}
|
|
9494
|
+
if (!current || typeof current !== "object") {
|
|
9495
|
+
return current;
|
|
9496
|
+
}
|
|
9497
|
+
const object = current;
|
|
9498
|
+
const next = {};
|
|
9499
|
+
for (const [key, child] of Object.entries(object)) {
|
|
9500
|
+
next[key] = key === "content" && typeof child === "string" ? "<redacted>" : redact(child);
|
|
9501
|
+
}
|
|
9502
|
+
return next;
|
|
9503
|
+
};
|
|
9504
|
+
return redact(value);
|
|
9505
|
+
}
|
|
9506
|
+
function normalizePositiveUsd(value, label) {
|
|
9507
|
+
const amount = Number(value);
|
|
9508
|
+
if (!Number.isFinite(amount) || amount <= 0) {
|
|
9509
|
+
throw new Error(`${label} must be a positive USD amount.`);
|
|
9510
|
+
}
|
|
9511
|
+
return amount;
|
|
9512
|
+
}
|
|
9382
9513
|
async function watchRun(runId, json = false, timeoutMs, pollMs) {
|
|
9383
9514
|
const handle = await createClient(loadConfig()).runs.watch(runId);
|
|
9384
9515
|
if (json) {
|
|
@@ -9595,11 +9726,12 @@ program2.command("datasets:list").option("--project <projectId>").description("L
|
|
|
9595
9726
|
printJson(await createClient(loadConfig()).datasets.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9596
9727
|
});
|
|
9597
9728
|
program2.command("datasets:get").argument("<datasetId>").description("Fetch dataset detail").action(async (datasetId) => {
|
|
9598
|
-
printJson(await createClient(loadConfig()).datasets.get(
|
|
9729
|
+
printJson(await createClient(loadConfig()).datasets.get(normalizeId(datasetId, "dataset", "Dataset ID")));
|
|
9599
9730
|
});
|
|
9600
9731
|
program2.command("datasets:delete").argument("<datasetId>").description("Delete a dataset").action(async (datasetId) => {
|
|
9601
|
-
|
|
9602
|
-
|
|
9732
|
+
const normalizedDatasetId = normalizeId(datasetId, "dataset", "Dataset ID");
|
|
9733
|
+
await createClient(loadConfig()).datasets.delete(normalizedDatasetId);
|
|
9734
|
+
printJson({ deleted: true, id: normalizedDatasetId });
|
|
9603
9735
|
});
|
|
9604
9736
|
program2.command("datasets:upload").argument("<file>").option("--project <projectId>").option("--name <name>").description("Upload a dataset through upload sessions").action(async (file, options) => {
|
|
9605
9737
|
assertReadableFile(file, "Dataset path");
|
|
@@ -9615,12 +9747,13 @@ program2.command("datasets:upload").argument("<file>").option("--project <projec
|
|
|
9615
9747
|
printJson(dataset);
|
|
9616
9748
|
});
|
|
9617
9749
|
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) => {
|
|
9750
|
+
const config = loadConfig();
|
|
9751
|
+
const runInput = buildRunInput(options, config);
|
|
9618
9752
|
if (!options.yes) {
|
|
9619
9753
|
throw new Error("Refusing to launch without explicit cost acceptance. Run `trainfabric runs:quote --summary ...` first, then rerun `runs:create` with --yes.");
|
|
9620
9754
|
}
|
|
9621
|
-
const config = loadConfig();
|
|
9622
9755
|
const client = createClient(config);
|
|
9623
|
-
const run = await client.runs.create(
|
|
9756
|
+
const run = await client.runs.create(runInput);
|
|
9624
9757
|
printJson(run.snapshot);
|
|
9625
9758
|
});
|
|
9626
9759
|
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) => {
|
|
@@ -9629,12 +9762,11 @@ program2.command("runtime:detect").option("--project <projectId>").option("--rep
|
|
|
9629
9762
|
throw new Error("Provide --repo or --git.");
|
|
9630
9763
|
}
|
|
9631
9764
|
const config = loadConfig();
|
|
9632
|
-
|
|
9633
|
-
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
);
|
|
9765
|
+
const detection = await createClient(config).runtime.detect({
|
|
9766
|
+
projectId: requireProjectId(options, config),
|
|
9767
|
+
source: sourceOptions.source
|
|
9768
|
+
});
|
|
9769
|
+
printJson(redactInlineSource(detection));
|
|
9638
9770
|
});
|
|
9639
9771
|
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
9772
|
const config = loadConfig();
|
|
@@ -9655,31 +9787,32 @@ program2.command("runs:quote").option("--project <projectId>").requiredOption("-
|
|
|
9655
9787
|
if (options.summary) {
|
|
9656
9788
|
printQuoteSummary(bundle);
|
|
9657
9789
|
} else {
|
|
9658
|
-
printJson(bundle);
|
|
9790
|
+
printJson(redactInlineSource(bundle));
|
|
9659
9791
|
}
|
|
9660
9792
|
});
|
|
9661
9793
|
program2.command("runs:list").option("--project <projectId>").description("List runs").action(async (options) => {
|
|
9662
9794
|
printJson(await createClient(loadConfig()).runs.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9663
9795
|
});
|
|
9664
9796
|
program2.command("runs:status").argument("<runId>").description("Fetch full run detail").action(async (runId) => {
|
|
9665
|
-
printJson(await createClient(loadConfig()).runs.get(
|
|
9797
|
+
printJson(await createClient(loadConfig()).runs.get(normalizeId(runId, "run", "Run ID")));
|
|
9666
9798
|
});
|
|
9667
9799
|
program2.command("runs:logs").argument("<runId>").description("Fetch run logs").action(async (runId) => {
|
|
9668
|
-
printJson(await createClient(loadConfig()).runs.logs(
|
|
9800
|
+
printJson(await createClient(loadConfig()).runs.logs(normalizeId(runId, "run", "Run ID")));
|
|
9669
9801
|
});
|
|
9670
9802
|
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) => {
|
|
9803
|
+
const normalizedRunId = normalizeId(runId, "run", "Run ID");
|
|
9671
9804
|
printJson(
|
|
9672
|
-
await createClient(loadConfig()).runs.wait(
|
|
9805
|
+
await createClient(loadConfig()).runs.wait(normalizedRunId, {
|
|
9673
9806
|
timeoutMs: parseDurationMs(options.timeout),
|
|
9674
9807
|
pollMs: parsePositiveMs(options.poll, 1e3)
|
|
9675
9808
|
})
|
|
9676
9809
|
);
|
|
9677
9810
|
});
|
|
9678
9811
|
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(
|
|
9812
|
+
await watchRun(normalizeId(runId, "run", "Run ID"), Boolean(options.json), parseDurationMs(options.timeout), parsePositiveMs(options.poll, 1e3));
|
|
9680
9813
|
});
|
|
9681
9814
|
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(
|
|
9815
|
+
const usage = await createClient(loadConfig()).runs.usage(normalizeId(runId, "run", "Run ID"));
|
|
9683
9816
|
if (options.summary) {
|
|
9684
9817
|
printUsageSummary(usage);
|
|
9685
9818
|
} else {
|
|
@@ -9687,7 +9820,7 @@ program2.command("runs:usage").argument("<runId>").option("--summary", "print a
|
|
|
9687
9820
|
}
|
|
9688
9821
|
});
|
|
9689
9822
|
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(
|
|
9823
|
+
const breakdown = await createClient(loadConfig()).runs.costBreakdown(normalizeId(runId, "run", "Run ID"));
|
|
9691
9824
|
if (options.summary) {
|
|
9692
9825
|
printUsageSummary(breakdown);
|
|
9693
9826
|
} else {
|
|
@@ -9695,22 +9828,22 @@ program2.command("runs:cost-breakdown").argument("<runId>").option("--summary",
|
|
|
9695
9828
|
}
|
|
9696
9829
|
});
|
|
9697
9830
|
program2.command("runs:allocation").argument("<runId>").description("Fetch run allocation").action(async (runId) => {
|
|
9698
|
-
printJson(await createClient(loadConfig()).runs.allocation(
|
|
9831
|
+
printJson(await createClient(loadConfig()).runs.allocation(normalizeId(runId, "run", "Run ID")));
|
|
9699
9832
|
});
|
|
9700
9833
|
program2.command("runs:configuration").argument("<runId>").description("Fetch run placement configuration").action(async (runId) => {
|
|
9701
|
-
printJson(await createClient(loadConfig()).runs.configuration(
|
|
9834
|
+
printJson(await createClient(loadConfig()).runs.configuration(normalizeId(runId, "run", "Run ID")));
|
|
9702
9835
|
});
|
|
9703
9836
|
program2.command("runs:explanation").argument("<runId>").description("Fetch run planning explanation").action(async (runId) => {
|
|
9704
|
-
printJson(await createClient(loadConfig()).runs.explanation(
|
|
9837
|
+
printJson(await createClient(loadConfig()).runs.explanation(normalizeId(runId, "run", "Run ID")));
|
|
9705
9838
|
});
|
|
9706
9839
|
program2.command("runs:cancel").argument("<runId>").description("Cancel a run").action(async (runId) => {
|
|
9707
|
-
printJson(await createClient(loadConfig()).runs.cancel(
|
|
9840
|
+
printJson(await createClient(loadConfig()).runs.cancel(normalizeId(runId, "run", "Run ID")));
|
|
9708
9841
|
});
|
|
9709
9842
|
program2.command("runs:resume").argument("<runId>").description("Resume a run from the latest checkpoint").action(async (runId) => {
|
|
9710
|
-
printJson(await createClient(loadConfig()).runs.resume(
|
|
9843
|
+
printJson(await createClient(loadConfig()).runs.resume(normalizeId(runId, "run", "Run ID")));
|
|
9711
9844
|
});
|
|
9712
9845
|
program2.command("runs:terminate").argument("<runId>").description("Terminate a queued, launchable, or active run").action(async (runId) => {
|
|
9713
|
-
printJson(await createClient(loadConfig()).runs.terminate(
|
|
9846
|
+
printJson(await createClient(loadConfig()).runs.terminate(normalizeId(runId, "run", "Run ID")));
|
|
9714
9847
|
});
|
|
9715
9848
|
program2.command("cluster:capacity").description("Fetch cluster capacity view").action(async () => {
|
|
9716
9849
|
printJson(await createClient(loadConfig()).cluster.getCapacity());
|
|
@@ -9722,26 +9855,39 @@ program2.command("models:list").description("List exported models").action(async
|
|
|
9722
9855
|
printJson(await createClient(loadConfig()).models.list());
|
|
9723
9856
|
});
|
|
9724
9857
|
program2.command("models:export").argument("<modelId>").description("Fetch the export package path for a model").action(async (modelId) => {
|
|
9725
|
-
printJson(await createClient(loadConfig()).models.export(
|
|
9858
|
+
printJson(await createClient(loadConfig()).models.export(normalizeId(modelId, "model", "Model ID")));
|
|
9726
9859
|
});
|
|
9727
9860
|
program2.command("deployments:list").description("List deployments").action(async () => {
|
|
9728
9861
|
printJson(await createClient(loadConfig()).deployments.list());
|
|
9729
9862
|
});
|
|
9730
9863
|
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
|
-
|
|
9864
|
+
const config = loadConfig();
|
|
9865
|
+
const client = createClient(config);
|
|
9866
|
+
const projectId = normalizeOptionalId(options.project, "project", "Project ID");
|
|
9867
|
+
if (projectId) {
|
|
9868
|
+
await client.projects.get(projectId);
|
|
9869
|
+
}
|
|
9870
|
+
const modelId = normalizeId(options.model, "model", "Model ID");
|
|
9871
|
+
printJson(await client.deployments.create({ modelId }));
|
|
9732
9872
|
});
|
|
9733
9873
|
program2.command("deployments:get").argument("<deploymentId>").description("Fetch a deployment").action(async (deploymentId) => {
|
|
9734
|
-
printJson(await createClient(loadConfig()).deployments.get(
|
|
9874
|
+
printJson(await createClient(loadConfig()).deployments.get(normalizeId(deploymentId, "deployment", "Deployment ID")));
|
|
9735
9875
|
});
|
|
9736
9876
|
program2.command("deployments:delete").argument("<deploymentId>").description("Delete a pending or cataloged deployment").action(async (deploymentId) => {
|
|
9737
|
-
|
|
9738
|
-
|
|
9877
|
+
const normalizedDeploymentId = normalizeId(deploymentId, "deployment", "Deployment ID");
|
|
9878
|
+
await createClient(loadConfig()).deployments.delete(normalizedDeploymentId);
|
|
9879
|
+
printJson({ deleted: true, id: normalizedDeploymentId });
|
|
9739
9880
|
});
|
|
9740
9881
|
program2.command("billing:summary").description("Fetch billing summary").action(async () => {
|
|
9741
9882
|
printJson(await createClient(loadConfig()).billing.getSummary());
|
|
9742
9883
|
});
|
|
9743
9884
|
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(
|
|
9885
|
+
printJson(
|
|
9886
|
+
await createClient(loadConfig()).billing.getUsage(
|
|
9887
|
+
normalizeOptionalBillingUnit(options.unit),
|
|
9888
|
+
normalizeOptionalId(options.run, "run", "Run ID")
|
|
9889
|
+
)
|
|
9890
|
+
);
|
|
9745
9891
|
});
|
|
9746
9892
|
program2.command("billing:invoices").description("List invoices").action(async () => {
|
|
9747
9893
|
printJson(await createClient(loadConfig()).billing.listInvoices());
|
|
@@ -9764,9 +9910,9 @@ program2.command("treasury:funding").description("List supplier funding summarie
|
|
|
9764
9910
|
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) => {
|
|
9765
9911
|
printJson(
|
|
9766
9912
|
await createClient(loadConfig()).treasury.previewFunding({
|
|
9767
|
-
estimatedSupplierCostUsd:
|
|
9768
|
-
poolId: options.pool,
|
|
9769
|
-
supplier: options.supplier
|
|
9913
|
+
estimatedSupplierCostUsd: normalizePositiveUsd(options.estimatedSupplierCost, "Estimated supplier cost"),
|
|
9914
|
+
poolId: normalizeOptionalId(options.pool, "pool", "Pool ID"),
|
|
9915
|
+
supplier: normalizeOptionalId(options.supplier, "supplier", "Supplier ID")
|
|
9770
9916
|
})
|
|
9771
9917
|
);
|
|
9772
9918
|
});
|
|
@@ -9785,8 +9931,9 @@ program2.command("api-keys:list").description("List API keys").action(async () =
|
|
|
9785
9931
|
printJson(await createClient(loadConfig()).apiKeys.list());
|
|
9786
9932
|
});
|
|
9787
9933
|
program2.command("api-keys:revoke").argument("<apiKeyId>").description("Revoke an API key").action(async (apiKeyId) => {
|
|
9788
|
-
|
|
9789
|
-
|
|
9934
|
+
const normalizedApiKeyId = normalizeId(apiKeyId, "apiKey", "API key ID");
|
|
9935
|
+
await createClient(loadConfig()).apiKeys.revoke(normalizedApiKeyId);
|
|
9936
|
+
console.log(`Revoked API key ${normalizedApiKeyId}.`);
|
|
9790
9937
|
});
|
|
9791
9938
|
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) => {
|
|
9792
9939
|
printJson(
|
|
@@ -9800,8 +9947,9 @@ program2.command("service-accounts:list").description("List service accounts").a
|
|
|
9800
9947
|
printJson(await createClient(loadConfig()).serviceAccounts.list());
|
|
9801
9948
|
});
|
|
9802
9949
|
program2.command("service-accounts:revoke").argument("<serviceAccountId>").description("Revoke a service account").action(async (serviceAccountId) => {
|
|
9803
|
-
|
|
9804
|
-
|
|
9950
|
+
const normalizedServiceAccountId = normalizeId(serviceAccountId, "serviceAccount", "Service account ID");
|
|
9951
|
+
await createClient(loadConfig()).serviceAccounts.revoke(normalizedServiceAccountId);
|
|
9952
|
+
console.log(`Revoked service account ${normalizedServiceAccountId}.`);
|
|
9805
9953
|
});
|
|
9806
9954
|
void program2.parseAsync(process.argv).catch((error) => {
|
|
9807
9955
|
console.error(error instanceof Error ? error.message : String(error));
|