cassian-cli 0.3.4 → 0.3.6

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,7 @@ 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;
10
6
  yes?: boolean;
11
7
  }
12
8
  export declare function init(options?: InitOptions): Promise<void>;
@@ -64,33 +64,17 @@ 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
- const storageLines = opts.storage.length
72
- ? ` storage:\n${opts.storage.map(p => ` - "${p}"`).join("\n")}\n` : "";
67
+ const storageLine = opts.storage ? "\nstorage: true\n" : "";
73
68
  const excludeLines = opts.exclude.length
74
- ? ` exclude:\n${opts.exclude.map(p => ` - "${p}"`).join("\n")}\n` : "";
69
+ ? `\nworkspace:\n exclude:\n${opts.exclude.map(p => ` - "${p}"`).join("\n")}\n` : "";
75
70
  return `name: ${opts.name}
76
71
 
77
72
  gpu:
78
73
  count: ${opts.gpuCount}
79
74
  type: ${opts.gpuType}
80
75
 
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}`;
76
+ disk: ${opts.diskSize}
77
+ ${storageLine}${excludeLines}`;
94
78
  }
95
79
  export async function init(options = {}) {
96
80
  if (existsSync("cassian.yaml")) {
@@ -98,19 +82,13 @@ export async function init(options = {}) {
98
82
  }
99
83
  const dirName = process.cwd().split("/").pop().toLowerCase().replace(/[^a-z0-9-]/g, "-");
100
84
  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) {
85
+ if (skipPrompts) {
104
86
  const yaml = buildYaml({
105
87
  name: options.name || dirName,
106
88
  gpuType: options.gpu || "rtx3090",
107
89
  gpuCount: parseInt(options.gpuCount || "1"),
108
- memory: options.memory || "32G",
109
- shmSize: options.shm || "16G",
110
90
  diskSize: options.disk || "50G",
111
- setup: options.setup || (existsSync("requirements.txt") ? "pip install -r /workspace/requirements.txt" : ""),
112
- noSync: ["checkpoints/", "outputs/"],
113
- storage: [],
91
+ storage: true,
114
92
  exclude: ["node_modules/", ".venv/", "__pycache__/", "*.pyc", "wandb/"],
115
93
  });
116
94
  writeFileSync("cassian.yaml", yaml);
@@ -125,7 +103,7 @@ export async function init(options = {}) {
125
103
  const rl = createInterface({ input: process.stdin, output: process.stdout });
126
104
  console.log();
127
105
  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"));
106
+ console.log(dim(" Press enter to accept defaults.\n"));
129
107
  const name = options.name || await prompt(rl, ` Instance name ${dim(`[${dirName}]`)}: `, dirName);
130
108
  console.log();
131
109
  const availability = await fetchAvailability();
@@ -140,48 +118,26 @@ export async function init(options = {}) {
140
118
  ], "1");
141
119
  const gpuCount = parseInt(gpuCountStr);
142
120
  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" },
121
+ const diskSize = options.disk || await promptChoice(rl, " Disk size (fast storage for code + training):", [
122
+ { label: "20 GB", value: "20G" },
123
+ { label: "50 GB", value: "50G", hint: "recommended" },
124
+ { label: "100 GB", value: "100G" },
125
+ { label: "200 GB", value: "200G" },
126
+ { label: "500 GB", value: "500G" },
157
127
  ], "50G");
158
128
  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]")}: `, "");
174
- const storage = storageRaw
175
- ? storageRaw.split(",").map(s => s.trim()).filter(Boolean)
176
- : [];
129
+ console.log(dim(" Cloud storage gives you unlimited persistent space at /workspace/storage"));
130
+ console.log(dim(" Great for datasets, model weights, and large files"));
131
+ const storageAnswer = await prompt(rl, ` Enable cloud storage? ${dim("[Y/n]")}: `, "Y");
132
+ const storage = storageAnswer.toLowerCase() !== "n";
177
133
  console.log();
178
- console.log(dim(" Folders to exclude entirely (build artifacts, can be recreated)"));
134
+ console.log(dim(" Folders to exclude (build artifacts, can be recreated)"));
179
135
  const excludeRaw = await prompt(rl, ` Exclude ${dim("[node_modules/, .venv/, __pycache__/, *.pyc, wandb/]")}: `, "");
180
136
  const exclude = excludeRaw
181
137
  ? excludeRaw.split(",").map(s => s.trim()).filter(Boolean)
182
138
  : ["node_modules/", ".venv/", "__pycache__/", "*.pyc", "wandb/"];
183
139
  rl.close();
184
- const yaml = buildYaml({ name, gpuType, gpuCount, memory, shmSize, diskSize, setup, noSync, storage, exclude });
140
+ const yaml = buildYaml({ name, gpuType, gpuCount, diskSize, storage, exclude });
185
141
  writeFileSync("cassian.yaml", yaml);
186
142
  console.log();
187
143
  console.log(` ${green("✓")} Created cassian.yaml`);
@@ -54,8 +54,14 @@ 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) {
58
- info("Storage", config.volumes.map((v) => `${v.size} at ${v.mount}`).join(", "));
57
+ if (config.disk) {
58
+ info("Disk", config.disk);
59
+ }
60
+ else if (config.volumes?.length) {
61
+ info("Disk", config.volumes.map((v) => `${v.size} at ${v.mount}`).join(", "));
62
+ }
63
+ if (config.storage) {
64
+ info("Storage", "enabled (/workspace/storage)");
59
65
  }
60
66
  console.log();
61
67
  const vm = dev.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 === true ? ["storage"] : (Array.isArray(config.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,13 @@ 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("-y, --yes", "Skip all prompts, use defaults")
96
92
  .action((opts) => init({
97
93
  name: opts.name,
98
94
  gpu: opts.gpu,
99
95
  gpuCount: opts.gpuCount,
100
- memory: opts.memory,
101
- shm: opts.shm,
102
96
  disk: opts.disk,
103
- setup: opts.setup,
104
- syncignore: opts.syncignore,
105
97
  yes: opts.yes,
106
98
  }));
107
99
  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?: boolean | 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.6",
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": {