cassian-cli 0.3.4 → 0.3.5

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.
@@ -2,11 +2,8 @@ export interface InitOptions {
2
2
  name?: string;
3
3
  gpu?: string;
4
4
  gpuCount?: string;
5
- memory?: string;
6
- shm?: string;
7
5
  disk?: string;
8
- setup?: string;
9
- syncignore?: string;
6
+ storage?: string;
10
7
  yes?: boolean;
11
8
  }
12
9
  export declare function init(options?: InitOptions): Promise<void>;
@@ -64,33 +64,18 @@ function promptChoice(rl, question, choices, fallback) {
64
64
  });
65
65
  }
66
66
  function buildYaml(opts) {
67
- const volName = opts.name.replace(/[^a-z0-9-]/g, "-") + "-data";
68
- const setupLine = opts.setup ? ` setup: ${opts.setup}\n` : "";
69
- const noSyncLines = opts.noSync.length
70
- ? ` no_sync:\n${opts.noSync.map(p => ` - "${p}"`).join("\n")}\n` : "";
71
67
  const storageLines = opts.storage.length
72
- ? ` storage:\n${opts.storage.map(p => ` - "${p}"`).join("\n")}\n` : "";
68
+ ? `\nstorage:\n${opts.storage.map(p => ` - ${p}`).join("\n")}\n` : "";
73
69
  const excludeLines = opts.exclude.length
74
- ? ` exclude:\n${opts.exclude.map(p => ` - "${p}"`).join("\n")}\n` : "";
70
+ ? `\nworkspace:\n exclude:\n${opts.exclude.map(p => ` - "${p}"`).join("\n")}\n` : "";
75
71
  return `name: ${opts.name}
76
72
 
77
73
  gpu:
78
74
  count: ${opts.gpuCount}
79
75
  type: ${opts.gpuType}
80
76
 
81
- image: default
82
-
83
- volumes:
84
- - name: ${volName}
85
- size: ${opts.diskSize}
86
- mount: /workspace
87
-
88
- resources:
89
- memory: ${opts.memory}
90
- shm_size: ${opts.shmSize}
91
-
92
- workspace:
93
- ${setupLine}${noSyncLines}${storageLines}${excludeLines}`;
77
+ disk: ${opts.diskSize}
78
+ ${storageLines}${excludeLines}`;
94
79
  }
95
80
  export async function init(options = {}) {
96
81
  if (existsSync("cassian.yaml")) {
@@ -98,18 +83,12 @@ export async function init(options = {}) {
98
83
  }
99
84
  const dirName = process.cwd().split("/").pop().toLowerCase().replace(/[^a-z0-9-]/g, "-");
100
85
  const skipPrompts = options.yes ?? false;
101
- // If all required flags are provided, skip the wizard entirely
102
- const allFlagsProvided = !!(options.name && options.gpu && options.gpuCount && options.memory && options.disk);
103
- if (allFlagsProvided || skipPrompts) {
86
+ if (skipPrompts) {
104
87
  const yaml = buildYaml({
105
88
  name: options.name || dirName,
106
89
  gpuType: options.gpu || "rtx3090",
107
90
  gpuCount: parseInt(options.gpuCount || "1"),
108
- memory: options.memory || "32G",
109
- shmSize: options.shm || "16G",
110
91
  diskSize: options.disk || "50G",
111
- setup: options.setup || (existsSync("requirements.txt") ? "pip install -r /workspace/requirements.txt" : ""),
112
- noSync: ["checkpoints/", "outputs/"],
113
92
  storage: [],
114
93
  exclude: ["node_modules/", ".venv/", "__pycache__/", "*.pyc", "wandb/"],
115
94
  });
@@ -125,7 +104,7 @@ export async function init(options = {}) {
125
104
  const rl = createInterface({ input: process.stdin, output: process.stdout });
126
105
  console.log();
127
106
  console.log(bold(" Let's set up your Cassian project."));
128
- console.log(dim(" Press enter to accept defaults. Or pass flags to skip: cassian init --help\n"));
107
+ console.log(dim(" Press enter to accept defaults.\n"));
129
108
  const name = options.name || await prompt(rl, ` Instance name ${dim(`[${dirName}]`)}: `, dirName);
130
109
  console.log();
131
110
  const availability = await fetchAvailability();
@@ -140,48 +119,28 @@ export async function init(options = {}) {
140
119
  ], "1");
141
120
  const gpuCount = parseInt(gpuCountStr);
142
121
  console.log();
143
- const memory = options.memory || await promptChoice(rl, " RAM:", [
144
- { label: "16 GB", value: "16G" }, { label: "32 GB", value: "32G", hint: "recommended" },
145
- { label: "64 GB", value: "64G" }, { label: "128 GB", value: "128G" }, { label: "256 GB", value: "256G" },
146
- ], "32G");
147
- console.log();
148
- const shmSize = options.shm || await promptChoice(rl, " Shared memory (PyTorch DataLoader):", [
149
- { label: "4 GB", value: "4G" }, { label: "8 GB", value: "8G" },
150
- { label: "16 GB", value: "16G", hint: "recommended" }, { label: "32 GB", value: "32G" },
151
- ], "16G");
152
- console.log();
153
- const diskSize = options.disk || await promptChoice(rl, " Workspace disk size:", [
154
- { label: "20 GB", value: "20G" }, { label: "50 GB", value: "50G", hint: "recommended" },
155
- { label: "100 GB", value: "100G" }, { label: "200 GB", value: "200G" },
156
- { label: "500 GB", value: "500G", hint: "for large datasets / checkpoints" }, { label: "1 TB", value: "1T" },
122
+ const diskSize = options.disk || await promptChoice(rl, " Disk size (fast storage for code + training):", [
123
+ { label: "20 GB", value: "20G" },
124
+ { label: "50 GB", value: "50G", hint: "recommended" },
125
+ { label: "100 GB", value: "100G" },
126
+ { label: "200 GB", value: "200G" },
127
+ { label: "500 GB", value: "500G" },
157
128
  ], "50G");
158
129
  console.log();
159
- const hasReqs = existsSync("requirements.txt");
160
- const defaultSetup = hasReqs ? "pip install -r /workspace/requirements.txt" : existsSync("pyproject.toml") ? "pip install -e /workspace" : "";
161
- const setupRaw = options.setup ?? await prompt(rl, ` Setup command ${dim(`[${defaultSetup || "skip"}]`)}: `, defaultSetup);
162
- const setup = setupRaw === "skip" ? "" : setupRaw;
163
- console.log();
164
- console.log(dim(" Folders that stay on the GPU instance but don't sync to your machine"));
165
- console.log(dim(" (large files like checkpoints, training outputs — still persists across sessions)"));
166
- const noSyncRaw = options.syncignore ?? await prompt(rl, ` No-sync folders ${dim("[checkpoints/, outputs/]")}: `, "");
167
- const noSync = noSyncRaw
168
- ? noSyncRaw.split(",").map(s => s.trim()).filter(Boolean)
169
- : ["checkpoints/", "outputs/"];
170
- console.log();
171
- console.log(dim(" Folders streamed from cloud storage (huge datasets, pretrained models)"));
172
- console.log(dim(" (doesn't use instance disk — reads directly from cloud)"));
173
- const storageRaw = await prompt(rl, ` Cloud storage folders ${dim("[none]")}: `, "");
130
+ console.log(dim(" Large folders that need more storage (datasets, pretrained models)"));
131
+ console.log(dim(" These get unlimited storage, accessible at /workspace/<folder>"));
132
+ const storageRaw = options.storage ?? await prompt(rl, ` Storage folders ${dim("[none]")}: `, "");
174
133
  const storage = storageRaw
175
- ? storageRaw.split(",").map(s => s.trim()).filter(Boolean)
134
+ ? storageRaw.split(",").map(s => s.trim().replace(/\/+$/, "/")).filter(Boolean)
176
135
  : [];
177
136
  console.log();
178
- console.log(dim(" Folders to exclude entirely (build artifacts, can be recreated)"));
137
+ console.log(dim(" Folders to exclude (build artifacts, can be recreated)"));
179
138
  const excludeRaw = await prompt(rl, ` Exclude ${dim("[node_modules/, .venv/, __pycache__/, *.pyc, wandb/]")}: `, "");
180
139
  const exclude = excludeRaw
181
140
  ? excludeRaw.split(",").map(s => s.trim()).filter(Boolean)
182
141
  : ["node_modules/", ".venv/", "__pycache__/", "*.pyc", "wandb/"];
183
142
  rl.close();
184
- const yaml = buildYaml({ name, gpuType, gpuCount, memory, shmSize, diskSize, setup, noSync, storage, exclude });
143
+ const yaml = buildYaml({ name, gpuType, gpuCount, diskSize, storage, exclude });
185
144
  writeFileSync("cassian.yaml", yaml);
186
145
  console.log();
187
146
  console.log(` ${green("✓")} Created cassian.yaml`);
@@ -54,9 +54,15 @@ export async function up() {
54
54
  console.log();
55
55
  info("Instance", config.name);
56
56
  info("GPUs", `${config.gpu.count}x ${config.gpu.type || "any"}`);
57
- if (config.volumes?.length) {
57
+ if (config.disk) {
58
+ info("Disk", config.disk);
59
+ }
60
+ else if (config.volumes?.length) {
58
61
  info("Storage", config.volumes.map((v) => `${v.size} at ${v.mount}`).join(", "));
59
62
  }
63
+ if (config.storage?.length) {
64
+ info("Storage", config.storage.join(", "));
65
+ }
60
66
  console.log();
61
67
  const vm = dev.vm;
62
68
  if (vm) {
@@ -99,17 +105,19 @@ export async function up() {
99
105
  await new Promise((r) => setTimeout(r, 1000));
100
106
  spinner.text = "Starting instance...";
101
107
  }
108
+ // Build volumes: use new `disk` field or legacy `volumes` array
109
+ const volumes = config.volumes
110
+ ? config.volumes.map((v) => ({ name: v.name, size_gb: parseSizeToGb(v.size), mount: v.mount }))
111
+ : config.disk
112
+ ? [{ name: config.name, size_gb: parseSizeToGb(config.disk), mount: "/workspace" }]
113
+ : [];
102
114
  const instance = await client.post("/v1/instances", {
103
115
  name: config.name,
104
116
  gpu_count: config.gpu.count,
105
117
  gpu_type: config.gpu.type || null,
106
118
  image: config.image || "default",
107
119
  ssh_public_key: sshPubKey,
108
- volumes: (config.volumes || []).map((v) => ({
109
- name: v.name,
110
- size_gb: parseSizeToGb(v.size),
111
- mount: v.mount,
112
- })),
120
+ volumes,
113
121
  resources: {
114
122
  memory: config.resources?.memory || null,
115
123
  shm_size: config.resources?.shm_size || null,
@@ -117,7 +125,7 @@ export async function up() {
117
125
  },
118
126
  workspace: {
119
127
  no_sync: config.workspace?.no_sync || [],
120
- storage: config.workspace?.storage || [],
128
+ storage: config.storage || config.workspace?.storage || [],
121
129
  exclude: config.workspace?.exclude || config.workspace?.syncignore || [],
122
130
  },
123
131
  });
package/dist/index.js CHANGED
@@ -87,21 +87,15 @@ program
87
87
  .option("--name <name>", "Instance name")
88
88
  .option("--gpu <type>", "GPU type (e.g. h100-sxm, a100-sxm, rtx3090)")
89
89
  .option("--gpu-count <n>", "Number of GPUs")
90
- .option("--memory <size>", "RAM (e.g. 32G, 128G)")
91
- .option("--shm <size>", "Shared memory for PyTorch (e.g. 16G)")
92
- .option("--disk <size>", "Workspace disk size (e.g. 50G, 1T)")
93
- .option("--setup <cmd>", "Setup command to run after sync")
94
- .option("--syncignore <list>", "Comma-separated patterns to exclude from sync")
95
- .option("-y, --yes", "Skip all prompts, use defaults or provided flags")
90
+ .option("--disk <size>", "Disk size (e.g. 50G, 200G)")
91
+ .option("--storage <folders>", "Comma-separated storage folders (e.g. datasets/,models/)")
92
+ .option("-y, --yes", "Skip all prompts, use defaults")
96
93
  .action((opts) => init({
97
94
  name: opts.name,
98
95
  gpu: opts.gpu,
99
96
  gpuCount: opts.gpuCount,
100
- memory: opts.memory,
101
- shm: opts.shm,
102
97
  disk: opts.disk,
103
- setup: opts.setup,
104
- syncignore: opts.syncignore,
98
+ storage: opts.storage,
105
99
  yes: opts.yes,
106
100
  }));
107
101
  program.parse();
package/dist/types.d.ts CHANGED
@@ -11,6 +11,8 @@ export interface CassianConfig {
11
11
  type?: string;
12
12
  };
13
13
  image?: string;
14
+ disk?: string;
15
+ storage?: string[];
14
16
  volumes?: Array<{
15
17
  name: string;
16
18
  size: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cassian-cli",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "The Cassian GPU cloud CLI — provision GPUs, sync files, and run workloads from your terminal.",
5
5
  "type": "module",
6
6
  "bin": {