diff --git a/deno.lock b/deno.lock index 78ad3fe8..a77c69c9 100644 --- a/deno.lock +++ b/deno.lock @@ -2,27 +2,24 @@ "version": "5", "specifiers": { "jsr:@std/assert@1.0.11": "1.0.11", - "jsr:@std/fmt@*": "1.0.8", "jsr:@std/internal@^1.0.5": "1.0.12", "npm:@commander-js/extra-typings@^12.1.0": "12.1.0_commander@12.1.0", "npm:@inkjs/ui@2": "2.0.0_ink@5.2.1__@types+react@18.3.27__react@18.3.1_@types+react@18.3.27_react@18.3.1", "npm:@inquirer/prompts@^5.1.2": "5.5.0", - "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22": "0.1.0-alpha.22", + "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27": "0.1.0-alpha.27", "npm:@types/ms@~0.7.34": "0.7.34", "npm:@types/node@*": "24.2.0", "npm:@types/react@^18.3.20": "18.3.27", "npm:@types/semver@^7.5.8": "7.7.1", "npm:async-retry@^1.3.3": "1.3.3", "npm:axios@^1.8.4": "1.13.2", - "npm:boxen@8.0.1": "8.0.1", "npm:boxen@^8.0.1": "8.0.1", "npm:chrono-node@^2.9.0": "2.9.0", "npm:cli-progress@^3.12.0": "3.12.0", - "npm:cli-spinners@*": "3.3.0", "npm:cli-table3@0.6.5": "0.6.5", "npm:commander@^12.1.0": "12.1.0", "npm:date-fns@^4.1.0": "4.1.0", - "npm:dayjs@^1.11.13": "1.11.19", + "npm:dayjs@^1.11.19": "1.11.19", "npm:dotenv@^16.4.5": "16.6.1", "npm:ink-link@^4.1.0": "4.1.0_ink@5.2.1__@types+react@18.3.27__react@18.3.1_@types+react@18.3.27_react@18.3.1", "npm:ink-spinner@5": "5.0.0_ink@5.2.1__@types+react@18.3.27__react@18.3.1_react@18.3.1_@types+react@18.3.27", @@ -37,17 +34,14 @@ "npm:ora@^8.1.0": "8.2.0", "npm:parse-duration@^2.1.3": "2.1.4", "npm:posthog-node@^4.10.1": "4.18.0", - "npm:prettier@^3.5.3": "3.6.2", + "npm:prettier@^3.5.3": "3.7.4", "npm:react@^18.3.1": "18.3.1", "npm:semver@^7.6.3": "7.7.3", "npm:shescape@^2.1.1": "2.1.7", "npm:tiny-invariant@^1.3.3": "1.3.3", - "npm:tweetnacl-util@*": "0.15.1", "npm:tweetnacl-util@~0.15.1": "0.15.1", - "npm:tweetnacl@*": "1.0.3", "npm:tweetnacl@^1.0.3": "1.0.3", - "npm:yaml@2.6.1": "2.6.1", - "npm:yn@*": "5.1.0" + "npm:yaml@2.6.1": "2.6.1" }, "jsr": { "@std/assert@1.0.11": { @@ -56,9 +50,6 @@ "jsr:@std/internal" ] }, - "@std/fmt@1.0.8": { - "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" - }, "@std/internal@1.0.12": { "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" } @@ -222,8 +213,8 @@ "mute-stream" ] }, - "@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22": { - "integrity": "sha512-9ThdeBlN2+l3lInRI/cv07aM2aCEircki87a30TSzUxjuJJs0IQIi39pmkrQFBGIaON6YAATcDmQai2YUhOGHQ==" + "@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27": { + "integrity": "sha512-IcnuTpfZszCLpBtEOFCas0fhc0yAgCvGZ8JvN9DdM1zWMUswHyuumYKDEVt6ZUXjJVnMt09Uhqh2sWHOEer+rQ==" }, "@types/ms@0.7.34": { "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" @@ -816,8 +807,8 @@ "axios" ] }, - "prettier@3.6.2": { - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "prettier@3.7.4": { + "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", "bin": true }, "prop-types@15.8.1": { @@ -1042,9 +1033,6 @@ "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", "bin": true }, - "yn@5.1.0": { - "integrity": "sha512-TfXLvT6eVsBNIm8rAXTwJYdQFtOXaHQ+rA7LU8HL8C/BFfaSfhvFE5T1rHAdBCbAj808HaqjXVkmo8jmeGOqhw==" - }, "yoctocolors-cjs@2.1.3": { "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==" }, @@ -1053,7 +1041,6 @@ } }, "redirects": { - "https://deno.land/std/assert/mod.ts": "https://deno.land/std@0.224.0/assert/mod.ts", "https://esm.sh/@colors/colors/safe?target=denonext": "https://esm.sh/@colors/colors@1.6.0/safe?target=denonext", "https://esm.sh/@types/ansi-align@~3.0.5/index.d.ts": "https://esm.sh/@types/ansi-align@3.0.5/index.d.ts", "https://esm.sh/@types/semver@~7.7.1/index.d.ts": "https://esm.sh/@types/semver@7.7.1/index.d.ts", @@ -1079,42 +1066,6 @@ "https://esm.sh/yn": "https://esm.sh/yn@5.1.0" }, "remote": { - "https://deno.land/std@0.112.0/fmt/colors.ts": "8368ddf2d48dfe413ffd04cdbb7ae6a1009cf0dccc9c7ff1d76259d9c61a0621", - "https://deno.land/std@0.112.0/testing/_diff.ts": "ccd6c3af6e44c74bf1591acb1361995f5f50df64323a6e7fb3f16c8ea792c940", - "https://deno.land/std@0.112.0/testing/asserts.ts": "cb82284da34f9e863250efacb985886336a9bbdcd2cf81cc9311a32aff53da35", - "https://deno.land/std@0.224.0/assert/_constants.ts": "a271e8ef5a573f1df8e822a6eb9d09df064ad66a4390f21b3e31f820a38e0975", - "https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834", - "https://deno.land/std@0.224.0/assert/assert_almost_equals.ts": "9e416114322012c9a21fa68e187637ce2d7df25bcbdbfd957cd639e65d3cf293", - "https://deno.land/std@0.224.0/assert/assert_array_includes.ts": "14c5094471bc8e4a7895fc6aa5a184300d8a1879606574cb1cd715ef36a4a3c7", - "https://deno.land/std@0.224.0/assert/assert_equals.ts": "3bbca947d85b9d374a108687b1a8ba3785a7850436b5a8930d81f34a32cb8c74", - "https://deno.land/std@0.224.0/assert/assert_exists.ts": "43420cf7f956748ae6ed1230646567b3593cb7a36c5a5327269279c870c5ddfd", - "https://deno.land/std@0.224.0/assert/assert_false.ts": "3e9be8e33275db00d952e9acb0cd29481a44fa0a4af6d37239ff58d79e8edeff", - "https://deno.land/std@0.224.0/assert/assert_greater.ts": "5e57b201fd51b64ced36c828e3dfd773412c1a6120c1a5a99066c9b261974e46", - "https://deno.land/std@0.224.0/assert/assert_greater_or_equal.ts": "9870030f997a08361b6f63400273c2fb1856f5db86c0c3852aab2a002e425c5b", - "https://deno.land/std@0.224.0/assert/assert_instance_of.ts": "e22343c1fdcacfaea8f37784ad782683ec1cf599ae9b1b618954e9c22f376f2c", - "https://deno.land/std@0.224.0/assert/assert_is_error.ts": "f856b3bc978a7aa6a601f3fec6603491ab6255118afa6baa84b04426dd3cc491", - "https://deno.land/std@0.224.0/assert/assert_less.ts": "60b61e13a1982865a72726a5fa86c24fad7eb27c3c08b13883fb68882b307f68", - "https://deno.land/std@0.224.0/assert/assert_less_or_equal.ts": "d2c84e17faba4afe085e6c9123a63395accf4f9e00150db899c46e67420e0ec3", - "https://deno.land/std@0.224.0/assert/assert_match.ts": "ace1710dd3b2811c391946954234b5da910c5665aed817943d086d4d4871a8b7", - "https://deno.land/std@0.224.0/assert/assert_not_equals.ts": "78d45dd46133d76ce624b2c6c09392f6110f0df9b73f911d20208a68dee2ef29", - "https://deno.land/std@0.224.0/assert/assert_not_instance_of.ts": "3434a669b4d20cdcc5359779301a0588f941ffdc2ad68803c31eabdb4890cf7a", - "https://deno.land/std@0.224.0/assert/assert_not_match.ts": "df30417240aa2d35b1ea44df7e541991348a063d9ee823430e0b58079a72242a", - "https://deno.land/std@0.224.0/assert/assert_not_strict_equals.ts": "37f73880bd672709373d6dc2c5f148691119bed161f3020fff3548a0496f71b8", - "https://deno.land/std@0.224.0/assert/assert_object_match.ts": "411450fd194fdaabc0089ae68f916b545a49d7b7e6d0026e84a54c9e7eed2693", - "https://deno.land/std@0.224.0/assert/assert_rejects.ts": "4bee1d6d565a5b623146a14668da8f9eb1f026a4f338bbf92b37e43e0aa53c31", - "https://deno.land/std@0.224.0/assert/assert_strict_equals.ts": "b4f45f0fd2e54d9029171876bd0b42dd9ed0efd8f853ab92a3f50127acfa54f5", - "https://deno.land/std@0.224.0/assert/assert_string_includes.ts": "496b9ecad84deab72c8718735373feb6cdaa071eb91a98206f6f3cb4285e71b8", - "https://deno.land/std@0.224.0/assert/assert_throws.ts": "c6508b2879d465898dab2798009299867e67c570d7d34c90a2d235e4553906eb", - "https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917", - "https://deno.land/std@0.224.0/assert/equal.ts": "bddf07bb5fc718e10bb72d5dc2c36c1ce5a8bdd3b647069b6319e07af181ac47", - "https://deno.land/std@0.224.0/assert/fail.ts": "0eba674ffb47dff083f02ced76d5130460bff1a9a68c6514ebe0cdea4abadb68", - "https://deno.land/std@0.224.0/assert/mod.ts": "48b8cb8a619ea0b7958ad7ee9376500fe902284bb36f0e32c598c3dc34cbd6f3", - "https://deno.land/std@0.224.0/assert/unimplemented.ts": "8c55a5793e9147b4f1ef68cd66496b7d5ba7a9e7ca30c6da070c1a58da723d73", - "https://deno.land/std@0.224.0/assert/unreachable.ts": "5ae3dbf63ef988615b93eb08d395dda771c96546565f9e521ed86f6510c29e19", - "https://deno.land/std@0.224.0/fmt/colors.ts": "508563c0659dd7198ba4bbf87e97f654af3c34eb56ba790260f252ad8012e1c5", - "https://deno.land/std@0.224.0/internal/diff.ts": "6234a4b493ebe65dc67a18a0eb97ef683626a1166a1906232ce186ae9f65f4e6", - "https://deno.land/std@0.224.0/internal/format.ts": "0a98ee226fd3d43450245b1844b47003419d34d210fa989900861c79820d21c2", - "https://deno.land/std@0.224.0/internal/mod.ts": "534125398c8e7426183e12dc255bb635d94e06d0f93c60a297723abe69d3b22e", "https://esm.sh/@colors/colors@1.6.0/denonext/safe.mjs": "6b343b0dd3738994881fba8a5933bf99015ccfa81620ef38de39d9ef78c51fdb", "https://esm.sh/@colors/colors@1.6.0/safe?target=denonext": "6affb64b51a87ea13b07ae8e13b329bacfb225a344bf6bc721656ae6b3abb2ad", "https://esm.sh/ansi-align@3.0.1/denonext/ansi-align.mjs": "0f4b86c34935f09a6bf23d184bb50b9c5df453c1a78527cbeaae9bb03af30a63", @@ -1141,12 +1092,12 @@ "https://esm.sh/dayjs@1.11.13/denonext/plugin/advancedFormat.mjs": "d70fe83416f063d309e4ad4f1486b252f7544952f5dcaccc22eea99b35fa9534", "https://esm.sh/dayjs@1.11.13/denonext/plugin/duration.mjs": "a24a30859a5253bba1c0ab23a67d759bbe4b6be86a8be5bf6c7624e7ea548127", "https://esm.sh/dayjs@1.11.13/denonext/plugin/relativeTime.mjs": "a2d9b5a2bc66374087650609b6a24dd83073fe9d11564e874b91c7388d4fefd5", - "https://esm.sh/dayjs@1.11.13/es2022/plugin/timezone.mjs": "94d48607684cc8423f815fee4e72bf334c801cdf52d79caa00481a7e83c345f4", + "https://esm.sh/dayjs@1.11.13/denonext/plugin/timezone.mjs": "94d48607684cc8423f815fee4e72bf334c801cdf52d79caa00481a7e83c345f4", "https://esm.sh/dayjs@1.11.13/es2022/plugin/utc.mjs": "e760160892182a9a92d2c92c3e74c381b678aa5034872422c880ec2d231c22ab", "https://esm.sh/dayjs@1.11.13/plugin/advancedFormat.js": "cbb82f21176632f2ad3590cc2c51356463d527be5a221545520d4a9034744fab", "https://esm.sh/dayjs@1.11.13/plugin/duration.js": "9f6f119d7af58b8e08424bf897daaa286cadaa63d58262140969102671296a51", "https://esm.sh/dayjs@1.11.13/plugin/relativeTime.js": "48ccbeb4a6e19c6320f41edc1028ac85e7baf8cd434faebe65e3208ec70cea6b", - "https://esm.sh/dayjs@1.11.13/plugin/timezone.js": "8885a15a7472e23b020957c28d9d49d3e5c00fd5b992bf5d733bfa4f952ed457", + "https://esm.sh/dayjs@1.11.13/plugin/timezone.js": "cfe2b0eec9855373de8fbc92d3503d25930ab3156522482e55f7a66bb3d4fb32", "https://esm.sh/dayjs@1.11.13/plugin/utc.js": "3c51f2f90816004281990ed17d5010311c2b77875663127cbc773dc440ae7dde", "https://esm.sh/emoji-regex@10.6.0/denonext/emoji-regex.mjs": "bec0264591b37a7132b080ed5586e3fc577392b1444230d973d7209d04f4f6e5", "https://esm.sh/emoji-regex@10.6.0?target=denonext": "0b0ed0e68f224d09d771339aaf83dd19d31c149e6fb1213ea3a12ef8ee977188", @@ -1213,7 +1164,7 @@ "npm:@commander-js/extra-typings@^12.1.0", "npm:@inkjs/ui@2", "npm:@inquirer/prompts@^5.1.2", - "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22", + "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27", "npm:@types/ms@~0.7.34", "npm:@types/react@^18.3.20", "npm:@types/semver@^7.5.8", @@ -1225,7 +1176,7 @@ "npm:cli-table3@0.6.5", "npm:commander@^12.1.0", "npm:date-fns@^4.1.0", - "npm:dayjs@^1.11.13", + "npm:dayjs@^1.11.19", "npm:dotenv@^16.4.5", "npm:ink-link@^4.1.0", "npm:ink-spinner@5", diff --git a/package.json b/package.json index f61a1f3a..f4326f7a 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@commander-js/extra-typings": "^12.1.0", "@inkjs/ui": "^2.0.0", "@inquirer/prompts": "^5.1.2", - "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.22", + "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", "@types/ms": "^0.7.34", "async-retry": "^1.3.3", "axios": "^1.8.4", @@ -20,7 +20,7 @@ "cli-table3": "0.6.5", "commander": "^12.1.0", "date-fns": "^4.1.0", - "dayjs": "^1.11.13", + "dayjs": "^1.11.19", "dotenv": "^16.4.5", "ink": "^5.2.0", "ink-link": "^4.1.0", diff --git a/src/lib/nodes/create.ts b/src/lib/nodes/create.ts index 927ec830..09dfda3e 100644 --- a/src/lib/nodes/create.ts +++ b/src/lib/nodes/create.ts @@ -21,7 +21,6 @@ import { pluralizeNodes, startOrNowOption, yesOption, - zoneOption, } from "./utils.ts"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; @@ -82,7 +81,18 @@ const create = new Command("create") "[Required: names or --count] Number of nodes to create with auto-generated names", validateCount, ) - .addOption(zoneOption) + .addOption( + new Option( + "-z, --zone ", + "[Required: zone or --any-zone if --auto is provided] Zone for your nodes", + ).conflicts("any-zone"), + ) + .addOption( + new Option( + "--any-zone", + "Use any zone that meets requirements", + ).conflicts("zone"), + ) .addOption(maxPriceOption) .addOption( new Option( @@ -125,41 +135,42 @@ const create = new Command("create") .addOption(jsonOption) .hook("preAction", (command) => { const names = command.args; - const { count, start, duration, end, auto, reserved } = command - .opts(); + const { count, start, duration, end, auto, reserved, anyZone, zone } = + command + .opts(); // Validate arguments if (names.length === 0 && !count) { - console.error( + command.error( red("Must specify either node names or use \`--count\` option\n"), ); - command.help(); - process.exit(1); } if (names.length > 0 && count) { if (names.length !== count) { - console.error(red( + command.error(red( `You specified ${names.length} ${ names.length === 1 ? "node name" : "node names" } but \`--count\` is set to ${count}. The number of names must match the \`count\`.\n`, )); - command.help(); - process.exit(1); } } + if (auto && !anyZone && !zone) { + command.error(red( + "If --auto is provided, you must specify a zone or use --any-zone\n", + )); + } + if (reserved && auto) { - console.error(red("Specify either --reserved or --auto, but not both\n")); - command.help(); - process.exit(1); + command.error(red( + "Specify either --reserved or --auto, but not both\n", + )); } // Validate duration/end like buy command if (typeof end !== "undefined" && typeof duration !== "undefined") { - console.error(red("Specify either --duration or --end, but not both\n")); - command.help(); - process.exit(1); + command.error(red("Specify either --duration or --end, but not both\n")); } // Validate that timing flags are only used with reserved nodes @@ -168,25 +179,21 @@ const create = new Command("create") (start !== "NOW" || typeof duration !== "undefined" || typeof end !== "undefined") ) { - console.error( + command.error( red( "Auto-reserved nodes start immediately and cannot have a start time, duration, or end time.\n", ), ); - command.help(); - process.exit(1); } if ( !auto && typeof duration === "undefined" && typeof end === "undefined" ) { - console.error( + command.error( red( "You must specify either --duration or --end to create a reserved node.\n", ), ); - command.help(); - process.exit(1); } }) .addHelpText( @@ -250,6 +257,7 @@ async function createNodesAction( desired_count: count, max_price_per_node_hour: options.maxPrice * 100, names: names.length > 0 ? names : undefined, + any_zone: options.anyZone ?? false, zone: options.zone, cloud_init_user_data: encodedUserData, image_id: options.image, @@ -393,10 +401,13 @@ async function createNodesAction( confirmationMessage += ` for ~$${ pricePerNodeHour.toFixed(2) }/node/hr`; + if ("zone" in quote) { + confirmationMessage += ` on ${cyan(quote.zone)}`; + } } else { logAndQuit( red( - "No nodes available matching your requirements. This is likely due to insufficient capacity.", + "No capacity available matching your hardware and pricing requirements. You can view zone capacity at https://sfcompute.com/dashboard/zones.", ), ); } @@ -405,6 +416,11 @@ async function createNodesAction( confirmationMessage += ` for up to $${ options.maxPrice.toFixed(2) }/node/hr`; + if (options.zone) { + confirmationMessage += ` on ${cyan(options.zone)}`; + } else { + confirmationMessage += ` on any matching zone`; + } } // Add node names at the end after a colon diff --git a/src/lib/nodes/list.tsx b/src/lib/nodes/list.tsx index 69ad6584..815ed5f1 100644 --- a/src/lib/nodes/list.tsx +++ b/src/lib/nodes/list.tsx @@ -311,7 +311,12 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { - + diff --git a/src/lib/nodes/utils.ts b/src/lib/nodes/utils.ts index e0a972de..047f5060 100644 --- a/src/lib/nodes/utils.ts +++ b/src/lib/nodes/utils.ts @@ -144,7 +144,7 @@ export function createNodesTable( getStatusColor(node.status), lastVm?.id ?? "", node.gpu_type, - node.zone || "N/A", + node.zone || (node.node_type === "autoreserved" ? "Any matching" : "N/A"), startEnd, maxPrice, ]); @@ -277,14 +277,6 @@ export const yesOption = new Option( "Skip confirmation prompt", ); -/** - * Common --zone option for zone selection - */ -export const zoneOption = new Option( - "-z, --zone ", - "[Required] Zone for your nodes", -).makeOptionMandatory(); - /** * Common --max-price option for nodes commands */