1- import { Command } from 'commander' ;
1+ import { Command , InvalidArgumentError } from 'commander' ;
22import kleur from 'kleur' ;
33
4+ export function parsePositiveSafeInteger ( value : string ) : number {
5+ const parsed = Number ( value ) ;
6+ if ( ! Number . isSafeInteger ( parsed ) || parsed < 1 ) {
7+ throw new InvalidArgumentError ( 'must be a positive safe integer' ) ;
8+ }
9+ return parsed ;
10+ }
11+
12+ export function parsePositiveFiniteNumber ( value : string ) : number {
13+ const parsed = Number ( value ) ;
14+ if ( ! Number . isFinite ( parsed ) || parsed <= 0 ) {
15+ throw new InvalidArgumentError ( 'must be a positive finite number' ) ;
16+ }
17+ return parsed ;
18+ }
19+
420export const deployCmd = new Command ( 'deploy' )
521 . description ( 'Provision cloud infrastructure — VPS, GPU, bare metal, managed databases, object storage' )
622 . action ( ( ) => {
@@ -21,10 +37,10 @@ deployCmd
2137 . command ( 'quote' )
2238 . description ( 'Price-check a spec across every connected provider before provisioning' )
2339 . requiredOption ( '--kind <kind>' , 'cpu-vps | gpu | bare-metal | managed-db | block-storage | object-storage' )
24- . option ( '--cpu <n>' , 'vCPU count' , Number )
25- . option ( '--memory <gb>' , 'RAM in GB' , Number )
40+ . option ( '--cpu <n>' , 'vCPU count' , parsePositiveSafeInteger )
41+ . option ( '--memory <gb>' , 'RAM in GB' , parsePositiveFiniteNumber )
2642 . option ( '--gpu <model>' , 'GPU model, e.g. A100, H100, RTX-4090' )
27- . option ( '--gpu-count <n>' , 'GPUs per instance' , Number )
43+ . option ( '--gpu-count <n>' , 'GPUs per instance' , parsePositiveSafeInteger )
2844 . option ( '--region <id>' )
2945 . option ( '--spot' , 'accept interruptible / spot instances for lower price' )
3046 . action ( ( opts ) => {
@@ -37,14 +53,14 @@ deployCmd
3753 . description ( 'Spin up a new instance (WILL start billing — pair with a --max-hourly-price guardrail)' )
3854 . requiredOption ( '--provider <id>' , 'e.g. cloud-runpod, cloud-digitalocean' )
3955 . requiredOption ( '--kind <kind>' )
40- . option ( '--cpu <n>' , 'vCPU count' , Number )
41- . option ( '--memory <gb>' , 'memory in GB' , Number )
56+ . option ( '--cpu <n>' , 'vCPU count' , parsePositiveSafeInteger )
57+ . option ( '--memory <gb>' , 'memory in GB' , parsePositiveFiniteNumber )
4258 . option ( '--gpu <model>' , 'GPU model, e.g. A100, H100' )
43- . option ( '--gpu-count <n>' , 'number of GPUs' , Number )
59+ . option ( '--gpu-count <n>' , 'number of GPUs' , parsePositiveSafeInteger )
4460 . option ( '--region <id>' )
4561 . option ( '--image <name>' )
4662 . option ( '--spot' )
47- . option ( '--max-hourly-price <usd>' , 'abort if quote exceeds this (strongly recommended for GPU)' , Number )
63+ . option ( '--max-hourly-price <usd>' , 'abort if quote exceeds this (strongly recommended for GPU)' , parsePositiveFiniteNumber )
4864 . option ( '--dry-run' , 'show the plan without starting a bill' )
4965 . action ( ( opts ) => {
5066 if ( opts . kind === 'gpu' && ! opts . maxHourlyPrice ) {
0 commit comments