diff --git a/deno.json b/deno.json index 4b9bbe5..ae82a30 100644 --- a/deno.json +++ b/deno.json @@ -3,7 +3,7 @@ "dev": "deno run --watch main.ts" }, "imports": { - "@std/assert": "jsr:@std/assert@^1.0.11", + "@std/assert": "jsr:@std/assert@1.0.11", "cli-table3": "https://esm.sh/cli-table3@0.6.5", "yaml": "https://esm.sh/yaml@2.6.1", "semver": "https://esm.sh/semver@7.6.3", diff --git a/deno.lock b/deno.lock index dba6ecd..9b73dfb 100644 --- a/deno.lock +++ b/deno.lock @@ -1,13 +1,12 @@ { "version": "5", "specifiers": { - "jsr:@std/assert@^1.0.11": "1.0.11", - "jsr:@std/fmt@*": "1.0.8", + "jsr:@std/assert@1.0.11": "1.0.11", "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.26__react@18.3.1_@types+react@18.3.26_react@18.3.1", "npm:@inquirer/prompts@^5.1.2": "5.5.0", - "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.21": "0.1.0-alpha.21", + "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22": "0.1.0-alpha.22", "npm:@types/ms@~0.7.34": "0.7.34", "npm:@types/node@*": "24.2.0", "npm:@types/react@^18.3.20": "18.3.26", @@ -17,12 +16,9 @@ "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", - "npm:dayjs@1.11.13": "1.11.13", "npm:dayjs@^1.11.13": "1.11.18", "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.26__react@18.3.1_@types+react@18.3.26_react@18.3.1", @@ -43,12 +39,9 @@ "npm:semver@^7.6.3": "7.7.3", "npm:shescape@^2.1.1": "2.1.6", "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": { @@ -57,9 +50,6 @@ "jsr:@std/internal" ] }, - "@std/fmt@1.0.8": { - "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" - }, "@std/internal@1.0.12": { "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" } @@ -117,7 +107,7 @@ "@inquirer/figures", "@inquirer/type@2.0.0", "@types/mute-stream", - "@types/node@22.18.10", + "@types/node@22.18.12", "@types/wrap-ansi", "ansi-escapes@4.3.2", "cli-width", @@ -223,8 +213,8 @@ "mute-stream" ] }, - "@sfcompute/nodes-sdk-alpha@0.1.0-alpha.21": { - "integrity": "sha512-MPto7ojkRy/ywcgWgy/MRFQmcGt4AQln2Gf3X/EkUwB+vlJmf+e7WXkrCxiEiz5PmGNSYxv33adypEphrABzCw==" + "@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22": { + "integrity": "sha512-9ThdeBlN2+l3lInRI/cv07aM2aCEircki87a30TSzUxjuJJs0IQIi39pmkrQFBGIaON6YAATcDmQai2YUhOGHQ==" }, "@types/ms@0.7.34": { "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" @@ -235,8 +225,8 @@ "@types/node@24.2.0" ] }, - "@types/node@22.18.10": { - "integrity": "sha512-anNG/V/Efn/YZY4pRzbACnKxNKoBng2VTFydVu8RRs5hQjikP8CQfaeAV59VFSCzKNp90mXiVXW2QzV56rwMrg==", + "@types/node@22.18.12": { + "integrity": "sha512-BICHQ67iqxQGFSzfCFTT7MRQ5XcBjG5aeKh5Ok38UBbPe5fxTyE+aHFxwVrGyr8GNlqFMLKD1D3P2K/1ks8tog==", "dependencies": [ "undici-types@6.21.0" ] @@ -442,9 +432,6 @@ "date-fns@4.1.0": { "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==" }, - "dayjs@1.11.13": { - "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==" - }, "dayjs@1.11.18": { "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==" }, @@ -1046,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==" }, @@ -1067,7 +1051,7 @@ "https://esm.sh/camelcase@^8.0.0?target=denonext": "https://esm.sh/camelcase@8.0.0?target=denonext", "https://esm.sh/chalk@^5.3.0?target=denonext": "https://esm.sh/chalk@5.6.2?target=denonext", "https://esm.sh/cli-boxes@^3.0.0?target=denonext": "https://esm.sh/cli-boxes@3.0.0?target=denonext", - "https://esm.sh/emoji-regex@^10.3.0?target=denonext": "https://esm.sh/emoji-regex@10.5.0?target=denonext", + "https://esm.sh/emoji-regex@^10.3.0?target=denonext": "https://esm.sh/emoji-regex@10.6.0?target=denonext", "https://esm.sh/emoji-regex@^8.0.0?target=denonext": "https://esm.sh/emoji-regex@8.0.0?target=denonext", "https://esm.sh/get-east-asian-width@^1.0.0?target=denonext": "https://esm.sh/get-east-asian-width@1.4.0?target=denonext", "https://esm.sh/is-fullwidth-code-point@^3.0.0?target=denonext": "https://esm.sh/is-fullwidth-code-point@3.0.0?target=denonext", @@ -1115,8 +1099,8 @@ "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/utc.js": "2e41a0673e6e7c7c962983f1680911ef6feb27ded6007bc7705787ac1b2637b7", - "https://esm.sh/emoji-regex@10.5.0/denonext/emoji-regex.mjs": "12f243696a48c88a1d760f38b8f6bc278d0551f83de923e57f1547e5263bc738", - "https://esm.sh/emoji-regex@10.5.0?target=denonext": "b784b44eceec32acf6cd4386e5ee6ab38e935d9fa7d2e9507841d79c1ba3f94c", + "https://esm.sh/emoji-regex@10.6.0/denonext/emoji-regex.mjs": "bec0264591b37a7132b080ed5586e3fc577392b1444230d973d7209d04f4f6e5", + "https://esm.sh/emoji-regex@10.6.0?target=denonext": "0b0ed0e68f224d09d771339aaf83dd19d31c149e6fb1213ea3a12ef8ee977188", "https://esm.sh/emoji-regex@8.0.0/denonext/emoji-regex.mjs": "3969ad7b107b6f585354a5019c24320e3d91d6bb70a96e2beead925247aed9dc", "https://esm.sh/emoji-regex@8.0.0?target=denonext": "60f2b0edb22d249525b4b284b0268e1d3a04a5e9dc4083e9e63c7372752e6de5", "https://esm.sh/get-east-asian-width@1.4.0/denonext/get-east-asian-width.mjs": "26d98853cab1abf823701e9f8594beab7b1d6e51a5c6d6cf151e653f7947796f", @@ -1173,14 +1157,14 @@ }, "workspace": { "dependencies": [ - "jsr:@std/assert@^1.0.11" + "jsr:@std/assert@1.0.11" ], "packageJson": { "dependencies": [ "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.21", + "npm:@sfcompute/nodes-sdk-alpha@0.1.0-alpha.22", "npm:@types/ms@~0.7.34", "npm:@types/react@^18.3.20", "npm:@types/semver@^7.5.8", diff --git a/package.json b/package.json index 20e2fc6..fdc5a9e 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.21", + "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.22", "@types/ms": "^0.7.34", "async-retry": "^1.3.3", "axios": "^1.8.4", diff --git a/src/lib/nodes/create.ts b/src/lib/nodes/create.ts index ca7acaa..9470658 100644 --- a/src/lib/nodes/create.ts +++ b/src/lib/nodes/create.ts @@ -31,6 +31,7 @@ import { roundStartDate, selectTime, } from "../../helpers/units.ts"; +import { formatDate } from "../../helpers/format-date.ts"; import { GPUS_PER_NODE } from "../constants.ts"; dayjs.extend(utc); @@ -253,9 +254,6 @@ async function createNodesAction( if (isReserved) { // Handle start time (options.start comes from parseStartDateOrNow parser) const startDate = options.start; - if (typeof startDate !== "string") { - createParams.start_at = Math.floor(startDate.getTime() / 1000); - } // Check if the start date is "NOW" or on an hour boundary const startDateIsValid = startDate === "NOW" || @@ -276,20 +274,21 @@ async function createNodesAction( : suggestedLowerStart.toDate(); } } - createParams.start_at = Math.floor( - (options.start === "NOW" - ? new Date().getTime() - : options.start.getTime()) / - 1000, - ); + // Pass undefined for "NOW" to avoid race conditions - the API will use current time + if (options.start !== "NOW") { + createParams.start_at = Math.floor(options.start.getTime() / 1000); + } // Handle end time and/or duration if (options.end || options.duration) { let endDate = options.end; + const endStartTime = typeof options.start === "string" + ? new Date() + : options.start; if (!endDate) { + // Use the actual start time (current time if "NOW", or the specified start) endDate = new Date( - new Date(createParams.start_at! * 1000).getTime() + - (options.duration! * 1000), + endStartTime.getTime() + (options.duration! * 1000), ); } @@ -297,6 +296,19 @@ async function createNodesAction( dayjs(endDate).startOf("hour"), ); if (!endDateIsValid) { + // If the start time was valid, show the user the start time so they're no confused about + // which time they're selecting + if (startDateIsValid) { + ora( + `Using start time: ${ + cyan( + `${formatDate(endStartTime, { forceIncludeTime: true })} ${ + dayjs(endStartTime).format("z") + }`, + ) + }`, + ).info(); + } if (!options.yes) { const selectedTime = await selectTime(endDate, { message: `End time must be on an hour boundary. ${