trainfabric 0.1.22 → 0.1.24
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 +94 -17
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -8816,6 +8816,11 @@ var import_node_crypto = __toESM(require("node:crypto"), 1);
|
|
|
8816
8816
|
var import_node_fs = __toESM(require("node:fs"), 1);
|
|
8817
8817
|
var import_node_os = __toESM(require("node:os"), 1);
|
|
8818
8818
|
var import_node_path2 = __toESM(require("node:path"), 1);
|
|
8819
|
+
var idPatterns = {
|
|
8820
|
+
org: /^org_[A-Za-z0-9_-]+$/,
|
|
8821
|
+
project: /^proj_[A-Za-z0-9_-]+$/,
|
|
8822
|
+
run: /^run_[A-Za-z0-9_-]+$/
|
|
8823
|
+
};
|
|
8819
8824
|
function collectRuntimeFiles(repoPath) {
|
|
8820
8825
|
const manifestFiles = /* @__PURE__ */ new Set([
|
|
8821
8826
|
"training.yaml",
|
|
@@ -8912,6 +8917,29 @@ function parseApiKeyScopes(input) {
|
|
|
8912
8917
|
}
|
|
8913
8918
|
return scopes;
|
|
8914
8919
|
}
|
|
8920
|
+
function normalizeHumanName(value, label) {
|
|
8921
|
+
let name = String(value ?? "").trim();
|
|
8922
|
+
while (name.length >= 2 && (name.startsWith("'") && name.endsWith("'") || name.startsWith('"') && name.endsWith('"'))) {
|
|
8923
|
+
name = name.slice(1, -1).trim();
|
|
8924
|
+
}
|
|
8925
|
+
if (!/[A-Za-z0-9]/.test(name)) {
|
|
8926
|
+
throw new Error(`${label} is required.`);
|
|
8927
|
+
}
|
|
8928
|
+
return name;
|
|
8929
|
+
}
|
|
8930
|
+
function normalizeId(value, kind, label) {
|
|
8931
|
+
const id = String(value ?? "").trim();
|
|
8932
|
+
if (!id || !idPatterns[kind].test(id)) {
|
|
8933
|
+
throw new Error(`${label} is invalid.`);
|
|
8934
|
+
}
|
|
8935
|
+
return id;
|
|
8936
|
+
}
|
|
8937
|
+
function normalizeOptionalId(value, kind, label) {
|
|
8938
|
+
if (value === void 0) {
|
|
8939
|
+
return void 0;
|
|
8940
|
+
}
|
|
8941
|
+
return normalizeId(value, kind, label);
|
|
8942
|
+
}
|
|
8915
8943
|
|
|
8916
8944
|
// src/run_input.ts
|
|
8917
8945
|
var BASE_MODEL_ALIASES = {
|
|
@@ -8954,7 +8982,7 @@ function buildComputeSpec(options) {
|
|
|
8954
8982
|
|
|
8955
8983
|
// src/index.ts
|
|
8956
8984
|
var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
|
|
8957
|
-
var CLI_VERSION = "0.1.
|
|
8985
|
+
var CLI_VERSION = "0.1.24";
|
|
8958
8986
|
var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
|
|
8959
8987
|
var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
|
|
8960
8988
|
var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
|
|
@@ -9293,7 +9321,7 @@ function visibleConfig(config) {
|
|
|
9293
9321
|
};
|
|
9294
9322
|
}
|
|
9295
9323
|
function requireProjectId(options, config) {
|
|
9296
|
-
const projectId = options.project ?? config.projectId;
|
|
9324
|
+
const projectId = normalizeOptionalId(options.project ?? config.projectId, "project", "Project ID");
|
|
9297
9325
|
if (!projectId) {
|
|
9298
9326
|
throw new Error("Project is required. Pass --project <projectId> or run `trainfabric config:set-project <projectId>`.");
|
|
9299
9327
|
}
|
|
@@ -9418,6 +9446,32 @@ function createClient(config) {
|
|
|
9418
9446
|
projectId: config.projectId
|
|
9419
9447
|
});
|
|
9420
9448
|
}
|
|
9449
|
+
function assertReadableFile(filePath, label) {
|
|
9450
|
+
let stats;
|
|
9451
|
+
try {
|
|
9452
|
+
stats = import_node_fs2.default.statSync(filePath);
|
|
9453
|
+
} catch {
|
|
9454
|
+
throw new Error(`${label} not found: ${filePath}`);
|
|
9455
|
+
}
|
|
9456
|
+
if (!stats.isFile()) {
|
|
9457
|
+
throw new Error(`${label} must be a file: ${filePath}`);
|
|
9458
|
+
}
|
|
9459
|
+
}
|
|
9460
|
+
function normalizeHttpBaseUrl(value) {
|
|
9461
|
+
let url;
|
|
9462
|
+
try {
|
|
9463
|
+
url = new URL(value);
|
|
9464
|
+
} catch {
|
|
9465
|
+
throw new Error(`Invalid base URL "${value}". Use an absolute http(s) URL such as https://api.trainfabric.com.`);
|
|
9466
|
+
}
|
|
9467
|
+
if (url.protocol !== "https:" && url.protocol !== "http:") {
|
|
9468
|
+
throw new Error(`Invalid base URL "${value}". Use an absolute http(s) URL such as https://api.trainfabric.com.`);
|
|
9469
|
+
}
|
|
9470
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
9471
|
+
url.search = "";
|
|
9472
|
+
url.hash = "";
|
|
9473
|
+
return url.toString().replace(/\/+$/, "");
|
|
9474
|
+
}
|
|
9421
9475
|
async function login(config) {
|
|
9422
9476
|
const apiKey = await promptApiKey();
|
|
9423
9477
|
if (!apiKey) {
|
|
@@ -9463,20 +9517,34 @@ program2.command("config:show").description("Show the active CLI configuration")
|
|
|
9463
9517
|
printJson(visibleConfig(loadConfig()));
|
|
9464
9518
|
});
|
|
9465
9519
|
program2.command("config:set-base-url").argument("<baseUrl>").description("Set the backend base URL").action((baseUrl) => {
|
|
9520
|
+
const normalizedBaseUrl = normalizeHttpBaseUrl(String(baseUrl));
|
|
9466
9521
|
const config = updateConfig((current) => {
|
|
9467
|
-
current.baseUrl =
|
|
9522
|
+
current.baseUrl = normalizedBaseUrl;
|
|
9468
9523
|
});
|
|
9469
9524
|
printJson(visibleConfig(config));
|
|
9470
9525
|
});
|
|
9471
|
-
program2.command("config:set-org").argument("<orgId>").description("Set the default organization ID").action((orgId) => {
|
|
9472
|
-
const
|
|
9473
|
-
|
|
9526
|
+
program2.command("config:set-org").argument("<orgId>").description("Set the default organization ID").action(async (orgId) => {
|
|
9527
|
+
const normalizedOrgId = normalizeId(orgId, "org", "Organization ID");
|
|
9528
|
+
const currentConfig = loadConfig();
|
|
9529
|
+
const organization = await createClient(currentConfig).organizations.get(normalizedOrgId);
|
|
9530
|
+
const updatedConfig = updateConfig((current) => {
|
|
9531
|
+
current.orgId = organization.id;
|
|
9532
|
+
if (current.projectId) {
|
|
9533
|
+
current.projectId = void 0;
|
|
9534
|
+
}
|
|
9474
9535
|
});
|
|
9475
|
-
printJson(visibleConfig(
|
|
9536
|
+
printJson(visibleConfig(updatedConfig));
|
|
9476
9537
|
});
|
|
9477
|
-
program2.command("config:set-project").argument("<projectId>").description("Set the default project ID").action((projectId) => {
|
|
9538
|
+
program2.command("config:set-project").argument("<projectId>").description("Set the default project ID").action(async (projectId) => {
|
|
9539
|
+
const normalizedProjectId = normalizeId(projectId, "project", "Project ID");
|
|
9540
|
+
const currentConfig = loadConfig();
|
|
9541
|
+
const project = await createClient(currentConfig).projects.get(normalizedProjectId);
|
|
9542
|
+
if (currentConfig.orgId && project.organizationId !== currentConfig.orgId) {
|
|
9543
|
+
throw new Error(`Project ${project.id} does not belong to configured organization ${currentConfig.orgId}.`);
|
|
9544
|
+
}
|
|
9478
9545
|
const config = updateConfig((current) => {
|
|
9479
|
-
current.projectId =
|
|
9546
|
+
current.projectId = project.id;
|
|
9547
|
+
current.orgId ??= project.organizationId;
|
|
9480
9548
|
});
|
|
9481
9549
|
printJson(visibleConfig(config));
|
|
9482
9550
|
});
|
|
@@ -9505,9 +9573,15 @@ program2.command("projects:list").description("List projects in the active organ
|
|
|
9505
9573
|
printJson(await createClient(loadConfig()).projects.list());
|
|
9506
9574
|
});
|
|
9507
9575
|
program2.command("projects:create").requiredOption("--name <name>").option("--org <organizationId>").description("Create a project").action(async (options) => {
|
|
9508
|
-
printJson(
|
|
9576
|
+
printJson(
|
|
9577
|
+
await createClient(loadConfig()).projects.create({
|
|
9578
|
+
name: normalizeHumanName(options.name, "Project name"),
|
|
9579
|
+
organizationId: normalizeOptionalId(options.org, "org", "Organization ID")
|
|
9580
|
+
})
|
|
9581
|
+
);
|
|
9509
9582
|
});
|
|
9510
9583
|
program2.command("datasets:validate").argument("<file>").description("Validate a local dataset file").action(async (file) => {
|
|
9584
|
+
assertReadableFile(file, "Dataset path");
|
|
9511
9585
|
const validation = await createClient(loadConfig()).datasets.validate({
|
|
9512
9586
|
path: file,
|
|
9513
9587
|
format: "chat_jsonl"
|
|
@@ -9518,7 +9592,7 @@ program2.command("datasets:validate").argument("<file>").description("Validate a
|
|
|
9518
9592
|
}
|
|
9519
9593
|
});
|
|
9520
9594
|
program2.command("datasets:list").option("--project <projectId>").description("List datasets").action(async (options) => {
|
|
9521
|
-
printJson(await createClient(loadConfig()).datasets.list(options.project));
|
|
9595
|
+
printJson(await createClient(loadConfig()).datasets.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9522
9596
|
});
|
|
9523
9597
|
program2.command("datasets:get").argument("<datasetId>").description("Fetch dataset detail").action(async (datasetId) => {
|
|
9524
9598
|
printJson(await createClient(loadConfig()).datasets.get(String(datasetId)));
|
|
@@ -9528,12 +9602,15 @@ program2.command("datasets:delete").argument("<datasetId>").description("Delete
|
|
|
9528
9602
|
printJson({ deleted: true, id: String(datasetId) });
|
|
9529
9603
|
});
|
|
9530
9604
|
program2.command("datasets:upload").argument("<file>").option("--project <projectId>").option("--name <name>").description("Upload a dataset through upload sessions").action(async (file, options) => {
|
|
9605
|
+
assertReadableFile(file, "Dataset path");
|
|
9531
9606
|
const config = loadConfig();
|
|
9607
|
+
const projectId = normalizeOptionalId(options.project ?? config.projectId, "project", "Project ID");
|
|
9608
|
+
const name = options.name === void 0 ? void 0 : normalizeHumanName(options.name, "Dataset name");
|
|
9532
9609
|
const dataset = await createClient(config).datasets.create({
|
|
9533
9610
|
path: file,
|
|
9534
9611
|
format: "chat_jsonl",
|
|
9535
|
-
name
|
|
9536
|
-
projectId
|
|
9612
|
+
name,
|
|
9613
|
+
projectId
|
|
9537
9614
|
});
|
|
9538
9615
|
printJson(dataset);
|
|
9539
9616
|
});
|
|
@@ -9582,7 +9659,7 @@ program2.command("runs:quote").option("--project <projectId>").requiredOption("-
|
|
|
9582
9659
|
}
|
|
9583
9660
|
});
|
|
9584
9661
|
program2.command("runs:list").option("--project <projectId>").description("List runs").action(async (options) => {
|
|
9585
|
-
printJson(await createClient(loadConfig()).runs.list(options.project));
|
|
9662
|
+
printJson(await createClient(loadConfig()).runs.list(normalizeOptionalId(options.project, "project", "Project ID")));
|
|
9586
9663
|
});
|
|
9587
9664
|
program2.command("runs:status").argument("<runId>").description("Fetch full run detail").action(async (runId) => {
|
|
9588
9665
|
printJson(await createClient(loadConfig()).runs.get(String(runId)));
|
|
@@ -9664,7 +9741,7 @@ program2.command("billing:summary").description("Fetch billing summary").action(
|
|
|
9664
9741
|
printJson(await createClient(loadConfig()).billing.getSummary());
|
|
9665
9742
|
});
|
|
9666
9743
|
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) => {
|
|
9667
|
-
printJson(await createClient(loadConfig()).billing.getUsage(options.unit, options.run));
|
|
9744
|
+
printJson(await createClient(loadConfig()).billing.getUsage(options.unit, normalizeOptionalId(options.run, "run", "Run ID")));
|
|
9668
9745
|
});
|
|
9669
9746
|
program2.command("billing:invoices").description("List invoices").action(async () => {
|
|
9670
9747
|
printJson(await createClient(loadConfig()).billing.listInvoices());
|
|
@@ -9697,7 +9774,7 @@ program2.command("api-keys:create").requiredOption("--name <name>").option("--ow
|
|
|
9697
9774
|
const config = loadConfig();
|
|
9698
9775
|
const session = await createClient(config).auth.me();
|
|
9699
9776
|
const apiKey = await createClient(config).apiKeys.create({
|
|
9700
|
-
name: options.name,
|
|
9777
|
+
name: normalizeHumanName(options.name, "API key name"),
|
|
9701
9778
|
ownerType: options.ownerType,
|
|
9702
9779
|
ownerId: options.ownerId ?? session.user.id,
|
|
9703
9780
|
scopes: parseApiKeyScopes(String(options.scopes))
|
|
@@ -9714,7 +9791,7 @@ program2.command("api-keys:revoke").argument("<apiKeyId>").description("Revoke a
|
|
|
9714
9791
|
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) => {
|
|
9715
9792
|
printJson(
|
|
9716
9793
|
await createClient(loadConfig()).serviceAccounts.create({
|
|
9717
|
-
name: options.name,
|
|
9794
|
+
name: normalizeHumanName(options.name, "Service account name"),
|
|
9718
9795
|
scopes: parseApiKeyScopes(String(options.scopes))
|
|
9719
9796
|
})
|
|
9720
9797
|
);
|