diff --git a/deno.lock b/deno.lock index 21d3fdb..6e7086d 100644 --- a/deno.lock +++ b/deno.lock @@ -2,6 +2,7 @@ "version": "5", "specifiers": { "jsr:@std/assert@^1.0.11": "1.0.14", + "jsr:@std/fmt@*": "1.0.8", "jsr:@std/internal@^1.0.10": "1.0.10", "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.24__react@18.3.1_@types+react@18.3.24_react@18.3.1", @@ -16,9 +17,12 @@ "npm:boxen@^8.0.1": "8.0.1", "npm:chrono-node@^2.7.6": "2.8.4", "npm:cli-progress@^3.12.0": "3.12.0", + "npm:cli-spinners@*": "3.2.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.24__react@18.3.1_@types+react@18.3.24_react@18.3.1", @@ -39,9 +43,12 @@ "npm:semver@^7.6.3": "7.7.2", "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:yaml@2.6.1": "2.6.1", + "npm:yn@*": "5.1.0" }, "jsr": { "@std/assert@1.0.14": { @@ -50,6 +57,9 @@ "jsr:@std/internal" ] }, + "@std/fmt@1.0.8": { + "integrity": "71e1fc498787e4434d213647a6e43e794af4fd393ef8f52062246e06f7e372b7" + }, "@std/internal@1.0.10": { "integrity": "e3be62ce42cab0e177c27698e5d9800122f67b766a0bea6ca4867886cbde8cf7" } @@ -344,7 +354,7 @@ "chrono-node@2.8.4": { "integrity": "sha512-F+Rq88qF3H2dwjnFrl3TZrn5v4ZO57XxeQ+AhuL1C685So1hdUV/hT/q8Ja5UbmPYEZfx8VrxFDa72Dgldcxpg==", "dependencies": [ - "dayjs" + "dayjs@1.11.18" ] }, "cli-boxes@3.0.0": { @@ -435,6 +445,9 @@ "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==" }, @@ -1036,6 +1049,9 @@ "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==" }, diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index 0c2a7b4..2d2c6a9 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -262,9 +262,30 @@ export function QuoteAndBuy(props: { options: SfBuyOptions }) { if (!pricePerGpuHour) { const quote = await getQuoteFromParsedSfBuyOptions(props.options); if (!quote) { - return logAndQuit( - "No quote found for the desired order. Try with a different start date, duration, or price.", - ); + // Build dynamic error message based on current parameters + let errorMessage = + "No availability for this time period and quantity.\n\n"; + // Use ANSI OSC 8 hyperlink format for terminals that support it + // Format: \x1b]8;;URL\x1b\\LINK_TEXT\x1b]8;;\x1b\\ + const url = "https://sfcompute.com/dashboard/zones"; + errorMessage += + `Check current availability: \x1b]8;;${url}\x1b\\${url}\x1b]8;;\x1b\\ (2 min delay)\n`; + + // Only suggest starting later if -s flag was not used (defaults to "NOW") + const showStartSuggestion = props.options.start === "NOW"; + if (showStartSuggestion) { + errorMessage += "Try a different start time: sf buy -s +3h\n"; + } + + // Only suggest adjusting quantity if -n was specified and is larger than 8 + const showQuantitySuggestion = props.options.accelerators > 8; + if (showQuantitySuggestion) { + // Use "Or" prefix only if there's another suggestion before this one + const prefix = showStartSuggestion ? "Or adjust" : "Try adjusting"; + errorMessage += `${prefix} quantity: sf buy -n 8`; + } + + return logAndQuit(errorMessage.trim()); } pricePerGpuHour = getPricePerGpuHourFromQuote(quote);