trainfabric 0.1.23 → 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 +175 -40
- 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),
|
|
@@ -8816,6 +8819,15 @@ var import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
|
8816
8819
|
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);
|
|
8822
|
+
var idPatterns = {
|
|
8823
|
+
dataset: /^ds_[A-Za-z0-9_-]+$/,
|
|
8824
|
+
deployment: /^dep_[A-Za-z0-9_-]+$/,
|
|
8825
|
+
model: /^mdl_[A-Za-z0-9_-]+$/,
|
|
8826
|
+
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8827
|
+
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8828
|
+
run: /^run_[A-Za-z0-9_-]+$/
|
|
8829
|
+
};
|
|
8830
|
+
var allowedBillingUnits = /* @__PURE__ */ new Set(["normalized_tflop_seconds", "reserved_gpu_seconds", "usd"]);
|
|
8819
8831
|
function collectRuntimeFiles(repoPath) {
|
|
8820
8832
|
const manifestFiles = /* @__PURE__ */ new Set([
|
|
8821
8833
|
"training.yaml",
|
|
@@ -8863,12 +8875,56 @@ function collectRuntimeFiles(repoPath) {
|
|
|
8863
8875
|
walk(repoPath);
|
|
8864
8876
|
return found.sort((left, right) => left.path.localeCompare(right.path));
|
|
8865
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
|
+
}
|
|
8866
8916
|
function buildSourceOptions(options) {
|
|
8917
|
+
if (options.repo && options.git) {
|
|
8918
|
+
throw new Error("Use either --repo or --git, not both.");
|
|
8919
|
+
}
|
|
8867
8920
|
if (options.repo) {
|
|
8921
|
+
const repoPath = validateRepoPath(options.repo);
|
|
8922
|
+
const files = collectRuntimeFiles(repoPath);
|
|
8868
8923
|
return {
|
|
8869
8924
|
source: {
|
|
8870
8925
|
kind: "inline",
|
|
8871
|
-
|
|
8926
|
+
entrypoint: inferEntrypoint(files),
|
|
8927
|
+
files
|
|
8872
8928
|
}
|
|
8873
8929
|
};
|
|
8874
8930
|
}
|
|
@@ -8876,7 +8932,7 @@ function buildSourceOptions(options) {
|
|
|
8876
8932
|
return {
|
|
8877
8933
|
source: {
|
|
8878
8934
|
kind: "git",
|
|
8879
|
-
repoUrl: options.git,
|
|
8935
|
+
repoUrl: validateGitUrl(options.git),
|
|
8880
8936
|
branch: options.branch
|
|
8881
8937
|
}
|
|
8882
8938
|
};
|
|
@@ -8910,7 +8966,40 @@ function parseApiKeyScopes(input) {
|
|
|
8910
8966
|
if (invalidScope) {
|
|
8911
8967
|
throw new Error(`Invalid API key scope: ${invalidScope}`);
|
|
8912
8968
|
}
|
|
8913
|
-
return scopes;
|
|
8969
|
+
return [...new Set(scopes)];
|
|
8970
|
+
}
|
|
8971
|
+
function normalizeHumanName(value, label) {
|
|
8972
|
+
let name = String(value ?? "").trim();
|
|
8973
|
+
while (name.length >= 2 && (name.startsWith("'") && name.endsWith("'") || name.startsWith('"') && name.endsWith('"'))) {
|
|
8974
|
+
name = name.slice(1, -1).trim();
|
|
8975
|
+
}
|
|
8976
|
+
if (!/[A-Za-z0-9]/.test(name)) {
|
|
8977
|
+
throw new Error(`${label} is required.`);
|
|
8978
|
+
}
|
|
8979
|
+
return name;
|
|
8980
|
+
}
|
|
8981
|
+
function normalizeId(value, kind, label) {
|
|
8982
|
+
const id = String(value ?? "").trim();
|
|
8983
|
+
if (!id || !idPatterns[kind].test(id)) {
|
|
8984
|
+
throw new Error(`${label} is invalid.`);
|
|
8985
|
+
}
|
|
8986
|
+
return id;
|
|
8987
|
+
}
|
|
8988
|
+
function normalizeOptionalId(value, kind, label) {
|
|
8989
|
+
if (value === void 0) {
|
|
8990
|
+
return void 0;
|
|
8991
|
+
}
|
|
8992
|
+
return normalizeId(value, kind, label);
|
|
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;
|
|
8914
9003
|
}
|
|
8915
9004
|
|
|
8916
9005
|
// src/run_input.ts
|
|
@@ -8954,7 +9043,7 @@ function buildComputeSpec(options) {
|
|
|
8954
9043
|
|
|
8955
9044
|
// src/index.ts
|
|
8956
9045
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
8957
|
-
var CLI_VERSION = "0.1.
|
|
9046
|
+
var CLI_VERSION = "0.1.25";
|
|
8958
9047
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
8959
9048
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
8960
9049
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9293,7 +9382,7 @@ function visibleConfig(config) {
|
|
|
9293
9382
|
};
|
|
9294
9383
|
}
|
|
9295
9384
|
function requireProjectId(options, config) {
|
|
9296
|
-
const projectId = options.project ?? config.projectId;
|
|
9385
|
+
const projectId = normalizeOptionalId(options.project ?? config.projectId, "project", "Project ID");
|
|
9297
9386
|
if (!projectId) {
|
|
9298
9387
|
throw new Error("Project is required. Pass --project <projectId> or run `trainfabric config:set-project <projectId>`.");
|
|
9299
9388
|
}
|
|
@@ -9351,6 +9440,16 @@ function filterQuoteBundleForRequestedMode(bundle, options) {
|
|
|
9351
9440
|
quotes: bundle.quotes.filter((item) => item.mode === options.mode || item.quote.mode === options.mode)
|
|
9352
9441
|
};
|
|
9353
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
|
+
}
|
|
9354
9453
|
async function watchRun(runId, json = false, timeoutMs, pollMs) {
|
|
9355
9454
|
const handle = await createClient(loadConfig()).runs.watch(runId);
|
|
9356
9455
|
if (json) {
|
|
@@ -9418,6 +9517,17 @@ function createClient(config) {
|
|
|
9418
9517
|
projectId: config.projectId
|
|
9419
9518
|
});
|
|
9420
9519
|
}
|
|
9520
|
+
function assertReadableFile(filePath, label) {
|
|
9521
|
+
let stats;
|
|
9522
|
+
try {
|
|
9523
|
+
stats = import_node_fs2.default.statSync(filePath);
|
|
9524
|
+
} catch {
|
|
9525
|
+
throw new Error(`${label} not found: ${filePath}`);
|
|
9526
|
+
}
|
|
9527
|
+
if (!stats.isFile()) {
|
|
9528
|
+
throw new Error(`${label} must be a file: ${filePath}`);
|
|
9529
|
+
}
|
|
9530
|
+
}
|
|
9421
9531
|
function normalizeHttpBaseUrl(value) {
|
|
9422
9532
|
let url;
|
|
9423
9533
|
try {
|
|
@@ -9485,8 +9595,9 @@ program2.command("config:set-base-url").argument("<baseUrl>").description("Set t
|
|
|
9485
9595
|
printJson(visibleConfig(config));
|
|
9486
9596
|
});
|
|
9487
9597
|
program2.command("config:set-org").argument("<orgId>").description("Set the default organization ID").action(async (orgId) => {
|
|
9598
|
+
const normalizedOrgId = normalizeId(orgId, "org", "Organization ID");
|
|
9488
9599
|
const currentConfig = loadConfig();
|
|
9489
|
-
const organization = await createClient(currentConfig).organizations.get(
|
|
9600
|
+
const organization = await createClient(currentConfig).organizations.get(normalizedOrgId);
|
|
9490
9601
|
const updatedConfig = updateConfig((current) => {
|
|
9491
9602
|
current.orgId = organization.id;
|
|
9492
9603
|
if (current.projectId) {
|
|
@@ -9496,8 +9607,9 @@ program2.command("config:set-org").argument("<orgId>").description("Set the defa
|
|
|
9496
9607
|
printJson(visibleConfig(updatedConfig));
|
|
9497
9608
|
});
|
|
9498
9609
|
program2.command("config:set-project").argument("<projectId>").description("Set the default project ID").action(async (projectId) => {
|
|
9610
|
+
const normalizedProjectId = normalizeId(projectId, "project", "Project ID");
|
|
9499
9611
|
const currentConfig = loadConfig();
|
|
9500
|
-
const project = await createClient(currentConfig).projects.get(
|
|
9612
|
+
const project = await createClient(currentConfig).projects.get(normalizedProjectId);
|
|
9501
9613
|
if (currentConfig.orgId && project.organizationId !== currentConfig.orgId) {
|
|
9502
9614
|
throw new Error(`Project ${project.id} does not belong to configured organization ${currentConfig.orgId}.`);
|
|
9503
9615
|
}
|
|
@@ -9532,9 +9644,15 @@ program2.command("projects:list").description("List projects in the active organ
|
|
|
9532
9644
|
printJson(await createClient(loadConfig()).projects.list());
|
|
9533
9645
|
});
|
|
9534
9646
|
program2.command("projects:create").requiredOption("--name <name>").option("--org <organizationId>").description("Create a project").action(async (options) => {
|
|
9535
|
-
printJson(
|
|
9647
|
+
printJson(
|
|
9648
|
+
await createClient(loadConfig()).projects.create({
|
|
9649
|
+
name: normalizeHumanName(options.name, "Project name"),
|
|
9650
|
+
organizationId: normalizeOptionalId(options.org, "org", "Organization ID")
|
|
9651
|
+
})
|
|
9652
|
+
);
|
|
9536
9653
|
});
|
|
9537
9654
|
program2.command("datasets:validate").argument("<file>").description("Validate a local dataset file").action(async (file) => {
|
|
9655
|
+
assertReadableFile(file, "Dataset path");
|
|
9538
9656
|
const validation = await createClient(loadConfig()).datasets.validate({
|
|
9539
9657
|
path: file,
|
|
9540
9658
|
format: "chat_jsonl"
|
|
@@ -9545,22 +9663,26 @@ program2.command("datasets:validate").argument("<file>").description("Validate a
|
|
|
9545
9663
|
}
|
|
9546
9664
|
});
|
|
9547
9665
|
program2.command("datasets:list").option("--project <projectId>").description("List datasets").action(async (options) => {
|
|
9548
|
-
printJson(await createClient(loadConfig()).datasets.list(options.project));
|
|
9666
|
+
printJson(await createClient(loadConfig()).datasets.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9549
9667
|
});
|
|
9550
9668
|
program2.command("datasets:get").argument("<datasetId>").description("Fetch dataset detail").action(async (datasetId) => {
|
|
9551
|
-
printJson(await createClient(loadConfig()).datasets.get(
|
|
9669
|
+
printJson(await createClient(loadConfig()).datasets.get(normalizeId(datasetId, "dataset", "Dataset ID")));
|
|
9552
9670
|
});
|
|
9553
9671
|
program2.command("datasets:delete").argument("<datasetId>").description("Delete a dataset").action(async (datasetId) => {
|
|
9554
|
-
|
|
9555
|
-
|
|
9672
|
+
const normalizedDatasetId = normalizeId(datasetId, "dataset", "Dataset ID");
|
|
9673
|
+
await createClient(loadConfig()).datasets.delete(normalizedDatasetId);
|
|
9674
|
+
printJson({ deleted: true, id: normalizedDatasetId });
|
|
9556
9675
|
});
|
|
9557
9676
|
program2.command("datasets:upload").argument("<file>").option("--project <projectId>").option("--name <name>").description("Upload a dataset through upload sessions").action(async (file, options) => {
|
|
9677
|
+
assertReadableFile(file, "Dataset path");
|
|
9558
9678
|
const config = loadConfig();
|
|
9679
|
+
const projectId = normalizeOptionalId(options.project ?? config.projectId, "project", "Project ID");
|
|
9680
|
+
const name = options.name === void 0 ? void 0 : normalizeHumanName(options.name, "Dataset name");
|
|
9559
9681
|
const dataset = await createClient(config).datasets.create({
|
|
9560
9682
|
path: file,
|
|
9561
9683
|
format: "chat_jsonl",
|
|
9562
|
-
name
|
|
9563
|
-
projectId
|
|
9684
|
+
name,
|
|
9685
|
+
projectId
|
|
9564
9686
|
});
|
|
9565
9687
|
printJson(dataset);
|
|
9566
9688
|
});
|
|
@@ -9579,12 +9701,11 @@ program2.command("runtime:detect").option("--project <projectId>").option("--rep
|
|
|
9579
9701
|
throw new Error("Provide --repo or --git.");
|
|
9580
9702
|
}
|
|
9581
9703
|
const config = loadConfig();
|
|
9582
|
-
|
|
9583
|
-
|
|
9584
|
-
|
|
9585
|
-
|
|
9586
|
-
|
|
9587
|
-
);
|
|
9704
|
+
const detection = await createClient(config).runtime.detect({
|
|
9705
|
+
projectId: requireProjectId(options, config),
|
|
9706
|
+
source: sourceOptions.source
|
|
9707
|
+
});
|
|
9708
|
+
printJson(redactInlineSource(detection));
|
|
9588
9709
|
});
|
|
9589
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) => {
|
|
9590
9711
|
const config = loadConfig();
|
|
@@ -9609,27 +9730,28 @@ program2.command("runs:quote").option("--project <projectId>").requiredOption("-
|
|
|
9609
9730
|
}
|
|
9610
9731
|
});
|
|
9611
9732
|
program2.command("runs:list").option("--project <projectId>").description("List runs").action(async (options) => {
|
|
9612
|
-
printJson(await createClient(loadConfig()).runs.list(options.project));
|
|
9733
|
+
printJson(await createClient(loadConfig()).runs.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9613
9734
|
});
|
|
9614
9735
|
program2.command("runs:status").argument("<runId>").description("Fetch full run detail").action(async (runId) => {
|
|
9615
|
-
printJson(await createClient(loadConfig()).runs.get(
|
|
9736
|
+
printJson(await createClient(loadConfig()).runs.get(normalizeId(runId, "run", "Run ID")));
|
|
9616
9737
|
});
|
|
9617
9738
|
program2.command("runs:logs").argument("<runId>").description("Fetch run logs").action(async (runId) => {
|
|
9618
|
-
printJson(await createClient(loadConfig()).runs.logs(
|
|
9739
|
+
printJson(await createClient(loadConfig()).runs.logs(normalizeId(runId, "run", "Run ID")));
|
|
9619
9740
|
});
|
|
9620
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");
|
|
9621
9743
|
printJson(
|
|
9622
|
-
await createClient(loadConfig()).runs.wait(
|
|
9744
|
+
await createClient(loadConfig()).runs.wait(normalizedRunId, {
|
|
9623
9745
|
timeoutMs: parseDurationMs(options.timeout),
|
|
9624
9746
|
pollMs: parsePositiveMs(options.poll, 1e3)
|
|
9625
9747
|
})
|
|
9626
9748
|
);
|
|
9627
9749
|
});
|
|
9628
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) => {
|
|
9629
|
-
await watchRun(
|
|
9751
|
+
await watchRun(normalizeId(runId, "run", "Run ID"), Boolean(options.json), parseDurationMs(options.timeout), parsePositiveMs(options.poll, 1e3));
|
|
9630
9752
|
});
|
|
9631
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) => {
|
|
9632
|
-
const usage = await createClient(loadConfig()).runs.usage(
|
|
9754
|
+
const usage = await createClient(loadConfig()).runs.usage(normalizeId(runId, "run", "Run ID"));
|
|
9633
9755
|
if (options.summary) {
|
|
9634
9756
|
printUsageSummary(usage);
|
|
9635
9757
|
} else {
|
|
@@ -9637,7 +9759,7 @@ program2.command("runs:usage").argument("<runId>").option("--summary", "print a
|
|
|
9637
9759
|
}
|
|
9638
9760
|
});
|
|
9639
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) => {
|
|
9640
|
-
const breakdown = await createClient(loadConfig()).runs.costBreakdown(
|
|
9762
|
+
const breakdown = await createClient(loadConfig()).runs.costBreakdown(normalizeId(runId, "run", "Run ID"));
|
|
9641
9763
|
if (options.summary) {
|
|
9642
9764
|
printUsageSummary(breakdown);
|
|
9643
9765
|
} else {
|
|
@@ -9645,22 +9767,22 @@ program2.command("runs:cost-breakdown").argument("<runId>").option("--summary",
|
|
|
9645
9767
|
}
|
|
9646
9768
|
});
|
|
9647
9769
|
program2.command("runs:allocation").argument("<runId>").description("Fetch run allocation").action(async (runId) => {
|
|
9648
|
-
printJson(await createClient(loadConfig()).runs.allocation(
|
|
9770
|
+
printJson(await createClient(loadConfig()).runs.allocation(normalizeId(runId, "run", "Run ID")));
|
|
9649
9771
|
});
|
|
9650
9772
|
program2.command("runs:configuration").argument("<runId>").description("Fetch run placement configuration").action(async (runId) => {
|
|
9651
|
-
printJson(await createClient(loadConfig()).runs.configuration(
|
|
9773
|
+
printJson(await createClient(loadConfig()).runs.configuration(normalizeId(runId, "run", "Run ID")));
|
|
9652
9774
|
});
|
|
9653
9775
|
program2.command("runs:explanation").argument("<runId>").description("Fetch run planning explanation").action(async (runId) => {
|
|
9654
|
-
printJson(await createClient(loadConfig()).runs.explanation(
|
|
9776
|
+
printJson(await createClient(loadConfig()).runs.explanation(normalizeId(runId, "run", "Run ID")));
|
|
9655
9777
|
});
|
|
9656
9778
|
program2.command("runs:cancel").argument("<runId>").description("Cancel a run").action(async (runId) => {
|
|
9657
|
-
printJson(await createClient(loadConfig()).runs.cancel(
|
|
9779
|
+
printJson(await createClient(loadConfig()).runs.cancel(normalizeId(runId, "run", "Run ID")));
|
|
9658
9780
|
});
|
|
9659
9781
|
program2.command("runs:resume").argument("<runId>").description("Resume a run from the latest checkpoint").action(async (runId) => {
|
|
9660
|
-
printJson(await createClient(loadConfig()).runs.resume(
|
|
9782
|
+
printJson(await createClient(loadConfig()).runs.resume(normalizeId(runId, "run", "Run ID")));
|
|
9661
9783
|
});
|
|
9662
9784
|
program2.command("runs:terminate").argument("<runId>").description("Terminate a queued, launchable, or active run").action(async (runId) => {
|
|
9663
|
-
printJson(await createClient(loadConfig()).runs.terminate(
|
|
9785
|
+
printJson(await createClient(loadConfig()).runs.terminate(normalizeId(runId, "run", "Run ID")));
|
|
9664
9786
|
});
|
|
9665
9787
|
program2.command("cluster:capacity").description("Fetch cluster capacity view").action(async () => {
|
|
9666
9788
|
printJson(await createClient(loadConfig()).cluster.getCapacity());
|
|
@@ -9678,20 +9800,33 @@ program2.command("deployments:list").description("List deployments").action(asyn
|
|
|
9678
9800
|
printJson(await createClient(loadConfig()).deployments.list());
|
|
9679
9801
|
});
|
|
9680
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) => {
|
|
9681
|
-
|
|
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 }));
|
|
9682
9811
|
});
|
|
9683
9812
|
program2.command("deployments:get").argument("<deploymentId>").description("Fetch a deployment").action(async (deploymentId) => {
|
|
9684
|
-
printJson(await createClient(loadConfig()).deployments.get(
|
|
9813
|
+
printJson(await createClient(loadConfig()).deployments.get(normalizeId(deploymentId, "deployment", "Deployment ID")));
|
|
9685
9814
|
});
|
|
9686
9815
|
program2.command("deployments:delete").argument("<deploymentId>").description("Delete a pending or cataloged deployment").action(async (deploymentId) => {
|
|
9687
|
-
|
|
9688
|
-
|
|
9816
|
+
const normalizedDeploymentId = normalizeId(deploymentId, "deployment", "Deployment ID");
|
|
9817
|
+
await createClient(loadConfig()).deployments.delete(normalizedDeploymentId);
|
|
9818
|
+
printJson({ deleted: true, id: normalizedDeploymentId });
|
|
9689
9819
|
});
|
|
9690
9820
|
program2.command("billing:summary").description("Fetch billing summary").action(async () => {
|
|
9691
9821
|
printJson(await createClient(loadConfig()).billing.getSummary());
|
|
9692
9822
|
});
|
|
9693
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) => {
|
|
9694
|
-
printJson(
|
|
9824
|
+
printJson(
|
|
9825
|
+
await createClient(loadConfig()).billing.getUsage(
|
|
9826
|
+
normalizeOptionalBillingUnit(options.unit),
|
|
9827
|
+
normalizeOptionalId(options.run, "run", "Run ID")
|
|
9828
|
+
)
|
|
9829
|
+
);
|
|
9695
9830
|
});
|
|
9696
9831
|
program2.command("billing:invoices").description("List invoices").action(async () => {
|
|
9697
9832
|
printJson(await createClient(loadConfig()).billing.listInvoices());
|
|
@@ -9724,7 +9859,7 @@ program2.command("api-keys:create").requiredOption("--name <name>").option("--ow
|
|
|
9724
9859
|
const config = loadConfig();
|
|
9725
9860
|
const session = await createClient(config).auth.me();
|
|
9726
9861
|
const apiKey = await createClient(config).apiKeys.create({
|
|
9727
|
-
name: options.name,
|
|
9862
|
+
name: normalizeHumanName(options.name, "API key name"),
|
|
9728
9863
|
ownerType: options.ownerType,
|
|
9729
9864
|
ownerId: options.ownerId ?? session.user.id,
|
|
9730
9865
|
scopes: parseApiKeyScopes(String(options.scopes))
|
|
@@ -9741,7 +9876,7 @@ program2.command("api-keys:revoke").argument("<apiKeyId>").description("Revoke a
|
|
|
9741
9876
|
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) => {
|
|
9742
9877
|
printJson(
|
|
9743
9878
|
await createClient(loadConfig()).serviceAccounts.create({
|
|
9744
|
-
name: options.name,
|
|
9879
|
+
name: normalizeHumanName(options.name, "Service account name"),
|
|
9745
9880
|
scopes: parseApiKeyScopes(String(options.scopes))
|
|
9746
9881
|
})
|
|
9747
9882
|
);
|