trainfabric 0.1.31 → 0.1.33

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.
Files changed (2) hide show
  1. package/dist/index.cjs +57 -11
  2. package/package.json +1 -1
package/dist/index.cjs CHANGED
@@ -8923,7 +8923,17 @@ function collectRuntimeFiles(repoPath) {
8923
8923
  if (found.length >= maxFiles) {
8924
8924
  return;
8925
8925
  }
8926
- for (const entry of import_node_fs.default.readdirSync(currentPath, { withFileTypes: true })) {
8926
+ let entries;
8927
+ try {
8928
+ entries = import_node_fs.default.readdirSync(currentPath, { withFileTypes: true });
8929
+ } catch (error) {
8930
+ if (error.code === "EACCES" || error.code === "EPERM") {
8931
+ const relativePath = import_node_path2.default.relative(repoPath, currentPath) || ".";
8932
+ throw new Error(`Permission denied while scanning repository path: ${relativePath}`);
8933
+ }
8934
+ throw new Error(`Failed to scan repository path: ${currentPath}`);
8935
+ }
8936
+ for (const entry of entries) {
8927
8937
  const absolute = import_node_path2.default.join(currentPath, entry.name);
8928
8938
  const relative = import_node_path2.default.relative(repoPath, absolute).split(import_node_path2.default.sep).join("/");
8929
8939
  if (entry.isDirectory()) {
@@ -8935,13 +8945,30 @@ function collectRuntimeFiles(repoPath) {
8935
8945
  if (!entry.isFile() || !shouldIncludeFile(entry.name)) {
8936
8946
  continue;
8937
8947
  }
8938
- const stat = import_node_fs.default.statSync(absolute);
8948
+ let stat;
8949
+ try {
8950
+ stat = import_node_fs.default.statSync(absolute);
8951
+ } catch (error) {
8952
+ if (error.code === "EACCES" || error.code === "EPERM") {
8953
+ throw new Error(`Permission denied while scanning repository file: ${relative}`);
8954
+ }
8955
+ throw new Error(`Failed to inspect repository file: ${relative}`);
8956
+ }
8939
8957
  if (stat.size > maxFileBytes) {
8940
8958
  continue;
8941
8959
  }
8960
+ let content;
8961
+ try {
8962
+ content = import_node_fs.default.readFileSync(absolute, "utf8");
8963
+ } catch (error) {
8964
+ if (error.code === "EACCES" || error.code === "EPERM") {
8965
+ throw new Error(`Permission denied while reading repository file: ${relative}`);
8966
+ }
8967
+ throw new Error(`Failed to read repository file: ${relative}`);
8968
+ }
8942
8969
  found.push({
8943
8970
  path: relative,
8944
- content: import_node_fs.default.readFileSync(absolute, "utf8")
8971
+ content
8945
8972
  });
8946
8973
  }
8947
8974
  }
@@ -8985,7 +9012,22 @@ function validateGitBranch(value) {
8985
9012
  function validateRepoPath(repoPath) {
8986
9013
  const absolute = import_node_path2.default.resolve(repoPath);
8987
9014
  const parsed = import_node_path2.default.parse(absolute);
8988
- if (absolute === parsed.root || absolute === import_node_os.default.homedir()) {
9015
+ const unsafeRoots = /* @__PURE__ */ new Set([
9016
+ parsed.root,
9017
+ import_node_os.default.homedir(),
9018
+ import_node_path2.default.resolve(import_node_os.default.tmpdir()),
9019
+ import_node_path2.default.resolve("/tmp"),
9020
+ import_node_path2.default.resolve("/private/tmp"),
9021
+ import_node_path2.default.resolve("/var"),
9022
+ import_node_path2.default.resolve("/Users"),
9023
+ import_node_path2.default.resolve("/System"),
9024
+ import_node_path2.default.resolve("/Library"),
9025
+ import_node_path2.default.resolve("/Applications"),
9026
+ import_node_path2.default.resolve("/etc"),
9027
+ import_node_path2.default.resolve("/bin"),
9028
+ import_node_path2.default.resolve("/usr")
9029
+ ]);
9030
+ if (unsafeRoots.has(absolute)) {
8989
9031
  throw new Error(`Refusing to package unsafe repository path: ${repoPath}`);
8990
9032
  }
8991
9033
  let stat;
@@ -9175,6 +9217,9 @@ function normalizeLearningRate(lr) {
9175
9217
  function buildComputeSpec(options) {
9176
9218
  const gpuCount = normalizePositiveInteger(options.gpus, "GPU count", 1);
9177
9219
  const nodeCount = normalizePositiveInteger(options.nodes, "Node count", 1);
9220
+ if (nodeCount > gpuCount) {
9221
+ throw new Error("Node count cannot exceed GPU count. Request at least one GPU per node.");
9222
+ }
9178
9223
  const compute = {
9179
9224
  gpuCount,
9180
9225
  nodeCount,
@@ -9207,7 +9252,7 @@ function buildComputeSpec(options) {
9207
9252
 
9208
9253
  // src/index.ts
9209
9254
  var DEFAULT_TRAINFABRIC_API_URL2 = "https://api.trainfabric.com";
9210
- var CLI_VERSION = "0.1.31";
9255
+ var CLI_VERSION = "0.1.33";
9211
9256
  var CONFIG_DIR = import_node_path3.default.join(import_node_os2.default.homedir(), ".trainfabric");
9212
9257
  var CONFIG_PATH = import_node_path3.default.join(CONFIG_DIR, "config.json");
9213
9258
  var FALLBACK_SECRET_PATH = import_node_path3.default.join(CONFIG_DIR, "session.enc");
@@ -9731,14 +9776,15 @@ function normalizeHttpBaseUrl(value) {
9731
9776
  try {
9732
9777
  url = new URL(value);
9733
9778
  } catch {
9734
- throw new Error(`Invalid base URL "${value}". Use an absolute http(s) URL such as https://api.trainfabric.com.`);
9779
+ throw new Error(`Invalid base URL "${value}". Use an absolute HTTPS origin such as https://api.trainfabric.com.`);
9780
+ }
9781
+ const isLocalDevHost = url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
9782
+ if (url.protocol !== "https:" && !(isLocalDevHost && url.protocol === "http:")) {
9783
+ throw new Error(`Invalid base URL "${value}". Use HTTPS, or http://localhost for local development.`);
9735
9784
  }
9736
- if (url.protocol !== "https:" && url.protocol !== "http:") {
9737
- throw new Error(`Invalid base URL "${value}". Use an absolute http(s) URL such as https://api.trainfabric.com.`);
9785
+ if (url.pathname !== "/" || url.search || url.hash) {
9786
+ throw new Error(`Invalid base URL "${value}". Use the API origin without a path, query, or hash.`);
9738
9787
  }
9739
- url.pathname = url.pathname.replace(/\/+$/, "");
9740
- url.search = "";
9741
- url.hash = "";
9742
9788
  return url.toString().replace(/\/+$/, "");
9743
9789
  }
9744
9790
  async function login(config) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trainfabric",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "description": "Trainfabric CLI for launching GPU training jobs on the hosted Trainfabric backend.",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",