From 12d4b74adb4dfb5a1351cafddbf7ddb383276e84 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:21:24 -0800 Subject: [PATCH 01/48] chore: add Node.js configuration files for Deno migration - Add tsconfig.json with bundler moduleResolution for tsx runtime - Restore biome.json from git history for linting/formatting - Add vitest.config.ts for test runner configuration Part of Deno to Node.js migration (Phase 1: Setup) --- biome.json | 26 ++++++++++++++++++++++++++ tsconfig.json | 22 ++++++++++++++++++++++ vitest.config.ts | 12 ++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 biome.json create mode 100644 tsconfig.json create mode 100644 vitest.config.ts diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..6099e757 --- /dev/null +++ b/biome.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2 + }, + "linter": { + "enabled": true, + "rules": { + "correctness": { + "noUnusedImports": "warn" + }, + "suspicious": { + "noExplicitAny": "warn" + }, + "style": { + "noUselessElse": "off", + "useTemplate": "off" + }, + "recommended": true + } + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..b13dad9f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "lib": ["ES2024", "DOM"], + "module": "ESNext", + "moduleResolution": "bundler", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "types": ["node"], + "noEmit": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "jsx": "react-jsx", + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 00000000..d1eee0ac --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["src/**/*.test.ts"], + }, + resolve: { + alias: { + "@": "./src", + }, + }, +}); From 5c5da4b38dae2930afe9962d6b5c77eb4d49f278 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:21:30 -0800 Subject: [PATCH 02/48] chore: update package.json for Node.js runtime - Add "type": "module" for ESM support - Add pkg configuration for binary compilation - Update scripts: dev, prod, lint, check, test - Add devDependencies: typescript, tsx, vitest, @biomejs/biome, @yao-pkg/pkg - Add chalk dependency (replaces jsr:@std/fmt/colors) - Add type declarations: @types/cli-progress, @types/async-retry - Add bun.lock for bun package manager Part of Deno to Node.js migration (Phase 1: Setup) --- bun.lock | 870 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 40 ++- 2 files changed, 899 insertions(+), 11 deletions(-) create mode 100644 bun.lock diff --git a/bun.lock b/bun.lock new file mode 100644 index 00000000..c0d54e9b --- /dev/null +++ b/bun.lock @@ -0,0 +1,870 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "sf-cli", + "dependencies": { + "@commander-js/extra-typings": "^14.0.0", + "@inkjs/ui": "^2.0.0", + "@inquirer/prompts": "^8.2.0", + "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", + "@types/ms": "^0.7.34", + "async-retry": "^1.3.3", + "axios": "^1.8.4", + "boxen": "^8.0.1", + "chalk": "^5.3.0", + "chrono-node": "^2.9.0", + "cli-progress": "^3.12.0", + "cli-table3": "0.6.5", + "commander": "^14.0.2", + "date-fns": "^4.1.0", + "dayjs": "^1.11.19", + "dotenv": "^16.4.5", + "ink": "^5.2.0", + "ink-link": "^4.1.0", + "ink-spinner": "^5.0.0", + "ink-testing-library": "^4.0.0", + "ink-text-input": "^6.0.0", + "inquirer": "^13.2.0", + "little-date": "^1.0.0", + "ms": "^2.1.3", + "node-fetch": "^3.3.2", + "openapi-fetch": "^0.11.1", + "ora": "^8.1.0", + "parse-duration": "^2.1.3", + "posthog-node": "^4.10.1", + "prettier": "^3.5.3", + "react": "^18.3.1", + "semver": "^7.6.3", + "shescape": "^2.1.1", + "tiny-invariant": "^1.3.3", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1", + "yaml": "2.6.1", + "yn": "^5.1.0", + }, + "devDependencies": { + "@biomejs/biome": "^1.9.0", + "@types/async-retry": "^1.4.9", + "@types/cli-progress": "^3.11.6", + "@types/node": "^20.0.0", + "@types/react": "^18.3.20", + "@types/semver": "^7.5.8", + "@yao-pkg/pkg": "^5.12.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vitest": "^2.1.0", + }, + "peerDependencies": { + "typescript": "^5.6.2", + }, + }, + }, + "packages": { + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], + + "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/parser": ["@babel/parser@7.28.6", "", { "dependencies": { "@babel/types": "^7.28.6" }, "bin": "./bin/babel-parser.js" }, "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ=="], + + "@babel/runtime": ["@babel/runtime@7.28.6", "", {}, "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA=="], + + "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], + + "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + + "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], + + "@commander-js/extra-typings": ["@commander-js/extra-typings@14.0.0", "", { "peerDependencies": { "commander": "~14.0.0" } }, "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.2", "", { "os": "android", "cpu": "arm" }, "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.2", "", { "os": "android", "cpu": "arm64" }, "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.2", "", { "os": "android", "cpu": "x64" }, "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.2", "", { "os": "linux", "cpu": "arm" }, "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.2", "", { "os": "linux", "cpu": "none" }, "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.2", "", { "os": "none", "cpu": "x64" }, "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.2", "", { "os": "none", "cpu": "arm64" }, "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], + + "@inkjs/ui": ["@inkjs/ui@2.0.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-spinners": "^3.0.0", "deepmerge": "^4.3.1", "figures": "^6.1.0" }, "peerDependencies": { "ink": ">=5" } }, "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg=="], + + "@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="], + + "@inquirer/checkbox": ["@inquirer/checkbox@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-DrAMU3YBGMUAp6ArwTIp/25CNDtDbxk7UjIrrtM25JVVrlVYlVzHh5HR1BDFu9JMyUoZ4ZanzeaHqNDttf3gVg=="], + + "@inquirer/confirm": ["@inquirer/confirm@6.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-WdaPe7foUnoGYvXzH4jp4wH/3l+dBhZ3uwhKjXjwdrq5tEIFaANxj6zrGHxLdsIA0yKM0kFPVcEalOZXBB5ISA=="], + + "@inquirer/core": ["@inquirer/core@11.1.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3", "cli-width": "^4.1.0", "mute-stream": "^3.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^9.0.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-hV9o15UxX46OyQAtaoMqAOxGR8RVl1aZtDx1jHbCtSJy1tBdTfKxLPKf7utsE4cRy4tcmCQ4+vdV+ca+oNxqNA=="], + + "@inquirer/editor": ["@inquirer/editor@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/external-editor": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-QI3Jfqcv6UO2/VJaEFONH8Im1ll++Xn/AJTBn9Xf+qx2M+H8KZAdQ5sAe2vtYlo+mLW+d7JaMJB4qWtK4BG3pw=="], + + "@inquirer/expand": ["@inquirer/expand@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-0I/16YwPPP0Co7a5MsomlZLpch48NzYfToyqYAOWtBmaXSB80RiNQ1J+0xx2eG+Wfxt0nHtpEWSRr6CzNVnOGg=="], + + "@inquirer/external-editor": ["@inquirer/external-editor@2.0.3", "", { "dependencies": { "chardet": "^2.1.1", "iconv-lite": "^0.7.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-LgyI7Agbda74/cL5MvA88iDpvdXI2KuMBCGRkbCl2Dg1vzHeOgs+s0SDcXV7b+WZJrv2+ERpWSM65Fpi9VfY3w=="], + + "@inquirer/figures": ["@inquirer/figures@2.0.3", "", {}, "sha512-y09iGt3JKoOCBQ3w4YrSJdokcD8ciSlMIWsD+auPu+OZpfxLuyz+gICAQ6GCBOmJJt4KEQGHuZSVff2jiNOy7g=="], + + "@inquirer/input": ["@inquirer/input@5.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-4B3s3jvTREDFvXWit92Yc6jF1RJMDy2VpSqKtm4We2oVU65YOh2szY5/G14h4fHlyQdpUmazU5MPCFZPRJ0AOw=="], + + "@inquirer/number": ["@inquirer/number@4.0.4", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-CmMp9LF5HwE+G/xWsC333TlCzYYbXMkcADkKzcawh49fg2a1ryLc7JL1NJYYt1lJ+8f4slikNjJM9TEL/AljYQ=="], + + "@inquirer/password": ["@inquirer/password@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-ZCEPyVYvHK4W4p2Gy6sTp9nqsdHQCfiPXIP9LbJVW4yCinnxL/dDDmPaEZVysGrj8vxVReRnpfS2fOeODe9zjg=="], + + "@inquirer/prompts": ["@inquirer/prompts@8.2.0", "", { "dependencies": { "@inquirer/checkbox": "^5.0.4", "@inquirer/confirm": "^6.0.4", "@inquirer/editor": "^5.0.4", "@inquirer/expand": "^5.0.4", "@inquirer/input": "^5.0.4", "@inquirer/number": "^4.0.4", "@inquirer/password": "^5.0.4", "@inquirer/rawlist": "^5.2.0", "@inquirer/search": "^4.1.0", "@inquirer/select": "^5.0.4" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-rqTzOprAj55a27jctS3vhvDDJzYXsr33WXTjODgVOru21NvBo9yIgLIAf7SBdSV0WERVly3dR6TWyp7ZHkvKFA=="], + + "@inquirer/rawlist": ["@inquirer/rawlist@5.2.0", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-CciqGoOUMrFo6HxvOtU5uL8fkjCmzyeB6fG7O1vdVAZVSopUBYECOwevDBlqNLyyYmzpm2Gsn/7nLrpruy9RFg=="], + + "@inquirer/search": ["@inquirer/search@4.1.0", "", { "dependencies": { "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-EAzemfiP4IFvIuWnrHpgZs9lAhWDA0GM3l9F4t4mTQ22IFtzfrk8xbkMLcAN7gmVML9O/i+Hzu8yOUyAaL6BKA=="], + + "@inquirer/select": ["@inquirer/select@5.0.4", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/figures": "^2.0.3", "@inquirer/type": "^4.0.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-s8KoGpPYMEQ6WXc0dT9blX2NtIulMdLOO3LA1UKOiv7KFWzlJ6eLkEYTDBIi+JkyKXyn8t/CD6TinxGjyLt57g=="], + + "@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="], + + "@sfcompute/nodes-sdk-alpha": ["@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27", "", {}, "sha512-IcnuTpfZszCLpBtEOFCas0fhc0yAgCvGZ8JvN9DdM1zWMUswHyuumYKDEVt6ZUXjJVnMt09Uhqh2sWHOEer+rQ=="], + + "@types/async-retry": ["@types/async-retry@1.4.9", "", { "dependencies": { "@types/retry": "*" } }, "sha512-s1ciZQJzRh3708X/m3vPExr5KJlzlZJvXsKpbtE2luqNcbROr64qU+3KpJsYHqWMeaxI839OvXf9PrUSw1Xtyg=="], + + "@types/cli-progress": ["@types/cli-progress@3.11.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/ms": ["@types/ms@0.7.34", "", {}, "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="], + + "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], + + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + + "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], + + "@types/retry": ["@types/retry@0.12.5", "", {}, "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw=="], + + "@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="], + + "@vitest/expect": ["@vitest/expect@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "@vitest/utils": "2.1.9", "chai": "^5.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw=="], + + "@vitest/mocker": ["@vitest/mocker@2.1.9", "", { "dependencies": { "@vitest/spy": "2.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.12" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@2.1.9", "", { "dependencies": { "tinyrainbow": "^1.2.0" } }, "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ=="], + + "@vitest/runner": ["@vitest/runner@2.1.9", "", { "dependencies": { "@vitest/utils": "2.1.9", "pathe": "^1.1.2" } }, "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g=="], + + "@vitest/snapshot": ["@vitest/snapshot@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "magic-string": "^0.30.12", "pathe": "^1.1.2" } }, "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ=="], + + "@vitest/spy": ["@vitest/spy@2.1.9", "", { "dependencies": { "tinyspy": "^3.0.2" } }, "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ=="], + + "@vitest/utils": ["@vitest/utils@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ=="], + + "@yao-pkg/pkg": ["@yao-pkg/pkg@5.16.1", "", { "dependencies": { "@babel/generator": "^7.23.0", "@babel/parser": "^7.23.0", "@babel/types": "^7.23.0", "@yao-pkg/pkg-fetch": "3.5.16", "into-stream": "^6.0.0", "minimist": "^1.2.6", "multistream": "^4.1.0", "picocolors": "^1.1.0", "picomatch": "^4.0.2", "prebuild-install": "^7.1.1", "resolve": "^1.22.0", "stream-meter": "^1.0.4", "tinyglobby": "^0.2.9" }, "bin": { "pkg": "lib-es5/bin.js" } }, "sha512-crUlnNFSReFNFuXDc4f3X2ignkFlc9kmEG7Bp/mJMA1jYyqR0lqjZGLgrSDYTYiNsYud8AzgA3RY1DrMdcUZWg=="], + + "@yao-pkg/pkg-fetch": ["@yao-pkg/pkg-fetch@3.5.16", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.6", "picocolors": "^1.1.0", "progress": "^2.0.3", "semver": "^7.3.5", "tar-fs": "^2.1.1", "yargs": "^16.2.0" }, "bin": { "pkg-fetch": "lib-es5/bin.js" } }, "sha512-mCnZvZz0/Ylpk4TGyt34pqWJyBGYJM8c3dPoMRV8Knodv2QhcYS4iXb5kB/JNWkrRtCKukGZIKkMLXZ3TQlzPg=="], + + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], + + "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], + + "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + + "async-retry": ["async-retry@1.3.3", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "auto-bind": ["auto-bind@5.0.1", "", {}, "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg=="], + + "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], + + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], + + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + + "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], + + "chardet": ["chardet@2.1.1", "", {}, "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ=="], + + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "chrono-node": ["chrono-node@2.9.0", "", {}, "sha512-glI4YY2Jy6JII5l3d5FN6rcrIbKSQqKPhWsIRYPK2IK8Mm4Q1ZZFdYIaDqglUNf7gNwG+kWIzTn0omzzE0VkvQ=="], + + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], + + "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], + + "cli-progress": ["cli-progress@3.12.0", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="], + + "cli-spinners": ["cli-spinners@3.4.0", "", {}, "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw=="], + + "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + + "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], + + "cliui": ["cliui@7.0.4", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ=="], + + "code-excerpt": ["code-excerpt@4.0.0", "", { "dependencies": { "convert-to-spaces": "^2.0.1" } }, "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], + + "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], + + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="], + + "date-fns": ["date-fns@4.1.0", "", {}, "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg=="], + + "dayjs": ["dayjs@1.11.19", "", {}, "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "es-toolkit": ["es-toolkit@1.44.0", "", {}, "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg=="], + + "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], + + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + + "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], + + "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], + + "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + + "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + + "from2": ["from2@2.3.0", "", { "dependencies": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" } }, "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + + "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "indent-string": ["indent-string@5.0.0", "", {}, "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "ink": ["ink@5.2.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.22.0", "indent-string": "^5.0.0", "is-in-ci": "^1.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg=="], + + "ink-link": ["ink-link@4.1.0", "", { "dependencies": { "prop-types": "^15.8.1", "terminal-link": "^3.0.0" }, "peerDependencies": { "ink": ">=4" } }, "sha512-3nNyJXum0FJIKAXBK8qat2jEOM41nJ1J60NRivwgK9Xh92R5UMN/k4vbz0A9xFzhJVrlf4BQEmmxMgXkCE1Jeg=="], + + "ink-spinner": ["ink-spinner@5.0.0", "", { "dependencies": { "cli-spinners": "^2.7.0" }, "peerDependencies": { "ink": ">=4.0.0", "react": ">=18.0.0" } }, "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA=="], + + "ink-testing-library": ["ink-testing-library@4.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q=="], + + "ink-text-input": ["ink-text-input@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="], + + "inquirer": ["inquirer@13.2.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/prompts": "^8.2.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-4CBv58vLrL4CnMgrscW/T5cLvfWM2nRLevttTiZTQyku7YV7/pc2IKyABBU2rDfVl4PiIB0sTRcwOac7BIYKLA=="], + + "into-stream": ["into-stream@6.0.0", "", { "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA=="], + + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-in-ci": ["is-in-ci@1.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="], + + "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + + "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + + "little-date": ["little-date@1.2.1", "", { "dependencies": { "date-fns": "^2.0.0" } }, "sha512-O5LUKWMw96qEyS+12mxnRP49MZEEM9fPWzIylgutX6RHf6F1JakvfNeBgteP0CIeEyNFiPM8nohTldtbmyjEqQ=="], + + "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], + + "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], + + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + + "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], + + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "multistream": ["multistream@4.1.0", "", { "dependencies": { "once": "^1.4.0", "readable-stream": "^3.6.0" } }, "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw=="], + + "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + + "node-abi": ["node-abi@3.86.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-sn9Et4N3ynsetj3spsZR729DVlGH6iBG4RiDMV7HEp3guyOW6W3S0unGpLDxT50mXortGUMax/ykUNQXdqc/Xg=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + + "openapi-fetch": ["openapi-fetch@0.11.3", "", { "dependencies": { "openapi-typescript-helpers": "^0.0.13" } }, "sha512-r18fERgpxFrI4pv79ABD1dqFetWz7pTfwRd7jQmRm/lFdCDpWF43kvHUiOqOZu+tWsMydDJMpJN1hlZ9inRvfA=="], + + "openapi-typescript-helpers": ["openapi-typescript-helpers@0.0.13", "", {}, "sha512-z44WK2e7ygW3aUtAtiurfEACohf/Qt9g6BsejmIYgEoY4REHeRzgFJmO3ium0libsuzPc145I+8lE9aiiZrQvQ=="], + + "ora": ["ora@8.2.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-cursor": "^5.0.0", "cli-spinners": "^2.9.2", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.0.0", "log-symbols": "^6.0.0", "stdin-discarder": "^0.2.2", "string-width": "^7.2.0", "strip-ansi": "^7.1.0" } }, "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw=="], + + "p-is-promise": ["p-is-promise@3.0.0", "", {}, "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ=="], + + "parse-duration": ["parse-duration@2.1.5", "", {}, "sha512-/IX1KRw6zHDOOJrgIz++gvFASbFl7nc8GEXaLdD7d1t1x/GnrK6hh5Fgk8G3RLpkIEi4tsGj9pupGLWNg0EiJA=="], + + "patch-console": ["patch-console@2.0.0", "", {}, "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA=="], + + "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], + + "pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "posthog-node": ["posthog-node@4.18.0", "", { "dependencies": { "axios": "^1.8.2" } }, "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw=="], + + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + + "prettier": ["prettier@3.8.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-yEPsovQfpxYfgWNhCfECjG5AQaO+K3dp6XERmOepyPDVqcJm+bjyCVO3pmU+nAPe0N5dDvekfGezt/EIiRe1TA=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + + "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], + + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], + + "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + + "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + + "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], + + "react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], + + "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + + "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], + + "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], + + "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], + + "run-async": ["run-async@4.0.6", "", {}, "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ=="], + + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], + + "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + + "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + + "shescape": ["shescape@2.1.7", "", { "dependencies": { "which": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-Y1syY0ggm3ow7mE1zrcK9YrOhAqv/IGbm3+J9S+MXLukwXf/M8yzL3hZp7ubVeSy250TT7M5SVKikTZkKyib6w=="], + + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + + "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + + "stdin-discarder": ["stdin-discarder@0.2.2", "", {}, "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ=="], + + "stream-meter": ["stream-meter@1.0.4", "", { "dependencies": { "readable-stream": "^2.1.4" } }, "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ=="], + + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], + + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "terminal-link": ["terminal-link@3.0.0", "", { "dependencies": { "ansi-escapes": "^5.0.0", "supports-hyperlinks": "^2.2.0" } }, "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg=="], + + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "tinyrainbow": ["tinyrainbow@1.2.0", "", {}, "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ=="], + + "tinyspy": ["tinyspy@3.0.2", "", {}, "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q=="], + + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], + + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + + "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], + + "tweetnacl-util": ["tweetnacl-util@0.15.1", "", {}, "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw=="], + + "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], + + "vite-node": ["vite-node@2.1.9", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.3.7", "es-module-lexer": "^1.5.4", "pathe": "^1.1.2", "vite": "^5.0.0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA=="], + + "vitest": ["vitest@2.1.9", "", { "dependencies": { "@vitest/expect": "2.1.9", "@vitest/mocker": "2.1.9", "@vitest/pretty-format": "^2.1.9", "@vitest/runner": "2.1.9", "@vitest/snapshot": "2.1.9", "@vitest/spy": "2.1.9", "@vitest/utils": "2.1.9", "chai": "^5.1.2", "debug": "^4.3.7", "expect-type": "^1.1.0", "magic-string": "^0.30.12", "pathe": "^1.1.2", "std-env": "^3.8.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.1", "tinypool": "^1.0.1", "tinyrainbow": "^1.2.0", "vite": "^5.0.0", "vite-node": "2.1.9", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", "@vitest/browser": "2.1.9", "@vitest/ui": "2.1.9", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q=="], + + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "which": ["which@6.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg=="], + + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + + "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], + + "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], + + "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + + "yaml": ["yaml@2.6.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="], + + "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], + + "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + + "yn": ["yn@5.1.0", "", {}, "sha512-TfXLvT6eVsBNIm8rAXTwJYdQFtOXaHQ+rA7LU8HL8C/BFfaSfhvFE5T1rHAdBCbAj808HaqjXVkmo8jmeGOqhw=="], + + "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], + + "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "@yao-pkg/pkg-fetch/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cli-progress/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cli-table3/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "from2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "ink-spinner/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "little-date/date-fns": ["date-fns@2.30.0", "", { "dependencies": { "@babel/runtime": "^7.21.0" } }, "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw=="], + + "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + + "ora/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], + + "ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + + "stream-meter/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "terminal-link/ansi-escapes": ["ansi-escapes@5.0.0", "", { "dependencies": { "type-fest": "^1.0.2" } }, "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA=="], + + "vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + + "yargs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "cli-progress/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "cli-progress/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "cli-table3/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "cli-table3/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "from2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + + "stream-meter/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "terminal-link/ansi-escapes/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], + + "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + + "vite/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + + "vite/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + + "vite/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + + "vite/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + + "vite/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + + "vite/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + + "vite/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + + "vite/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + + "vite/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + + "vite/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + + "vite/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + + "vite/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + + "vite/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + + "vite/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + + "vite/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + + "vite/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + + "vite/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + + "vite/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + + "vite/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + + "vite/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + + "vite/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + + "vite/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + + "yargs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "yargs/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "cli-progress/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "cli-table3/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ora/cli-cursor/restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], + + "ora/cli-cursor/restore-cursor/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "yargs/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + } +} diff --git a/package.json b/package.json index 023e683c..15945e6c 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,32 @@ { + "name": "sf-cli", + "type": "module", + "bin": "src/index.ts", + "pkg": { + "scripts": "src/**/*.ts", + "assets": ["package.json"], + "outputPath": "dist" + }, "scripts": { - "devv": "IS_DEVELOPMENT_CLI_ENV=true deno run --allow-all src/index.ts", - "prod": "deno run --allow-all src/index.ts", + "dev": "IS_DEVELOPMENT_CLI_ENV=true tsx src/index.ts", + "prod": "tsx src/index.ts", "schema": "npx openapi-typescript https://api.sfcompute.com/docs/json -o src/schema.ts", - "lint": "prettier --write .", - "test": "deno test --allow-all" + "lint": "biome check .", + "lint:fix": "biome check --write .", + "check": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@commander-js/extra-typings": "^14.0.0", "@inkjs/ui": "^2.0.0", - "@inquirer/prompts": "^5.1.2", + "@inquirer/prompts": "^8.2.0", "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", "@types/ms": "^0.7.34", "async-retry": "^1.3.3", "axios": "^1.8.4", "boxen": "^8.0.1", + "chalk": "^5.3.0", "chrono-node": "^2.9.0", "cli-progress": "^3.12.0", "cli-table3": "0.6.5", @@ -27,7 +39,7 @@ "ink-spinner": "^5.0.0", "ink-testing-library": "^4.0.0", "ink-text-input": "^6.0.0", - "inquirer": "^10.1.2", + "inquirer": "^13.2.0", "little-date": "^1.0.0", "ms": "^2.1.3", "node-fetch": "^3.3.2", @@ -42,17 +54,23 @@ "tiny-invariant": "^1.3.3", "tweetnacl": "^1.0.3", "tweetnacl-util": "^0.15.1", - "yaml": "2.6.1" + "yaml": "2.6.1", + "yn": "^5.1.0" }, "devDependencies": { + "@biomejs/biome": "^1.9.0", + "@types/async-retry": "^1.4.9", + "@types/cli-progress": "^3.11.6", + "@types/node": "^20.0.0", "@types/react": "^18.3.20", - "@types/semver": "^7.5.8" + "@types/semver": "^7.5.8", + "@yao-pkg/pkg": "^5.12.0", + "tsx": "^4.19.0", + "typescript": "^5.6.0", + "vitest": "^2.1.0" }, "peerDependencies": { "typescript": "^5.6.2" }, - "resolutions": { - "chalk": "^5.6.2" - }, "version": "0.28.1" } From efaef285fdbcab5145efc77693681d71f9263aa0 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:21:39 -0800 Subject: [PATCH 03/48] refactor: replace Deno APIs with Node.js equivalents Files updated: - src/helpers/config.ts: Deno.readTextFile -> fs.readFile, etc. - src/helpers/feature-flags.ts: Deno.mkdir -> fs.mkdir, etc. - src/lib/upgrade.ts: Deno.Command -> child_process.spawn - src/lib/clusters/kubeconfig.ts: Deno.errors -> Node.js error codes - src/lib/clusters/keys.tsx: Deno.chmod -> fs.chmod - src/lib/nodes/image/upload.ts: Deno.open -> fs.open, Deno.exit -> process.exit Replaced APIs: - Deno.readTextFile() -> fs.promises.readFile() - Deno.writeTextFile() -> fs.promises.writeFile() - Deno.mkdir() -> fs.promises.mkdir() - Deno.Command -> child_process.spawn() - Deno.errors.NotFound -> err.code === 'ENOENT' Part of Deno to Node.js migration (Phase 3) --- src/helpers/config.ts | 18 ++++--- src/helpers/feature-flags.ts | 9 ++-- src/lib/nodes/image/upload.ts | 94 +++++++++++++++++++---------------- src/lib/upgrade.ts | 47 +++++++++++------- 4 files changed, 98 insertions(+), 70 deletions(-) diff --git a/src/helpers/config.ts b/src/helpers/config.ts index df267cd4..25e5ee7c 100644 --- a/src/helpers/config.ts +++ b/src/helpers/config.ts @@ -1,5 +1,6 @@ -import { unlinkSync } from "node:fs"; import * as console from "node:console"; +import { unlinkSync } from "node:fs"; +import * as fs from "node:fs/promises"; import { homedir } from "node:os"; import { join } from "node:path"; import process from "node:process"; @@ -37,8 +38,8 @@ export async function saveConfig( try { // Ensure config directory exists - await Deno.mkdir(configDir, { recursive: true }); - await Deno.writeTextFile(configPath, configData); + await fs.mkdir(configDir, { recursive: true }); + await fs.writeFile(configPath, configData); return { success: true }; } catch (error) { @@ -92,10 +93,13 @@ export function getConfigPath(): string { async function configFileExists(): Promise { const configPath = getConfigPath(); try { - await Deno.stat(configPath); + await fs.stat(configPath); return true; - } catch { - return false; + } catch (error) { + if ((error as NodeJS.ErrnoException).code === "ENOENT") { + return false; + } + throw error; } } @@ -107,7 +111,7 @@ async function readConfigFile(): Promise { const configPath = getConfigPath(); try { - const configData = await Deno.readTextFile(configPath); + const configData = await fs.readFile(configPath, "utf-8"); const config = JSON.parse(configData); if (typeof config === "object" && config !== null) { return config; diff --git a/src/helpers/feature-flags.ts b/src/helpers/feature-flags.ts index 6d726fca..6e8a2ccf 100644 --- a/src/helpers/feature-flags.ts +++ b/src/helpers/feature-flags.ts @@ -1,3 +1,4 @@ +import * as fs from "node:fs/promises"; import { homedir } from "node:os"; import { join } from "node:path"; @@ -22,8 +23,8 @@ export async function saveFeatureFlags(flags: FeatureFlagCache): Promise { const configDir = join(homedir(), ".sfcompute"); try { - await Deno.mkdir(configDir, { recursive: true }); - await Deno.writeTextFile(cachePath, JSON.stringify(flags, null, 2)); + await fs.mkdir(configDir, { recursive: true }); + await fs.writeFile(cachePath, JSON.stringify(flags, null, 2)); } catch { // Silent error } @@ -33,7 +34,7 @@ export async function loadFeatureFlags(): Promise { const cachePath = getFeatureFlagCachePath(); try { - const cacheData = await Deno.readTextFile(cachePath); + const cacheData = await fs.readFile(cachePath, "utf-8"); return JSON.parse(cacheData); } catch { return {}; @@ -81,7 +82,7 @@ export async function cacheFeatureFlag( export async function clearFeatureFlags(): Promise { const cachePath = getFeatureFlagCachePath(); try { - await Deno.remove(cachePath); + await fs.unlink(cachePath); } catch { // Silent error if file doesn't exist } diff --git a/src/lib/nodes/image/upload.ts b/src/lib/nodes/image/upload.ts index ac75574b..32f1b2f6 100644 --- a/src/lib/nodes/image/upload.ts +++ b/src/lib/nodes/image/upload.ts @@ -1,12 +1,15 @@ -import { Command } from "@commander-js/extra-typings"; -import { brightBlack, cyan, gray, green, red } from "jsr:@std/fmt/colors"; -import cliProgress from "cli-progress"; import console from "node:console"; import crypto from "node:crypto"; +import fs from "node:fs"; +import { open, stat } from "node:fs/promises"; +import process from "node:process"; import { clearInterval, setInterval } from "node:timers"; +import { Command } from "@commander-js/extra-typings"; import retry from "async-retry"; +import chalk from "chalk"; +import cliProgress from "cli-progress"; +import cliSpinners from "cli-spinners"; import ora, { type Ora } from "ora"; -import cliSpinners from "npm:cli-spinners"; import { apiClient } from "../../../apiClient.ts"; import { logAndQuit } from "../../../helpers/errors.ts"; @@ -16,16 +19,19 @@ async function readChunk( length: number, onProgress?: (bytesRead: number) => void, ): Promise { - const file = await Deno.open(filePath, { read: true }); + const fileHandle = await open(filePath, "r"); try { - await file.seek(start, Deno.SeekMode.Start); - const buffer = new Uint8Array(length); let offset = 0; while (offset < length) { - const bytesRead = await file.read(buffer.subarray(offset)); - if (bytesRead === null) { + const { bytesRead } = await fileHandle.read( + buffer, + offset, + length - offset, + start + offset, + ); + if (bytesRead === 0) { // EOF reached break; } @@ -43,7 +49,7 @@ async function readChunk( return buffer; } finally { - file.close(); + await fileHandle.close(); } } @@ -55,7 +61,7 @@ const upload = new Command("upload") "-c, --concurrency ", "Number of parts to upload concurrently", (value) => { - const parsed = parseInt(value, 10); + const parsed = Number.parseInt(value, 10); if (isNaN(parsed) || parsed < 1) { throw new Error("Concurrency must be a positive integer"); } @@ -88,20 +94,20 @@ const upload = new Command("upload") const imageId = startResponse.data.image_id; preparingSpinner.succeed( - `Started upload for image ${cyan(name)} (${brightBlack(imageId)})`, + `Started upload for image ${chalk.cyan(name)} (${chalk.blackBright(imageId)})`, ); // Get file info and open as stream - const fileInfo = await Deno.stat(filePath); + const fileInfo = await stat(filePath); const fileSize = fileInfo.size; // Check file size limit (128 GiB) const maxFileSize = 128 * 1024 * 1024 * 1024; // 128 GiB in bytes if (fileSize > maxFileSize) { logAndQuit( - `File size exceeds maximum allowed size of 128 GiB. File size: ${ - (fileSize / (1024 * 1024 * 1024)).toFixed(2) - } GiB`, + `File size exceeds maximum allowed size of 128 GiB. File size: ${( + fileSize / (1024 * 1024 * 1024) + ).toFixed(2)} GiB`, ); } @@ -112,9 +118,10 @@ const upload = new Command("upload") // For files smaller than default chunk, use the whole file as one part // Otherwise use default chunk size, but ensure we don't exceed maxParts - const chunkSize = fileSize <= defaultChunk - ? Math.max(fileSize, minChunk) - : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); + const chunkSize = + fileSize <= defaultChunk + ? Math.max(fileSize, minChunk) + : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); const totalParts = Math.ceil(fileSize / chunkSize); @@ -151,8 +158,7 @@ const upload = new Command("upload") let spinnerIndex = 0; progressBar = new cliProgress.SingleBar({ - format: - `{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}`, + format: `{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}`, barCompleteChar: "\u2588", barIncompleteChar: "\u2591", hideCursor: true, @@ -185,7 +191,7 @@ const upload = new Command("upload") speedStr = `${speed.toFixed(0)} B/s`; } - progressBar.update(totalBytesUploaded, { + progressBar?.update(totalBytesUploaded, { spinner: spinner.frames[spinnerIndex % spinner.frames.length], speed: speedStr, uploadedMB: (totalBytesUploaded / (1024 * 1024)).toFixed(1), @@ -213,13 +219,15 @@ const upload = new Command("upload") }; // Upload parts concurrently with specified concurrency limit - const uploadPart = async ( - { part, start, end }: { - part: number; - start: number; - end: number; - }, - ) => { + const uploadPart = async ({ + part, + start, + end, + }: { + part: number; + start: number; + end: number; + }) => { const chunkSize = end - start; // Upload the chunk with retry, fetching fresh URL each attempt @@ -253,7 +261,9 @@ const upload = new Command("upload") // Bail on non-transient 4xx errors (except 408 Request Timeout and 429 Too Many Requests) if ( - status >= 400 && status < 500 && status !== 408 && + status >= 400 && + status < 500 && + status !== 408 && status !== 429 ) { bail( @@ -286,13 +296,15 @@ const upload = new Command("upload") headers: { "Content-Type": "application/octet-stream", }, - body: payload, + body: payload as unknown as BodyInit, }); if (!res.ok) { // Bail on non-transient 4xx errors (except 408 and 429) if ( - res.status >= 400 && res.status < 500 && res.status !== 408 && + res.status >= 400 && + res.status < 500 && + res.status !== 408 && res.status !== 429 ) { bail( @@ -324,9 +336,7 @@ const upload = new Command("upload") try { for (let i = 0; i < uploadParts.length; i += concurrencyLimit) { const batch = uploadParts.slice(i, i + concurrencyLimit); - const batchResults = await Promise.allSettled( - batch.map(uploadPart), - ); + const batchResults = await Promise.allSettled(batch.map(uploadPart)); for (const result of batchResults) { if (result.status === "fulfilled") { @@ -345,7 +355,7 @@ const upload = new Command("upload") } progressBar.update(fileSize, { - spinner: green("✔"), + spinner: chalk.green("✔"), speed: "0 B/s", uploadedMB: (fileSize / (1024 * 1024)).toFixed(1), totalMB: (fileSize / (1024 * 1024)).toFixed(1), @@ -356,8 +366,8 @@ const upload = new Command("upload") // Calculate SHA256 hash for integrity verification using streaming const hash = crypto.createHash("sha256"); - using file = await Deno.open(filePath, { read: true }); - for await (const chunk of file.readable) { + const fileStream = fs.createReadStream(filePath); + for await (const chunk of fileStream) { hash.update(chunk); } @@ -385,8 +395,8 @@ const upload = new Command("upload") finalizingSpinner.succeed(`Image uploaded and verified`); const object = completeResponse.data; - console.log(gray("\nNext steps:")); - console.log(` sf nodes images show ${cyan(object.image_id)}`); + console.log(chalk.gray("\nNext steps:")); + console.log(` sf nodes images show ${chalk.cyan(object.image_id)}`); } catch (err) { // Clean up spinner timer if (spinnerTimer) { @@ -416,10 +426,10 @@ const upload = new Command("upload") ); } else { console.error( - `\n${red("✗")} ${err instanceof Error ? err.message : String(err)}`, + `\n${chalk.red("✗")} ${err instanceof Error ? err.message : String(err)}`, ); } - Deno.exit(1); + process.exit(1); } }); diff --git a/src/lib/upgrade.ts b/src/lib/upgrade.ts index 0760b26e..d30881fb 100644 --- a/src/lib/upgrade.ts +++ b/src/lib/upgrade.ts @@ -1,6 +1,7 @@ -import type { Command } from "@commander-js/extra-typings"; +import { spawn } from "node:child_process"; import * as console from "node:console"; import process from "node:process"; +import type { Command } from "@commander-js/extra-typings"; import ora from "ora"; /** @@ -16,8 +17,9 @@ const getIsOnLatestVersion = async (currentVersion: string | undefined) => { const latestVersionResponse = await fetch(latestVersionUrl); if (latestVersionResponse.ok) { - const latestVersionData = await latestVersionResponse.json(); - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. + const latestVersionData = (await latestVersionResponse.json()) as { + tag_name: string; + }; const latestVersion = latestVersionData.tag_name; return latestVersion === currentVersion; @@ -37,8 +39,7 @@ export function registerUpgrade(program: Command) { if (version) { spinner.start(`Checking if version ${version} exists`); - const url = - `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; + const url = `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; const response = await fetch(url, { method: "HEAD" }); if (response.status === 404) { @@ -72,24 +73,36 @@ export function registerUpgrade(program: Command) { // Execute the script with bash spinner.start("Installing upgrade"); - const command = new Deno.Command("bash", { - stdin: "piped", - stdout: "piped", - stderr: "piped", - env: version ? { SF_CLI_VERSION: version } : undefined, + + const bashProcess = spawn("bash", [], { + stdio: ["pipe", "pipe", "pipe"], + env: version + ? { ...process.env, SF_CLI_VERSION: version } + : process.env, }); - const bashProcess = command.spawn(); - const stdinWriter = bashProcess.stdin.getWriter(); - await stdinWriter.write(new TextEncoder().encode(script)); - stdinWriter.close(); + let stdout = ""; + let stderr = ""; + + bashProcess.stdout.on("data", (data: Buffer) => { + stdout += data.toString(); + }); - const { code, stdout, stderr } = await bashProcess.output(); + bashProcess.stderr.on("data", (data: Buffer) => { + stderr += data.toString(); + }); + + bashProcess.stdin.write(script); + bashProcess.stdin.end(); + + const code = await new Promise((resolve) => { + bashProcess.on("close", resolve); + }); if (code !== 0) { spinner.fail("Upgrade failed"); - console.error(new TextDecoder().decode(stderr)); - console.log(new TextDecoder().decode(stdout)); + console.error(stderr); + console.log(stdout); process.exit(1); } From d1e40731d1eeff4e7c15f36ae308136af199b27d Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:21:48 -0800 Subject: [PATCH 04/48] refactor: replace jsr:@std/fmt/colors with chalk Migrated 26 files from Deno's JSR color imports to chalk: - import { cyan, gray, yellow } from "jsr:@std/fmt/colors" -> import chalk from "chalk" - cyan("text") -> chalk.cyan("text") - brightRed("text") -> chalk.redBright("text") Part of Deno to Node.js migration (Phase 4) --- src/checkVersion.ts | 10 +- src/helpers/units.ts | 80 +++++----- src/lib/app-banner.ts | 6 +- src/lib/balance.ts | 19 +-- src/lib/buy/index.tsx | 276 ++++++++++++++++----------------- src/lib/dev.ts | 18 +-- src/lib/extend/index.tsx | 46 +++--- src/lib/nodes/create.ts | 231 ++++++++++++++-------------- src/lib/nodes/delete.ts | 103 ++++++------- src/lib/nodes/extend.ts | 116 +++++++------- src/lib/nodes/get.tsx | 27 ++-- src/lib/nodes/image/list.tsx | 52 +++---- src/lib/nodes/image/show.tsx | 54 +++---- src/lib/nodes/list.tsx | 289 ++++++++++++++++++----------------- src/lib/nodes/logs.ts | 39 +++-- src/lib/nodes/redeploy.ts | 101 ++++++------ src/lib/nodes/release.ts | 83 +++++----- src/lib/nodes/set.ts | 50 +++--- src/lib/nodes/ssh.ts | 46 +++--- src/lib/nodes/utils.ts | 110 ++++++------- src/lib/scale/create.tsx | 126 +++++++-------- src/lib/scale/update.tsx | 221 ++++++++++++++------------- src/lib/tokens.ts | 103 ++++++------- src/lib/vm/index.ts | 37 +++-- src/lib/vm/list.ts | 57 +++---- src/lib/zones.tsx | 11 +- 26 files changed, 1122 insertions(+), 1189 deletions(-) diff --git a/src/checkVersion.ts b/src/checkVersion.ts index b434e539..4a23fe7a 100644 --- a/src/checkVersion.ts +++ b/src/checkVersion.ts @@ -1,11 +1,11 @@ -import boxen from "boxen"; -import { cyan, gray, yellow } from "jsr:@std/fmt/colors"; import { execSync } from "node:child_process"; import * as console from "node:console"; import { mkdir, readFile, stat, writeFile } from "node:fs/promises"; import { homedir } from "node:os"; import { join } from "node:path"; import process from "node:process"; +import boxen from "boxen"; +import chalk from "chalk"; import semver from "semver"; import pkg from "../package.json" with { type: "json" }; @@ -122,11 +122,11 @@ export async function checkVersion() { if (isPatchUpdate && !latestIsPrerelease) { console.log( - cyan(`Automatically upgrading ${version} → ${latestVersion}`), + chalk.cyan(`Automatically upgrading ${version} → ${latestVersion}`), ); try { execSync("sf upgrade", { stdio: "inherit" }); - console.log(gray("\n☁️☁️☁️\n")); + console.log(chalk.gray("\n☁️☁️☁️\n")); // Re-run the original command const args = process.argv.slice(2); @@ -146,7 +146,7 @@ Latest version: ${latestVersion} Run 'sf upgrade' to update to the latest version `; console.log( - boxen(yellow(message), { + boxen(chalk.yellow(message), { padding: 1, borderColor: "yellow", borderStyle: "round", diff --git a/src/helpers/units.ts b/src/helpers/units.ts index 62cf7ebc..3b0049ba 100644 --- a/src/helpers/units.ts +++ b/src/helpers/units.ts @@ -1,9 +1,9 @@ +import { select } from "@inquirer/prompts"; +import chalk from "chalk"; import * as chrono from "chrono-node"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; -import { select } from "@inquirer/prompts"; -import { gray } from "jsr:@std/fmt/colors"; +import utc from "dayjs/plugin/utc"; import type { Nullable } from "../types/empty.ts"; import { dateSameAcrossTimezones, formatDate } from "./format-date.ts"; @@ -39,9 +39,8 @@ export function computeApproximateDurationSeconds( startDate: Date | "NOW", endDate: Date, ): number { - const startEpoch = startDate === "NOW" - ? currentEpoch() - : dateToEpoch(startDate); + const startEpoch = + startDate === "NOW" ? currentEpoch() : dateToEpoch(startDate); const endEpoch = dateToEpoch(endDate); return dayjs(epochToDate(endEpoch)).diff(dayjs(epochToDate(startEpoch)), "s"); } @@ -149,23 +148,21 @@ type SelectChoice = { */ export async function selectTime( date: Date, - config: Omit[0], "choices">, + config: Omit>[0], "choices">, ): Promise { const suggestedLower = dayjs(date).startOf("hour"); const suggestedHigher = dayjs(date).startOf("hour").add(1, "hour"); const choices: SelectChoice[] = []; - const suggestedHigherUserTZ = `${ - formatDate(suggestedHigher.toDate(), { forceIncludeTime: true }) - } ${dayjs(suggestedHigher).format("z")}`; - const suggestedHigherUTC = `${ - formatDate(suggestedHigher.utc(true).toDate(), { - today: suggestedHigher.toDate(), - showToday: dateSameAcrossTimezones(suggestedHigher.toDate()), - forceIncludeTime: true, - }) - } UTC`; + const suggestedHigherUserTZ = `${formatDate(suggestedHigher.toDate(), { + forceIncludeTime: true, + })} ${dayjs(suggestedHigher).format("z")}`; + const suggestedHigherUTC = `${formatDate(suggestedHigher.utc(true).toDate(), { + today: suggestedHigher.toDate(), + showToday: dateSameAcrossTimezones(suggestedHigher.toDate()), + forceIncludeTime: true, + })} UTC`; // If the lower boundary is in the past, suggest "NOW" if (suggestedLower.isBefore(dayjs(new Date()))) { @@ -176,33 +173,30 @@ export async function selectTime( ); choices.push({ - name: `${immediatelyText.padEnd(maxLength)} ${ - gray( - `${ - formatDate(new Date(), { forceIncludeTime: true, showToday: false }) - } ${dayjs(new Date()).format("z")}`, - ) - }`, + name: `${immediatelyText.padEnd(maxLength)} ${chalk.gray( + `${formatDate(new Date(), { + forceIncludeTime: true, + showToday: false, + })} ${dayjs(new Date()).format("z")}`, + )}`, value: "NOW", }); choices.push({ - name: `${suggestedHigherUserTZ.padEnd(maxLength)} ${ - gray(`${suggestedHigherUTC}`) - }`, + name: `${suggestedHigherUserTZ.padEnd(maxLength)} ${chalk.gray( + `${suggestedHigherUTC}`, + )}`, value: suggestedHigher.toDate(), }); } else { - const suggestedLowerUserTZ = `${ - formatDate(suggestedLower.toDate(), { forceIncludeTime: true }) - } ${dayjs(suggestedLower).format("z")}`; - const suggestedLowerUTC = `${ - formatDate(suggestedLower.utc(true).toDate(), { - today: suggestedLower.toDate(), - showToday: dateSameAcrossTimezones(suggestedLower.toDate()), - forceIncludeTime: true, - }) - } UTC`; + const suggestedLowerUserTZ = `${formatDate(suggestedLower.toDate(), { + forceIncludeTime: true, + })} ${dayjs(suggestedLower).format("z")}`; + const suggestedLowerUTC = `${formatDate(suggestedLower.utc(true).toDate(), { + today: suggestedLower.toDate(), + showToday: dateSameAcrossTimezones(suggestedLower.toDate()), + forceIncludeTime: true, + })} UTC`; const maxLength = Math.max( suggestedLowerUserTZ.length, @@ -210,16 +204,16 @@ export async function selectTime( ); choices.push({ - name: `${suggestedLowerUserTZ.padEnd(maxLength)} ${ - gray(`${suggestedLowerUTC}`) - }`, + name: `${suggestedLowerUserTZ.padEnd(maxLength)} ${chalk.gray( + `${suggestedLowerUTC}`, + )}`, value: suggestedLower.toDate(), }); choices.push({ - name: `${suggestedHigherUserTZ.padEnd(maxLength)} ${ - gray(`${suggestedHigherUTC}`) - }`, + name: `${suggestedHigherUserTZ.padEnd(maxLength)} ${chalk.gray( + `${suggestedHigherUTC}`, + )}`, value: suggestedHigher.toDate(), }); } diff --git a/src/lib/app-banner.ts b/src/lib/app-banner.ts index 685defff..716eae67 100644 --- a/src/lib/app-banner.ts +++ b/src/lib/app-banner.ts @@ -1,6 +1,6 @@ -import boxen from "boxen"; -import { yellow } from "jsr:@std/fmt/colors"; import * as console from "node:console"; +import boxen from "boxen"; +import chalk from "chalk"; type AppBanner = { type: "warning"; @@ -18,7 +18,7 @@ export const getAppBanner = async () => { const message = `${data.content}`; console.log( - boxen(yellow(message), { + boxen(chalk.yellow(message), { padding: 1, borderColor: "yellow", borderStyle: "round", diff --git a/src/lib/balance.ts b/src/lib/balance.ts index 4f716f13..4b288b52 100644 --- a/src/lib/balance.ts +++ b/src/lib/balance.ts @@ -1,7 +1,7 @@ +import * as console from "node:console"; import type { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; import Table from "cli-table3"; -import * as console from "node:console"; -import { gray, green } from "jsr:@std/fmt/colors"; import { apiClient } from "../apiClient.ts"; import { isLoggedIn } from "../helpers/config.ts"; import { @@ -44,10 +44,7 @@ export function registerBalance(program: Command) { ); } - const { - available_balance_cents, - current_balance_cents, - } = data; + const { available_balance_cents, current_balance_cents } = data; const availableWhole = available_balance_cents / 100; const availableCents = available_balance_cents; @@ -71,20 +68,20 @@ export function registerBalance(program: Command) { const formattedBalance = usdFormatter.format(balanceWhole); const table = new Table({ - head: [gray("Type"), gray("Amount"), gray("Cents")], + head: [chalk.gray("Type"), chalk.gray("Amount"), chalk.gray("Cents")], colWidths: [15, 15, 35], }); table.push( [ "Available", - green(formattedAvailable), - green(availableCents.toLocaleString()), + chalk.green(formattedAvailable), + chalk.green(availableCents.toLocaleString()), ], [ "Balance", - gray(formattedBalance), - gray(balanceCents.toLocaleString()), + chalk.gray(formattedBalance), + chalk.gray(balanceCents.toLocaleString()), ], ); diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index 01a3a76a..775e7fc7 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -1,16 +1,16 @@ -import { type Command, Option } from "@commander-js/extra-typings"; -import { yellow } from "jsr:@std/fmt/colors"; -import { parseDate } from "chrono-node"; -import { Box, render, Text, useApp } from "ink"; -import Spinner from "ink-spinner"; -import ms from "ms"; import console from "node:console"; import process from "node:process"; import { setTimeout } from "node:timers"; -import boxen from "npm:boxen@8.0.1"; +import { type Command, Option } from "@commander-js/extra-typings"; +import boxen from "boxen"; +import chalk from "chalk"; +import { parseDate } from "chrono-node"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; +import { Box, Text, render, useApp } from "ink"; +import Spinner from "ink-spinner"; +import ms from "ms"; import parseDurationFromLibrary from "parse-duration"; import React, { useCallback, useEffect, useState } from "react"; import invariant from "tiny-invariant"; @@ -28,6 +28,7 @@ import { roundEndDate, roundStartDate, } from "../../helpers/units.ts"; +import type { components } from "../../schema.ts"; import ConfirmInput from "../ConfirmInput.tsx"; import type { Quote } from "../Quote.tsx"; import QuoteDisplay from "../Quote.tsx"; @@ -35,7 +36,6 @@ import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; import { parseAccelerators } from "../index.ts"; import { analytics } from "../posthog.ts"; -import { components } from "../../schema.ts"; dayjs.extend(relativeTime); dayjs.extend(duration); @@ -48,10 +48,7 @@ export function _registerBuy(program: Command) { .command("buy") .description("Place a buy order") .showHelpAfterError() - .option( - "-t, --type ", - "Type of GPU", - ) + .option("-t, --type ", "Type of GPU") .option( "-n, --accelerators ", "Number of GPUs to purchase", @@ -85,7 +82,7 @@ export function _registerBuy(program: Command) { const { duration, end } = command.opts(); if ((!duration && !end) || (!!duration && !!end)) { console.error( - yellow("Specify either --duration or --end, but not both"), + chalk.yellow("Specify either --duration or --end, but not both"), ); command.help(); process.exit(1); @@ -106,17 +103,17 @@ export function _registerBuy(program: Command) { ) .option( "-z, --zone ", - "Send into a specific zone. If provided, \`-t\`/`--type` will be ignored.", + "Send into a specific zone. If provided, `-t`/`--type` will be ignored.", ) .option( "-c, --cluster ", - "Send into a specific cluster (deprecated, alias for --zone). If provided, \`-t\`/`--type` will be ignored.", + "Send into a specific cluster (deprecated, alias for --zone). If provided, `-t`/`--type` will be ignored.", ) .hook("preAction", (command) => { const { type, zone, cluster, colocate } = command.opts(); if (!type && !zone && !cluster && !colocate) { console.error( - yellow("Must specify either --type, --zone or --colocate"), + chalk.yellow("Must specify either --type, --zone or --colocate"), ); command.help(); process.exit(1); @@ -231,16 +228,16 @@ export function QuoteComponent(props: { options: SfBuyOptions }) { })(); }, [props.options, exit]); - return isLoading - ? ( + return isLoading ? ( + + - - - Getting quote... - + Getting quote... - ) - : ; + + ) : ( + + ); } export function QuoteAndBuy(props: { options: SfBuyOptions }) { @@ -325,16 +322,16 @@ export function QuoteAndBuy(props: { options: SfBuyOptions }) { })(); }, [props.options]); - return orderProps === null - ? ( + return orderProps === null ? ( + + - - - Getting quote... - + Getting quote... - ) - : ; + + ) : ( + + ); } export function getTotalPrice( @@ -360,11 +357,11 @@ function BuyOrderPreview(props: BuyOrderProps) { const realDurationHours = realDuration / 3600 / 1000; const realDurationString = ms(realDuration); - const totalPrice = getTotalPrice(props.price, props.size, realDurationHours) / - 100; + const totalPrice = + getTotalPrice(props.price, props.size, realDurationHours) / 100; - const isSupportedType = typeof props.type === "string" && - props.type in InstanceTypeMetadata; + const isSupportedType = + typeof props.type === "string" && props.type in InstanceTypeMetadata; const typeLabel = isSupportedType ? InstanceTypeMetadata[props.type!]?.displayName : props.type; @@ -404,11 +401,7 @@ function BuyOrderPreview(props: BuyOrderProps) { {typeLabel} - {isSupportedType && ( - - ({props.type!}) - - )} + {isSupportedType && ({props.type!})} )} @@ -446,13 +439,14 @@ function BuyOrderPreview(props: BuyOrderProps) { const MemoizedBuyOrderPreview = React.memo(BuyOrderPreview); -type Order = - & Omit>>, "status"> - & { - status: - | NonNullable>>["status"] - | NonNullable>>["status"]; - }; +type Order = Omit< + NonNullable>>, + "status" +> & { + status: + | NonNullable>>["status"] + | NonNullable>>["status"]; +}; type BuyOrderProps = { price: number; size: number; @@ -476,9 +470,9 @@ function VMWarning(props: BuyOrderProps) { let equivalentCommand = `sf nodes create -n ${props.size}`; if (props.price) { - equivalentCommand += ` -p ${ - (props.price * GPUS_PER_NODE / 100).toFixed(2) - }`; + equivalentCommand += ` -p ${((props.price * GPUS_PER_NODE) / 100).toFixed( + 2, + )}`; } if (props.startAt !== "NOW") { const startFormatted = startDate.toISOString(); @@ -514,9 +508,7 @@ function BuyOrder(props: BuyOrderProps) { const isVM = type?.endsWith("v") || zone?.delivery_type === "VM"; const [vmWarningState, setVmWarningState] = useState< "prompt" | "accepted" | "dismissed" | "not_applicable" - >( - isVM ? (props.yes ? "accepted" : "prompt") : "not_applicable", - ); + >(isVM ? (props.yes ? "accepted" : "prompt") : "not_applicable"); const { exit } = useApp(); const [order, setOrder] = useState(null); @@ -551,8 +543,8 @@ function BuyOrder(props: BuyOrderProps) { const handleSubmit = useCallback( (submitValue: boolean) => { const { startAt, endsAt } = props; - const realDurationInHours = dayjs(endsAt).diff(dayjs(startAt)) / 1000 / - 3600; + const realDurationInHours = + dayjs(endsAt).diff(dayjs(startAt)) / 1000 / 3600; const totalPriceInCents = getTotalPrice( props.price, props.size, @@ -606,15 +598,18 @@ function BuyOrder(props: BuyOrderProps) { [props, exit, submitOrder], ); - const handleDismissVMWarning = useCallback((submitValue: boolean) => { - if (!submitValue) { - setIsLoading(false); - setResultMessage( - "VM order not placed. We recommend you use 'sf nodes create' instead.", - ); - setTimeout(exit, 0); - } else setVmWarningState("accepted"); - }, [exit]); + const handleDismissVMWarning = useCallback( + (submitValue: boolean) => { + if (!submitValue) { + setIsLoading(false); + setResultMessage( + "VM order not placed. We recommend you use 'sf nodes create' instead.", + ); + setTimeout(exit, 0); + } else setVmWarningState("accepted"); + }, + [exit], + ); useEffect(() => { if (!isLoading || !order?.id) { @@ -655,9 +650,7 @@ function BuyOrder(props: BuyOrderProps) { <> Place an order for a legacy VM anyway?{" "} - - (y/n) - + (y/n) )} - {(vmWarningState === "dismissed" || vmWarningState === "not_applicable" || + {(vmWarningState === "dismissed" || + vmWarningState === "not_applicable" || vmWarningState === "accepted") && ( - + )} {vmWarningState === "accepted" && !isLoading && !props.yes && ( Place order? (y/n) - + )} {(vmWarningState === "dismissed" || - vmWarningState === "not_applicable") && !isLoading && !props.yes && ( - - Place order? (y/n) + vmWarningState === "not_applicable") && + !isLoading && + !props.yes && ( + + Place order? (y/n) - - - )} + + + )} {isLoading && ( @@ -724,52 +712,47 @@ function BuyOrder(props: BuyOrderProps) { order.status === "filled" && (order as Awaited>) && order.execution_price && ( - - {order.start_at && order.end_at && - order.start_at !== order.end_at && ( + + {order.start_at && + order.end_at && + order.start_at !== order.end_at && ( + + )} - )} - - {order.execution_price && Number(order.price) > 0 && - Number(order.execution_price) > 0 && - Number(order.execution_price) < Number(order.price) && ( - - )} - - )} + {order.execution_price && + Number(order.price) > 0 && + Number(order.execution_price) > 0 && + Number(order.execution_price) < Number(order.price) && ( + + )} + + )} )} @@ -834,8 +817,9 @@ export async function placeBuyOrder(options: { start_at, end_at: roundEndDate(options.endsAt).toISOString(), price: options.totalPriceInCents, - colocate_with: - (options.colocateWith ? [options.colocateWith] : []) as string[], + colocate_with: (options.colocateWith + ? [options.colocateWith] + : []) as string[], flags: { ioc: !options.standing, }, @@ -888,9 +872,10 @@ export function getPricePerGpuHourFromQuote( // from the market's perspective, "NOW" means at the beginning of the next minute. // when the order duration is very short, this can cause the rate to be computed incorrectly // if we implicitly assume it to mean `new Date()`. - const coercedStartTime = startTimeOrNow === "NOW" - ? roundDateUpToNextMinute(new Date()) - : startTimeOrNow; + const coercedStartTime = + startTimeOrNow === "NOW" + ? roundDateUpToNextMinute(new Date()) + : startTimeOrNow; const durationSeconds = dayjs(quote.end_at).diff(dayjs(coercedStartTime)); const durationHours = durationSeconds / 3600 / 1000; @@ -898,9 +883,10 @@ export function getPricePerGpuHourFromQuote( } async function getQuoteFromParsedSfBuyOptions(options: SfBuyOptions) { - const startsAt = options.start === "NOW" - ? "NOW" - : roundStartDate(parseStartDate(options.start)); + const startsAt = + options.start === "NOW" + ? "NOW" + : roundStartDate(parseStartDate(options.start)); const durationSeconds = options.duration ? options.duration : dayjs(options.end).diff(dayjs(parseStartDate(startsAt)), "seconds"); @@ -946,12 +932,14 @@ export async function getQuote(options: QuoteOptions) { side: "buy", instance_type: options.instanceType, quantity: options.quantity, - min_start_date: options.minStartTime === "NOW" - ? ("NOW" as const) - : options.minStartTime.toISOString(), - max_start_date: options.maxStartTime === "NOW" - ? ("NOW" as const) - : options.maxStartTime.toISOString(), + min_start_date: + options.minStartTime === "NOW" + ? ("NOW" as const) + : options.minStartTime.toISOString(), + max_start_date: + options.maxStartTime === "NOW" + ? ("NOW" as const) + : options.maxStartTime.toISOString(), min_duration: options.minDurationSeconds, max_duration: options.maxDurationSeconds, cluster: options.cluster, @@ -1002,7 +990,11 @@ export async function getQuote(options: QuoteOptions) { export async function getOrder(orderId: string) { const api = await apiClient(); - const { data: order, error, response } = await api.GET("/v0/orders/{id}", { + const { + data: order, + error, + response, + } = await api.GET("/v0/orders/{id}", { params: { path: { id: orderId } }, }); diff --git a/src/lib/dev.ts b/src/lib/dev.ts index e8b84eda..52576b46 100644 --- a/src/lib/dev.ts +++ b/src/lib/dev.ts @@ -1,8 +1,8 @@ -import process from "node:process"; import * as console from "node:console"; -import { confirm } from "@inquirer/prompts"; -import { gray, green, white, yellow } from "jsr:@std/fmt/colors"; +import process from "node:process"; import type { Command } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; +import chalk from "chalk"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import { @@ -33,7 +33,7 @@ export function registerDev(program: Command) { const unixEpochSecondsNow = dayjs().unix(); console.log(unixEpochSecondsNow); console.log( - green(dayjs().utc().format("dddd, MMMM D, YYYY h:mm:ss A")), + chalk.green(dayjs().utc().format("dddd, MMMM D, YYYY h:mm:ss A")), ); // process.exit(0); @@ -114,11 +114,9 @@ function registerEpoch(program: Command) { timestamps.forEach((epochTimestamp, i) => { const date = epochToDate(Number.parseInt(epochTimestamp)); console.log( - `${colorDiffedEpochs[i]} | ${ - yellow( - dayjs(date).format("hh:mm A MM-DD-YYYY"), - ) - } Local`, + `${colorDiffedEpochs[i]} | ${chalk.yellow( + dayjs(date).format("hh:mm A MM-DD-YYYY"), + )} Local`, ); }); } @@ -162,7 +160,7 @@ function colorDiffEpochs(epochStrings: string[]): string[] { const rest = num.slice(prefix.length); // return the string with appropriate coloring (gray for common prefix, white for the rest) - return gray(prefix) + white(rest); + return chalk.gray(prefix) + chalk.white(rest); }); } diff --git a/src/lib/extend/index.tsx b/src/lib/extend/index.tsx index bb85a6dd..b780ede7 100644 --- a/src/lib/extend/index.tsx +++ b/src/lib/extend/index.tsx @@ -1,13 +1,13 @@ -import { type Command } from "@commander-js/extra-typings"; +import console from "node:console"; +import process from "node:process"; +import type { Command } from "@commander-js/extra-typings"; +import boxen from "boxen"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; -import boxen from "npm:boxen@8.0.1"; -import console from "node:console"; -import process from "node:process"; import ora from "ora"; -import { getContractRange } from "../contracts/utils.ts"; import { apiClient } from "../../apiClient.ts"; +import { getContractRange } from "../contracts/utils.ts"; dayjs.extend(relativeTime); dayjs.extend(duration); @@ -40,19 +40,17 @@ function _registerExtend(program: Command) { .addHelpText( "before", ` -${ - boxen( - `\x1b[31m\x1b[97msf extend\x1b[31m is deprecated.\x1b[0m +${boxen( + `\x1b[31m\x1b[97msf extend\x1b[31m is deprecated.\x1b[0m \x1b[31mTo create, extend, and release specific machines directly, use \x1b[97msf nodes\x1b[31m.\x1b[0m \x1b[31mTo "extend" a contract, use \x1b[97msf buy -colo -s -d \x1b[31m.\x1b[0m \x1b[31mHowever, contracts don't map to specific machines, so you can't choose which will persist.\x1b[0m \x1b[31mWe strongly recommend using \x1b[97msf nodes\x1b[31m instead.\x1b[0m`, - { - padding: 0.75, - borderColor: "red", - }, - ) - } + { + padding: 0.75, + borderColor: "red", + }, +)} `, ) .action(async function extendAction(options) { @@ -74,8 +72,8 @@ ${ spinner.clear(); } - const contractRange = contract?.shape && - getContractRange(contract?.shape); + const contractRange = + contract?.shape && getContractRange(contract?.shape); // Build the equivalent sf buy command let equivalentCommand = `sf buy -colo ${options.contract} -s ${ @@ -95,17 +93,19 @@ ${ equivalentCommand += ` --standing`; } - console.error(boxen( - `\x1b[31m\x1b[97msf extend\x1b[31m is deprecated.\x1b[0m + console.error( + boxen( + `\x1b[31m\x1b[97msf extend\x1b[31m is deprecated.\x1b[0m \x1b[31mTo create, extend, and release specific machines directly, use \x1b[97msf nodes\x1b[31m.\x1b[0m \x1b[31mTo "extend" a contract, use: \x1b[97m${equivalentCommand}\x1b[31m.\x1b[0m \x1b[31mHowever, contracts don't map to specific machines, so you can't choose which will persist.\x1b[0m \x1b[31mWe strongly recommend using \x1b[97msf nodes\x1b[31m instead.\x1b[0m`, - { - padding: 0.75, - borderColor: "red", - }, - )); + { + padding: 0.75, + borderColor: "red", + }, + ), + ); if (fetchFailed) { process.exit(1); diff --git a/src/lib/nodes/create.ts b/src/lib/nodes/create.ts index c0f56184..91695cca 100644 --- a/src/lib/nodes/create.ts +++ b/src/lib/nodes/create.ts @@ -1,16 +1,26 @@ -import { Command, CommanderError, Option } from "@commander-js/extra-typings"; -import { confirm } from "@inquirer/prompts"; -import { readFileSync } from "node:fs"; -import { cyan, gray, red, yellow } from "jsr:@std/fmt/colors"; import console from "node:console"; +import { readFileSync } from "node:fs"; import process from "node:process"; -import ora from "ora"; -import { type SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import { Command, CommanderError, Option } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; +import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; import advanced from "dayjs/plugin/advancedFormat"; import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; +import ora from "ora"; +import { logAndQuit } from "../../helpers/errors.ts"; +import { formatDate } from "../../helpers/format-date.ts"; +import { + parseStartDate, + roundStartDate, + selectTime, +} from "../../helpers/units.ts"; +import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import { getPricePerGpuHourFromQuote, getQuote } from "../buy/index.tsx"; +import { GPUS_PER_NODE } from "../constants.ts"; import { isFeatureEnabled } from "../posthog.ts"; import { createNodesTable, @@ -22,16 +32,6 @@ import { startOrNowOption, yesOption, } from "./utils.ts"; -import { handleNodesError, nodesClient } from "../../nodesClient.ts"; -import { logAndQuit } from "../../helpers/errors.ts"; -import { getPricePerGpuHourFromQuote, getQuote } from "../buy/index.tsx"; -import { - parseStartDate, - roundStartDate, - selectTime, -} from "../../helpers/units.ts"; -import { formatDate } from "../../helpers/format-date.ts"; -import { GPUS_PER_NODE } from "../constants.ts"; dayjs.extend(utc); dayjs.extend(advanced); @@ -58,7 +58,7 @@ function formatNodeDescription( * @throws CommanderError if invalid */ function validateCount(val: string): number { - const parsed = parseInt(val, 10); + const parsed = Number.parseInt(val, 10); if (isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, @@ -88,10 +88,9 @@ const create = new Command("create") ).conflicts("any-zone"), ) .addOption( - new Option( - "--any-zone", - "Use any zone that meets requirements", - ).conflicts("zone"), + new Option("--any-zone", "Use any zone that meets requirements").conflicts( + "zone", + ), ) .addOption(maxPriceOption) .addOption( @@ -136,61 +135,69 @@ const create = new Command("create") .hook("preAction", (command) => { const names = command.args; const { count, start, duration, end, auto, reserved, anyZone, zone } = - command - .opts(); + command.opts(); // Validate arguments if (names.length === 0 && !count) { command.error( - red("Must specify either node names or use \`--count\` option\n"), + chalk.red("Must specify either node names or use `--count` option\n"), ); } if (names.length > 0 && count) { if (names.length !== count) { - 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.error( + chalk.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`, + ), + ); } } if (auto && !anyZone && !zone) { - command.error(red( - "If --auto is provided, you must specify a zone or use --any-zone\n", - )); + command.error( + chalk.red( + "If --auto is provided, you must specify a zone or use --any-zone\n", + ), + ); } if (reserved && auto) { - command.error(red( - "Specify either --reserved or --auto, but not both\n", - )); + command.error( + chalk.red("Specify either --reserved or --auto, but not both\n"), + ); } // Validate duration/end like buy command if (typeof end !== "undefined" && typeof duration !== "undefined") { - command.error(red("Specify either --duration or --end, but not both\n")); + command.error( + chalk.red("Specify either --duration or --end, but not both\n"), + ); } // Validate that timing flags are only used with reserved nodes if ( auto && - (start !== "NOW" || typeof duration !== "undefined" || + (start !== "NOW" || + typeof duration !== "undefined" || typeof end !== "undefined") ) { command.error( - red( + chalk.red( "Auto-reserved nodes start immediately and cannot have a start time, duration, or end time.\n", ), ); } if ( - !auto && typeof duration === "undefined" && typeof end === "undefined" + !auto && + typeof duration === "undefined" && + typeof end === "undefined" ) { command.error( - red( + chalk.red( "You must specify either --duration or --end to create a reserved node.\n", ), ); @@ -237,19 +244,19 @@ async function createNodesAction( // we can use the presence of --auto to determine if the nodes are auto-reserved. const isReserved = !options.auto; const nodeType = options.auto - ? "autoreserved" as const - : "reserved" as const; + ? ("autoreserved" as const) + : ("reserved" as const); const rawUserData = options.userData ?? options.userDataFile; const wellFormedUserData = rawUserData?.isWellFormed?.() ? rawUserData : rawUserData - ? encodeURIComponent(rawUserData) - : undefined; + ? encodeURIComponent(rawUserData) + : undefined; const encodedUserData = wellFormedUserData ? btoa( - String.fromCodePoint(...new TextEncoder().encode(wellFormedUserData)), - ) + String.fromCodePoint(...new TextEncoder().encode(wellFormedUserData)), + ) : undefined; // Convert CLI options to SDK parameters @@ -269,22 +276,24 @@ async function createNodesAction( const startDate = options.start; // Check if the start date is "NOW" or on an hour boundary - const startDateIsValid = startDate === "NOW" || - (dayjs(startDate).startOf("hour").isSame(dayjs(startDate))); + const startDateIsValid = + startDate === "NOW" || + dayjs(startDate).startOf("hour").isSame(dayjs(startDate)); if (!startDateIsValid) { if (!options.yes) { options.start = await selectTime(startDate, { - message: `Start time must be "NOW" or on an hour boundary. ${ - cyan("Choose a time:") - }`, + message: `Start time must be "NOW" or on an hour boundary. ${chalk.cyan( + "Choose a time:", + )}`, }); } else { // Clamp down to "NOW" or lower hour const suggestedLowerStart = dayjs(startDate).startOf("hour"); - options.start = suggestedLowerStart < dayjs() - ? "NOW" - : suggestedLowerStart.toDate(); + options.start = + suggestedLowerStart < dayjs() + ? "NOW" + : suggestedLowerStart.toDate(); } } // Pass undefined for "NOW" to avoid race conditions - the API will use current time @@ -295,14 +304,11 @@ async function createNodesAction( // 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; + 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( - endStartTime.getTime() + (options.duration! * 1000), - ); + endDate = new Date(endStartTime.getTime() + options.duration! * 1000); } const endDateIsValid = dayjs(endDate).isSame( @@ -313,30 +319,28 @@ async function createNodesAction( // which time they're selecting if (startDateIsValid) { ora( - `Using start time: ${ - cyan( - `${formatDate(endStartTime, { forceIncludeTime: true })} ${ - dayjs(endStartTime).format("z") - }`, - ) - }`, + `Using start time: ${chalk.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. ${ - cyan("Choose a time:") - }`, + message: `End time must be on an hour boundary. ${chalk.cyan( + "Choose a time:", + )}`, }); endDate = selectedTime === "NOW" ? new Date() : selectedTime; } else { - const suggestedHigherEnd = dayjs(endDate).startOf("hour").add( - 1, - "hour", - ); - endDate = suggestedHigherEnd < dayjs() - ? new Date() - : suggestedHigherEnd.toDate(); + const suggestedHigherEnd = dayjs(endDate) + .startOf("hour") + .add(1, "hour"); + endDate = + suggestedHigherEnd < dayjs() + ? new Date() + : suggestedHigherEnd.toDate(); } } createParams.end_at = Math.floor(endDate.getTime() / 1000); @@ -345,34 +349,34 @@ async function createNodesAction( // Only show pricing and get confirmation if not using --yes if (!options.yes) { - let confirmationMessage = `Create ${ - formatNodeDescription(count, nodeType) - }`; + let confirmationMessage = `Create ${formatNodeDescription( + count, + nodeType, + )}`; if (isReserved) { // Reserved nodes - get quote for accurate pricing const spinner = ora( `Quoting ${formatNodeDescription(count, nodeType)}...`, - ) - .start(); + ).start(); // Calculate duration for quote - let durationSeconds: number = 3600; // Default 1 hour + let durationSeconds = 3600; // Default 1 hour if (options.duration) { durationSeconds = options.duration; } else if (options.end) { - const startDate = typeof options.start === "string" - ? new Date() - : options.start; + const startDate = + typeof options.start === "string" ? new Date() : options.start; durationSeconds = Math.floor( (options.end.getTime() - startDate.getTime()) / 1000, ); } // Add flexibility to duration for better quote matching (matches buy command logic) - const startsAt = options.start === "NOW" - ? "NOW" - : roundStartDate(parseStartDate(options.start)); + const startsAt = + options.start === "NOW" + ? "NOW" + : roundStartDate(parseStartDate(options.start)); const minDurationSeconds = Math.max( 1, durationSeconds - Math.ceil(durationSeconds * 0.1), @@ -398,27 +402,27 @@ async function createNodesAction( if (quote) { const pricePerGpuHour = getPricePerGpuHourFromQuote(quote); const pricePerNodeHour = (pricePerGpuHour * GPUS_PER_NODE) / 100; - confirmationMessage += ` for ~$${ - pricePerNodeHour.toFixed(2) - }/node/hr`; + confirmationMessage += ` for ~$${pricePerNodeHour.toFixed( + 2, + )}/node/hr`; if ("zone" in quote) { - confirmationMessage += ` on ${cyan(quote.zone)}`; + confirmationMessage += ` on ${chalk.cyan(quote.zone)}`; createParams.zone ||= quote.zone; } } else { logAndQuit( - red( + chalk.red( "No capacity available matching your hardware and pricing requirements. You can view zone capacity at https://sfcompute.com/dashboard/zones.", ), ); } } else if (options.maxPrice) { // Auto Reserved nodes - show max price they're willing to pay - confirmationMessage += ` for up to $${ - options.maxPrice.toFixed(2) - }/node/hr`; + confirmationMessage += ` for up to $${options.maxPrice.toFixed( + 2, + )}/node/hr`; if (options.zone) { - confirmationMessage += ` on ${cyan(options.zone)}`; + confirmationMessage += ` on ${chalk.cyan(options.zone)}`; } else { confirmationMessage += ` on any matching zone`; } @@ -444,9 +448,10 @@ async function createNodesAction( const { data: createdNodes } = await client.nodes.create(createParams); spinner.succeed( - `Successfully created ${ - formatNodeDescription(createdNodes.length, nodeType) - }`, + `Successfully created ${formatNodeDescription( + createdNodes.length, + nodeType, + )}`, ); if (options.json) { @@ -455,31 +460,27 @@ async function createNodesAction( } if (createdNodes.length > 0) { - console.log(gray("\nCreated nodes:")); + console.log(chalk.gray("\nCreated nodes:")); console.log(createNodesTable(createdNodes)); - console.log( - `\n${gray("Next steps:")}`, - ); - console.log( - ` sf nodes list`, - ); + console.log(`\n${chalk.gray("Next steps:")}`); + console.log(` sf nodes list`); // Auto Reserved nodes can't be extended, so only suggest it for reserved nodes if (isReserved) { console.log( - ` sf nodes extend ${cyan(createdNodes?.[0]?.name ?? "my-node")}`, + ` sf nodes extend ${chalk.cyan(createdNodes?.[0]?.name ?? "my-node")}`, ); } else { console.log( - ` sf nodes set ${ - cyan(createdNodes?.[0]?.name ?? "my-node") - } --max-price ${cyan("12.50")}`, + ` sf nodes set ${chalk.cyan( + createdNodes?.[0]?.name ?? "my-node", + )} --max-price ${chalk.cyan("12.50")}`, ); } console.log( - ` sf nodes release ${cyan(createdNodes?.[0]?.name ?? "my-node")}`, + ` sf nodes release ${chalk.cyan(createdNodes?.[0]?.name ?? "my-node")}`, ); } else { - console.log(yellow("No nodes created.\n")); + console.log(chalk.yellow("No nodes created.\n")); create.help(); } } catch (err) { diff --git a/src/lib/nodes/delete.ts b/src/lib/nodes/delete.ts index c35605b2..5ef2f87e 100644 --- a/src/lib/nodes/delete.ts +++ b/src/lib/nodes/delete.ts @@ -1,10 +1,10 @@ -import { Command } from "@commander-js/extra-typings"; -import { confirm } from "@inquirer/prompts"; -import { brightRed, gray, red } from "jsr:@std/fmt/colors"; import console from "node:console"; import process from "node:process"; -import ora from "ora"; +import { Command } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import ora from "ora"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { @@ -15,9 +15,7 @@ import { } from "./utils.ts"; const deleteCommand = new Command("delete") - .description( - "Permanently delete compute nodes", - ) + .description("Permanently delete compute nodes") .showHelpAfterError() .argument("", "Node IDs or names to delete") .addOption(yesOption) @@ -67,8 +65,8 @@ async function deleteNodesAction( const notFound: string[] = []; for (const nameOrId of nodeNames) { - const node = fetchedNodes.find((n) => - n.name === nameOrId || n.id === nameOrId + const node = fetchedNodes.find( + (n) => n.name === nameOrId || n.id === nameOrId, ); if (node) { foundNodes.push({ name: nameOrId, node }); @@ -79,10 +77,10 @@ async function deleteNodesAction( if (notFound.length > 0) { console.log( - red( - `Could not find ${notFound.length === 1 ? "this" : "these"} ${ - pluralizeNodes(notFound.length) - }:`, + chalk.red( + `Could not find ${notFound.length === 1 ? "this" : "these"} ${pluralizeNodes( + notFound.length, + )}:`, ), ); for (const name of notFound) { @@ -104,7 +102,7 @@ async function deleteNodesAction( for (const { name, node } of foundNodes) { // Check if node has an active or pending VM const hasActiveVM = node.vms?.data?.some((vm) => - activeOrPendingVMStatuses.includes(vm.status) + activeOrPendingVMStatuses.includes(vm.status), ); if (hasActiveVM) { @@ -128,17 +126,17 @@ async function deleteNodesAction( // Print nodes with active or pending VMs if (activeVMNodes.length > 0) { console.log( - red( - `Cannot delete ${activeVMNodes.length === 1 ? "this" : "these"} ${ - pluralizeNodes(activeVMNodes.length) - } with active or pending VMs:`, + chalk.red( + `Cannot delete ${activeVMNodes.length === 1 ? "this" : "these"} ${pluralizeNodes( + activeVMNodes.length, + )} with active or pending VMs:`, ), ); for (const { name } of activeVMNodes) { console.log(` • ${name}`); } console.log( - brightRed( + chalk.redBright( "\nNodes cannot be deleted while they have an active or pending VM.\n", ), ); @@ -147,22 +145,22 @@ async function deleteNodesAction( // Print non-released auto-reserved nodes if (unreleasedAutoNodes.length > 0) { console.log( - red( + chalk.red( `Cannot delete ${ unreleasedAutoNodes.length === 1 ? "this" : "these" - } ${ - pluralizeNodes(unreleasedAutoNodes.length) - } non-released auto-reserved nodes:`, + } ${pluralizeNodes( + unreleasedAutoNodes.length, + )} non-released auto-reserved nodes:`, ), ); for (const { name } of unreleasedAutoNodes) { console.log(` • ${name}`); } console.log( - brightRed( - `\nRelease the ${ - pluralizeNodes(unreleasedAutoNodes.length) - } first with: sf nodes release \n`, + chalk.redBright( + `\nRelease the ${pluralizeNodes( + unreleasedAutoNodes.length, + )} first with: sf nodes release \n`, ), ); } @@ -177,11 +175,7 @@ async function deleteNodesAction( if (options.dryRun) { if (options.json) { - console.log(JSON.stringify( - nodesToDelete, - null, - 2, - )); + console.log(JSON.stringify(nodesToDelete, null, 2)); process.exit(0); } @@ -190,10 +184,10 @@ async function deleteNodesAction( if (nodesToDelete.length > 0) { console.log(createNodesTable(nodesToDelete)); console.log( - gray( - `\nWould delete ${nodesToDelete.length} ${ - pluralizeNodes(nodesToDelete.length) - }.`, + chalk.gray( + `\nWould delete ${nodesToDelete.length} ${pluralizeNodes( + nodesToDelete.length, + )}.`, ), ); } @@ -205,16 +199,16 @@ async function deleteNodesAction( if (!options.yes) { if (nodesToDelete.length > 0) { console.log( - `The following ${ - pluralizeNodes(nodesToDelete.length) - } will be permanently deleted:`, + `The following ${pluralizeNodes( + nodesToDelete.length, + )} will be permanently deleted:`, ); console.log(createNodesTable(nodesToDelete)); } if (nodesToDelete.length > 0) { console.log( - brightRed( + chalk.redBright( `\nWarning: ${ nodesToDelete.length === 1 ? "This node" : "These nodes" } will be permanently deleted and cannot be recovered.\n`, @@ -223,9 +217,9 @@ async function deleteNodesAction( } const confirmed = await confirm({ - message: `Delete ${nodesToDelete.length} ${ - pluralizeNodes(nodesToDelete.length) - }? This action cannot be undone.`, + message: `Delete ${nodesToDelete.length} ${pluralizeNodes( + nodesToDelete.length, + )}? This action cannot be undone.`, default: false, }); if (!confirmed) { @@ -235,11 +229,10 @@ async function deleteNodesAction( } const deleteSpinner = ora( - `Deleting ${nodesToDelete.length} ${ - pluralizeNodes(nodesToDelete.length) - }...`, - ) - .start(); + `Deleting ${nodesToDelete.length} ${pluralizeNodes( + nodesToDelete.length, + )}...`, + ).start(); const results: { name: string; status: string }[] = []; const errors: { name: string; error: string }[] = []; @@ -266,23 +259,19 @@ async function deleteNodesAction( deleteSpinner.fail("Failed to delete any nodes"); } else { deleteSpinner.warn( - `Deleted ${results.length} ${ - pluralizeNodes(results.length) - }, but ${errors.length} failed`, + `Deleted ${results.length} ${pluralizeNodes( + results.length, + )}, but ${errors.length} failed`, ); } - console.error(gray("\nFailed to delete:")); + console.error(chalk.gray("\nFailed to delete:")); for (const error of errors) { console.error(` • ${error.name}: ${error.error}`); } } if (options.json) { - console.log(JSON.stringify( - results, - null, - 2, - )); + console.log(JSON.stringify(results, null, 2)); process.exit(0); } } catch (err) { diff --git a/src/lib/nodes/extend.ts b/src/lib/nodes/extend.ts index f67d9ae6..3ca28d1a 100644 --- a/src/lib/nodes/extend.ts +++ b/src/lib/nodes/extend.ts @@ -1,11 +1,17 @@ -import { Command } from "@commander-js/extra-typings"; -import { confirm } from "@inquirer/prompts"; -import { brightRed, cyan, gray, red } from "jsr:@std/fmt/colors"; import console from "node:console"; import process from "node:process"; -import ora from "ora"; +import { Command } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; +import type SFCNodes from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import { formatDuration } from "date-fns/formatDuration"; +import { intervalToDuration } from "date-fns/intervalToDuration"; import dayjs from "dayjs"; +import ora from "ora"; +import { selectTime } from "../../helpers/units.ts"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import { getPricePerGpuHourFromQuote, getQuote } from "../buy/index.tsx"; +import { GPUS_PER_NODE } from "../constants.ts"; import { createNodesTable, jsonOption, @@ -14,12 +20,6 @@ import { requiredDurationOption, yesOption, } from "./utils.ts"; -import SFCNodes from "@sfcompute/nodes-sdk-alpha"; -import { getPricePerGpuHourFromQuote, getQuote } from "../buy/index.tsx"; -import { GPUS_PER_NODE } from "../constants.ts"; -import { formatDuration } from "date-fns/formatDuration"; -import { intervalToDuration } from "date-fns/intervalToDuration"; -import { selectTime } from "../../helpers/units.ts"; const extend = new Command("extend") .description("Extend the duration of reserved nodes and update their pricing") @@ -70,8 +70,8 @@ async function extendNodeAction( const notFound: string[] = []; for (const nameOrId of nodeNames) { - const node = fetchedNodes.find((n) => - n.name === nameOrId || n.id === nameOrId + const node = fetchedNodes.find( + (n) => n.name === nameOrId || n.id === nameOrId, ); if (node) { nodes.push({ name: nameOrId, node }); @@ -82,10 +82,10 @@ async function extendNodeAction( if (notFound.length > 0) { console.log( - red( - `Could not find ${notFound.length === 1 ? "this" : "these"} ${ - pluralizeNodes(notFound.length) - }:`, + chalk.red( + `Could not find ${notFound.length === 1 ? "this" : "these"} ${pluralizeNodes( + notFound.length, + )}:`, ), ); for (const name of notFound) { @@ -95,28 +95,28 @@ async function extendNodeAction( } // Filter out auto reserved nodes (they can't be extended) - const autoReservedNodes = nodes.filter(({ node }) => - node.node_type === "autoreserved" + const autoReservedNodes = nodes.filter( + ({ node }) => node.node_type === "autoreserved", ); - const extendableNodes = nodes.filter(({ node }) => - node.node_type !== "autoreserved" + const extendableNodes = nodes.filter( + ({ node }) => node.node_type !== "autoreserved", ); if (autoReservedNodes.length > 0) { console.log( - red( + chalk.red( `Cannot extend ${ autoReservedNodes.length === 1 ? "this" : "these" - } auto reserved ${ - pluralizeNodes(autoReservedNodes.length) - } (they auto-extend):`, + } auto reserved ${pluralizeNodes( + autoReservedNodes.length, + )} (they auto-extend):`, ), ); for (const { name } of autoReservedNodes) { console.log(` • ${name}`); } console.log( - brightRed( + chalk.redBright( `\nTo configure auto reserved nodes, use the \`sf nodes set\` command.`, ), ); @@ -141,22 +141,20 @@ async function extendNodeAction( ); const selectedTime = await selectTime(calculatedEndTime, { - message: `Nodes must be extended to an hour boundary. ${ - cyan("Choose an end time:") - }`, + message: `Nodes must be extended to an hour boundary. ${chalk.cyan( + "Choose an end time:", + )}`, }); if (selectedTime === "NOW") { - console.error(red("You must extend to a future time")); + console.error(chalk.red("You must extend to a future time")); process.exit(1); } // Update duration based on selected time options.duration = Math.max( 0, - Math.floor( - (selectedTime.getTime() - startTime.getTime()) / 1000, - ), + Math.floor((selectedTime.getTime() - startTime.getTime()) / 1000), ); } else if (!isHourMultiple && options.yes) { // Round up to the next hour boundary @@ -177,9 +175,9 @@ async function extendNodeAction( if (!options.yes) { // Get quote for accurate pricing preview const spinner = ora( - `Quoting extending ${extendableNodes.length} ${ - pluralizeNodes(extendableNodes.length) - }...`, + `Quoting extending ${extendableNodes.length} ${pluralizeNodes( + extendableNodes.length, + )}...`, ).start(); // Add flexibility to duration for better quote matching (matches buy command logic) @@ -207,15 +205,15 @@ async function extendNodeAction( }), ); - const filteredQuotes = quotes.filter((quote) => - quote.status === "fulfilled" + const filteredQuotes = quotes.filter( + (quote) => quote.status === "fulfilled", ); spinner.stop(); - let confirmationMessage = `Extend ${extendableNodes.length} ${ - pluralizeNodes(extendableNodes.length) - } for ${formattedDuration}`; + let confirmationMessage = `Extend ${extendableNodes.length} ${pluralizeNodes( + extendableNodes.length, + )} for ${formattedDuration}`; // If there's only one node, show the price per node per hour if (filteredQuotes.length === 1 && filteredQuotes[0].value) { @@ -231,7 +229,7 @@ async function extendNodeAction( // If there's multiple nodes, show the total price, as nodes could be on different zones or have different hardware confirmationMessage += ` for ~$${totalPrice / 100}`; } else { - confirmationMessage = red( + confirmationMessage = chalk.red( "No nodes available matching your requirements. This is likely due to insufficient capacity. Attempt to extend anyway", ); } @@ -244,9 +242,9 @@ async function extendNodeAction( } const spinner = ora( - `Extending ${extendableNodes.length} ${ - pluralizeNodes(extendableNodes.length) - }...`, + `Extending ${extendableNodes.length} ${pluralizeNodes( + extendableNodes.length, + )}...`, ).start(); const results: { name: string; node: SFCNodes.Node }[] = []; @@ -267,15 +265,21 @@ async function extendNodeAction( } if (options.json) { - console.log(JSON.stringify(results.map((r) => r.node), null, 2)); + console.log( + JSON.stringify( + results.map((r) => r.node), + null, + 2, + ), + ); process.exit(0); } if (results.length > 0) { spinner.succeed( - `Successfully extended ${results.length} ${ - pluralizeNodes(results.length) - }`, + `Successfully extended ${results.length} ${pluralizeNodes( + results.length, + )}`, ); } @@ -284,26 +288,24 @@ async function extendNodeAction( spinner.fail("Failed to extend any nodes"); } else { spinner.warn( - `Extended ${results.length} ${ - pluralizeNodes(results.length) - }, but ${errors.length} failed`, + `Extended ${results.length} ${pluralizeNodes( + results.length, + )}, but ${errors.length} failed`, ); } } if (results.length > 0) { - console.log(gray("\nExtended nodes:")); + console.log(chalk.gray("\nExtended nodes:")); console.log(createNodesTable(results.map((r) => r.node))); + console.log(chalk.gray(`\nDuration extended by ${formattedDuration}`)); console.log( - gray( - `\nDuration extended by ${formattedDuration}`, - ), + chalk.gray(`Max price: $${options.maxPrice.toFixed(2)}/hour`), ); - console.log(gray(`Max price: $${options.maxPrice.toFixed(2)}/hour`)); } if (errors.length > 0) { - console.log(gray("\nFailed to extend:")); + console.log(chalk.gray("\nFailed to extend:")); for (const error of errors) { console.log(` • ${error.name}: ${error.error}`); } diff --git a/src/lib/nodes/get.tsx b/src/lib/nodes/get.tsx index 81fa93de..1d4d201f 100644 --- a/src/lib/nodes/get.tsx +++ b/src/lib/nodes/get.tsx @@ -1,16 +1,15 @@ -import React from "react"; -import { Command } from "@commander-js/extra-typings"; -import { gray, red } from "jsr:@std/fmt/colors"; import console from "node:console"; import process from "node:process"; -import ora from "ora"; +import { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; import { render } from "ink"; +import ora from "ora"; -import { handleNodesError, nodesClient } from "../../nodesClient.ts"; -import { createNodesTable, jsonOption, pluralizeNodes } from "./utils.ts"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit } from "../../helpers/errors.ts"; +import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { NodesVerboseDisplay } from "./list.tsx"; +import { createNodesTable, jsonOption, pluralizeNodes } from "./utils.ts"; const get = new Command("get") .alias("show") @@ -68,10 +67,10 @@ async function getNodesAction( if (notFound.length > 0) { console.error( - red( - `Could not find ${notFound.length === 1 ? "this" : "these"} ${ - pluralizeNodes(notFound.length) - }:`, + chalk.red( + `Could not find ${notFound.length === 1 ? "this" : "these"} ${pluralizeNodes( + notFound.length, + )}:`, ), ); for (const name of notFound) { @@ -89,10 +88,10 @@ async function getNodesAction( // Show table format console.log(createNodesTable(fetchedNodes)); console.log( - gray( - `\nFound ${fetchedNodes.length} ${ - pluralizeNodes(fetchedNodes.length) - }.`, + chalk.gray( + `\nFound ${fetchedNodes.length} ${pluralizeNodes( + fetchedNodes.length, + )}.`, ), ); } else { diff --git a/src/lib/nodes/image/list.tsx b/src/lib/nodes/image/list.tsx index dd036768..2d0fd367 100644 --- a/src/lib/nodes/image/list.tsx +++ b/src/lib/nodes/image/list.tsx @@ -1,15 +1,8 @@ -import { Command } from "@commander-js/extra-typings"; -import { - brightBlack, - cyan, - gray, - green, - red, - yellow, -} from "jsr:@std/fmt/colors"; import console from "node:console"; -import ora from "ora"; +import { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; import Table from "cli-table3"; +import ora from "ora"; import { getAuthToken } from "../../../helpers/config.ts"; import { logAndQuit } from "../../../helpers/errors.ts"; @@ -55,7 +48,7 @@ Next Steps:\n if (images.length === 0) { console.log("No images found."); - console.log(gray("\nUpload your first image:")); + console.log(chalk.gray("\nUpload your first image:")); console.log(" sf node images upload -f ./my-image.img -n my-image"); return; } @@ -71,10 +64,10 @@ Next Steps:\n // Create and display images table const table = new Table({ head: [ - cyan("NAME"), - cyan("ID"), - cyan("STATUS"), - cyan("CREATED"), + chalk.cyan("NAME"), + chalk.cyan("ID"), + chalk.cyan("STATUS"), + chalk.cyan("CREATED"), ], style: { head: [], @@ -90,30 +83,25 @@ Next Steps:\n const status = (() => { switch (image.upload_status) { case "started": - return green("Started"); + return chalk.green("Started"); case "uploading": - return yellow("Uploading"); + return chalk.yellow("Uploading"); case "completed": - return cyan("Completed"); + return chalk.cyan("Completed"); case "failed": - return red("Failed"); + return chalk.red("Failed"); default: - return gray("Unknown"); + return chalk.gray("Unknown"); } })(); - table.push([ - image.name, - image.image_id, - status, - createdAt, - ]); + table.push([image.name, image.image_id, status, createdAt]); } if (images.length > 5) { table.push([ { colSpan: 4, - content: brightBlack( + content: chalk.blackBright( `${images.length - 5} older ${ images.length - 5 === 1 ? "image" : "images" } not shown. Use sf node images list --json to list all images.`, @@ -125,21 +113,21 @@ Next Steps:\n console.log(table.toString()); // Show next steps - console.log(gray("\nNext steps:")); + console.log(chalk.gray("\nNext steps:")); // Always show how to get info for a specific image const firstImage = sortedImages[0]; if (firstImage) { - console.log(` sf node images show ${cyan(firstImage.image_id)}`); + console.log(` sf node images show ${chalk.cyan(firstImage.image_id)}`); } const firstCompletedImage = sortedImages.find( (image) => image.upload_status === "completed", ); if (firstCompletedImage) { console.log( - ` sf nodes create -z hayesvalley -d 2h -p 13.50 --image ${ - cyan(firstCompletedImage.image_id) - }`, + ` sf nodes create -z hayesvalley -d 2h -p 13.50 --image ${chalk.cyan( + firstCompletedImage.image_id, + )}`, ); } } catch (err) { diff --git a/src/lib/nodes/image/show.tsx b/src/lib/nodes/image/show.tsx index 90c3d753..666da358 100644 --- a/src/lib/nodes/image/show.tsx +++ b/src/lib/nodes/image/show.tsx @@ -1,27 +1,26 @@ -import React from "react"; -import SFCNodes from "@sfcompute/nodes-sdk-alpha"; -import { Command } from "@commander-js/extra-typings"; -import { Box, render, Text } from "ink"; -import Link from "ink-link"; import console from "node:console"; +import { Command } from "@commander-js/extra-typings"; +import type SFCNodes from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; import advanced from "dayjs/plugin/advancedFormat"; import timezone from "dayjs/plugin/timezone"; -import { brightBlack } from "jsr:@std/fmt/colors"; +import utc from "dayjs/plugin/utc"; +import { Box, Text, render } from "ink"; +import Link from "ink-link"; +import { formatDate } from "../../../helpers/format-date.ts"; import { handleNodesError, nodesClient } from "../../../nodesClient.ts"; import { Row } from "../../Row.tsx"; -import { formatDate } from "../../../helpers/format-date.ts"; dayjs.extend(utc); dayjs.extend(advanced); dayjs.extend(timezone); -export function ImageDisplay( - { image }: { - image: SFCNodes.VMs.ImageGetResponse; - }, -) { +export function ImageDisplay({ + image, +}: { + image: SFCNodes.VMs.ImageGetResponse; +}) { const expiresAt = image.expires_at ? new Date(image.expires_at) : null; const isExpired = expiresAt ? expiresAt < new Date() : false; @@ -29,17 +28,11 @@ export function ImageDisplay( const statusText = isExpired ? "Expired" : "Ready"; return ( - - - Image: {image.name} ({image.image_id}) + + + + Image: {image.name} ({image.image_id}) + @@ -55,9 +48,7 @@ export function ImageDisplay( head="URL: " value={ - - Use curl or wget to download. - + Use curl or wget to download. {image.download_url} @@ -70,10 +61,11 @@ export function ImageDisplay( value={ - {expiresAt.toISOString()} {brightBlack( - `(${formatDate(dayjs(expiresAt).toDate())} ${ - dayjs(expiresAt).format("z") - })`, + {expiresAt.toISOString()}{" "} + {chalk.blackBright( + `(${formatDate(dayjs(expiresAt).toDate())} ${dayjs( + expiresAt, + ).format("z")})`, )} {isExpired && (Expired)} diff --git a/src/lib/nodes/list.tsx b/src/lib/nodes/list.tsx index 5a28b8f3..114fd390 100644 --- a/src/lib/nodes/list.tsx +++ b/src/lib/nodes/list.tsx @@ -1,15 +1,14 @@ -import React from "react"; -import { Command, Option } from "@commander-js/extra-typings"; -import { brightBlack, cyan, gray } from "jsr:@std/fmt/colors"; import console from "node:console"; -import ora from "ora"; +import { Command, Option } from "@commander-js/extra-typings"; +import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import { formatDuration, intervalToDuration } from "date-fns"; import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; import advanced from "dayjs/plugin/advancedFormat"; import timezone from "dayjs/plugin/timezone"; -import { Box, render, Text } from "ink"; -import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; -import { formatDuration, intervalToDuration } from "date-fns"; +import utc from "dayjs/plugin/utc"; +import { Box, Text, render } from "ink"; +import ora from "ora"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit } from "../../helpers/errors.ts"; @@ -20,8 +19,8 @@ import { import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { Row } from "../Row.tsx"; import { - createNodesTable, DEFAULT_NODE_LS_LIMIT, + createNodesTable, getLastVM, getStatusColor, getVMStatusColor, @@ -65,7 +64,9 @@ function VMTable({ vms }: { vms: NonNullable["data"] }) { borderRight={false} borderColor="gray" > - VM History + + VM History + {vmsToShow.map((vm) => ( @@ -92,7 +93,9 @@ function VMTable({ vms }: { vms: NonNullable["data"] }) { borderColor="gray" marginRight={-1} > - Status + + Status + {vmsToShow.map((vm) => ( @@ -119,11 +122,13 @@ function VMTable({ vms }: { vms: NonNullable["data"] }) { borderColor="gray" marginRight={-1} > - Zone + + Zone + {vmsToShow.map((vm) => ( - {cyan(vm.zone)} + {chalk.cyan(vm.zone)} ))} @@ -139,7 +144,9 @@ function VMTable({ vms }: { vms: NonNullable["data"] }) { borderRight={false} borderColor="gray" > - Start/End + + Start/End + {vmsToShow.map((vm) => { const startDate = vm.start_at ? dayjs.unix(vm.start_at) : null; @@ -160,9 +167,7 @@ function VMTable({ vms }: { vms: NonNullable["data"] }) { {remainingVms} past {remainingVms === 1 ? "VM" : "VMs"} not shown. - - (To see all VMs, use `sf nodes ls --json`) - + (To see all VMs, use `sf nodes ls --json`) )} @@ -179,7 +184,7 @@ function getActionsForNode(node: SFCNodes.Node) { if (lastVm?.image_id) { nodeActions.push({ label: "Image", - command: `sf nodes image show ${brightBlack(lastVm.image_id)}`, + command: `sf nodes image show ${chalk.blackBright(lastVm.image_id)}`, }); } @@ -189,18 +194,16 @@ function getActionsForNode(node: SFCNodes.Node) { if (lastVm?.id) { nodeActions.push({ label: "SSH", - command: `sf nodes ssh root@${brightBlack(node.name)}`, + command: `sf nodes ssh root@${chalk.blackBright(node.name)}`, + }); + nodeActions.push({ + label: "Logs", + command: `sf nodes logs ${chalk.blackBright(node.name)}`, }); - nodeActions.push( - { - label: "Logs", - command: `sf nodes logs ${brightBlack(node.name)}`, - }, - ); } nodeActions.push({ label: "Delete", - command: `sf nodes delete ${brightBlack(node.name)}`, + command: `sf nodes delete ${chalk.blackBright(node.name)}`, }); break; @@ -216,11 +219,11 @@ function getActionsForNode(node: SFCNodes.Node) { nodeActions.push( { label: "SSH", - command: `sf nodes ssh root@${brightBlack(node.name)}`, + command: `sf nodes ssh root@${chalk.blackBright(node.name)}`, }, { label: "Logs", - command: `sf nodes logs ${brightBlack(node.name)}`, + command: `sf nodes logs ${chalk.blackBright(node.name)}`, }, ); } @@ -228,7 +231,7 @@ function getActionsForNode(node: SFCNodes.Node) { // Redeploy is available for all running nodes nodeActions.push({ label: "Redeploy", - command: `sf nodes redeploy ${brightBlack(node.name)}`, + command: `sf nodes redeploy ${chalk.blackBright(node.name)}`, }); if (node.node_type === "reserved") { @@ -236,13 +239,13 @@ function getActionsForNode(node: SFCNodes.Node) { nodeActions.push( { label: "Extend", - command: `sf nodes extend ${ - brightBlack(node.name) - } --duration 60 --max-price 12.00`, + command: `sf nodes extend ${chalk.blackBright( + node.name, + )} --duration 60 --max-price 12.00`, }, { label: "Delete", - command: `sf nodes delete ${brightBlack(node.name)}`, + command: `sf nodes delete ${chalk.blackBright(node.name)}`, }, ); } else if (node.node_type === "autoreserved") { @@ -250,15 +253,15 @@ function getActionsForNode(node: SFCNodes.Node) { nodeActions.push( { label: "Update", - command: `sf nodes set ${brightBlack(node.name)} --max-price 12.50`, + command: `sf nodes set ${chalk.blackBright(node.name)} --max-price 12.50`, }, { label: "Release", - command: `sf nodes release ${brightBlack(node.name)}`, + command: `sf nodes release ${chalk.blackBright(node.name)}`, }, { label: "Delete", - command: `sf nodes delete ${brightBlack(node.name)}`, + command: `sf nodes delete ${chalk.blackBright(node.name)}`, }, ); } @@ -268,12 +271,10 @@ function getActionsForNode(node: SFCNodes.Node) { case "awaitingcapacity": // Pending/awaiting nodes if (lastVm?.id) { - nodeActions.push( - { - label: "Logs", - command: `sf nodes logs ${brightBlack(node.name)}`, - }, - ); + nodeActions.push({ + label: "Logs", + command: `sf nodes logs ${chalk.blackBright(node.name)}`, + }); } if (node.node_type === "autoreserved") { @@ -281,22 +282,22 @@ function getActionsForNode(node: SFCNodes.Node) { nodeActions.push( { label: "Update", - command: `sf nodes set ${brightBlack(node.name)} --max-price 12.50`, + command: `sf nodes set ${chalk.blackBright(node.name)} --max-price 12.50`, }, { label: "Release", - command: `sf nodes release ${brightBlack(node.name)}`, + command: `sf nodes release ${chalk.blackBright(node.name)}`, }, { label: "Delete", - command: `sf nodes delete ${brightBlack(node.name)}`, + command: `sf nodes delete ${chalk.blackBright(node.name)}`, }, ); } else if (node.node_type === "reserved") { // Reserved nodes: can delete nodeActions.push({ label: "Delete", - command: `sf nodes delete ${brightBlack(node.name)}`, + command: `sf nodes delete ${chalk.blackBright(node.name)}`, }); } break; @@ -307,17 +308,17 @@ function getActionsForNode(node: SFCNodes.Node) { nodeActions.push( { label: "SSH", - command: `sf nodes ssh root@${brightBlack(node.name)}`, + command: `sf nodes ssh root@${chalk.blackBright(node.name)}`, }, { label: "Logs", - command: `sf nodes logs ${brightBlack(node.name)}`, + command: `sf nodes logs ${chalk.blackBright(node.name)}`, }, ); } nodeActions.push({ label: "Release", - command: `sf nodes release ${brightBlack(node.name)}`, + command: `sf nodes release ${chalk.blackBright(node.name)}`, }); break; } @@ -336,23 +337,24 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { } const durationLabel = duration ? formatDuration( - intervalToDuration({ - start: 0, - end: duration * 60 * 60 * 1000, - }), - { - delimiter: ", ", - format: ["years", "months", "weeks", "days", "hours"], - }, - ) + intervalToDuration({ + start: 0, + end: duration * 60 * 60 * 1000, + }), + { + delimiter: ", ", + format: ["years", "months", "weeks", "days", "hours"], + }, + ) : null; // Convert max_price_per_node_hour from cents to dollars const pricePerHour = node.max_price_per_node_hour - ? (node.max_price_per_node_hour / 100) - : 0; - const totalCost = duration && node.max_price_per_node_hour - ? (duration * node.max_price_per_node_hour / 100) + ? node.max_price_per_node_hour / 100 : 0; + const totalCost = + duration && node.max_price_per_node_hour + ? (duration * node.max_price_per_node_hour) / 100 + : 0; // Get available actions for this node const nodeActions = getActionsForNode(node); @@ -376,7 +378,9 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { borderRight={false} borderColor="cyan" > - Node: {node.name} + + Node: {node.name} + {/* Basic Information */} @@ -390,11 +394,14 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { @@ -403,39 +410,43 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {lastVM && ( <> - Active VM: + + Active VM: + - + @@ -443,57 +454,55 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {node.vms?.data && node.vms.data.length > 1 && ( - - - + + )} - Schedule: + + Schedule: + - {duration && ( - - )} + {duration && } {node.max_price_per_node_hour && ( <> - Pricing: + + Pricing: + {node.node_type === "autoreserved" && ( @@ -513,9 +522,9 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { )} @@ -527,7 +536,9 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {nodeActions.length > 0 && ( <> - Actions: + + Actions: + {nodeActions.map((action, index) => ( @@ -568,11 +579,10 @@ async function listNodesAction(options: ReturnType) { spinner.stop(); - const filteredNodes = (options.status?.length) - ? nodes.filter((n) => - options.status?.length && - options.status.includes(n.status) - ) + const filteredNodes = options.status?.length + ? nodes.filter( + (n) => options.status?.length && options.status.includes(n.status), + ) : nodes; if (options.json) { @@ -582,7 +592,7 @@ async function listNodesAction(options: ReturnType) { if (filteredNodes.length === 0) { console.log("No nodes found."); - console.log(gray("\nCreate your first node:")); + console.log(chalk.gray("\nCreate your first node:")); console.log(" sf nodes create my-first-node"); return; } @@ -594,10 +604,10 @@ async function listNodesAction(options: ReturnType) { } else { console.log(createNodesTable(filteredNodes, options.limit)); console.log( - gray( - `\nFound ${filteredNodes.length} ${ - pluralizeNodes(filteredNodes.length) - } total. Use --verbose for detailed information, such as previous virtual machines.`, + chalk.gray( + `\nFound ${filteredNodes.length} ${pluralizeNodes( + filteredNodes.length, + )} total. Use --verbose for detailed information, such as previous virtual machines.`, ), ); @@ -627,7 +637,7 @@ async function listNodesAction(options: ReturnType) { // Print Next Steps section if (allCommands.length > 0) { - console.log(gray("\nNext steps:")); + console.log(chalk.gray("\nNext steps:")); for (const command of allCommands) { console.log(` ${command}`); } @@ -650,8 +660,9 @@ const list = new Command("list") DEFAULT_NODE_LS_LIMIT, ) .addOption( - new Option("--status ", "Filter by node status") - .choices(VALID_STATES as (readonly SFCNodes.Status[])), + new Option("--status ", "Filter by node status").choices( + VALID_STATES as readonly SFCNodes.Status[], + ), ) .addOption(jsonOption) .addHelpText( diff --git a/src/lib/nodes/logs.ts b/src/lib/nodes/logs.ts index 62f49494..6ec91bd2 100644 --- a/src/lib/nodes/logs.ts +++ b/src/lib/nodes/logs.ts @@ -1,29 +1,27 @@ -import { Command, CommanderError } from "@commander-js/extra-typings"; import console from "node:console"; +import process from "node:process"; import { setTimeout } from "node:timers"; +import { Command, CommanderError } from "@commander-js/extra-typings"; +import chalk from "chalk"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; import ora from "ora"; -import { cyan } from "jsr:@std/fmt/colors"; -import process from "node:process"; -import { getLastVM } from "./utils.ts"; import { apiClient } from "../../apiClient.ts"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; -import { paths } from "../../schema.ts"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import type { paths } from "../../schema.ts"; +import { getLastVM } from "./utils.ts"; dayjs.extend(utc); type VMLogsParams = paths["/v0/vms/logs2"]["get"]["parameters"]["query"]; type VMLogsResponse = - paths["/v0/vms/logs2"]["get"]["responses"]["200"]["content"][ - "application/json" - ]["data"]; + paths["/v0/vms/logs2"]["get"]["responses"]["200"]["content"]["application/json"]["data"]; function formatTimestampToISO(timestamp: string): string { const date = dayjs(timestamp); @@ -51,7 +49,8 @@ const logs = new Command("logs") (val) => { const parsedValue = Number(val); if ( - Number.isNaN(parsedValue) || !Number.isInteger(parsedValue) || + Number.isNaN(parsedValue) || + !Number.isInteger(parsedValue) || parsedValue <= 0 ) { throw new CommanderError( @@ -103,9 +102,7 @@ Examples: try { // Validate that either node or instance is provided, but not both if (!node && !options.instance) { - logAndQuit( - "Either a node name/ID or --instance flag must be provided", - ); + logAndQuit("Either a node name/ID or --instance flag must be provided"); } if (node && options.instance) { @@ -123,15 +120,15 @@ Examples: try { const nodeData = await nodesClientInstance.nodes.get(node); - spinner.succeed(`Node found for name ${cyan(node)}.`); + spinner.succeed(`Node found for name ${chalk.cyan(node)}.`); const lastVm = getLastVM(nodeData); if (!lastVm) { spinner.fail( - `Node ${ - cyan(node) - } does not have a VM. VMs can take up to 5-10 minutes to spin up.`, + `Node ${chalk.cyan( + node, + )} does not have a VM. VMs can take up to 5-10 minutes to spin up.`, ); process.exit(1); } @@ -139,7 +136,7 @@ Examples: vmId = lastVm.id; } catch { spinner.info( - `No node found for name ${cyan(node)}. Interpreting as VM ID...`, + `No node found for name ${chalk.cyan(node)}. Interpreting as VM ID...`, ); vmId = node; } @@ -204,9 +201,7 @@ Examples: const flushIncompleteLine = () => { if (incompleteLine.length > 0) { - console.log( - `[${lastTimestamp}] ${incompleteLine}`, - ); + console.log(`[${lastTimestamp}] ${incompleteLine}`); } }; @@ -248,8 +243,8 @@ Examples: if (newResponse?.data?.length) { processLogs(newResponse.data); - sinceSeqnum = newResponse.data[newResponse.data.length - 1].seqnum + - 1; + sinceSeqnum = + newResponse.data[newResponse.data.length - 1].seqnum + 1; } await new Promise((resolve) => setTimeout(resolve, 2000)); diff --git a/src/lib/nodes/redeploy.ts b/src/lib/nodes/redeploy.ts index 11c9e698..52592a64 100644 --- a/src/lib/nodes/redeploy.ts +++ b/src/lib/nodes/redeploy.ts @@ -1,25 +1,23 @@ -import { Command, CommanderError, Option } from "@commander-js/extra-typings"; -import { confirm } from "@inquirer/prompts"; -import { readFileSync } from "node:fs"; -import { brightRed, gray, red } from "jsr:@std/fmt/colors"; import console from "node:console"; +import { readFileSync } from "node:fs"; import process from "node:process"; -import ora from "ora"; +import { Command, CommanderError, Option } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import ora from "ora"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import { isFeatureEnabled } from "../posthog.ts"; import { createNodesTable, jsonOption, pluralizeNodes, yesOption, } from "./utils.ts"; -import { isFeatureEnabled } from "../posthog.ts"; const redeploy = new Command("redeploy") - .description( - "Redeploy nodes by replacing their current VMs with new ones", - ) + .description("Redeploy nodes by replacing their current VMs with new ones") .showHelpAfterError() .argument("", "Node IDs or names to redeploy") .addOption( @@ -92,8 +90,8 @@ async function redeployNodeAction( const notFound: string[] = []; for (const nameOrId of nodeNames) { - const node = fetchedNodes.find((n) => - n.name === nameOrId || n.id === nameOrId + const node = fetchedNodes.find( + (n) => n.name === nameOrId || n.id === nameOrId, ); if (node) { foundNodes.push({ name: nameOrId, node }); @@ -104,10 +102,10 @@ async function redeployNodeAction( if (notFound.length > 0) { console.log( - red( - `Could not find ${notFound.length === 1 ? "this" : "these"} ${ - pluralizeNodes(notFound.length) - }:`, + chalk.red( + `Could not find ${notFound.length === 1 ? "this" : "these"} ${pluralizeNodes( + notFound.length, + )}:`, ), ); for (const name of notFound) { @@ -120,16 +118,16 @@ async function redeployNodeAction( const redeployableStatuses = [ "running" as const, ] as SFCNodes.Node["status"][]; - const nonRedeployableNodes = foundNodes.filter(({ node }) => - !redeployableStatuses.includes(node.status) + const nonRedeployableNodes = foundNodes.filter( + ({ node }) => !redeployableStatuses.includes(node.status), ); const redeployableNodes = foundNodes.filter(({ node }) => - redeployableStatuses.includes(node.status) + redeployableStatuses.includes(node.status), ); if (nonRedeployableNodes.length > 0) { console.log( - red( + chalk.red( `Cannot redeploy ${ nonRedeployableNodes.length === 1 ? "this" : "these" } ${pluralizeNodes(nonRedeployableNodes.length)} (not running):`, @@ -138,11 +136,7 @@ async function redeployNodeAction( for (const { name } of nonRedeployableNodes) { console.log(` • ${name}`); } - console.log( - brightRed( - `\nOnly running nodes can be redeployed.\n`, - ), - ); + console.log(chalk.redBright(`\nOnly running nodes can be redeployed.\n`)); } if (redeployableNodes.length === 0) { @@ -154,21 +148,21 @@ async function redeployNodeAction( const wellFormedUserData = rawUserData?.isWellFormed?.() ? rawUserData : rawUserData - ? encodeURIComponent(rawUserData) - : undefined; + ? encodeURIComponent(rawUserData) + : undefined; const encodedUserData = wellFormedUserData ? btoa( - String.fromCodePoint(...new TextEncoder().encode(wellFormedUserData)), - ) + String.fromCodePoint(...new TextEncoder().encode(wellFormedUserData)), + ) : undefined; // Show nodes table and get confirmation for destructive action if (!options.yes) { if (redeployableNodes.length > 0) { console.log( - `The following ${ - pluralizeNodes(redeployableNodes.length) - } will be redeployed:`, + `The following ${pluralizeNodes( + redeployableNodes.length, + )} will be redeployed:`, ); console.log(createNodesTable(redeployableNodes.map((n) => n.node))); } @@ -206,9 +200,9 @@ async function redeployNodeAction( ); const confirmed = await confirm({ - message: `Redeploy ${redeployableNodes.length} ${ - pluralizeNodes(redeployableNodes.length) - }? This action cannot be undone.`, + message: `Redeploy ${redeployableNodes.length} ${pluralizeNodes( + redeployableNodes.length, + )}? This action cannot be undone.`, default: false, }); if (!confirmed) { @@ -218,17 +212,18 @@ async function redeployNodeAction( } const spinner = ora( - `Redeploying ${redeployableNodes.length} ${ - pluralizeNodes(redeployableNodes.length) - }...`, + `Redeploying ${redeployableNodes.length} ${pluralizeNodes( + redeployableNodes.length, + )}...`, ).start(); const results: { name: string; node: SFCNodes.Node }[] = []; const errors: { name: string; error: string }[] = []; - for ( - const { name: nodeIdOrName, node: originalNode } of redeployableNodes - ) { + for (const { + name: nodeIdOrName, + node: originalNode, + } of redeployableNodes) { try { const redeployedNode = await client.nodes.redeploy(originalNode.id, { image_id: options.image, @@ -244,15 +239,21 @@ async function redeployNodeAction( } if (options.json) { - console.log(JSON.stringify(results.map((r) => r.node), null, 2)); + console.log( + JSON.stringify( + results.map((r) => r.node), + null, + 2, + ), + ); process.exit(0); } if (results.length > 0) { spinner.succeed( - `Successfully redeployed ${results.length} ${ - pluralizeNodes(results.length) - }`, + `Successfully redeployed ${results.length} ${pluralizeNodes( + results.length, + )}`, ); } @@ -261,23 +262,23 @@ async function redeployNodeAction( spinner.fail("Failed to redeploy any nodes"); } else { spinner.warn( - `Redeployed ${results.length} ${ - pluralizeNodes(results.length) - }, but ${errors.length} failed`, + `Redeployed ${results.length} ${pluralizeNodes( + results.length, + )}, but ${errors.length} failed`, ); } } if (results.length > 0) { - console.log(gray("\nRedeployed nodes:")); + console.log(chalk.gray("\nRedeployed nodes:")); console.log(createNodesTable(results.map((r) => r.node))); console.log( - gray("\nNew VMs are being provisioned on these nodes."), + chalk.gray("\nNew VMs are being provisioned on these nodes."), ); } if (errors.length > 0) { - console.log(gray("\nFailed to redeploy:")); + console.log(chalk.gray("\nFailed to redeploy:")); for (const error of errors) { console.log(` • ${error.name}: ${error.error}`); } diff --git a/src/lib/nodes/release.ts b/src/lib/nodes/release.ts index 1695efc9..35fd9834 100644 --- a/src/lib/nodes/release.ts +++ b/src/lib/nodes/release.ts @@ -1,10 +1,10 @@ -import { Command } from "@commander-js/extra-typings"; -import { confirm } from "@inquirer/prompts"; -import { brightRed, gray, red } from "jsr:@std/fmt/colors"; import console from "node:console"; import process from "node:process"; -import ora from "ora"; +import { Command } from "@commander-js/extra-typings"; +import { confirm } from "@inquirer/prompts"; import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import ora from "ora"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { @@ -16,9 +16,7 @@ import { } from "./utils.ts"; const release = new Command("release") - .description( - "Release compute nodes (stop a node from auto-renewing)", - ) + .description("Release compute nodes (stop a node from auto-renewing)") .showHelpAfterError() .argument("", "Node IDs or names to release") .addOption(yesOption) @@ -71,8 +69,8 @@ async function releaseNodesAction( const notFound: string[] = []; for (const nameOrId of nodeNames) { - const node = fetchedNodes.find((n) => - n.name === nameOrId || n.id === nameOrId + const node = fetchedNodes.find( + (n) => n.name === nameOrId || n.id === nameOrId, ); if (node) { foundNodes.push({ name: nameOrId, node }); @@ -83,10 +81,10 @@ async function releaseNodesAction( if (notFound.length > 0) { console.log( - red( - `Could not find ${notFound.length === 1 ? "this" : "these"} ${ - pluralizeNodes(notFound.length) - }:`, + chalk.red( + `Could not find ${notFound.length === 1 ? "this" : "these"} ${pluralizeNodes( + notFound.length, + )}:`, ), ); for (const name of notFound) { @@ -101,16 +99,16 @@ async function releaseNodesAction( "pending", "awaitingcapacity", ] as SFCNodes.Node["status"][]; - const nonReleasableNodes = foundNodes.filter(({ node }) => - !releasableStatuses.includes(node.status) + const nonReleasableNodes = foundNodes.filter( + ({ node }) => !releasableStatuses.includes(node.status), ); const releasableNodes = foundNodes.filter(({ node }) => - releasableStatuses.includes(node.status) + releasableStatuses.includes(node.status), ); if (nonReleasableNodes.length > 0) { console.log( - red( + chalk.red( `Cannot release ${ nonReleasableNodes.length === 1 ? "this" : "these" } ${pluralizeNodes(nonReleasableNodes.length)}:`, @@ -120,7 +118,7 @@ async function releaseNodesAction( console.log(` • ${name} (${printNodeStatus(node.status)})`); } console.log( - brightRed( + chalk.redBright( `\nOnly nodes with status 'Running', 'Pending', or 'Awaiting Capacity' can be released.\n`, ), ); @@ -134,11 +132,7 @@ async function releaseNodesAction( if (options.dryRun) { if (options.json) { - console.log(JSON.stringify( - nodesToRelease, - null, - 2, - )); + console.log(JSON.stringify(nodesToRelease, null, 2)); process.exit(0); } @@ -147,10 +141,10 @@ async function releaseNodesAction( if (nodesToRelease.length > 0) { console.log(createNodesTable(nodesToRelease)); console.log( - gray( - `\nWould release ${nodesToRelease.length} ${ - pluralizeNodes(nodesToRelease.length) - }.`, + chalk.gray( + `\nWould release ${nodesToRelease.length} ${pluralizeNodes( + nodesToRelease.length, + )}.`, ), ); } @@ -166,9 +160,9 @@ async function releaseNodesAction( if (!options.yes) { if (nodesToRelease.length > 0) { console.log( - `The following ${ - pluralizeNodes(nodesToRelease.length) - } will be released:`, + `The following ${pluralizeNodes( + nodesToRelease.length, + )} will be released:`, ); console.log(createNodesTable(nodesToRelease)); } @@ -184,9 +178,9 @@ async function releaseNodesAction( } const confirmed = await confirm({ - message: `Release ${nodesToRelease.length} ${ - pluralizeNodes(nodesToRelease.length) - }? This action cannot be undone.`, + message: `Release ${nodesToRelease.length} ${pluralizeNodes( + nodesToRelease.length, + )}? This action cannot be undone.`, default: false, }); if (!confirmed) { @@ -196,11 +190,10 @@ async function releaseNodesAction( } const releaseSpinner = ora( - `Releasing ${nodesToRelease.length} ${ - pluralizeNodes(nodesToRelease.length) - }...`, - ) - .start(); + `Releasing ${nodesToRelease.length} ${pluralizeNodes( + nodesToRelease.length, + )}...`, + ).start(); const results: { name: string; status: string }[] = []; const errors: { name: string; error: string }[] = []; @@ -227,23 +220,19 @@ async function releaseNodesAction( releaseSpinner.fail("Failed to release any nodes"); } else { releaseSpinner.warn( - `Released ${results.length} ${ - pluralizeNodes(results.length) - }, but ${errors.length} failed`, + `Released ${results.length} ${pluralizeNodes( + results.length, + )}, but ${errors.length} failed`, ); } - console.error(gray("\nFailed to release:")); + console.error(chalk.gray("\nFailed to release:")); for (const error of errors) { console.error(` • ${error.name}: ${error.error}`); } } if (options.json) { - console.log(JSON.stringify( - results, - null, - 2, - )); + console.log(JSON.stringify(results, null, 2)); process.exit(0); } diff --git a/src/lib/nodes/set.ts b/src/lib/nodes/set.ts index 38a5671e..988ab051 100644 --- a/src/lib/nodes/set.ts +++ b/src/lib/nodes/set.ts @@ -1,12 +1,12 @@ +import console from "node:console"; import { Command, CommanderError } from "@commander-js/extra-typings"; +import chalk from "chalk"; import ora from "ora"; -import { gray } from "jsr:@std/fmt/colors"; -import console from "node:console"; import { handleNodesError, nodesClient } from "../../nodesClient.ts"; -import { maxPriceOption, pluralizeNodes } from "./utils.ts"; -import { updateProcurement } from "../scale/update.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; +import { updateProcurement } from "../scale/update.tsx"; +import { maxPriceOption, pluralizeNodes } from "./utils.ts"; async function setNodesAction( names: string[], @@ -38,11 +38,11 @@ async function setNodesAction( } // Filter nodes that have procurement_id (auto reserved nodes) - const nodesWithProcurement = fetchedNodes.filter((node) => - node.procurement_id + const nodesWithProcurement = fetchedNodes.filter( + (node) => node.procurement_id, ); - const nodesWithoutProcurement = fetchedNodes.filter((node) => - !node.procurement_id + const nodesWithoutProcurement = fetchedNodes.filter( + (node) => !node.procurement_id, ); if (nodesWithProcurement.length === 0) { @@ -54,9 +54,11 @@ async function setNodesAction( ); } - const results: Array< - { name: string; maxPrice: number; procurementId: string } - > = []; + const results: Array<{ + name: string; + maxPrice: number; + procurementId: string; + }> = []; const errors: Array<{ name: string; error: string }> = []; const priceInCents = Math.round(options.maxPrice * 100); @@ -80,15 +82,15 @@ async function setNodesAction( // Conclude spinner based on results if (results.length > 0 && errors.length === 0) { spinner.succeed( - `Successfully updated ${results.length} ${ - pluralizeNodes(results.length) - }`, + `Successfully updated ${results.length} ${pluralizeNodes( + results.length, + )}`, ); } else if (results.length > 0 && errors.length > 0) { spinner.warn( - `Updated ${results.length} ${ - pluralizeNodes(results.length) - }, but ${errors.length} failed`, + `Updated ${results.length} ${pluralizeNodes( + results.length, + )}, but ${errors.length} failed`, ); } else { spinner.fail("Failed to update any nodes"); @@ -96,32 +98,32 @@ async function setNodesAction( // Show outcome lists if (results.length > 0) { - console.log(gray("\nUpdated nodes:")); + console.log(chalk.gray("\nUpdated nodes:")); for (const result of results) { console.log( - ` • ${result.name}: Max price set to $${ - result.maxPrice.toFixed(2) - }/GPU/hr`, + ` • ${result.name}: Max price set to $${result.maxPrice.toFixed( + 2, + )}/GPU/hr`, ); } } if (errors.length > 0) { - console.log(gray("\nFailed to update:")); + console.log(chalk.gray("\nFailed to update:")); for (const error of errors) { console.log(` • ${error.name}: ${error.error}`); } } if (nodesWithoutProcurement.length > 0) { - console.log(gray("\nReserved nodes (cannot update pricing):")); + console.log(chalk.gray("\nReserved nodes (cannot update pricing):")); for (const node of nodesWithoutProcurement) { console.log(` • ${node.name} (${node.node_type})`); } } if (notFound.length > 0) { - console.log(gray("\nNodes not found:")); + console.log(chalk.gray("\nNodes not found:")); for (const name of notFound) { console.log(` • ${name}`); } diff --git a/src/lib/nodes/ssh.ts b/src/lib/nodes/ssh.ts index c781ee45..db065f1b 100644 --- a/src/lib/nodes/ssh.ts +++ b/src/lib/nodes/ssh.ts @@ -1,19 +1,19 @@ -import { Command } from "@commander-js/extra-typings"; +import child_process from "node:child_process"; import console from "node:console"; import process from "node:process"; -import { Shescape } from "shescape"; -import child_process from "node:child_process"; +import { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; import ora from "ora"; -import { cyan } from "jsr:@std/fmt/colors"; +import { Shescape } from "shescape"; -import { jsonOption } from "./utils.ts"; -import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; import { getApiUrl } from "../../helpers/urls.ts"; -import { getAuthToken } from "../../helpers/config.ts"; +import { handleNodesError, nodesClient } from "../../nodesClient.ts"; +import { jsonOption } from "./utils.ts"; const ssh = new Command("ssh") .description(`SSH into a VM on a node. @@ -76,13 +76,13 @@ Examples: try { const node = await client.nodes.get(nodeOrVmId); - spinner.succeed(`Node found for name ${cyan(nodeOrVmId)}.`); + spinner.succeed(`Node found for name ${chalk.cyan(nodeOrVmId)}.`); if (!node?.current_vm) { spinner.fail( - `Node ${ - cyan(nodeOrVmId) - } does not have a current VM. VMs can take up to 5-10 minutes to spin up.`, + `Node ${chalk.cyan( + nodeOrVmId, + )} does not have a current VM. VMs can take up to 5-10 minutes to spin up.`, ); process.exit(1); } @@ -90,9 +90,9 @@ Examples: vmId = node.current_vm.id; } catch { spinner.info( - `No node found for name ${ - cyan(nodeOrVmId) - }. Interpreting as VM ID...`, + `No node found for name ${chalk.cyan( + nodeOrVmId, + )}. Interpreting as VM ID...`, ); vmId = nodeOrVmId; } @@ -119,9 +119,9 @@ Examples: } sshSpinner.fail( - `Failed to retrieve SSH information for ${ - cyan(vmId) - }: ${response.statusText}`, + `Failed to retrieve SSH information for ${chalk.cyan( + vmId, + )}: ${response.statusText}`, ); process.exit(1); } @@ -129,10 +129,12 @@ Examples: const data = (await response.json()) as { ssh_hostname: string; ssh_port: number; - ssh_host_keys: { - key_type: string; - base64_encoded_key: string; - }[] | undefined; + ssh_host_keys: + | { + key_type: string; + base64_encoded_key: string; + }[] + | undefined; }; sshSpinner.succeed("SSH information fetched successfully."); @@ -203,7 +205,7 @@ Examples: console.log(`Executing (${shell} style output): ${shell_cmd}`); } - // Ideally this would use `@alphahydrae/exec` but `deno compile` doesn't + // Ideally this would use `@alphahydrae/exec` but `pkg` doesn't // support ffi modules. const result = child_process.spawnSync(cmd[0], cmd.slice(1), { stdio: "inherit", diff --git a/src/lib/nodes/utils.ts b/src/lib/nodes/utils.ts index b4d80139..16f83ab3 100644 --- a/src/lib/nodes/utils.ts +++ b/src/lib/nodes/utils.ts @@ -1,20 +1,13 @@ -import { - brightBlack, - cyan, - gray, - green, - red, - yellow, -} from "jsr:@std/fmt/colors"; +import { CommanderError, Option } from "@commander-js/extra-typings"; import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; +import chalk from "chalk"; +import { parseDate } from "chrono-node"; import Table from "cli-table3"; import dayjs from "dayjs"; -import { CommanderError, Option } from "@commander-js/extra-typings"; -import { parseDate } from "chrono-node"; import { parseDurationArgument } from "../../helpers/duration.ts"; -import { parseStartDateOrNow } from "../../helpers/units.ts"; import { logAndQuit } from "../../helpers/errors.ts"; import { formatNullableDateRange } from "../../helpers/format-date.ts"; +import { parseStartDateOrNow } from "../../helpers/units.ts"; export function printNodeStatus(status: SFCNodes.Node["status"]): string { switch (status) { @@ -32,16 +25,16 @@ export function getStatusColor(status: SFCNodes.Node["status"]): string { switch (status) { case "pending": case "awaitingcapacity": - return yellow(statusText); + return chalk.yellow(statusText); case "running": - return green(statusText); + return chalk.green(statusText); case "released": - return cyan(statusText); + return chalk.cyan(statusText); case "failed": case "terminated": - return red(statusText); + return chalk.red(statusText); case "deleted": - return gray(statusText); + return chalk.gray(statusText); case "unknown": default: return statusText; @@ -63,15 +56,15 @@ export function getVMStatusColor(status: string): string { switch (status) { case "Pending": - return yellow(statusText); + return chalk.yellow(statusText); case "Running": - return green(statusText); + return chalk.green(statusText); case "Destroyed": - return brightBlack(statusText); + return chalk.blackBright(statusText); case "NodeFailure": - return red(statusText); + return chalk.red(statusText); case "Unspecified": - return gray(statusText); + return chalk.gray(statusText); default: return statusText; } @@ -89,10 +82,14 @@ export function printNodeType(nodeType: SFCNodes.Node["node_type"]) { } export function getLastVM(node: SFCNodes.Node) { - return node.current_vm ?? - node.vms?.data?.sort((a, b) => - (b.start_at ?? b.updated_at) - (a.start_at ?? a.updated_at) - ).at(0); + return ( + node.current_vm ?? + node.vms?.data + ?.sort( + (a, b) => (b.start_at ?? b.updated_at) - (a.start_at ?? a.updated_at), + ) + .at(0) + ); } export const DEFAULT_NODE_LS_LIMIT = 12 as const; @@ -109,14 +106,14 @@ export function createNodesTable( ): string { const table = new Table({ head: [ - cyan("NAME"), - cyan("TYPE"), - cyan("STATUS"), - cyan("CURRENT VM"), - cyan("GPU"), - cyan("ZONE"), - cyan("START/END"), - cyan("MAX PRICE"), + chalk.cyan("NAME"), + chalk.cyan("TYPE"), + chalk.cyan("STATUS"), + chalk.cyan("CURRENT VM"), + chalk.cyan("GPU"), + chalk.cyan("ZONE"), + chalk.cyan("START/END"), + chalk.cyan("MAX PRICE"), ], style: { head: [], @@ -145,11 +142,11 @@ export function createNodesTable( lastVm?.id ?? "", node.gpu_type, node.zone || - (node.node_type === "autoreserved" - ? (lastVm?.zone - ? `Any matching (${brightBlack(lastVm.zone)})` - : "Any matching") - : "N/A"), + (node.node_type === "autoreserved" + ? lastVm?.zone + ? `Any matching (${chalk.blackBright(lastVm.zone)})` + : "Any matching" + : "N/A"), startEnd, maxPrice, ]); @@ -159,10 +156,10 @@ export function createNodesTable( table.push([ { colSpan: 8, - content: brightBlack( - `${nodes.length - limit} older ${ - pluralizeNodes(nodes.length - limit) - } not shown. Use sf nodes list --limit ${nodes.length} or sf nodes list --json to list all nodes.`, + content: chalk.blackBright( + `${nodes.length - limit} older ${pluralizeNodes( + nodes.length - limit, + )} not shown. Use sf nodes list --limit ${nodes.length} or sf nodes list --json to list all nodes.`, ), }, ]); @@ -172,7 +169,7 @@ export function createNodesTable( } export function pluralizeNodes(count: number) { - return count === 1 ? "node" as const : "nodes" as const; + return count === 1 ? ("node" as const) : ("nodes" as const); } /** @@ -183,7 +180,7 @@ export function pluralizeNodes(count: number) { * @throws CommanderError if invalid */ export function validatePrice(val: string, minimum = 0): number { - const parsed = parseFloat(val); + const parsed = Number.parseFloat(val); if (isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, @@ -209,7 +206,7 @@ export function validatePrice(val: string, minimum = 0): number { * @throws CommanderError if invalid */ export function validateDuration(val: string, minimum = 3600): number { - const parsed = parseInt(val, 10); + const parsed = Number.parseInt(val, 10); if (isNaN(parsed)) { throw new CommanderError( 1, @@ -221,9 +218,9 @@ export function validateDuration(val: string, minimum = 3600): number { throw new CommanderError( 1, "INVALID_DURATION", - `Duration must be at least ${minimum} seconds (${ - Math.round(minimum / 3600) - } hour${minimum === 3600 ? "" : "s"})`, + `Duration must be at least ${minimum} seconds (${Math.round( + minimum / 3600, + )} hour${minimum === 3600 ? "" : "s"})`, ); } return parsed; @@ -277,10 +274,7 @@ export const jsonOption = new Option("-j, --json", "Output in JSON format"); /** * Common --yes option to skip confirmation prompts */ -export const yesOption = new Option( - "-y, --yes", - "Skip confirmation prompt", -); +export const yesOption = new Option("-y, --yes", "Skip confirmation prompt"); /** * Common --max-price option for nodes commands @@ -288,7 +282,9 @@ export const yesOption = new Option( export const maxPriceOption = new Option( "-p, --max-price ", "[Required] Maximum price per node hour in dollars", -).argParser(validatePrice).makeOptionMandatory(); +) + .argParser(validatePrice) + .makeOptionMandatory(); /** * Common --start option using same parser as buy command @@ -296,7 +292,9 @@ export const maxPriceOption = new Option( export const startOrNowOption = new Option( "-s, --start ", "Start time (ISO 8601 format:'2022-10-27T14:30:00Z' or relative time like '+1d', or 'NOW')", -).argParser(parseStartDateOrNow).default("NOW" as const); +) + .argParser(parseStartDateOrNow) + .default("NOW" as const); /** * Common --end option using same parser as buy command @@ -320,4 +318,6 @@ export const durationOption = new Option( export const requiredDurationOption = new Option( "-d, --duration ", "[Required] Duration (e.g., '1h', '30m', '2d', 3600) - rounded up to the nearest hour", -).argParser(parseDurationArgument).makeOptionMandatory(); +) + .argParser(parseDurationArgument) + .makeOptionMandatory(); diff --git a/src/lib/scale/create.tsx b/src/lib/scale/create.tsx index 8ac20882..79147288 100644 --- a/src/lib/scale/create.tsx +++ b/src/lib/scale/create.tsx @@ -1,36 +1,37 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react"; -import { Box, render, Text, useApp } from "ink"; -import Spinner from "ink-spinner"; -import { Command, Option } from "@commander-js/extra-typings"; -import process from "node:process"; import console from "node:console"; +import process from "node:process"; import { setTimeout } from "node:timers"; -import boxen from "npm:boxen@8.0.1"; +import { Command, Option } from "@commander-js/extra-typings"; +import boxen from "boxen"; import dayjs from "dayjs"; +import { Box, Text, render, useApp } from "ink"; +import Spinner from "ink-spinner"; +import type React from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { pluralizeNodes } from "../nodes/utils.ts"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; +import type { components } from "../../schema.ts"; import ConfirmInput from "../ConfirmInput.tsx"; -import { GPUS_PER_NODE } from "../constants.ts"; import { getQuote } from "../buy/index.tsx"; -import { components } from "../../schema.ts"; +import { GPUS_PER_NODE } from "../constants.ts"; +import { roundDateUpToNextMinute } from "../../helpers/units.ts"; +import ConfirmationMessage from "./ConfirmationMessage.tsx"; +import ProcurementDisplay from "./ProcurementDisplay.tsx"; import { - acceleratorsToNodes, type ColocationStrategyName, DEFAULT_LIMIT_PRICE_MULTIPLIER, DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS, MIN_CONTRACT_MINUTES, + type Procurement, + acceleratorsToNodes, parseAccelerators, parseHorizonArg, parsePriceArg, - type Procurement, } from "./utils.ts"; -import { roundDateUpToNextMinute } from "../../helpers/units.ts"; -import ProcurementDisplay from "./ProcurementDisplay.tsx"; -import ConfirmationMessage from "./ConfirmationMessage.tsx"; type ZoneInfo = components["schemas"]["node-api_ZoneInfo"]; @@ -56,9 +57,9 @@ function ScaleWarning(props: CreateProcurementCommandProps) { const warningMessage = boxen( `\x1b[31mWe're deprecating \x1b[97msf scale\x1b[31m.\x1b[0m \x1b[31mCreate auto reserved nodes using \x1b[97msf nodes\x1b[31m instead:\x1b[0m \x1b[97m${equivalentCommand}\x1b[0m -\x1b[31mThe above command creates ${nodesRequired} self-extending ${ - pluralizeNodes(nodesRequired) - } that ${ +\x1b[31mThe above command creates ${nodesRequired} self-extending ${pluralizeNodes( + nodesRequired, + )} that ${ nodesRequired === 1 ? "maintains" : "maintain" } capacity automatically.\x1b[0m`, { @@ -134,16 +135,13 @@ type CreateProcurementCommandProps = ReturnType & { function CreateProcurementCommand(props: CreateProcurementCommandProps) { const { exit } = useApp(); const clusterName = props.zone || props.cluster; - const isVM = props.type?.endsWith("v") || - props.zoneInfo?.delivery_type === "VM"; + const isVM = + props.type?.endsWith("v") || props.zoneInfo?.delivery_type === "VM"; const [scaleWarningState, setScaleWarningState] = useState< "prompt" | "accepted" | "dismissed" | "not_applicable" - >( - isVM ? (props.yes ? "accepted" : "prompt") : "not_applicable", - ); - const [confirmationMessage, setConfirmationMessage] = useState< - React.ReactNode - >(); + >(isVM ? (props.yes ? "accepted" : "prompt") : "not_applicable"); + const [confirmationMessage, setConfirmationMessage] = + useState(); const nodesRequired = useMemo( () => acceleratorsToNodes(props.accelerators), @@ -192,9 +190,10 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { // from the market's perspective, "NOW" means at the beginning of the next minute. // when the order duration is very short, this can cause the rate to be computed incorrectly // if we implicitly assume it to mean `new Date()`. - const coercedStartTime = quote.start_at === "NOW" - ? roundDateUpToNextMinute(new Date()) - : new Date(quote.start_at); + const coercedStartTime = + quote.start_at === "NOW" + ? roundDateUpToNextMinute(new Date()) + : new Date(quote.start_at); const durationSeconds = dayjs(quote.end_at).diff( dayjs(coercedStartTime), ); @@ -243,12 +242,8 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { })(); }, []); - const { - isLoading, - error, - result, - createProcurement, - } = useCreateProcurement(); + const { isLoading, error, result, createProcurement } = + useCreateProcurement(); useEffect(() => { if (error) { @@ -258,13 +253,16 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { } }, [error, result, exit]); - const handleDismissScaleWarning = useCallback((submitValue: boolean) => { - if (!submitValue) { - exit(); - } else { - setScaleWarningState("accepted"); - } - }, [exit]); + const handleDismissScaleWarning = useCallback( + (submitValue: boolean) => { + if (!submitValue) { + exit(); + } else { + setScaleWarningState("accepted"); + } + }, + [exit], + ); const handleSubmit = (submitValue: boolean) => { if (!submitValue) { @@ -328,15 +326,9 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { - Create a procurement anyway?{" "} - - (y/N) - + Create a procurement anyway? (y/N) - + ); } @@ -349,10 +341,7 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { {confirmationMessage} Create procurement? (y/N) - + ); @@ -360,17 +349,16 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { // Show confirmation for non-VM procurements (h100i, etc.) if ( - confirmationMessage && !props.yes && scaleWarningState === "not_applicable" + confirmationMessage && + !props.yes && + scaleWarningState === "not_applicable" ) { return ( {confirmationMessage} Create procurement? (y/N) - + ); @@ -414,25 +402,22 @@ $ sf scale create -n 8 --horizon '30m' .addOption( new Option( "-z, --zone ", - "Only buy on the specified zone. If provided, \`-t\`/`--type` will be ignored.", + "Only buy on the specified zone. If provided, `-t`/`--type` will be ignored.", ).implies({ colocationStrategy: "pinned" as const }), ) .addOption( new Option( "-c, --cluster ", - "Only buy on the specified cluster (deprecated, alias for --zone). If provided, \`-t\`/`--type` will be ignored.", + "Only buy on the specified cluster (deprecated, alias for --zone). If provided, `-t`/`--type` will be ignored.", ).implies({ colocationStrategy: "pinned" as const }), ) .addOption( new Option( "-s, --colocation-strategy ", `Colocation strategy to use for the procurement. Can be one of \`anywhere\`, \`colocate\`, \`colocate-pinned\`, or \`pinned\`. See https://docs.sfcompute.com/docs/on-demand-and-spot#colocation-behavior for more information.`, - ).choices([ - "anywhere", - "colocate", - "colocate-pinned", - "pinned", - ]).default("colocate-pinned"), + ) + .choices(["anywhere", "colocate", "colocate-pinned", "pinned"]) + .default("colocate-pinned"), ) .option( "-d, --horizon ", @@ -442,9 +427,9 @@ $ sf scale create -n 8 --horizon '30m' ) .option( "-p, --price ", - `Limit price per GPU per hour, in dollars. Buy compute only if it's at most this price. Defaults to the current market price times 1.5, or ${ - (DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS / 100).toFixed(2) - } if we can't get a price estimate.`, + `Limit price per GPU per hour, in dollars. Buy compute only if it's at most this price. Defaults to the current market price times 1.5, or ${( + DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS / 100 + ).toFixed(2)} if we can't get a price estimate.`, parsePriceArg, ) .option("-y, --yes", "Automatically confirm the command.") @@ -483,10 +468,7 @@ $ sf scale create -n 8 --horizon '30m' } const { waitUntilExit } = render( - , + , ); await waitUntilExit(); }); diff --git a/src/lib/scale/update.tsx b/src/lib/scale/update.tsx index f3c5a70a..b855bd4d 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -1,33 +1,33 @@ -import React, { +import { setTimeout } from "node:timers"; +import { Command } from "@commander-js/extra-typings"; +import { Box, Text, render, useApp } from "ink"; +import Spinner from "ink-spinner"; +import { type ReactNode, useCallback, useEffect, useMemo, useState, } from "react"; -import { Box, render, Text, useApp } from "ink"; -import Spinner from "ink-spinner"; -import { Command } from "@commander-js/extra-typings"; -import { setTimeout } from "node:timers"; +import console from "node:console"; +import chalk from "chalk"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; import ConfirmInput from "../ConfirmInput.tsx"; +import ConfirmationMessage from "./ConfirmationMessage.tsx"; import ProcurementDisplay, { ProcurementHeader, } from "./ProcurementDisplay.tsx"; -import ConfirmationMessage from "./ConfirmationMessage.tsx"; import { + type Procurement, acceleratorsToNodes, getProcurement, parseAccelerators, parseHorizonArg, parseIds, parsePriceArg, - type Procurement, } from "./utils.ts"; -import console from "node:console"; -import { yellow } from "jsr:@std/fmt/colors"; export async function updateProcurement({ procurementId, @@ -65,43 +65,46 @@ export async function updateProcurement({ function useUpdateProcurements() { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); - const [results, setResults] = useState< - PromiseSettledResult[] - >(); + const [results, setResults] = + useState[]>(); - const updateProcurements = useCallback(async (ids: string[], params: { - horizonMinutes?: number; - nodesRequired?: number; - pricePerGpuHourInCents?: number; - }) => { - try { - setIsLoading(true); - setError(undefined); + const updateProcurements = useCallback( + async ( + ids: string[], + params: { + horizonMinutes?: number; + nodesRequired?: number; + pricePerGpuHourInCents?: number; + }, + ) => { + try { + setIsLoading(true); + setError(undefined); - const updatePromises = ids.map((id) => - updateProcurement({ - procurementId: id, - ...params, - }) - ); + const updatePromises = ids.map((id) => + updateProcurement({ + procurementId: id, + ...params, + }), + ); - const results = await Promise.allSettled( - updatePromises, - ); - setResults(results); + const results = await Promise.allSettled(updatePromises); + setResults(results); - const failures = results.filter((r) => r.status === "rejected"); - if (failures.length > 0) { - setError(`Failed to update ${failures.length} procurement(s)`); + const failures = results.filter((r) => r.status === "rejected"); + if (failures.length > 0) { + setError(`Failed to update ${failures.length} procurement(s)`); + } + } catch (err: unknown) { + setError( + err instanceof Error ? err.message : "An unknown error occurred", + ); + } finally { + setIsLoading(false); } - } catch (err: unknown) { - setError( - err instanceof Error ? err.message : "An unknown error occurred", - ); - } finally { - setIsLoading(false); - } - }, []); + }, + [], + ); return { isLoading, @@ -117,13 +120,13 @@ type UpdateProcurementCommandProps = ReturnType & { function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { const { exit } = useApp(); - const [procurements, setProcurements] = useState< - PromiseSettledResult[] - >(); + const [procurements, setProcurements] = + useState[]>(); const { successfulProcurements } = useMemo(() => { const successfulProcurements = procurements - ?.filter?.((p): p is PromiseFulfilledResult => - p.status === "fulfilled" && p.value != null + ?.filter?.( + (p): p is PromiseFulfilledResult => + p.status === "fulfilled" && p.value != null, ) ?.map?.((p) => p.value); return { successfulProcurements }; @@ -134,9 +137,7 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { props.accelerators !== undefined ? acceleratorsToNodes(props.accelerators) : undefined, - [ - props.accelerators, - ], + [props.accelerators], ); const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = @@ -149,22 +150,26 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { ); const successfullyFetched = settledResults - .filter((r): r is PromiseFulfilledResult => - r.status === "fulfilled" && r.value != null + .filter( + (r): r is PromiseFulfilledResult => + r.status === "fulfilled" && r.value != null, ) .map((r) => r.value); const failedToFetch = settledResults .map((r, i) => [r, props.ids[i]] as const) - .filter((r): r is [PromiseRejectedResult, string] => - r[0].status === "rejected" - ).map((r) => - [ - r[0].reason instanceof Error - ? r[0].reason.message - : "Unknown error", - r[1], - ] as const + .filter( + (r): r is [PromiseRejectedResult, string] => + r[0].status === "rejected", + ) + .map( + (r) => + [ + r[0].reason instanceof Error + ? r[0].reason.message + : "Unknown error", + r[1], + ] as const, ); if (successfullyFetched.length === 0) { @@ -198,18 +203,21 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { key={p.id} quote={false} type={p.instance_type} - horizonMinutes={props.horizon === p.horizon - ? undefined - : props.horizon} - pricePerGpuHourInCents={props.price === - p.buy_limit_price_per_gpu_hour - ? undefined - : props.price} - accelerators={props.accelerators !== undefined && - acceleratorsToNodes(props.accelerators) !== - p.desired_quantity - ? props.accelerators - : undefined} + horizonMinutes={ + props.horizon === p.horizon ? undefined : props.horizon + } + pricePerGpuHourInCents={ + props.price === p.buy_limit_price_per_gpu_hour + ? undefined + : props.price + } + accelerators={ + props.accelerators !== undefined && + acceleratorsToNodes(props.accelerators) !== + p.desired_quantity + ? props.accelerators + : undefined + } update /> @@ -248,25 +256,28 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { } }, [results, isLoading, exit]); - const handleSubmit = useCallback((submitValue: boolean) => { - if (!submitValue || !successfulProcurements) { - exit(); - return; - } - updateProcurements( - successfulProcurements.map((p) => p.id), - { - horizonMinutes: props.horizon, - nodesRequired, - pricePerGpuHourInCents: displayedPricePerGpuHourInCents, - }, - ); - }, [ - successfulProcurements, - props.horizon, - nodesRequired, - displayedPricePerGpuHourInCents, - ]); + const handleSubmit = useCallback( + (submitValue: boolean) => { + if (!submitValue || !successfulProcurements) { + exit(); + return; + } + updateProcurements( + successfulProcurements.map((p) => p.id), + { + horizonMinutes: props.horizon, + nodesRequired, + pricePerGpuHourInCents: displayedPricePerGpuHourInCents, + }, + ); + }, + [ + successfulProcurements, + props.horizon, + nodesRequired, + displayedPricePerGpuHourInCents, + ], + ); if (error && !results) { return Error: {error}; @@ -282,16 +293,17 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { } if (results) { - const successfulProcurements = results.filter(( - r, - ): r is PromiseFulfilledResult => - r.status === "fulfilled" && r.value != null - ).map((r) => r.value); - const failedProcurements = results.filter((r): r is PromiseRejectedResult => - r.status === "rejected" - ).map((r) => - r.reason instanceof Error ? r.reason.message : "Unknown error" - ); + const successfulProcurements = results + .filter( + (r): r is PromiseFulfilledResult => + r.status === "fulfilled" && r.value != null, + ) + .map((r) => r.value); + const failedProcurements = results + .filter((r): r is PromiseRejectedResult => r.status === "rejected") + .map((r) => + r.reason instanceof Error ? r.reason.message : "Unknown error", + ); return ( {successfulProcurements && successfulProcurements.length > 0 && ( @@ -382,7 +394,7 @@ $ sf scale update -p 1.50 .action(async (id, options) => { if (Object.keys(options).length === 0) { console.error( - yellow( + chalk.yellow( "No options provided. Please provide at least one option.\n", ), ); @@ -390,10 +402,7 @@ $ sf scale update -p 1.50 return; } const { waitUntilExit } = render( - , + , ); await waitUntilExit(); }); diff --git a/src/lib/tokens.ts b/src/lib/tokens.ts index b0b4e886..140df036 100644 --- a/src/lib/tokens.ts +++ b/src/lib/tokens.ts @@ -1,17 +1,17 @@ +import * as console from "node:console"; +import process from "node:process"; import type { Command } from "@commander-js/extra-typings"; import { confirm, input, select } from "@inquirer/prompts"; -import { cyan, gray, green, magenta, red, white } from "jsr:@std/fmt/colors"; +import chalk from "chalk"; import Table from "cli-table3"; -import * as console from "node:console"; -import process from "node:process"; import ora from "ora"; import { getAuthToken, isLoggedIn } from "../helpers/config.ts"; import { logLoginMessageAndQuit, logSessionTokenExpiredAndQuit, } from "../helpers/errors.ts"; -import { getApiUrl } from "../helpers/urls.ts"; import { formatDate } from "../helpers/format-date.ts"; +import { getApiUrl } from "../helpers/urls.ts"; export const TOKEN_EXPIRATION_SECONDS = { IN_7_DAYS: 7 * 24 * 60 * 60, @@ -94,15 +94,13 @@ async function createTokenAction() { // collect name & description const name = await input({ - message: `Name your token ${gray("(optional, ↵ to skip)")}:`, + message: `Name your token ${chalk.gray("(optional, ↵ to skip)")}:`, default: "", }); const description = await input({ - message: `Description for your token ${ - gray( - "(optional, ↵ to skip)", - ) - }:`, + message: `Description for your token ${chalk.gray( + "(optional, ↵ to skip)", + )}:`, default: "", }); @@ -135,34 +133,33 @@ async function createTokenAction() { } // display token to user - const data = await response.json(); - loadingSpinner.succeed(gray("Access token created 🎉")); - console.log(`${green(data.token)}\n`); + const data = (await response.json()) as TokenObject; + loadingSpinner.succeed(chalk.gray("Access token created 🎉")); + console.log(`${chalk.green(data.token)}\n`); // tell them they will set this in the Authorization header console.log( - `${gray(`Pass this in the 'Authorization' header of API requests:`)}`, + `${chalk.gray(`Pass this in the 'Authorization' header of API requests:`)}`, ); console.log( [ - gray("{ "), - white("Authorization"), - gray(": "), - green('"Bearer '), - magenta(""), - green('"'), - gray(" }"), + chalk.gray("{ "), + chalk.white("Authorization"), + chalk.gray(": "), + chalk.green('"Bearer '), + chalk.magenta(""), + chalk.green('"'), + chalk.gray(" }"), ].join(""), ); console.log("\n"); // give them a sample curl const pingUrl = await getApiUrl("ping"); - console.log(`${gray("Here is a sample curl to get your started:")}`); + console.log(`${chalk.gray("Here is a sample curl to get your started:")}`); console.log( - white( - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. - `curl --request GET --url ${pingUrl} --header 'Authorization: Bearer ${data.token}'`, + chalk.white( + `curl --request GET --url ${pingUrl} --header 'Authorization: Bearer ${data.token as string}'`, ), ); console.log("\n"); @@ -171,10 +168,10 @@ async function createTokenAction() { const table = new Table({ colWidths: [20, 30], }); - table.push(["View All Tokens", magenta("sf tokens list")]); - table.push(["Delete a Token", magenta("sf tokens delete")]); + table.push(["View All Tokens", chalk.magenta("sf tokens list")]); + table.push(["Delete a Token", chalk.magenta("sf tokens delete")]); - console.log(`${gray("And other commands you can try:")}`); + console.log(`${chalk.gray("And other commands you can try:")}`); console.log(table.toString()); process.exit(0); @@ -212,14 +209,13 @@ async function listTokensAction() { loadingSpinner.stop(); // hide spinner // show account tokens - const responseBody = await response.json(); - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. - const tokens = responseBody.data as Array; + const responseBody = (await response.json()) as { data: Array }; + const tokens = responseBody.data; // show empty table if no tokens if (tokens.length === 0) { const table = new Table({ - head: [cyan("Access Tokens")], + head: [chalk.cyan("Access Tokens")], colWidths: [50], }); table.push([ @@ -229,11 +225,9 @@ async function listTokensAction() { // prompt user that they can generate one console.log( - `${gray("Generate your first token with: ")}${ - magenta( - `sf tokens create`, - ) - }`, + `${chalk.gray("Generate your first token with: ")}${chalk.magenta( + `sf tokens create`, + )}`, ); process.exit(0); @@ -241,18 +235,14 @@ async function listTokensAction() { // display table const tokensTable = new Table({ - head: [ - cyan("Token ID"), - cyan("Name"), - cyan("Expires"), - ], + head: [chalk.cyan("Token ID"), chalk.cyan("Name"), chalk.cyan("Expires")], colWidths: [40, 15, 25], }); for (const token of tokens) { tokensTable.push([ - white(token.id), - token.name ? token.name : gray("(empty)"), - white(formatDate(new Date(token.expires_at))), + chalk.white(token.id), + token.name ? token.name : chalk.gray("(empty)"), + chalk.white(formatDate(new Date(token.expires_at))), ]); } console.log(tokensTable.toString()); @@ -274,27 +264,23 @@ async function deleteTokenAction({ if (yes) { await deleteTokenById(id); - console.log(`${green("✓")} Token deleted successfully`); + console.log(`${chalk.green("✓")} Token deleted successfully`); process.exit(0); } const deleteTokenConfirmed = await confirm({ - message: `Are you sure you want to delete this token? ${ - gray( - "(it will stop working immediately.)", - ) - }`, + message: `Are you sure you want to delete this token? ${chalk.gray( + "(it will stop working immediately.)", + )}`, default: false, }); if (!deleteTokenConfirmed) { process.exit(0); } else { const verySureConfirmed = await confirm({ - message: `${red("Very sure?")} ${ - gray( - "(just double-checking)", - ) - }`, + message: `${chalk.red("Very sure?")} ${chalk.gray( + "(just double-checking)", + )}`, default: false, }); @@ -320,8 +306,7 @@ async function deleteTokenById(id: string) { await logSessionTokenExpiredAndQuit(); } - const error = await response.json(); - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. + const error = (await response.json()) as { code?: string }; if (error.code === "token.not_found") { loadingSpinner.fail("Token not found"); process.exit(1); @@ -335,7 +320,7 @@ async function deleteTokenById(id: string) { } loadingSpinner.stop(); - console.log(gray("Token deleted. 🧼")); + console.log(chalk.gray("Token deleted. 🧼")); process.exit(0); } diff --git a/src/lib/vm/index.ts b/src/lib/vm/index.ts index e8ded6b6..e370f8da 100644 --- a/src/lib/vm/index.ts +++ b/src/lib/vm/index.ts @@ -1,25 +1,27 @@ -import { Command } from "@commander-js/extra-typings"; -import { red, yellow } from "jsr:@std/fmt/colors"; -import boxen from "boxen"; import console from "node:console"; +import type { Command } from "@commander-js/extra-typings"; +import boxen from "boxen"; +import chalk from "chalk"; import { nodesClient } from "../../nodesClient.ts"; -import { pluralizeNodes } from "../nodes/utils.ts"; -import { registerSsh } from "./ssh.ts"; import { addImage } from "../nodes/image/index.ts"; +import { pluralizeNodes } from "../nodes/utils.ts"; import list from "./list.ts"; import logs from "./logs.ts"; import replace from "./replace.ts"; import script from "./script.ts"; +import { registerSsh } from "./ssh.ts"; -const DEPRECATION_WARNING = red(boxen( - `\x1b[31mWe're deprecating \x1b[37msf buy\x1b[31m and \x1b[37msf vm\x1b[31m for Virtual Machines.\x1b[31m +const DEPRECATION_WARNING = chalk.red( + boxen( + `\x1b[31mWe're deprecating \x1b[37msf buy\x1b[31m and \x1b[37msf vm\x1b[31m for Virtual Machines.\x1b[31m \x1b[31mWe recommend you create a VM Node instead: \x1b[37msf nodes create --help\x1b[31m \x1b[37msf nodes\x1b[31m allows you to create, extend, and release specific machines directly.\x1b[31m`, - { - padding: 0.75, - borderColor: "red", - }, -)); + { + padding: 0.75, + borderColor: "red", + }, + ), +); async function getVMDeprecationWarning() { const client = await nodesClient(); @@ -27,13 +29,14 @@ async function getVMDeprecationWarning() { const numNodesWithVMs = nodes.data?.filter((node) => node.vms?.data?.length ?? 0 > 0).length ?? 0; if (numNodesWithVMs > 0) { - return yellow( + return chalk.yellow( boxen( `You have \x1b[1m\x1b[33m${numNodesWithVMs} ${ // Capitalize the first letter of the word ((word) => word.charAt(0).toUpperCase() + word.slice(1))( pluralizeNodes(numNodesWithVMs), - )}\x1b[0m\x1b[33m with active or previous VMs. + ) + }\x1b[0m\x1b[33m with active or previous VMs. Managing VM Nodes with deprecated \x1b[37msf vm\x1b[33m commands may cause undefined behavior. Use \x1b[37msf nodes\x1b[33m to create, extend and release specific machines directly.`, { @@ -59,11 +62,7 @@ export async function registerVM(program: Command) { registerSsh(vm); - vm - .addCommand(list) - .addCommand(logs) - .addCommand(replace) - .addCommand(script); + vm.addCommand(list).addCommand(logs).addCommand(replace).addCommand(script); // Add images command if feature flag is enabled await addImage(vm); diff --git a/src/lib/vm/list.ts b/src/lib/vm/list.ts index f0431be0..7553e7c0 100644 --- a/src/lib/vm/list.ts +++ b/src/lib/vm/list.ts @@ -1,17 +1,17 @@ -import { Command } from "@commander-js/extra-typings"; -import Table from "cli-table3"; -import { cyan, gray, green, red, yellow } from "jsr:@std/fmt/colors"; import console from "node:console"; +import { Command } from "@commander-js/extra-typings"; import boxen from "boxen"; +import chalk from "chalk"; +import Table from "cli-table3"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; +import { apiClient } from "../../apiClient.ts"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; -import { apiClient } from "../../apiClient.ts"; dayjs.extend(utc); @@ -66,10 +66,10 @@ const list = new Command("list") const hasRecentlyCreatedVMs = contractsData.some((contract) => dayjs(contract.shape.intervals[0]).isAfter( dayjs().subtract(10, "minutes"), - ) + ), ); - if ((!(vmsData.length > 0) && !hasRecentlyCreatedVMs)) { + if (!(vmsData.length > 0) && !hasRecentlyCreatedVMs) { if (options.json) { console.log(JSON.stringify([], null, 2)); return; @@ -91,13 +91,13 @@ const list = new Command("list") } if (unscheduledVMs > 0 || hasRecentlyCreatedVMs) { - const message = `VMs take 5-10 minutes to spin up and may show as ${ - green("Running") - } before they are ready for ssh. + const message = `VMs take 5-10 minutes to spin up and may show as ${chalk.green( + "Running", + )} before they are ready for ssh. -You can use ${ - cyan("sf vm logs -f") - } to follow your VM's startup script output.`; +You can use ${chalk.cyan( + "sf vm logs -f", + )} to follow your VM's startup script output.`; console.error( boxen(message, { @@ -108,7 +108,11 @@ You can use ${ } const table = new Table({ - head: [cyan("ID"), cyan("Status"), cyan("Last Updated")], + head: [ + chalk.cyan("ID"), + chalk.cyan("Status"), + chalk.cyan("Last Updated"), + ], style: { head: [], border: ["gray"], @@ -119,7 +123,7 @@ You can use ${ table.push([ { colSpan: 3, - content: yellow( + content: chalk.yellow( `${unscheduledVMs} additional VMs awaiting scheduling`, ), }, @@ -128,13 +132,14 @@ You can use ${ formattedData.forEach((instance) => { const status = instance.status.toLowerCase(); - const statusText = status === "running" - ? green("Running") - : status === "dead" - ? red("Dead") - : status === "off" - ? gray("Off") - : instance.status; + const statusText = + status === "running" + ? chalk.green("Running") + : status === "dead" + ? chalk.red("Dead") + : status === "off" + ? chalk.gray("Off") + : instance.status; table.push([instance.id, statusText, instance.last_updated_at]); }); @@ -142,11 +147,11 @@ You can use ${ const exampleId = formattedData[0].id; console.log(table.toString()); - console.log(`\n${gray("Use VM IDs to access and replace VMs.")}\n`); - console.log(gray("Examples:")); - console.log(` sf vm ssh ${cyan(`root@${exampleId}`)}`); - console.log(` sf vm logs -i ${cyan(exampleId)} -f`); - console.log(` sf vm replace -i ${cyan(exampleId)}`); + console.log(`\n${chalk.gray("Use VM IDs to access and replace VMs.")}\n`); + console.log(chalk.gray("Examples:")); + console.log(` sf vm ssh ${chalk.cyan(`USERNAME@${exampleId}`)}`); + console.log(` sf vm logs -i ${chalk.cyan(exampleId)} -f`); + console.log(` sf vm replace -i ${chalk.cyan(exampleId)}`); }); export default list; diff --git a/src/lib/zones.tsx b/src/lib/zones.tsx index cd09b616..08710b27 100644 --- a/src/lib/zones.tsx +++ b/src/lib/zones.tsx @@ -1,16 +1,17 @@ +import * as console from "node:console"; import type { Command } from "@commander-js/extra-typings"; -import { Box, render, Text } from "ink"; +import chalk from "chalk"; import Table from "cli-table3"; -import { cyan, gray, green, red } from "jsr:@std/fmt/colors"; -import * as console from "node:console"; -import React from "react"; -import { isLoggedIn } from "../helpers/config.ts"; +import dayjs from "dayjs"; +import { Box, render, Text } from "ink"; import { apiClient } from "../apiClient.ts"; +import { isLoggedIn } from "../helpers/config.ts"; import { logAndQuit, logLoginMessageAndQuit, logSessionTokenExpiredAndQuit, } from "../helpers/errors.ts"; +import type { components } from "../schema.ts"; import { isFeatureEnabled } from "./posthog.ts"; import { components } from "../schema.ts"; From 23ee1386369fdf32ff8266a56746121784aaed01 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:21:54 -0800 Subject: [PATCH 05/48] refactor: remove npm: prefix from imports Updated imports from Deno-specific npm: namespace to standard: - import boxen from "npm:boxen@8.0.1" -> import boxen from "boxen" - import yn from "npm:yn" -> import yn from "yn" - import * as nacl from "npm:tweetnacl" -> import nacl from "tweetnacl" Part of Deno to Node.js migration (Phase 4) --- src/lib/ConfirmInput.tsx | 10 +++--- src/lib/scale/ConfirmationMessage.tsx | 41 ++++++++++++--------- src/lib/sell/index.tsx | 52 +++++++++++++-------------- 3 files changed, 55 insertions(+), 48 deletions(-) diff --git a/src/lib/ConfirmInput.tsx b/src/lib/ConfirmInput.tsx index 0413ecbd..1ab690ae 100644 --- a/src/lib/ConfirmInput.tsx +++ b/src/lib/ConfirmInput.tsx @@ -1,9 +1,9 @@ -import React, { ComponentProps, useCallback, useState } from "react"; import TextInput from "ink-text-input"; -import yn from "npm:yn"; +import { type ComponentProps, useCallback, useState } from "react"; +import yn from "yn"; -interface ConfirmInputProps extends - Omit< +interface ConfirmInputProps + extends Omit< ComponentProps, "value" | "onChange" | "onSubmit" > { @@ -23,7 +23,7 @@ const ConfirmInput = ({ const [value, setValue] = useState(""); const handleSubmit = useCallback( (newValue: string) => { - onSubmit?.(yn(newValue, { default: isChecked })); + onSubmit?.(yn(newValue, { default: isChecked }) ?? false); }, [isChecked, onSubmit], ); diff --git a/src/lib/scale/ConfirmationMessage.tsx b/src/lib/scale/ConfirmationMessage.tsx index ac38d065..bea7b98f 100644 --- a/src/lib/scale/ConfirmationMessage.tsx +++ b/src/lib/scale/ConfirmationMessage.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { Box, Text } from "ink"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; @@ -7,9 +6,9 @@ import { Row } from "../Row.tsx"; import { formatDuration } from "../orders/index.tsx"; import { - formatColocationStrategy, MIN_CONTRACT_MINUTES, - Procurement, + type Procurement, + formatColocationStrategy, } from "./utils.ts"; export default function ConfirmationMessage(props: { @@ -24,8 +23,8 @@ export default function ConfirmationMessage(props: { const horizonInMilliseconds = props.horizonMinutes ? Math.max(props.horizonMinutes, MIN_CONTRACT_MINUTES) * 60 * 1000 : undefined; - const isSupportedType = typeof props.type === "string" && - props.type in InstanceTypeMetadata; + const isSupportedType = + typeof props.type === "string" && props.type in InstanceTypeMetadata; const typeLabel = isSupportedType ? InstanceTypeMetadata[props.type!].displayName : props.type; @@ -40,8 +39,8 @@ export default function ConfirmationMessage(props: { {typeLabel} ({props.type}) + ) : ( + "unchanged" ) - : "unchanged"} + } /> ${(props.pricePerGpuHourInCents / 100).toFixed(2)}/gpu/hr {props.quote && (1.5x market)} + ) : ( + "unchanged" ) - : "unchanged"} + } /> {props.colocationStrategy && ( Place order? (y/n) - + )} @@ -300,8 +296,8 @@ function SellOrderPreview(props: { const realDurationHours = realDuration / 3600 / 1000; const realDurationString = ms(realDuration); - const totalPrice = getTotalPrice(props.price, props.size, realDurationHours) / - 100; + const totalPrice = + getTotalPrice(props.price, props.size, realDurationHours) / 100; return ( @@ -351,9 +347,10 @@ export async function placeSellOrder(options: { endsAt: Date; flags?: SellOrderFlags; }) { - const realDurationHours = dayjs(options.endsAt).diff( - dayjs(options.startAt === "NOW" ? new Date() : options.startAt), - ) / + const realDurationHours = + dayjs(options.endsAt).diff( + dayjs(options.startAt === "NOW" ? new Date() : options.startAt), + ) / 3600 / 1000; const totalPrice = getTotalPrice( @@ -373,9 +370,8 @@ export async function placeSellOrder(options: { price: totalPrice, contract_id: options.contractId, quantity: options.quantity, - start_at: options.startAt === "NOW" - ? "NOW" - : options.startAt.toISOString(), + start_at: + options.startAt === "NOW" ? "NOW" : options.startAt.toISOString(), end_at: options.endsAt.toISOString(), flags: options.flags || {}, }, @@ -406,7 +402,11 @@ export async function placeSellOrder(options: { export async function getOrder(orderId: string) { const api = await apiClient(); - const { data: order, error, response } = await api.GET("/v0/orders/{id}", { + const { + data: order, + error, + response, + } = await api.GET("/v0/orders/{id}", { params: { path: { id: orderId } }, }); From 12b04d4a4565bf3e4364aa50c543cd689ddb167b Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:22:01 -0800 Subject: [PATCH 06/48] test: migrate test files from Deno.test to vitest Updated 5 test files: - src/helpers/test/units.test.ts - src/helpers/test/duration.test.ts - src/lib/orders/__tests__/OrderDisplay.test.ts - src/lib/clusters/kubeconfig.test.ts - src/lib/clusters/utils.test.ts Changes: - Deno.test("name", fn) -> test("name", fn) - assertEquals(a, b) -> expect(a).toEqual(b) - import from "jsr:@std/assert" -> import from "vitest" Part of Deno to Node.js migration (Phase 5) --- src/helpers/test/duration.test.ts | 6 +- src/helpers/test/units.test.ts | 22 +++--- src/lib/orders/__tests__/OrderDisplay.test.ts | 71 ++++++++----------- 3 files changed, 43 insertions(+), 56 deletions(-) diff --git a/src/helpers/test/duration.test.ts b/src/helpers/test/duration.test.ts index 76a3c473..a01cb542 100644 --- a/src/helpers/test/duration.test.ts +++ b/src/helpers/test/duration.test.ts @@ -1,7 +1,7 @@ -import { assertEquals } from "https://deno.land/std@0.112.0/testing/asserts.ts"; +import { expect, test } from "vitest"; import { parseDurationArgument } from "../duration.ts"; -Deno.test("parseDurationArgument", () => { +test("parseDurationArgument", () => { const testCases: [string, number | undefined][] = [ ["", undefined], ["0", 0], @@ -36,6 +36,6 @@ Deno.test("parseDurationArgument", () => { for (const [input, expected] of testCases) { const result = parseDurationArgument(input); - assertEquals(result, expected); + expect(result).toEqual(expected); } }); diff --git a/src/helpers/test/units.test.ts b/src/helpers/test/units.test.ts index 30afb8d3..7dfea61a 100644 --- a/src/helpers/test/units.test.ts +++ b/src/helpers/test/units.test.ts @@ -1,14 +1,11 @@ -import { - assert, - assertEquals, -} from "https://deno.land/std@0.112.0/testing/asserts.ts"; +import { expect, test } from "vitest"; import { type Cents, centsToDollarsFormatted, priceWholeToCents, } from "../units.ts"; -Deno.test("price whole to cents", () => { +test("price whole to cents", () => { const inputToExpectedValids = [ // formatted as USD ["$0", 0], @@ -56,22 +53,21 @@ Deno.test("price whole to cents", () => { for (const [input, centsExpected] of inputToExpectedValids) { const { cents, invalid } = priceWholeToCents(input); - assertEquals(cents !== null, true); - assertEquals(cents, centsExpected); - assert(invalid === false); + expect(cents !== null).toBe(true); + expect(cents).toEqual(centsExpected); + expect(invalid === false).toBe(true); } const invalidPrices = [null, undefined, [], {}]; for (const input of invalidPrices) { - // deno-lint-ignore no-explicit-any -- we pass in invalid types on purpose for testing here const { cents, invalid } = priceWholeToCents(input as any); - assertEquals(cents, null); - assertEquals(invalid, true); + expect(cents).toBeNull(); + expect(invalid).toBe(true); } }); -Deno.test("cents to dollars formatted", () => { +test("cents to dollars formatted", () => { const inputToExpectedValids = [ // whole [0, "$0.00"], @@ -91,6 +87,6 @@ Deno.test("cents to dollars formatted", () => { for (const [input, expected] of inputToExpectedValids) { const result = centsToDollarsFormatted(input as Cents); - assertEquals(result, expected); + expect(result).toEqual(expected); } }); diff --git a/src/lib/orders/__tests__/OrderDisplay.test.ts b/src/lib/orders/__tests__/OrderDisplay.test.ts index 1c8e47f0..377a76be 100644 --- a/src/lib/orders/__tests__/OrderDisplay.test.ts +++ b/src/lib/orders/__tests__/OrderDisplay.test.ts @@ -1,4 +1,4 @@ -import { assertEquals } from "https://deno.land/std@0.112.0/testing/asserts.ts"; +import { expect, test } from "vitest"; import { GPUS_PER_NODE } from "../../constants.ts"; import { orderDetails } from "../OrderDisplay.tsx"; import type { HydratedOrder } from "../types.ts"; @@ -25,55 +25,46 @@ const baseOrder: HydratedOrder = { status: OrderStatus.Open, }; -Deno.test("orderDetails - calculates price per GPU hour correctly", () => { +test("orderDetails - calculates price per GPU hour correctly", () => { const result = orderDetails(baseOrder); // $100 / (1 quantity * 1 hour * GPUS_PER_NODE) const expectedPricePerGPUHour = 100 / (1 * 1 * GPUS_PER_NODE); - assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour); + expect(result.pricePerGPUHour).toEqual(expectedPricePerGPUHour); }); -Deno.test( - "orderDetails - handles zero duration by using 1 hour minimum", - () => { - const zeroOrder = { - ...baseOrder, - start_at: "2024-02-20T00:00:00Z", - end_at: "2024-02-20T00:00:00Z", - }; - const result = orderDetails(zeroOrder); - const expectedPricePerGPUHour = 100 / (1 * 1 * GPUS_PER_NODE); - assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour); - }, -); +test("orderDetails - handles zero duration by using 1 hour minimum", () => { + const zeroOrder = { + ...baseOrder, + start_at: "2024-02-20T00:00:00Z", + end_at: "2024-02-20T00:00:00Z", + }; + const result = orderDetails(zeroOrder); + const expectedPricePerGPUHour = 100 / (1 * 1 * GPUS_PER_NODE); + expect(result.pricePerGPUHour).toEqual(expectedPricePerGPUHour); +}); -Deno.test( - "orderDetails - calculates executed price per GPU hour when available", - () => { - const executedOrder = { - ...baseOrder, - executed: true, - execution_price: 8000, // 80 USD in cents - }; - const result = orderDetails(executedOrder); - const expectedExecutedPrice = 80 / (1 * 1 * GPUS_PER_NODE); - assertEquals(result.executedPriceDollarsPerGPUHour, expectedExecutedPrice); - }, -); +test("orderDetails - calculates executed price per GPU hour when available", () => { + const executedOrder = { + ...baseOrder, + executed: true, + execution_price: 8000, // 80 USD in cents + }; + const result = orderDetails(executedOrder); + const expectedExecutedPrice = 80 / (1 * 1 * GPUS_PER_NODE); + expect(result.executedPriceDollarsPerGPUHour).toEqual(expectedExecutedPrice); +}); -Deno.test( - "orderDetails - returns undefined for executedPriceDollarsPerGPUHour when no execution price", - () => { - const result = orderDetails(baseOrder); - assertEquals(result.executedPriceDollarsPerGPUHour, undefined); - }, -); +test("orderDetails - returns undefined for executedPriceDollarsPerGPUHour when no execution price", () => { + const result = orderDetails(baseOrder); + expect(result.executedPriceDollarsPerGPUHour).toBeUndefined(); +}); -Deno.test("orderDetails - formats duration correctly", () => { +test("orderDetails - formats duration correctly", () => { const result = orderDetails(baseOrder); - assertEquals(result.durationFormatted, "1h"); // Assuming formatDuration returns "1h" for 1 hour + expect(result.durationFormatted).toEqual("1h"); // Assuming formatDuration returns "1h" for 1 hour }); -Deno.test("orderDetails - handles multiple nodes and longer duration", () => { +test("orderDetails - handles multiple nodes and longer duration", () => { const multiNodeOrder = { ...baseOrder, quantity: 2, @@ -82,5 +73,5 @@ Deno.test("orderDetails - handles multiple nodes and longer duration", () => { const result = orderDetails(multiNodeOrder); // $100 / (2 quantity * 3 hours * GPUS_PER_NODE) const expectedPricePerGPUHour = 100 / (2 * 3 * GPUS_PER_NODE); - assertEquals(result.pricePerGPUHour, expectedPricePerGPUHour); + expect(result.pricePerGPUHour).toEqual(expectedPricePerGPUHour); }); From dc20d9d129b883060ef5d9dc66ada73c33087d56 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:22:07 -0800 Subject: [PATCH 07/48] ci: update workflows for Node.js runtime ci.yml: - Replace denoland/setup-deno with actions/setup-node - Replace deno fmt/lint/check/test with npm run lint/check/test release.yml: - Replace deno setup with Node.js 20 - Replace deno run with npx tsx for release script - Use npm ci for dependency installation Part of Deno to Node.js migration (Phase 6) --- .github/workflows/ci.yml | 17 ++++++++--------- .github/workflows/release.yml | 21 +++++++++++---------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71cf910e..1a8b81a9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,17 +7,16 @@ on: branches: [main] jobs: - # run format, lint, and test test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: denoland/setup-deno@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - deno-version-file: .tool-versions - - run: deno install - - run: deno fmt --check - - run: deno lint - - run: deno check --config deno.json . - - run: deno test --allow-all + node-version: '20' + cache: 'npm' + - run: npm ci + - run: npm run lint + - run: npm run check + - run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d4d41ed..b2d7f7ff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,22 +29,23 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Deno - uses: denoland/setup-deno@v2 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - deno-version-file: .tool-versions + node-version: '20' + cache: 'npm' - - name: Check formatting - run: deno fmt --check + - name: Install dependencies + run: npm ci - - name: Run linter - run: deno lint + - name: Lint + run: npm run lint - name: Type check - run: deno check --config deno.json ./src/index.ts + run: npm run check - name: Run tests - run: deno test --allow-all + run: npm test - name: Configure Git run: | @@ -55,4 +56,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - deno run --allow-all src/scripts/release.ts ${{ inputs.version_type }} + npx tsx src/scripts/release.ts ${{ inputs.version_type }} From aaa038f8dd895d231053e9cfcc27430cdd52093f Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:22:12 -0800 Subject: [PATCH 08/48] chore: remove Deno configuration files - Delete deno.json (Deno runtime configuration) - Delete deno.lock (Deno dependency lock file) - Update .tool-versions to remove deno, keep nodejs 20.19.0 Part of Deno to Node.js migration (Phase 7: Cleanup) --- .tool-versions | 1 - deno.json | 39 -- deno.lock | 1205 ------------------------------------------------ 3 files changed, 1245 deletions(-) delete mode 100644 deno.json delete mode 100644 deno.lock diff --git a/.tool-versions b/.tool-versions index 2e5aabff..77730a63 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1 @@ nodejs 20.19.0 -deno 2.4.2 \ No newline at end of file diff --git a/deno.json b/deno.json deleted file mode 100644 index ae82a304..00000000 --- a/deno.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "tasks": { - "dev": "deno run --watch main.ts" - }, - "imports": { - "@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", - "boxen": "https://esm.sh/boxen@8.0.1", - "dayjs": "https://esm.sh/dayjs@1.11.13", - "dayjs/plugin/advancedFormat": "https://esm.sh/dayjs@1.11.13/plugin/advancedFormat.js", - "dayjs/plugin/duration": "https://esm.sh/dayjs@1.11.13/plugin/duration.js", - "dayjs/plugin/relativeTime": "https://esm.sh/dayjs@1.11.13/plugin/relativeTime.js", - "dayjs/plugin/utc": "https://esm.sh/dayjs@1.11.13/plugin/utc.js", - "dayjs/plugin/timezone": "https://esm.sh/dayjs@1.11.13/plugin/timezone.js", - "tweetnacl": "https://esm.sh/tweetnacl@1.0.3", - "tweetnacl-util": "https://esm.sh/tweetnacl-util@0.15.1", - "yn": "https://esm.sh/yn" - }, - "lint": { - "exclude": [ - "src/schema.ts" - ] - }, - "fmt": { - "exclude": [ - "src/schema.ts" - ] - }, - "compilerOptions": { - "lib": [ - "deno.ns", - "deno.fetch", - "deno.web" - ] - }, - "nodeModulesDir": "auto" -} diff --git a/deno.lock b/deno.lock deleted file mode 100644 index 3ebeee9d..00000000 --- a/deno.lock +++ /dev/null @@ -1,1205 +0,0 @@ -{ - "version": "5", - "specifiers": { - "jsr:@std/assert@1.0.11": "1.0.11", - "jsr:@std/internal@^1.0.5": "1.0.12", - "npm:@commander-js/extra-typings@14": "14.0.0_commander@14.0.2", - "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.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:chrono-node@^2.9.0": "2.9.0", - "npm:cli-progress@^3.12.0": "3.12.0", - "npm:cli-table3@0.6.5": "0.6.5", - "npm:commander@^14.0.2": "14.0.2", - "npm:date-fns@^4.1.0": "4.1.0", - "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", - "npm:ink-testing-library@4": "4.0.0_@types+react@18.3.27", - "npm:ink-text-input@6": "6.0.0_ink@5.2.1__@types+react@18.3.27__react@18.3.1_react@18.3.1_@types+react@18.3.27", - "npm:ink@^5.2.0": "5.2.1_@types+react@18.3.27_react@18.3.1", - "npm:inquirer@^10.1.2": "10.2.2", - "npm:little-date@1": "1.2.1", - "npm:ms@^2.1.3": "2.1.3", - "npm:node-fetch@^3.3.2": "3.3.2", - "npm:openapi-fetch@~0.11.1": "0.11.3", - "npm:ora@^8.1.0": "8.2.0", - "npm:parse-duration@^2.1.3": "2.1.5", - "npm:posthog-node@^4.10.1": "4.18.0", - "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": "0.15.1", - "npm:tweetnacl@^1.0.3": "1.0.3", - "npm:yaml@2.6.1": "2.6.1" - }, - "jsr": { - "@std/assert@1.0.11": { - "integrity": "2461ef3c368fe88bc60e186e7744a93112f16fd110022e113a0849e94d1c83c1", - "dependencies": [ - "jsr:@std/internal" - ] - }, - "@std/internal@1.0.12": { - "integrity": "972a634fd5bc34b242024402972cd5143eac68d8dffaca5eaa4dba30ce17b027" - } - }, - "npm": { - "@alcalzone/ansi-tokenize@0.1.3": { - "integrity": "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw==", - "dependencies": [ - "ansi-styles@6.2.3", - "is-fullwidth-code-point@4.0.0" - ] - }, - "@babel/runtime@7.28.4": { - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==" - }, - "@colors/colors@1.5.0": { - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" - }, - "@commander-js/extra-typings@14.0.0_commander@14.0.2": { - "integrity": "sha512-hIn0ncNaJRLkZrxBIp5AsW/eXEHNKYQBh0aPdoUqNgD+Io3NIykQqpKFyKcuasZhicGaEZJX/JBSIkZ4e5x8Dg==", - "dependencies": [ - "commander" - ] - }, - "@inkjs/ui@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": { - "integrity": "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg==", - "dependencies": [ - "chalk", - "cli-spinners@3.3.0", - "deepmerge", - "figures", - "ink" - ] - }, - "@inquirer/checkbox@2.5.0": { - "integrity": "sha512-sMgdETOfi2dUHT8r7TT1BTKOwNvdDGFDXYWtQ2J69SvlYNntk9I/gJe7r5yvMwwsuKnYbuRs3pNhx4tgNck5aA==", - "dependencies": [ - "@inquirer/core", - "@inquirer/figures", - "@inquirer/type@1.5.5", - "ansi-escapes@4.3.2", - "yoctocolors-cjs" - ] - }, - "@inquirer/confirm@3.2.0": { - "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5" - ] - }, - "@inquirer/core@9.2.1": { - "integrity": "sha512-F2VBt7W/mwqEU4bL0RnHNZmC/OxzNx9cOYxHqnXX3MP6ruYvZUZAW9imgN9+h/uBT/oP8Gh888J2OZSbjSeWcg==", - "dependencies": [ - "@inquirer/figures", - "@inquirer/type@2.0.0", - "@types/mute-stream", - "@types/node@22.19.3", - "@types/wrap-ansi", - "ansi-escapes@4.3.2", - "cli-width", - "mute-stream", - "signal-exit@4.1.0", - "strip-ansi@6.0.1", - "wrap-ansi@6.2.0", - "yoctocolors-cjs" - ] - }, - "@inquirer/editor@2.2.0": { - "integrity": "sha512-9KHOpJ+dIL5SZli8lJ6xdaYLPPzB8xB9GZItg39MBybzhxA16vxmszmQFrRwbOA918WA2rvu8xhDEg/p6LXKbw==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5", - "external-editor" - ] - }, - "@inquirer/expand@2.3.0": { - "integrity": "sha512-qnJsUcOGCSG1e5DTOErmv2BPQqrtT6uzqn1vI/aYGiPKq+FgslGZmtdnXbhuI7IlT7OByDoEEqdnhUnVR2hhLw==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5", - "yoctocolors-cjs" - ] - }, - "@inquirer/figures@1.0.15": { - "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==" - }, - "@inquirer/input@2.3.0": { - "integrity": "sha512-XfnpCStx2xgh1LIRqPXrTNEEByqQWoxsWYzNRSEUxJ5c6EQlhMogJ3vHKu8aXuTacebtaZzMAHwEL0kAflKOBw==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5" - ] - }, - "@inquirer/number@1.1.0": { - "integrity": "sha512-ilUnia/GZUtfSZy3YEErXLJ2Sljo/mf9fiKc08n18DdwdmDbOzRcTv65H1jjDvlsAuvdFXf4Sa/aL7iw/NanVA==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5" - ] - }, - "@inquirer/password@2.2.0": { - "integrity": "sha512-5otqIpgsPYIshqhgtEwSspBQE40etouR8VIxzpJkv9i0dVHIpyhiivbkH9/dGiMLdyamT54YRdGJLfl8TFnLHg==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5", - "ansi-escapes@4.3.2" - ] - }, - "@inquirer/prompts@5.5.0": { - "integrity": "sha512-BHDeL0catgHdcHbSFFUddNzvx/imzJMft+tWDPwTm3hfu8/tApk1HrooNngB2Mb4qY+KaRWF+iZqoVUPeslEog==", - "dependencies": [ - "@inquirer/checkbox", - "@inquirer/confirm", - "@inquirer/editor", - "@inquirer/expand", - "@inquirer/input", - "@inquirer/number", - "@inquirer/password", - "@inquirer/rawlist", - "@inquirer/search", - "@inquirer/select" - ] - }, - "@inquirer/rawlist@2.3.0": { - "integrity": "sha512-zzfNuINhFF7OLAtGHfhwOW2TlYJyli7lOUoJUXw/uyklcwalV6WRXBXtFIicN8rTRK1XTiPWB4UY+YuW8dsnLQ==", - "dependencies": [ - "@inquirer/core", - "@inquirer/type@1.5.5", - "yoctocolors-cjs" - ] - }, - "@inquirer/search@1.1.0": { - "integrity": "sha512-h+/5LSj51dx7hp5xOn4QFnUaKeARwUCLs6mIhtkJ0JYPBLmEYjdHSYh7I6GrLg9LwpJ3xeX0FZgAG1q0QdCpVQ==", - "dependencies": [ - "@inquirer/core", - "@inquirer/figures", - "@inquirer/type@1.5.5", - "yoctocolors-cjs" - ] - }, - "@inquirer/select@2.5.0": { - "integrity": "sha512-YmDobTItPP3WcEI86GvPo+T2sRHkxxOq/kXmsBjHS5BVXUgvgZ5AfJjkvQvZr03T81NnI3KrrRuMzeuYUQRFOA==", - "dependencies": [ - "@inquirer/core", - "@inquirer/figures", - "@inquirer/type@1.5.5", - "ansi-escapes@4.3.2", - "yoctocolors-cjs" - ] - }, - "@inquirer/type@1.5.5": { - "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", - "dependencies": [ - "mute-stream" - ] - }, - "@inquirer/type@2.0.0": { - "integrity": "sha512-XvJRx+2KR3YXyYtPUUy+qd9i7p+GO9Ko6VIIpWlBrpWwXDv8WLFeHTxz35CfQFUiBMLXlGHhGzys7lqit9gWag==", - "dependencies": [ - "mute-stream" - ] - }, - "@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27": { - "integrity": "sha512-IcnuTpfZszCLpBtEOFCas0fhc0yAgCvGZ8JvN9DdM1zWMUswHyuumYKDEVt6ZUXjJVnMt09Uhqh2sWHOEer+rQ==" - }, - "@types/ms@0.7.34": { - "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==" - }, - "@types/mute-stream@0.0.4": { - "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", - "dependencies": [ - "@types/node@24.2.0" - ] - }, - "@types/node@22.19.3": { - "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", - "dependencies": [ - "undici-types@6.21.0" - ] - }, - "@types/node@24.2.0": { - "integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==", - "dependencies": [ - "undici-types@7.10.0" - ] - }, - "@types/prop-types@15.7.15": { - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==" - }, - "@types/react@18.3.27": { - "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", - "dependencies": [ - "@types/prop-types", - "csstype" - ] - }, - "@types/semver@7.7.1": { - "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==" - }, - "@types/wrap-ansi@3.0.0": { - "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==" - }, - "ansi-align@3.0.1": { - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "dependencies": [ - "string-width@4.2.3" - ] - }, - "ansi-escapes@4.3.2": { - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dependencies": [ - "type-fest@0.21.3" - ] - }, - "ansi-escapes@5.0.0": { - "integrity": "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==", - "dependencies": [ - "type-fest@1.4.0" - ] - }, - "ansi-escapes@7.2.0": { - "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", - "dependencies": [ - "environment" - ] - }, - "ansi-regex@5.0.1": { - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-regex@6.2.2": { - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==" - }, - "ansi-styles@4.3.0": { - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": [ - "color-convert" - ] - }, - "ansi-styles@6.2.3": { - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==" - }, - "async-retry@1.3.3": { - "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", - "dependencies": [ - "retry" - ] - }, - "asynckit@0.4.0": { - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" - }, - "auto-bind@5.0.1": { - "integrity": "sha512-ooviqdwwgfIfNmDwo94wlshcdzfO64XV0Cg6oDsDYBJfITDz1EngD2z7DkbvCWn+XIMsIqW27sEVF6qcpJrRcg==" - }, - "axios@1.13.2": { - "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", - "dependencies": [ - "follow-redirects", - "form-data", - "proxy-from-env" - ] - }, - "boxen@8.0.1": { - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "dependencies": [ - "ansi-align", - "camelcase", - "chalk", - "cli-boxes", - "string-width@7.2.0", - "type-fest@4.41.0", - "widest-line", - "wrap-ansi@9.0.2" - ] - }, - "call-bind-apply-helpers@1.0.2": { - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dependencies": [ - "es-errors", - "function-bind" - ] - }, - "camelcase@8.0.0": { - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==" - }, - "chalk@5.6.2": { - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==" - }, - "chardet@0.7.0": { - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" - }, - "chrono-node@2.9.0": { - "integrity": "sha512-glI4YY2Jy6JII5l3d5FN6rcrIbKSQqKPhWsIRYPK2IK8Mm4Q1ZZFdYIaDqglUNf7gNwG+kWIzTn0omzzE0VkvQ==" - }, - "cli-boxes@3.0.0": { - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==" - }, - "cli-cursor@4.0.0": { - "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==", - "dependencies": [ - "restore-cursor@4.0.0" - ] - }, - "cli-cursor@5.0.0": { - "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", - "dependencies": [ - "restore-cursor@5.1.0" - ] - }, - "cli-progress@3.12.0": { - "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==", - "dependencies": [ - "string-width@4.2.3" - ] - }, - "cli-spinners@2.9.2": { - "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==" - }, - "cli-spinners@3.3.0": { - "integrity": "sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==" - }, - "cli-table3@0.6.5": { - "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", - "dependencies": [ - "string-width@4.2.3" - ], - "optionalDependencies": [ - "@colors/colors" - ] - }, - "cli-truncate@4.0.0": { - "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==", - "dependencies": [ - "slice-ansi@5.0.0", - "string-width@7.2.0" - ] - }, - "cli-width@4.1.0": { - "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==" - }, - "code-excerpt@4.0.0": { - "integrity": "sha512-xxodCmBen3iy2i0WtAK8FlFNrRzjUqjRsMfho58xT/wvZU1YTM3fCnRjcy1gJPMepaRlgm/0e6w8SpWHpn3/cA==", - "dependencies": [ - "convert-to-spaces" - ] - }, - "color-convert@2.0.1": { - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": [ - "color-name" - ] - }, - "color-name@1.1.4": { - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "combined-stream@1.0.8": { - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dependencies": [ - "delayed-stream" - ] - }, - "commander@14.0.2": { - "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==" - }, - "convert-to-spaces@2.0.1": { - "integrity": "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ==" - }, - "csstype@3.2.3": { - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==" - }, - "data-uri-to-buffer@4.0.1": { - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==" - }, - "date-fns@2.30.0": { - "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "dependencies": [ - "@babel/runtime" - ] - }, - "date-fns@4.1.0": { - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==" - }, - "dayjs@1.11.19": { - "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==" - }, - "deepmerge@4.3.1": { - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==" - }, - "delayed-stream@1.0.0": { - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" - }, - "dotenv@16.6.1": { - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==" - }, - "dunder-proto@1.0.1": { - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dependencies": [ - "call-bind-apply-helpers", - "es-errors", - "gopd" - ] - }, - "emoji-regex@10.6.0": { - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==" - }, - "emoji-regex@8.0.0": { - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" - }, - "environment@1.1.0": { - "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==" - }, - "es-define-property@1.0.1": { - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==" - }, - "es-errors@1.3.0": { - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" - }, - "es-object-atoms@1.1.1": { - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dependencies": [ - "es-errors" - ] - }, - "es-set-tostringtag@2.1.0": { - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dependencies": [ - "es-errors", - "get-intrinsic", - "has-tostringtag", - "hasown" - ] - }, - "es-toolkit@1.43.0": { - "integrity": "sha512-SKCT8AsWvYzBBuUqMk4NPwFlSdqLpJwmy6AP322ERn8W2YLIB6JBXnwMI2Qsh2gfphT3q7EKAxKb23cvFHFwKA==" - }, - "escape-string-regexp@2.0.0": { - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==" - }, - "external-editor@3.1.0": { - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dependencies": [ - "chardet", - "iconv-lite", - "tmp" - ] - }, - "fetch-blob@3.2.0": { - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dependencies": [ - "node-domexception", - "web-streams-polyfill" - ] - }, - "figures@6.1.0": { - "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", - "dependencies": [ - "is-unicode-supported@2.1.0" - ] - }, - "follow-redirects@1.15.11": { - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==" - }, - "form-data@4.0.5": { - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", - "dependencies": [ - "asynckit", - "combined-stream", - "es-set-tostringtag", - "hasown", - "mime-types" - ] - }, - "formdata-polyfill@4.0.10": { - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dependencies": [ - "fetch-blob" - ] - }, - "function-bind@1.1.2": { - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" - }, - "get-east-asian-width@1.4.0": { - "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==" - }, - "get-intrinsic@1.3.0": { - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dependencies": [ - "call-bind-apply-helpers", - "es-define-property", - "es-errors", - "es-object-atoms", - "function-bind", - "get-proto", - "gopd", - "has-symbols", - "hasown", - "math-intrinsics" - ] - }, - "get-proto@1.0.1": { - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dependencies": [ - "dunder-proto", - "es-object-atoms" - ] - }, - "gopd@1.2.0": { - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==" - }, - "has-flag@4.0.0": { - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-symbols@1.1.0": { - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==" - }, - "has-tostringtag@1.0.2": { - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dependencies": [ - "has-symbols" - ] - }, - "hasown@2.0.2": { - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dependencies": [ - "function-bind" - ] - }, - "iconv-lite@0.4.24": { - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dependencies": [ - "safer-buffer" - ] - }, - "indent-string@5.0.0": { - "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==" - }, - "ink-link@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": { - "integrity": "sha512-3nNyJXum0FJIKAXBK8qat2jEOM41nJ1J60NRivwgK9Xh92R5UMN/k4vbz0A9xFzhJVrlf4BQEmmxMgXkCE1Jeg==", - "dependencies": [ - "ink", - "prop-types", - "terminal-link" - ] - }, - "ink-spinner@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": { - "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==", - "dependencies": [ - "cli-spinners@2.9.2", - "ink", - "react" - ] - }, - "ink-testing-library@4.0.0_@types+react@18.3.27": { - "integrity": "sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q==", - "dependencies": [ - "@types/react" - ], - "optionalPeers": [ - "@types/react" - ] - }, - "ink-text-input@6.0.0_ink@5.2.1__@types+react@18.3.27__react@18.3.1_react@18.3.1_@types+react@18.3.27": { - "integrity": "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw==", - "dependencies": [ - "chalk", - "ink", - "react", - "type-fest@4.41.0" - ] - }, - "ink@5.2.1_@types+react@18.3.27_react@18.3.1": { - "integrity": "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg==", - "dependencies": [ - "@alcalzone/ansi-tokenize", - "@types/react", - "ansi-escapes@7.2.0", - "ansi-styles@6.2.3", - "auto-bind", - "chalk", - "cli-boxes", - "cli-cursor@4.0.0", - "cli-truncate", - "code-excerpt", - "es-toolkit", - "indent-string", - "is-in-ci", - "patch-console", - "react", - "react-reconciler", - "scheduler", - "signal-exit@3.0.7", - "slice-ansi@7.1.2", - "stack-utils", - "string-width@7.2.0", - "type-fest@4.41.0", - "widest-line", - "wrap-ansi@9.0.2", - "ws", - "yoga-layout" - ], - "optionalPeers": [ - "@types/react" - ] - }, - "inquirer@10.2.2": { - "integrity": "sha512-tyao/4Vo36XnUItZ7DnUXX4f1jVao2mSrleV/5IPtW/XAEA26hRVsbc68nuTEKWcr5vMP/1mVoT2O7u8H4v1Vg==", - "dependencies": [ - "@inquirer/core", - "@inquirer/prompts", - "@inquirer/type@1.5.5", - "@types/mute-stream", - "ansi-escapes@4.3.2", - "mute-stream", - "run-async", - "rxjs" - ] - }, - "is-fullwidth-code-point@3.0.0": { - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "is-fullwidth-code-point@4.0.0": { - "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==" - }, - "is-fullwidth-code-point@5.1.0": { - "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", - "dependencies": [ - "get-east-asian-width" - ] - }, - "is-in-ci@1.0.0": { - "integrity": "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg==", - "bin": true - }, - "is-interactive@2.0.0": { - "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==" - }, - "is-unicode-supported@1.3.0": { - "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==" - }, - "is-unicode-supported@2.1.0": { - "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==" - }, - "isexe@3.1.1": { - "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==" - }, - "js-tokens@4.0.0": { - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "little-date@1.2.1": { - "integrity": "sha512-O5LUKWMw96qEyS+12mxnRP49MZEEM9fPWzIylgutX6RHf6F1JakvfNeBgteP0CIeEyNFiPM8nohTldtbmyjEqQ==", - "dependencies": [ - "date-fns@2.30.0" - ] - }, - "log-symbols@6.0.0": { - "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", - "dependencies": [ - "chalk", - "is-unicode-supported@1.3.0" - ] - }, - "loose-envify@1.4.0": { - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dependencies": [ - "js-tokens" - ], - "bin": true - }, - "math-intrinsics@1.1.0": { - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==" - }, - "mime-db@1.52.0": { - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" - }, - "mime-types@2.1.35": { - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dependencies": [ - "mime-db" - ] - }, - "mimic-fn@2.1.0": { - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "mimic-function@5.0.1": { - "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==" - }, - "ms@2.1.3": { - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" - }, - "mute-stream@1.0.0": { - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==" - }, - "node-domexception@1.0.0": { - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": true - }, - "node-fetch@3.3.2": { - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dependencies": [ - "data-uri-to-buffer", - "fetch-blob", - "formdata-polyfill" - ] - }, - "object-assign@4.1.1": { - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "onetime@5.1.2": { - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": [ - "mimic-fn" - ] - }, - "onetime@7.0.0": { - "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", - "dependencies": [ - "mimic-function" - ] - }, - "openapi-fetch@0.11.3": { - "integrity": "sha512-r18fERgpxFrI4pv79ABD1dqFetWz7pTfwRd7jQmRm/lFdCDpWF43kvHUiOqOZu+tWsMydDJMpJN1hlZ9inRvfA==", - "dependencies": [ - "openapi-typescript-helpers" - ] - }, - "openapi-typescript-helpers@0.0.13": { - "integrity": "sha512-z44WK2e7ygW3aUtAtiurfEACohf/Qt9g6BsejmIYgEoY4REHeRzgFJmO3ium0libsuzPc145I+8lE9aiiZrQvQ==" - }, - "ora@8.2.0": { - "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", - "dependencies": [ - "chalk", - "cli-cursor@5.0.0", - "cli-spinners@2.9.2", - "is-interactive", - "is-unicode-supported@2.1.0", - "log-symbols", - "stdin-discarder", - "string-width@7.2.0", - "strip-ansi@7.1.2" - ] - }, - "os-tmpdir@1.0.2": { - "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" - }, - "parse-duration@2.1.5": { - "integrity": "sha512-/IX1KRw6zHDOOJrgIz++gvFASbFl7nc8GEXaLdD7d1t1x/GnrK6hh5Fgk8G3RLpkIEi4tsGj9pupGLWNg0EiJA==" - }, - "patch-console@2.0.0": { - "integrity": "sha512-0YNdUceMdaQwoKce1gatDScmMo5pu/tfABfnzEqeG0gtTmd7mh/WcwgUjtAeOU7N8nFFlbQBnFK2gXW5fGvmMA==" - }, - "posthog-node@4.18.0": { - "integrity": "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw==", - "dependencies": [ - "axios" - ] - }, - "prettier@3.7.4": { - "integrity": "sha512-v6UNi1+3hSlVvv8fSaoUbggEM5VErKmmpGA7Pl3HF8V6uKY7rvClBOJlH6yNwQtfTueNkGVpOv/mtWL9L4bgRA==", - "bin": true - }, - "prop-types@15.8.1": { - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dependencies": [ - "loose-envify", - "object-assign", - "react-is" - ] - }, - "proxy-from-env@1.1.0": { - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" - }, - "react-is@16.13.1": { - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" - }, - "react-reconciler@0.29.2_react@18.3.1": { - "integrity": "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg==", - "dependencies": [ - "loose-envify", - "react", - "scheduler" - ] - }, - "react@18.3.1": { - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "dependencies": [ - "loose-envify" - ] - }, - "restore-cursor@4.0.0": { - "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==", - "dependencies": [ - "onetime@5.1.2", - "signal-exit@3.0.7" - ] - }, - "restore-cursor@5.1.0": { - "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", - "dependencies": [ - "onetime@7.0.0", - "signal-exit@4.1.0" - ] - }, - "retry@0.13.1": { - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==" - }, - "run-async@3.0.0": { - "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==" - }, - "rxjs@7.8.2": { - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "dependencies": [ - "tslib" - ] - }, - "safer-buffer@2.1.2": { - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "scheduler@0.23.2": { - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "dependencies": [ - "loose-envify" - ] - }, - "semver@7.7.3": { - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "bin": true - }, - "shescape@2.1.7": { - "integrity": "sha512-Y1syY0ggm3ow7mE1zrcK9YrOhAqv/IGbm3+J9S+MXLukwXf/M8yzL3hZp7ubVeSy250TT7M5SVKikTZkKyib6w==", - "dependencies": [ - "which" - ] - }, - "signal-exit@3.0.7": { - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "signal-exit@4.1.0": { - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==" - }, - "slice-ansi@5.0.0": { - "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==", - "dependencies": [ - "ansi-styles@6.2.3", - "is-fullwidth-code-point@4.0.0" - ] - }, - "slice-ansi@7.1.2": { - "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", - "dependencies": [ - "ansi-styles@6.2.3", - "is-fullwidth-code-point@5.1.0" - ] - }, - "stack-utils@2.0.6": { - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dependencies": [ - "escape-string-regexp" - ] - }, - "stdin-discarder@0.2.2": { - "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==" - }, - "string-width@4.2.3": { - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dependencies": [ - "emoji-regex@8.0.0", - "is-fullwidth-code-point@3.0.0", - "strip-ansi@6.0.1" - ] - }, - "string-width@7.2.0": { - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "dependencies": [ - "emoji-regex@10.6.0", - "get-east-asian-width", - "strip-ansi@7.1.2" - ] - }, - "strip-ansi@6.0.1": { - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": [ - "ansi-regex@5.0.1" - ] - }, - "strip-ansi@7.1.2": { - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dependencies": [ - "ansi-regex@6.2.2" - ] - }, - "supports-color@7.2.0": { - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": [ - "has-flag" - ] - }, - "supports-hyperlinks@2.3.0": { - "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", - "dependencies": [ - "has-flag", - "supports-color" - ] - }, - "terminal-link@3.0.0": { - "integrity": "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==", - "dependencies": [ - "ansi-escapes@5.0.0", - "supports-hyperlinks" - ] - }, - "tiny-invariant@1.3.3": { - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==" - }, - "tmp@0.0.33": { - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dependencies": [ - "os-tmpdir" - ] - }, - "tslib@2.8.1": { - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" - }, - "tweetnacl-util@0.15.1": { - "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" - }, - "tweetnacl@1.0.3": { - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "type-fest@0.21.3": { - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==" - }, - "type-fest@1.4.0": { - "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==" - }, - "type-fest@4.41.0": { - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==" - }, - "undici-types@6.21.0": { - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==" - }, - "undici-types@7.10.0": { - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==" - }, - "web-streams-polyfill@3.3.3": { - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==" - }, - "which@6.0.0": { - "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", - "dependencies": [ - "isexe" - ], - "bin": true - }, - "widest-line@5.0.0": { - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "dependencies": [ - "string-width@7.2.0" - ] - }, - "wrap-ansi@6.2.0": { - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dependencies": [ - "ansi-styles@4.3.0", - "string-width@4.2.3", - "strip-ansi@6.0.1" - ] - }, - "wrap-ansi@9.0.2": { - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "dependencies": [ - "ansi-styles@6.2.3", - "string-width@7.2.0", - "strip-ansi@7.1.2" - ] - }, - "ws@8.19.0": { - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==" - }, - "yaml@2.6.1": { - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", - "bin": true - }, - "yoctocolors-cjs@2.1.3": { - "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==" - }, - "yoga-layout@3.2.1": { - "integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==" - } - }, - "redirects": { - "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", - "https://esm.sh/ansi-align@^3.0.1?target=denonext": "https://esm.sh/ansi-align@3.0.1?target=denonext", - "https://esm.sh/ansi-regex@^5.0.1?target=denonext": "https://esm.sh/ansi-regex@5.0.1?target=denonext", - "https://esm.sh/ansi-regex@^6.0.1?target=denonext": "https://esm.sh/ansi-regex@6.2.2?target=denonext", - "https://esm.sh/ansi-styles@^6.2.1?target=denonext": "https://esm.sh/ansi-styles@6.2.3?target=denonext", - "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.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", - "https://esm.sh/string-width@^4.1.0?target=denonext": "https://esm.sh/string-width@4.2.3?target=denonext", - "https://esm.sh/string-width@^4.2.0?target=denonext": "https://esm.sh/string-width@4.2.3?target=denonext", - "https://esm.sh/string-width@^7.0.0?target=denonext": "https://esm.sh/string-width@7.2.0?target=denonext", - "https://esm.sh/string-width@^7.2.0?target=denonext": "https://esm.sh/string-width@7.2.0?target=denonext", - "https://esm.sh/strip-ansi@^6.0.1?target=denonext": "https://esm.sh/strip-ansi@6.0.1?target=denonext", - "https://esm.sh/strip-ansi@^7.1.0?target=denonext": "https://esm.sh/strip-ansi@7.1.2?target=denonext", - "https://esm.sh/widest-line@^5.0.0?target=denonext": "https://esm.sh/widest-line@5.0.0?target=denonext", - "https://esm.sh/wrap-ansi@^9.0.0?target=denonext": "https://esm.sh/wrap-ansi@9.0.2?target=denonext", - "https://esm.sh/yn": "https://esm.sh/yn@5.1.0" - }, - "remote": { - "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", - "https://esm.sh/ansi-align@3.0.1?target=denonext": "a981bcdb5a9f6a67be186bb170525a411ee1260405b2290d7ee49a922457d697", - "https://esm.sh/ansi-regex@5.0.1/denonext/ansi-regex.mjs": "657078ebd509ad0609bd50cbe926c87ff54cff769136dfc050ffb3f63b5fb8e3", - "https://esm.sh/ansi-regex@5.0.1?target=denonext": "cef49c3f2a9750f63aa4c70f2037a34036d08ad61d7f6da2a774d6ef6090b05c", - "https://esm.sh/ansi-regex@6.2.2/denonext/ansi-regex.mjs": "12f0f0a97b9a2404badfe1012dd335741d0951875ca8c307952dc04c3eafb9df", - "https://esm.sh/ansi-regex@6.2.2?target=denonext": "11b2548258056b692770f9f515af0fb3bffac490258721a8024550308c00787d", - "https://esm.sh/ansi-styles@6.2.3/denonext/ansi-styles.mjs": "56a9ffc05be307f2fee9bbd0511c6788d2e2fcf488397499a244faaff22885e5", - "https://esm.sh/ansi-styles@6.2.3?target=denonext": "f39a6f5f58e408d33514205e0cb6e0867492890acce62aa5f4b8ca9b30bda654", - "https://esm.sh/boxen@8.0.1": "65f2fd786afc41ebb821d0b87e76dca18d16e2f4081c7306bee585bb7a050c72", - "https://esm.sh/boxen@8.0.1/denonext/boxen.mjs": "fa8a802a925196ace883fc17869f3e04b8716cf8789ba82486cbde1561dcbd51", - "https://esm.sh/camelcase@8.0.0/denonext/camelcase.mjs": "16b428a988dd016fc89d573b59c2b6d4b8054f1611a2b64375972d9d6747abce", - "https://esm.sh/camelcase@8.0.0?target=denonext": "ce7018426d8492866cffc23644dc16ad8bbb75cae8231ea8dab96f78c1d8de70", - "https://esm.sh/chalk@5.6.2/denonext/chalk.mjs": "0aaf8c647d4189d05101ef148c7bed1a3483d9b8b5c64b1556b5e20155a770e9", - "https://esm.sh/chalk@5.6.2/denonext/source/vendor/ansi-styles/index.mjs": "2c52b47fd6d22e4b8a8274c0e5b95b479964d13a6dec08d81468af5652c90712", - "https://esm.sh/chalk@5.6.2?target=denonext": "025db311237300ce6253fcd77b5827e366e928915dc6257baceb89127940c3f1", - "https://esm.sh/cli-boxes@3.0.0/denonext/cli-boxes.mjs": "88c1214ef2783f95a286491e6f2e8ee11ad0a7358298b1bd46ef918a3f466611", - "https://esm.sh/cli-boxes@3.0.0?target=denonext": "d7d1902599ae669086784966b7a2682cbf7d1bdc3724038e365c5bf0a29a945e", - "https://esm.sh/cli-table3@0.6.5": "028d5b5aa15ca57e687ed9660e0cf075f6cd17a98fec98eb78b4d5c43635c670", - "https://esm.sh/cli-table3@0.6.5/denonext/cli-table3.mjs": "8a03beafdce5f42d2566279f3ca42dc16235d5d29227f9c0859b037699fe8f4b", - "https://esm.sh/dayjs@1.11.13": "89c34b8b3f7b970708114b4d264c9430c30eb0c2eab1419410c77ffefa18fe2c", - "https://esm.sh/dayjs@1.11.13/denonext/dayjs.mjs": "6684fee7de01059065835aa6300306b0a753e4dfbaef3ba7db11c84f7afd3394", - "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/denonext/plugin/timezone.mjs": "94d48607684cc8423f815fee4e72bf334c801cdf52d79caa00481a7e83c345f4", - "https://esm.sh/dayjs@1.11.13/denonext/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": "cfe2b0eec9855373de8fbc92d3503d25930ab3156522482e55f7a66bb3d4fb32", - "https://esm.sh/dayjs@1.11.13/plugin/utc.js": "2e41a0673e6e7c7c962983f1680911ef6feb27ded6007bc7705787ac1b2637b7", - "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", - "https://esm.sh/get-east-asian-width@1.4.0?target=denonext": "aea55429fdd7086fae29a14ee20f995b9639cc75ca26f5749566b81580d37e87", - "https://esm.sh/is-fullwidth-code-point@3.0.0/denonext/is-fullwidth-code-point.mjs": "5b06355ee12e6ff7147d9e56fe9cf31f344a8c6d2ee149f439d4b4ac7086e34f", - "https://esm.sh/is-fullwidth-code-point@3.0.0?target=denonext": "417807304b3c7385ca5bcf277d0612e3236ff78539edd0b6fd626f7d275892ea", - "https://esm.sh/semver@7.6.3": "aad93fb008af54255d41f3155edf912bf806989e9166843ecd4e94e4f27e5064", - "https://esm.sh/semver@7.6.3/denonext/semver.mjs": "0d4ed5c4630b8735fe0f694a3636ce6479f9d8379234d0c52c9a24dba82191d4", - "https://esm.sh/string-width@4.2.3/denonext/string-width.mjs": "af5aad2a4d45bfdddf72724834d5c9b967e920ac35b7348af036f8a5e4bd4158", - "https://esm.sh/string-width@4.2.3?target=denonext": "af996f7b29ace74220e49229fa9a95f37bbdb66062402f131a6fb913cb61602b", - "https://esm.sh/string-width@7.2.0/denonext/string-width.mjs": "329caba194cd7e62a0d893c070d9a88bbc5123e70e7d4076dbf6b0e4a7b925ce", - "https://esm.sh/string-width@7.2.0?target=denonext": "288b87103bb29550239fb06990b1a8081101083e1f5715b28ce93efc4b8f732a", - "https://esm.sh/strip-ansi@6.0.1/denonext/strip-ansi.mjs": "8b3f7024373c01cd27607e74c7607a410d31f91c302d725ba83ba611f5c3e70a", - "https://esm.sh/strip-ansi@6.0.1?target=denonext": "91e030dde3ce1109ebda5c33832f9c96b678aee0d6d0360095eb127b33939976", - "https://esm.sh/strip-ansi@7.1.2/denonext/strip-ansi.mjs": "4a5e18ef24c95f8409c6fd57d79271eef28db274e989f45bbac56bbcd239d30f", - "https://esm.sh/strip-ansi@7.1.2?target=denonext": "8802bcc4e186607cec5d53a5ee276d963aaf2c1aae2cead6bd6f906b7261c30c", - "https://esm.sh/tweetnacl-util@0.15.1": "e0fd14af65295558349a3500af602425f46c54b3af8c48e0c4eaf87371150bbd", - "https://esm.sh/tweetnacl-util@0.15.1/denonext/tweetnacl-util.mjs": "816cc58f87948ec6974d0ebd06112972aadcf03180b5782545a8e0e9031b5e14", - "https://esm.sh/tweetnacl@1.0.3": "278caeeeb136b538b5839595306abfe0ed8e0263a990a446fd30d89aaef9a8c5", - "https://esm.sh/tweetnacl@1.0.3/denonext/tweetnacl.mjs": "71347ea6adf760e1378fe39edf421fcbb2c05c21816e6762c7ae425084143830", - "https://esm.sh/widest-line@5.0.0/denonext/widest-line.mjs": "9fbc109cfe353f84218bcccf4226a3cbf46b432a81337597f9457844a32c94d1", - "https://esm.sh/widest-line@5.0.0?target=denonext": "8c98d5536e51a2c06ebdf6e4c32a7293701f24b16264ed5c84a966f864117c02", - "https://esm.sh/wrap-ansi@9.0.2/denonext/wrap-ansi.mjs": "b162a4bee79e191c104203070dcdc38374981423ff1e18b67601499537125ddb", - "https://esm.sh/wrap-ansi@9.0.2?target=denonext": "ee9c396dbed056d6329fa947f3d93319c3d47712ae13df83786a4662e6979478", - "https://esm.sh/yaml@2.6.1": "85be0b85bd286a20ce4a1210cad1f9f759789401ed600e9582eb296dda21a3b4", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/doc/anchors.mjs": "7ccff982bc18e6bf3b758bd0a21d105f0833a864d839ecafecfcad102b1d0695", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/doc/applyReviver.mjs": "4ea65ce5e1262b855da11b5d1736fe0ef286acb855b86631bf556f62bf727a27", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/doc/createNode.mjs": "73d8f8abe25ec875efa8400bb883d3f8ee4f6b8b9114698fed854830db8158c3", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/log.mjs": "cb5403058dd400f92a72f45b20015d4ac0040bc16a23d27c7029044340e396e2", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/Alias.mjs": "a919b963f01b433a6de6373ce0573c75ee554e80185276c88e10bfc2af8b6491", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/Collection.mjs": "896654d0ed10fb107a9acd20d34d312eea44f3ff8b34a3727eb1a6f345730326", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/Node.mjs": "e37e20535d2e346b79361aacf16c109b4eb16bd27f334a2859bfdb4a000c64ff", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/Pair.mjs": "cf3e91ca9d8ca81211583a39078de2d72743a26c9566b204c239baf0dfaf3405", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/Scalar.mjs": "431c4afa0507e56a926aff327ca984104e173c2ec4a67a206f7cd32dc00c94c6", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/YAMLMap.mjs": "277febadd6f9b056b1bfadf90cb85dc69f04cfbf7108a6e2d7e7420f48ae1839", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/YAMLSeq.mjs": "b96966105a4e5d15b5516f4973bcc043673e86dc7b395827a3a706273de00442", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/addPairToJSMap.mjs": "140d81d273f18af06e4bc7c520f9315d69a80dc9f1862e7e518724eff9d1fd6e", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/identity.mjs": "ce73c067b2d8660ec6ce329b5c0dd9f27510f2f497f1da7e2387a7ab474ffff4", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/nodes/toJS.mjs": "ba35966f54f18765734c78805c2a3ad8bf6fd7b050be4142a1950f28d1946d54", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/schema/common/map.mjs": "322978203af05ebf001ade82943eba0cabd640b1fd0379ef3153349ad895f949", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/schema/common/seq.mjs": "26de5eacefc409937b3b08253ef439d88867d4957e7b26a2fcd129d2057354a0", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/schema/common/string.mjs": "95046acdf5f688f753dc65358099277f8f5ce483dcd828e068e31526d626835d", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/schema/yaml-1.1/merge.mjs": "b1485dcbe162da6934ce194d9b3cef038fe4330ac48199d4ff65802d2cc4a7b2", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/foldFlowLines.mjs": "c65e5a7576fe75963da6fab1f7109015aa2dc10ebc6ac401bcb43adc2c72fe85", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/stringify.mjs": "1adb70440e26de6295594a944bebe06e25c5e9bd148b8fdcbf6dfa7b8c7dc4eb", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/stringifyCollection.mjs": "9f516cd9463fa2ff8fce33fd9b91aa8689c9abb48dae39dffdc86654311e76d8", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/stringifyComment.mjs": "b2bc66df33af45b1f7322ee6f94950cb882df8b68ce33cc42511ff3f28a3a188", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/stringifyNumber.mjs": "386258d11f3b574c84bccfaf7dab684877b6519e3f1b28ca7814b68103723677", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/stringify/stringifyString.mjs": "8d26638256459a4a06a654d1811b4ca764346aa47296616ec8a15a1c65486017", - "https://esm.sh/yaml@2.6.1/denonext/browser/dist/visit.mjs": "d82f6ad92aef02820ed64af0a0bde135bc11fbe2bb270819d225e976b692024f", - "https://esm.sh/yaml@2.6.1/denonext/yaml.mjs": "e61152f5ad0a3b1a242366f80e838ae5d086d5ed4a798c3d535f8d893c97a067", - "https://esm.sh/yn@5.1.0": "e67ac1de66ce16657ff7bb6d95e36221beb5e8da3fbb44a7665a47dbff9d23d9", - "https://esm.sh/yn@5.1.0/denonext/yn.mjs": "73a9e6c587465f586da78e51b5d5b338b8c3255aa50e91264b9971e8ac9477f2" - }, - "workspace": { - "dependencies": [ - "jsr:@std/assert@1.0.11" - ], - "packageJson": { - "dependencies": [ - "npm:@commander-js/extra-typings@14", - "npm:@inkjs/ui@2", - "npm:@inquirer/prompts@^5.1.2", - "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", - "npm:async-retry@^1.3.3", - "npm:axios@^1.8.4", - "npm:boxen@^8.0.1", - "npm:chrono-node@^2.9.0", - "npm:cli-progress@^3.12.0", - "npm:cli-table3@0.6.5", - "npm:commander@^14.0.2", - "npm:date-fns@^4.1.0", - "npm:dayjs@^1.11.19", - "npm:dotenv@^16.4.5", - "npm:ink-link@^4.1.0", - "npm:ink-spinner@5", - "npm:ink-testing-library@4", - "npm:ink-text-input@6", - "npm:ink@^5.2.0", - "npm:inquirer@^10.1.2", - "npm:little-date@1", - "npm:ms@^2.1.3", - "npm:node-fetch@^3.3.2", - "npm:openapi-fetch@~0.11.1", - "npm:ora@^8.1.0", - "npm:parse-duration@^2.1.3", - "npm:posthog-node@^4.10.1", - "npm:prettier@^3.5.3", - "npm:react@^18.3.1", - "npm:semver@^7.6.3", - "npm:shescape@^2.1.1", - "npm:tiny-invariant@^1.3.3", - "npm:tweetnacl-util@~0.15.1", - "npm:tweetnacl@^1.0.3", - "npm:yaml@2.6.1" - ] - } - } -} From d0b6c28ca656dd85c4aa33dc5d39bfe23240f4e4 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:22:18 -0800 Subject: [PATCH 09/48] refactor: update release script to use pkg instead of deno compile - Replace deno compile with npx pkg for binary compilation - Map pkg targets to original output names for backward compatibility: - node20-linux-x64 -> sf-x86_64-unknown-linux-gnu - node20-linux-arm64 -> sf-aarch64-unknown-linux-gnu - node20-macos-x64 -> sf-x86_64-apple-darwin - node20-macos-arm64 -> sf-aarch64-apple-darwin - Replace Deno.Command with child_process.spawnSync - Replace Deno.readTextFile/writeTextFile with fs.readFileSync/writeFileSync Part of Deno to Node.js migration --- src/scripts/release.ts | 126 +++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 69 deletions(-) diff --git a/src/scripts/release.ts b/src/scripts/release.ts index ff3346ce..5fecd66c 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -1,6 +1,8 @@ -import { Argument, Command } from "@commander-js/extra-typings"; +import { spawnSync } from "node:child_process"; import * as console from "node:console"; +import * as fs from "node:fs"; import process from "node:process"; +import { Argument, Command } from "@commander-js/extra-typings"; const program = new Command(); @@ -17,7 +19,7 @@ function bumpVersion( Number.parseInt( // Remove everything after the - if there is one v.includes("-") ? v.split("-")[0] : v, - ) + ), ); switch (type) { case "major": @@ -34,76 +36,74 @@ function bumpVersion( } async function getLocalVersion() { - const packageJson = await Deno.readTextFile("package.json"); + const packageJson = fs.readFileSync("package.json", "utf-8"); return JSON.parse(packageJson).version; } async function saveVersion(version: string) { - const packageJson = await Deno.readTextFile("package.json"); + const packageJson = fs.readFileSync("package.json", "utf-8"); const packageObj = JSON.parse(packageJson); packageObj.version = version; // Ensure exactly one newline at the end of the file - await Deno.writeTextFile( - "package.json", - `${JSON.stringify(packageObj, null, 2)}\n`, - ); + fs.writeFileSync("package.json", `${JSON.stringify(packageObj, null, 2)}\n`); } -const COMPILE_TARGETS: string[] = [ - "x86_64-unknown-linux-gnu", - "aarch64-unknown-linux-gnu", - "x86_64-apple-darwin", - "aarch64-apple-darwin", +// Map pkg targets to output names (keeping original names for backward compatibility) +const COMPILE_TARGETS: { pkgTarget: string; outputName: string }[] = [ + { pkgTarget: "node20-linux-x64", outputName: "x86_64-unknown-linux-gnu" }, + { pkgTarget: "node20-linux-arm64", outputName: "aarch64-unknown-linux-gnu" }, + { pkgTarget: "node20-macos-x64", outputName: "x86_64-apple-darwin" }, + { pkgTarget: "node20-macos-arm64", outputName: "aarch64-apple-darwin" }, ]; async function compileDistribution() { - for (const target of COMPILE_TARGETS) { - const result = await new Deno.Command("deno", { - args: [ - "compile", - "-A", - "--target", - target, - "--output", - `dist/sf-${target}`, - "./src/index.ts", - ], - }).output(); - - if (!result.success) { - console.error(new TextDecoder().decode(result.stderr)); - logAndError(`Failed to compile for ${target}`); + // Create dist directory + fs.mkdirSync("./dist", { recursive: true }); + + for (const { pkgTarget, outputName } of COMPILE_TARGETS) { + const result = spawnSync("npx", [ + "pkg", + ".", + "--target", + pkgTarget, + "--output", + `dist/sf-${outputName}`, + ]); + + if (result.status !== 0) { + console.error(result.stderr?.toString() ?? ""); + logAndError(`Failed to compile for ${pkgTarget}`); } - console.log(`✅ Compiled for ${target}`); - - const zipFileName = `dist/sf-${target}.zip`; - const zipResult = await new Deno.Command("zip", { - args: ["-j", zipFileName, `dist/sf-${target}`], - }).output(); - - if (!zipResult.success) { - console.error(zipResult.stderr); - logAndError(`Failed to zip the binary for ${target}`); + console.log(`✅ Compiled for ${outputName}`); + + const zipFileName = `dist/sf-${outputName}.zip`; + const zipResult = spawnSync("zip", [ + "-j", + zipFileName, + `dist/sf-${outputName}`, + ]); + + if (zipResult.status !== 0) { + console.error(zipResult.stderr?.toString() ?? ""); + logAndError(`Failed to zip the binary for ${outputName}`); } - console.log(`✅ Zipped binary for ${target}`); + console.log(`✅ Zipped binary for ${outputName}`); } } async function asyncSpawn(cmds: string[]) { console.log("cmds", cmds); - const result = await new Deno.Command(cmds[0], { - args: cmds.slice(1), - }).output(); + const result = spawnSync(cmds[0], cmds.slice(1)); return { - exitCode: result.success ? 0 : 1, + exitCode: result.status ?? 1, }; } async function createRelease(version: string) { // Verify zip files are valid before creating release - const distFiles = Array.from(Deno.readDirSync("./dist")); + const distFiles = fs.readdirSync("./dist", { withFileTypes: true }); const zipFiles = distFiles - .filter((entry) => entry.isFile) + .filter((entry) => entry.isFile()) .filter((entry) => entry.name.endsWith(".zip")) .map((entry) => `./dist/${entry.name}`); @@ -111,11 +111,9 @@ async function createRelease(version: string) { // Verify each zip file is valid for (const zipFile of zipFiles) { - const verifyResult = await new Deno.Command("unzip", { - args: ["-t", zipFile], - }).output(); + const verifyResult = spawnSync("unzip", ["-t", zipFile]); - if (!verifyResult.success) { + if (verifyResult.status !== 0) { logAndError(`Invalid zip file: ${zipFile}`); } console.log(`✅ Verified zip file: ${zipFile}`); @@ -170,13 +168,7 @@ async function createRelease(version: string) { } async function cleanDist() { - try { - await Deno.remove("./dist", { recursive: true }); - } catch (error) { - if (!(error instanceof Deno.errors.NotFound)) { - throw error; - } - } + fs.rmSync("./dist", { recursive: true, force: true }); } program @@ -185,22 +177,18 @@ program "A github release tool for the project. Valid types are: major, minor, patch, prerelease", ) .addArgument( - new Argument("type").choices( - [ - "major", - "minor", - "patch", - "prerelease", - ] as const, - ), + new Argument("type").choices([ + "major", + "minor", + "patch", + "prerelease", + ] as const), ) .action(async (type) => { try { - const ghCheckResult = await new Deno.Command("which", { - args: ["gh"], - }).output(); + const ghCheckResult = spawnSync("which", ["gh"]); - if (!ghCheckResult.success) { + if (ghCheckResult.status !== 0) { console.error( `The 'gh' command is not installed. Please install it. From 6ba0621e251e50d830f85ae73e1577d39100c49e Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:22:31 -0800 Subject: [PATCH 10/48] fix: add type assertions for fetch response typing - Remove unused @ts-expect-error directives after migration - Add explicit type assertions for response.json() calls - Fix 'unknown' type errors by adding proper type casts Files: me.ts, orders/index.tsx, tokens.ts, posthog.ts, vm/replace.ts --- src/lib/me.ts | 5 ++--- src/lib/orders/index.tsx | 11 ++++------- src/lib/posthog.ts | 6 +----- src/lib/vm/replace.ts | 12 ++++-------- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/lib/me.ts b/src/lib/me.ts index 855422cb..6c8e85de 100644 --- a/src/lib/me.ts +++ b/src/lib/me.ts @@ -1,5 +1,5 @@ -import type { Command } from "@commander-js/extra-typings"; import * as console from "node:console"; +import type { Command } from "@commander-js/extra-typings"; import { isLoggedIn, loadConfig } from "../helpers/config.ts"; import { logAndQuit, @@ -51,8 +51,7 @@ export async function getLoggedInAccountId(tokenOverride?: string) { logAndQuit("Failed to fetch account info"); } - const data = await response.json(); + const data = (await response.json()) as { id: string }; - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. return data.id; } diff --git a/src/lib/orders/index.tsx b/src/lib/orders/index.tsx index 3bdf53d7..9505d027 100644 --- a/src/lib/orders/index.tsx +++ b/src/lib/orders/index.tsx @@ -1,10 +1,9 @@ -import { type Command, Option } from "@commander-js/extra-typings"; -import { render } from "ink"; import * as console from "node:console"; +import { type Command, Option } from "@commander-js/extra-typings"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; -import React from "react"; +import { render } from "ink"; import { getAuthToken, isLoggedIn } from "../../helpers/config.ts"; import { parseDurationArgument } from "../../helpers/duration.ts"; import { @@ -339,8 +338,7 @@ export async function submitOrderCancellationByIdAction(orderId: string) { return await logSessionTokenExpiredAndQuit(); } - const error = await response.json(); - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. + const error = (await response.json()) as { code?: string }; switch (error.code) { case "order.not_found": return logAndQuit(`Order ${orderId} not found`); @@ -352,8 +350,7 @@ export async function submitOrderCancellationByIdAction(orderId: string) { } } - const resp = await response.json(); - // @ts-ignore: Deno has narrower types for fetch responses, but we know this code works atm. + const resp = (await response.json()) as { object?: string }; const cancellationSubmitted = resp.object === "pending"; if (!cancellationSubmitted) { return logAndQuit(`Failed to cancel order ${orderId}`); diff --git a/src/lib/posthog.ts b/src/lib/posthog.ts index 42d15313..ee7571b4 100644 --- a/src/lib/posthog.ts +++ b/src/lib/posthog.ts @@ -46,7 +46,6 @@ const trackEvent = ({ Authorization: `Bearer ${config.auth_token}`, }, }); - // deno-lint-ignore no-explicit-any -- Deno has narrower types for fetch responses, but we know this code works atm. const data = (await response.json()) as any; if (data.id) { exchangeAccountId = data.id; @@ -69,10 +68,7 @@ const trackEvent = ({ } }; -type FeatureFlags = - | "procurements" - | "zones" - | "custom-vm-images"; +type FeatureFlags = "procurements" | "zones" | "custom-vm-images"; /** * Checks if a feature is enabled for the current user. diff --git a/src/lib/vm/replace.ts b/src/lib/vm/replace.ts index 12aef296..c718c332 100644 --- a/src/lib/vm/replace.ts +++ b/src/lib/vm/replace.ts @@ -1,6 +1,6 @@ +import process from "node:process"; import { Command } from "@commander-js/extra-typings"; import { confirm } from "@inquirer/prompts"; -import process from "node:process"; import ora from "ora"; import { getAuthToken } from "../../helpers/config.ts"; @@ -17,8 +17,7 @@ const replace = new Command("replace") .action(async (options) => { if (!options.yes) { const replaceConfirmed = await confirm({ - message: - `Are you sure you want to replace VM instance ${options.id}? (You cannot undo this action)`, + message: `Are you sure you want to replace VM instance ${options.id}? (You cannot undo this action)`, default: false, }); if (!replaceConfirmed) { @@ -52,13 +51,10 @@ const replace = new Command("replace") logSupportCTAAndQuit(); } - const { - replaced, - replaced_by, - }: { + const { replaced, replaced_by } = (await response.json()) as { replaced: string; replaced_by: string; - } = await response.json(); + }; if (!replaced || !replaced_by) { loadingSpinner.fail("Invalid API response format"); logSupportCTAAndQuit(); From 4298386996ab654005d25145984ed1d1faed7e06 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:24:11 -0800 Subject: [PATCH 11/48] refactor: update main entry point and error handling - Remove @ts-expect-error directive in index.ts - Update import organization (biome) - Minor error handling improvements --- src/helpers/errors.ts | 2 +- src/index.ts | 15 +++++++-------- src/lib/login.ts | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/helpers/errors.ts b/src/helpers/errors.ts index d3663a52..7ba74e4b 100644 --- a/src/helpers/errors.ts +++ b/src/helpers/errors.ts @@ -1,5 +1,5 @@ -import process from "node:process"; import * as console from "node:console"; +import process from "node:process"; import { clearAuthFromConfig } from "./config.ts"; export function logAndQuit(message: string): never { diff --git a/src/index.ts b/src/index.ts index 5272c947..966a8541 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ #!/usr/bin/env bun -import { Command } from "@commander-js/extra-typings"; import * as console from "node:console"; import os from "node:os"; import process from "node:process"; +import { Command } from "@commander-js/extra-typings"; import pkg from "../package.json" with { type: "json" }; import { checkVersion } from "./checkVersion.ts"; import { loadConfig, saveConfig } from "./helpers/config.ts"; @@ -11,20 +11,20 @@ import { getApiUrl } from "./helpers/urls.ts"; import { getAppBanner } from "./lib/app-banner.ts"; import { registerBalance } from "./lib/balance.ts"; import { registerBuy } from "./lib/buy/index.tsx"; -import { registerExtend } from "./lib/extend/index.tsx"; import { registerContracts } from "./lib/contracts/index.tsx"; import { registerDev } from "./lib/dev.ts"; +import { registerExtend } from "./lib/extend/index.tsx"; import { registerLogin } from "./lib/login.ts"; import { registerMe } from "./lib/me.ts"; +import { registerNodes } from "./lib/nodes/index.ts"; import { registerOrders } from "./lib/orders/index.tsx"; -import { analytics, IS_TRACKING_DISABLED } from "./lib/posthog.ts"; +import { IS_TRACKING_DISABLED, analytics } from "./lib/posthog.ts"; +import { registerScale } from "./lib/scale/index.tsx"; import { registerSell } from "./lib/sell.ts"; import { registerTokens } from "./lib/tokens.ts"; import { registerUpgrade } from "./lib/upgrade.ts"; import { registerVM } from "./lib/vm/index.ts"; -import { registerScale } from "./lib/scale/index.tsx"; import { registerZones } from "./lib/zones.tsx"; -import { registerNodes } from "./lib/nodes/index.ts"; const program = new Command(); @@ -94,7 +94,6 @@ if (IS_TRACKING_DISABLED) { }, }); - // deno-lint-ignore no-explicit-any -- Deno has narrower types for fetch responses, but we know this code works atm. const data = (await response.json()) as any; if (data.id) { exchangeAccountId = data.id; @@ -123,8 +122,8 @@ if (IS_TRACKING_DISABLED) { const nextArg = arr[i + 1]; if (nextArg && !nextArg.startsWith("-")) { (acc as Record)[key] = isNaN( - Number(nextArg), - ) + Number(nextArg), + ) ? nextArg : Number(nextArg); } else { diff --git a/src/lib/login.ts b/src/lib/login.ts index 746427c3..6f903ee2 100644 --- a/src/lib/login.ts +++ b/src/lib/login.ts @@ -1,20 +1,20 @@ -import type { Command } from "@commander-js/extra-typings"; import { exec } from "node:child_process"; import * as console from "node:console"; import process from "node:process"; import { setTimeout } from "node:timers"; +import type { Command } from "@commander-js/extra-typings"; import ora from "ora"; import { saveConfig } from "../helpers/config.ts"; import { clearScreen } from "../helpers/prompt.ts"; import { getWebAppUrl } from "../helpers/urls.ts"; +import { randomInt } from "node:crypto"; // We're using Axios here because there's a bug // where the fetch API in Bun isn't passing the body // through redirects correctly import axios from "axios"; import { clearFeatureFlags } from "../helpers/feature-flags.ts"; import { getLoggedInAccountId } from "./me.ts"; -import { randomInt } from "node:crypto"; export function registerLogin(program: Command) { program From ec7848ec49025dd7e9a6dc895fcb8837de5e3e3f Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:24:18 -0800 Subject: [PATCH 12/48] style: apply biome formatting and import organization Auto-fixed by biome check --write: - Organize imports alphabetically - Fix quote style consistency - Remove unused imports - Standardize formatting --- src/helpers/format-date.ts | 36 +++++----- src/helpers/instance-types-meta.ts | 15 +++-- src/lib/Quote.tsx | 29 ++++----- src/lib/Row.tsx | 2 +- src/lib/contracts/ContractDisplay.tsx | 5 +- src/lib/contracts/index.tsx | 14 ++-- src/lib/nodes/image/index.ts | 4 +- src/lib/nodes/index.ts | 12 ++-- src/lib/orders/OrderDisplay.tsx | 94 ++++++++++++--------------- src/lib/scale/ProcurementDisplay.tsx | 59 ++++++++--------- src/lib/scale/index.tsx | 4 +- src/lib/scale/list.tsx | 38 ++++++----- src/lib/scale/utils.ts | 10 +-- src/lib/sell.ts | 20 +++--- src/lib/vm/logs.ts | 11 ++-- src/lib/vm/script.ts | 4 +- src/lib/vm/ssh.ts | 18 ++--- 17 files changed, 182 insertions(+), 193 deletions(-) diff --git a/src/helpers/format-date.ts b/src/helpers/format-date.ts index befd8135..bb20506d 100644 --- a/src/helpers/format-date.ts +++ b/src/helpers/format-date.ts @@ -5,9 +5,9 @@ import { isSameYear, startOfDay, } from "date-fns"; -import { formatDateRange } from "little-date"; import dayjs, { type Dayjs } from "dayjs"; import utc from "dayjs/plugin/utc"; +import { formatDateRange } from "little-date"; dayjs.extend(utc); @@ -32,25 +32,31 @@ const formatTime = (date: Date, locale?: string): string => { ); }; -const createFormatTime = (locale?: string) => (date: Date): string => - formatTime(date, locale); +const createFormatTime = + (locale?: string) => + (date: Date): string => + formatTime(date, locale); -export const formatDate = (date: Date, { - today = new Date(), - showToday = true, - forceIncludeTime = false, -}: { - showToday?: boolean; - forceIncludeTime?: boolean; - today?: Date; -} = {}): string => { +export const formatDate = ( + date: Date, + { + today = new Date(), + showToday = true, + forceIncludeTime = false, + }: { + showToday?: boolean; + forceIncludeTime?: boolean; + today?: Date; + } = {}, +): string => { const thisYear = isSameYear(date, today); const thisDay = isSameDay(date, today); const formatTimeWithLocale = createFormatTime("en-US"); - const timeSuffix = !isSameMinute(startOfDay(date), date) || forceIncludeTime - ? `, ${formatTimeWithLocale(date)}` - : ""; + const timeSuffix = + !isSameMinute(startOfDay(date), date) || forceIncludeTime + ? `, ${formatTimeWithLocale(date)}` + : ""; const yearSuffix = thisYear ? "" : `, ${format(date, "yyyy")}`; diff --git a/src/helpers/instance-types-meta.ts b/src/helpers/instance-types-meta.ts index bf325892..c0c0e884 100644 --- a/src/helpers/instance-types-meta.ts +++ b/src/helpers/instance-types-meta.ts @@ -1,13 +1,16 @@ -export const InstanceTypeMetadata: Record = { - "h100i": { +export const InstanceTypeMetadata: Record< + string, + { + displayName: string; + } +> = { + h100i: { displayName: "Kubernetes", }, - "h100v": { + h100v: { displayName: "Virtual Machine", }, - "h200ki": { + h200ki: { displayName: "Kubernetes", }, } as const; diff --git a/src/lib/Quote.tsx b/src/lib/Quote.tsx index 2e353a1c..081ab483 100644 --- a/src/lib/Quote.tsx +++ b/src/lib/Quote.tsx @@ -1,4 +1,3 @@ -import React from "react"; import { Box, Text } from "ink"; import { Row } from "./Row.tsx"; import { getPricePerGpuHourFromQuote } from "./buy/index.tsx"; @@ -38,19 +37,19 @@ export default function QuoteDisplay(props: { quote: Quote }) { export type Quote = | { - price: number; - quantity: number; - start_at: string; - end_at: string; - instance_type: string; - zone?: string; - } + price: number; + quantity: number; + start_at: string; + end_at: string; + instance_type: string; + zone?: string; + } | { - price: number; - quantity: number; - start_at: string; - end_at: string; - contract_id: string; - zone?: string; - } + price: number; + quantity: number; + start_at: string; + end_at: string; + contract_id: string; + zone?: string; + } | null; diff --git a/src/lib/Row.tsx b/src/lib/Row.tsx index 4232daa5..ef470cc7 100644 --- a/src/lib/Row.tsx +++ b/src/lib/Row.tsx @@ -1,5 +1,5 @@ import { Box, Text } from "ink"; -import React from "react"; +import type React from "react"; export function Row({ head, diff --git a/src/lib/contracts/ContractDisplay.tsx b/src/lib/contracts/ContractDisplay.tsx index 48708e23..ef1b3760 100644 --- a/src/lib/contracts/ContractDisplay.tsx +++ b/src/lib/contracts/ContractDisplay.tsx @@ -2,7 +2,6 @@ import { Badge } from "@inkjs/ui"; import { Box, Text } from "ink"; import { formatDateRange } from "little-date"; import ms from "ms"; -import * as React from "react"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; @@ -113,9 +112,7 @@ export function ContractDisplay(props: { contract: Contract }) { {typeLabel} {isSupportedType && ( - - ({props.contract.instance_type}) - + ({props.contract.instance_type}) )} diff --git a/src/lib/contracts/index.tsx b/src/lib/contracts/index.tsx index 8e01270a..11d62b68 100644 --- a/src/lib/contracts/index.tsx +++ b/src/lib/contracts/index.tsx @@ -1,7 +1,6 @@ +import * as console from "node:console"; import { Command } from "@commander-js/extra-typings"; import { render } from "ink"; -import * as console from "node:console"; -import React from "react"; import { apiClient } from "../../apiClient.ts"; import { isLoggedIn } from "../../helpers/config.ts"; import { @@ -34,11 +33,9 @@ export function registerContracts(program: Command) { const validStates = ["active", "upcoming", "expired"]; if (!validStates.includes(value.toLowerCase())) { throw new Error( - `Invalid state: ${value}. Valid states are: ${ - validStates.join( - ", ", - ) - }`, + `Invalid state: ${value}. Valid states are: ${validStates.join( + ", ", + )}`, ); } // Convert lowercase input to title case for internal use @@ -68,8 +65,7 @@ async function listContracts( const api = await apiClient(); - const state = showAll ? "All" : (stateFilter as ContractState) || - undefined; + const state = showAll ? "All" : (stateFilter as ContractState) || undefined; const { data, error, response } = await api.GET("/v0/contracts", { params: { diff --git a/src/lib/nodes/image/index.ts b/src/lib/nodes/image/index.ts index ac6590ac..92fc06f5 100644 --- a/src/lib/nodes/image/index.ts +++ b/src/lib/nodes/image/index.ts @@ -1,8 +1,8 @@ import { Command } from "@commander-js/extra-typings"; import { isFeatureEnabled } from "../../posthog.ts"; -import upload from "./upload.ts"; -import show from "./show.tsx"; import list from "./list.tsx"; +import show from "./show.tsx"; +import upload from "./upload.ts"; const image = new Command("images") .alias("os") diff --git a/src/lib/nodes/index.ts b/src/lib/nodes/index.ts index 9dcfd76e..29c3e186 100644 --- a/src/lib/nodes/index.ts +++ b/src/lib/nodes/index.ts @@ -1,17 +1,17 @@ -import { type Command } from "@commander-js/extra-typings"; import console from "node:console"; +import type { Command } from "@commander-js/extra-typings"; import { addCreate } from "./create.ts"; -import list from "./list.tsx"; -import release from "./release.ts"; import deleteCommand from "./delete.ts"; -import set from "./set.ts"; import extend from "./extend.ts"; import get from "./get.tsx"; +import { addImage } from "./image/index.ts"; +import list from "./list.tsx"; +import logs from "./logs.ts"; import { addRedeploy } from "./redeploy.ts"; +import release from "./release.ts"; +import set from "./set.ts"; import ssh from "./ssh.ts"; -import logs from "./logs.ts"; -import { addImage } from "./image/index.ts"; export async function registerNodes(program: Command) { const nodes = program diff --git a/src/lib/orders/OrderDisplay.tsx b/src/lib/orders/OrderDisplay.tsx index 5d75b3c1..d6958772 100644 --- a/src/lib/orders/OrderDisplay.tsx +++ b/src/lib/orders/OrderDisplay.tsx @@ -1,6 +1,6 @@ -import { Box, measureElement, Text, useInput } from "ink"; import process from "node:process"; import dayjs from "dayjs"; +import { Box, Text, measureElement, useInput } from "ink"; import React, { useEffect } from "react"; import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; @@ -10,8 +10,8 @@ import type { HydratedOrder } from "./types.ts"; export function orderDetails(order: HydratedOrder) { const duration = dayjs(order.end_at).diff(order.start_at); const durationInHours = duration === 0 ? 1 : duration / 1000 / 60 / 60; - const pricePerGPUHour = order.price / - (order.quantity * durationInHours * GPUS_PER_NODE) / 100; + const pricePerGPUHour = + order.price / (order.quantity * durationInHours * GPUS_PER_NODE) / 100; const durationFormatted = formatDuration(duration); const executedPriceDollarsPerGPUHour = @@ -94,23 +94,21 @@ function OrderMinimal(props: { {executedPriceDollarsPerGPUHour && - executedPriceDollarsPerGPUHour.toFixed(2) !== - pricePerGPUHour.toFixed(2) - ? ( - <> - - ${pricePerGPUHour.toFixed(2)} - /gpu/hr - - ${executedPriceDollarsPerGPUHour.toFixed(2)} - - ) - : ( - + executedPriceDollarsPerGPUHour.toFixed(2) !== + pricePerGPUHour.toFixed(2) ? ( + <> + ${pricePerGPUHour.toFixed(2)} /gpu/hr - )} + ${executedPriceDollarsPerGPUHour.toFixed(2)} + + ) : ( + + ${pricePerGPUHour.toFixed(2)} + /gpu/hr + + )} @@ -188,17 +186,17 @@ export function OrderDisplay(props: { ); } - const orders = activeTab === "all" - ? props.orders - : props.orders.filter((order) => order.side === activeTab); + const orders = + activeTab === "all" + ? props.orders + : props.orders.filter((order) => order.side === activeTab); const { sellOrdersCount, buyOrdersCount } = React.useMemo(() => { return { sellOrdersCount: props.orders.filter((order) => order.side === "sell") .length, - buyOrdersCount: props.orders.filter((order) => - order.side === "buy" - ).length, + buyOrdersCount: props.orders.filter((order) => order.side === "buy") + .length, }; }, [props.orders]); @@ -212,22 +210,17 @@ export function OrderDisplay(props: { buyOrdersCount={buyOrdersCount} > {orders.map((order) => { - return props.expanded - ? - : ( - - ); + return props.expanded ? ( + + ) : ( + + ); })} {orders.length === 0 && ( - There are 0 outstanding {activeTab === "all" ? "" : activeTab} - {" "} + There are 0 outstanding {activeTab === "all" ? "" : activeTab}{" "} orders right now. @@ -341,23 +334,22 @@ export function ScrollArea({ const innerRef = React.useRef(null); const canScrollUp = state.scrollTop > 0 && orders.length > 0; const numberOfOrdersAboveScrollArea = state.scrollTop; - const dateRangeAboveScrollArea = orders.length > 0 - ? `${formatDateTime(orders[0].start_at)} → ${ - formatDateTime( - orders[numberOfOrdersAboveScrollArea - 1]?.end_at || "0", - ) - }` - : ""; - const numberOfOrdersBelowScrollArea = orders.length - - (state.scrollTop + state.height); - const dateRangeBelowScrollArea = orders.length > 0 - ? `${ - formatDateTime( - orders[state.scrollTop + state.height]?.start_at || "0", - ) - } → ${formatDateTime(orders[orders.length - 1].end_at)}` - : ""; - const canScrollDown = state.scrollTop + state.height < state.innerHeight && + const dateRangeAboveScrollArea = + orders.length > 0 + ? `${formatDateTime(orders[0].start_at)} → ${formatDateTime( + orders[numberOfOrdersAboveScrollArea - 1]?.end_at || "0", + )}` + : ""; + const numberOfOrdersBelowScrollArea = + orders.length - (state.scrollTop + state.height); + const dateRangeBelowScrollArea = + orders.length > 0 + ? `${formatDateTime( + orders[state.scrollTop + state.height]?.start_at || "0", + )} → ${formatDateTime(orders[orders.length - 1].end_at)}` + : ""; + const canScrollDown = + state.scrollTop + state.height < state.innerHeight && numberOfOrdersBelowScrollArea >= 0; useEffect(() => { diff --git a/src/lib/scale/ProcurementDisplay.tsx b/src/lib/scale/ProcurementDisplay.tsx index 58898aa2..6e22b7a5 100644 --- a/src/lib/scale/ProcurementDisplay.tsx +++ b/src/lib/scale/ProcurementDisplay.tsx @@ -1,6 +1,5 @@ -import React from "react"; -import { Box, Text } from "ink"; import { Badge } from "@inkjs/ui"; +import { Box, Text } from "ink"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; @@ -8,9 +7,13 @@ import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; import { formatDuration } from "../orders/index.tsx"; -import { formatColocationStrategy, Procurement } from "./utils.ts"; +import { type Procurement, formatColocationStrategy } from "./utils.ts"; -export function ProcurementHeader({ id, quantity, status }: { +export function ProcurementHeader({ + id, + quantity, + status, +}: { id: string; quantity: number; status: Procurement["status"]; @@ -19,32 +22,30 @@ export function ProcurementHeader({ id, quantity, status }: { return ( - {isActive - ? Active - : Disabled} + {isActive ? ( + Active + ) : ( + Disabled + )} - - {id} - + {id} ); } -export default function ProcurementDisplay( - { - procurement: { - id, - instance_type, - status, - desired_quantity, - buy_limit_price_per_gpu_hour, - horizon, - colocation_strategy, - }, - }: { procurement: Procurement }, -) { +export default function ProcurementDisplay({ + procurement: { + id, + instance_type, + status, + desired_quantity, + buy_limit_price_per_gpu_hour, + horizon, + colocation_strategy, + }, +}: { procurement: Procurement }) { const horizonMinutes = horizon; const quantity = desired_quantity * GPUS_PER_NODE; const pricePerGpuHourInCents = buy_limit_price_per_gpu_hour; @@ -59,16 +60,16 @@ export default function ProcurementDisplay( {typeLabel} - - ({instance_type}) - + ({instance_type}) + ) : ( + instance_type ) - : instance_type} + } /> { const parsedValue = Number(val); if ( - Number.isNaN(parsedValue) || !Number.isInteger(parsedValue) || + Number.isNaN(parsedValue) || + !Number.isInteger(parsedValue) || parsedValue <= 0 ) { throw new CommanderError( diff --git a/src/lib/vm/script.ts b/src/lib/vm/script.ts index b29ddbcc..c208c387 100644 --- a/src/lib/vm/script.ts +++ b/src/lib/vm/script.ts @@ -1,6 +1,6 @@ -import { Command } from "@commander-js/extra-typings"; -import { readFileSync } from "node:fs"; import console from "node:console"; +import { readFileSync } from "node:fs"; +import { Command } from "@commander-js/extra-typings"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit, diff --git a/src/lib/vm/ssh.ts b/src/lib/vm/ssh.ts index 6f0d1fcf..8ec08614 100644 --- a/src/lib/vm/ssh.ts +++ b/src/lib/vm/ssh.ts @@ -1,14 +1,14 @@ -import type { Command } from "@commander-js/extra-typings"; +import child_process from "node:child_process"; import console from "node:console"; import process from "node:process"; +import type { Command } from "@commander-js/extra-typings"; import { Shescape } from "shescape"; +import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; import { getApiUrl } from "../../helpers/urls.ts"; -import { getAuthToken } from "../../helpers/config.ts"; -import child_process from "node:child_process"; export function registerSsh(program: Command) { program @@ -62,10 +62,12 @@ export function registerSsh(program: Command) { const data = (await response.json()) as { ssh_hostname: string; ssh_port: number; - ssh_host_keys: { - key_type: string; - base64_encoded_key: string; - }[] | undefined; + ssh_host_keys: + | { + key_type: string; + base64_encoded_key: string; + }[] + | undefined; }; const sshHostname = data.ssh_hostname; const sshPort = data.ssh_port; @@ -129,7 +131,7 @@ export function registerSsh(program: Command) { console.log(`Executing (${shell} style output): ${shell_cmd}`); } - // Ideally this would use `@alphahydrae/exec` but `deno compile` doesn't + // Ideally this would use `@alphahydrae/exec` but `pkg` doesn't // support ffi modules. const result = child_process.spawnSync(cmd[0], cmd.slice(1), { stdio: "inherit", From c4a2b5e02c2aad6ee2c233b7ade87200ee97d1bc Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:24:30 -0800 Subject: [PATCH 13/48] style: format schema.ts with biome Auto-generated schema file reformatted by biome for consistency. No functional changes. --- src/schema.ts | 12743 ++++++++++++++++++++++++------------------------ 1 file changed, 6463 insertions(+), 6280 deletions(-) diff --git a/src/schema.ts b/src/schema.ts index 11047958..3dea346c 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -4,6420 +4,6603 @@ */ export interface paths { - "/v0/balance": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The balance of the account. */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountBalance"]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/me": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description The authenticated account */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_Account"]; - }; - }; - /** @description Account not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/tokens": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: { - include_system?: boolean; - origin_client?: "cli" | "web" | "manual"; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List tokens */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_ListTokenResponse"]; - }; - }; - /** @description Account not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; - }; - }; - }; - }; - put?: never; - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: { - content: { - "application/json": { - /** - * @description Number of seconds until token expires. - * @example 604800 - */ - expires_in_seconds: number; - /** @description Name of the token. */ - name?: string; - /** @description Description of the token. */ - description?: string; - /** @enum {string} */ - origin_client: "cli"; - }; - }; - }; - responses: { - /** @description Token created successfully */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_Token"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_MaxTokenLimitReached"]; - }; - }; - /** @description Authentication required */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; - }; - }; - /** @description Account is frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountFrozenError"]; - }; - }; - /** @description Token not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"] | components["schemas"]["San_Francisco_Compute_Documentation_InvalidTokenCreateOriginClient"]; - }; - }; - /** @description Token generation rate limit exceeded */ - 429: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_MaxTokenLimitReached"]; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/tokens/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - delete: { - parameters: { - query?: never; - header?: never; - path: { - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Token deleted */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": { - success: boolean; - }; - }; - }; - /** @description Token or account not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"] | components["schemas"]["San_Francisco_Compute_Documentation_TokenNotFound"]; - }; - }; - }; - }; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/transactions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: { - starting_after?: string; - ending_before?: string; - limit?: string; - transaction_type?: ("buy" | "sell" | "transfer" | "error" | "instant_deposit" | "deposit_ach" | "withdrawal_bank" | "refund" | "admin_ledger_transfer" | "fulfillment_escrow_release" | "promotional_credit" | "delivery_fees_collected") | ("buy" | "sell" | "transfer" | "error" | "instant_deposit" | "deposit_ach" | "withdrawal_bank" | "refund" | "admin_ledger_transfer" | "fulfillment_escrow_release" | "promotional_credit" | "delivery_fees_collected")[]; - account_kind?: components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"] | components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"][]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of transactions for the account */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountTransactions"]; - }; - }; - }; - }; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/credentials": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ListResponse"]; - }; - }; - }; - }; - put?: never; - post: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Credential details */ - requestBody: { - content: { - "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_types.KubernetesCredentialBody"]; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.BaseCredentialResponse"]; - }; - }; - }; - }; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/credentials/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - delete: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Credential ID (format: cred_) */ - id: string; - }; - cookie?: never; - }; - requestBody?: { - content: { - "application/json": Record; - }; - }; - responses: { - /** @description OK */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.DeleteResponse"]; - }; - }; - }; - }; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/vms/instances": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_vms_instances"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/vms/logs2": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_vms_logs2"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/vms/replace": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post: operations["post_vms_replace"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/vms/script": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_vms_user_data"]; - put?: never; - post: operations["post_vms_user_data"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/vms/ssh": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_vms_ssh"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/vms/images": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List Images - * @description List all VM Images for the authenticated account - */ - get: operations["list_images"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/vms/images/start_upload": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post: operations["start_image_upload"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/vms/images/{image_id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Download Image - * @description Get the download URL for a VM image by ID - */ - get: operations["download_image"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/vms/images/{image_id}/complete_upload": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put: operations["complete_image_upload"]; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/vms/images/{image_id}/upload": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post: operations["create_image_upload_url"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/zones": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List zones - * @description List all available zones - */ - get: operations["list_zones_handler"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/zones/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get zone by ID - * @description Get detailed information about a specific zone - */ - get: operations["get_zone_handler"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/_healthz": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Health check - * @description Health check endpoint - */ - get: operations["health_handler"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/nodes": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List nodes - * @description List all VM nodes for the authenticated account - */ - get: operations["list_nodes_handler"]; - put?: never; - /** - * Create nodes - * @description Create VM nodes - */ - post: operations["create_nodes_handler"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/nodes/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get Node - * @description Retrieve details of a specific node by its ID or name - */ - get: operations["get_node_handler"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/nodes/{id}/extend": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - /** - * Extend node reservation - * @description Purchase additional time to extend the end time of a reserved VM node - */ - patch: operations["extend_node_handler"]; - trace?: never; - }; - "/v1/nodes/{id}/release": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - /** - * Release node - * @description Release an auto reserved VM node from its procurement, reducing the procurement's desired quantity by 1 - */ - patch: operations["release_node_handler"]; - trace?: never; + "/v0/balance": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - "/v0/clusters": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List Kubernetes clusters - * @description List all Kubernetes clusters - */ - get: operations["listClusters"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/contracts": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List contracts - * @description List contracts - */ - get: operations["listContracts"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/contracts/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get contract - * @description Get a contract - */ - get: operations["getContract"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/orders": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List Orders - * @description List active and historical orders - */ - get: operations["listOrders"]; - put?: never; - /** Create a new order */ - post: operations["createOrder"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/orders/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** Get order details */ - get: operations["getOrder"]; - put?: never; - post?: never; - /** Cancel an order */ - delete: operations["cancelOrder"]; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/orders/{id}/clusters": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get order clusters - * @description Get clusters associated with this order. This can currently canonly be one, but might in the future be multiple. - */ - get: operations["getOrderClusters"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/procurements": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * List procurements - * @description List all procurements - */ - get: operations["listProcurements"]; - put?: never; - /** - * Create procurement - * @description Create a procurement. A procurement tries to continuously buy small blocks of compute over and over again. Advantage being that you might be able to get a cheaper price than a longer term reservation. Disadvantage being that you can never be sure if you're nodes will spin up and down as market prices fluctuate. - */ - post: operations["createProcurement"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/procurements/{id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get procurement - * @description Get procurements state and details - */ - get: operations["getProcurement"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - /** - * Update procurement - * @description Update an existing procurement. - */ - patch: operations["updateProcurement"]; - trace?: never; + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The balance of the account. */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountBalance"]; + }; + }; + }; }; - "/v0/refunds": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get refunds - * @description Get status of refunds - */ - get: operations["getRefunds"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/account/me": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** - * Get current account information - * @description Retrieves detailed information about the currently authenticated account. - * - * This endpoint returns: - * - Account ID - * - Account status and permissions (role, KYC level, frozen status) - * - Account metadata (creation date, waitlist status) - * - * Use this endpoint to: - * - Display user profile information - * - Check account permissions and limits - * - Verify KYC status before performing restricted operations - * - Determine feature availability based on account role - */ - get: operations["getAccountMe"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/balances": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description The authenticated account */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_Account"]; + }; + }; + /** @description Account not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/tokens": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: { + include_system?: boolean; + origin_client?: "cli" | "web" | "manual"; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List tokens */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_ListTokenResponse"]; + }; + }; + /** @description Account not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; + }; + }; + }; + }; + put?: never; + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": { + /** + * @description Number of seconds until token expires. + * @example 604800 + */ + expires_in_seconds: number; + /** @description Name of the token. */ + name?: string; + /** @description Description of the token. */ + description?: string; + /** @enum {string} */ + origin_client: "cli"; + }; + }; + }; + responses: { + /** @description Token created successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_Token"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_MaxTokenLimitReached"]; + }; + }; + /** @description Authentication required */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"]; + }; + }; + /** @description Account is frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountFrozenError"]; + }; + }; + /** @description Token not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": + | components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"] + | components["schemas"]["San_Francisco_Compute_Documentation_InvalidTokenCreateOriginClient"]; + }; + }; + /** @description Token generation rate limit exceeded */ + 429: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_MaxTokenLimitReached"]; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/tokens/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: { + parameters: { + query?: never; + header?: never; + path: { + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Token deleted */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + success: boolean; + }; + }; + }; + /** @description Token or account not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": + | components["schemas"]["San_Francisco_Compute_Documentation_AccountNotFoundError"] + | components["schemas"]["San_Francisco_Compute_Documentation_TokenNotFound"]; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/transactions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: { + starting_after?: string; + ending_before?: string; + limit?: string; + transaction_type?: + | ( + | "buy" + | "sell" + | "transfer" + | "error" + | "instant_deposit" + | "deposit_ach" + | "withdrawal_bank" + | "refund" + | "admin_ledger_transfer" + | "fulfillment_escrow_release" + | "promotional_credit" + | "delivery_fees_collected" + ) + | ( + | "buy" + | "sell" + | "transfer" + | "error" + | "instant_deposit" + | "deposit_ach" + | "withdrawal_bank" + | "refund" + | "admin_ledger_transfer" + | "fulfillment_escrow_release" + | "promotional_credit" + | "delivery_fees_collected" + )[]; + account_kind?: + | components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"] + | components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"][]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of transactions for the account */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["San_Francisco_Compute_Documentation_AccountTransactions"]; + }; + }; + }; + }; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/credentials": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ListResponse"]; + }; + }; + }; + }; + put?: never; + post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** @description Credential details */ + requestBody: { + content: { + "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_types.KubernetesCredentialBody"]; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.BaseCredentialResponse"]; + }; + }; + }; + }; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/credentials/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Credential ID (format: cred_) */ + id: string; + }; + cookie?: never; + }; + requestBody?: { + content: { + "application/json": Record; + }; + }; + responses: { + /** @description OK */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.DeleteResponse"]; + }; + }; + }; + }; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/vms/instances": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_vms_instances"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/vms/logs2": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_vms_logs2"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/vms/replace": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["post_vms_replace"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/vms/script": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_vms_user_data"]; + put?: never; + post: operations["post_vms_user_data"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/vms/ssh": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_vms_ssh"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/vms/images": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List Images + * @description List all VM Images for the authenticated account + */ + get: operations["list_images"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/vms/images/start_upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["start_image_upload"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/vms/images/{image_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Download Image + * @description Get the download URL for a VM image by ID + */ + get: operations["download_image"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/vms/images/{image_id}/complete_upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put: operations["complete_image_upload"]; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/vms/images/{image_id}/upload": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["create_image_upload_url"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/zones": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List zones + * @description List all available zones + */ + get: operations["list_zones_handler"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/zones/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get zone by ID + * @description Get detailed information about a specific zone + */ + get: operations["get_zone_handler"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/_healthz": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Health check + * @description Health check endpoint + */ + get: operations["health_handler"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/nodes": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List nodes + * @description List all VM nodes for the authenticated account + */ + get: operations["list_nodes_handler"]; + put?: never; + /** + * Create nodes + * @description Create VM nodes + */ + post: operations["create_nodes_handler"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/nodes/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get Node + * @description Retrieve details of a specific node by its ID or name + */ + get: operations["get_node_handler"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/nodes/{id}/extend": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * Extend node reservation + * @description Purchase additional time to extend the end time of a reserved VM node + */ + patch: operations["extend_node_handler"]; + trace?: never; + }; + "/v1/nodes/{id}/release": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * Release node + * @description Release an auto reserved VM node from its procurement, reducing the procurement's desired quantity by 1 + */ + patch: operations["release_node_handler"]; + trace?: never; + }; + "/v0/clusters": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List Kubernetes clusters + * @description List all Kubernetes clusters + */ + get: operations["listClusters"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/contracts": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List contracts + * @description List contracts + */ + get: operations["listContracts"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/contracts/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get contract + * @description Get a contract + */ + get: operations["getContract"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/orders": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List Orders + * @description List active and historical orders + */ + get: operations["listOrders"]; + put?: never; + /** Create a new order */ + post: operations["createOrder"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/orders/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Get order details */ + get: operations["getOrder"]; + put?: never; + post?: never; + /** Cancel an order */ + delete: operations["cancelOrder"]; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/orders/{id}/clusters": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get order clusters + * @description Get clusters associated with this order. This can currently canonly be one, but might in the future be multiple. + */ + get: operations["getOrderClusters"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/procurements": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * List procurements + * @description List all procurements + */ + get: operations["listProcurements"]; + put?: never; + /** + * Create procurement + * @description Create a procurement. A procurement tries to continuously buy small blocks of compute over and over again. Advantage being that you might be able to get a cheaper price than a longer term reservation. Disadvantage being that you can never be sure if you're nodes will spin up and down as market prices fluctuate. + */ + post: operations["createProcurement"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/procurements/{id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get procurement + * @description Get procurements state and details + */ + get: operations["getProcurement"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + /** + * Update procurement + * @description Update an existing procurement. + */ + patch: operations["updateProcurement"]; + trace?: never; + }; + "/v0/refunds": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get refunds + * @description Get status of refunds + */ + get: operations["getRefunds"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/account/me": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get current account information + * @description Retrieves detailed information about the currently authenticated account. + * + * This endpoint returns: + * - Account ID + * - Account status and permissions (role, KYC level, frozen status) + * - Account metadata (creation date, waitlist status) + * + * Use this endpoint to: + * - Display user profile information + * - Check account permissions and limits + * - Verify KYC status before performing restricted operations + * - Determine feature availability based on account role + */ + get: operations["getAccountMe"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/balances": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get current account balance + * @description Retrieves the current account balance. + */ + get: operations["getBalance"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/credits": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get current credit balance + * @description Retrieves the current credit balance for the authenticated user's account. + * + * This endpoint returns: + * - The current balance in cents + * - The available prepaid credits in cents + * - The amount due for the next billing period in cents + * + */ + get: operations["getCredits"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/credits/migrate": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Migrate account to new billing system + * @description Migrates your account from the legacy billing system to the new credit-based billing system. + * + * This endpoint supports two migration options: + * - **Withdraw**: Transfer your account balance to an external bank account + * - **Convert to Credits**: Convert your existing balance to credits for use in the new system + * + * The migration process will: + * - Check your current account balance + * - Verify that your account is eligible for automatic migration + * - Process the migration according to your chosen option + * + * **Note**: Some accounts with complex balance states may require manual migration assistance. In such cases, the response will indicate `requires_manual_migration` and our support team will help you complete the process. + */ + post: operations["migrateAccount"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/transactions": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Get transactions + * @description Lists transaction history. Provide either `starting_after_cursor` or `ending_before_cursor` (not both) for cursor-based pagination. + */ + get: operations["getTransactions"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/batches": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["list_batches"]; + put?: never; + /** Create a batch job. */ + post: operations["create_batches"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/batches/{batch_id}": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_batch"]; + put?: never; + post: operations["archive_batch"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/batches/{batch_id}/cancel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + post: operations["cancel_batch"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["health_check"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/models": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_models"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/inference/models/{model_id}/history": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["get_model_history"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v0/quote": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get: operations["handle_quote"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + San_Francisco_Compute_Documentation_AccountBalance: { + /** + * @example balance + * @enum {string} + */ + object: "balance"; + available: { /** - * Get current account balance - * @description Retrieves the current account balance. + * @description Funds available to spend or withdraw. + * @example 1000000 */ - get: operations["getBalance"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/credits": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; + amount: number; /** - * Get current credit balance - * @description Retrieves the current credit balance for the authenticated user's account. - * - * This endpoint returns: - * - The current balance in cents - * - The available prepaid credits in cents - * - The amount due for the next billing period in cents - * + * @example usd + * @enum {string} */ - get: operations["getCredits"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/credits/migrate": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; + currency: "usd"; + }; + reserved: { /** - * Migrate account to new billing system - * @description Migrates your account from the legacy billing system to the new credit-based billing system. - * - * This endpoint supports two migration options: - * - **Withdraw**: Transfer your account balance to an external bank account - * - **Convert to Credits**: Convert your existing balance to credits for use in the new system - * - * The migration process will: - * - Check your current account balance - * - Verify that your account is eligible for automatic migration - * - Process the migration according to your chosen option - * - * **Note**: Some accounts with complex balance states may require manual migration assistance. In such cases, the response will indicate `requires_manual_migration` and our support team will help you complete the process. + * @description Funds held in reserve for pending withdrawals & open buy orders. + * @example 1000000 */ - post: operations["migrateAccount"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/transactions": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; + amount: number; /** - * Get transactions - * @description Lists transaction history. Provide either `starting_after_cursor` or `ending_before_cursor` (not both) for cursor-based pagination. + * @example usd + * @enum {string} */ - get: operations["getTransactions"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/batches": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["list_batches"]; - put?: never; - /** Create a batch job. */ - post: operations["create_batches"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/batches/{batch_id}": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_batch"]; - put?: never; - post: operations["archive_batch"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/batches/{batch_id}/cancel": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get?: never; - put?: never; - post: operations["cancel_batch"]; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/health": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["health_check"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/models": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_models"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v1/inference/models/{model_id}/history": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["get_model_history"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; - }; - "/v0/quote": { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - get: operations["handle_quote"]; - put?: never; - post?: never; - delete?: never; - options?: never; - head?: never; - patch?: never; - trace?: never; + currency: "usd"; + }; }; -} -export type webhooks = Record; -export interface components { - schemas: { - San_Francisco_Compute_Documentation_AccountBalance: { - /** - * @example balance - * @enum {string} - */ - object: "balance"; - available: { - /** - * @description Funds available to spend or withdraw. - * @example 1000000 - */ - amount: number; - /** - * @example usd - * @enum {string} - */ - currency: "usd"; - }; - reserved: { - /** - * @description Funds held in reserve for pending withdrawals & open buy orders. - * @example 1000000 - */ - amount: number; - /** - * @example usd - * @enum {string} - */ - currency: "usd"; - }; - }; - San_Francisco_Compute_Documentation_Account: { - /** - * @example account - * @enum {string} - */ - object: "account"; - /** @example acc_123 */ - id: string; - /** - * @example user - * @enum {string} - */ - role: "admin" | "user" | "vendor" | "clops" | "sfcd"; - /** @example true */ - can_buy: boolean; - /** @example false */ - can_sell: boolean; - }; - San_Francisco_Compute_Documentation_AccountNotFoundError: { - /** - * @example error - * @enum {string} - */ - object: "error"; - /** - * @example account.not_found - * @enum {string} - */ - code: "account.not_found"; - /** @example Account not found */ - message?: string; - /** @example {} */ - details?: { - [key: string]: unknown; - }; - }; - San_Francisco_Compute_Documentation_Token: { - /** - * @description The type of object - * @enum {string} - */ - object: "token"; - id: string; + San_Francisco_Compute_Documentation_Account: { + /** + * @example account + * @enum {string} + */ + object: "account"; + /** @example acc_123 */ + id: string; + /** + * @example user + * @enum {string} + */ + role: "admin" | "user" | "vendor" | "clops" | "sfcd"; + /** @example true */ + can_buy: boolean; + /** @example false */ + can_sell: boolean; + }; + San_Francisco_Compute_Documentation_AccountNotFoundError: { + /** + * @example error + * @enum {string} + */ + object: "error"; + /** + * @example account.not_found + * @enum {string} + */ + code: "account.not_found"; + /** @example Account not found */ + message?: string; + /** @example {} */ + details?: { + [key: string]: unknown; + }; + }; + San_Francisco_Compute_Documentation_Token: { + /** + * @description The type of object + * @enum {string} + */ + object: "token"; + id: string; + /** + * @description Only available after creation + * @example + */ + token?: string; + /** @description Name of the token. */ + name: string | null; + /** @description Description of the token. */ + description: string | null; + is_sandbox: boolean; + /** @description ISO 8601 date string of when token expires (in UTC). */ + last_active_at: string | null; + /** @description ISO 8601 date string of when token expires (in UTC). */ + expires_at: string; + /** @description ISO 8601 date string of when token was created (in UTC). */ + created_at: string; + /** @enum {string|null} */ + origin_client: "cli" | "web" | "manual" | null; + /** @description Whether the token was generated by the system & is being used in the background, or whether a user explicitly created it for their own use. */ + is_system: boolean; + }; + San_Francisco_Compute_Documentation_ListTokenResponse: { + data: components["schemas"]["San_Francisco_Compute_Documentation_Token"][]; + /** + * @example true + * @example false + */ + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + San_Francisco_Compute_Documentation_MaxTokenLimitReached: { + /** + * @example error + * @enum {string} + */ + object: "error"; + /** + * @example token.max_token_limit_reached + * @enum {string} + */ + code: "token.max_token_limit_reached"; + /** @example Max token limit reached */ + message?: string; + /** @example {} */ + details?: { + [key: string]: unknown; + }; + }; + San_Francisco_Compute_Documentation_AccountFrozenError: { + /** + * @example error + * @enum {string} + */ + object: "error"; + /** + * @example account.frozen + * @enum {string} + */ + code: "account.frozen"; + /** @example Account is frozen */ + message?: string; + /** @example {} */ + details?: { + [key: string]: unknown; + }; + }; + San_Francisco_Compute_Documentation_InvalidTokenCreateOriginClient: { + /** + * @example error + * @enum {string} + */ + object: "error"; + /** + * @example token.invalid_token_create_origin_client + * @enum {string} + */ + code: "token.invalid_token_create_origin_client"; + /** @example Invalid token create origin client */ + message?: string; + /** @example {} */ + details?: { + [key: string]: unknown; + }; + }; + San_Francisco_Compute_Documentation_TokenNotFound: { + /** + * @example error + * @enum {string} + */ + object: "error"; + /** + * @example token.not_found + * @enum {string} + */ + code: "token.not_found"; + /** @example Token not found */ + message?: string; + /** @example {} */ + details?: { + [key: string]: unknown; + }; + }; + /** + * @description Whether the transaction is incoming to or outgoing from the account. + * @example incoming + * @example outgoing + * @enum {string} + */ + San_Francisco_Compute_Documentation_TransactionDirection: + | "incoming" + | "outgoing"; + /** + * @description The current status of a transaction. + * @example accepted + * @example rejected + * @enum {string} + */ + San_Francisco_Compute_Documentation_TransactionStatus: + | "accepted" + | "rejected"; + /** + * @description The kind of account involved in the transaction. Each user has one account of each account kind. The account "primary" is the main one used for buy/sell on the platform. + * @example primary + * @example withdrawal_reserve + * @enum {string} + */ + San_Francisco_Compute_Documentation_AccountKind: + | "primary" + | "capital" + | "withdrawal_reserve" + | "fulfillment_escrow" + | "delivery_fee_escrow"; + /** + * @description A transaction representing a movement of funds + * @example { + * "id": "tx_1234567890-4", + * "timestamp": "2024-03-15T00:30:00.000Z", + * "direction": "outgoing", + * "status": "accepted", + * "amount": 5000, + * "account_kind": "primary", + * "balance_before": 70000, + * "balance_after": 65000, + * "metadata": { + * "type": "buy", + * "start_time": "2024-11-05T07:24:24.207Z", + * "end_time": "2024-11-05T09:24:24.207Z", + * "quantity": 2, + * "order_id": "ordr_34905N", + * "instance_type_requirements": { + * "accelerator_types": [ + * "H100" + * ], + * "interconnect_types": [ + * "Infiniband" + * ], + * "regions": [ + * "NorthAmerica" + * ], + * "delivery_type": "K8s" + * } + * } + * } + */ + San_Francisco_Compute_Documentation_Transaction: { + /** @description Unique identifier for the transaction */ + id: string; + /** + * Format: date-time + * @description When the money movement occurred within SFCs system + */ + timestamp: string; + direction: components["schemas"]["San_Francisco_Compute_Documentation_TransactionDirection"]; + status: components["schemas"]["San_Francisco_Compute_Documentation_TransactionStatus"]; + /** @description Transaction amount in cents */ + amount: number; + account_kind: components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"]; + /** @description Account balance before transaction in cents */ + balance_before: number; + /** @description Account balance after transaction in cents */ + balance_after: number; + /** @description Additional transaction-specific details */ + metadata: + | { + /** @enum {string} */ + type: "buy"; + /** @description Requirements specified for the buy order */ + instance_type_requirements: { + /** @description List of allowed accelerator types. */ + accelerator_types?: ("H100" | "H200")[]; + /** @description List of allowed interconnect types. */ + interconnect_types?: ("Infiniband" | "None")[]; + /** @description List of allowed regions. */ + regions?: ( + | "NorthAmerica" + | "AsiaPacific" + | "EuropeMiddleEastAfrica" + )[]; + /** + * @description Delivery method. + * @enum {string} + */ + delivery_type: "K8s" | "VM"; + }; + /** @description Quantity requested in the buy order, in number of nodes (8 gpus) */ + quantity: number; /** - * @description Only available after creation - * @example + * Format: date-time + * @description The buy orders requested start time of the compute */ - token?: string; - /** @description Name of the token. */ - name: string | null; - /** @description Description of the token. */ - description: string | null; - is_sandbox: boolean; - /** @description ISO 8601 date string of when token expires (in UTC). */ - last_active_at: string | null; - /** @description ISO 8601 date string of when token expires (in UTC). */ - expires_at: string; - /** @description ISO 8601 date string of when token was created (in UTC). */ - created_at: string; - /** @enum {string|null} */ - origin_client: "cli" | "web" | "manual" | null; - /** @description Whether the token was generated by the system & is being used in the background, or whether a user explicitly created it for their own use. */ - is_system: boolean; - }; - San_Francisco_Compute_Documentation_ListTokenResponse: { - data: components["schemas"]["San_Francisco_Compute_Documentation_Token"][]; + start_time: string; /** - * @example true - * @example false + * Format: date-time + * @description The buy orders requested end time of compute */ - has_more: boolean; + end_time: string; + /** @description An external ID with prefix and alphanumeric string with underscores */ + order_id: string; + } + | { + /** @enum {string} */ + type: "sell"; + /** @description Quantity that was requested to sell, In number of nodes (8 gpus) */ + quantity: number; /** - * @example list - * @enum {string} + * Format: date-time + * @description The orders requested start time of the compute to sell */ - object: "list"; - }; - San_Francisco_Compute_Documentation_MaxTokenLimitReached: { + start_time: string; /** - * @example error - * @enum {string} + * Format: date-time + * @description The orders requested end time of compute to sell */ - object: "error"; + end_time: string; + /** @description The sell order that was executed. */ + order_id: string; + /** @description The backing contract that was sold from. */ + backing_contract_id: string; + } + | { + /** @enum {string} */ + type: "transfer"; + } + | { + /** @enum {string} */ + type: "error"; + /** @description Best effort type of the transaction without additional metadata, but may be 'unknown' */ + inner_type: string; + /** @description message of what went wrong */ + message?: unknown; + } + | { + /** @enum {string} */ + type: "instant_deposit"; /** - * @example token.max_token_limit_reached - * @enum {string} + * Format: date-time + * @description The time the bank involved marked marked this transaction as completed. */ - code: "token.max_token_limit_reached"; - /** @example Max token limit reached */ - message?: string; - /** @example {} */ - details?: { - [key: string]: unknown; - }; - }; - San_Francisco_Compute_Documentation_AccountFrozenError: { + completed_at: string; /** - * @example error - * @enum {string} + * @description The last 4 digits of the card that was used to perform the payment. + * @example 7302 + * @example 1038 */ - object: "error"; + card_last4: string; /** - * @example account.frozen - * @enum {string} + * @description Brand of card used for payment. + * @example visa + * @example mastercard */ - code: "account.frozen"; - /** @example Account is frozen */ - message?: string; - /** @example {} */ - details?: { - [key: string]: unknown; - }; - }; - San_Francisco_Compute_Documentation_InvalidTokenCreateOriginClient: { + card_brand: string; /** - * @example error - * @enum {string} + * @description The funding type of the card. + * @example credit + * @example debit */ - object: "error"; + card_funding: string; /** - * @example token.invalid_token_create_origin_client - * @enum {string} + * Format: uri + * @description URL for the receipt if available. + * @example https://dashboard.stripe.com/receipts/payment/... */ - code: "token.invalid_token_create_origin_client"; - /** @example Invalid token create origin client */ - message?: string; - /** @example {} */ - details?: { - [key: string]: unknown; - }; - }; - San_Francisco_Compute_Documentation_TokenNotFound: { + receipt_url?: string; + } + | { + /** @enum {string} */ + type: "deposit_ach"; /** - * @example error - * @enum {string} + * Format: date-time + * @description The date the bank withdrawal was initiated. */ - object: "error"; + date_initiated: string; /** - * @example token.not_found - * @enum {string} + * Format: date-time + * @description The date the bank withdrawal was cancelled, if it was cancelled. */ - code: "token.not_found"; - /** @example Token not found */ - message?: string; - /** @example {} */ - details?: { - [key: string]: unknown; - }; - }; - /** - * @description Whether the transaction is incoming to or outgoing from the account. - * @example incoming - * @example outgoing - * @enum {string} - */ - San_Francisco_Compute_Documentation_TransactionDirection: "incoming" | "outgoing"; - /** - * @description The current status of a transaction. - * @example accepted - * @example rejected - * @enum {string} - */ - San_Francisco_Compute_Documentation_TransactionStatus: "accepted" | "rejected"; - /** - * @description The kind of account involved in the transaction. Each user has one account of each account kind. The account "primary" is the main one used for buy/sell on the platform. - * @example primary - * @example withdrawal_reserve - * @enum {string} - */ - San_Francisco_Compute_Documentation_AccountKind: "primary" | "capital" | "withdrawal_reserve" | "fulfillment_escrow" | "delivery_fee_escrow"; - /** - * @description A transaction representing a movement of funds - * @example { - * "id": "tx_1234567890-4", - * "timestamp": "2024-03-15T00:30:00.000Z", - * "direction": "outgoing", - * "status": "accepted", - * "amount": 5000, - * "account_kind": "primary", - * "balance_before": 70000, - * "balance_after": 65000, - * "metadata": { - * "type": "buy", - * "start_time": "2024-11-05T07:24:24.207Z", - * "end_time": "2024-11-05T09:24:24.207Z", - * "quantity": 2, - * "order_id": "ordr_34905N", - * "instance_type_requirements": { - * "accelerator_types": [ - * "H100" - * ], - * "interconnect_types": [ - * "Infiniband" - * ], - * "regions": [ - * "NorthAmerica" - * ], - * "delivery_type": "K8s" - * } - * } - * } - */ - San_Francisco_Compute_Documentation_Transaction: { - /** @description Unique identifier for the transaction */ - id: string; + date_cancelled: string | null; /** * Format: date-time - * @description When the money movement occurred within SFCs system + * @description The date the bank withdrawal settled by the bank, if it was cancelled. This will in most cases be close to the time SFC processed the transaction. */ - timestamp: string; - direction: components["schemas"]["San_Francisco_Compute_Documentation_TransactionDirection"]; - status: components["schemas"]["San_Francisco_Compute_Documentation_TransactionStatus"]; - /** @description Transaction amount in cents */ - amount: number; - account_kind: components["schemas"]["San_Francisco_Compute_Documentation_AccountKind"]; - /** @description Account balance before transaction in cents */ - balance_before: number; - /** @description Account balance after transaction in cents */ - balance_after: number; - /** @description Additional transaction-specific details */ - metadata: { - /** @enum {string} */ - type: "buy"; - /** @description Requirements specified for the buy order */ - instance_type_requirements: { - /** @description List of allowed accelerator types. */ - accelerator_types?: ("H100" | "H200")[]; - /** @description List of allowed interconnect types. */ - interconnect_types?: ("Infiniband" | "None")[]; - /** @description List of allowed regions. */ - regions?: ("NorthAmerica" | "AsiaPacific" | "EuropeMiddleEastAfrica")[]; - /** - * @description Delivery method. - * @enum {string} - */ - delivery_type: "K8s" | "VM"; - }; - /** @description Quantity requested in the buy order, in number of nodes (8 gpus) */ - quantity: number; - /** - * Format: date-time - * @description The buy orders requested start time of the compute - */ - start_time: string; - /** - * Format: date-time - * @description The buy orders requested end time of compute - */ - end_time: string; - /** @description An external ID with prefix and alphanumeric string with underscores */ - order_id: string; - } | { - /** @enum {string} */ - type: "sell"; - /** @description Quantity that was requested to sell, In number of nodes (8 gpus) */ - quantity: number; - /** - * Format: date-time - * @description The orders requested start time of the compute to sell - */ - start_time: string; - /** - * Format: date-time - * @description The orders requested end time of compute to sell - */ - end_time: string; - /** @description The sell order that was executed. */ - order_id: string; - /** @description The backing contract that was sold from. */ - backing_contract_id: string; - } | { - /** @enum {string} */ - type: "transfer"; - } | { - /** @enum {string} */ - type: "error"; - /** @description Best effort type of the transaction without additional metadata, but may be 'unknown' */ - inner_type: string; - /** @description message of what went wrong */ - message?: unknown; - } | { - /** @enum {string} */ - type: "instant_deposit"; - /** - * Format: date-time - * @description The time the bank involved marked marked this transaction as completed. - */ - completed_at: string; - /** - * @description The last 4 digits of the card that was used to perform the payment. - * @example 7302 - * @example 1038 - */ - card_last4: string; - /** - * @description Brand of card used for payment. - * @example visa - * @example mastercard - */ - card_brand: string; - /** - * @description The funding type of the card. - * @example credit - * @example debit - */ - card_funding: string; - /** - * Format: uri - * @description URL for the receipt if available. - * @example https://dashboard.stripe.com/receipts/payment/... - */ - receipt_url?: string; - } | { - /** @enum {string} */ - type: "deposit_ach"; - /** - * Format: date-time - * @description The date the bank withdrawal was initiated. - */ - date_initiated: string; - /** - * Format: date-time - * @description The date the bank withdrawal was cancelled, if it was cancelled. - */ - date_cancelled: string | null; - /** - * Format: date-time - * @description The date the bank withdrawal settled by the bank, if it was cancelled. This will in most cases be close to the time SFC processed the transaction. - */ - date_settled: string | null; - /** - * @description A short description of the account. - * @example TOTAL CHECKING (checking) - */ - account_description: string; - /** - * @description The last 4 digits of the bank account number. - * @example 4902 - * @example 2038 - */ - bank_account_last4: string; - } | { - /** @enum {string} */ - type: "withdrawal_bank"; - /** - * Format: date-time - * @description The date this bank withdrawal was approved. - */ - approved_at: string; - /** - * @description A short description of the account. - * @example TOTAL CHECKING (checking) - */ - account_description: string; - /** - * @description The last 4 digits of the bank account number. - * @example 4902 - * @example 2038 - */ - bank_account_last4: string; - } | { - /** @enum {string} */ - type: "refund"; - /** - * Format: date-time - * @description Start time of experienced downtime. - */ - start_time: string; - /** - * Format: date-time - * @description End time of experienced downtime. - */ - end_time: string; - /** @description In number of nodes (8 gpus) affected during this period. */ - quantity: number; - /** @description The index rate per GPU, in cents, used to calculate the refund amount. */ - refund_cents_per_gpu_hour: number; - /** @description Downtime report ID that resulted in this refund being issued. */ - report_id: string; - /** - * Format: date-time - * @description The time at which SFC approved the refund. - */ - issue_date: string; - } | { - /** @enum {string} */ - type: "admin_ledger_transfer"; - } | { - /** @enum {string} */ - type: "fulfillment_escrow_release"; - } | { - /** @enum {string} */ - type: "promotional_credit"; - } | { - /** @enum {string} */ - type: "delivery_fees_collected"; - }; - }; - /** - * @description List of transactions for an account - * @example { - * "object": "transactions", - * "transactions": [ - * { - * "id": "tx_1234567890-4", - * "timestamp": "2024-03-15T00:30:00.000Z", - * "direction": "outgoing", - * "status": "accepted", - * "amount": 5000, - * "account_kind": "primary", - * "balance_before": 70000, - * "balance_after": 65000, - * "metadata": { - * "type": "buy", - * "start_time": "2024-11-05T07:24:24.207Z", - * "end_time": "2024-11-05T09:24:24.207Z", - * "quantity": 2, - * "order_id": "ordr_34Az95N", - * "instance_type_requirements": { - * "accelerator_types": [ - * "H100" - * ], - * "interconnect_types": [ - * "Infiniband" - * ], - * "regions": [ - * "NorthAmerica" - * ], - * "delivery_type": "K8s" - * } - * } - * } - * ], - * "pagination": { - * "has_more": true, - * "count": 1, - * "oldest_timestamp": "2024-03-15T00:30:00.000Z", - * "newest_timestamp": "2024-03-15T00:30:00.000Z" - * } - * } - */ - San_Francisco_Compute_Documentation_AccountTransactions: { + date_settled: string | null; /** - * @description The type of the response object - * @example transactions - * @enum {string} + * @description A short description of the account. + * @example TOTAL CHECKING (checking) */ - object: "transactions"; - /** @description Transactions for all accounts (see account kind) associated with this user. */ - transactions: components["schemas"]["San_Francisco_Compute_Documentation_Transaction"][]; - /** @description Pagination information */ - pagination: { - /** - * @description Whether there are more transactions available - * @example true - */ - has_more: boolean; - /** - * @description Number of transactions returned - * @example 20 - */ - count: number; - /** - * Format: date-time - * @description Timestamp of the oldest transaction returned, useful for pagination - * @example 2024-03-15T00:30:00.000Z - */ - oldest_timestamp: string | null; - /** - * Format: date-time - * @description Timestamp of the newest transaction returned, useful for pagination - * @example 2024-03-15T00:30:00.000Z - */ - newest_timestamp: string | null; - }; - }; - "SF_Compute_K8s_Orchestration_API_frontend_server.BaseCredentialResponse": { - cluster?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo"]; - id?: string; - label?: string; - object?: string; - pubkey?: string; - username?: string; - }; - "SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo": { - id?: string; - kubernetes_api_url?: string; - kubernetes_ca_cert?: string; - kubernetes_namespace?: string; - name?: string; - object?: string; - }; - "SF_Compute_K8s_Orchestration_API_frontend_server.CredentialResponse": { - cluster?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo"]; - cluster_type?: string; - encrypted_kubeconfig?: string; - encrypted_token?: string; - ephemeral_pubkey?: string; - id?: string; - label?: string; - nonce?: string; - object?: string; - pubkey?: string; - username?: string; - }; - "SF_Compute_K8s_Orchestration_API_frontend_server.DeleteResponse": { - deleted?: boolean; - id?: string; - object?: string; - }; - "SF_Compute_K8s_Orchestration_API_frontend_server.ListResponse": { - data?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.CredentialResponse"][]; - has_more?: boolean; - object?: string; - }; - /** @description Credential request body */ - "SF_Compute_K8s_Orchestration_API_types.KubernetesCredentialBody": { - cluster?: string; - label?: string; - object?: string; - pubkey?: string; - username?: string; - }; - /** @description Request body for completing a multipart upload */ - vmorch_CompleteUploadRequest: { - /** @description SHA256 hash of the uploaded file for integrity verification */ - sha256_hash: string; - }; - /** @description Response body for completing a multipart upload */ - vmorch_CompleteUploadResponse: { - /** @description The image ID */ - image_id: string; - object: components["schemas"]["vmorch_ImageDiscriminator"]; - /** @description Status of the upload verification */ - upload_status: string; - }; - vmorch_GetInstancesResponse: { - data: components["schemas"]["vmorch_VmInstance"][]; - }; - vmorch_GetSshResponse: { - ssh_host_keys?: components["schemas"]["vmorch_SshHostKeyInfo"][] | null; - ssh_hostname: string; - /** Format: u-int16 */ - ssh_port: number; - }; - vmorch_GetUserDataResponse: { - script: components["schemas"]["vmorch_UserData"]; - }; - /** - * @example image - * @enum {string} - */ - vmorch_ImageDiscriminator: "image"; - /** @description Response body for image download presigned URL generation */ - vmorch_ImageDownloadResponse: { - /** @description The presigned URL that can be used to download the image */ - download_url: string; - /** @description Timestamp when the presigned URL expires (RFC 3339 format) */ - expires_at: string; - /** @description The image ID */ - image_id: string; - /** @description Human readable name of the image */ - name: string; + account_description: string; /** - * @example image - * @enum {string} + * @description The last 4 digits of the bank account number. + * @example 4902 + * @example 2038 */ - object: "image"; - /** @description SHA256 hash of the image file for integrity verification */ - sha256_hash: string; - }; - /** @description Response body for individual image info (used in lists) */ - vmorch_ImageInfo: { + bank_account_last4: string; + } + | { + /** @enum {string} */ + type: "withdrawal_bank"; /** - * Format: int64 - * @description Creation timestamp as Unix timestamp in seconds + * Format: date-time + * @description The date this bank withdrawal was approved. */ - created_at: number; - /** @description The image ID */ - image_id: string; - /** @description Client given name of the image */ - name: string; + approved_at: string; /** - * @example image - * @enum {string} + * @description A short description of the account. + * @example TOTAL CHECKING (checking) */ - object: "image"; - /** @description SHA256 hash of the image file for integrity verification */ - sha256_hash?: string | null; - /** @description Upload status of the image */ - upload_status: string; - }; - /** @description Request body for image upload presigned URL generation */ - vmorch_ImageUploadRequest: { + account_description: string; /** - * Format: int32 - * @description part idx (1-based) + * @description The last 4 digits of the bank account number. + * @example 4902 + * @example 2038 */ - part_id: number; - }; - /** @description Response body for image upload presigned URL generation */ - vmorch_ImageUploadResponse: { - /** @description Timestamp when the presigned URL expires (RFC 3339 format) */ - expires_at: string; - /** @description The presigned URL that can be used to upload the image part */ - upload_url: string; - }; - /** @description Response body for listing images */ - vmorch_ListImagesResponse: { - data: components["schemas"]["vmorch_ImageInfo"][]; - has_more: boolean; + bank_account_last4: string; + } + | { + /** @enum {string} */ + type: "refund"; /** - * @example list - * @enum {string} + * Format: date-time + * @description Start time of experienced downtime. */ - object: "list"; - }; - vmorch_PostReplaceRequest: { - vm_id: string; - }; - vmorch_PostReplaceResponse: { - replaced: string; - replaced_by: string; - }; - vmorch_PostUserDataRequest: { - script: components["schemas"]["vmorch_UserData"]; - }; - vmorch_PostUserDataResponse: { - script: components["schemas"]["vmorch_UserData"]; - }; - vmorch_SshHostKeyInfo: { - base64_encoded_key: string; - key_type: string; - }; - /** @description Request body for starting a multipart upload */ - vmorch_StartMultipartUploadRequest: { - /** @description Name of the image file */ - name: string; - }; - /** @description Response body for starting a multipart upload */ - vmorch_StartMultipartUploadResponse: { - /** @description The image ID for the created image */ - image_id: string; - object: components["schemas"]["vmorch_ImageDiscriminator"]; - }; - /** @description if the script is valid utf8 then the response may be in either string, or byte form and the client must handle both */ - vmorch_UserData: string | number[]; - vmorch_VmInstance: { - cluster_id: string; - current_status: string; - id: string; - last_updated_at: string; - }; - vmorch_VmsLogChunk: { - data: number[]; - instance_id: string; - /** Format: u-int32 */ - monotonic_timestamp_nano_sec: number; - /** Format: u-int64 */ - monotonic_timestamp_sec: number; - /** @description In RFC 3339 format */ - realtime_timestamp: string; - /** Format: u-int64 */ - seqnum: number; - }; - vmorch_VmsLogsResponse: { - data: components["schemas"]["vmorch_VmsLogChunk"][]; - }; - /** @enum {string} */ - "node-api_AcceleratorType": "H100" | "H200"; - "node-api_AvailabilityRectangle": { - end_timestamp: components["schemas"]["node-api_UnixEpoch"]; + start_time: string; /** - * Format: u-int64 - * @description The number of nodes available during this time period + * Format: date-time + * @description End time of experienced downtime. */ + end_time: string; + /** @description In number of nodes (8 gpus) affected during this period. */ quantity: number; - start_timestamp: components["schemas"]["node-api_UnixEpoch"]; - }; - "node-api_CreateNodesRequest": { - /** @description User script to be executed during the VM's boot process */ - cloud_init_user_data?: number[]; - /** - * Format: u-int32 - * @example 1 - */ - desired_count: number; - /** - * Format: int64 - * @description End time as Unix timestamp in seconds. - * If provided, end time must be aligned to the hour. - * If not provided, the node will be created as an autoreserved node. - */ - end_at?: number | null; - /** - * @description Custom image ID to use for the VM instances - * @example vmi_1234567890abcdef - */ - image_id?: string; + /** @description The index rate per GPU, in cents, used to calculate the refund amount. */ + refund_cents_per_gpu_hour: number; + /** @description Downtime report ID that resulted in this refund being issued. */ + report_id: string; /** - * Format: int64 - * @description Max price per hour for a node in cents - * @example 1000 - */ - max_price_per_node_hour: number; - /** - * @description Custom node names. - * Names cannot follow the vm_{alpha_numeric_chars} as this is reserved for system-generated IDs. - * Names cannot be numeric strings. - * @example [ - * "cuda-crunch" - * ] - */ - names?: string[]; - /** @default autoreserved */ - node_type: null | components["schemas"]["node-api_NodeType"]; - /** - * Format: int64 - * @description Start time as Unix timestamp in seconds - * @example 1640995200 - */ - start_at?: number | null; - /** - * @description Zone to create the nodes in - * @example hayesvalley + * Format: date-time + * @description The time at which SFC approved the refund. */ - zone: string; - }; - /** @enum {string} */ - "node-api_DeliveryType": "K8s" | "VM"; - "node-api_ErrorContent": { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - type: components["schemas"]["node-api_ErrorType"]; - }; - "node-api_ErrorDetail": { - /** @description Specific error code for this detail */ - code: string; - /** @description The field that caused the error (for validation errors) */ - field?: string | null; - /** @description Detailed error message */ - message: string; - }; - "node-api_ErrorKind": { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "api_error"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "invalid_request_error"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; + issue_date: string; + } + | { /** @enum {string} */ - type: "authentication_error"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; + type: "admin_ledger_transfer"; + } + | { /** @enum {string} */ - type: "idempotency_error"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; + type: "fulfillment_escrow_release"; + } + | { /** @enum {string} */ - type: "conflict"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; + type: "promotional_credit"; + } + | { /** @enum {string} */ - type: "not_found"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "request_timed_out"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "forbidden"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "not_implemented"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "upgrade_required"; - } | { - details?: components["schemas"]["node-api_ErrorDetail"][]; - message: string; - /** @enum {string} */ - type: "payment_required"; - }; - "node-api_ErrorObject": { - error: components["schemas"]["node-api_ErrorContent"]; - }; - /** @enum {string} */ - "node-api_ErrorType": "api_error" | "invalid_request_error" | "authentication_error" | "idempotency_error" | "conflict" | "not_found" | "request_timed_out" | "forbidden" | "not_implemented" | "upgrade_required" | "payment_required" | "service_unavailable"; - "node-api_ExtendNodeRequest": { - /** - * Format: int64 - * @description Duration in seconds to extend the node - * Must be at least 1 hour (3600 seconds) and a multiple of 1 hour. - * @example 7200 - */ - duration_seconds: number; - /** - * Format: int64 - * @description Max price per hour for the extension in cents - * @example 1000 - */ - max_price_per_node_hour: number; - }; - /** @enum {string} */ - "node-api_InterconnectType": "Infiniband" | "None"; - "node-api_ListResponse_Node": { - data: { - /** - * Format: int64 - * @description Creation time as Unix timestamp in seconds - * @example 1640995200 - */ - created_at?: number | null; - /** - * Format: int64 - * @description Deletion time as Unix timestamp in seconds - * @example 1640995200 - */ - deleted_at?: number | null; - /** - * Format: int64 - * @description End time as Unix timestamp in seconds - * @example 1640995200 - */ - end_at?: number | null; - gpu_type: components["schemas"]["node-api_AcceleratorType"]; - /** @example n_b1dc52505c6db142 */ - id: string; - /** - * Format: int64 - * @description Max price per hour you're willing to pay for a node in cents - * @example 1000 - */ - max_price_per_node_hour?: number | null; - /** @example cuda-crunch */ - name: string; - node_type: components["schemas"]["node-api_NodeType"]; - /** @example node */ - object: string; - /** @example sfcompute */ - owner: string; - /** @example proc_b1dc52505c6de142 */ - procurement_id?: string | null; - /** - * Format: int64 - * @description Start time as Unix timestamp in seconds - * @example 1640995200 - */ - start_at?: number | null; - status: components["schemas"]["node-api_Status"]; - /** - * Format: int64 - * @description Last updated time as Unix timestamp in seconds - * @example 1640995200 - */ - updated_at?: number | null; - vms?: null | components["schemas"]["node-api_VmList"]; - /** @example hayesvalley */ - zone?: string | null; - }[]; - /** @example list */ - object: string; - }; - "node-api_ListResponse_ZoneInfo": { - data: { - /** @description The available capacity on this cluster, in the - * shape of consecutive "availability rectangles". */ - available_capacity: components["schemas"]["node-api_AvailabilityRectangle"][]; - delivery_type: components["schemas"]["node-api_DeliveryType"]; - hardware_type: components["schemas"]["node-api_AcceleratorType"]; - interconnect_type: components["schemas"]["node-api_InterconnectType"]; - name: string; - object: string; - region: components["schemas"]["node-api_Region"]; - }[]; - /** @example list */ - object: string; - }; - "node-api_Node": { - /** - * Format: int64 - * @description Creation time as Unix timestamp in seconds - * @example 1640995200 - */ - created_at?: number | null; - /** - * Format: int64 - * @description Deletion time as Unix timestamp in seconds - * @example 1640995200 - */ - deleted_at?: number | null; - /** - * Format: int64 - * @description End time as Unix timestamp in seconds - * @example 1640995200 - */ - end_at?: number | null; - gpu_type: components["schemas"]["node-api_AcceleratorType"]; - /** @example n_b1dc52505c6db142 */ - id: string; - /** - * Format: int64 - * @description Max price per hour you're willing to pay for a node in cents - * @example 1000 - */ - max_price_per_node_hour?: number | null; - /** @example cuda-crunch */ - name: string; - node_type: components["schemas"]["node-api_NodeType"]; - /** @example node */ - object: string; - /** @example sfcompute */ - owner: string; - /** @example proc_b1dc52505c6de142 */ - procurement_id?: string | null; - /** - * Format: int64 - * @description Start time as Unix timestamp in seconds - * @example 1640995200 - */ - start_at?: number | null; - status: components["schemas"]["node-api_Status"]; - /** - * Format: int64 - * @description Last updated time as Unix timestamp in seconds - * @example 1640995200 - */ - updated_at?: number | null; - vms?: null | components["schemas"]["node-api_VmList"]; - /** @example hayesvalley */ - zone?: string | null; - }; - /** @enum {string} */ - "node-api_NodeType": "autoreserved" | "reserved"; - /** @enum {string} */ - "node-api_Region": "NorthAmerica" | "AsiaPacific" | "EuropeMiddleEastAfrica"; + type: "delivery_fees_collected"; + }; + }; + /** + * @description List of transactions for an account + * @example { + * "object": "transactions", + * "transactions": [ + * { + * "id": "tx_1234567890-4", + * "timestamp": "2024-03-15T00:30:00.000Z", + * "direction": "outgoing", + * "status": "accepted", + * "amount": 5000, + * "account_kind": "primary", + * "balance_before": 70000, + * "balance_after": 65000, + * "metadata": { + * "type": "buy", + * "start_time": "2024-11-05T07:24:24.207Z", + * "end_time": "2024-11-05T09:24:24.207Z", + * "quantity": 2, + * "order_id": "ordr_34Az95N", + * "instance_type_requirements": { + * "accelerator_types": [ + * "H100" + * ], + * "interconnect_types": [ + * "Infiniband" + * ], + * "regions": [ + * "NorthAmerica" + * ], + * "delivery_type": "K8s" + * } + * } + * } + * ], + * "pagination": { + * "has_more": true, + * "count": 1, + * "oldest_timestamp": "2024-03-15T00:30:00.000Z", + * "newest_timestamp": "2024-03-15T00:30:00.000Z" + * } + * } + */ + San_Francisco_Compute_Documentation_AccountTransactions: { + /** + * @description The type of the response object + * @example transactions + * @enum {string} + */ + object: "transactions"; + /** @description Transactions for all accounts (see account kind) associated with this user. */ + transactions: components["schemas"]["San_Francisco_Compute_Documentation_Transaction"][]; + /** @description Pagination information */ + pagination: { /** - * @description Node Status - * @enum {string} + * @description Whether there are more transactions available + * @example true */ - "node-api_Status": "pending" | "awaitingcapacity" | "running" | "released" | "terminated" | "deleted" | "failed" | "unknown"; + has_more: boolean; /** - * Format: int64 - * @description Unix timestamp in seconds since epoch + * @description Number of transactions returned + * @example 20 */ - "node-api_UnixEpoch": number; - "node-api_Vm": { - /** - * Format: int64 - * @example 1640995200 - */ - created_at: number; - /** - * Format: int64 - * @example 1640998200 - */ - end_at: number | null; - /** @example vm_myOZZXw4pfcp7H9DQOldd */ - id: string; - /** @example vmi_myOZZXw4pfcp7H9DQOldd */ - image_id?: string | null; - /** @example vm */ - object: string; - /** - * Format: int64 - * @example 1640995200 - */ - start_at: number | null; - status: components["schemas"]["node-api_VmStatus"]; - /** - * Format: int64 - * @example 1640995200 - */ - updated_at: number; - }; - "node-api_VmList": { - data: components["schemas"]["node-api_Vm"][]; - /** @example list */ - object: string; - }; - /** @enum {string} */ - "node-api_VmStatus": "Pending" | "Running" | "Destroyed" | "NodeFailure" | "Unspecified"; - "node-api_ZoneInfo": { - /** @description The available capacity on this cluster, in the - * shape of consecutive "availability rectangles". */ - available_capacity: components["schemas"]["node-api_AvailabilityRectangle"][]; - delivery_type: components["schemas"]["node-api_DeliveryType"]; - hardware_type: components["schemas"]["node-api_AcceleratorType"]; - interconnect_type: components["schemas"]["node-api_InterconnectType"]; - name: string; - object: string; - region: components["schemas"]["node-api_Region"]; - }; - /** @description Response body for the account/me endpoint. */ - "market-api_AccountMeResponse": { - /** - * Format: u-int64 - * @description When the account was created as a epoch unix timestamp - * @example 1749704176 - */ - created_at: number; - /** - * @description The unique identifier for the account - * @example gmail-com-name - */ - id: string; - /** - * @description Whether the account is currently frozen - * @example false - */ - is_frozen: boolean; - /** - * @description The KYC (Know Your Customer) verification level - * @example basic - */ - kyc: string; - /** - * @description Whether the KYC-B form has been submitted - * @example false - */ - kycb_form_submitted: boolean; - /** - * @description The role assigned to this account - * @example user - */ - role: string; - /** - * @description Whether the user has submitted the waitlist form - * @example true - */ - submitted_waitlist: boolean; - /** - * @description Whether the user is on the waitlist - * @example false - */ - waitlist: boolean; - }; - "market-api_AccountRefundsResponse": { - /** - * @example refunds - * @enum {string} - */ - object: "refunds"; - refunds: components["schemas"]["market-api_RefundResponse"][]; - }; - "market-api_CancelOrderResponse": { - object: components["schemas"]["market-api_CancelOrderStatus"]; - }; - /** @enum {string} */ - "market-api_CancelOrderStatus": "pending"; - /** @description string with format '"clus"_{base62_encoded_id}' used for referencing a ClusterId resource. Never user-generated. */ - "market-api_ClusterId": string; - "market-api_ClusterName": string; - "market-api_ColocationStrategy": { - /** @enum {string} */ - type: "anywhere"; - } | { - /** @enum {string} */ - type: "colocate"; - } | { - /** @enum {string} */ - type: "colocate-pinned"; - } | { - cluster_name: components["schemas"]["market-api_ClusterName"]; - /** @enum {string} */ - type: "pinned"; - }; - /** @description string with format '"cont"_{base62_encoded_id}' used for referencing a ContractId resource. Never user-generated. */ - "market-api_ContractId": string; - "market-api_ContractResponse": { - cluster_id?: null | components["schemas"]["market-api_ClusterId"]; - colocate_with?: components["schemas"]["market-api_ContractId"][]; - created_at: components["schemas"]["market-api_ISO8601DateTime"]; - id: components["schemas"]["market-api_ContractId"]; - instance_type: components["schemas"]["market-api_Ticker"]; - /** - * @example contract - * @enum {string} - */ - object: "contract"; - shape: components["schemas"]["market-api_ContractShape"]; - state: components["schemas"]["market-api_ContractState"]; - status: components["schemas"]["market-api_ContractStatus"]; - }; - "market-api_ContractShape": { - intervals: components["schemas"]["market-api_ISO8601DateTime"][]; - quantities: number[]; - }; - /** @enum {string} */ - "market-api_ContractState": "Upcoming" | "Active" | "Expired"; - /** @enum {string} */ - "market-api_ContractStatus": "active" | "pending"; - "market-api_CreateOrderRequest": { - cluster?: null | components["schemas"]["market-api_ClusterName"]; - /** @description A contract to colocate with. This overrides the instance_type if specified. */ - colocate_with?: components["schemas"]["market-api_ContractId"][]; - end_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; - flags?: components["schemas"]["market-api_OrderFlags"]; - instance_type?: null | components["schemas"]["market-api_Ticker"]; - /** - * Format: int64 - * @description Price in cents - * @example 5000 - */ - price: number; - /** - * Format: int32 - * @example 10 - */ - quantity: number; - /** @enum {string} */ - side: "buy"; - start_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; - } | { - contract_id: components["schemas"]["market-api_ContractId"]; - end_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; - flags?: components["schemas"]["market-api_OrderFlags"]; - /** - * Format: int64 - * @description Price in cents - * @example 4500 - */ - price: number; - /** - * Format: int32 - * @example 5 - */ - quantity: number; - reprice?: unknown; - /** @enum {string} */ - side: "sell"; - start_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; - }; - /** @example { - * "object": "order", - * "status": "pending", - * "id": "order_xyz789", - * "idempotency_key": "key_123" - * } */ - "market-api_CreateOrderResponse": { - id: components["schemas"]["market-api_OrderId"]; - /** @example key_123 */ - idempotency_key?: string | null; - /** - * @example order - * @enum {string} - */ - object: "order"; - status: components["schemas"]["market-api_CreateOrderStatus"]; - }; - /** @enum {string} */ - "market-api_CreateOrderStatus": "pending" | "filled" | "cancelled"; - "market-api_CreateProcurementRequest": { - /** - * Format: int32 - * @description Maximum price per GPU hour in cents - * @example 250 - */ - buy_limit_price_per_gpu_hour?: number; - /** @description Colocation strategy for the procurement */ - colocation_strategy?: components["schemas"]["market-api_ColocationStrategy"]; - /** - * Format: int32 - * @description Desired quantity of nodes - * @example 5 - */ - desired_quantity: number; - /** - * Format: int32 - * @description Planning horizon in minutes - * @example 60 - */ - horizon?: number; - /** @description Instance type to procure */ - instance_type: components["schemas"]["market-api_Ticker"]; - /** - * Format: int32 - * @description Minimum price per GPU hour in cents when selling back - * @example 25 - */ - sell_limit_price_per_gpu_hour?: number; - /** @description Procurement status (active or disabled) */ - status?: components["schemas"]["market-api_ProcurementStatus"]; - }; - "market-api_ErrorDetail": { - /** @description Specific error code for this detail */ - code: string; - /** @description The field that caused the error (for validation errors) */ - field?: string | null; - /** @description Detailed error message */ - message: string; - }; - /** @enum {string} */ - "market-api_ErrorType": "api_error" | "invalid_request_error" | "authentication_error" | "idempotency_error" | "conflict" | "not_found" | "request_timed_out" | "forbidden" | "not_implemented" | "upgrade_required" | "payment_required" | "service_unavailable"; + count: number; /** - * @description Response body for getting account balance from the credit ledger. - * @example { - * "object": "balance", - * "available_cents": 150000, - * "current_cents": 180000, - * "current_overage_cents": 0, - * "current_hold_cents": 0, - * "updated_at": 1640995200 - * } + * Format: date-time + * @description Timestamp of the oldest transaction returned, useful for pagination + * @example 2024-03-15T00:30:00.000Z */ - "market-api_GetBalanceResponse": { - /** - * Format: u-int64 - * @description Available balance in cents: sum(credit) - sum(debit) - sum(committed holds) - * @example 150000 - */ - available_balance_cents: number; - /** - * Format: u-int64 - * @description Reserved balance in cents: sum(credit) - sum(debit) - * @example 180000 - */ - current_balance_cents: number; - /** - * Format: u-int64 - * @description Total spend that hasn't been paid for yet, in cents. - * @example 0 - */ - current_overage_cents: number; - /** - * @example balances - * @enum {string} - */ - object: "balances"; - /** - * Format: u-int64 - * @description The maximum amount of overages the account can incur before they are blocked from buying compute. - * @example 0 - */ - overage_limit_cents: number; - /** - * Format: int64 - * @description When the balance was last updated as a unix timestamp - * @example 1640995200 - */ - updated_at: number; - }; + oldest_timestamp: string | null; /** - * @description TODO rename route to "balance"? - * Response body for getting account credits. - * @example { - * "current_balance_cents": 150000, - * "available_credits_cents": 150000, - * "amount_due_next_billing_period_cents": 0 - * } + * Format: date-time + * @description Timestamp of the newest transaction returned, useful for pagination + * @example 2024-03-15T00:30:00.000Z */ - "market-api_GetCreditsResponse": { - /** - * Format: int64 - * @description The amount due for the next billing period in cents (positive means customer owes, 0 or negative means no payment due) - * @example 0 - */ - amount_due_next_billing_period_cents: number; - /** - * Format: int64 - * @description The available prepaid credits in cents (always positive) - * @example 150000 - */ - available_credits_cents: number; - /** - * Format: int64 - * @description The current balance in cents (from customer account) - * @example 150000 - */ - current_balance_cents: number; - }; - /** @description string with format 'txc_base62_encoded_id' used for paginating a query to GET /v1/transactions */ - "market-api_GetTransactionsCursor": string; + newest_timestamp: string | null; + }; + }; + "SF_Compute_K8s_Orchestration_API_frontend_server.BaseCredentialResponse": { + cluster?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo"]; + id?: string; + label?: string; + object?: string; + pubkey?: string; + username?: string; + }; + "SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo": { + id?: string; + kubernetes_api_url?: string; + kubernetes_ca_cert?: string; + kubernetes_namespace?: string; + name?: string; + object?: string; + }; + "SF_Compute_K8s_Orchestration_API_frontend_server.CredentialResponse": { + cluster?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.ClusterInfo"]; + cluster_type?: string; + encrypted_kubeconfig?: string; + encrypted_token?: string; + ephemeral_pubkey?: string; + id?: string; + label?: string; + nonce?: string; + object?: string; + pubkey?: string; + username?: string; + }; + "SF_Compute_K8s_Orchestration_API_frontend_server.DeleteResponse": { + deleted?: boolean; + id?: string; + object?: string; + }; + "SF_Compute_K8s_Orchestration_API_frontend_server.ListResponse": { + data?: components["schemas"]["SF_Compute_K8s_Orchestration_API_frontend_server.CredentialResponse"][]; + has_more?: boolean; + object?: string; + }; + /** @description Credential request body */ + "SF_Compute_K8s_Orchestration_API_types.KubernetesCredentialBody": { + cluster?: string; + label?: string; + object?: string; + pubkey?: string; + username?: string; + }; + /** @description Request body for completing a multipart upload */ + vmorch_CompleteUploadRequest: { + /** @description SHA256 hash of the uploaded file for integrity verification */ + sha256_hash: string; + }; + /** @description Response body for completing a multipart upload */ + vmorch_CompleteUploadResponse: { + /** @description The image ID */ + image_id: string; + object: components["schemas"]["vmorch_ImageDiscriminator"]; + /** @description Status of the upload verification */ + upload_status: string; + }; + vmorch_GetInstancesResponse: { + data: components["schemas"]["vmorch_VmInstance"][]; + }; + vmorch_GetSshResponse: { + ssh_host_keys?: components["schemas"]["vmorch_SshHostKeyInfo"][] | null; + ssh_hostname: string; + /** Format: u-int16 */ + ssh_port: number; + }; + vmorch_GetUserDataResponse: { + script: components["schemas"]["vmorch_UserData"]; + }; + /** + * @example image + * @enum {string} + */ + vmorch_ImageDiscriminator: "image"; + /** @description Response body for image download presigned URL generation */ + vmorch_ImageDownloadResponse: { + /** @description The presigned URL that can be used to download the image */ + download_url: string; + /** @description Timestamp when the presigned URL expires (RFC 3339 format) */ + expires_at: string; + /** @description The image ID */ + image_id: string; + /** @description Human readable name of the image */ + name: string; + /** + * @example image + * @enum {string} + */ + object: "image"; + /** @description SHA256 hash of the image file for integrity verification */ + sha256_hash: string; + }; + /** @description Response body for individual image info (used in lists) */ + vmorch_ImageInfo: { + /** + * Format: int64 + * @description Creation timestamp as Unix timestamp in seconds + */ + created_at: number; + /** @description The image ID */ + image_id: string; + /** @description Client given name of the image */ + name: string; + /** + * @example image + * @enum {string} + */ + object: "image"; + /** @description SHA256 hash of the image file for integrity verification */ + sha256_hash?: string | null; + /** @description Upload status of the image */ + upload_status: string; + }; + /** @description Request body for image upload presigned URL generation */ + vmorch_ImageUploadRequest: { + /** + * Format: int32 + * @description part idx (1-based) + */ + part_id: number; + }; + /** @description Response body for image upload presigned URL generation */ + vmorch_ImageUploadResponse: { + /** @description Timestamp when the presigned URL expires (RFC 3339 format) */ + expires_at: string; + /** @description The presigned URL that can be used to upload the image part */ + upload_url: string; + }; + /** @description Response body for listing images */ + vmorch_ListImagesResponse: { + data: components["schemas"]["vmorch_ImageInfo"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + vmorch_PostReplaceRequest: { + vm_id: string; + }; + vmorch_PostReplaceResponse: { + replaced: string; + replaced_by: string; + }; + vmorch_PostUserDataRequest: { + script: components["schemas"]["vmorch_UserData"]; + }; + vmorch_PostUserDataResponse: { + script: components["schemas"]["vmorch_UserData"]; + }; + vmorch_SshHostKeyInfo: { + base64_encoded_key: string; + key_type: string; + }; + /** @description Request body for starting a multipart upload */ + vmorch_StartMultipartUploadRequest: { + /** @description Name of the image file */ + name: string; + }; + /** @description Response body for starting a multipart upload */ + vmorch_StartMultipartUploadResponse: { + /** @description The image ID for the created image */ + image_id: string; + object: components["schemas"]["vmorch_ImageDiscriminator"]; + }; + /** @description if the script is valid utf8 then the response may be in either string, or byte form and the client must handle both */ + vmorch_UserData: string | number[]; + vmorch_VmInstance: { + cluster_id: string; + current_status: string; + id: string; + last_updated_at: string; + }; + vmorch_VmsLogChunk: { + data: number[]; + instance_id: string; + /** Format: u-int32 */ + monotonic_timestamp_nano_sec: number; + /** Format: u-int64 */ + monotonic_timestamp_sec: number; + /** @description In RFC 3339 format */ + realtime_timestamp: string; + /** Format: u-int64 */ + seqnum: number; + }; + vmorch_VmsLogsResponse: { + data: components["schemas"]["vmorch_VmsLogChunk"][]; + }; + /** @enum {string} */ + "node-api_AcceleratorType": "H100" | "H200"; + "node-api_AvailabilityRectangle": { + end_timestamp: components["schemas"]["node-api_UnixEpoch"]; + /** + * Format: u-int64 + * @description The number of nodes available during this time period + */ + quantity: number; + start_timestamp: components["schemas"]["node-api_UnixEpoch"]; + }; + "node-api_CreateNodesRequest": { + /** @description User script to be executed during the VM's boot process */ + cloud_init_user_data?: number[]; + /** + * Format: u-int32 + * @example 1 + */ + desired_count: number; + /** + * Format: int64 + * @description End time as Unix timestamp in seconds. + * If provided, end time must be aligned to the hour. + * If not provided, the node will be created as an autoreserved node. + */ + end_at?: number | null; + /** + * @description Custom image ID to use for the VM instances + * @example vmi_1234567890abcdef + */ + image_id?: string; + /** + * Format: int64 + * @description Max price per hour for a node in cents + * @example 1000 + */ + max_price_per_node_hour: number; + /** + * @description Custom node names. + * Names cannot follow the vm_{alpha_numeric_chars} as this is reserved for system-generated IDs. + * Names cannot be numeric strings. + * @example [ + * "cuda-crunch" + * ] + */ + names?: string[]; + /** @default autoreserved */ + node_type: null | components["schemas"]["node-api_NodeType"]; + /** + * Format: int64 + * @description Start time as Unix timestamp in seconds + * @example 1640995200 + */ + start_at?: number | null; + /** + * @description Zone to create the nodes in + * @example hayesvalley + */ + zone: string; + }; + /** @enum {string} */ + "node-api_DeliveryType": "K8s" | "VM"; + "node-api_ErrorContent": { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + type: components["schemas"]["node-api_ErrorType"]; + }; + "node-api_ErrorDetail": { + /** @description Specific error code for this detail */ + code: string; + /** @description The field that caused the error (for validation errors) */ + field?: string | null; + /** @description Detailed error message */ + message: string; + }; + "node-api_ErrorKind": + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "api_error"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "invalid_request_error"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "authentication_error"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "idempotency_error"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "conflict"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "not_found"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "request_timed_out"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "forbidden"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "not_implemented"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "upgrade_required"; + } + | { + details?: components["schemas"]["node-api_ErrorDetail"][]; + message: string; + /** @enum {string} */ + type: "payment_required"; + }; + "node-api_ErrorObject": { + error: components["schemas"]["node-api_ErrorContent"]; + }; + /** @enum {string} */ + "node-api_ErrorType": + | "api_error" + | "invalid_request_error" + | "authentication_error" + | "idempotency_error" + | "conflict" + | "not_found" + | "request_timed_out" + | "forbidden" + | "not_implemented" + | "upgrade_required" + | "payment_required" + | "service_unavailable"; + "node-api_ExtendNodeRequest": { + /** + * Format: int64 + * @description Duration in seconds to extend the node + * Must be at least 1 hour (3600 seconds) and a multiple of 1 hour. + * @example 7200 + */ + duration_seconds: number; + /** + * Format: int64 + * @description Max price per hour for the extension in cents + * @example 1000 + */ + max_price_per_node_hour: number; + }; + /** @enum {string} */ + "node-api_InterconnectType": "Infiniband" | "None"; + "node-api_ListResponse_Node": { + data: { /** - * Format: date-time - * @description An ISO 8601 datetime string - * @example 2025-07-11T20:41:37.423Z + * Format: int64 + * @description Creation time as Unix timestamp in seconds + * @example 1640995200 */ - "market-api_ISO8601DateTime": string; - "market-api_KubernetesClusterResponse": { - contract?: null | components["schemas"]["market-api_ContractResponse"]; - /** @example https://cluster.example.com */ - kubernetes_api_url?: string | null; - /** @example -----BEGIN CERTIFICATE-----... */ - kubernetes_ca_cert?: string | null; - /** @example sf-user123 */ - kubernetes_namespace: string; - name: components["schemas"]["market-api_ClusterName"]; - /** - * @example kubernetes_cluster - * @enum {string} - */ - object: "kubernetes_cluster"; - }; - "market-api_ListClustersResponse": { - data: components["schemas"]["market-api_KubernetesClusterResponse"][]; - has_more: boolean; - /** - * @example list - * @enum {string} - */ - object: "list"; - }; - "market-api_ListContractsResponse": { - data: components["schemas"]["market-api_ContractResponse"][]; - has_more: boolean; - /** - * @example list - * @enum {string} - */ - object: "list"; - }; - "market-api_ListOrdersResponse": { - data: components["schemas"]["market-api_OrderResponse"][]; - has_more: boolean; - /** - * @example list - * @enum {string} - */ - object: "list"; - }; - "market-api_ListProcurementsResponse": { - data: components["schemas"]["market-api_ProcurementResponse"][]; - has_more: boolean; - /** - * @example list - * @enum {string} - */ - object: "list"; - }; - "market-api_ListTransactionsResponse": { - data: components["schemas"]["market-api_TransactionResponse"][]; - has_more: boolean; - /** - * @example list - * @enum {string} - */ - object: "list"; - }; + created_at?: number | null; /** - * @description Request body for migrating an account to the new billing system. - * @example { - * "type": "withdraw", - * "column_counterparty_id": "cpty_2n4f8bxg3qj5p6r7s9t1v" - * } + * Format: int64 + * @description Deletion time as Unix timestamp in seconds + * @example 1640995200 */ - "market-api_MigrateAccountRequest": { - /** - * @description The identifier for your external bank account where funds will be transferred - * @example cpty_2n4f8bxg3qj5p6r7s9t1v - */ - column_counterparty_id: string; - /** @enum {string} */ - type: "withdraw"; - } | { - /** @enum {string} */ - type: "convert_to_credits"; - }; + deleted_at?: number | null; /** - * @description Response body for account migration requests. - * @example { - * "status": "migrated" - * } - * @enum {string} + * Format: int64 + * @description End time as Unix timestamp in seconds + * @example 1640995200 */ - "market-api_MigrateAccountResponse": "migrated" | "requires_manual_migration"; + end_at?: number | null; + gpu_type: components["schemas"]["node-api_AcceleratorType"]; + /** @example n_b1dc52505c6db142 */ + id: string; /** - * @description A date/time value that can be either "NOW" or an ISO 8601 datetime string - * @example NOW - * @example 2025-07-11T20:41:37.423Z + * Format: int64 + * @description Max price per hour you're willing to pay for a node in cents + * @example 1000 */ - "market-api_NowOrISO8601DateTime": string; - /** @description Configure more fine grained order behavior. */ - "market-api_OrderFlags": { - /** @description If true, the order will be automatically cancelled if it doesn't - * immediately fill when being placed. */ - ioc?: boolean; - /** @description If true, ignores the set limit price and matches any price that is available. */ - market?: boolean; - /** @description If true, places the order straight into the book without trying to match - * against existing orders. */ - post_only?: boolean; - /** @description If the order start time should be automatically changed to "now" once - * start time < "now", proportionally changing it's limit price to reflect - * it's shorter duration. */ - prorate?: boolean; - }; - /** @description string with format '"ordr"_{base62_encoded_id}' used for referencing a OrderId resource. Never user-generated. */ - "market-api_OrderId": string; - "market-api_OrderResponse": { - cancelled: boolean; - cancelled_at?: null | components["schemas"]["market-api_ISO8601DateTime"]; - cluster?: null | components["schemas"]["market-api_ClusterName"]; - colocate_with?: components["schemas"]["market-api_ContractId"][]; - created_at: components["schemas"]["market-api_ISO8601DateTime"]; - duration: components["schemas"]["market-api_UnixEpoch"]; - end_at: components["schemas"]["market-api_ISO8601DateTime"]; - executed: boolean; - executed_at?: null | components["schemas"]["market-api_ISO8601DateTime"]; - /** Format: int64 */ - execution_price?: number | null; - flags: components["schemas"]["market-api_OrderFlags"]; - id: components["schemas"]["market-api_OrderId"]; - instance_type: components["schemas"]["market-api_Ticker"]; - /** - * @example order - * @enum {string} - */ - object: "order"; - /** - * Format: int64 - * @description Price in cents - * @example 2850000 - */ - price: number; - /** - * Format: int32 - * @example 4 - */ - quantity: number; - rejected: boolean; - rejected_reason?: string | null; - side: components["schemas"]["market-api_OrderSide"]; - start_at: components["schemas"]["market-api_ISO8601DateTime"]; - status: components["schemas"]["market-api_OrderStatus"]; - }; - /** @enum {string} */ - "market-api_OrderSide": "buy" | "sell"; - /** @enum {string} */ - "market-api_OrderStatus": "open" | "filled" | "cancelled" | "rejected"; - /** @enum {string} */ - "market-api_ProcurementMessage": "insufficient_balance" | "running"; - "market-api_ProcurementResponse": { - /** - * Format: int32 - * @example 250 - */ - buy_limit_price_per_gpu_hour: number; - colocation_strategy: components["schemas"]["market-api_ColocationStrategy"]; - /** - * Format: int32 - * @example 1 - */ - desired_quantity: number; - /** - * Format: int32 - * @example 60 - */ - horizon: number; - /** @example proc_W9TRG */ - id: string; - instance_type: components["schemas"]["market-api_Ticker"]; - last_message: components["schemas"]["market-api_ProcurementMessage"]; - /** - * @example procurement - * @enum {string} - */ - object: "procurement"; - /** - * Format: int32 - * @example 25 - */ - sell_limit_price_per_gpu_hour: number; - status: components["schemas"]["market-api_ProcurementStatus"]; - }; - /** @enum {string} */ - "market-api_ProcurementStatus": "active" | "disabled"; - "market-api_RefundResponse": { - memo_amount?: string | null; - refund_timestamp?: null | components["schemas"]["market-api_ISO8601DateTime"]; - report_cluster_id: components["schemas"]["market-api_ClusterId"]; - report_created_at: components["schemas"]["market-api_ISO8601DateTime"]; - report_end_time: components["schemas"]["market-api_ISO8601DateTime"]; - /** Format: int64 */ - report_id: number; - report_memo: string; - /** Format: int32 */ - report_nodes_affected: number; - report_start_time: components["schemas"]["market-api_ISO8601DateTime"]; - status: components["schemas"]["market-api_RefundStatus"]; - }; - /** @enum {string} */ - "market-api_RefundStatus": "in_review" | "approved" | "refunded" | "denied"; - /** @description Standard error response format following API guidelines. */ - "market-api_SerdeErrorProxy": { - /** @description Array of detailed error information when applicable */ - details: components["schemas"]["market-api_ErrorDetail"][]; - /** @description Human-readable error message */ - message: string; - /** @description The error type identifier */ - type: components["schemas"]["market-api_ErrorType"]; - }; + max_price_per_node_hour?: number | null; + /** @example cuda-crunch */ + name: string; + node_type: components["schemas"]["node-api_NodeType"]; + /** @example node */ + object: string; + /** @example sfcompute */ + owner: string; + /** @example proc_b1dc52505c6de142 */ + procurement_id?: string | null; /** - * @description Sort field for listing orders - * @example created_at - * @example start_time - * @enum {string} + * Format: int64 + * @description Start time as Unix timestamp in seconds + * @example 1640995200 */ - "market-api_SortBy": "created_at" | "start_time"; + start_at?: number | null; + status: components["schemas"]["node-api_Status"]; /** - * @description Sort direction for listing orders - * @example ASC - * @example DESC - * @enum {string} + * Format: int64 + * @description Last updated time as Unix timestamp in seconds + * @example 1640995200 + */ + updated_at?: number | null; + vms?: null | components["schemas"]["node-api_VmList"]; + /** @example hayesvalley */ + zone?: string | null; + }[]; + /** @example list */ + object: string; + }; + "node-api_ListResponse_ZoneInfo": { + data: { + /** @description The available capacity on this cluster, in the + * shape of consecutive "availability rectangles". */ + available_capacity: components["schemas"]["node-api_AvailabilityRectangle"][]; + delivery_type: components["schemas"]["node-api_DeliveryType"]; + hardware_type: components["schemas"]["node-api_AcceleratorType"]; + interconnect_type: components["schemas"]["node-api_InterconnectType"]; + name: string; + object: string; + region: components["schemas"]["node-api_Region"]; + }[]; + /** @example list */ + object: string; + }; + "node-api_Node": { + /** + * Format: int64 + * @description Creation time as Unix timestamp in seconds + * @example 1640995200 + */ + created_at?: number | null; + /** + * Format: int64 + * @description Deletion time as Unix timestamp in seconds + * @example 1640995200 + */ + deleted_at?: number | null; + /** + * Format: int64 + * @description End time as Unix timestamp in seconds + * @example 1640995200 + */ + end_at?: number | null; + gpu_type: components["schemas"]["node-api_AcceleratorType"]; + /** @example n_b1dc52505c6db142 */ + id: string; + /** + * Format: int64 + * @description Max price per hour you're willing to pay for a node in cents + * @example 1000 + */ + max_price_per_node_hour?: number | null; + /** @example cuda-crunch */ + name: string; + node_type: components["schemas"]["node-api_NodeType"]; + /** @example node */ + object: string; + /** @example sfcompute */ + owner: string; + /** @example proc_b1dc52505c6de142 */ + procurement_id?: string | null; + /** + * Format: int64 + * @description Start time as Unix timestamp in seconds + * @example 1640995200 + */ + start_at?: number | null; + status: components["schemas"]["node-api_Status"]; + /** + * Format: int64 + * @description Last updated time as Unix timestamp in seconds + * @example 1640995200 + */ + updated_at?: number | null; + vms?: null | components["schemas"]["node-api_VmList"]; + /** @example hayesvalley */ + zone?: string | null; + }; + /** @enum {string} */ + "node-api_NodeType": "autoreserved" | "reserved"; + /** @enum {string} */ + "node-api_Region": + | "NorthAmerica" + | "AsiaPacific" + | "EuropeMiddleEastAfrica"; + /** + * @description Node Status + * @enum {string} + */ + "node-api_Status": + | "pending" + | "awaitingcapacity" + | "running" + | "released" + | "terminated" + | "deleted" + | "failed" + | "unknown"; + /** + * Format: int64 + * @description Unix timestamp in seconds since epoch + */ + "node-api_UnixEpoch": number; + "node-api_Vm": { + /** + * Format: int64 + * @example 1640995200 + */ + created_at: number; + /** + * Format: int64 + * @example 1640998200 + */ + end_at: number | null; + /** @example vm_myOZZXw4pfcp7H9DQOldd */ + id: string; + /** @example vmi_myOZZXw4pfcp7H9DQOldd */ + image_id?: string | null; + /** @example vm */ + object: string; + /** + * Format: int64 + * @example 1640995200 + */ + start_at: number | null; + status: components["schemas"]["node-api_VmStatus"]; + /** + * Format: int64 + * @example 1640995200 + */ + updated_at: number; + }; + "node-api_VmList": { + data: components["schemas"]["node-api_Vm"][]; + /** @example list */ + object: string; + }; + /** @enum {string} */ + "node-api_VmStatus": + | "Pending" + | "Running" + | "Destroyed" + | "NodeFailure" + | "Unspecified"; + "node-api_ZoneInfo": { + /** @description The available capacity on this cluster, in the + * shape of consecutive "availability rectangles". */ + available_capacity: components["schemas"]["node-api_AvailabilityRectangle"][]; + delivery_type: components["schemas"]["node-api_DeliveryType"]; + hardware_type: components["schemas"]["node-api_AcceleratorType"]; + interconnect_type: components["schemas"]["node-api_InterconnectType"]; + name: string; + object: string; + region: components["schemas"]["node-api_Region"]; + }; + /** @description Response body for the account/me endpoint. */ + "market-api_AccountMeResponse": { + /** + * Format: u-int64 + * @description When the account was created as a epoch unix timestamp + * @example 1749704176 + */ + created_at: number; + /** + * @description The unique identifier for the account + * @example gmail-com-name + */ + id: string; + /** + * @description Whether the account is currently frozen + * @example false + */ + is_frozen: boolean; + /** + * @description The KYC (Know Your Customer) verification level + * @example basic + */ + kyc: string; + /** + * @description Whether the KYC-B form has been submitted + * @example false + */ + kycb_form_submitted: boolean; + /** + * @description The role assigned to this account + * @example user + */ + role: string; + /** + * @description Whether the user has submitted the waitlist form + * @example true + */ + submitted_waitlist: boolean; + /** + * @description Whether the user is on the waitlist + * @example false + */ + waitlist: boolean; + }; + "market-api_AccountRefundsResponse": { + /** + * @example refunds + * @enum {string} + */ + object: "refunds"; + refunds: components["schemas"]["market-api_RefundResponse"][]; + }; + "market-api_CancelOrderResponse": { + object: components["schemas"]["market-api_CancelOrderStatus"]; + }; + /** @enum {string} */ + "market-api_CancelOrderStatus": "pending"; + /** @description string with format '"clus"_{base62_encoded_id}' used for referencing a ClusterId resource. Never user-generated. */ + "market-api_ClusterId": string; + "market-api_ClusterName": string; + "market-api_ColocationStrategy": + | { + /** @enum {string} */ + type: "anywhere"; + } + | { + /** @enum {string} */ + type: "colocate"; + } + | { + /** @enum {string} */ + type: "colocate-pinned"; + } + | { + cluster_name: components["schemas"]["market-api_ClusterName"]; + /** @enum {string} */ + type: "pinned"; + }; + /** @description string with format '"cont"_{base62_encoded_id}' used for referencing a ContractId resource. Never user-generated. */ + "market-api_ContractId": string; + "market-api_ContractResponse": { + cluster_id?: null | components["schemas"]["market-api_ClusterId"]; + colocate_with?: components["schemas"]["market-api_ContractId"][]; + created_at: components["schemas"]["market-api_ISO8601DateTime"]; + id: components["schemas"]["market-api_ContractId"]; + instance_type: components["schemas"]["market-api_Ticker"]; + /** + * @example contract + * @enum {string} + */ + object: "contract"; + shape: components["schemas"]["market-api_ContractShape"]; + state: components["schemas"]["market-api_ContractState"]; + status: components["schemas"]["market-api_ContractStatus"]; + }; + "market-api_ContractShape": { + intervals: components["schemas"]["market-api_ISO8601DateTime"][]; + quantities: number[]; + }; + /** @enum {string} */ + "market-api_ContractState": "Upcoming" | "Active" | "Expired"; + /** @enum {string} */ + "market-api_ContractStatus": "active" | "pending"; + "market-api_CreateOrderRequest": + | { + cluster?: null | components["schemas"]["market-api_ClusterName"]; + /** @description A contract to colocate with. This overrides the instance_type if specified. */ + colocate_with?: components["schemas"]["market-api_ContractId"][]; + end_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; + flags?: components["schemas"]["market-api_OrderFlags"]; + instance_type?: null | components["schemas"]["market-api_Ticker"]; + /** + * Format: int64 + * @description Price in cents + * @example 5000 + */ + price: number; + /** + * Format: int32 + * @example 10 + */ + quantity: number; + /** @enum {string} */ + side: "buy"; + start_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; + } + | { + contract_id: components["schemas"]["market-api_ContractId"]; + end_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; + flags?: components["schemas"]["market-api_OrderFlags"]; + /** + * Format: int64 + * @description Price in cents + * @example 4500 + */ + price: number; + /** + * Format: int32 + * @example 5 + */ + quantity: number; + reprice?: unknown; + /** @enum {string} */ + side: "sell"; + start_at: components["schemas"]["market-api_NowOrISO8601DateTime"]; + }; + /** @example { + * "object": "order", + * "status": "pending", + * "id": "order_xyz789", + * "idempotency_key": "key_123" + * } */ + "market-api_CreateOrderResponse": { + id: components["schemas"]["market-api_OrderId"]; + /** @example key_123 */ + idempotency_key?: string | null; + /** + * @example order + * @enum {string} + */ + object: "order"; + status: components["schemas"]["market-api_CreateOrderStatus"]; + }; + /** @enum {string} */ + "market-api_CreateOrderStatus": "pending" | "filled" | "cancelled"; + "market-api_CreateProcurementRequest": { + /** + * Format: int32 + * @description Maximum price per GPU hour in cents + * @example 250 + */ + buy_limit_price_per_gpu_hour?: number; + /** @description Colocation strategy for the procurement */ + colocation_strategy?: components["schemas"]["market-api_ColocationStrategy"]; + /** + * Format: int32 + * @description Desired quantity of nodes + * @example 5 + */ + desired_quantity: number; + /** + * Format: int32 + * @description Planning horizon in minutes + * @example 60 + */ + horizon?: number; + /** @description Instance type to procure */ + instance_type: components["schemas"]["market-api_Ticker"]; + /** + * Format: int32 + * @description Minimum price per GPU hour in cents when selling back + * @example 25 + */ + sell_limit_price_per_gpu_hour?: number; + /** @description Procurement status (active or disabled) */ + status?: components["schemas"]["market-api_ProcurementStatus"]; + }; + "market-api_ErrorDetail": { + /** @description Specific error code for this detail */ + code: string; + /** @description The field that caused the error (for validation errors) */ + field?: string | null; + /** @description Detailed error message */ + message: string; + }; + /** @enum {string} */ + "market-api_ErrorType": + | "api_error" + | "invalid_request_error" + | "authentication_error" + | "idempotency_error" + | "conflict" + | "not_found" + | "request_timed_out" + | "forbidden" + | "not_implemented" + | "upgrade_required" + | "payment_required" + | "service_unavailable"; + /** + * @description Response body for getting account balance from the credit ledger. + * @example { + * "object": "balance", + * "available_cents": 150000, + * "current_cents": 180000, + * "current_overage_cents": 0, + * "current_hold_cents": 0, + * "updated_at": 1640995200 + * } + */ + "market-api_GetBalanceResponse": { + /** + * Format: u-int64 + * @description Available balance in cents: sum(credit) - sum(debit) - sum(committed holds) + * @example 150000 + */ + available_balance_cents: number; + /** + * Format: u-int64 + * @description Reserved balance in cents: sum(credit) - sum(debit) + * @example 180000 + */ + current_balance_cents: number; + /** + * Format: u-int64 + * @description Total spend that hasn't been paid for yet, in cents. + * @example 0 + */ + current_overage_cents: number; + /** + * @example balances + * @enum {string} + */ + object: "balances"; + /** + * Format: u-int64 + * @description The maximum amount of overages the account can incur before they are blocked from buying compute. + * @example 0 + */ + overage_limit_cents: number; + /** + * Format: int64 + * @description When the balance was last updated as a unix timestamp + * @example 1640995200 + */ + updated_at: number; + }; + /** + * @description TODO rename route to "balance"? + * Response body for getting account credits. + * @example { + * "current_balance_cents": 150000, + * "available_credits_cents": 150000, + * "amount_due_next_billing_period_cents": 0 + * } + */ + "market-api_GetCreditsResponse": { + /** + * Format: int64 + * @description The amount due for the next billing period in cents (positive means customer owes, 0 or negative means no payment due) + * @example 0 + */ + amount_due_next_billing_period_cents: number; + /** + * Format: int64 + * @description The available prepaid credits in cents (always positive) + * @example 150000 + */ + available_credits_cents: number; + /** + * Format: int64 + * @description The current balance in cents (from customer account) + * @example 150000 + */ + current_balance_cents: number; + }; + /** @description string with format 'txc_base62_encoded_id' used for paginating a query to GET /v1/transactions */ + "market-api_GetTransactionsCursor": string; + /** + * Format: date-time + * @description An ISO 8601 datetime string + * @example 2025-07-11T20:41:37.423Z + */ + "market-api_ISO8601DateTime": string; + "market-api_KubernetesClusterResponse": { + contract?: null | components["schemas"]["market-api_ContractResponse"]; + /** @example https://cluster.example.com */ + kubernetes_api_url?: string | null; + /** @example -----BEGIN CERTIFICATE-----... */ + kubernetes_ca_cert?: string | null; + /** @example sf-user123 */ + kubernetes_namespace: string; + name: components["schemas"]["market-api_ClusterName"]; + /** + * @example kubernetes_cluster + * @enum {string} + */ + object: "kubernetes_cluster"; + }; + "market-api_ListClustersResponse": { + data: components["schemas"]["market-api_KubernetesClusterResponse"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + "market-api_ListContractsResponse": { + data: components["schemas"]["market-api_ContractResponse"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + "market-api_ListOrdersResponse": { + data: components["schemas"]["market-api_OrderResponse"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + "market-api_ListProcurementsResponse": { + data: components["schemas"]["market-api_ProcurementResponse"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + "market-api_ListTransactionsResponse": { + data: components["schemas"]["market-api_TransactionResponse"][]; + has_more: boolean; + /** + * @example list + * @enum {string} + */ + object: "list"; + }; + /** + * @description Request body for migrating an account to the new billing system. + * @example { + * "type": "withdraw", + * "column_counterparty_id": "cpty_2n4f8bxg3qj5p6r7s9t1v" + * } + */ + "market-api_MigrateAccountRequest": + | { + /** + * @description The identifier for your external bank account where funds will be transferred + * @example cpty_2n4f8bxg3qj5p6r7s9t1v + */ + column_counterparty_id: string; + /** @enum {string} */ + type: "withdraw"; + } + | { + /** @enum {string} */ + type: "convert_to_credits"; + }; + /** + * @description Response body for account migration requests. + * @example { + * "status": "migrated" + * } + * @enum {string} + */ + "market-api_MigrateAccountResponse": + | "migrated" + | "requires_manual_migration"; + /** + * @description A date/time value that can be either "NOW" or an ISO 8601 datetime string + * @example NOW + * @example 2025-07-11T20:41:37.423Z + */ + "market-api_NowOrISO8601DateTime": string; + /** @description Configure more fine grained order behavior. */ + "market-api_OrderFlags": { + /** @description If true, the order will be automatically cancelled if it doesn't + * immediately fill when being placed. */ + ioc?: boolean; + /** @description If true, ignores the set limit price and matches any price that is available. */ + market?: boolean; + /** @description If true, places the order straight into the book without trying to match + * against existing orders. */ + post_only?: boolean; + /** @description If the order start time should be automatically changed to "now" once + * start time < "now", proportionally changing it's limit price to reflect + * it's shorter duration. */ + prorate?: boolean; + }; + /** @description string with format '"ordr"_{base62_encoded_id}' used for referencing a OrderId resource. Never user-generated. */ + "market-api_OrderId": string; + "market-api_OrderResponse": { + cancelled: boolean; + cancelled_at?: null | components["schemas"]["market-api_ISO8601DateTime"]; + cluster?: null | components["schemas"]["market-api_ClusterName"]; + colocate_with?: components["schemas"]["market-api_ContractId"][]; + created_at: components["schemas"]["market-api_ISO8601DateTime"]; + duration: components["schemas"]["market-api_UnixEpoch"]; + end_at: components["schemas"]["market-api_ISO8601DateTime"]; + executed: boolean; + executed_at?: null | components["schemas"]["market-api_ISO8601DateTime"]; + /** Format: int64 */ + execution_price?: number | null; + flags: components["schemas"]["market-api_OrderFlags"]; + id: components["schemas"]["market-api_OrderId"]; + instance_type: components["schemas"]["market-api_Ticker"]; + /** + * @example order + * @enum {string} + */ + object: "order"; + /** + * Format: int64 + * @description Price in cents + * @example 2850000 + */ + price: number; + /** + * Format: int32 + * @example 4 + */ + quantity: number; + rejected: boolean; + rejected_reason?: string | null; + side: components["schemas"]["market-api_OrderSide"]; + start_at: components["schemas"]["market-api_ISO8601DateTime"]; + status: components["schemas"]["market-api_OrderStatus"]; + }; + /** @enum {string} */ + "market-api_OrderSide": "buy" | "sell"; + /** @enum {string} */ + "market-api_OrderStatus": "open" | "filled" | "cancelled" | "rejected"; + /** @enum {string} */ + "market-api_ProcurementMessage": "insufficient_balance" | "running"; + "market-api_ProcurementResponse": { + /** + * Format: int32 + * @example 250 + */ + buy_limit_price_per_gpu_hour: number; + colocation_strategy: components["schemas"]["market-api_ColocationStrategy"]; + /** + * Format: int32 + * @example 1 + */ + desired_quantity: number; + /** + * Format: int32 + * @example 60 + */ + horizon: number; + /** @example proc_W9TRG */ + id: string; + instance_type: components["schemas"]["market-api_Ticker"]; + last_message: components["schemas"]["market-api_ProcurementMessage"]; + /** + * @example procurement + * @enum {string} + */ + object: "procurement"; + /** + * Format: int32 + * @example 25 + */ + sell_limit_price_per_gpu_hour: number; + status: components["schemas"]["market-api_ProcurementStatus"]; + }; + /** @enum {string} */ + "market-api_ProcurementStatus": "active" | "disabled"; + "market-api_RefundResponse": { + memo_amount?: string | null; + refund_timestamp?: + | null + | components["schemas"]["market-api_ISO8601DateTime"]; + report_cluster_id: components["schemas"]["market-api_ClusterId"]; + report_created_at: components["schemas"]["market-api_ISO8601DateTime"]; + report_end_time: components["schemas"]["market-api_ISO8601DateTime"]; + /** Format: int64 */ + report_id: number; + report_memo: string; + /** Format: int32 */ + report_nodes_affected: number; + report_start_time: components["schemas"]["market-api_ISO8601DateTime"]; + status: components["schemas"]["market-api_RefundStatus"]; + }; + /** @enum {string} */ + "market-api_RefundStatus": "in_review" | "approved" | "refunded" | "denied"; + /** @description Standard error response format following API guidelines. */ + "market-api_SerdeErrorProxy": { + /** @description Array of detailed error information when applicable */ + details: components["schemas"]["market-api_ErrorDetail"][]; + /** @description Human-readable error message */ + message: string; + /** @description The error type identifier */ + type: components["schemas"]["market-api_ErrorType"]; + }; + /** + * @description Sort field for listing orders + * @example created_at + * @example start_time + * @enum {string} + */ + "market-api_SortBy": "created_at" | "start_time"; + /** + * @description Sort direction for listing orders + * @example ASC + * @example DESC + * @enum {string} + */ + "market-api_SortDirection": "ASC" | "DESC"; + /** + * @example h100i + * @example h100v + * @example h200ki + */ + "market-api_Ticker": string; + "market-api_TransactionDetails": + | { + card_brand: string; + card_funding: string; + card_last4: string; + /** + * @example transaction_details + * @enum {string} + */ + object: "transaction_details"; + receipt_url: string; + /** @enum {string} */ + type: "stripe_card_payment"; + } + | { + description: string; + /** + * @example transaction_details + * @enum {string} + */ + object: "transaction_details"; + /** @enum {string} */ + type: "manual_payment"; + } + | { + memo: string; + /** + * @example transaction_details + * @enum {string} + */ + object: "transaction_details"; + /** @enum {string} */ + type: "credit_grant"; + } + | { + /** + * @example transaction_details + * @enum {string} + */ + object: "transaction_details"; + /** @enum {string} */ + type: "refund"; + } + | { + /** + * @example transaction_details + * @enum {string} + */ + object: "transaction_details"; + order_id: components["schemas"]["market-api_OrderId"]; + /** @enum {string} */ + type: "buy_order"; + }; + "market-api_TransactionResponse": { + /** + * Format: int64 + * @description Transaction amount in cents (e.g., 50000 = $500.00) + * @example 50000 + */ + amount_cents: number; + /** + * Format: int64 + * @description Balance immediately after this transaction took place, in cents (e.g., 50000 = $500.00). + */ + balance: number; + /** @description Opaque cursor for use in pagination */ + cursor: components["schemas"]["market-api_GetTransactionsCursor"]; + /** @description Transaction details */ + details: components["schemas"]["market-api_TransactionDetails"]; + /** + * @example transaction + * @enum {string} + */ + object: "transaction"; + /** + * Format: int64 + * @description Time the transaction took place as UNIX timestamp in seconds. + * @example 1640995200 + */ + transaction_time: number; + }; + /** + * Format: int64 + * @description Unix timestamp in seconds since epoch + */ + "market-api_UnixEpoch": number; + "market-api_UpdateProcurementRequest": { + /** + * Format: int32 + * @description Maximum price per GPU hour in cents + * @example 350 + */ + buy_limit_price_per_gpu_hour?: number | null; + /** + * Format: int32 + * @description Desired quantity of nodes + * @example 5 + */ + desired_quantity?: number | null; + /** + * Format: int32 + * @description Planning horizon in minutes. The procurement will try to buy compute + * ahead of time as to always have at least this amount of time ahead + * scheduled at a minimum. + * @example 120 + */ + horizon?: number | null; + instance_type?: null | components["schemas"]["market-api_Ticker"]; + /** + * Format: int32 + * @description Minimum price per GPU hour in cents when selling back + * @example 50 + */ + sell_limit_price_per_gpu_hour?: number | null; + status?: null | components["schemas"]["market-api_ProcurementStatus"]; + }; + large_scale_inference_Batch: { + account_id: string; + /** Format: date-time */ + cancelled_at?: string | null; + /** Format: date-time */ + cancelling_at?: string | null; + /** Format: date-time */ + completed_at?: string | null; + completion_window: string; + /** Format: date-time */ + created_at: string; + /** Format: date-time */ + deadline_at: string; + endpoint: string; + errors?: + | null + | components["schemas"]["large_scale_inference_BatchErrors"]; + /** Format: date-time */ + expired_at?: string | null; + /** Format: date-time */ + expires_at?: string | null; + /** Format: date-time */ + failed_at?: string | null; + /** Format: date-time */ + finalizing_at?: string | null; + id: string; + /** Format: date-time */ + in_progress_at?: string | null; + input_file_uri: string; + metadata?: + | null + | components["schemas"]["large_scale_inference_BatchMetadata"]; + model_id: string; + object: string; + output_file_uri: string; + request_counts: components["schemas"]["large_scale_inference_RequestCounts"]; + status: components["schemas"]["large_scale_inference_BatchStatus"]; + }; + large_scale_inference_BatchErrorData: { + code: string; + /** Format: u-int32 */ + line?: number | null; + message: string; + param?: string | null; + }; + large_scale_inference_BatchErrors: { + data: components["schemas"]["large_scale_inference_BatchErrorData"][]; + object: string; + }; + large_scale_inference_BatchMetadata: { + input_token_usage?: string | null; + output_token_usage?: string | null; + }; + large_scale_inference_BatchRequest: { + completion_window: string; + endpoint: string; + input_file_uri: string; + metadata?: + | null + | components["schemas"]["large_scale_inference_BatchMetadata"]; + model_id: string; + output_file_uri: string; + store: string; + }; + large_scale_inference_BatchRequests: + | components["schemas"]["large_scale_inference_BatchRequest"] + | components["schemas"]["large_scale_inference_BatchRequest"][]; + /** @enum {string} */ + large_scale_inference_BatchStatus: + | "accepted" + | "compute_purchased" + | "not_started" + | "started" + | "validating" + | "failed" + | "in_progress" + | "finalizing" + | "completed" + | "expired" + | "cancelling" + | "cancelled" + | "waiting"; + large_scale_inference_BlendedPrice: { + /** Format: double */ + cents_per_million_tokens: number; + }; + large_scale_inference_ErrorDetail: { + /** @description Specific error code for this detail */ + code: string; + /** @description The field that caused the error (for validation errors) */ + field?: string | null; + /** @description Detailed error message */ + message: string; + }; + /** @enum {string} */ + large_scale_inference_ErrorType: + | "api_error" + | "invalid_request_error" + | "authentication_error" + | "idempotency_error" + | "conflict" + | "not_found" + | "request_timed_out" + | "forbidden" + | "not_implemented" + | "upgrade_required" + | "payment_required" + | "service_unavailable"; + large_scale_inference_Model: { + /** Format: date-time */ + created_at: string; + external_reference: string; + id: string; + name: string; + /** Format: date-time */ + retired_at?: string | null; + }; + large_scale_inference_Price: + | { + Blended: components["schemas"]["large_scale_inference_BlendedPrice"]; + } + | { + Specific: components["schemas"]["large_scale_inference_SpecificPrice"]; + }; + large_scale_inference_RequestCounts: { + /** Format: u-int32 */ + completed: number; + /** Format: u-int32 */ + failed: number; + /** Format: u-int32 */ + total: number; + }; + /** @description Standard error response format following API guidelines. */ + large_scale_inference_SerdeErrorProxy: { + /** @description Array of detailed error information when applicable */ + details: components["schemas"]["large_scale_inference_ErrorDetail"][]; + /** @description Human-readable error message */ + message: string; + /** @description The error type identifier */ + type: components["schemas"]["large_scale_inference_ErrorType"]; + }; + large_scale_inference_SpecificPrice: { + /** Format: double */ + cents_per_million_input_tokens: number; + /** Format: double */ + cents_per_million_output_tokens: number; + }; + /** @description Quote details with side-specific fields + * + * The response structure differs based on order side: + * - Buy orders include `instance_type` + * - Sell orders include `contract_id` */ + quoter_ApiQuoteDetails: + | { + /** @description End time: ISO 8601 */ + end_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; + /** @description Instance type being quoted */ + instance_type: string; + /** + * Format: u-int64 + * @description Total price in cents (USD) + */ + price: number; + /** + * Format: u-int64 + * @description Number of nodes + */ + quantity: number; + /** @description Start time: ISO 8601 or "NOW" */ + start_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; + zone: string; + } + | { + /** @description Contract being sold from */ + contract_id: components["schemas"]["quoter_ContractId"]; + /** @description End time: ISO 8601 */ + end_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; + /** + * Format: u-int64 + * @description Total price in cents (USD) + */ + price: number; + /** + * Format: u-int64 + * @description Number of nodes + */ + quantity: number; + /** @description Start time: ISO 8601 or "NOW" */ + start_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; + }; + /** @description Response format for GET /v0/quote + * + * Returns a quote object with side-specific details. + * If no quote is available, the `quote` field will be `None`. */ + quoter_ApiQuoteResponse: { + /** @description Always "quote" */ + object: string; + quote?: null | components["schemas"]["quoter_ApiQuoteDetails"]; + /** @description Matches the requested side: "buy" or "sell" */ + side: string; + }; + /** @description string with format '"cont"_{base62_encoded_id}' used for referencing a ContractId resource. Never user-generated. */ + quoter_ContractId: string; + /** @enum {string} */ + quoter_ErrorType: + | "api_error" + | "invalid_request_error" + | "authentication_error" + | "idempotency_error" + | "conflict" + | "not_found" + | "request_timed_out" + | "forbidden" + | "not_implemented" + | "upgrade_required" + | "payment_required" + | "service_unavailable"; + /** + * @description A date/time value that can be either "NOW" or an ISO 8601 datetime string + * @example NOW + * @example 2025-07-11T20:41:37.423Z + */ + quoter_NowOrISO8601DateTime: string; + /** @description Side-specific parameters for quote requests */ + quoter_OrderSideParams: + | { + /** @description Cluster constraint (optional) - hostname to resolve to cluster_id */ + cluster?: string | null; + colocate_with?: null | components["schemas"]["quoter_ContractId"]; + /** @description Instance type for buy orders: "h100i", "h100v", or "h200ki" */ + instance_type?: string | null; + /** @enum {string} */ + side: "buy"; + } + | { + /** @description Contract ID for sell orders (must be owned by user) */ + contract_id: components["schemas"]["quoter_ContractId"]; + /** @enum {string} */ + side: "sell"; + }; + /** @description Query parameters for GET /v0/quote + * + * # Validation Rules + * + * ## Required Fields + * - `side`: Must be "buy" or "sell" + * - `quantity`: Must be a positive integer (1-1024) + * + * ## Date Constraints + * - `min_start_date` and `max_start_date` accept: + * - "NOW" (literal string for current time) + * - ISO 8601 date string + * - If neither provided, defaults to "NOW" + * - `max_start_date` must be >= `min_start_date` + * + * ## Duration Constraints (mutually exclusive) + * - EITHER provide `duration` (in seconds) + * - OR provide both `min_duration` AND `max_duration` (in seconds) + * - All durations must be positive + * - Maximum duration: 3 years (94,608,000 seconds) + * - When `duration` is provided, it expands to a range: + * - min = duration + * - max = duration + 59 minutes + * + * ## Side-Specific Constraints + * + * ### Buy Orders + * - **Required**: `instance_type` (must be one of: "h100i", "h100v", "h200ki") + * - **Optional**: `colocate_with` (contract ID), `cluster` + * - **Forbidden**: `contract_id` + * + * ### Sell Orders + * - **Required**: `contract_id` (must be owned by authenticated user) + * - **Forbidden**: `instance_type`, `cluster`, `colocate_with` */ + quoter_QuoteRequestParams: components["schemas"]["quoter_OrderSideParams"] & + components["schemas"]["quoter_SpatialQuoteParams"]; + /** @description Spatial parameters for quote requests (time, quantity, duration) */ + quoter_SpatialQuoteParams: { + /** + * Format: u-int64 + * @description Exact duration in seconds (mutually exclusive with min_duration/max_duration) + */ + duration?: number | null; + /** + * Format: u-int64 + * @description Maximum duration in seconds (must be used with min_duration) + */ + max_duration?: number | null; + max_start_date?: + | null + | components["schemas"]["quoter_NowOrISO8601DateTime"]; + /** + * Format: u-int64 + * @description Minimum duration in seconds (must be used with max_duration) + */ + min_duration?: number | null; + min_start_date?: + | null + | components["schemas"]["quoter_NowOrISO8601DateTime"]; + /** + * Format: u-int64 + * @description Number of nodes (1-1024) + */ + quantity: number; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + get_vms_instances: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_GetInstancesResponse"]; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + get_vms_logs2: { + parameters: { + query: { + instance_id: string; + before_seqnum?: number; + since_seqnum?: number; + before_realtime_timestamp?: string; + since_realtime_timestamp?: string; + order_by: "seqnum_asc" | "seqnum_desc"; + limit?: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_VmsLogsResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + post_vms_replace: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["vmorch_PostReplaceRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_PostReplaceResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + get_vms_user_data: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_GetUserDataResponse"]; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + post_vms_user_data: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["vmorch_PostUserDataRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_PostUserDataResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + get_vms_ssh: { + parameters: { + query: { + vm_id: string; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_GetSshResponse"]; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + list_images: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of images */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_ListImagesResponse"]; + }; + }; + /** @description Node API features not enabled */ + 403: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + start_image_upload: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["vmorch_StartMultipartUploadRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_StartMultipartUploadResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 409: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + download_image: { + parameters: { + query?: never; + header?: never; + path: { + /** @description VM image ID */ + image_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_ImageDownloadResponse"]; + }; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 409: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + complete_image_upload: { + parameters: { + query?: never; + header?: never; + path: { + /** @description VM image ID */ + image_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["vmorch_CompleteUploadRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_CompleteUploadResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + create_image_upload_url: { + parameters: { + query?: never; + header?: never; + path: { + /** @description VM image ID */ + image_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["vmorch_ImageUploadRequest"]; + }; + }; + responses: { + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["vmorch_ImageUploadResponse"]; + }; + }; + 400: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 403: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 404: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + 500: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + }; + }; + list_zones_handler: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of zones */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["node-api_ListResponse_ZoneInfo"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token", + * "request_id": "req_1234567890" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error", + * "request_id": "req_1234567890" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + get_zone_handler: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Zone ID */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Zone information */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["node-api_ZoneInfo"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token", + * "request_id": "req_1234567890" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "You do not have permission to get zones" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Zone not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Zone not found", + * "request_id": "req_1234567890" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error", + * "request_id": "req_1234567890" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + health_handler: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Health check successful */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example OK */ + "text/plain": string; + }; + }; + /** @description Health check failed */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example Internal Server Error */ + "text/plain": string; + }; + }; + }; + }; + list_nodes_handler: { + parameters: { + query?: { + /** @description Filter nodes by node_id + * Use ?id=n_b1dc52505c6db142&id=n_b1dc52505c6db133 to specify multiple IDs. + * Cannot be used with name */ + id?: string[]; + /** + * @description Filter nodes by their names + * Use ?name=val1&name=val2 to specify multiple names. + * Cannot be used with id + * @example [ + * "cuda-crunch" + * ] */ - "market-api_SortDirection": "ASC" | "DESC"; + name?: string[]; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of nodes */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "node", + * "id": "n_b1dc52505c6db142", + * "name": "cuda-crunch", + * "zone": "hayesvalley", + * "gpu_type": "H100", + * "owner": "sfcompute", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "procurement_id": "proc_b1dc52505c6de142", + * "max_price_per_node_hour": 1000, + * "node_type": "autoreserved", + * "vms": { + * "object": "list", + * "data": [ + * { + * "object": "vm", + * "id": "vm_myOZZXw4pfcp7H9DQOldd", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641007200 + * } + * ] + * } + * } + * ] + * } */ + "application/json": components["schemas"]["node-api_ListResponse_Node"]; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Invalid query parameters", + * "details": [ + * { + * "field": "node_id|name", + * "code": "invalid_value", + * "message": "Cannot specify both node_id and name parameters" + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "You do not have permission to list nodes" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + create_nodes_handler: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["node-api_CreateNodesRequest"]; + }; + }; + responses: { + /** @description Nodes created successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "node", + * "id": "n_b1dc52505c6db142", + * "name": "cuda-crunch", + * "zone": "hayesvalley", + * "gpu_type": "H100", + * "owner": "sfcompute", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "procurement_id": "proc_b1dc52505c6de142", + * "max_price_per_node_hour": 1000, + * "node_type": "autoreserved" + * } + * ] + * } */ + "application/json": components["schemas"]["node-api_ListResponse_Node"]; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "One or more fields are invalid", + * "details": [ + * { + * "field": "start_at", + * "code": "invalid_value", + * "message": "start_at time must be in the future" + * }, + * { + * "field": "names", + * "code": "invalid_value", + * "message": "node name 'vm_test' cannot follow the vm_id pattern vm_{16_hex_chars} as this prefix is reserved for system-generated IDs" + * }, + * { + * "field": "names", + * "code": "invalid_value", + * "message": "node name cannot be a numeric string" + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "You do not have permission to create nodes" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + get_node_handler: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Node ID or name */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Node details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "node", + * "id": "n_b1dc52505c6db142", + * "name": "cuda-crunch", + * "zone": "hayesvalley", + * "gpu_type": "H100", + * "owner": "sfcompute", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "procurement_id": "proc_b1dc52505c6de142", + * "max_price_per_node_hour": 1000, + * "node_type": "autoreserved", + * "vms": { + * "object": "list", + * "data": [ + * { + * "object": "vm", + * "id": "vm_myOZZXw4pfcp7H9DQOldd", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641007200 + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_Node"]; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Invalid node identifier", + * "details": [ + * { + * "field": "id", + * "code": "invalid_value", + * "message": "Numeric strings are not supported as node identifiers" + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "You do not have permission to access this node" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Node not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Node not found" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + extend_node_handler: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Node ID or name */ + id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["node-api_ExtendNodeRequest"]; + }; + }; + responses: { + /** @description Node extended successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "node", + * "id": "n_b1dc52505c6db142", + * "name": "cuda-crunch", + * "zone": "hayesvalley", + * "gpu_type": "H100", + * "owner": "sfcompute", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641007200, + * "max_price_per_node_hour": 1000, + * "node_type": "reserved", + * "vms": { + * "object": "list", + * "data": [ + * { + * "object": "vm", + * "id": "vm_myOZZXw4pfcp7H9DQOldd", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641007200 + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_Node"]; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Node is Auto Reserved not Reserved - only reservation nodes can be extended", + * "details": [ + * { + * "field": "duration_seconds", + * "code": "invalid_value", + * "message": "duration must be at least 1 hour (3600 seconds)" + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen or node is not associated with this account" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Node not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Node not found" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Extension failed due to capacity */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Extension failed: No capacity available for the requested time period" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + release_node_handler: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Node ID or name */ + id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Node released successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "node", + * "id": "n_b1dc52505c6db142", + * "name": "cuda-crunch", + * "zone": "hayesvalley", + * "gpu_type": "H100", + * "owner": "sfcompute", + * "status": "terminated", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641000000, + * "max_price_per_node_hour": 1000, + * "node_type": "autoreserved", + * "procurement_id": "proc_b1dc52505c6de142", + * "vms": { + * "object": "list", + * "data": [ + * { + * "object": "vm", + * "id": "vm_myOZZXw4pfcp7H9DQOldd", + * "status": "running", + * "created_at": 1640995200, + * "updated_at": 1640995200, + * "start_at": 1640995200, + * "end_at": 1641007200 + * } + * ] + * } + * } */ + "application/json": components["schemas"]["node-api_Node"]; + }; + }; + /** @description Bad request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Node is Reserved not Auto Reserved - only autoreserved nodes can be released" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Unauthorized request */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "Unauthorized: missing or invalid authentication token" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Forbidden */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen or node is not associated with this account" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Node not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Node not found" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "Internal server error" + * } + * } */ + "application/json": components["schemas"]["node-api_ErrorObject"]; + }; + }; + }; + }; + listClusters: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of Kubernetes clusters */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "kubernetes_cluster", + * "name": "starlight", + * "kubernetes_namespace": "sf-user123", + * "kubernetes_api_url": "https://starlight.sf-k8s.com", + * "kubernetes_ca_cert": "-----BEGIN CERTIFICATE-----...", + * "contract": { + * "object": "contract", + * "status": "active", + * "id": "cont_xyz123", + * "created_at": "2024-07-15T22:30:17.426Z", + * "instance_type": "h100i", + * "shape": { + * "intervals": [ + * "2024-07-16T00:00:00Z", + * "2024-07-17T00:00:00Z" + * ], + * "quantities": [ + * 4, + * 0 + * ] + * }, + * "colocate_with": [], + * "cluster_id": "clstr_abc456", + * "state": "Active" + * } + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListClustersResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + listContracts: { + parameters: { + query?: { /** + * @description Filter by instance type * @example h100i - * @example h100v - * @example h200ki */ - "market-api_Ticker": string; - "market-api_TransactionDetails": { - card_brand: string; - card_funding: string; - card_last4: string; - /** - * @example transaction_details - * @enum {string} - */ - object: "transaction_details"; - receipt_url: string; - /** @enum {string} */ - type: "stripe_card_payment"; - } | { - description: string; - /** - * @example transaction_details - * @enum {string} - */ - object: "transaction_details"; - /** @enum {string} */ - type: "manual_payment"; - } | { - memo: string; - /** - * @example transaction_details - * @enum {string} - */ - object: "transaction_details"; - /** @enum {string} */ - type: "credit_grant"; - } | { - /** - * @example transaction_details - * @enum {string} - */ - object: "transaction_details"; - /** @enum {string} */ - type: "refund"; - } | { - /** - * @example transaction_details - * @enum {string} - */ - object: "transaction_details"; - order_id: components["schemas"]["market-api_OrderId"]; - /** @enum {string} */ - type: "buy_order"; - }; - "market-api_TransactionResponse": { - /** - * Format: int64 - * @description Transaction amount in cents (e.g., 50000 = $500.00) - * @example 50000 - */ - amount_cents: number; - /** - * Format: int64 - * @description Balance immediately after this transaction took place, in cents (e.g., 50000 = $500.00). - */ - balance: number; - /** @description Opaque cursor for use in pagination */ - cursor: components["schemas"]["market-api_GetTransactionsCursor"]; - /** @description Transaction details */ - details: components["schemas"]["market-api_TransactionDetails"]; - /** - * @example transaction - * @enum {string} - */ - object: "transaction"; - /** - * Format: int64 - * @description Time the transaction took place as UNIX timestamp in seconds. - * @example 1640995200 - */ - transaction_time: number; - }; + instance_type?: components["schemas"]["market-api_Ticker"]; /** - * Format: int64 - * @description Unix timestamp in seconds since epoch + * @description Start of interval to find active contracts. Must be used with active_within_interval_end + * @example 2024-01-01T00:00:00Z */ - "market-api_UnixEpoch": number; - "market-api_UpdateProcurementRequest": { - /** - * Format: int32 - * @description Maximum price per GPU hour in cents - * @example 350 - */ - buy_limit_price_per_gpu_hour?: number | null; - /** - * Format: int32 - * @description Desired quantity of nodes - * @example 5 - */ - desired_quantity?: number | null; - /** - * Format: int32 - * @description Planning horizon in minutes. The procurement will try to buy compute - * ahead of time as to always have at least this amount of time ahead - * scheduled at a minimum. - * @example 120 - */ - horizon?: number | null; - instance_type?: null | components["schemas"]["market-api_Ticker"]; - /** - * Format: int32 - * @description Minimum price per GPU hour in cents when selling back - * @example 50 - */ - sell_limit_price_per_gpu_hour?: number | null; - status?: null | components["schemas"]["market-api_ProcurementStatus"]; - }; - large_scale_inference_Batch: { - account_id: string; - /** Format: date-time */ - cancelled_at?: string | null; - /** Format: date-time */ - cancelling_at?: string | null; - /** Format: date-time */ - completed_at?: string | null; - completion_window: string; - /** Format: date-time */ - created_at: string; - /** Format: date-time */ - deadline_at: string; - endpoint: string; - errors?: null | components["schemas"]["large_scale_inference_BatchErrors"]; - /** Format: date-time */ - expired_at?: string | null; - /** Format: date-time */ - expires_at?: string | null; - /** Format: date-time */ - failed_at?: string | null; - /** Format: date-time */ - finalizing_at?: string | null; - id: string; - /** Format: date-time */ - in_progress_at?: string | null; - input_file_uri: string; - metadata?: null | components["schemas"]["large_scale_inference_BatchMetadata"]; - model_id: string; - object: string; - output_file_uri: string; - request_counts: components["schemas"]["large_scale_inference_RequestCounts"]; - status: components["schemas"]["large_scale_inference_BatchStatus"]; - }; - large_scale_inference_BatchErrorData: { - code: string; - /** Format: u-int32 */ - line?: number | null; - message: string; - param?: string | null; - }; - large_scale_inference_BatchErrors: { - data: components["schemas"]["large_scale_inference_BatchErrorData"][]; - object: string; - }; - large_scale_inference_BatchMetadata: { - input_token_usage?: string | null; - output_token_usage?: string | null; - }; - large_scale_inference_BatchRequest: { - completion_window: string; - endpoint: string; - input_file_uri: string; - metadata?: null | components["schemas"]["large_scale_inference_BatchMetadata"]; - model_id: string; - output_file_uri: string; - store: string; - }; - large_scale_inference_BatchRequests: components["schemas"]["large_scale_inference_BatchRequest"] | components["schemas"]["large_scale_inference_BatchRequest"][]; - /** @enum {string} */ - large_scale_inference_BatchStatus: "accepted" | "compute_purchased" | "not_started" | "started" | "validating" | "failed" | "in_progress" | "finalizing" | "completed" | "expired" | "cancelling" | "cancelled" | "waiting"; - large_scale_inference_BlendedPrice: { - /** Format: double */ - cents_per_million_tokens: number; - }; - large_scale_inference_ErrorDetail: { - /** @description Specific error code for this detail */ - code: string; - /** @description The field that caused the error (for validation errors) */ - field?: string | null; - /** @description Detailed error message */ - message: string; - }; - /** @enum {string} */ - large_scale_inference_ErrorType: "api_error" | "invalid_request_error" | "authentication_error" | "idempotency_error" | "conflict" | "not_found" | "request_timed_out" | "forbidden" | "not_implemented" | "upgrade_required" | "payment_required" | "service_unavailable"; - large_scale_inference_Model: { - /** Format: date-time */ - created_at: string; - external_reference: string; - id: string; - name: string; - /** Format: date-time */ - retired_at?: string | null; - }; - large_scale_inference_Price: { - Blended: components["schemas"]["large_scale_inference_BlendedPrice"]; - } | { - Specific: components["schemas"]["large_scale_inference_SpecificPrice"]; - }; - large_scale_inference_RequestCounts: { - /** Format: u-int32 */ - completed: number; - /** Format: u-int32 */ - failed: number; - /** Format: u-int32 */ - total: number; - }; - /** @description Standard error response format following API guidelines. */ - large_scale_inference_SerdeErrorProxy: { - /** @description Array of detailed error information when applicable */ - details: components["schemas"]["large_scale_inference_ErrorDetail"][]; - /** @description Human-readable error message */ - message: string; - /** @description The error type identifier */ - type: components["schemas"]["large_scale_inference_ErrorType"]; - }; - large_scale_inference_SpecificPrice: { - /** Format: double */ - cents_per_million_input_tokens: number; - /** Format: double */ - cents_per_million_output_tokens: number; - }; - /** @description Quote details with side-specific fields - * - * The response structure differs based on order side: - * - Buy orders include `instance_type` - * - Sell orders include `contract_id` */ - quoter_ApiQuoteDetails: { - /** @description End time: ISO 8601 */ - end_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; - /** @description Instance type being quoted */ - instance_type: string; - /** - * Format: u-int64 - * @description Total price in cents (USD) - */ - price: number; - /** - * Format: u-int64 - * @description Number of nodes - */ - quantity: number; - /** @description Start time: ISO 8601 or "NOW" */ - start_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; - zone: string; - } | { - /** @description Contract being sold from */ - contract_id: components["schemas"]["quoter_ContractId"]; - /** @description End time: ISO 8601 */ - end_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; - /** - * Format: u-int64 - * @description Total price in cents (USD) - */ - price: number; - /** - * Format: u-int64 - * @description Number of nodes - */ - quantity: number; - /** @description Start time: ISO 8601 or "NOW" */ - start_at: components["schemas"]["quoter_NowOrISO8601DateTime"]; - }; - /** @description Response format for GET /v0/quote - * - * Returns a quote object with side-specific details. - * If no quote is available, the `quote` field will be `None`. */ - quoter_ApiQuoteResponse: { - /** @description Always "quote" */ - object: string; - quote?: null | components["schemas"]["quoter_ApiQuoteDetails"]; - /** @description Matches the requested side: "buy" or "sell" */ - side: string; - }; - /** @description string with format '"cont"_{base62_encoded_id}' used for referencing a ContractId resource. Never user-generated. */ - quoter_ContractId: string; - /** @enum {string} */ - quoter_ErrorType: "api_error" | "invalid_request_error" | "authentication_error" | "idempotency_error" | "conflict" | "not_found" | "request_timed_out" | "forbidden" | "not_implemented" | "upgrade_required" | "payment_required" | "service_unavailable"; + active_within_interval_start?: components["schemas"]["market-api_ISO8601DateTime"]; /** - * @description A date/time value that can be either "NOW" or an ISO 8601 datetime string - * @example NOW - * @example 2025-07-11T20:41:37.423Z + * @description End of interval to find active contracts. Must be used with active_within_interval_start + * @example 2024-01-02T00:00:00Z */ - quoter_NowOrISO8601DateTime: string; - /** @description Side-specific parameters for quote requests */ - quoter_OrderSideParams: { - /** @description Cluster constraint (optional) - hostname to resolve to cluster_id */ - cluster?: string | null; - colocate_with?: null | components["schemas"]["quoter_ContractId"]; - /** @description Instance type for buy orders: "h100i", "h100v", or "h200ki" */ - instance_type?: string | null; - /** @enum {string} */ - side: "buy"; - } | { - /** @description Contract ID for sell orders (must be owned by user) */ - contract_id: components["schemas"]["quoter_ContractId"]; - /** @enum {string} */ - side: "sell"; - }; - /** @description Query parameters for GET /v0/quote - * - * # Validation Rules - * - * ## Required Fields - * - `side`: Must be "buy" or "sell" - * - `quantity`: Must be a positive integer (1-1024) - * - * ## Date Constraints - * - `min_start_date` and `max_start_date` accept: - * - "NOW" (literal string for current time) - * - ISO 8601 date string - * - If neither provided, defaults to "NOW" - * - `max_start_date` must be >= `min_start_date` - * - * ## Duration Constraints (mutually exclusive) - * - EITHER provide `duration` (in seconds) - * - OR provide both `min_duration` AND `max_duration` (in seconds) - * - All durations must be positive - * - Maximum duration: 3 years (94,608,000 seconds) - * - When `duration` is provided, it expands to a range: - * - min = duration - * - max = duration + 59 minutes - * - * ## Side-Specific Constraints - * - * ### Buy Orders - * - **Required**: `instance_type` (must be one of: "h100i", "h100v", "h200ki") - * - **Optional**: `colocate_with` (contract ID), `cluster` - * - **Forbidden**: `contract_id` - * - * ### Sell Orders - * - **Required**: `contract_id` (must be owned by authenticated user) - * - **Forbidden**: `instance_type`, `cluster`, `colocate_with` */ - quoter_QuoteRequestParams: components["schemas"]["quoter_OrderSideParams"] & components["schemas"]["quoter_SpatialQuoteParams"]; - /** @description Spatial parameters for quote requests (time, quantity, duration) */ - quoter_SpatialQuoteParams: { - /** - * Format: u-int64 - * @description Exact duration in seconds (mutually exclusive with min_duration/max_duration) - */ - duration?: number | null; - /** - * Format: u-int64 - * @description Maximum duration in seconds (must be used with min_duration) - */ - max_duration?: number | null; - max_start_date?: null | components["schemas"]["quoter_NowOrISO8601DateTime"]; - /** - * Format: u-int64 - * @description Minimum duration in seconds (must be used with max_duration) - */ - min_duration?: number | null; - min_start_date?: null | components["schemas"]["quoter_NowOrISO8601DateTime"]; - /** - * Format: u-int64 - * @description Number of nodes (1-1024) - */ - quantity: number; - }; + active_within_interval_end?: components["schemas"]["market-api_ISO8601DateTime"]; + /** + * @description Filter by contract state. Options: "All", "Upcoming", "Active", "Expired". Default excludes expired contracts + * @example Active + */ + state?: string; + }; + header?: never; + path?: never; + cookie?: never; }; - responses: never; - parameters: never; - requestBodies: never; - headers: never; - pathItems: never; -} -export type $defs = Record; -export interface operations { - get_vms_instances: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_GetInstancesResponse"]; - }; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody?: never; + responses: { + /** @description List of contracts */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "contract", + * "status": "active", + * "id": "cont_xyz789", + * "created_at": "2024-07-15T22:30:17.426Z", + * "instance_type": "h100i", + * "shape": { + * "intervals": [ + * "2024-07-16T00:00:00Z", + * "2024-07-17T00:00:00Z" + * ], + * "quantities": [ + * 10, + * 0 + * ] + * }, + * "state": "Active" + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListContractsResponse"]; + }; + }; + /** @description Bad request - invalid query parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "must provide both active_within_interval_start and active_within_interval_end or none of them" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - get_vms_logs2: { - parameters: { - query: { - instance_id: string; - before_seqnum?: number; - since_seqnum?: number; - before_realtime_timestamp?: string; - since_realtime_timestamp?: string; - order_by: "seqnum_asc" | "seqnum_desc"; - limit?: number; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_VmsLogsResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + getContract: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Contract ID + * @example cont_xyz789 + */ + id: components["schemas"]["market-api_ContractId"]; + }; + cookie?: never; }; - post_vms_replace: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["vmorch_PostReplaceRequest"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_PostReplaceResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody?: never; + responses: { + /** @description Contract details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "contract", + * "status": "active", + * "id": "cont_xyz789", + * "created_at": "2024-07-15T22:30:17.426Z", + * "instance_type": "h100i", + * "shape": { + * "intervals": [ + * "2024-07-16T22:30:16Z", + * "2024-07-17T22:30:16Z", + * "2024-07-18T22:30:16Z" + * ], + * "quantities": [ + * 10, + * 20, + * 0 + * ] + * }, + * "colocate_with": [ + * "cont_abc456" + * ], + * "cluster_id": "clus_xyz123", + * "state": "Active" + * } */ + "application/json": components["schemas"]["market-api_ContractResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Contract not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Contract not found" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - get_vms_user_data: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_GetUserDataResponse"]; - }; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + listOrders: { + parameters: { + query?: { + /** @description Filter by order side (buy or sell) */ + side?: components["schemas"]["market-api_OrderSide"]; + /** @description Filter by instance type */ + instance_type?: components["schemas"]["market-api_Ticker"]; + /** @description Minimum price in cents */ + min_price?: number; + /** @description Maximum price in cents */ + max_price?: number; + /** @description Minimum start date */ + min_start_date?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Maximum start date */ + max_start_date?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Minimum duration in seconds */ + min_duration?: components["schemas"]["market-api_UnixEpoch"]; + /** @description Maximum duration in seconds */ + max_duration?: components["schemas"]["market-api_UnixEpoch"]; + /** @description Minimum quantity */ + min_quantity?: number; + /** @description Maximum quantity */ + max_quantity?: number; + /** @description Filter by contract ID */ + contract_id?: components["schemas"]["market-api_ContractId"]; + /** @description Show only open orders */ + only_open?: boolean; + /** @description Exclude filled orders */ + exclude_filled?: boolean; + /** @description Show only filled orders */ + only_filled?: boolean; + /** @description Minimum filled at date */ + min_filled_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Maximum filled at date */ + max_filled_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Minimum fill price in cents */ + min_fill_price?: number; + /** @description Maximum fill price in cents */ + max_fill_price?: number; + /** @description Exclude cancelled orders */ + exclude_cancelled?: boolean; + /** @description Show only cancelled orders */ + only_cancelled?: boolean; + /** @description Minimum cancelled at date */ + min_cancelled_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Maximum cancelled at date */ + max_cancelled_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Minimum placed at date */ + min_placed_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Maximum placed at date */ + max_placed_at?: components["schemas"]["market-api_ISO8601DateTime"]; + /** @description Maximum number of results to return (default: 100, max: 100) */ + limit?: number; + /** @description Number of results to skip */ + offset?: number; + /** @description Sort field */ + sort_by?: components["schemas"]["market-api_SortBy"]; + /** @description Sort direction */ + sort_direction?: components["schemas"]["market-api_SortDirection"]; + include_public?: unknown; + }; + header?: never; + path?: never; + cookie?: never; }; - post_vms_user_data: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["vmorch_PostUserDataRequest"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_PostUserDataResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody?: never; + responses: { + /** @description List of orders */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "order", + * "id": "ordr_xyz123", + * "side": "buy", + * "instance_type": "h100i", + * "price": 5000, + * "quantity": 2, + * "status": "open", + * "created_at": "2024-07-15T22:30:17.426Z", + * "start_at": "2024-07-16T00:00:00Z", + * "end_at": "2024-07-17T00:00:00Z", + * "flags": { + * "ioc": false, + * "post_only": false, + * "market": false, + * "prorate": false + * } + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListOrdersResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "Cannot specify both 'only_filled' and 'only_cancelled'" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - get_vms_ssh: { - parameters: { - query: { - vm_id: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_GetSshResponse"]; - }; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + createOrder: { + parameters: { + query?: never; + header?: { + /** @description Optional key to ensure idempotent order creation */ + "Idempotency-Key"?: string | null; + }; + path?: never; + cookie?: never; }; - list_images: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of images */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_ListImagesResponse"]; - }; - }; - /** @description Node API features not enabled */ - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody: { + content: { + "application/json": components["schemas"]["market-api_CreateOrderRequest"]; + }; }; - start_image_upload: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["vmorch_StartMultipartUploadRequest"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_StartMultipartUploadResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + responses: { + /** @description Order created successfully */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "order", + * "status": "pending", + * "id": "order_xyz789", + * "idempotency_key": "key_123" + * } */ + "application/json": components["schemas"]["market-api_CreateOrderResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "invalid_request_error", + * "message": "Start time must be within +/- 1 minute of now, on a future hour, or the string literal 'NOW'", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "authentication_error", + * "message": "missing authentication token", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen, insufficient credits, unauthorized seller, or trading halted */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "forbidden", + * "message": "Account is frozen", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Not found - contract or cluster not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "not_found", + * "message": "Contract not found", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Upgrade required - legacy account needs migration */ + 426: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "invalid_request_error", + * "message": "Legacy account must be upgraded before placing orders", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "api_error", + * "message": "An internal server error occurred", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + getOrder: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Order ID + * @example order_xyz789 + */ + id: components["schemas"]["market-api_OrderId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Order details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "order", + * "id": "order_xyz789", + * "side": "buy", + * "status": "open", + * "instance_type": "h100i", + * "quantity": 4, + * "start_at": "2021-01-01T00:00:00Z", + * "end_at": "2021-01-01T01:00:00Z", + * "price": 2850000, + * "flags": { + * "market": false, + * "post_only": false, + * "ioc": false + * }, + * "executed": false, + * "cancelled": false, + * "colocate_with": [], + * "created_at": "2021-01-01T00:00:00Z", + * "rejected": false + * } */ + "application/json": components["schemas"]["market-api_OrderResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "authentication_error", + * "message": "missing authentication token", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - user not authorized to view this order */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "forbidden", + * "message": "User not authorized to view this order", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Order not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "not_found", + * "message": "Order not found", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "api_error", + * "message": "An internal server error occurred", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + cancelOrder: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Order ID + * @example ordr_xyz789 + */ + id: components["schemas"]["market-api_OrderId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Order cancelled successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "pending" + * } */ + "application/json": components["schemas"]["market-api_CancelOrderResponse"]; + }; + }; + /** @description Order already cancelled */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "validation_error", + * "message": "Order already cancelled", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Order not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "not_found", + * "message": "Order not found", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "type": "api_error", + * "message": "An internal server error occurred", + * "details": [] + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + getOrderClusters: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Order ID + * @example ordr_W9TRG + */ + id: components["schemas"]["market-api_OrderId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of clusters associated with the order */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "kubernetes_cluster", + * "name": "starlight", + * "kubernetes_namespace": "sf-user123", + * "kubernetes_api_url": "https://starlight.sf-k8s.com", + * "kubernetes_ca_cert": "-----BEGIN CERTIFICATE-----...", + * "contract": { + * "object": "contract", + * "status": "active", + * "id": "cont_xyz123", + * "created_at": "2024-07-15T22:30:17.426Z", + * "instance_type": "h100i", + * "shape": { + * "intervals": [ + * "2024-07-16T00:00:00Z", + * "2024-07-17T00:00:00Z" + * ], + * "quantities": [ + * 4, + * 0 + * ] + * }, + * "colocate_with": [], + * "cluster_id": "clstr_abc456", + * "state": "Active" + * } + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListClustersResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Order not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Order not found" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + listProcurements: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of procurements */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "procurement", + * "id": "proc_test123", + * "status": "active", + * "instance_type": "h100i", + * "desired_quantity": 5, + * "buy_limit_price_per_gpu_hour": 250, + * "sell_limit_price_per_gpu_hour": 25, + * "horizon": 60, + * "colocation_strategy": "colocate_pinned", + * "actual_quantity": 5, + * "active_order_count": 2, + * "last_message": "Running" + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListProcurementsResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + }; + }; + createProcurement: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - download_image: { - parameters: { - query?: never; - header?: never; - path: { - /** @description VM image ID */ - image_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_ImageDownloadResponse"]; - }; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 409: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody: { + content: { + "application/json": components["schemas"]["market-api_CreateProcurementRequest"]; + }; }; - complete_image_upload: { - parameters: { - query?: never; - header?: never; - path: { - /** @description VM image ID */ - image_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["vmorch_CompleteUploadRequest"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_CompleteUploadResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + responses: { + /** @description Successfully created procurement */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "procurement", + * "id": "proc_xyz123", + * "status": "active", + * "instance_type": "h100i", + * "desired_quantity": 5, + * "buy_limit_price_per_gpu_hour": 250, + * "sell_limit_price_per_gpu_hour": 25, + * "horizon": 60, + * "colocation_strategy": "colocate_pinned", + * "actual_quantity": 0, + * "active_order_count": 0 + * } */ + "application/json": components["schemas"]["market-api_ProcurementResponse"]; + }; + }; + /** @description Bad request - invalid field values */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "desired_quantity must be non-negative" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Payment required - insufficient funds */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "payment_required", + * "message": "insufficient funds" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Not found - cluster not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "cluster starlight not found" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - create_image_upload_url: { - parameters: { - query?: never; - header?: never; - path: { - /** @description VM image ID */ - image_id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["vmorch_ImageUploadRequest"]; - }; - }; - responses: { - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["vmorch_ImageUploadResponse"]; - }; - }; - 400: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 403: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 404: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - 500: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + getProcurement: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Procurement ID + * @example proc_xyz123 + */ + id: string; + }; + cookie?: never; }; - list_zones_handler: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of zones */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["node-api_ListResponse_ZoneInfo"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token", - * "request_id": "req_1234567890" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error", - * "request_id": "req_1234567890" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Procurement details */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "procurement", + * "id": "proc_xyz123", + * "status": "active", + * "instance_type": "h100i", + * "desired_quantity": 5, + * "buy_limit_price_per_gpu_hour": 250, + * "sell_limit_price_per_gpu_hour": 25, + * "horizon": 60, + * "colocation_strategy": "colocate_pinned", + * "actual_quantity": 5, + * "active_order_count": 2, + * "last_message": "Running" + * } */ + "application/json": components["schemas"]["market-api_ProcurementResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Procurement not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "Procurement proc_xyz123 not found" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - get_zone_handler: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Zone ID */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Zone information */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["node-api_ZoneInfo"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token", - * "request_id": "req_1234567890" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "You do not have permission to get zones" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Zone not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Zone not found", - * "request_id": "req_1234567890" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error", - * "request_id": "req_1234567890" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + }; + updateProcurement: { + parameters: { + query?: never; + header?: never; + path: { + /** + * @description Procurement ID + * @example proc_xyz123 + */ + id: string; + }; + cookie?: never; }; - health_handler: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Health check successful */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example OK */ - "text/plain": string; - }; - }; - /** @description Health check failed */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example Internal Server Error */ - "text/plain": string; - }; - }; - }; + requestBody: { + content: { + "application/json": components["schemas"]["market-api_UpdateProcurementRequest"]; + }; }; - list_nodes_handler: { - parameters: { - query?: { - /** @description Filter nodes by node_id - * Use ?id=n_b1dc52505c6db142&id=n_b1dc52505c6db133 to specify multiple IDs. - * Cannot be used with name */ - id?: string[]; - /** - * @description Filter nodes by their names - * Use ?name=val1&name=val2 to specify multiple names. - * Cannot be used with id - * @example [ - * "cuda-crunch" - * ] - */ - name?: string[]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of nodes */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "node", - * "id": "n_b1dc52505c6db142", - * "name": "cuda-crunch", - * "zone": "hayesvalley", - * "gpu_type": "H100", - * "owner": "sfcompute", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "procurement_id": "proc_b1dc52505c6de142", - * "max_price_per_node_hour": 1000, - * "node_type": "autoreserved", - * "vms": { - * "object": "list", - * "data": [ - * { - * "object": "vm", - * "id": "vm_myOZZXw4pfcp7H9DQOldd", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641007200 - * } - * ] - * } - * } - * ] - * } */ - "application/json": components["schemas"]["node-api_ListResponse_Node"]; - }; - }; - /** @description Bad request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Invalid query parameters", - * "details": [ - * { - * "field": "node_id|name", - * "code": "invalid_value", - * "message": "Cannot specify both node_id and name parameters" - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "You do not have permission to list nodes" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + responses: { + /** @description Successfully updated procurement */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "procurement", + * "id": "proc_xyz123", + * "status": "disabled", + * "instance_type": "h100i", + * "desired_quantity": 5, + * "buy_limit_price_per_gpu_hour": 400, + * "sell_limit_price_per_gpu_hour": 100, + * "horizon": 120, + * "colocation_strategy": "colocate_pinned", + * "actual_quantity": 3, + * "active_order_count": 1 + * } */ + "application/json": components["schemas"]["market-api_ProcurementResponse"]; + }; + }; + /** @description Bad request - invalid field values */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "invalid_request_error", + * "message": "desired_quantity must be non-negative" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Payment required - insufficient funds */ + 402: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "payment_required", + * "message": "insufficient funds" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Procurement not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "procurement proc_xyz123 not found" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - create_nodes_handler: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["node-api_CreateNodesRequest"]; - }; - }; - responses: { - /** @description Nodes created successfully */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "node", - * "id": "n_b1dc52505c6db142", - * "name": "cuda-crunch", - * "zone": "hayesvalley", - * "gpu_type": "H100", - * "owner": "sfcompute", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "procurement_id": "proc_b1dc52505c6de142", - * "max_price_per_node_hour": 1000, - * "node_type": "autoreserved" - * } - * ] - * } */ - "application/json": components["schemas"]["node-api_ListResponse_Node"]; - }; - }; - /** @description Bad request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "One or more fields are invalid", - * "details": [ - * { - * "field": "start_at", - * "code": "invalid_value", - * "message": "start_at time must be in the future" - * }, - * { - * "field": "names", - * "code": "invalid_value", - * "message": "node name 'vm_test' cannot follow the vm_id pattern vm_{16_hex_chars} as this prefix is reserved for system-generated IDs" - * }, - * { - * "field": "names", - * "code": "invalid_value", - * "message": "node name cannot be a numeric string" - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "You do not have permission to create nodes" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + }; + getRefunds: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - get_node_handler: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Node ID or name */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Node details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "node", - * "id": "n_b1dc52505c6db142", - * "name": "cuda-crunch", - * "zone": "hayesvalley", - * "gpu_type": "H100", - * "owner": "sfcompute", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "procurement_id": "proc_b1dc52505c6de142", - * "max_price_per_node_hour": 1000, - * "node_type": "autoreserved", - * "vms": { - * "object": "list", - * "data": [ - * { - * "object": "vm", - * "id": "vm_myOZZXw4pfcp7H9DQOldd", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641007200 - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_Node"]; - }; - }; - /** @description Bad request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Invalid node identifier", - * "details": [ - * { - * "field": "id", - * "code": "invalid_value", - * "message": "Numeric strings are not supported as node identifiers" - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "You do not have permission to access this node" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Node not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Node not found" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description List of refund requests and their statuses for the account */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "refunds", + * "refunds": [ + * { + * "report_id": 12345, + * "report_start_time": "2024-03-15T00:00:00Z", + * "report_end_time": "2024-03-15T02:00:00Z", + * "report_nodes_affected": 4, + * "report_cluster_id": "cluster_us_west_1", + * "report_memo": "Network connectivity issues in US-West-1", + * "report_created_at": "2024-03-15T00:30:00Z", + * "memo_amount": "500.00", + * "refund_timestamp": "2024-03-16T00:00:00Z", + * "status": "refunded" + * } + * ] + * } */ + "application/json": components["schemas"]["market-api_AccountRefundsResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "authentication_error", + * "message": "missing authentication token" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Forbidden - account frozen */ + 403: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "forbidden", + * "message": "Account is frozen" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "api_error", + * "message": "An internal server error occurred" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - extend_node_handler: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Node ID or name */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["node-api_ExtendNodeRequest"]; - }; - }; - responses: { - /** @description Node extended successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "node", - * "id": "n_b1dc52505c6db142", - * "name": "cuda-crunch", - * "zone": "hayesvalley", - * "gpu_type": "H100", - * "owner": "sfcompute", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641007200, - * "max_price_per_node_hour": 1000, - * "node_type": "reserved", - * "vms": { - * "object": "list", - * "data": [ - * { - * "object": "vm", - * "id": "vm_myOZZXw4pfcp7H9DQOldd", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641007200 - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_Node"]; - }; - }; - /** @description Bad request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Node is Auto Reserved not Reserved - only reservation nodes can be extended", - * "details": [ - * { - * "field": "duration_seconds", - * "code": "invalid_value", - * "message": "duration must be at least 1 hour (3600 seconds)" - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen or node is not associated with this account" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Node not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Node not found" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Extension failed due to capacity */ - 422: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Extension failed: No capacity available for the requested time period" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + }; + getAccountMe: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - release_node_handler: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Node ID or name */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Node released successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "node", - * "id": "n_b1dc52505c6db142", - * "name": "cuda-crunch", - * "zone": "hayesvalley", - * "gpu_type": "H100", - * "owner": "sfcompute", - * "status": "terminated", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641000000, - * "max_price_per_node_hour": 1000, - * "node_type": "autoreserved", - * "procurement_id": "proc_b1dc52505c6de142", - * "vms": { - * "object": "list", - * "data": [ - * { - * "object": "vm", - * "id": "vm_myOZZXw4pfcp7H9DQOldd", - * "status": "running", - * "created_at": 1640995200, - * "updated_at": 1640995200, - * "start_at": 1640995200, - * "end_at": 1641007200 - * } - * ] - * } - * } */ - "application/json": components["schemas"]["node-api_Node"]; - }; - }; - /** @description Bad request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Node is Reserved not Auto Reserved - only autoreserved nodes can be released" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Unauthorized request */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "Unauthorized: missing or invalid authentication token" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Forbidden */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen or node is not associated with this account" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Node not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Node not found" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "Internal server error" - * } - * } */ - "application/json": components["schemas"]["node-api_ErrorObject"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Account information retrieved successfully */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "id": "gmail-com-name", + * "role": "user", + * "is_frozen": false, + * "kyc": "basic", + * "submitted_waitlist": true, + * "waitlist": false, + * "kycb_form_submitted": false, + * "created_at": "2024-01-15T10:30:00Z" + * } */ + "application/json": components["schemas"]["market-api_AccountMeResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Account not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "not_found", + * "message": "account not found", + * "details": [], + * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - listClusters: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of Kubernetes clusters */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "kubernetes_cluster", - * "name": "starlight", - * "kubernetes_namespace": "sf-user123", - * "kubernetes_api_url": "https://starlight.sf-k8s.com", - * "kubernetes_ca_cert": "-----BEGIN CERTIFICATE-----...", - * "contract": { - * "object": "contract", - * "status": "active", - * "id": "cont_xyz123", - * "created_at": "2024-07-15T22:30:17.426Z", - * "instance_type": "h100i", - * "shape": { - * "intervals": [ - * "2024-07-16T00:00:00Z", - * "2024-07-17T00:00:00Z" - * ], - * "quantities": [ - * 4, - * 0 - * ] - * }, - * "colocate_with": [], - * "cluster_id": "clstr_abc456", - * "state": "Active" - * } - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListClustersResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + getBalance: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - listContracts: { - parameters: { - query?: { - /** - * @description Filter by instance type - * @example h100i - */ - instance_type?: components["schemas"]["market-api_Ticker"]; - /** - * @description Start of interval to find active contracts. Must be used with active_within_interval_end - * @example 2024-01-01T00:00:00Z - */ - active_within_interval_start?: components["schemas"]["market-api_ISO8601DateTime"]; - /** - * @description End of interval to find active contracts. Must be used with active_within_interval_start - * @example 2024-01-02T00:00:00Z - */ - active_within_interval_end?: components["schemas"]["market-api_ISO8601DateTime"]; - /** - * @description Filter by contract state. Options: "All", "Upcoming", "Active", "Expired". Default excludes expired contracts - * @example Active - */ - state?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of contracts */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "contract", - * "status": "active", - * "id": "cont_xyz789", - * "created_at": "2024-07-15T22:30:17.426Z", - * "instance_type": "h100i", - * "shape": { - * "intervals": [ - * "2024-07-16T00:00:00Z", - * "2024-07-17T00:00:00Z" - * ], - * "quantities": [ - * 10, - * 0 - * ] - * }, - * "state": "Active" - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListContractsResponse"]; - }; - }; - /** @description Bad request - invalid query parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "must provide both active_within_interval_start and active_within_interval_end or none of them" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Successfully retrieved balance */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "balance", + * "available_cents": 150000, + * "current_cents": 180000, + * "current_overage_cents": 150000, + * "current_hold_cents": 180000, + * "updated_at": 1640995200 + * } */ + "application/json": components["schemas"]["market-api_GetBalanceResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - getContract: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Contract ID - * @example cont_xyz789 - */ - id: components["schemas"]["market-api_ContractId"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Contract details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "contract", - * "status": "active", - * "id": "cont_xyz789", - * "created_at": "2024-07-15T22:30:17.426Z", - * "instance_type": "h100i", - * "shape": { - * "intervals": [ - * "2024-07-16T22:30:16Z", - * "2024-07-17T22:30:16Z", - * "2024-07-18T22:30:16Z" - * ], - * "quantities": [ - * 10, - * 20, - * 0 - * ] - * }, - * "colocate_with": [ - * "cont_abc456" - * ], - * "cluster_id": "clus_xyz123", - * "state": "Active" - * } */ - "application/json": components["schemas"]["market-api_ContractResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Contract not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Contract not found" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + getCredits: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - listOrders: { - parameters: { - query?: { - /** @description Filter by order side (buy or sell) */ - side?: components["schemas"]["market-api_OrderSide"]; - /** @description Filter by instance type */ - instance_type?: components["schemas"]["market-api_Ticker"]; - /** @description Minimum price in cents */ - min_price?: number; - /** @description Maximum price in cents */ - max_price?: number; - /** @description Minimum start date */ - min_start_date?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Maximum start date */ - max_start_date?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Minimum duration in seconds */ - min_duration?: components["schemas"]["market-api_UnixEpoch"]; - /** @description Maximum duration in seconds */ - max_duration?: components["schemas"]["market-api_UnixEpoch"]; - /** @description Minimum quantity */ - min_quantity?: number; - /** @description Maximum quantity */ - max_quantity?: number; - /** @description Filter by contract ID */ - contract_id?: components["schemas"]["market-api_ContractId"]; - /** @description Show only open orders */ - only_open?: boolean; - /** @description Exclude filled orders */ - exclude_filled?: boolean; - /** @description Show only filled orders */ - only_filled?: boolean; - /** @description Minimum filled at date */ - min_filled_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Maximum filled at date */ - max_filled_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Minimum fill price in cents */ - min_fill_price?: number; - /** @description Maximum fill price in cents */ - max_fill_price?: number; - /** @description Exclude cancelled orders */ - exclude_cancelled?: boolean; - /** @description Show only cancelled orders */ - only_cancelled?: boolean; - /** @description Minimum cancelled at date */ - min_cancelled_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Maximum cancelled at date */ - max_cancelled_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Minimum placed at date */ - min_placed_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Maximum placed at date */ - max_placed_at?: components["schemas"]["market-api_ISO8601DateTime"]; - /** @description Maximum number of results to return (default: 100, max: 100) */ - limit?: number; - /** @description Number of results to skip */ - offset?: number; - /** @description Sort field */ - sort_by?: components["schemas"]["market-api_SortBy"]; - /** @description Sort direction */ - sort_direction?: components["schemas"]["market-api_SortDirection"]; - include_public?: unknown; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of orders */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "order", - * "id": "ordr_xyz123", - * "side": "buy", - * "instance_type": "h100i", - * "price": 5000, - * "quantity": 2, - * "status": "open", - * "created_at": "2024-07-15T22:30:17.426Z", - * "start_at": "2024-07-16T00:00:00Z", - * "end_at": "2024-07-17T00:00:00Z", - * "flags": { - * "ioc": false, - * "post_only": false, - * "market": false, - * "prorate": false - * } - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListOrdersResponse"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "Cannot specify both 'only_filled' and 'only_cancelled'" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Successfully retrieved credit balance */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "current_balance_cents": 150000, + * "available_credits_cents": 150000, + * "amount_due_next_billing_period_cents": 0 + * } */ + "application/json": components["schemas"]["market-api_GetCreditsResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - createOrder: { - parameters: { - query?: never; - header?: { - /** @description Optional key to ensure idempotent order creation */ - "Idempotency-Key"?: string | null; - }; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["market-api_CreateOrderRequest"]; - }; - }; - responses: { - /** @description Order created successfully */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "order", - * "status": "pending", - * "id": "order_xyz789", - * "idempotency_key": "key_123" - * } */ - "application/json": components["schemas"]["market-api_CreateOrderResponse"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "invalid_request_error", - * "message": "Start time must be within +/- 1 minute of now, on a future hour, or the string literal 'NOW'", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "authentication_error", - * "message": "missing authentication token", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen, insufficient credits, unauthorized seller, or trading halted */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "forbidden", - * "message": "Account is frozen", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Not found - contract or cluster not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "not_found", - * "message": "Contract not found", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Upgrade required - legacy account needs migration */ - 426: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "invalid_request_error", - * "message": "Legacy account must be upgraded before placing orders", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "api_error", - * "message": "An internal server error occurred", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + migrateAccount: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - getOrder: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Order ID - * @example order_xyz789 - */ - id: components["schemas"]["market-api_OrderId"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Order details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "order", - * "id": "order_xyz789", - * "side": "buy", - * "status": "open", - * "instance_type": "h100i", - * "quantity": 4, - * "start_at": "2021-01-01T00:00:00Z", - * "end_at": "2021-01-01T01:00:00Z", - * "price": 2850000, - * "flags": { - * "market": false, - * "post_only": false, - * "ioc": false - * }, - * "executed": false, - * "cancelled": false, - * "colocate_with": [], - * "created_at": "2021-01-01T00:00:00Z", - * "rejected": false - * } */ - "application/json": components["schemas"]["market-api_OrderResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "authentication_error", - * "message": "missing authentication token", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - user not authorized to view this order */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "forbidden", - * "message": "User not authorized to view this order", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Order not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "not_found", - * "message": "Order not found", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "api_error", - * "message": "An internal server error occurred", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + /** @description Migration request specifying the desired migration method */ + requestBody: { + content: { + "application/json": components["schemas"]["market-api_MigrateAccountRequest"]; + }; }; - cancelOrder: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Order ID - * @example ordr_xyz789 - */ - id: components["schemas"]["market-api_OrderId"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Order cancelled successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "pending" - * } */ - "application/json": components["schemas"]["market-api_CancelOrderResponse"]; - }; - }; - /** @description Order already cancelled */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "validation_error", - * "message": "Order already cancelled", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Order not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "not_found", - * "message": "Order not found", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "type": "api_error", - * "message": "An internal server error occurred", - * "details": [] - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + responses: { + /** @description Account successfully migrated */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "status": "migrated" + * } */ + "application/json": components["schemas"]["market-api_MigrateAccountResponse"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Account already migrated */ + 409: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "account_already_migrated", + * "message": "Account has already been migrated", + * "details": [], + * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "error": { + * "type": "internal_server_error", + * "message": "An internal server error occurred", + * "details": [], + * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" + * } + * } */ + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - getOrderClusters: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Order ID - * @example ordr_W9TRG - */ - id: components["schemas"]["market-api_OrderId"]; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of clusters associated with the order */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "kubernetes_cluster", - * "name": "starlight", - * "kubernetes_namespace": "sf-user123", - * "kubernetes_api_url": "https://starlight.sf-k8s.com", - * "kubernetes_ca_cert": "-----BEGIN CERTIFICATE-----...", - * "contract": { - * "object": "contract", - * "status": "active", - * "id": "cont_xyz123", - * "created_at": "2024-07-15T22:30:17.426Z", - * "instance_type": "h100i", - * "shape": { - * "intervals": [ - * "2024-07-16T00:00:00Z", - * "2024-07-17T00:00:00Z" - * ], - * "quantities": [ - * 4, - * 0 - * ] - * }, - * "colocate_with": [], - * "cluster_id": "clstr_abc456", - * "state": "Active" - * } - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListClustersResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Order not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Order not found" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + getTransactions: { + parameters: { + query?: { + /** @description Number of transactions to return (1-100, default 10) */ + limit?: number; + /** + * @description Filter for transactions after this UNIX timestamp (exclusive) + * @example 1640995200 + */ + after_time?: number; + /** + * @description Filter for transactions before this UNIX timestamp (exclusive) + * @example 1640995200 + */ + before_time?: number; + /** + * @description Cursor for forward pagination + * @example 1640995200 + */ + starting_after_cursor?: components["schemas"]["market-api_GetTransactionsCursor"]; + /** + * @description Cursor for backward pagination + * @example 1640995200 + */ + ending_before_cursor?: components["schemas"]["market-api_GetTransactionsCursor"]; + }; + header?: never; + path?: never; + cookie?: never; }; - listProcurements: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of procurements */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "procurement", - * "id": "proc_test123", - * "status": "active", - * "instance_type": "h100i", - * "desired_quantity": 5, - * "buy_limit_price_per_gpu_hour": 250, - * "sell_limit_price_per_gpu_hour": 25, - * "horizon": 60, - * "colocation_strategy": "colocate_pinned", - * "actual_quantity": 5, - * "active_order_count": 2, - * "last_message": "Running" - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListProcurementsResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Successfully retrieved transactions */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + /** @example { + * "object": "list", + * "data": [ + * { + * "object": "transaction", + * "transaction_time": 1640995200, + * "amount_cents": 25000, + * "details": { + * "object": "transaction_details", + * "type": "credit_grant", + * "memo": "Promotional credit" + * } + * } + * ], + * "has_more": false + * } */ + "application/json": components["schemas"]["market-api_ListTransactionsResponse"]; + }; + }; + /** @description Invalid request parameters */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Unauthorized - missing or invalid authentication token */ + 401: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; + }; + }; }; - createProcurement: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["market-api_CreateProcurementRequest"]; - }; + }; + list_batches: { + parameters: { + query: { + /** @description Max items to return */ + limit: number; + /** @description Pagination offset */ + after: number; + }; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description List of batches */ + 200: { + headers: { + [name: string]: unknown; }; - responses: { - /** @description Successfully created procurement */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "procurement", - * "id": "proc_xyz123", - * "status": "active", - * "instance_type": "h100i", - * "desired_quantity": 5, - * "buy_limit_price_per_gpu_hour": 250, - * "sell_limit_price_per_gpu_hour": 25, - * "horizon": 60, - * "colocation_strategy": "colocate_pinned", - * "actual_quantity": 0, - * "active_order_count": 0 - * } */ - "application/json": components["schemas"]["market-api_ProcurementResponse"]; - }; - }; - /** @description Bad request - invalid field values */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "desired_quantity must be non-negative" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Payment required - insufficient funds */ - 402: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "payment_required", - * "message": "insufficient funds" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Not found - cluster not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "cluster starlight not found" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; + content: { + "application/json": components["schemas"]["large_scale_inference_Batch"][]; }; + }; }; - getProcurement: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Procurement ID - * @example proc_xyz123 - */ - id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Procurement details */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "procurement", - * "id": "proc_xyz123", - * "status": "active", - * "instance_type": "h100i", - * "desired_quantity": 5, - * "buy_limit_price_per_gpu_hour": 250, - * "sell_limit_price_per_gpu_hour": 25, - * "horizon": 60, - * "colocation_strategy": "colocate_pinned", - * "actual_quantity": 5, - * "active_order_count": 2, - * "last_message": "Running" - * } */ - "application/json": components["schemas"]["market-api_ProcurementResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Procurement not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "Procurement proc_xyz123 not found" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + create_batches: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - updateProcurement: { - parameters: { - query?: never; - header?: never; - path: { - /** - * @description Procurement ID - * @example proc_xyz123 - */ - id: string; - }; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["market-api_UpdateProcurementRequest"]; - }; - }; - responses: { - /** @description Successfully updated procurement */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "procurement", - * "id": "proc_xyz123", - * "status": "disabled", - * "instance_type": "h100i", - * "desired_quantity": 5, - * "buy_limit_price_per_gpu_hour": 400, - * "sell_limit_price_per_gpu_hour": 100, - * "horizon": 120, - * "colocation_strategy": "colocate_pinned", - * "actual_quantity": 3, - * "active_order_count": 1 - * } */ - "application/json": components["schemas"]["market-api_ProcurementResponse"]; - }; - }; - /** @description Bad request - invalid field values */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "invalid_request_error", - * "message": "desired_quantity must be non-negative" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Payment required - insufficient funds */ - 402: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "payment_required", - * "message": "insufficient funds" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Procurement not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "procurement proc_xyz123 not found" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + requestBody: { + content: { + "application/json": components["schemas"]["large_scale_inference_BatchRequest"]; + }; }; - getRefunds: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of refund requests and their statuses for the account */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "refunds", - * "refunds": [ - * { - * "report_id": 12345, - * "report_start_time": "2024-03-15T00:00:00Z", - * "report_end_time": "2024-03-15T02:00:00Z", - * "report_nodes_affected": 4, - * "report_cluster_id": "cluster_us_west_1", - * "report_memo": "Network connectivity issues in US-West-1", - * "report_created_at": "2024-03-15T00:30:00Z", - * "memo_amount": "500.00", - * "refund_timestamp": "2024-03-16T00:00:00Z", - * "status": "refunded" - * } - * ] - * } */ - "application/json": components["schemas"]["market-api_AccountRefundsResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "authentication_error", - * "message": "missing authentication token" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Forbidden - account frozen */ - 403: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "forbidden", - * "message": "Account is frozen" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "api_error", - * "message": "An internal server error occurred" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + responses: { + /** @description Batches created */ + 201: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_Batch"][]; + }; + }; + /** @description Invalid request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; }; - getAccountMe: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Account information retrieved successfully */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "id": "gmail-com-name", - * "role": "user", - * "is_frozen": false, - * "kyc": "basic", - * "submitted_waitlist": true, - * "waitlist": false, - * "kycb_form_submitted": false, - * "created_at": "2024-01-15T10:30:00Z" - * } */ - "application/json": components["schemas"]["market-api_AccountMeResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Account not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "not_found", - * "message": "account not found", - * "details": [], - * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + get_batch: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Batch identifier */ + batch_id: string; + }; + cookie?: never; }; - getBalance: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successfully retrieved balance */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "balance", - * "available_cents": 150000, - * "current_cents": 180000, - * "current_overage_cents": 150000, - * "current_hold_cents": 180000, - * "updated_at": 1640995200 - * } */ - "application/json": components["schemas"]["market-api_GetBalanceResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + requestBody?: never; + responses: { + /** @description Batch found */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_Batch"]; + }; + }; + /** @description Batch not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; }; - getCredits: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successfully retrieved credit balance */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "current_balance_cents": 150000, - * "available_credits_cents": 150000, - * "amount_due_next_billing_period_cents": 0 - * } */ - "application/json": components["schemas"]["market-api_GetCreditsResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + archive_batch: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Batch identifier */ + batch_id: string; + }; + cookie?: never; }; - migrateAccount: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - /** @description Migration request specifying the desired migration method */ - requestBody: { - content: { - "application/json": components["schemas"]["market-api_MigrateAccountRequest"]; - }; - }; - responses: { - /** @description Account successfully migrated */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "status": "migrated" - * } */ - "application/json": components["schemas"]["market-api_MigrateAccountResponse"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Account already migrated */ - 409: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "account_already_migrated", - * "message": "Account has already been migrated", - * "details": [], - * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "error": { - * "type": "internal_server_error", - * "message": "An internal server error occurred", - * "details": [], - * "request_id": "req_550e8400-e29b-41d4-a716-446655440000" - * } - * } */ - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Batch archived */ + 204: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; + /** @description Batch not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; }; - getTransactions: { - parameters: { - query?: { - /** @description Number of transactions to return (1-100, default 10) */ - limit?: number; - /** - * @description Filter for transactions after this UNIX timestamp (exclusive) - * @example 1640995200 - */ - after_time?: number; - /** - * @description Filter for transactions before this UNIX timestamp (exclusive) - * @example 1640995200 - */ - before_time?: number; - /** - * @description Cursor for forward pagination - * @example 1640995200 - */ - starting_after_cursor?: components["schemas"]["market-api_GetTransactionsCursor"]; - /** - * @description Cursor for backward pagination - * @example 1640995200 - */ - ending_before_cursor?: components["schemas"]["market-api_GetTransactionsCursor"]; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Successfully retrieved transactions */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - /** @example { - * "object": "list", - * "data": [ - * { - * "object": "transaction", - * "transaction_time": 1640995200, - * "amount_cents": 25000, - * "details": { - * "object": "transaction_details", - * "type": "credit_grant", - * "memo": "Promotional credit" - * } - * } - * ], - * "has_more": false - * } */ - "application/json": components["schemas"]["market-api_ListTransactionsResponse"]; - }; - }; - /** @description Invalid request parameters */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Unauthorized - missing or invalid authentication token */ - 401: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["market-api_SerdeErrorProxy"]; - }; - }; - }; + }; + cancel_batch: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Batch identifier */ + batch_id: string; + }; + cookie?: never; }; - list_batches: { - parameters: { - query: { - /** @description Max items to return */ - limit: number; - /** @description Pagination offset */ - after: number; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List of batches */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_Batch"][]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Batch cancelled */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_Batch"]; + }; + }; + /** @description Batch not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; + }; + }; }; - create_batches: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody: { - content: { - "application/json": components["schemas"]["large_scale_inference_BatchRequest"]; - }; + }; + health_check: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Service is healthy */ + 200: { + headers: { + [name: string]: unknown; }; - responses: { - /** @description Batches created */ - 201: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_Batch"][]; - }; - }; - /** @description Invalid request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; + content: { + "application/json": unknown; }; + }; }; - get_batch: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Batch identifier */ - batch_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Batch found */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_Batch"]; - }; - }; - /** @description Batch not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; - }; + }; + get_models: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; }; - archive_batch: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Batch identifier */ - batch_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Batch archived */ - 204: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - /** @description Batch not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; + requestBody?: never; + responses: { + /** @description List available models */ + 200: { + headers: { + [name: string]: unknown; }; - }; - cancel_batch: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Batch identifier */ - batch_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Batch cancelled */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_Batch"]; - }; - }; - /** @description Batch not found */ - 404: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_SerdeErrorProxy"]; - }; - }; + content: { + "application/json": components["schemas"]["large_scale_inference_Model"][]; }; + }; }; - health_check: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Service is healthy */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": unknown; - }; - }; - }; + }; + get_model_history: { + parameters: { + query?: never; + header?: never; + path: { + /** @description Model identifier */ + model_id: string; + }; + cookie?: never; }; - get_models: { - parameters: { - query?: never; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description List available models */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["large_scale_inference_Model"][]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Not implemented */ + 501: { + headers: { + [name: string]: unknown; + }; + content?: never; + }; }; - get_model_history: { - parameters: { - query?: never; - header?: never; - path: { - /** @description Model identifier */ - model_id: string; - }; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Not implemented */ - 501: { - headers: { - [name: string]: unknown; - }; - content?: never; - }; - }; + }; + handle_quote: { + parameters: { + query: { + /** @description Order side: buy or sell */ + side: string; + /** @description Number of nodes (1-1024) */ + quantity: number; + /** @description Instance type for buy orders (h100i, h100v, h200ki) */ + instance_type?: string; + /** @description Contract ID for sell orders */ + contract_id?: string; + /** @description Cluster constraint (hostname) */ + cluster?: string; + /** @description Contract ID to colocate with */ + colocate_with?: string; + /** @description Exact duration in seconds (mutually exclusive with min/max, minimum 3600) */ + duration?: number; + /** @description Min duration in seconds (minimum 3600, requires max_duration) */ + min_duration?: number; + /** @description Max duration in seconds (requires min_duration) */ + max_duration?: number; + /** @description Min start date: 'NOW' or ISO8601 */ + min_start_date?: string; + /** @description Max start date: 'NOW' or ISO8601 */ + max_start_date?: string; + }; + header?: never; + path?: never; + cookie?: never; }; - handle_quote: { - parameters: { - query: { - /** @description Order side: buy or sell */ - side: string; - /** @description Number of nodes (1-1024) */ - quantity: number; - /** @description Instance type for buy orders (h100i, h100v, h200ki) */ - instance_type?: string; - /** @description Contract ID for sell orders */ - contract_id?: string; - /** @description Cluster constraint (hostname) */ - cluster?: string; - /** @description Contract ID to colocate with */ - colocate_with?: string; - /** @description Exact duration in seconds (mutually exclusive with min/max, minimum 3600) */ - duration?: number; - /** @description Min duration in seconds (minimum 3600, requires max_duration) */ - min_duration?: number; - /** @description Max duration in seconds (requires min_duration) */ - max_duration?: number; - /** @description Min start date: 'NOW' or ISO8601 */ - min_start_date?: string; - /** @description Max start date: 'NOW' or ISO8601 */ - max_start_date?: string; - }; - header?: never; - path?: never; - cookie?: never; - }; - requestBody?: never; - responses: { - /** @description Quote retrieved */ - 200: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["quoter_ApiQuoteResponse"]; - }; - }; - /** @description Invalid request */ - 400: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["quoter_ErrorType"]; - }; - }; - /** @description Internal server error */ - 500: { - headers: { - [name: string]: unknown; - }; - content: { - "application/json": components["schemas"]["quoter_ErrorType"]; - }; - }; - }; + requestBody?: never; + responses: { + /** @description Quote retrieved */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["quoter_ApiQuoteResponse"]; + }; + }; + /** @description Invalid request */ + 400: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["quoter_ErrorType"]; + }; + }; + /** @description Internal server error */ + 500: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["quoter_ErrorType"]; + }; + }; }; + }; } From 9ac79246ac037faae3d7c4f41ce887dea2204fdc Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 03:24:37 -0800 Subject: [PATCH 14/48] docs: add Deno to Node.js migration plan Comprehensive migration plan documenting: - CI workflow updates - Release process changes (deno compile -> pkg) - Auto-upgrade compatibility considerations - Deno-specific packages replaced (jsr:, npm:, Deno.* APIs) - Configuration changes (tsconfig, biome, vitest) - Migration phases and execution order --- MIGRATION_PLAN.md | 431 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 431 insertions(+) create mode 100644 MIGRATION_PLAN.md diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md new file mode 100644 index 00000000..3050e8ad --- /dev/null +++ b/MIGRATION_PLAN.md @@ -0,0 +1,431 @@ +# Deno to Node.js Migration Plan + +## Overview + +This document outlines the migration strategy for the SF CLI from Deno to Node.js. The CLI is currently a Deno-native application that uses npm packages via Deno's compatibility layer. + +--- + +## 1. CI Updates + +### Current Setup (`.github/workflows/ci.yml`) +```yaml +- uses: denoland/setup-deno@v2 +- run: deno install +- run: deno fmt --check +- run: deno lint +- run: deno check --config deno.json . +- run: deno test --allow-all +``` + +### Migration Steps + +1. **Replace Deno setup with Node.js setup:** + ```yaml + - uses: actions/setup-node@v4 + with: + node-version-file: '.tool-versions' # or .nvmrc + cache: 'npm' + - run: npm ci + ``` + +2. **Replace linting/formatting tools:** + | Deno Command | Node.js Replacement | + |--------------|---------------------| + | `deno fmt --check` | `biome format --check .` | + | `deno lint` | `biome lint .` | + | `deno check` | `tsc --noEmit` | + | `deno test` | `vitest` | + + **Note:** This project used Biome before migrating to Deno. Run `git log --all --oneline -- biome.json` to find the commit where Biome was removed, then extract the old `biome.json` config from that commit. + +3. **New CI workflow:** + ```yaml + name: CLI (Check) + on: + push: + branches: [main] + pull_request: + branches: [main] + + jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + - run: npm ci + - run: npm run lint + - run: npm run check + - run: npm test + ``` + +4. **New package.json scripts:** + ```json + { + "scripts": { + "dev": "IS_DEVELOPMENT_CLI_ENV=true tsx src/index.ts", + "prod": "tsx src/index.ts", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "check": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest" + } + } + ``` + + **Note:** Retain both `dev` and `prod` scripts from the original Deno setup. The `dev` script sets `IS_DEVELOPMENT_CLI_ENV=true` which configures the CLI to use localhost API endpoints. + +--- + +## 2. Release Process Updates + +### Current Setup (`release.yml` + `src/scripts/release.ts`) +- Uses `deno compile` for cross-platform binaries +- Targets: Linux x64/arm64, macOS x64/arm64 +- Creates zip files and uploads to GitHub releases + +### Compilation Tool: `@yao-pkg/pkg` + +Use `@yao-pkg/pkg` (community fork of Vercel's pkg) for cross-platform binary compilation. + +```bash +npm install -D @yao-pkg/pkg +``` + +Compile command: +```bash +pkg . --targets node20-linux-x64,node20-linux-arm64,node20-macos-x64,node20-macos-arm64 +``` + +**Note:** pkg binaries are larger than Deno compiled binaries, but pkg is mature and well-tested. + +Update `src/scripts/release.ts`: +```typescript +const targets = [ + 'node20-linux-x64', + 'node20-linux-arm64', + 'node20-macos-x64', + 'node20-macos-arm64', +]; + +async function compileDistribution() { + // Replace Deno.Command with child_process + const { execSync } = await import('child_process'); + + for (const target of targets) { + const outputName = target.replace('node20-', 'sf-').replace('-', '-'); + execSync(`npx pkg . --target ${target} --output dist/${outputName}`, { + stdio: 'inherit' + }); + // Create zip... + } +} +``` + +--- + +## 3. Auto-Upgrade Compatibility + +### Critical Files +- `src/checkVersion.ts` - Version checking and auto-upgrade logic +- `src/lib/upgrade.ts` - Manual upgrade command +- `install.sh` - Installation script + +### What Stays the Same +1. **Version check URL** - `https://raw.githubusercontent.com/sfcompute/cli/refs/heads/main/package.json` +2. **Binary download URLs** - `https://github.com/sfcompute/cli/releases/download/{version}/sf-{target}.zip` +3. **Install script URL** - `https://www.sfcompute.com/cli/install` +4. **Binary naming convention** - `sf-{target}.zip` + +### What Needs to Change + +1. **Binary target names** (in `install.sh`): + ```bash + # Old (Deno targets) + x86_64-unknown-linux-gnu + aarch64-unknown-linux-gnu + x86_64-apple-darwin + aarch64-apple-darwin + + # New (pkg targets) - KEEP THE SAME OUTPUT NAMES + # Map pkg output to same names for backwards compatibility + ``` + +2. **Maintain backwards compatibility** by keeping the same zip file names: + - `sf-x86_64-unknown-linux-gnu.zip` + - `sf-aarch64-unknown-linux-gnu.zip` + - `sf-x86_64-apple-darwin.zip` + - `sf-aarch64-apple-darwin.zip` + +3. **Update `src/lib/upgrade.ts`**: + ```typescript + // Replace Deno.Command with child_process + import { spawn } from 'child_process'; + + const upgradeProcess = spawn('bash', ['-c', `curl -fsSL ${installScriptUrl} | bash`], { + env: { ...process.env, SF_CLI_VERSION: version }, + stdio: 'inherit' + }); + ``` + +4. **Update `src/scripts/release.ts`**: + - Replace all `Deno.Command` with `child_process.execSync` + - Replace `Deno.readTextFile` with `fs.readFileSync` + - Replace `Deno.writeTextFile` with `fs.writeFileSync` + +### Migration Safety + +To ensure existing users can upgrade: +1. **Keep the same binary names** in GitHub releases +2. **Keep the same install.sh script** (it's platform-detection only) +3. **First Node.js release** should be a minor version bump to signal the change +4. **Test auto-upgrade** from last Deno version to first Node.js version + +--- + +## 4. Deno-Specific Packages to Replace + +### JSR Packages + +| Deno Import | Node.js Replacement | Install | +|-------------|---------------------|---------| +| `jsr:@std/fmt/colors` | `chalk` | `npm i chalk` | +| `jsr:@std/assert` | `vitest` | `npm i -D vitest` | + +**Files affected:** 24+ files use `jsr:@std/fmt/colors` + +**Migration example:** +```typescript +// Before (Deno) +import { cyan, gray, yellow } from "jsr:@std/fmt/colors"; +console.log(cyan('text')); + +// After (Node.js with chalk) +import chalk from 'chalk'; +console.log(chalk.cyan('text')); +``` + +### NPM: Namespace Imports + +These imports use `npm:` prefix which is Deno-specific: + +| Current Import | Change To | +|----------------|-----------| +| `import boxen from "npm:boxen@8.0.1"` | `import boxen from "boxen"` | +| `import * as nacl from "npm:tweetnacl"` | `import * as nacl from "tweetnacl"` | +| `import util from "npm:tweetnacl-util"` | `import util from "tweetnacl-util"` | +| `import cliSpinners from "npm:cli-spinners"` | `import cliSpinners from "cli-spinners"` | +| `import yn from "npm:yn"` | `import yn from "yn"` | + +**Files affected:** 6 files + +### Deno.* APIs + +| Deno API | Node.js Replacement | Notes | +|----------|---------------------|-------| +| `Deno.readTextFile()` | `fs.promises.readFile(path, 'utf-8')` | 11 usages | +| `Deno.writeTextFile()` | `fs.promises.writeFile(path, content)` | 10 usages | +| `Deno.mkdir()` | `fs.promises.mkdir(path, { recursive: true })` | 5 usages | +| `Deno.remove()` | `fs.promises.rm(path, { recursive: true })` | 1 usage | +| `Deno.stat()` | `fs.promises.stat(path)` | 2 usages | +| `Deno.chmod()` | `fs.promises.chmod(path, mode)` | 1 usage | +| `Deno.open()` | `fs.promises.open(path)` | 2 usages | +| `Deno.readDirSync()` | `fs.readdirSync(path)` | 1 usage | +| `Deno.Command` | `child_process.spawn()` | 4 usages | +| `Deno.exit()` | `process.exit()` | 1 usage | +| `Deno.errors.NotFound` | Check `err.code === 'ENOENT'` | 2 usages | +| `Deno.errors.AlreadyExists` | Check `err.code === 'EEXIST'` | 1 usage | +| `Deno.SeekMode.Start` | Use `fileHandle.read()` with position | 1 usage | +| `Deno.test()` | `test()` from vitest/jest | 21 tests | + +**Files requiring Deno API migration:** +1. `src/scripts/release.ts` +2. `src/lib/nodes/image/upload.ts` +3. `src/lib/clusters/kubeconfig.ts` +4. `src/lib/clusters/keys.tsx` +5. `src/helpers/config.ts` +6. `src/helpers/feature-flags.ts` +7. `src/lib/upgrade.ts` + +### Test Files + +| Current | Migration | +|---------|-----------| +| `Deno.test('name', fn)` | `test('name', fn)` (vitest) | +| `import { assertEquals } from "jsr:@std/assert"` | `import { expect } from 'vitest'` | + +**Test files to migrate:** +- `src/helpers/test/units.test.ts` +- `src/helpers/test/duration.test.ts` +- `src/lib/orders/__tests__/OrderDisplay.test.ts` +- `src/lib/clusters/kubeconfig.test.ts` +- `src/lib/clusters/utils.test.ts` + +--- + +## 5. Configuration Changes + +### Remove +- `deno.json` +- `deno.lock` + +### Add/Update + +**tsconfig.json:** +```json +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "outDir": "dist", + "rootDir": "src", + "declaration": true, + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + } + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} +``` + +**Note:** After adding path aliases, you'll need to configure `tsx` and `pkg` to resolve them. For `pkg`, you may need to bundle first with a tool like `tsup` or `esbuild` that resolves the paths, or use `tsc-alias` to rewrite imports after compilation. + +**biome.json:** + +Retrieve the old Biome config from git history: +```bash +# Find the commit where biome.json was removed +git log --all --oneline --diff-filter=D -- biome.json + +# Show the file contents from that commit's parent +git show ^:biome.json > biome.json +``` + +Or create a new config: +```json +{ + "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2 + }, + "files": { + "ignore": ["dist/", "src/schema.ts", "node_modules/"] + } +} +``` + +**vitest.config.ts:** +```typescript +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + }, +}); +``` + +**.tool-versions update:** +``` +nodejs 20.19.0 +# Remove deno line +``` + +--- + +## 6. New Dependencies to Add + +```bash +npm install -D \ + typescript \ + tsx \ + vitest \ + @biomejs/biome \ + @yao-pkg/pkg \ + @types/node + +npm install \ + chalk \ + cli-spinners +``` + +--- + +## 7. Migration Order + +1. **Phase 1: Setup** + - Add tsconfig.json with `baseUrl` and `paths` for absolute imports + - Restore biome.json from git history (run `git log --all --oneline --diff-filter=D -- biome.json`) + - Add vitest config + - Update package.json scripts + +2. **Phase 2: Convert to Absolute Imports** + - Configure tsconfig.json paths (e.g., `"@/*": ["./src/*"]`) + - Update all relative imports to use absolute imports + - Example: `import { getConfig } from "../../helpers/config"` → `import { getConfig } from "@/helpers/config"` + - This makes the codebase easier to navigate and refactor + +3. **Phase 3: Replace Deno APIs** (7 files) + - Start with helpers (config.ts, feature-flags.ts) + - Then lib files (upgrade.ts, clusters/*, nodes/image/upload.ts) + - Finally release.ts + +4. **Phase 4: Replace imports** (30+ files) + - Replace `jsr:@std/fmt/colors` with `chalk` + - Remove `npm:` prefixes from imports + +5. **Phase 5: Migrate tests** (5 files) + - Replace Deno.test with vitest + - Replace @std/assert with vitest expects + +6. **Phase 6: Update build/release** + - Update release.ts for pkg + - Update CI workflows + - Test cross-compilation + +7. **Phase 7: Cleanup** + - Remove deno.json + - Remove deno.lock + - Update .tool-versions + - Update README + +8. **Phase 8: Test auto-upgrade path** + - Install old Deno version + - Release new Node.js version + - Verify auto-upgrade works + +--- + +## 9. Risk Mitigation + +1. **Binary size increase**: pkg binaries are larger than Deno compiled binaries. This is an acceptable tradeoff for pkg's maturity and reliability. + +2. **Platform compatibility**: Test all 4 targets before release. + +3. **Auto-upgrade breakage**: Keep exact same zip file names and install.sh logic. + +4. **ESM/CJS issues**: The codebase uses ESM. Ensure all dependencies support ESM or use dynamic imports. + +5. **React/Ink compatibility**: Ink works fine with Node.js, but test thoroughly. From 098a68643ab2b7aec47f2847960247f717019f9d Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 15:47:28 -0800 Subject: [PATCH 15/48] fix: wrap entry point in async main function Fixes tsx warning about unsettled top-level await by wrapping the entire script in an async main() function. Co-Authored-By: Claude Opus 4.5 --- src/index.ts | 246 ++++++++++++++++++++++++++------------------------- 1 file changed, 125 insertions(+), 121 deletions(-) diff --git a/src/index.ts b/src/index.ts index 966a8541..e42ebc17 100644 --- a/src/index.ts +++ b/src/index.ts @@ -26,138 +26,142 @@ import { registerUpgrade } from "./lib/upgrade.ts"; import { registerVM } from "./lib/vm/index.ts"; import { registerZones } from "./lib/zones.tsx"; -const program = new Command(); +async function main() { + const program = new Command(); -if (!process.argv.includes("--json")) { - await Promise.all([checkVersion(), getAppBanner()]); -} + if (!process.argv.includes("--json")) { + await Promise.all([checkVersion(), getAppBanner()]); + } -program - .name("sf") - .description("The San Francisco Compute command line tool.") - .version(pkg.version); - -// commands -registerLogin(program); -registerBuy(program); -registerExtend(program); -registerOrders(program); -registerContracts(program); -registerSell(program); -registerBalance(program); -registerTokens(program); -registerUpgrade(program); -await registerScale(program); -registerMe(program); -await registerVM(program); -await registerNodes(program); -await registerZones(program); - -// (development commands) -registerDev(program); - -if (IS_TRACKING_DISABLED) { - await program.parseAsync(process.argv); -} else { - // Add global process exit handlers to ensure analytics cleanup - let isShuttingDown = false; - const ensureAnalyticsShutdown = async () => { - if (!isShuttingDown) { - isShuttingDown = true; - try { - await analytics.shutdown(); - } catch (_err) { - // Silently ignore analytics shutdown errors + program + .name("sf") + .description("The San Francisco Compute command line tool.") + .version(pkg.version); + + // commands + registerLogin(program); + registerBuy(program); + registerExtend(program); + registerOrders(program); + registerContracts(program); + registerSell(program); + registerBalance(program); + registerTokens(program); + registerUpgrade(program); + await registerScale(program); + registerMe(program); + await registerVM(program); + await registerNodes(program); + await registerZones(program); + + // (development commands) + registerDev(program); + + if (IS_TRACKING_DISABLED) { + await program.parseAsync(process.argv); + } else { + // Add global process exit handlers to ensure analytics cleanup + let isShuttingDown = false; + const ensureAnalyticsShutdown = async () => { + if (!isShuttingDown) { + isShuttingDown = true; + try { + await analytics.shutdown(); + } catch (_err) { + // Silently ignore analytics shutdown errors + } } - } - }; - - process.on("beforeExit", ensureAnalyticsShutdown); - process.on("SIGINT", async () => { - await ensureAnalyticsShutdown(); - process.exit(130); - }); - process.on("SIGTERM", async () => { - await ensureAnalyticsShutdown(); - process.exit(0); - }); - - const config = await loadConfig(); - let exchangeAccountId = config.account_id; - - if (!exchangeAccountId) { - const response = await fetch(await getApiUrl("me"), { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${config.auth_token}`, - }, + }; + + process.on("beforeExit", ensureAnalyticsShutdown); + process.on("SIGINT", async () => { + await ensureAnalyticsShutdown(); + process.exit(130); + }); + process.on("SIGTERM", async () => { + await ensureAnalyticsShutdown(); + process.exit(0); }); - const data = (await response.json()) as any; - if (data.id) { - exchangeAccountId = data.id; - saveConfig({ ...config, account_id: data.id }); + const config = await loadConfig(); + let exchangeAccountId = config.account_id; + + if (!exchangeAccountId) { + const response = await fetch(await getApiUrl("me"), { + method: "GET", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${config.auth_token}`, + }, + }); + + const data = (await response.json()) as any; + if (data.id) { + exchangeAccountId = data.id; + saveConfig({ ...config, account_id: data.id }); + } } - } - program.exitOverride((error) => { - let isError = true; + program.exitOverride((error) => { + let isError = true; - switch (error.code) { - case "commander.helpDisplayed": - case "commander.help": - case "commander.version": - isError = false; - break; - } - - process.exit(isError ? 1 : 0); - }); - - if (exchangeAccountId) { - const args = process.argv.slice(2).reduce((acc, arg, i, arr) => { - if (arg.startsWith("--")) { - const key = arg.slice(2); - const nextArg = arr[i + 1]; - if (nextArg && !nextArg.startsWith("-")) { - (acc as Record)[key] = isNaN( - Number(nextArg), - ) - ? nextArg - : Number(nextArg); - } else { - (acc as Record)[key] = true; - } + switch (error.code) { + case "commander.helpDisplayed": + case "commander.help": + case "commander.version": + isError = false; + break; } - return acc; - }, {}); - - analytics.track({ - event: `${process.argv[2] || "unknown"}${ - process.argv[3] ? "_" + process.argv[3] : "" - }`, - properties: { - ...args, - shell: process.env.SHELL, - os: os.platform(), - cliVersion: program.version(), - argsRaw: process.argv.slice(2).join(" "), - }, + + process.exit(isError ? 1 : 0); }); - } - try { - await program.parseAsync(process.argv); + if (exchangeAccountId) { + const args = process.argv.slice(2).reduce((acc, arg, i, arr) => { + if (arg.startsWith("--")) { + const key = arg.slice(2); + const nextArg = arr[i + 1]; + if (nextArg && !nextArg.startsWith("-")) { + (acc as Record)[key] = isNaN( + Number(nextArg), + ) + ? nextArg + : Number(nextArg); + } else { + (acc as Record)[key] = true; + } + } + return acc; + }, {}); + + analytics.track({ + event: `${process.argv[2] || "unknown"}${ + process.argv[3] ? "_" + process.argv[3] : "" + }`, + properties: { + ...args, + shell: process.env.SHELL, + os: os.platform(), + cliVersion: program.version(), + argsRaw: process.argv.slice(2).join(" "), + }, + }); + } - // Add cleanup only when the process would naturally exit but PostHog keeps it alive - // This only triggers when the event loop empties (command is done) but process doesn't exit - process.on("beforeExit", async () => { - await ensureAnalyticsShutdown(); - process.exit(0); - }); - } catch (err) { - console.log(err); - process.exit(1); + try { + await program.parseAsync(process.argv); + + // Add cleanup only when the process would naturally exit but PostHog keeps it alive + // This only triggers when the event loop empties (command is done) but process doesn't exit + process.on("beforeExit", async () => { + await ensureAnalyticsShutdown(); + process.exit(0); + }); + } catch (err) { + console.log(err); + process.exit(1); + } } } + +main(); From 769a6cbdb99292059a1d6265ffbb080e44a7c451 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 15:47:33 -0800 Subject: [PATCH 16/48] ci: add biome setup action to workflow Use biomejs/setup-biome@v2 for proper biome installation and run biome ci for optimized CI output. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a8b81a9..bfdee691 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,8 @@ jobs: with: node-version: '20' cache: 'npm' + - uses: biomejs/setup-biome@v2 - run: npm ci - - run: npm run lint + - run: biome ci . - run: npm run check - run: npm test From a972ee11f2cf9923440d77c0322e8bf0c69c388e Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:44:17 -0800 Subject: [PATCH 17/48] refactor: use pkg target names directly Update release script and install.sh to use pkg target names (node20-linux-x64, etc.) instead of mapping to Deno-style names. Co-Authored-By: Claude Opus 4.5 --- install.sh | 8 ++++---- src/scripts/release.ts | 33 ++++++++++++++------------------- 2 files changed, 18 insertions(+), 23 deletions(-) diff --git a/install.sh b/install.sh index 942f3e7b..9b045b2f 100755 --- a/install.sh +++ b/install.sh @@ -46,10 +46,10 @@ TARGET_FILE="${TARGET_DIR}/${BINARY_NAME}" if [ "$OS" = "Linux" ]; then case "${ARCH}" in x86_64) - target='x86_64-unknown-linux-gnu' + target='node20-linux-x64' ;; aarch64) - target='aarch64-unknown-linux-gnu' + target='node20-linux-arm64' ;; *) echo "Unsupported Linux architecture: ${ARCH}" >&2 @@ -59,10 +59,10 @@ if [ "$OS" = "Linux" ]; then elif [ "$OS" = "Darwin" ]; then case "${ARCH}" in x86_64) - target='x86_64-apple-darwin' + target='node20-macos-x64' ;; arm64) - target='aarch64-apple-darwin' + target='node20-macos-arm64' ;; *) echo "Unsupported macOS architecture: ${ARCH}" >&2 diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 5fecd66c..eb0aaca8 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -48,46 +48,41 @@ async function saveVersion(version: string) { fs.writeFileSync("package.json", `${JSON.stringify(packageObj, null, 2)}\n`); } -// Map pkg targets to output names (keeping original names for backward compatibility) -const COMPILE_TARGETS: { pkgTarget: string; outputName: string }[] = [ - { pkgTarget: "node20-linux-x64", outputName: "x86_64-unknown-linux-gnu" }, - { pkgTarget: "node20-linux-arm64", outputName: "aarch64-unknown-linux-gnu" }, - { pkgTarget: "node20-macos-x64", outputName: "x86_64-apple-darwin" }, - { pkgTarget: "node20-macos-arm64", outputName: "aarch64-apple-darwin" }, +const COMPILE_TARGETS: string[] = [ + "node20-linux-x64", + "node20-linux-arm64", + "node20-macos-x64", + "node20-macos-arm64", ]; async function compileDistribution() { // Create dist directory fs.mkdirSync("./dist", { recursive: true }); - for (const { pkgTarget, outputName } of COMPILE_TARGETS) { + for (const target of COMPILE_TARGETS) { const result = spawnSync("npx", [ "pkg", ".", "--target", - pkgTarget, + target, "--output", - `dist/sf-${outputName}`, + `dist/sf-${target}`, ]); if (result.status !== 0) { console.error(result.stderr?.toString() ?? ""); - logAndError(`Failed to compile for ${pkgTarget}`); + logAndError(`Failed to compile for ${target}`); } - console.log(`✅ Compiled for ${outputName}`); + console.log(`✅ Compiled for ${target}`); - const zipFileName = `dist/sf-${outputName}.zip`; - const zipResult = spawnSync("zip", [ - "-j", - zipFileName, - `dist/sf-${outputName}`, - ]); + const zipFileName = `dist/sf-${target}.zip`; + const zipResult = spawnSync("zip", ["-j", zipFileName, `dist/sf-${target}`]); if (zipResult.status !== 0) { console.error(zipResult.stderr?.toString() ?? ""); - logAndError(`Failed to zip the binary for ${outputName}`); + logAndError(`Failed to zip the binary for ${target}`); } - console.log(`✅ Zipped binary for ${outputName}`); + console.log(`✅ Zipped binary for ${target}`); } } From 805e70907417d6baa60bfdcb9ac63f5221ccb590 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:44:39 -0800 Subject: [PATCH 18/48] docs: update README for Node.js migration - Replace Deno setup instructions with Node.js - Update development commands (npm run dev/prod) - Update CI quality checks (biome, tsc, vitest) Co-Authored-By: Claude Opus 4.5 --- README.md | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index e40cfafc..09086127 100644 --- a/README.md +++ b/README.md @@ -20,21 +20,19 @@ sf --version # 0.1.0 ### Setup -- Install [Deno](https://docs.deno.com/runtime/) - - `2.2.3` -- Install dependencies `deno install` - - Use same mental model as `npm install` -- Auth your CLI with `deno run prod login` +- Install [Node.js](https://nodejs.org/) (v20 or later) - used as the runtime +- Install [Bun](https://bun.sh/) - used as the package manager +- Install dependencies: `bun install` +- Auth your CLI with `bun run prod login` ### Development Loop - Make code changes - Test changes with - - `deno run devv` to test against local API - - `deno run prod` to test against production API - - The `deno run ` is an alias to the user facing `sf` command. So if you - wanted to run `sf login` locally against the local API, run - `deno run devv login` + - `bun run dev` to test against local API + - `bun run prod` to test against production API + - These are aliases to the user facing `sf` command. So if you wanted to run + `sf login` locally against the local API, run `bun run dev login` ## New Release @@ -78,10 +76,9 @@ Releases are managed through GitHub Actions. To create a new release: The workflow will: - Run quality checks: - - Format checking with `deno fmt` - - Linting with `deno lint` - - Type checking with `deno check` - - Run all tests with `deno test --allow-all` + - Linting with `biome ci` + - Type checking with `tsc --noEmit` + - Run all tests with `vitest` - Bump the version in package.json - Create a new GitHub release with compiled binaries - Push the version bump commit back to main From 55935a9e7d3dffda494b3bd7a0318a3cdad6dee8 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:55:30 -0800 Subject: [PATCH 19/48] style: remove unnecessary type stripping --- src/lib/nodes/image/upload.ts | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/lib/nodes/image/upload.ts b/src/lib/nodes/image/upload.ts index 32f1b2f6..aa6ec9ca 100644 --- a/src/lib/nodes/image/upload.ts +++ b/src/lib/nodes/image/upload.ts @@ -94,7 +94,9 @@ const upload = new Command("upload") const imageId = startResponse.data.image_id; preparingSpinner.succeed( - `Started upload for image ${chalk.cyan(name)} (${chalk.blackBright(imageId)})`, + `Started upload for image ${chalk.cyan(name)} (${ + chalk.blackBright(imageId) + })`, ); // Get file info and open as stream @@ -105,9 +107,11 @@ const upload = new Command("upload") const maxFileSize = 128 * 1024 * 1024 * 1024; // 128 GiB in bytes if (fileSize > maxFileSize) { logAndQuit( - `File size exceeds maximum allowed size of 128 GiB. File size: ${( - fileSize / (1024 * 1024 * 1024) - ).toFixed(2)} GiB`, + `File size exceeds maximum allowed size of 128 GiB. File size: ${ + ( + fileSize / (1024 * 1024 * 1024) + ).toFixed(2) + } GiB`, ); } @@ -118,10 +122,9 @@ const upload = new Command("upload") // For files smaller than default chunk, use the whole file as one part // Otherwise use default chunk size, but ensure we don't exceed maxParts - const chunkSize = - fileSize <= defaultChunk - ? Math.max(fileSize, minChunk) - : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); + const chunkSize = fileSize <= defaultChunk + ? Math.max(fileSize, minChunk) + : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); const totalParts = Math.ceil(fileSize / chunkSize); @@ -158,7 +161,8 @@ const upload = new Command("upload") let spinnerIndex = 0; progressBar = new cliProgress.SingleBar({ - format: `{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}`, + format: + `{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}`, barCompleteChar: "\u2588", barIncompleteChar: "\u2591", hideCursor: true, @@ -296,7 +300,7 @@ const upload = new Command("upload") headers: { "Content-Type": "application/octet-stream", }, - body: payload as unknown as BodyInit, + body: payload as BodyInit, }); if (!res.ok) { @@ -426,7 +430,9 @@ const upload = new Command("upload") ); } else { console.error( - `\n${chalk.red("✗")} ${err instanceof Error ? err.message : String(err)}`, + `\n${chalk.red("✗")} ${ + err instanceof Error ? err.message : String(err) + }`, ); } process.exit(1); From 4159c3a48a1496b7acf518ef89ab0cf46fde822d Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:55:38 -0800 Subject: [PATCH 20/48] fix: remove duplicate import and use chalk for colors in zones.tsx Co-Authored-By: Claude Opus 4.5 --- src/lib/zones.tsx | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/lib/zones.tsx b/src/lib/zones.tsx index 08710b27..a4501d78 100644 --- a/src/lib/zones.tsx +++ b/src/lib/zones.tsx @@ -13,7 +13,6 @@ import { } from "../helpers/errors.ts"; import type { components } from "../schema.ts"; import { isFeatureEnabled } from "./posthog.ts"; -import { components } from "../schema.ts"; type ZoneInfo = components["schemas"]["node-api_ZoneInfo"]; @@ -150,12 +149,12 @@ function displayZonesTable(zones: ZoneInfo[]) { const table = new Table({ head: [ - cyan("Zone"), - cyan("Delivery Type"), - cyan("Available Nodes"), - cyan("GPU Type"), - cyan("Interconnect"), - cyan("Region"), + chalk.cyan("Zone"), + chalk.cyan("Delivery Type"), + chalk.cyan("Available Nodes"), + chalk.cyan("GPU Type"), + chalk.cyan("Interconnect"), + chalk.cyan("Region"), ], style: { head: [], @@ -166,8 +165,8 @@ function displayZonesTable(zones: ZoneInfo[]) { sortedZones.forEach((zone) => { const available = getCurrentAvailableCapacity(zone); const availableNodesText = available > 0 - ? green(available.toString()) - : red(available.toString()); + ? chalk.green(available.toString()) + : chalk.red(available.toString()); table.push([ zone.name, @@ -185,11 +184,11 @@ function displayZonesTable(zones: ZoneInfo[]) { const availableZoneName = availableZones?.[0]?.name ?? "alamo"; console.log(table.toString()); console.log( - `\n${gray("Use zone names when placing orders or configuring nodes.")}\n`, + `\n${chalk.gray("Use zone names when placing orders or configuring nodes.")}\n`, ); - console.log(gray("Examples:")); - console.log(` sf buy --zone ${green(availableZoneName)}`); - console.log(` sf scale create -n 16 --zone ${green(availableZoneName)}`); + console.log(chalk.gray("Examples:")); + console.log(` sf buy --zone ${chalk.green(availableZoneName)}`); + console.log(` sf scale create -n 16 --zone ${chalk.green(availableZoneName)}`); } function EmptyZonesDisplay() { From 136bfa98a521b8d8e803c2fda59b63b590a303ab Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:55:59 -0800 Subject: [PATCH 21/48] style: address Greptile style nits --- src/lib/upgrade.ts | 7 ++++--- src/scripts/release.ts | 28 +++++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/lib/upgrade.ts b/src/lib/upgrade.ts index d30881fb..a3c71155 100644 --- a/src/lib/upgrade.ts +++ b/src/lib/upgrade.ts @@ -39,7 +39,8 @@ export function registerUpgrade(program: Command) { if (version) { spinner.start(`Checking if version ${version} exists`); - const url = `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; + const url = + `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; const response = await fetch(url, { method: "HEAD" }); if (response.status === 404) { @@ -84,11 +85,11 @@ export function registerUpgrade(program: Command) { let stdout = ""; let stderr = ""; - bashProcess.stdout.on("data", (data: Buffer) => { + bashProcess.stdout.on("data", (data) => { stdout += data.toString(); }); - bashProcess.stderr.on("data", (data: Buffer) => { + bashProcess.stderr.on("data", (data) => { stderr += data.toString(); }); diff --git a/src/scripts/release.ts b/src/scripts/release.ts index eb0aaca8..59d4190c 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -19,7 +19,7 @@ function bumpVersion( Number.parseInt( // Remove everything after the - if there is one v.includes("-") ? v.split("-")[0] : v, - ), + ) ); switch (type) { case "major": @@ -35,12 +35,12 @@ function bumpVersion( } } -async function getLocalVersion() { +function getLocalVersion() { const packageJson = fs.readFileSync("package.json", "utf-8"); return JSON.parse(packageJson).version; } -async function saveVersion(version: string) { +function saveVersion(version: string) { const packageJson = fs.readFileSync("package.json", "utf-8"); const packageObj = JSON.parse(packageJson); packageObj.version = version; @@ -76,7 +76,11 @@ async function compileDistribution() { console.log(`✅ Compiled for ${target}`); const zipFileName = `dist/sf-${target}.zip`; - const zipResult = spawnSync("zip", ["-j", zipFileName, `dist/sf-${target}`]); + const zipResult = spawnSync("zip", [ + "-j", + zipFileName, + `dist/sf-${target}`, + ]); if (zipResult.status !== 0) { console.error(zipResult.stderr?.toString() ?? ""); @@ -162,7 +166,7 @@ async function createRelease(version: string) { console.log("✅ Pushed to origin main"); } -async function cleanDist() { +function cleanDist() { fs.rmSync("./dist", { recursive: true, force: true }); } @@ -172,12 +176,14 @@ program "A github release tool for the project. Valid types are: major, minor, patch, prerelease", ) .addArgument( - new Argument("type").choices([ - "major", - "minor", - "patch", - "prerelease", - ] as const), + new Argument("type").choices( + [ + "major", + "minor", + "patch", + "prerelease", + ] as const, + ), ) .action(async (type) => { try { From dccccc766a6b18b7f8d9b70c18d38667831c61ec Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 16:57:06 -0800 Subject: [PATCH 22/48] ci: replace setup-node with setup-bun in workflows Use oven-sh/setup-bun@v2 and bun commands instead of actions/setup-node@v4 with npm. Bun is the package manager. Co-Authored-By: Claude Opus 4.5 --- .github/workflows/ci.yml | 11 ++++------- .github/workflows/release.yml | 17 +++++++---------- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bfdee691..9100c1f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,12 +12,9 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' + - uses: oven-sh/setup-bun@v2 - uses: biomejs/setup-biome@v2 - - run: npm ci + - run: bun install - run: biome ci . - - run: npm run check - - run: npm test + - run: bun run check + - run: bun test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b2d7f7ff..d653d623 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,23 +29,20 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' + - name: Setup Bun + uses: oven-sh/setup-bun@v2 - name: Install dependencies - run: npm ci + run: bun install - name: Lint - run: npm run lint + run: bun run lint - name: Type check - run: npm run check + run: bun run check - name: Run tests - run: npm test + run: bun test - name: Configure Git run: | @@ -56,4 +53,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - npx tsx src/scripts/release.ts ${{ inputs.version_type }} + bun run src/scripts/release.ts ${{ inputs.version_type }} From 995098fac46d9aee372cff6103d6983e7ca261b9 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:06:01 -0800 Subject: [PATCH 23/48] chore: update VSCode settings to use biome as the default formatter Replaced Deno formatter with Biome for various file types in settings.json and updated extension recommendations accordingly. --- .vscode/extensions.json | 2 +- .vscode/settings.json | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 74baffcc..699ed733 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["denoland.vscode-deno"] + "recommendations": ["biomejs.biome"] } diff --git a/.vscode/settings.json b/.vscode/settings.json index e710176f..719479f4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,18 +1,25 @@ { - "deno.enable": true, - "deno.lint": true, "editor.formatOnSave": true, - "editor.defaultFormatter": "denoland.vscode-deno", - "[typescriptreact]": { - "editor.defaultFormatter": "denoland.vscode-deno" + "editor.defaultFormatter": "biomejs.biome", + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" }, "[typescript]": { - "editor.defaultFormatter": "denoland.vscode-deno" + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" }, "[javascript]": { - "editor.defaultFormatter": "denoland.vscode-deno" + "editor.defaultFormatter": "biomejs.biome" }, "[javascriptreact]": { - "editor.defaultFormatter": "denoland.vscode-deno" - } + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "typescript.tsdk": "node_modules/typescript/lib", + "typescript.enablePromptUseWorkspaceTsdk": true } From c73adbfba4c526d3efb8c735930840b4b76c4ae6 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:06:28 -0800 Subject: [PATCH 24/48] style: run `biome check` - Updated number parsing to use Number.isNaN for clarity. - Streamlined string formatting in various log messages and commands for consistency. - Removed unnecessary type stripping and improved readability in several files. --- src/helpers/errors.ts | 4 ++-- src/index.ts | 7 ++----- src/lib/buy/index.tsx | 4 ++-- src/lib/extend/index.tsx | 6 +++--- src/lib/nodes/create.ts | 6 +++--- src/lib/nodes/extend.ts | 2 +- src/lib/nodes/image/upload.ts | 29 +++++++++++++------------- src/lib/nodes/list.tsx | 12 +++++------ src/lib/nodes/redeploy.ts | 6 +++--- src/lib/nodes/ssh.ts | 2 +- src/lib/nodes/utils.ts | 7 +++---- src/lib/scale/create.tsx | 6 +++--- src/lib/scale/update.tsx | 7 +++---- src/lib/scale/utils.ts | 2 +- src/lib/sell/index.tsx | 6 +++--- src/lib/tokens.ts | 2 +- src/lib/upgrade.ts | 3 +-- src/lib/vm/ssh.ts | 4 ++-- src/lib/zones.tsx | 38 ++++++++++++++++++----------------- src/scripts/release.ts | 16 +++++++-------- 20 files changed, 80 insertions(+), 89 deletions(-) diff --git a/src/helpers/errors.ts b/src/helpers/errors.ts index 7ba74e4b..c3d7107e 100644 --- a/src/helpers/errors.ts +++ b/src/helpers/errors.ts @@ -19,13 +19,13 @@ export function logSupportCTAAndQuit(): never { } export function logLoginMessageAndQuit(): never { - logAndQuit(`You need to login first.\n\n\t$ sf login\n`); + logAndQuit("You need to login first.\n\n\t$ sf login\n"); } export async function logSessionTokenExpiredAndQuit(): Promise { await clearAuthFromConfig(); logAndQuit( - `\nYour session has expired. Please login again.\n\n\t$ sf login\n`, + "\nYour session has expired. Please login again.\n\n\t$ sf login\n", ); } diff --git a/src/index.ts b/src/index.ts index e42ebc17..f7ab9567 100644 --- a/src/index.ts +++ b/src/index.ts @@ -122,11 +122,8 @@ async function main() { const key = arg.slice(2); const nextArg = arr[i + 1]; if (nextArg && !nextArg.startsWith("-")) { - (acc as Record)[key] = isNaN( - Number(nextArg), - ) - ? nextArg - : Number(nextArg); + (acc as Record)[key] = + Number.isNaN(Number(nextArg)) ? nextArg : Number(nextArg); } else { (acc as Record)[key] = true; } diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index 775e7fc7..738901a6 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -288,7 +288,7 @@ export function QuoteAndBuy(props: { options: SfBuyOptions }) { if (cluster) { const api = await apiClient(); - const { data, error, response } = await api.GET(`/v0/zones/{id}`, { + const { data, error, response } = await api.GET("/v0/zones/{id}", { params: { path: { id: cluster, @@ -480,7 +480,7 @@ function VMWarning(props: BuyOrderProps) { } equivalentCommand += ` -d ${realDurationString}`; if (props.yes) { - equivalentCommand += ` -y`; + equivalentCommand += " -y"; } if (props.cluster) { equivalentCommand += ` -z ${props.cluster}`; diff --git a/src/lib/extend/index.tsx b/src/lib/extend/index.tsx index b780ede7..50e6f1fb 100644 --- a/src/lib/extend/index.tsx +++ b/src/lib/extend/index.tsx @@ -84,13 +84,13 @@ ${boxen( equivalentCommand += ` -p ${options.price}`; } if (options.yes) { - equivalentCommand += ` -y`; + equivalentCommand += " -y"; } if (options.quote) { - equivalentCommand += ` -q`; + equivalentCommand += " -q"; } if (options.standing) { - equivalentCommand += ` --standing`; + equivalentCommand += " --standing"; } console.error( diff --git a/src/lib/nodes/create.ts b/src/lib/nodes/create.ts index 91695cca..7294dcd6 100644 --- a/src/lib/nodes/create.ts +++ b/src/lib/nodes/create.ts @@ -59,7 +59,7 @@ function formatNodeDescription( */ function validateCount(val: string): number { const parsed = Number.parseInt(val, 10); - if (isNaN(parsed) || parsed <= 0) { + if (Number.isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, "INVALID_COUNT", @@ -424,7 +424,7 @@ async function createNodesAction( if (options.zone) { confirmationMessage += ` on ${chalk.cyan(options.zone)}`; } else { - confirmationMessage += ` on any matching zone`; + confirmationMessage += " on any matching zone"; } } @@ -463,7 +463,7 @@ async function createNodesAction( console.log(chalk.gray("\nCreated nodes:")); console.log(createNodesTable(createdNodes)); console.log(`\n${chalk.gray("Next steps:")}`); - console.log(` sf nodes list`); + console.log(" sf nodes list"); // Auto Reserved nodes can't be extended, so only suggest it for reserved nodes if (isReserved) { console.log( diff --git a/src/lib/nodes/extend.ts b/src/lib/nodes/extend.ts index 3ca28d1a..704bd1ad 100644 --- a/src/lib/nodes/extend.ts +++ b/src/lib/nodes/extend.ts @@ -117,7 +117,7 @@ async function extendNodeAction( } console.log( chalk.redBright( - `\nTo configure auto reserved nodes, use the \`sf nodes set\` command.`, + "\nTo configure auto reserved nodes, use the `sf nodes set` command.", ), ); } diff --git a/src/lib/nodes/image/upload.ts b/src/lib/nodes/image/upload.ts index aa6ec9ca..fba97497 100644 --- a/src/lib/nodes/image/upload.ts +++ b/src/lib/nodes/image/upload.ts @@ -62,7 +62,7 @@ const upload = new Command("upload") "Number of parts to upload concurrently", (value) => { const parsed = Number.parseInt(value, 10); - if (isNaN(parsed) || parsed < 1) { + if (Number.isNaN(parsed) || parsed < 1) { throw new Error("Concurrency must be a positive integer"); } return parsed; @@ -94,9 +94,9 @@ const upload = new Command("upload") const imageId = startResponse.data.image_id; preparingSpinner.succeed( - `Started upload for image ${chalk.cyan(name)} (${ - chalk.blackBright(imageId) - })`, + `Started upload for image ${chalk.cyan(name)} (${chalk.blackBright( + imageId, + )})`, ); // Get file info and open as stream @@ -107,11 +107,9 @@ const upload = new Command("upload") const maxFileSize = 128 * 1024 * 1024 * 1024; // 128 GiB in bytes if (fileSize > maxFileSize) { logAndQuit( - `File size exceeds maximum allowed size of 128 GiB. File size: ${ - ( - fileSize / (1024 * 1024 * 1024) - ).toFixed(2) - } GiB`, + `File size exceeds maximum allowed size of 128 GiB. File size: ${( + fileSize / (1024 * 1024 * 1024) + ).toFixed(2)} GiB`, ); } @@ -122,9 +120,10 @@ const upload = new Command("upload") // For files smaller than default chunk, use the whole file as one part // Otherwise use default chunk size, but ensure we don't exceed maxParts - const chunkSize = fileSize <= defaultChunk - ? Math.max(fileSize, minChunk) - : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); + const chunkSize = + fileSize <= defaultChunk + ? Math.max(fileSize, minChunk) + : Math.max(minChunk, Math.ceil(fileSize / maxParts), defaultChunk); const totalParts = Math.ceil(fileSize / chunkSize); @@ -162,7 +161,7 @@ const upload = new Command("upload") progressBar = new cliProgress.SingleBar({ format: - `{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}`, + "{spinner} Uploading [{bar}] {percentage}% | {uploadedMB}/{totalMB} MB | {speed}", barCompleteChar: "\u2588", barIncompleteChar: "\u2591", hideCursor: true, @@ -366,7 +365,7 @@ const upload = new Command("upload") }); progressBar.stop(); - finalizingSpinner = ora(`Validating upload...`).start(); + finalizingSpinner = ora("Validating upload...").start(); // Calculate SHA256 hash for integrity verification using streaming const hash = crypto.createHash("sha256"); @@ -396,7 +395,7 @@ const upload = new Command("upload") ); } - finalizingSpinner.succeed(`Image uploaded and verified`); + finalizingSpinner.succeed("Image uploaded and verified"); const object = completeResponse.data; console.log(chalk.gray("\nNext steps:")); diff --git a/src/lib/nodes/list.tsx b/src/lib/nodes/list.tsx index 114fd390..82029a4d 100644 --- a/src/lib/nodes/list.tsx +++ b/src/lib/nodes/list.tsx @@ -454,7 +454,7 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {node.vms?.data && node.vms.data.length > 1 && ( - + )} @@ -506,12 +506,10 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {node.node_type === "autoreserved" && ( - <> - - + )} {node.node_type !== "autoreserved" && ( <> diff --git a/src/lib/nodes/redeploy.ts b/src/lib/nodes/redeploy.ts index 52592a64..bff3ddb9 100644 --- a/src/lib/nodes/redeploy.ts +++ b/src/lib/nodes/redeploy.ts @@ -136,7 +136,7 @@ async function redeployNodeAction( for (const { name } of nonRedeployableNodes) { console.log(` • ${name}`); } - console.log(chalk.redBright(`\nOnly running nodes can be redeployed.\n`)); + console.log(chalk.redBright("\nOnly running nodes can be redeployed.\n")); } if (redeployableNodes.length === 0) { @@ -172,11 +172,11 @@ async function redeployNodeAction( configChanges.push(` • New image: ${options.image}`); } if (encodedUserData) { - configChanges.push(` • Updated cloud-init user data`); + configChanges.push(" • Updated cloud-init user data"); } if (options.overrideEmpty) { configChanges.push( - ` • Clear any unspecified configuration (override empty)`, + " • Clear any unspecified configuration (override empty)", ); } diff --git a/src/lib/nodes/ssh.ts b/src/lib/nodes/ssh.ts index db065f1b..a99720d8 100644 --- a/src/lib/nodes/ssh.ts +++ b/src/lib/nodes/ssh.ts @@ -32,7 +32,7 @@ Standard \`ssh\` behavior applies (e.g. defaults to your current username).`) .addOption(jsonOption) .argument( "", - "Node name, Node ID, or VM ID to SSH into.\nFollows \`ssh\` behavior (i.e. root@node or jenson@node).", + "Node name, Node ID, or VM ID to SSH into.\nFollows `ssh` behavior (i.e. root@node or jenson@node).", ) .usage("[options] [user@]") .allowExcessArguments(false) diff --git a/src/lib/nodes/utils.ts b/src/lib/nodes/utils.ts index 16f83ab3..0af8da46 100644 --- a/src/lib/nodes/utils.ts +++ b/src/lib/nodes/utils.ts @@ -35,7 +35,6 @@ export function getStatusColor(status: SFCNodes.Node["status"]): string { return chalk.red(statusText); case "deleted": return chalk.gray(statusText); - case "unknown": default: return statusText; } @@ -181,7 +180,7 @@ export function pluralizeNodes(count: number) { */ export function validatePrice(val: string, minimum = 0): number { const parsed = Number.parseFloat(val); - if (isNaN(parsed) || parsed <= 0) { + if (Number.isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, "INVALID_PRICE", @@ -207,7 +206,7 @@ export function validatePrice(val: string, minimum = 0): number { */ export function validateDuration(val: string, minimum = 3600): number { const parsed = Number.parseInt(val, 10); - if (isNaN(parsed)) { + if (Number.isNaN(parsed)) { throw new CommanderError( 1, "INVALID_DURATION", @@ -245,7 +244,7 @@ export function parseDuration(duration: string): number { throw new CommanderError( 1, "INVALID_DURATION", - `Duration must be at least 1 hour`, + "Duration must be at least 1 hour", ); } return parsed; diff --git a/src/lib/scale/create.tsx b/src/lib/scale/create.tsx index 79147288..576a7b14 100644 --- a/src/lib/scale/create.tsx +++ b/src/lib/scale/create.tsx @@ -51,7 +51,7 @@ function ScaleWarning(props: CreateProcurementCommandProps) { equivalentCommand += ` -p ${pricePerNodeHour.toFixed(2)}`; } if (props.yes) { - equivalentCommand += ` -y`; + equivalentCommand += " -y"; } const warningMessage = boxen( @@ -414,7 +414,7 @@ $ sf scale create -n 8 --horizon '30m' .addOption( new Option( "-s, --colocation-strategy ", - `Colocation strategy to use for the procurement. Can be one of \`anywhere\`, \`colocate\`, \`colocate-pinned\`, or \`pinned\`. See https://docs.sfcompute.com/docs/on-demand-and-spot#colocation-behavior for more information.`, + "Colocation strategy to use for the procurement. Can be one of `anywhere`, `colocate`, `colocate-pinned`, or `pinned`. See https://docs.sfcompute.com/docs/on-demand-and-spot#colocation-behavior for more information.", ) .choices(["anywhere", "colocate", "colocate-pinned", "pinned"]) .default("colocate-pinned"), @@ -449,7 +449,7 @@ $ sf scale create -n 8 --horizon '30m' if (clusterName) { const api = await apiClient(); - const { data, error, response } = await api.GET(`/v0/zones/{id}`, { + const { data, error, response } = await api.GET("/v0/zones/{id}", { params: { path: { id: clusterName, diff --git a/src/lib/scale/update.tsx b/src/lib/scale/update.tsx index b855bd4d..fc5e27c1 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -323,10 +323,9 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { ))} )} - {successfulProcurements && - successfulProcurements.map((s, i) => ( - - ))} + {successfulProcurements?.map((s, i) => ( + + ))} ); } diff --git a/src/lib/scale/utils.ts b/src/lib/scale/utils.ts index 1d73031f..4814fc06 100644 --- a/src/lib/scale/utils.ts +++ b/src/lib/scale/utils.ts @@ -40,7 +40,7 @@ export function parseHorizonArg(horizon: string) { logAndQuit(`Failed to parse horizon: ${horizon}`); } if (parsedHorizon < 1) { - logAndQuit(`Minimum horizon is 1 minute`); + logAndQuit("Minimum horizon is 1 minute"); } return Math.ceil(parsedHorizon); } diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index 92d8859b..8ef731d2 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -64,7 +64,7 @@ export function registerSell(program: Command) { } const size = parseAccelerators(options.accelerators); - if (isNaN(size) || size <= 0) { + if (Number.isNaN(size) || size <= 0) { return logAndQuit( `Invalid number of accelerators: ${options.accelerators}`, ); @@ -212,7 +212,7 @@ function SellOrder(props: { return; } - const o = await getOrder(order!.id); + const o = await getOrder(order?.id); setOrder(o); if (o) { setTimeout(exit, 0); @@ -359,7 +359,7 @@ export async function placeSellOrder(options: { realDurationHours, ); invariant( - totalPrice == Math.ceil(totalPrice), + totalPrice === Math.ceil(totalPrice), "totalPrice must be a whole number", ); diff --git a/src/lib/tokens.ts b/src/lib/tokens.ts index 140df036..f6cdf03e 100644 --- a/src/lib/tokens.ts +++ b/src/lib/tokens.ts @@ -226,7 +226,7 @@ async function listTokensAction() { // prompt user that they can generate one console.log( `${chalk.gray("Generate your first token with: ")}${chalk.magenta( - `sf tokens create`, + "sf tokens create", )}`, ); diff --git a/src/lib/upgrade.ts b/src/lib/upgrade.ts index a3c71155..9b4fade0 100644 --- a/src/lib/upgrade.ts +++ b/src/lib/upgrade.ts @@ -39,8 +39,7 @@ export function registerUpgrade(program: Command) { if (version) { spinner.start(`Checking if version ${version} exists`); - const url = - `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; + const url = `https://github.com/sfcompute/cli/archive/refs/tags/${version}.zip`; const response = await fetch(url, { method: "HEAD" }); if (response.status === 404) { diff --git a/src/lib/vm/ssh.ts b/src/lib/vm/ssh.ts index 8ec08614..e9a09c6e 100644 --- a/src/lib/vm/ssh.ts +++ b/src/lib/vm/ssh.ts @@ -28,10 +28,10 @@ export function registerSsh(program: Command) { const splitDestination = destination.split("@"); let vmId: string; let sshUsername: string | undefined; - if (splitDestination.length == 1) { + if (splitDestination.length === 1) { sshUsername = undefined; vmId = splitDestination[0]; - } else if (splitDestination.length == 2) { + } else if (splitDestination.length === 2) { sshUsername = splitDestination[0]; vmId = splitDestination[1]; } else { diff --git a/src/lib/zones.tsx b/src/lib/zones.tsx index a4501d78..4700182a 100644 --- a/src/lib/zones.tsx +++ b/src/lib/zones.tsx @@ -2,8 +2,7 @@ import * as console from "node:console"; import type { Command } from "@commander-js/extra-typings"; import chalk from "chalk"; import Table from "cli-table3"; -import dayjs from "dayjs"; -import { Box, render, Text } from "ink"; +import { Box, Text, render } from "ink"; import { apiClient } from "../apiClient.ts"; import { isLoggedIn } from "../helpers/config.ts"; import { @@ -18,9 +17,9 @@ type ZoneInfo = components["schemas"]["node-api_ZoneInfo"]; // Delivery type conversion similar to InstanceTypeMetadata pattern const DeliveryTypeMetadata: Record = { - "K8s": { displayName: "Kubernetes" }, - "K8sNamespace": { displayName: "Kubernetes" }, - "VM": { displayName: "Virtual Machine" }, + K8s: { displayName: "Kubernetes" }, + K8sNamespace: { displayName: "Kubernetes" }, + VM: { displayName: "Virtual Machine" }, } as const; function formatDeliveryType(deliveryType: string): string { @@ -28,9 +27,9 @@ function formatDeliveryType(deliveryType: string): string { } function getCurrentAvailableCapacity(zone: ZoneInfo): number { - const oldestShape = zone.available_capacity?.sort( - (a, b) => a.start_timestamp - b.start_timestamp, - ).at(0); + const oldestShape = zone.available_capacity + ?.sort((a, b) => a.start_timestamp - b.start_timestamp) + .at(0); if ( oldestShape?.start_timestamp && oldestShape.start_timestamp >= Math.floor(Date.now() / 1000) @@ -42,9 +41,9 @@ function getCurrentAvailableCapacity(zone: ZoneInfo): number { // Region conversion to short slugs const RegionMetadata: Record = { - "NorthAmerica": { slug: "North America" }, - "AsiaPacific": { slug: "Asia" }, - "EuropeMiddleEastAfrica": { slug: "EMEA" }, + NorthAmerica: { slug: "North America" }, + AsiaPacific: { slug: "Asia" }, + EuropeMiddleEastAfrica: { slug: "EMEA" }, } as const; function formatRegion(region: string): string { @@ -83,7 +82,7 @@ Note: This is an early access feature (v0) that may change at any time. async function listZonesAction(options: { json?: boolean }) { console.error( - `\x1b[33mNote: This is an early access feature (v0) and may change at any time.\x1b[0m\n`, + "\x1b[33mNote: This is an early access feature (v0) and may change at any time.\x1b[0m\n", ); const loggedIn = await isLoggedIn(); @@ -164,9 +163,10 @@ function displayZonesTable(zones: ZoneInfo[]) { sortedZones.forEach((zone) => { const available = getCurrentAvailableCapacity(zone); - const availableNodesText = available > 0 - ? chalk.green(available.toString()) - : chalk.red(available.toString()); + const availableNodesText = + available > 0 + ? chalk.green(available.toString()) + : chalk.red(available.toString()); table.push([ zone.name, @@ -178,8 +178,8 @@ function displayZonesTable(zones: ZoneInfo[]) { ]); }); - const availableZones = sortedZones.filter((zone) => - getCurrentAvailableCapacity(zone) > 0 + const availableZones = sortedZones.filter( + (zone) => getCurrentAvailableCapacity(zone) > 0, ); const availableZoneName = availableZones?.[0]?.name ?? "alamo"; console.log(table.toString()); @@ -188,7 +188,9 @@ function displayZonesTable(zones: ZoneInfo[]) { ); console.log(chalk.gray("Examples:")); console.log(` sf buy --zone ${chalk.green(availableZoneName)}`); - console.log(` sf scale create -n 16 --zone ${chalk.green(availableZoneName)}`); + console.log( + ` sf scale create -n 16 --zone ${chalk.green(availableZoneName)}`, + ); } function EmptyZonesDisplay() { diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 59d4190c..14831550 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -19,7 +19,7 @@ function bumpVersion( Number.parseInt( // Remove everything after the - if there is one v.includes("-") ? v.split("-")[0] : v, - ) + ), ); switch (type) { case "major": @@ -176,14 +176,12 @@ program "A github release tool for the project. Valid types are: major, minor, patch, prerelease", ) .addArgument( - new Argument("type").choices( - [ - "major", - "minor", - "patch", - "prerelease", - ] as const, - ), + new Argument("type").choices([ + "major", + "minor", + "patch", + "prerelease", + ] as const), ) .action(async (type) => { try { From 5aef2e9b630a0b542d52d90bf527e52ce2ba4e31 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:19:02 -0800 Subject: [PATCH 25/48] build: upgrade from Ink v5 -> v6 and React v18 -> v19 --- bun.lock | 32 +++++++++++++++----------------- package.json | 6 +++--- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/bun.lock b/bun.lock index c0d54e9b..8f31564c 100644 --- a/bun.lock +++ b/bun.lock @@ -20,7 +20,7 @@ "date-fns": "^4.1.0", "dayjs": "^1.11.19", "dotenv": "^16.4.5", - "ink": "^5.2.0", + "ink": "^6.6.0", "ink-link": "^4.1.0", "ink-spinner": "^5.0.0", "ink-testing-library": "^4.0.0", @@ -34,7 +34,7 @@ "parse-duration": "^2.1.3", "posthog-node": "^4.10.1", "prettier": "^3.5.3", - "react": "^18.3.1", + "react": "^19.2.3", "semver": "^7.6.3", "shescape": "^2.1.1", "tiny-invariant": "^1.3.3", @@ -48,7 +48,7 @@ "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", - "@types/react": "^18.3.20", + "@types/react": "^19.2.9", "@types/semver": "^7.5.8", "@yao-pkg/pkg": "^5.12.0", "tsx": "^4.19.0", @@ -61,7 +61,7 @@ }, }, "packages": { - "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ=="], "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], @@ -253,9 +253,7 @@ "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], - "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], - - "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], + "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], "@types/retry": ["@types/retry@0.12.5", "", {}, "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw=="], @@ -335,7 +333,7 @@ "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], - "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], @@ -465,7 +463,7 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "ink": ["ink@5.2.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "ansi-escapes": "^7.0.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^4.0.0", "code-excerpt": "^4.0.0", "es-toolkit": "^1.22.0", "indent-string": "^5.0.0", "is-in-ci": "^1.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^7.2.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-BqcUyWrG9zq5HIwW6JcfFHsIYebJkWWb4fczNah1goUO0vv5vneIlfwuS85twyJ5hYR/y18FlAYUxrO9ChIWVg=="], + "ink": ["ink@6.6.0", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ=="], "ink-link": ["ink-link@4.1.0", "", { "dependencies": { "prop-types": "^15.8.1", "terminal-link": "^3.0.0" }, "peerDependencies": { "ink": ">=4" } }, "sha512-3nNyJXum0FJIKAXBK8qat2jEOM41nJ1J60NRivwgK9Xh92R5UMN/k4vbz0A9xFzhJVrlf4BQEmmxMgXkCE1Jeg=="], @@ -483,7 +481,7 @@ "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "is-in-ci": ["is-in-ci@1.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-eUuAjybVTHMYWm/U+vBO1sY/JOCgoPCXRxzdju0K+K0BiGW0SChEL1MLC0PoCIR1OlPo5YAp8HuQoUlsWEICwg=="], + "is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="], "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], @@ -587,11 +585,11 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], + "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], + "react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -615,7 +613,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], + "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -733,7 +731,7 @@ "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], "@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -745,7 +743,7 @@ "cli-table3/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + "cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -755,6 +753,8 @@ "from2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "ink/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], + "ink-spinner/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], "little-date/date-fns": ["date-fns@2.30.0", "", { "dependencies": { "@babel/runtime": "^7.21.0" } }, "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw=="], @@ -789,8 +789,6 @@ "cli-table3/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], diff --git a/package.json b/package.json index 15945e6c..f3ac7223 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "date-fns": "^4.1.0", "dayjs": "^1.11.19", "dotenv": "^16.4.5", - "ink": "^5.2.0", + "ink": "^6.6.0", "ink-link": "^4.1.0", "ink-spinner": "^5.0.0", "ink-testing-library": "^4.0.0", @@ -48,7 +48,7 @@ "parse-duration": "^2.1.3", "posthog-node": "^4.10.1", "prettier": "^3.5.3", - "react": "^18.3.1", + "react": "^19.2.3", "semver": "^7.6.3", "shescape": "^2.1.1", "tiny-invariant": "^1.3.3", @@ -62,7 +62,7 @@ "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", - "@types/react": "^18.3.20", + "@types/react": "^19.2.9", "@types/semver": "^7.5.8", "@yao-pkg/pkg": "^5.12.0", "tsx": "^4.19.0", From 8d3c9292115de4fa4ef32393a9628a31bff63179 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:25:08 -0800 Subject: [PATCH 26/48] build: update from biome v1 to biome v2 --- biome.json | 6 ++---- bun.lock | 20 ++++++++++---------- package.json | 2 +- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/biome.json b/biome.json index 6099e757..27dc20cb 100644 --- a/biome.json +++ b/biome.json @@ -1,8 +1,6 @@ { - "$schema": "https://biomejs.dev/schemas/1.8.2/schema.json", - "organizeImports": { - "enabled": true - }, + "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", + "assist": { "actions": { "source": { "organizeImports": "on" } } }, "formatter": { "indentStyle": "space", "indentWidth": 2 diff --git a/bun.lock b/bun.lock index 8f31564c..e60de565 100644 --- a/bun.lock +++ b/bun.lock @@ -44,7 +44,7 @@ "yn": "^5.1.0", }, "devDependencies": { - "@biomejs/biome": "^1.9.0", + "@biomejs/biome": "^2.3.11", "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", @@ -75,23 +75,23 @@ "@babel/types": ["@babel/types@7.28.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg=="], - "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + "@biomejs/biome": ["@biomejs/biome@2.3.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.11", "@biomejs/cli-darwin-x64": "2.3.11", "@biomejs/cli-linux-arm64": "2.3.11", "@biomejs/cli-linux-arm64-musl": "2.3.11", "@biomejs/cli-linux-x64": "2.3.11", "@biomejs/cli-linux-x64-musl": "2.3.11", "@biomejs/cli-win32-arm64": "2.3.11", "@biomejs/cli-win32-x64": "2.3.11" }, "bin": { "biome": "bin/biome" } }, "sha512-/zt+6qazBWguPG6+eWmiELqO+9jRsMZ/DBU3lfuU2ngtIQYzymocHhKiZRyrbra4aCOoyTg/BmY+6WH5mv9xmQ=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@1.9.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bFBsPWrNvkdKrNCYeAp+xo2HecOGPAy9WyNyB/jKnnedgzl4W4Hb9ZMzYNbf8dMCGmUdSavlYHiR01QaYR58cw=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@1.9.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-ngYBh/+bEedqkSevPVhLP4QfVPCpb+4BBe2p7Xs32dBgs7rh9nY2AIYUL6BgLw1JVXV8GlpKmb/hNiuIxfPfZg=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.3.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-fh7nnvbweDPm2xEmFjfmq7zSUiox88plgdHF9OIW4i99WnXrAC3o2P3ag9judoUMv8FCSUnlwJCM1B64nO5Fbg=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-l4xkGa9E7Uc0/05qU2lMYfN1H+fzzkHgaJoy98wO+b/7Gl78srbCRRgwYSW+BTLixTBrM6Ede5NSBwt7rd/i6g=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@1.9.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-v665Ct9WCRjGa8+kTr0CzApU0+XXtRgwmzIf1SeKSGAv+2scAlW6JR5PMFo6FzqqZ64Po79cKODKf3/AAmECqA=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-/1s9V/H3cSe0r0Mv/Z8JryF5x9ywRxywomqZVLHAoa/uN0eY7F8gEngWKNS5vbbN/BsfpCG5yeBT5ENh50Frxg=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@1.9.4", "", { "os": "linux", "cpu": "x64" }, "sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@1.9.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.3.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-PZQ6ElCOnkYapSsysiTy0+fYX+agXPlWugh6+eQ6uPKI3vKAqNp6TnMhoM3oY2NltSB89hz59o8xIfOdyhi9Iw=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.3.11", "", { "os": "win32", "cpu": "x64" }, "sha512-43VrG813EW+b5+YbDbz31uUsheX+qFKCpXeY9kfdAx+ww3naKxeVkTD9zLIWxUPfJquANMHrmW3wbe/037G0Qg=="], "@colors/colors": ["@colors/colors@1.5.0", "", {}, "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="], diff --git a/package.json b/package.json index f3ac7223..54f99933 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "yn": "^5.1.0" }, "devDependencies": { - "@biomejs/biome": "^1.9.0", + "@biomejs/biome": "^2.3.11", "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", From a697969c58af2fab29e25ad0becf090c6c9fedeb Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:31:08 -0800 Subject: [PATCH 27/48] style: run `biome check` after upgrade --- src/helpers/duration.ts | 2 +- src/helpers/fetchers.ts | 2 +- src/helpers/units.ts | 2 +- src/index.ts | 2 +- src/lib/Quote.tsx | 2 +- src/lib/buy/index.tsx | 10 ++--- src/lib/contracts/ContractDisplay.tsx | 2 +- src/lib/dev.ts | 2 +- src/lib/index.ts | 2 +- src/lib/login.ts | 11 +++--- src/lib/nodes/image/show.tsx | 2 +- src/lib/nodes/list.tsx | 4 +- src/lib/nodes/ssh.ts | 4 +- src/lib/orders/OrderDisplay.tsx | 54 +++++++++++++-------------- src/lib/scale/ConfirmationMessage.tsx | 5 +-- src/lib/scale/ProcurementDisplay.tsx | 9 +++-- src/lib/scale/utils.ts | 8 +--- src/lib/tokens.ts | 8 +--- src/lib/vm/ssh.ts | 4 +- src/lib/zones.tsx | 2 +- src/scripts/release.ts | 1 + 21 files changed, 63 insertions(+), 75 deletions(-) diff --git a/src/helpers/duration.ts b/src/helpers/duration.ts index 6a0306a4..18eeed41 100644 --- a/src/helpers/duration.ts +++ b/src/helpers/duration.ts @@ -15,7 +15,7 @@ export const parseDurationArgument = (rawDuration: string | undefined) => { // For backwards compatibility, we want to support users passing in seconds directly. // Some of the CLI use to support seconds directly, now we support durations strings. - const attemptedParseAsNumber = Number.parseInt(duration); + const attemptedParseAsNumber = Number.parseInt(duration, 10); if ( !Number.isNaN(attemptedParseAsNumber) && diff --git a/src/helpers/fetchers.ts b/src/helpers/fetchers.ts index 12c21dc9..69629f8d 100644 --- a/src/helpers/fetchers.ts +++ b/src/helpers/fetchers.ts @@ -22,7 +22,7 @@ export async function getOrder(orderId: string) { }, }); if (!response.ok) { - // @ts-ignore -- TODO: FIXME: include error in OpenAPI schema output + // @ts-expect-error -- TODO: FIXME: include error in OpenAPI schema output if (error?.code === "order.not_found") { return null; } diff --git a/src/helpers/units.ts b/src/helpers/units.ts index 3b0049ba..9d366874 100644 --- a/src/helpers/units.ts +++ b/src/helpers/units.ts @@ -90,7 +90,7 @@ export function priceWholeToCents( return { cents: price * 100, invalid: false }; } else if (typeof price === "string") { // remove any whitespace, dollar signs, negative signs, single and double quotes - const priceCleaned = price.replace(/[\s\$\-\'\"]/g, ""); + const priceCleaned = price.replace(/[\s$\-'"]/g, ""); if (priceCleaned === "") { return { cents: null, invalid: true }; } diff --git a/src/index.ts b/src/index.ts index f7ab9567..312fc5e4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -18,7 +18,7 @@ import { registerLogin } from "./lib/login.ts"; import { registerMe } from "./lib/me.ts"; import { registerNodes } from "./lib/nodes/index.ts"; import { registerOrders } from "./lib/orders/index.tsx"; -import { IS_TRACKING_DISABLED, analytics } from "./lib/posthog.ts"; +import { analytics, IS_TRACKING_DISABLED } from "./lib/posthog.ts"; import { registerScale } from "./lib/scale/index.tsx"; import { registerSell } from "./lib/sell.ts"; import { registerTokens } from "./lib/tokens.ts"; diff --git a/src/lib/Quote.tsx b/src/lib/Quote.tsx index 081ab483..517bd3c1 100644 --- a/src/lib/Quote.tsx +++ b/src/lib/Quote.tsx @@ -1,6 +1,6 @@ import { Box, Text } from "ink"; -import { Row } from "./Row.tsx"; import { getPricePerGpuHourFromQuote } from "./buy/index.tsx"; +import { Row } from "./Row.tsx"; export default function QuoteDisplay(props: { quote: Quote }) { if (!props.quote) { diff --git a/src/lib/buy/index.tsx b/src/lib/buy/index.tsx index 738901a6..f82bdd29 100644 --- a/src/lib/buy/index.tsx +++ b/src/lib/buy/index.tsx @@ -8,7 +8,7 @@ import { parseDate } from "chrono-node"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; -import { Box, Text, render, useApp } from "ink"; +import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import ms from "ms"; import parseDurationFromLibrary from "parse-duration"; @@ -30,12 +30,12 @@ import { } from "../../helpers/units.ts"; import type { components } from "../../schema.ts"; import ConfirmInput from "../ConfirmInput.tsx"; -import type { Quote } from "../Quote.tsx"; -import QuoteDisplay from "../Quote.tsx"; -import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; import { parseAccelerators } from "../index.ts"; import { analytics } from "../posthog.ts"; +import type { Quote } from "../Quote.tsx"; +import QuoteDisplay from "../Quote.tsx"; +import { Row } from "../Row.tsx"; dayjs.extend(relativeTime); dayjs.extend(duration); @@ -999,7 +999,7 @@ export async function getOrder(orderId: string) { }); if (error) { - // @ts-ignore -- TODO: FIXME: include error in OpenAPI schema output + // @ts-expect-error -- TODO: FIXME: include error in OpenAPI schema output if (error?.code === "order.not_found" || response.status === 404) { return undefined; } diff --git a/src/lib/contracts/ContractDisplay.tsx b/src/lib/contracts/ContractDisplay.tsx index ef1b3760..05e78b71 100644 --- a/src/lib/contracts/ContractDisplay.tsx +++ b/src/lib/contracts/ContractDisplay.tsx @@ -3,8 +3,8 @@ import { Box, Text } from "ink"; import { formatDateRange } from "little-date"; import ms from "ms"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; -import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; +import { Row } from "../Row.tsx"; import type { ActiveContract, Contract } from "./types.ts"; import { type ContractState, diff --git a/src/lib/dev.ts b/src/lib/dev.ts index 52576b46..6e098963 100644 --- a/src/lib/dev.ts +++ b/src/lib/dev.ts @@ -112,7 +112,7 @@ function registerEpoch(program: Command) { const colorDiffedEpochs = colorDiffEpochs(timestamps); timestamps.forEach((epochTimestamp, i) => { - const date = epochToDate(Number.parseInt(epochTimestamp)); + const date = epochToDate(Number.parseInt(epochTimestamp, 10)); console.log( `${colorDiffedEpochs[i]} | ${chalk.yellow( dayjs(date).format("hh:mm A MM-DD-YYYY"), diff --git a/src/lib/index.ts b/src/lib/index.ts index 359ef8d5..62804213 100644 --- a/src/lib/index.ts +++ b/src/lib/index.ts @@ -6,7 +6,7 @@ export function parseAccelerators(accelerators: string, type: "buy" | "sell") { return 1; } - const parsedValue = Number.parseInt(accelerators); + const parsedValue = Number.parseInt(accelerators, 10); if (!Number.isInteger(parsedValue / GPUS_PER_NODE)) { return logAndQuit( `You can only ${type} whole nodes, or multiples of ${GPUS_PER_NODE} GPUs at a time. Got: ${accelerators}`, diff --git a/src/lib/login.ts b/src/lib/login.ts index 6f903ee2..4f0fb751 100644 --- a/src/lib/login.ts +++ b/src/lib/login.ts @@ -1,19 +1,18 @@ import { exec } from "node:child_process"; import * as console from "node:console"; +import { randomInt } from "node:crypto"; import process from "node:process"; import { setTimeout } from "node:timers"; import type { Command } from "@commander-js/extra-typings"; -import ora from "ora"; -import { saveConfig } from "../helpers/config.ts"; -import { clearScreen } from "../helpers/prompt.ts"; -import { getWebAppUrl } from "../helpers/urls.ts"; - -import { randomInt } from "node:crypto"; // We're using Axios here because there's a bug // where the fetch API in Bun isn't passing the body // through redirects correctly import axios from "axios"; +import ora from "ora"; +import { saveConfig } from "../helpers/config.ts"; import { clearFeatureFlags } from "../helpers/feature-flags.ts"; +import { clearScreen } from "../helpers/prompt.ts"; +import { getWebAppUrl } from "../helpers/urls.ts"; import { getLoggedInAccountId } from "./me.ts"; export function registerLogin(program: Command) { diff --git a/src/lib/nodes/image/show.tsx b/src/lib/nodes/image/show.tsx index 666da358..40479cce 100644 --- a/src/lib/nodes/image/show.tsx +++ b/src/lib/nodes/image/show.tsx @@ -6,7 +6,7 @@ import dayjs from "dayjs"; import advanced from "dayjs/plugin/advancedFormat"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; -import { Box, Text, render } from "ink"; +import { Box, render, Text } from "ink"; import Link from "ink-link"; import { formatDate } from "../../../helpers/format-date.ts"; import { handleNodesError, nodesClient } from "../../../nodesClient.ts"; diff --git a/src/lib/nodes/list.tsx b/src/lib/nodes/list.tsx index 82029a4d..a49cedbb 100644 --- a/src/lib/nodes/list.tsx +++ b/src/lib/nodes/list.tsx @@ -7,7 +7,7 @@ import dayjs from "dayjs"; import advanced from "dayjs/plugin/advancedFormat"; import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; -import { Box, Text, render } from "ink"; +import { Box, render, Text } from "ink"; import ora from "ora"; import { getAuthToken } from "../../helpers/config.ts"; @@ -19,8 +19,8 @@ import { import { handleNodesError, nodesClient } from "../../nodesClient.ts"; import { Row } from "../Row.tsx"; import { - DEFAULT_NODE_LS_LIMIT, createNodesTable, + DEFAULT_NODE_LS_LIMIT, getLastVM, getStatusColor, getVMStatusColor, diff --git a/src/lib/nodes/ssh.ts b/src/lib/nodes/ssh.ts index a99720d8..98ac9a14 100644 --- a/src/lib/nodes/ssh.ts +++ b/src/lib/nodes/ssh.ts @@ -180,8 +180,8 @@ Examples: cmd = cmd.concat(["-o", `HostKeyAlias=${vmId}.vms.sfcompute.dev`]); cmd = cmd.concat([sshDestination]); - let shescape: undefined | Shescape = undefined; - let shell: undefined | string = undefined; + let shescape: undefined | Shescape; + let shell: undefined | string; if (process.env.SHELL !== undefined) { try { shescape = new Shescape({ diff --git a/src/lib/orders/OrderDisplay.tsx b/src/lib/orders/OrderDisplay.tsx index d6958772..3dee254c 100644 --- a/src/lib/orders/OrderDisplay.tsx +++ b/src/lib/orders/OrderDisplay.tsx @@ -1,9 +1,9 @@ import process from "node:process"; import dayjs from "dayjs"; -import { Box, Text, measureElement, useInput } from "ink"; +import { Box, measureElement, Text, useInput } from "ink"; import React, { useEffect } from "react"; -import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; +import { Row } from "../Row.tsx"; import { formatDuration } from "./index.tsx"; import type { HydratedOrder } from "./types.ts"; @@ -201,32 +201,30 @@ export function OrderDisplay(props: { }, [props.orders]); return ( - <> - - {orders.map((order) => { - return props.expanded ? ( - - ) : ( - - ); - })} - - {orders.length === 0 && ( - - - There are 0 outstanding {activeTab === "all" ? "" : activeTab}{" "} - orders right now. - - - )} - - + + {orders.map((order) => { + return props.expanded ? ( + + ) : ( + + ); + })} + + {orders.length === 0 && ( + + + There are 0 outstanding {activeTab === "all" ? "" : activeTab}{" "} + orders right now. + + + )} + ); } diff --git a/src/lib/scale/ConfirmationMessage.tsx b/src/lib/scale/ConfirmationMessage.tsx index bea7b98f..dd1b0cb2 100644 --- a/src/lib/scale/ConfirmationMessage.tsx +++ b/src/lib/scale/ConfirmationMessage.tsx @@ -1,14 +1,13 @@ import { Box, Text } from "ink"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; - -import { Row } from "../Row.tsx"; import { formatDuration } from "../orders/index.tsx"; +import { Row } from "../Row.tsx"; import { + formatColocationStrategy, MIN_CONTRACT_MINUTES, type Procurement, - formatColocationStrategy, } from "./utils.ts"; export default function ConfirmationMessage(props: { diff --git a/src/lib/scale/ProcurementDisplay.tsx b/src/lib/scale/ProcurementDisplay.tsx index 6e22b7a5..e56e4c6c 100644 --- a/src/lib/scale/ProcurementDisplay.tsx +++ b/src/lib/scale/ProcurementDisplay.tsx @@ -2,12 +2,11 @@ import { Badge } from "@inkjs/ui"; import { Box, Text } from "ink"; import { InstanceTypeMetadata } from "../../helpers/instance-types-meta.ts"; - -import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; import { formatDuration } from "../orders/index.tsx"; +import { Row } from "../Row.tsx"; -import { type Procurement, formatColocationStrategy } from "./utils.ts"; +import { formatColocationStrategy, type Procurement } from "./utils.ts"; export function ProcurementHeader({ id, @@ -45,7 +44,9 @@ export default function ProcurementDisplay({ horizon, colocation_strategy, }, -}: { procurement: Procurement }) { +}: { + procurement: Procurement; +}) { const horizonMinutes = horizon; const quantity = desired_quantity * GPUS_PER_NODE; const pricePerGpuHourInCents = buy_limit_price_per_gpu_hour; diff --git a/src/lib/scale/utils.ts b/src/lib/scale/utils.ts index 4814fc06..9b5170de 100644 --- a/src/lib/scale/utils.ts +++ b/src/lib/scale/utils.ts @@ -46,7 +46,7 @@ export function parseHorizonArg(horizon: string) { } export function parseAccelerators(accelerators: string) { - const parsedAccelerators = Number.parseInt(accelerators); + const parsedAccelerators = Number.parseInt(accelerators, 10); if (parsedAccelerators % GPUS_PER_NODE !== 0) { logAndQuit(`Only multiples of ${GPUS_PER_NODE} GPUs are allowed.`); } @@ -58,11 +58,7 @@ export function acceleratorsToNodes(accelerators: number) { return Math.floor(accelerators / GPUS_PER_NODE); } -export async function getProcurement({ - id, -}: { - id: string; -}) { +export async function getProcurement({ id }: { id: string }) { const client = await apiClient(); const res = await client.GET("/v0/procurements/{id}", { params: { path: { id: id } }, diff --git a/src/lib/tokens.ts b/src/lib/tokens.ts index f6cdf03e..cbfd3d7c 100644 --- a/src/lib/tokens.ts +++ b/src/lib/tokens.ts @@ -250,13 +250,7 @@ async function listTokensAction() { process.exit(0); } -async function deleteTokenAction({ - id, - yes, -}: { - id: string; - yes?: boolean; -}) { +async function deleteTokenAction({ id, yes }: { id: string; yes?: boolean }) { const loggedIn = await isLoggedIn(); if (!loggedIn) { logLoginMessageAndQuit(); diff --git a/src/lib/vm/ssh.ts b/src/lib/vm/ssh.ts index e9a09c6e..ed489e1c 100644 --- a/src/lib/vm/ssh.ts +++ b/src/lib/vm/ssh.ts @@ -106,8 +106,8 @@ export function registerSsh(program: Command) { cmd = cmd.concat(["-o", `HostKeyAlias=${vmId}.vms.sfcompute.dev`]); cmd = cmd.concat([sshDestination]); - let shescape: undefined | Shescape = undefined; - let shell: undefined | string = undefined; + let shescape: undefined | Shescape; + let shell: undefined | string; if (process.env.SHELL !== undefined) { try { shescape = new Shescape({ diff --git a/src/lib/zones.tsx b/src/lib/zones.tsx index 4700182a..bbaf3b47 100644 --- a/src/lib/zones.tsx +++ b/src/lib/zones.tsx @@ -2,7 +2,7 @@ import * as console from "node:console"; import type { Command } from "@commander-js/extra-typings"; import chalk from "chalk"; import Table from "cli-table3"; -import { Box, Text, render } from "ink"; +import { Box, render, Text } from "ink"; import { apiClient } from "../apiClient.ts"; import { isLoggedIn } from "../helpers/config.ts"; import { diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 14831550..2ddac31e 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -19,6 +19,7 @@ function bumpVersion( Number.parseInt( // Remove everything after the - if there is one v.includes("-") ? v.split("-")[0] : v, + 10, ), ); switch (type) { From 3e0c11b45ad12c7df3a6a91f16946bc0de80a691 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:31:15 -0800 Subject: [PATCH 28/48] build: bump deps --- bun.lock | 64 +++++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 35 deletions(-) diff --git a/bun.lock b/bun.lock index e60de565..617680f7 100644 --- a/bun.lock +++ b/bun.lock @@ -191,55 +191,55 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.1", "", { "os": "android", "cpu": "arm" }, "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.2", "", { "os": "android", "cpu": "arm" }, "sha512-21J6xzayjy3O6NdnlO6aXi/urvSRjm6nCI6+nF6ra2YofKruGixN9kfT+dt55HVNwfDmpDHJcaS3JuP/boNnlA=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.2", "", { "os": "android", "cpu": "arm64" }, "sha512-eXBg7ibkNUZ+sTwbFiDKou0BAckeV6kIigK7y5Ko4mB/5A1KLhuzEKovsmfvsL8mQorkoincMFGnQuIT92SKqA=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.55.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-UCbaTklREjrc5U47ypLulAgg4njaqfOVLU18VrCrI+6E5MQjuG0lSWaqLlAJwsD7NpFV249XgB0Bi37Zh5Sz4g=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.55.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dP67MA0cCMHFT2g5XyjtpVOtp7y4UyUxN3dhLdt11at5cPKnSm4lY+EhwNvDXIMzAMIo2KU+mc9wxaAQJTn7sQ=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.1", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.55.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-WDUPLUwfYV9G1yxNRJdXcvISW15mpvod1Wv3ok+Ws93w1HjIVmCIFxsG2DquO+3usMNCpJQ0wqO+3GhFdl6Fow=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.55.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Ng95wtHVEulRwn7R0tMrlUuiLVL/HXA8Lt/MYVpy88+s5ikpntzZba1qEulTuPnPIZuOPcW9wNEiqvZxZmgmqQ=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.55.2", "", { "os": "linux", "cpu": "arm" }, "sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.1", "", { "os": "linux", "cpu": "arm" }, "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.55.2", "", { "os": "linux", "cpu": "arm" }, "sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.55.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.55.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA=="], - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g=="], + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA=="], - "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw=="], + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA=="], - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw=="], + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.55.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw=="], - "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.1", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw=="], + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.55.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.1", "", { "os": "linux", "cpu": "none" }, "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.55.2", "", { "os": "linux", "cpu": "none" }, "sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.1", "", { "os": "linux", "cpu": "s390x" }, "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.55.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.55.2", "", { "os": "linux", "cpu": "x64" }, "sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.55.2", "", { "os": "linux", "cpu": "x64" }, "sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA=="], - "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.1", "", { "os": "openbsd", "cpu": "x64" }, "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg=="], + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.55.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw=="], - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.1", "", { "os": "none", "cpu": "arm64" }, "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw=="], + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.55.2", "", { "os": "none", "cpu": "arm64" }, "sha512-sZnyUgGkuzIXaK3jNMPmUIyJrxu/PjmATQrocpGA1WbCPX8H5tfGgRSuYtqBYAvLuIGp8SPRb1O4d1Fkb5fXaQ=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.55.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sDpFbenhmWjNcEbBcoTV0PWvW5rPJFvu+P7XoTY0YLGRupgLbFY0XPfwIbJOObzO7QgkRDANh65RjhPmgSaAjQ=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.55.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-GvJ03TqqaweWCigtKQVBErw2bEhu1tyfNQbarwr94wCGnczA9HF8wqEe3U/Lfu6EdeNP0p6R+APeHVwEqVxpUQ=="], - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg=="], + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.55.2", "", { "os": "win32", "cpu": "x64" }, "sha512-KvXsBvp13oZz9JGe5NYS7FNizLe99Ny+W8ETsuCyjXiKdiGrcz2/J/N8qxZ/RSwivqjQguug07NLHqrIHrqfYw=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.1", "", { "os": "win32", "cpu": "x64" }, "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.55.2", "", { "os": "win32", "cpu": "x64" }, "sha512-xNO+fksQhsAckRtDSPWaMeT1uIM+JrDRXlerpnWNXhn1TdB3YZ6uKBMBTKP0eX9XtYEP978hHk1f8332i2AW8Q=="], "@sfcompute/nodes-sdk-alpha": ["@sfcompute/nodes-sdk-alpha@0.1.0-alpha.27", "", {}, "sha512-IcnuTpfZszCLpBtEOFCas0fhc0yAgCvGZ8JvN9DdM1zWMUswHyuumYKDEVt6ZUXjJVnMt09Uhqh2sWHOEer+rQ=="], @@ -473,7 +473,7 @@ "ink-text-input": ["ink-text-input@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="], - "inquirer": ["inquirer@13.2.0", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/prompts": "^8.2.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-4CBv58vLrL4CnMgrscW/T5cLvfWM2nRLevttTiZTQyku7YV7/pc2IKyABBU2rDfVl4PiIB0sTRcwOac7BIYKLA=="], + "inquirer": ["inquirer@13.2.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/prompts": "^8.2.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-kjIN+joqgbSncQJ6GfN7gV9AbDQlMA+hJ96xcwkQUwP9KN/ZIusoJ2mAfdt0LPrZJQsEyk5i/YrgJQTxSgzlPw=="], "into-stream": ["into-stream@6.0.0", "", { "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA=="], @@ -531,7 +531,7 @@ "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], - "node-abi": ["node-abi@3.86.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-sn9Et4N3ynsetj3spsZR729DVlGH6iBG4RiDMV7HEp3guyOW6W3S0unGpLDxT50mXortGUMax/ykUNQXdqc/Xg=="], + "node-abi": ["node-abi@3.87.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ=="], "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], @@ -603,7 +603,7 @@ "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], - "rollup": ["rollup@4.55.1", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.1", "@rollup/rollup-android-arm64": "4.55.1", "@rollup/rollup-darwin-arm64": "4.55.1", "@rollup/rollup-darwin-x64": "4.55.1", "@rollup/rollup-freebsd-arm64": "4.55.1", "@rollup/rollup-freebsd-x64": "4.55.1", "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", "@rollup/rollup-linux-arm-musleabihf": "4.55.1", "@rollup/rollup-linux-arm64-gnu": "4.55.1", "@rollup/rollup-linux-arm64-musl": "4.55.1", "@rollup/rollup-linux-loong64-gnu": "4.55.1", "@rollup/rollup-linux-loong64-musl": "4.55.1", "@rollup/rollup-linux-ppc64-gnu": "4.55.1", "@rollup/rollup-linux-ppc64-musl": "4.55.1", "@rollup/rollup-linux-riscv64-gnu": "4.55.1", "@rollup/rollup-linux-riscv64-musl": "4.55.1", "@rollup/rollup-linux-s390x-gnu": "4.55.1", "@rollup/rollup-linux-x64-gnu": "4.55.1", "@rollup/rollup-linux-x64-musl": "4.55.1", "@rollup/rollup-openbsd-x64": "4.55.1", "@rollup/rollup-openharmony-arm64": "4.55.1", "@rollup/rollup-win32-arm64-msvc": "4.55.1", "@rollup/rollup-win32-ia32-msvc": "4.55.1", "@rollup/rollup-win32-x64-gnu": "4.55.1", "@rollup/rollup-win32-x64-msvc": "4.55.1", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A=="], + "rollup": ["rollup@4.55.2", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.55.2", "@rollup/rollup-android-arm64": "4.55.2", "@rollup/rollup-darwin-arm64": "4.55.2", "@rollup/rollup-darwin-x64": "4.55.2", "@rollup/rollup-freebsd-arm64": "4.55.2", "@rollup/rollup-freebsd-x64": "4.55.2", "@rollup/rollup-linux-arm-gnueabihf": "4.55.2", "@rollup/rollup-linux-arm-musleabihf": "4.55.2", "@rollup/rollup-linux-arm64-gnu": "4.55.2", "@rollup/rollup-linux-arm64-musl": "4.55.2", "@rollup/rollup-linux-loong64-gnu": "4.55.2", "@rollup/rollup-linux-loong64-musl": "4.55.2", "@rollup/rollup-linux-ppc64-gnu": "4.55.2", "@rollup/rollup-linux-ppc64-musl": "4.55.2", "@rollup/rollup-linux-riscv64-gnu": "4.55.2", "@rollup/rollup-linux-riscv64-musl": "4.55.2", "@rollup/rollup-linux-s390x-gnu": "4.55.2", "@rollup/rollup-linux-x64-gnu": "4.55.2", "@rollup/rollup-linux-x64-musl": "4.55.2", "@rollup/rollup-openbsd-x64": "4.55.2", "@rollup/rollup-openharmony-arm64": "4.55.2", "@rollup/rollup-win32-arm64-msvc": "4.55.2", "@rollup/rollup-win32-ia32-msvc": "4.55.2", "@rollup/rollup-win32-x64-gnu": "4.55.2", "@rollup/rollup-win32-x64-msvc": "4.55.2", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-PggGy4dhwx5qaW+CKBilA/98Ql9keyfnb7lh4SR6shQ91QQQi1ORJ1v4UinkdP2i87OBs9AQFooQylcrrRfIcg=="], "run-async": ["run-async@4.0.6", "", {}, "sha512-IoDlSLTs3Yq593mb3ZoKWKXMNu3UpObxhgA/Xuid5p4bbfi2jdY1Hj0m1K+0/tEuQTxIGMhQDqGjKb7RuxGpAQ=="], @@ -643,7 +643,7 @@ "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], @@ -769,8 +769,6 @@ "stream-meter/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], - "terminal-link/ansi-escapes": ["ansi-escapes@5.0.0", "", { "dependencies": { "type-fest": "^1.0.2" } }, "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA=="], "vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], @@ -795,12 +793,8 @@ "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], - "from2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], - "stream-meter/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], - "terminal-link/ansi-escapes/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], From d22009fdab4841ce4db801f4a83224ba6b640f74 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:34:37 -0800 Subject: [PATCH 29/48] style: use OAPI client instead of raw `fetch` call --- src/index.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 312fc5e4..ff04b58f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,8 +6,8 @@ import process from "node:process"; import { Command } from "@commander-js/extra-typings"; import pkg from "../package.json" with { type: "json" }; import { checkVersion } from "./checkVersion.ts"; +import { apiClient } from "./apiClient.ts"; import { loadConfig, saveConfig } from "./helpers/config.ts"; -import { getApiUrl } from "./helpers/urls.ts"; import { getAppBanner } from "./lib/app-banner.ts"; import { registerBalance } from "./lib/balance.ts"; import { registerBuy } from "./lib/buy/index.tsx"; @@ -87,16 +87,9 @@ async function main() { let exchangeAccountId = config.account_id; if (!exchangeAccountId) { - const response = await fetch(await getApiUrl("me"), { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${config.auth_token}`, - }, - }); - - const data = (await response.json()) as any; - if (data.id) { + const client = await apiClient(config.auth_token); + const { data } = await client.GET("/v0/me"); + if (data?.id) { exchangeAccountId = data.id; saveConfig({ ...config, account_id: data.id }); } From c023637792484cb73e52a4e2ea2d8e16e0a9222e Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:43:32 -0800 Subject: [PATCH 30/48] fix: edit hook ordering in `OrderDisplay` to satisfy Rules of Hooks --- src/lib/orders/OrderDisplay.tsx | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/lib/orders/OrderDisplay.tsx b/src/lib/orders/OrderDisplay.tsx index 3dee254c..bc79b7c1 100644 --- a/src/lib/orders/OrderDisplay.tsx +++ b/src/lib/orders/OrderDisplay.tsx @@ -173,6 +173,15 @@ export function OrderDisplay(props: { } }, [props.orders]); + const { sellOrdersCount, buyOrdersCount } = React.useMemo(() => { + return { + sellOrdersCount: props.orders.filter((order) => order.side === "sell") + .length, + buyOrdersCount: props.orders.filter((order) => order.side === "buy") + .length, + }; + }, [props.orders]); + if (props.orders.length === 0) { return ( @@ -191,15 +200,6 @@ export function OrderDisplay(props: { ? props.orders : props.orders.filter((order) => order.side === activeTab); - const { sellOrdersCount, buyOrdersCount } = React.useMemo(() => { - return { - sellOrdersCount: props.orders.filter((order) => order.side === "sell") - .length, - buyOrdersCount: props.orders.filter((order) => order.side === "buy") - .length, - }; - }, [props.orders]); - return ( - >(reducer, { + const [state, dispatch] = React.useReducer(reducer, { height, scrollTop: 0, innerHeight: 0, From 23cf1caaff10fdd6d4b564cac014ab5743c7f5bd Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:45:19 -0800 Subject: [PATCH 31/48] style: use OAPI client instead of raw fetch call --- src/lib/posthog.ts | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/lib/posthog.ts b/src/lib/posthog.ts index ee7571b4..1ff0776a 100644 --- a/src/lib/posthog.ts +++ b/src/lib/posthog.ts @@ -1,11 +1,11 @@ import process from "node:process"; import { PostHog } from "posthog-node"; +import { apiClient } from "../apiClient.ts"; import { loadConfig, saveConfig } from "../helpers/config.ts"; import { cacheFeatureFlag, getCachedFeatureFlag, } from "../helpers/feature-flags.ts"; -import { getApiUrl } from "../helpers/urls.ts"; const postHogClient = new PostHog( "phc_ErsIQYNj6gPFTkHfupfuUGeKjabwtk3WTPdkTDktbU4", @@ -39,15 +39,9 @@ const trackEvent = ({ let exchangeAccountId = config.account_id; if (!exchangeAccountId) { - const response = await fetch(await getApiUrl("me"), { - method: "GET", - headers: { - "Content-Type": "application/json", - Authorization: `Bearer ${config.auth_token}`, - }, - }); - const data = (await response.json()) as any; - if (data.id) { + const client = await apiClient(config.auth_token); + const { data } = await client.GET("/v0/me"); + if (data?.id) { exchangeAccountId = data.id; saveConfig({ ...config, account_id: data.id }); } From 79d62be299b82f90a0e13821b5e8c5b93a8ac0f4 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:47:10 -0800 Subject: [PATCH 32/48] style: fix more biome warnings --- package.json | 4 +++- src/index.ts | 2 +- src/lib/vm/index.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 54f99933..90978ce1 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "bin": "src/index.ts", "pkg": { "scripts": "src/**/*.ts", - "assets": ["package.json"], + "assets": [ + "package.json" + ], "outputPath": "dist" }, "scripts": { diff --git a/src/index.ts b/src/index.ts index ff04b58f..ed59bc48 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,8 +5,8 @@ import os from "node:os"; import process from "node:process"; import { Command } from "@commander-js/extra-typings"; import pkg from "../package.json" with { type: "json" }; -import { checkVersion } from "./checkVersion.ts"; import { apiClient } from "./apiClient.ts"; +import { checkVersion } from "./checkVersion.ts"; import { loadConfig, saveConfig } from "./helpers/config.ts"; import { getAppBanner } from "./lib/app-banner.ts"; import { registerBalance } from "./lib/balance.ts"; diff --git a/src/lib/vm/index.ts b/src/lib/vm/index.ts index e370f8da..60b69e66 100644 --- a/src/lib/vm/index.ts +++ b/src/lib/vm/index.ts @@ -27,7 +27,7 @@ async function getVMDeprecationWarning() { const client = await nodesClient(); const nodes = await client.nodes.list(); const numNodesWithVMs = - nodes.data?.filter((node) => node.vms?.data?.length ?? 0 > 0).length ?? 0; + nodes.data?.filter((node) => (node.vms?.data?.length ?? 0) > 0).length ?? 0; if (numNodesWithVMs > 0) { return chalk.yellow( boxen( From f7fb9d03cb6ed9f191aaafc2f72e5b9b5a97c2fd Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:47:24 -0800 Subject: [PATCH 33/48] style: ignore deliberate non-typechecking values --- src/helpers/test/units.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/helpers/test/units.test.ts b/src/helpers/test/units.test.ts index 7dfea61a..591db31b 100644 --- a/src/helpers/test/units.test.ts +++ b/src/helpers/test/units.test.ts @@ -60,6 +60,7 @@ test("price whole to cents", () => { const invalidPrices = [null, undefined, [], {}]; for (const input of invalidPrices) { + // biome-ignore lint/suspicious/noExplicitAny: these are invalid inputs and will not typecheck const { cents, invalid } = priceWholeToCents(input as any); expect(cents).toBeNull(); From 86748a6256cb6b9ee41e80d94b8074bda2c73a5a Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:47:52 -0800 Subject: [PATCH 34/48] refactor: use `useEffectEvent` to explicitely handle non-exhaustive deps --- src/lib/nodes/list.tsx | 4 +- src/lib/scale/create.tsx | 169 +++++++++++++------------- src/lib/scale/list.tsx | 18 +-- src/lib/scale/update.tsx | 253 +++++++++++++++++++++------------------ src/lib/sell/index.tsx | 78 ++++++------ 5 files changed, 278 insertions(+), 244 deletions(-) diff --git a/src/lib/nodes/list.tsx b/src/lib/nodes/list.tsx index a49cedbb..caf5630d 100644 --- a/src/lib/nodes/list.tsx +++ b/src/lib/nodes/list.tsx @@ -539,9 +539,9 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { - {nodeActions.map((action, index) => ( + {nodeActions.map((action) => ( diff --git a/src/lib/scale/create.tsx b/src/lib/scale/create.tsx index 576a7b14..a5e3bf4c 100644 --- a/src/lib/scale/create.tsx +++ b/src/lib/scale/create.tsx @@ -4,30 +4,34 @@ import { setTimeout } from "node:timers"; import { Command, Option } from "@commander-js/extra-typings"; import boxen from "boxen"; import dayjs from "dayjs"; -import { Box, Text, render, useApp } from "ink"; +import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import type React from "react"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { pluralizeNodes } from "../nodes/utils.ts"; - +import { + useCallback, + useEffect, + useEffectEvent, + useMemo, + useState, +} from "react"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; +import { roundDateUpToNextMinute } from "../../helpers/units.ts"; import type { components } from "../../schema.ts"; -import ConfirmInput from "../ConfirmInput.tsx"; import { getQuote } from "../buy/index.tsx"; +import ConfirmInput from "../ConfirmInput.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; - -import { roundDateUpToNextMinute } from "../../helpers/units.ts"; +import { pluralizeNodes } from "../nodes/utils.ts"; import ConfirmationMessage from "./ConfirmationMessage.tsx"; import ProcurementDisplay from "./ProcurementDisplay.tsx"; import { + acceleratorsToNodes, type ColocationStrategyName, DEFAULT_LIMIT_PRICE_MULTIPLIER, DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS, MIN_CONTRACT_MINUTES, type Procurement, - acceleratorsToNodes, parseAccelerators, parseHorizonArg, parsePriceArg, @@ -117,7 +121,7 @@ function useCreateProcurement() { setIsLoading(false); } }, - [setIsLoading, setResult, setError], + [], ); return { @@ -163,83 +167,86 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { const [isQuoting, setIsQuoting] = useState(false); const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - useEffect(() => { - (async function init() { - try { - let limitPricePerGpuHourInCents = props.price; - // Get quote if price not specified and not skipping confirmation - if (!props.yes && limitPricePerGpuHourInCents === undefined) { - const quoteMinutes = Math.max(MIN_CONTRACT_MINUTES, props.horizon); - setIsQuoting(true); - - const quoteQuantity = nodesRequired === 0 ? 1 : nodesRequired; - const quote = await getQuote({ - instanceType: props.type, - quantity: quoteQuantity, - minStartTime: "NOW", - maxStartTime: "NOW", - minDurationSeconds: quoteMinutes * 60, - maxDurationSeconds: quoteMinutes * 60 + 3600, - cluster: clusterName, - }); - setIsQuoting(false); - - // Calculate market price from quote or use default - limitPricePerGpuHourInCents = DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; - if (quote) { - // from the market's perspective, "NOW" means at the beginning of the next minute. - // when the order duration is very short, this can cause the rate to be computed incorrectly - // if we implicitly assume it to mean `new Date()`. - const coercedStartTime = - quote.start_at === "NOW" - ? roundDateUpToNextMinute(new Date()) - : new Date(quote.start_at); - const durationSeconds = dayjs(quote.end_at).diff( - dayjs(coercedStartTime), - ); - const quoteDurationHours = durationSeconds / 1000 / 60 / 60; - limitPricePerGpuHourInCents = Math.ceil( - DEFAULT_LIMIT_PRICE_MULTIPLIER * - (quote.price / - (quoteDurationHours * (quoteQuantity * GPUS_PER_NODE))), - ); - } - } - // Use default if still undefined (e.g., --yes without --price) - limitPricePerGpuHourInCents ??= DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; - setDisplayedPricePerGpuHourInCents(limitPricePerGpuHourInCents); - - if (props.yes) { - await createProcurement({ - horizonMinutes: props.horizon, - nodesRequired, - type: props.type, - pricePerGpuHourInCents: limitPricePerGpuHourInCents, - cluster: clusterName, - colocationStrategy, - }); - } else { - setConfirmationMessage( - , + const onInit = useEffectEvent(async () => { + try { + let limitPricePerGpuHourInCents = props.price; + // Get quote if price not specified and not skipping confirmation + if (!props.yes && limitPricePerGpuHourInCents === undefined) { + const quoteMinutes = Math.max(MIN_CONTRACT_MINUTES, props.horizon); + setIsQuoting(true); + + const quoteQuantity = nodesRequired === 0 ? 1 : nodesRequired; + const quote = await getQuote({ + instanceType: props.type, + quantity: quoteQuantity, + minStartTime: "NOW", + maxStartTime: "NOW", + minDurationSeconds: quoteMinutes * 60, + maxDurationSeconds: quoteMinutes * 60 + 3600, + cluster: clusterName, + }); + setIsQuoting(false); + + // Calculate market price from quote or use default + limitPricePerGpuHourInCents = DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; + if (quote) { + // from the market's perspective, "NOW" means at the beginning of the next minute. + // when the order duration is very short, this can cause the rate to be computed incorrectly + // if we implicitly assume it to mean `new Date()`. + const coercedStartTime = + quote.start_at === "NOW" + ? roundDateUpToNextMinute(new Date()) + : new Date(quote.start_at); + const durationSeconds = dayjs(quote.end_at).diff( + dayjs(coercedStartTime), + ); + const quoteDurationHours = durationSeconds / 1000 / 60 / 60; + limitPricePerGpuHourInCents = Math.ceil( + DEFAULT_LIMIT_PRICE_MULTIPLIER * + (quote.price / + (quoteDurationHours * (quoteQuantity * GPUS_PER_NODE))), ); } - } catch (err: unknown) { - setIsQuoting(false); - logAndQuit( - err instanceof Error - ? err.message - : "An unknown error occurred during initialization", + } + + // Use default if still undefined (e.g., --yes without --price) + limitPricePerGpuHourInCents ??= DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; + setDisplayedPricePerGpuHourInCents(limitPricePerGpuHourInCents); + + if (props.yes) { + await createProcurement({ + horizonMinutes: props.horizon, + nodesRequired, + type: props.type, + pricePerGpuHourInCents: limitPricePerGpuHourInCents, + cluster: clusterName, + colocationStrategy, + }); + } else { + setConfirmationMessage( + , ); } - })(); + } catch (err: unknown) { + setIsQuoting(false); + logAndQuit( + err instanceof Error + ? err.message + : "An unknown error occurred during initialization", + ); + } + }); + + useEffect(() => { + onInit(); }, []); const { isLoading, error, result, createProcurement } = diff --git a/src/lib/scale/list.tsx b/src/lib/scale/list.tsx index ad47807d..d6599039 100644 --- a/src/lib/scale/list.tsx +++ b/src/lib/scale/list.tsx @@ -1,13 +1,13 @@ import { setTimeout } from "node:timers"; import { Command } from "@commander-js/extra-typings"; -import { Box, Text, render, useApp } from "ink"; +import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; -import { useEffect, useState } from "react"; +import { useEffect, useEffectEvent, useState } from "react"; import { apiClient } from "../../apiClient.ts"; import ProcurementDisplay from "./ProcurementDisplay.tsx"; -import { type Procurement, getProcurement, parseIds } from "./utils.ts"; +import { getProcurement, type Procurement, parseIds } from "./utils.ts"; async function listProcurements() { const client = await apiClient(); @@ -46,6 +46,10 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { >([]); const { exit } = useApp(); + const onFetchComplete = useEffectEvent(() => { + setTimeout(exit, 0); + }); + useEffect(() => { async function fetchInfo() { try { @@ -60,7 +64,7 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { const failed: { id: string; message: string }[] = []; - settled.forEach((result, idx) => { + for (const [idx, result] of settled.entries()) { if (result.status === "fulfilled" && result.value !== null) { fetchedProcurements.push(result.value); } else { @@ -74,7 +78,7 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { : "Unknown error", }); } - }); + } setFailedFetches(failed); } else { @@ -93,11 +97,11 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { ); } finally { setIsLoading(false); - setTimeout(exit, 0); + onFetchComplete(); } } fetchInfo(); - }, [props.type, props.ids, exit]); + }, [props]); if (isLoading) { return ( diff --git a/src/lib/scale/update.tsx b/src/lib/scale/update.tsx index fc5e27c1..135a1c21 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -1,17 +1,17 @@ +import console from "node:console"; import { setTimeout } from "node:timers"; import { Command } from "@commander-js/extra-typings"; -import { Box, Text, render, useApp } from "ink"; +import chalk from "chalk"; +import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import { type ReactNode, useCallback, useEffect, + useEffectEvent, useMemo, useState, } from "react"; - -import console from "node:console"; -import chalk from "chalk"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; import ConfirmInput from "../ConfirmInput.tsx"; @@ -20,9 +20,9 @@ import ProcurementDisplay, { ProcurementHeader, } from "./ProcurementDisplay.tsx"; import { - type Procurement, acceleratorsToNodes, getProcurement, + type Procurement, parseAccelerators, parseHorizonArg, parseIds, @@ -62,11 +62,15 @@ export async function updateProcurement({ return data; } +interface UpdateResult { + id: string; + result: PromiseSettledResult; +} + function useUpdateProcurements() { const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); - const [results, setResults] = - useState[]>(); + const [results, setResults] = useState(); const updateProcurements = useCallback( async ( @@ -88,10 +92,14 @@ function useUpdateProcurements() { }), ); - const results = await Promise.allSettled(updatePromises); - setResults(results); + const settledResults = await Promise.allSettled(updatePromises); + const resultsWithIds = ids.map((id, i) => ({ + id, + result: settledResults[i], + })); + setResults(resultsWithIds); - const failures = results.filter((r) => r.status === "rejected"); + const failures = settledResults.filter((r) => r.status === "rejected"); if (failures.length > 0) { setError(`Failed to update ${failures.length} procurement(s)`); } @@ -142,109 +150,110 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - useEffect(() => { - (async function init() { - try { - const settledResults = await Promise.allSettled( - props.ids.map((id) => getProcurement({ id })), - ); - const successfullyFetched = settledResults - .filter( - (r): r is PromiseFulfilledResult => - r.status === "fulfilled" && r.value != null, - ) - .map((r) => r.value); + const onInit = useEffectEvent(async () => { + try { + const settledResults = await Promise.allSettled( + props.ids.map((id) => getProcurement({ id })), + ); - const failedToFetch = settledResults - .map((r, i) => [r, props.ids[i]] as const) - .filter( - (r): r is [PromiseRejectedResult, string] => - r[0].status === "rejected", - ) - .map( - (r) => - [ - r[0].reason instanceof Error - ? r[0].reason.message - : "Unknown error", - r[1], - ] as const, - ); + const successfullyFetched = settledResults + .filter( + (r): r is PromiseFulfilledResult => + r.status === "fulfilled" && r.value != null, + ) + .map((r) => r.value); - if (successfullyFetched.length === 0) { - logAndQuit("No procurements could be fetched"); - } + const failedToFetch = settledResults + .map((r, i) => [r, props.ids[i]] as const) + .filter( + (r): r is [PromiseRejectedResult, string] => + r[0].status === "rejected", + ) + .map( + (r) => + [ + r[0].reason instanceof Error + ? r[0].reason.message + : "Unknown error", + r[1], + ] as const, + ); + + if (successfullyFetched.length === 0) { + logAndQuit("No procurements could be fetched"); + } - setProcurements(settledResults); - setDisplayedPricePerGpuHourInCents(props.price); + setProcurements(settledResults); + setDisplayedPricePerGpuHourInCents(props.price); - if (props.yes) { - await updateProcurements( - successfullyFetched.map((p) => p.id), - { - horizonMinutes: props.horizon, - nodesRequired, - pricePerGpuHourInCents: props.price, - }, - ); - } else { - setConfirmationMessage( - - {successfullyFetched?.length > 0 && - successfullyFetched.map((p) => ( - - - + if (props.yes) { + await updateProcurements( + successfullyFetched.map((p) => p.id), + { + horizonMinutes: props.horizon, + nodesRequired, + pricePerGpuHourInCents: props.price, + }, + ); + } else { + setConfirmationMessage( + + {successfullyFetched?.length > 0 && + successfullyFetched.map((p) => ( + + + + + ))} + {failedToFetch?.length > 0 && ( + <> + + Failed to fetch {failedToFetch.length} procurement(s): + + {failedToFetch.map(([message, id]) => ( + + + - {message} ({id}) + ))} - {failedToFetch?.length > 0 && ( - <> - - Failed to fetch {failedToFetch.length} procurement(s): - - {failedToFetch.map(([message, id]) => ( - - - - {message} ({id}) - - - ))} - - )} - , - ); - } - } catch (err: unknown) { - exit( - err instanceof Error ? err : new Error("An unknown error occurred"), + + )} + , ); } - })(); + } catch (err: unknown) { + exit(err instanceof Error ? err : new Error("An unknown error occurred")); + } + }); + + useEffect(() => { + onInit(); }, []); const { isLoading, error, results, updateProcurements } = @@ -276,6 +285,8 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { props.horizon, nodesRequired, displayedPricePerGpuHourInCents, + exit, + updateProcurements, ], ); @@ -295,15 +306,25 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { if (results) { const successfulProcurements = results .filter( - (r): r is PromiseFulfilledResult => - r.status === "fulfilled" && r.value != null, + ( + r, + ): r is UpdateResult & { + result: PromiseFulfilledResult; + } => r.result.status === "fulfilled" && r.result.value != null, ) - .map((r) => r.value); + .map((r) => r.result.value); const failedProcurements = results - .filter((r): r is PromiseRejectedResult => r.status === "rejected") - .map((r) => - r.reason instanceof Error ? r.reason.message : "Unknown error", - ); + .filter( + (r): r is UpdateResult & { result: PromiseRejectedResult } => + r.result.status === "rejected", + ) + .map((r) => ({ + id: r.id, + error: + r.result.reason instanceof Error + ? r.result.reason.message + : "Unknown error", + })); return ( {successfulProcurements && successfulProcurements.length > 0 && ( @@ -316,15 +337,15 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { Failed to update {failedProcurements.length} procurement(s): - {failedProcurements.map((f, i) => ( - - - {f} + {failedProcurements.map((f) => ( + + - {f.id}: {f.error} ))} )} - {successfulProcurements?.map((s, i) => ( - + {successfulProcurements?.map((s) => ( + ))} ); diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index 8ef731d2..3bf8b612 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -3,12 +3,11 @@ import type { Command } from "@commander-js/extra-typings"; import dayjs from "dayjs"; import duration from "dayjs/plugin/duration"; import relativeTime from "dayjs/plugin/relativeTime"; -import { Box, render, useApp } from "ink"; -import { Text } from "ink"; +import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import ms from "ms"; import parseDurationFromLibrary from "parse-duration"; -import { useCallback, useEffect, useState } from "react"; +import { useCallback, useEffect, useEffectEvent, useState } from "react"; import invariant from "tiny-invariant"; import { apiClient } from "../../apiClient.ts"; import { isLoggedIn } from "../../helpers/config.ts"; @@ -21,8 +20,8 @@ import { getContract } from "../../helpers/fetchers.ts"; import { parseStartDate } from "../../helpers/units.ts"; import type { components } from "../../schema.ts"; import ConfirmInput from "../ConfirmInput.tsx"; -import { Row } from "../Row.tsx"; import { GPUS_PER_NODE } from "../constants.ts"; +import { Row } from "../Row.tsx"; type SellOrderFlags = components["schemas"]["market-api_OrderFlags"]; @@ -112,7 +111,7 @@ function parseAccelerators(accelerators?: string) { return 1; } - return Number.parseInt(accelerators) / GPUS_PER_NODE; + return Number.parseInt(accelerators, 10) / GPUS_PER_NODE; } function parseDuration(duration?: string) { @@ -167,6 +166,20 @@ function SellOrder(props: { const { exit } = useApp(); const [order, setOrder] = useState(null); + const submitOrder = useEffectEvent(async () => { + setIsLoading(true); + // Place the sell order + const placedOrder = await placeSellOrder({ + price: props.price, + contractId: props.contractId, + quantity: props.size, + startAt: props.startAt, + endsAt: props.endsAt, + flags: props.flags, + }); + setOrder(placedOrder); + }); + const handleSubmit = useCallback( (submitValue: boolean) => { if (submitValue === false) { @@ -180,44 +193,33 @@ function SellOrder(props: { [exit], ); - async function submitOrder() { - setIsLoading(true); - // Place the sell order - const order = await placeSellOrder({ - price: props.price, - contractId: props.contractId, - quantity: props.size, - startAt: props.startAt, - endsAt: props.endsAt, - flags: props.flags, - }); - setOrder(order); - } - useEffect(() => { if (props.autoConfirm) { submitOrder(); } }, [props.autoConfirm]); + const onPollOrder = useEffectEvent(async () => { + if (!isLoading) { + exit(); + return; + } + + if (!order) { + return; + } + + const o = await getOrder(order.id); + setOrder(o); + if (o) { + setTimeout(exit, 0); + } + }); + useEffect(() => { let interval: ReturnType | null = null; if (isLoading) { - interval = setInterval(async () => { - if (!isLoading) { - exit(); - } - - if (!order) { - return; - } - - const o = await getOrder(order?.id); - setOrder(o); - if (o) { - setTimeout(exit, 0); - } - }, 200); + interval = setInterval(onPollOrder, 200); } return () => { @@ -225,7 +227,7 @@ function SellOrder(props: { clearInterval(interval); } }; - }, [isLoading, exit, order]); + }, [isLoading]); return ( @@ -283,13 +285,13 @@ function SellOrderPreview(props: { const startDate = props.startAt === "NOW" ? dayjs() : dayjs(props.startAt); const start = startDate.format("MMM D h:mm a").toLowerCase(); - // @ts-ignore fromNow not typed + // @ts-expect-error fromNow not typed const startFromNow = startDate.fromNow(); const endDate = roundEndDate(props.endsAt); const end = endDate.format("MMM D h:mm a").toLowerCase(); - // @ts-ignore fromNow not typed + // @ts-expect-error fromNow not typed const endFromNow = endDate.fromNow(); const realDuration = endDate.diff(startDate); @@ -411,7 +413,7 @@ export async function getOrder(orderId: string) { }); if (error) { - // @ts-ignore -- TODO: FIXME: include error in OpenAPI schema output + // @ts-expect-error -- TODO: FIXME: include error in OpenAPI schema output if (error?.code === "order.not_found" || response.status === 404) { return undefined; } From c879d2bd3daf09156128b2a2a8e0891f1edd6a97 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:49:06 -0800 Subject: [PATCH 35/48] style: remove unused `// @ts-expect-error` --- src/lib/sell/index.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index 3bf8b612..07769c79 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -285,13 +285,11 @@ function SellOrderPreview(props: { const startDate = props.startAt === "NOW" ? dayjs() : dayjs(props.startAt); const start = startDate.format("MMM D h:mm a").toLowerCase(); - // @ts-expect-error fromNow not typed const startFromNow = startDate.fromNow(); const endDate = roundEndDate(props.endsAt); const end = endDate.format("MMM D h:mm a").toLowerCase(); - // @ts-expect-error fromNow not typed const endFromNow = endDate.fromNow(); const realDuration = endDate.diff(startDate); From ab29ac671e7e97f1d3a76eaf31b289309f41c51b Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:49:53 -0800 Subject: [PATCH 36/48] ci: use `vitest` --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9100c1f3..729c1a30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,4 +17,4 @@ jobs: - run: bun install - run: biome ci . - run: bun run check - - run: bun test + - run: bun run test From 8d6db5a25b6fa85c34b9cb4e1371223a00f5257f Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 17:53:26 -0800 Subject: [PATCH 37/48] chore: remove `MIGRATION_PLAN` since migration is finished --- MIGRATION_PLAN.md | 431 ---------------------------------------------- 1 file changed, 431 deletions(-) delete mode 100644 MIGRATION_PLAN.md diff --git a/MIGRATION_PLAN.md b/MIGRATION_PLAN.md deleted file mode 100644 index 3050e8ad..00000000 --- a/MIGRATION_PLAN.md +++ /dev/null @@ -1,431 +0,0 @@ -# Deno to Node.js Migration Plan - -## Overview - -This document outlines the migration strategy for the SF CLI from Deno to Node.js. The CLI is currently a Deno-native application that uses npm packages via Deno's compatibility layer. - ---- - -## 1. CI Updates - -### Current Setup (`.github/workflows/ci.yml`) -```yaml -- uses: denoland/setup-deno@v2 -- run: deno install -- run: deno fmt --check -- run: deno lint -- run: deno check --config deno.json . -- run: deno test --allow-all -``` - -### Migration Steps - -1. **Replace Deno setup with Node.js setup:** - ```yaml - - uses: actions/setup-node@v4 - with: - node-version-file: '.tool-versions' # or .nvmrc - cache: 'npm' - - run: npm ci - ``` - -2. **Replace linting/formatting tools:** - | Deno Command | Node.js Replacement | - |--------------|---------------------| - | `deno fmt --check` | `biome format --check .` | - | `deno lint` | `biome lint .` | - | `deno check` | `tsc --noEmit` | - | `deno test` | `vitest` | - - **Note:** This project used Biome before migrating to Deno. Run `git log --all --oneline -- biome.json` to find the commit where Biome was removed, then extract the old `biome.json` config from that commit. - -3. **New CI workflow:** - ```yaml - name: CLI (Check) - on: - push: - branches: [main] - pull_request: - branches: [main] - - jobs: - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: '20' - cache: 'npm' - - run: npm ci - - run: npm run lint - - run: npm run check - - run: npm test - ``` - -4. **New package.json scripts:** - ```json - { - "scripts": { - "dev": "IS_DEVELOPMENT_CLI_ENV=true tsx src/index.ts", - "prod": "tsx src/index.ts", - "lint": "biome check .", - "lint:fix": "biome check --write .", - "check": "tsc --noEmit", - "test": "vitest run", - "test:watch": "vitest" - } - } - ``` - - **Note:** Retain both `dev` and `prod` scripts from the original Deno setup. The `dev` script sets `IS_DEVELOPMENT_CLI_ENV=true` which configures the CLI to use localhost API endpoints. - ---- - -## 2. Release Process Updates - -### Current Setup (`release.yml` + `src/scripts/release.ts`) -- Uses `deno compile` for cross-platform binaries -- Targets: Linux x64/arm64, macOS x64/arm64 -- Creates zip files and uploads to GitHub releases - -### Compilation Tool: `@yao-pkg/pkg` - -Use `@yao-pkg/pkg` (community fork of Vercel's pkg) for cross-platform binary compilation. - -```bash -npm install -D @yao-pkg/pkg -``` - -Compile command: -```bash -pkg . --targets node20-linux-x64,node20-linux-arm64,node20-macos-x64,node20-macos-arm64 -``` - -**Note:** pkg binaries are larger than Deno compiled binaries, but pkg is mature and well-tested. - -Update `src/scripts/release.ts`: -```typescript -const targets = [ - 'node20-linux-x64', - 'node20-linux-arm64', - 'node20-macos-x64', - 'node20-macos-arm64', -]; - -async function compileDistribution() { - // Replace Deno.Command with child_process - const { execSync } = await import('child_process'); - - for (const target of targets) { - const outputName = target.replace('node20-', 'sf-').replace('-', '-'); - execSync(`npx pkg . --target ${target} --output dist/${outputName}`, { - stdio: 'inherit' - }); - // Create zip... - } -} -``` - ---- - -## 3. Auto-Upgrade Compatibility - -### Critical Files -- `src/checkVersion.ts` - Version checking and auto-upgrade logic -- `src/lib/upgrade.ts` - Manual upgrade command -- `install.sh` - Installation script - -### What Stays the Same -1. **Version check URL** - `https://raw.githubusercontent.com/sfcompute/cli/refs/heads/main/package.json` -2. **Binary download URLs** - `https://github.com/sfcompute/cli/releases/download/{version}/sf-{target}.zip` -3. **Install script URL** - `https://www.sfcompute.com/cli/install` -4. **Binary naming convention** - `sf-{target}.zip` - -### What Needs to Change - -1. **Binary target names** (in `install.sh`): - ```bash - # Old (Deno targets) - x86_64-unknown-linux-gnu - aarch64-unknown-linux-gnu - x86_64-apple-darwin - aarch64-apple-darwin - - # New (pkg targets) - KEEP THE SAME OUTPUT NAMES - # Map pkg output to same names for backwards compatibility - ``` - -2. **Maintain backwards compatibility** by keeping the same zip file names: - - `sf-x86_64-unknown-linux-gnu.zip` - - `sf-aarch64-unknown-linux-gnu.zip` - - `sf-x86_64-apple-darwin.zip` - - `sf-aarch64-apple-darwin.zip` - -3. **Update `src/lib/upgrade.ts`**: - ```typescript - // Replace Deno.Command with child_process - import { spawn } from 'child_process'; - - const upgradeProcess = spawn('bash', ['-c', `curl -fsSL ${installScriptUrl} | bash`], { - env: { ...process.env, SF_CLI_VERSION: version }, - stdio: 'inherit' - }); - ``` - -4. **Update `src/scripts/release.ts`**: - - Replace all `Deno.Command` with `child_process.execSync` - - Replace `Deno.readTextFile` with `fs.readFileSync` - - Replace `Deno.writeTextFile` with `fs.writeFileSync` - -### Migration Safety - -To ensure existing users can upgrade: -1. **Keep the same binary names** in GitHub releases -2. **Keep the same install.sh script** (it's platform-detection only) -3. **First Node.js release** should be a minor version bump to signal the change -4. **Test auto-upgrade** from last Deno version to first Node.js version - ---- - -## 4. Deno-Specific Packages to Replace - -### JSR Packages - -| Deno Import | Node.js Replacement | Install | -|-------------|---------------------|---------| -| `jsr:@std/fmt/colors` | `chalk` | `npm i chalk` | -| `jsr:@std/assert` | `vitest` | `npm i -D vitest` | - -**Files affected:** 24+ files use `jsr:@std/fmt/colors` - -**Migration example:** -```typescript -// Before (Deno) -import { cyan, gray, yellow } from "jsr:@std/fmt/colors"; -console.log(cyan('text')); - -// After (Node.js with chalk) -import chalk from 'chalk'; -console.log(chalk.cyan('text')); -``` - -### NPM: Namespace Imports - -These imports use `npm:` prefix which is Deno-specific: - -| Current Import | Change To | -|----------------|-----------| -| `import boxen from "npm:boxen@8.0.1"` | `import boxen from "boxen"` | -| `import * as nacl from "npm:tweetnacl"` | `import * as nacl from "tweetnacl"` | -| `import util from "npm:tweetnacl-util"` | `import util from "tweetnacl-util"` | -| `import cliSpinners from "npm:cli-spinners"` | `import cliSpinners from "cli-spinners"` | -| `import yn from "npm:yn"` | `import yn from "yn"` | - -**Files affected:** 6 files - -### Deno.* APIs - -| Deno API | Node.js Replacement | Notes | -|----------|---------------------|-------| -| `Deno.readTextFile()` | `fs.promises.readFile(path, 'utf-8')` | 11 usages | -| `Deno.writeTextFile()` | `fs.promises.writeFile(path, content)` | 10 usages | -| `Deno.mkdir()` | `fs.promises.mkdir(path, { recursive: true })` | 5 usages | -| `Deno.remove()` | `fs.promises.rm(path, { recursive: true })` | 1 usage | -| `Deno.stat()` | `fs.promises.stat(path)` | 2 usages | -| `Deno.chmod()` | `fs.promises.chmod(path, mode)` | 1 usage | -| `Deno.open()` | `fs.promises.open(path)` | 2 usages | -| `Deno.readDirSync()` | `fs.readdirSync(path)` | 1 usage | -| `Deno.Command` | `child_process.spawn()` | 4 usages | -| `Deno.exit()` | `process.exit()` | 1 usage | -| `Deno.errors.NotFound` | Check `err.code === 'ENOENT'` | 2 usages | -| `Deno.errors.AlreadyExists` | Check `err.code === 'EEXIST'` | 1 usage | -| `Deno.SeekMode.Start` | Use `fileHandle.read()` with position | 1 usage | -| `Deno.test()` | `test()` from vitest/jest | 21 tests | - -**Files requiring Deno API migration:** -1. `src/scripts/release.ts` -2. `src/lib/nodes/image/upload.ts` -3. `src/lib/clusters/kubeconfig.ts` -4. `src/lib/clusters/keys.tsx` -5. `src/helpers/config.ts` -6. `src/helpers/feature-flags.ts` -7. `src/lib/upgrade.ts` - -### Test Files - -| Current | Migration | -|---------|-----------| -| `Deno.test('name', fn)` | `test('name', fn)` (vitest) | -| `import { assertEquals } from "jsr:@std/assert"` | `import { expect } from 'vitest'` | - -**Test files to migrate:** -- `src/helpers/test/units.test.ts` -- `src/helpers/test/duration.test.ts` -- `src/lib/orders/__tests__/OrderDisplay.test.ts` -- `src/lib/clusters/kubeconfig.test.ts` -- `src/lib/clusters/utils.test.ts` - ---- - -## 5. Configuration Changes - -### Remove -- `deno.json` -- `deno.lock` - -### Add/Update - -**tsconfig.json:** -```json -{ - "compilerOptions": { - "target": "ES2022", - "module": "NodeNext", - "moduleResolution": "NodeNext", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "outDir": "dist", - "rootDir": "src", - "declaration": true, - "baseUrl": ".", - "paths": { - "@/*": ["./src/*"] - } - }, - "include": ["src/**/*"], - "exclude": ["node_modules", "dist"] -} -``` - -**Note:** After adding path aliases, you'll need to configure `tsx` and `pkg` to resolve them. For `pkg`, you may need to bundle first with a tool like `tsup` or `esbuild` that resolves the paths, or use `tsc-alias` to rewrite imports after compilation. - -**biome.json:** - -Retrieve the old Biome config from git history: -```bash -# Find the commit where biome.json was removed -git log --all --oneline --diff-filter=D -- biome.json - -# Show the file contents from that commit's parent -git show ^:biome.json > biome.json -``` - -Or create a new config: -```json -{ - "$schema": "https://biomejs.dev/schemas/1.9.0/schema.json", - "organizeImports": { - "enabled": true - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true - } - }, - "formatter": { - "enabled": true, - "indentStyle": "space", - "indentWidth": 2 - }, - "files": { - "ignore": ["dist/", "src/schema.ts", "node_modules/"] - } -} -``` - -**vitest.config.ts:** -```typescript -import { defineConfig } from 'vitest/config'; - -export default defineConfig({ - test: { - include: ['src/**/*.test.ts'], - }, -}); -``` - -**.tool-versions update:** -``` -nodejs 20.19.0 -# Remove deno line -``` - ---- - -## 6. New Dependencies to Add - -```bash -npm install -D \ - typescript \ - tsx \ - vitest \ - @biomejs/biome \ - @yao-pkg/pkg \ - @types/node - -npm install \ - chalk \ - cli-spinners -``` - ---- - -## 7. Migration Order - -1. **Phase 1: Setup** - - Add tsconfig.json with `baseUrl` and `paths` for absolute imports - - Restore biome.json from git history (run `git log --all --oneline --diff-filter=D -- biome.json`) - - Add vitest config - - Update package.json scripts - -2. **Phase 2: Convert to Absolute Imports** - - Configure tsconfig.json paths (e.g., `"@/*": ["./src/*"]`) - - Update all relative imports to use absolute imports - - Example: `import { getConfig } from "../../helpers/config"` → `import { getConfig } from "@/helpers/config"` - - This makes the codebase easier to navigate and refactor - -3. **Phase 3: Replace Deno APIs** (7 files) - - Start with helpers (config.ts, feature-flags.ts) - - Then lib files (upgrade.ts, clusters/*, nodes/image/upload.ts) - - Finally release.ts - -4. **Phase 4: Replace imports** (30+ files) - - Replace `jsr:@std/fmt/colors` with `chalk` - - Remove `npm:` prefixes from imports - -5. **Phase 5: Migrate tests** (5 files) - - Replace Deno.test with vitest - - Replace @std/assert with vitest expects - -6. **Phase 6: Update build/release** - - Update release.ts for pkg - - Update CI workflows - - Test cross-compilation - -7. **Phase 7: Cleanup** - - Remove deno.json - - Remove deno.lock - - Update .tool-versions - - Update README - -8. **Phase 8: Test auto-upgrade path** - - Install old Deno version - - Release new Node.js version - - Verify auto-upgrade works - ---- - -## 9. Risk Mitigation - -1. **Binary size increase**: pkg binaries are larger than Deno compiled binaries. This is an acceptable tradeoff for pkg's maturity and reliability. - -2. **Platform compatibility**: Test all 4 targets before release. - -3. **Auto-upgrade breakage**: Keep exact same zip file names and install.sh logic. - -4. **ESM/CJS issues**: The codebase uses ESM. Ensure all dependencies support ESM or use dynamic imports. - -5. **React/Ink compatibility**: Ink works fine with Node.js, but test thoroughly. From e71a09f8d288cfca86c445bac6f7a18d262e89ea Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 18:52:42 -0800 Subject: [PATCH 38/48] revert: don't use React 19 features --- src/lib/scale/create.tsx | 157 ++++++++++++++++---------------- src/lib/scale/list.tsx | 11 +-- src/lib/scale/update.tsx | 192 +++++++++++++++++++-------------------- src/lib/sell/index.tsx | 69 +++++++------- 4 files changed, 213 insertions(+), 216 deletions(-) diff --git a/src/lib/scale/create.tsx b/src/lib/scale/create.tsx index a5e3bf4c..8325cb5d 100644 --- a/src/lib/scale/create.tsx +++ b/src/lib/scale/create.tsx @@ -7,13 +7,7 @@ import dayjs from "dayjs"; import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import type React from "react"; -import { - useCallback, - useEffect, - useEffectEvent, - useMemo, - useState, -} from "react"; +import { useCallback, useEffect, useMemo, useState } from "react"; import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; import { roundDateUpToNextMinute } from "../../helpers/units.ts"; @@ -168,85 +162,86 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - const onInit = useEffectEvent(async () => { - try { - let limitPricePerGpuHourInCents = props.price; - // Get quote if price not specified and not skipping confirmation - if (!props.yes && limitPricePerGpuHourInCents === undefined) { - const quoteMinutes = Math.max(MIN_CONTRACT_MINUTES, props.horizon); - setIsQuoting(true); - - const quoteQuantity = nodesRequired === 0 ? 1 : nodesRequired; - const quote = await getQuote({ - instanceType: props.type, - quantity: quoteQuantity, - minStartTime: "NOW", - maxStartTime: "NOW", - minDurationSeconds: quoteMinutes * 60, - maxDurationSeconds: quoteMinutes * 60 + 3600, - cluster: clusterName, - }); - setIsQuoting(false); + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. + // We read props inside the effect but don't want to re-run when they change. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + useEffect(() => { + (async function init() { + try { + let limitPricePerGpuHourInCents = props.price; + // Get quote if price not specified and not skipping confirmation + if (!props.yes && limitPricePerGpuHourInCents === undefined) { + const quoteMinutes = Math.max(MIN_CONTRACT_MINUTES, props.horizon); + setIsQuoting(true); + + const quoteQuantity = nodesRequired === 0 ? 1 : nodesRequired; + const quote = await getQuote({ + instanceType: props.type, + quantity: quoteQuantity, + minStartTime: "NOW", + maxStartTime: "NOW", + minDurationSeconds: quoteMinutes * 60, + maxDurationSeconds: quoteMinutes * 60 + 3600, + cluster: clusterName, + }); + setIsQuoting(false); + + // Calculate market price from quote or use default + limitPricePerGpuHourInCents = DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; + if (quote) { + // from the market's perspective, "NOW" means at the beginning of the next minute. + // when the order duration is very short, this can cause the rate to be computed incorrectly + // if we implicitly assume it to mean `new Date()`. + const coercedStartTime = + quote.start_at === "NOW" + ? roundDateUpToNextMinute(new Date()) + : new Date(quote.start_at); + const durationSeconds = dayjs(quote.end_at).diff( + dayjs(coercedStartTime), + ); + const quoteDurationHours = durationSeconds / 1000 / 60 / 60; + limitPricePerGpuHourInCents = Math.ceil( + DEFAULT_LIMIT_PRICE_MULTIPLIER * + (quote.price / + (quoteDurationHours * (quoteQuantity * GPUS_PER_NODE))), + ); + } + } - // Calculate market price from quote or use default - limitPricePerGpuHourInCents = DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; - if (quote) { - // from the market's perspective, "NOW" means at the beginning of the next minute. - // when the order duration is very short, this can cause the rate to be computed incorrectly - // if we implicitly assume it to mean `new Date()`. - const coercedStartTime = - quote.start_at === "NOW" - ? roundDateUpToNextMinute(new Date()) - : new Date(quote.start_at); - const durationSeconds = dayjs(quote.end_at).diff( - dayjs(coercedStartTime), - ); - const quoteDurationHours = durationSeconds / 1000 / 60 / 60; - limitPricePerGpuHourInCents = Math.ceil( - DEFAULT_LIMIT_PRICE_MULTIPLIER * - (quote.price / - (quoteDurationHours * (quoteQuantity * GPUS_PER_NODE))), + // Use default if still undefined (e.g., --yes without --price) + limitPricePerGpuHourInCents ??= DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; + setDisplayedPricePerGpuHourInCents(limitPricePerGpuHourInCents); + + if (props.yes) { + await createProcurement({ + horizonMinutes: props.horizon, + nodesRequired, + type: props.type, + pricePerGpuHourInCents: limitPricePerGpuHourInCents, + cluster: clusterName, + colocationStrategy, + }); + } else { + setConfirmationMessage( + , ); } - } - - // Use default if still undefined (e.g., --yes without --price) - limitPricePerGpuHourInCents ??= DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS; - setDisplayedPricePerGpuHourInCents(limitPricePerGpuHourInCents); - - if (props.yes) { - await createProcurement({ - horizonMinutes: props.horizon, - nodesRequired, - type: props.type, - pricePerGpuHourInCents: limitPricePerGpuHourInCents, - cluster: clusterName, - colocationStrategy, - }); - } else { - setConfirmationMessage( - , + } catch (err: unknown) { + setIsQuoting(false); + logAndQuit( + err instanceof Error + ? err.message + : "An unknown error occurred during initialization", ); } - } catch (err: unknown) { - setIsQuoting(false); - logAndQuit( - err instanceof Error - ? err.message - : "An unknown error occurred during initialization", - ); - } - }); - - useEffect(() => { - onInit(); + })(); }, []); const { isLoading, error, result, createProcurement } = diff --git a/src/lib/scale/list.tsx b/src/lib/scale/list.tsx index d6599039..659014f3 100644 --- a/src/lib/scale/list.tsx +++ b/src/lib/scale/list.tsx @@ -2,7 +2,7 @@ import { setTimeout } from "node:timers"; import { Command } from "@commander-js/extra-typings"; import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; -import { useEffect, useEffectEvent, useState } from "react"; +import { useEffect, useState } from "react"; import { apiClient } from "../../apiClient.ts"; @@ -46,10 +46,9 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { >([]); const { exit } = useApp(); - const onFetchComplete = useEffectEvent(() => { - setTimeout(exit, 0); - }); - + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only when props change. + // We call exit() inside but don't want it as a dependency to avoid re-running on exit changes. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event useEffect(() => { async function fetchInfo() { try { @@ -97,7 +96,7 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { ); } finally { setIsLoading(false); - onFetchComplete(); + setTimeout(exit, 0); } } fetchInfo(); diff --git a/src/lib/scale/update.tsx b/src/lib/scale/update.tsx index 135a1c21..89d35543 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -8,7 +8,6 @@ import { type ReactNode, useCallback, useEffect, - useEffectEvent, useMemo, useState, } from "react"; @@ -151,109 +150,110 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - const onInit = useEffectEvent(async () => { - try { - const settledResults = await Promise.allSettled( - props.ids.map((id) => getProcurement({ id })), - ); + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. + // We read props inside the effect but don't want to re-run when they change. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + useEffect(() => { + (async function onInit() { + try { + const settledResults = await Promise.allSettled( + props.ids.map((id) => getProcurement({ id })), + ); - const successfullyFetched = settledResults - .filter( - (r): r is PromiseFulfilledResult => - r.status === "fulfilled" && r.value != null, - ) - .map((r) => r.value); + const successfullyFetched = settledResults + .filter( + (r): r is PromiseFulfilledResult => + r.status === "fulfilled" && r.value != null, + ) + .map((r) => r.value); - const failedToFetch = settledResults - .map((r, i) => [r, props.ids[i]] as const) - .filter( - (r): r is [PromiseRejectedResult, string] => - r[0].status === "rejected", - ) - .map( - (r) => - [ - r[0].reason instanceof Error - ? r[0].reason.message - : "Unknown error", - r[1], - ] as const, - ); + const failedToFetch = settledResults + .map((r, i) => [r, props.ids[i]] as const) + .filter( + (r): r is [PromiseRejectedResult, string] => + r[0].status === "rejected", + ) + .map( + (r) => + [ + r[0].reason instanceof Error + ? r[0].reason.message + : "Unknown error", + r[1], + ] as const, + ); - if (successfullyFetched.length === 0) { - logAndQuit("No procurements could be fetched"); - } + if (successfullyFetched.length === 0) { + logAndQuit("No procurements could be fetched"); + } - setProcurements(settledResults); - setDisplayedPricePerGpuHourInCents(props.price); + setProcurements(settledResults); + setDisplayedPricePerGpuHourInCents(props.price); - if (props.yes) { - await updateProcurements( - successfullyFetched.map((p) => p.id), - { - horizonMinutes: props.horizon, - nodesRequired, - pricePerGpuHourInCents: props.price, - }, - ); - } else { - setConfirmationMessage( - - {successfullyFetched?.length > 0 && - successfullyFetched.map((p) => ( - - - - - ))} - {failedToFetch?.length > 0 && ( - <> - - Failed to fetch {failedToFetch.length} procurement(s): - - {failedToFetch.map(([message, id]) => ( - - - - {message} ({id}) - + if (props.yes) { + await updateProcurements( + successfullyFetched.map((p) => p.id), + { + horizonMinutes: props.horizon, + nodesRequired, + pricePerGpuHourInCents: props.price, + }, + ); + } else { + setConfirmationMessage( + + {successfullyFetched?.length > 0 && + successfullyFetched.map((p) => ( + + + ))} - - )} - , - ); + {failedToFetch?.length > 0 && ( + <> + + Failed to fetch {failedToFetch.length} procurement(s): + + {failedToFetch.map(([message, id]) => ( + + + - {message} ({id}) + + + ))} + + )} + , + ); + } + } catch (err: unknown) { + exit(err instanceof Error ? err : new Error("An unknown error occurred")); } - } catch (err: unknown) { - exit(err instanceof Error ? err : new Error("An unknown error occurred")); - } - }); - - useEffect(() => { - onInit(); + })(); }, []); const { isLoading, error, results, updateProcurements } = diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index 07769c79..cabe9f9b 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -7,7 +7,7 @@ import { Box, render, Text, useApp } from "ink"; import Spinner from "ink-spinner"; import ms from "ms"; import parseDurationFromLibrary from "parse-duration"; -import { useCallback, useEffect, useEffectEvent, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; import invariant from "tiny-invariant"; import { apiClient } from "../../apiClient.ts"; import { isLoggedIn } from "../../helpers/config.ts"; @@ -166,20 +166,6 @@ function SellOrder(props: { const { exit } = useApp(); const [order, setOrder] = useState(null); - const submitOrder = useEffectEvent(async () => { - setIsLoading(true); - // Place the sell order - const placedOrder = await placeSellOrder({ - price: props.price, - contractId: props.contractId, - quantity: props.size, - startAt: props.startAt, - endsAt: props.endsAt, - flags: props.flags, - }); - setOrder(placedOrder); - }); - const handleSubmit = useCallback( (submitValue: boolean) => { if (submitValue === false) { @@ -190,36 +176,53 @@ function SellOrder(props: { submitOrder(); }, + // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we don't want to re-create callback when props change. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event [exit], ); + async function submitOrder() { + setIsLoading(true); + // Place the sell order + const placedOrder = await placeSellOrder({ + price: props.price, + contractId: props.contractId, + quantity: props.size, + startAt: props.startAt, + endsAt: props.endsAt, + flags: props.flags, + }); + setOrder(placedOrder); + } + + // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we only want to run on mount if autoConfirm is true. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event useEffect(() => { if (props.autoConfirm) { submitOrder(); } }, [props.autoConfirm]); - const onPollOrder = useEffectEvent(async () => { - if (!isLoading) { - exit(); - return; - } - - if (!order) { - return; - } - - const o = await getOrder(order.id); - setOrder(o); - if (o) { - setTimeout(exit, 0); - } - }); - + // biome-ignore lint/correctness/useExhaustiveDependencies: Polling interval reads current state but shouldn't restart on state changes. + // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event useEffect(() => { let interval: ReturnType | null = null; if (isLoading) { - interval = setInterval(onPollOrder, 200); + interval = setInterval(async () => { + if (!isLoading) { + exit(); + } + + if (!order) { + return; + } + + const o = await getOrder(order?.id); + setOrder(o); + if (o) { + setTimeout(exit, 0); + } + }, 200); } return () => { From 9a4fcc8fd86f58789e2f8562f8ae5467b8cb6566 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 18:57:03 -0800 Subject: [PATCH 39/48] style: use biome-ignore on lint errors --- src/lib/orders/OrderDisplay.tsx | 2 +- src/lib/scale/create.tsx | 4 +--- src/lib/scale/list.tsx | 4 +--- src/lib/scale/update.tsx | 8 ++++---- src/lib/sell/index.tsx | 9 +++------ 5 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/lib/orders/OrderDisplay.tsx b/src/lib/orders/OrderDisplay.tsx index bc79b7c1..71011cc8 100644 --- a/src/lib/orders/OrderDisplay.tsx +++ b/src/lib/orders/OrderDisplay.tsx @@ -361,7 +361,7 @@ export function ScrollArea({ }); }, []); - // biome-ignore lint/correctness/useExhaustiveDependencies: + // biome-ignore lint/correctness/useExhaustiveDependencies: dispatch is stable from useReducer and doesn't need to be in deps. useEffect(() => { dispatch({ type: "SWITCHED_TAB" }); }, [activeTab]); diff --git a/src/lib/scale/create.tsx b/src/lib/scale/create.tsx index 8325cb5d..42126f4a 100644 --- a/src/lib/scale/create.tsx +++ b/src/lib/scale/create.tsx @@ -162,9 +162,7 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. - // We read props inside the effect but don't want to re-run when they change. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. We read props inside the effect but don't want to re-run when they change. useEffect(() => { (async function init() { try { diff --git a/src/lib/scale/list.tsx b/src/lib/scale/list.tsx index 659014f3..df27bf3c 100644 --- a/src/lib/scale/list.tsx +++ b/src/lib/scale/list.tsx @@ -46,9 +46,7 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { >([]); const { exit } = useApp(); - // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only when props change. - // We call exit() inside but don't want it as a dependency to avoid re-running on exit changes. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only when props change. We call exit() inside but don't want it as a dependency to avoid re-running on exit changes. useEffect(() => { async function fetchInfo() { try { diff --git a/src/lib/scale/update.tsx b/src/lib/scale/update.tsx index 89d35543..311bd730 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -150,9 +150,7 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { const [displayedPricePerGpuHourInCents, setDisplayedPricePerGpuHourInCents] = useState(); - // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. - // We read props inside the effect but don't want to re-run when they change. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + // biome-ignore lint/correctness/useExhaustiveDependencies: This effect intentionally runs only on mount. We read props inside the effect but don't want to re-run when they change. useEffect(() => { (async function onInit() { try { @@ -251,7 +249,9 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { ); } } catch (err: unknown) { - exit(err instanceof Error ? err : new Error("An unknown error occurred")); + exit( + err instanceof Error ? err : new Error("An unknown error occurred"), + ); } })(); }, []); diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index cabe9f9b..815b5baf 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -166,6 +166,7 @@ function SellOrder(props: { const { exit } = useApp(); const [order, setOrder] = useState(null); + // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we don't want to re-create callback when props change. const handleSubmit = useCallback( (submitValue: boolean) => { if (submitValue === false) { @@ -176,8 +177,6 @@ function SellOrder(props: { submitOrder(); }, - // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we don't want to re-create callback when props change. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event [exit], ); @@ -195,16 +194,14 @@ function SellOrder(props: { setOrder(placedOrder); } - // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we only want to run on mount if autoConfirm is true. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + // biome-ignore lint/correctness/useExhaustiveDependencies: submitOrder reads props but we only want to run on mount if autoConfirm is true. See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event useEffect(() => { if (props.autoConfirm) { submitOrder(); } }, [props.autoConfirm]); - // biome-ignore lint/correctness/useExhaustiveDependencies: Polling interval reads current state but shouldn't restart on state changes. - // See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event + // biome-ignore lint/correctness/useExhaustiveDependencies: Polling interval reads current state but shouldn't restart on state changes. See: https://react.dev/blog/2025/10/01/react-19-2#use-effect-event useEffect(() => { let interval: ReturnType | null = null; if (isLoading) { From 67fbf8b025051fe8e06cd8249e527146d51edc9f Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 19:10:39 -0800 Subject: [PATCH 40/48] fix: remove deprecated biome option --- .vscode/settings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 719479f4..473826ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,8 +2,7 @@ "editor.formatOnSave": true, "editor.defaultFormatter": "biomejs.biome", "editor.codeActionsOnSave": { - "quickfix.biome": "explicit", - "source.organizeImports.biome": "explicit" + "source.fixAll.biome": "explicit" }, "[typescript]": { "editor.defaultFormatter": "biomejs.biome" From 3ba9e8b27eb2c193f7e30868c9f23e65bf3287cf Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 19:10:58 -0800 Subject: [PATCH 41/48] build: use `ink-cjs` and alias `ink-cjs` to `ink@v4` --- bun.lock | 158 ++++++++++++++++++++++++++++++++++++++++----------- package.json | 14 ++--- 2 files changed, 132 insertions(+), 40 deletions(-) diff --git a/bun.lock b/bun.lock index 617680f7..681baf99 100644 --- a/bun.lock +++ b/bun.lock @@ -5,7 +5,7 @@ "name": "sf-cli", "dependencies": { "@commander-js/extra-typings": "^14.0.0", - "@inkjs/ui": "^2.0.0", + "@inkjs/ui": "^1.0.0", "@inquirer/prompts": "^8.2.0", "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", "@types/ms": "^0.7.34", @@ -20,11 +20,11 @@ "date-fns": "^4.1.0", "dayjs": "^1.11.19", "dotenv": "^16.4.5", - "ink": "^6.6.0", + "ink": "npm:ink-cjs@^4.4.1", "ink-link": "^4.1.0", "ink-spinner": "^5.0.0", "ink-testing-library": "^4.0.0", - "ink-text-input": "^6.0.0", + "ink-text-input": "^5.0.1", "inquirer": "^13.2.0", "little-date": "^1.0.0", "ms": "^2.1.3", @@ -34,7 +34,7 @@ "parse-duration": "^2.1.3", "posthog-node": "^4.10.1", "prettier": "^3.5.3", - "react": "^19.2.3", + "react": "^18.3.1", "semver": "^7.6.3", "shescape": "^2.1.1", "tiny-invariant": "^1.3.3", @@ -48,9 +48,11 @@ "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", - "@types/react": "^19.2.9", + "@types/react": "^18.3.21", "@types/semver": "^7.5.8", "@yao-pkg/pkg": "^5.12.0", + "esbuild": "^0.27.2", + "tsup": "^8.5.1", "tsx": "^4.19.0", "typescript": "^5.6.0", "vitest": "^2.1.0", @@ -61,7 +63,7 @@ }, }, "packages": { - "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.2.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-jsElTJ0sQ4wHRz+C45tfect76BwbTbgkgKByOzpCN9xG61N5V6u/glvg1CsNJhq2xJIFpKHSwG3D2wPPuEYOrQ=="], + "@alcalzone/ansi-tokenize": ["@alcalzone/ansi-tokenize@0.1.3", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-3yWxPTq3UQ/FY9p1ErPxIyfT64elWaMvM9lIHnaqpyft63tkxodF5aUElYHrdisWve5cETkh1+KBw1yJuW0aRw=="], "@babel/generator": ["@babel/generator@7.28.6", "", { "dependencies": { "@babel/parser": "^7.28.6", "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw=="], @@ -149,7 +151,7 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], - "@inkjs/ui": ["@inkjs/ui@2.0.0", "", { "dependencies": { "chalk": "^5.3.0", "cli-spinners": "^3.0.0", "deepmerge": "^4.3.1", "figures": "^6.1.0" }, "peerDependencies": { "ink": ">=5" } }, "sha512-5+8fJmwtF9UvikzLfph9sA+LS+l37Ij/szQltkuXLOAXwNkBX9innfzh4pLGXIB59vKEQUtc6D4qGvhD7h3pAg=="], + "@inkjs/ui": ["@inkjs/ui@1.0.0", "", { "dependencies": { "chalk": "^5.2.0", "cli-spinners": "^2.9.0", "deepmerge": "^4.3.1", "figures": "^5.0.0" }, "peerDependencies": { "ink": "^4.2.0" } }, "sha512-JAVX5ntXG3QokXsGelobIc1ORkTQiJU4XiemUoMUzVaZkBpwzOD7NkMm0qzKvysDyrE1nD6keFHRhwsRvhVamw=="], "@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="], @@ -191,6 +193,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@react-pdf/yoga": ["@react-pdf/yoga@4.1.2", "", { "dependencies": { "@babel/runtime": "^7.20.13" } }, "sha512-OlMZkFrJDj4GyKZ70thiObwwPVZ52B7mlPyfzwa+sgwsioqHXg9nMWOO+7SQFNUbbOGagMUu0bCuTv+iXYZuaQ=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.55.2", "", { "os": "android", "cpu": "arm" }, "sha512-21J6xzayjy3O6NdnlO6aXi/urvSRjm6nCI6+nF6ra2YofKruGixN9kfT+dt55HVNwfDmpDHJcaS3JuP/boNnlA=="], "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.55.2", "", { "os": "android", "cpu": "arm64" }, "sha512-eXBg7ibkNUZ+sTwbFiDKou0BAckeV6kIigK7y5Ko4mB/5A1KLhuzEKovsmfvsL8mQorkoincMFGnQuIT92SKqA=="], @@ -253,7 +257,9 @@ "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], - "@types/react": ["@types/react@19.2.9", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA=="], + "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], + + "@types/react": ["@types/react@18.3.27", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" } }, "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w=="], "@types/retry": ["@types/retry@0.12.5", "", {}, "sha512-3xSjTp3v03X/lSQLkczaN9UIEwJMoMCA1+Nb5HfbJEQWogdeQIyVtTvxPXDQjZ5zws8rFQfVfRdz03ARihPJgw=="], @@ -277,16 +283,20 @@ "@yao-pkg/pkg-fetch": ["@yao-pkg/pkg-fetch@3.5.16", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.6", "picocolors": "^1.1.0", "progress": "^2.0.3", "semver": "^7.3.5", "tar-fs": "^2.1.1", "yargs": "^16.2.0" }, "bin": { "pkg-fetch": "lib-es5/bin.js" } }, "sha512-mCnZvZz0/Ylpk4TGyt34pqWJyBGYJM8c3dPoMRV8Knodv2QhcYS4iXb5kB/JNWkrRtCKukGZIKkMLXZ3TQlzPg=="], + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], - "ansi-escapes": ["ansi-escapes@7.2.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw=="], + "ansi-escapes": ["ansi-escapes@6.2.1", "", {}, "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig=="], "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "async-retry": ["async-retry@1.3.3", "", { "dependencies": { "retry": "0.13.1" } }, "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw=="], @@ -305,6 +315,8 @@ "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -319,21 +331,25 @@ "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], "chrono-node": ["chrono-node@2.9.0", "", {}, "sha512-glI4YY2Jy6JII5l3d5FN6rcrIbKSQqKPhWsIRYPK2IK8Mm4Q1ZZFdYIaDqglUNf7gNwG+kWIzTn0omzzE0VkvQ=="], + "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], + "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], "cli-cursor": ["cli-cursor@4.0.0", "", { "dependencies": { "restore-cursor": "^4.0.0" } }, "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg=="], "cli-progress": ["cli-progress@3.12.0", "", { "dependencies": { "string-width": "^4.2.3" } }, "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A=="], - "cli-spinners": ["cli-spinners@3.4.0", "", {}, "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw=="], + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], "cli-table3": ["cli-table3@0.6.5", "", { "dependencies": { "string-width": "^4.2.0" }, "optionalDependencies": { "@colors/colors": "1.5.0" } }, "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ=="], - "cli-truncate": ["cli-truncate@5.1.1", "", { "dependencies": { "slice-ansi": "^7.1.0", "string-width": "^8.0.0" } }, "sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A=="], + "cli-truncate": ["cli-truncate@3.1.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^5.0.0" } }, "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA=="], "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], @@ -349,6 +365,10 @@ "commander": ["commander@14.0.2", "", {}, "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ=="], + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + "convert-to-spaces": ["convert-to-spaces@2.0.1", "", {}, "sha512-rcQ1bsQO9799wq24uE5AM2tAILy4gXGIK/njFWcVQkGNZ96edlpY+A7bjwvzjYvLDyzmG1MmMLZhpcsb+klNMQ=="], "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], @@ -379,12 +399,12 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], - "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], - "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], @@ -395,13 +415,11 @@ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], - "es-toolkit": ["es-toolkit@1.44.0", "", {}, "sha512-6penXeZalaV88MM3cGkFZZfOoLGWshWWfdy0tWw/RlVVyhvMaWSBTOvXNeiW3e5FwdS5ePW0LGEu17zT139ktg=="], - "esbuild": ["esbuild@0.27.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.2", "@esbuild/android-arm": "0.27.2", "@esbuild/android-arm64": "0.27.2", "@esbuild/android-x64": "0.27.2", "@esbuild/darwin-arm64": "0.27.2", "@esbuild/darwin-x64": "0.27.2", "@esbuild/freebsd-arm64": "0.27.2", "@esbuild/freebsd-x64": "0.27.2", "@esbuild/linux-arm": "0.27.2", "@esbuild/linux-arm64": "0.27.2", "@esbuild/linux-ia32": "0.27.2", "@esbuild/linux-loong64": "0.27.2", "@esbuild/linux-mips64el": "0.27.2", "@esbuild/linux-ppc64": "0.27.2", "@esbuild/linux-riscv64": "0.27.2", "@esbuild/linux-s390x": "0.27.2", "@esbuild/linux-x64": "0.27.2", "@esbuild/netbsd-arm64": "0.27.2", "@esbuild/netbsd-x64": "0.27.2", "@esbuild/openbsd-arm64": "0.27.2", "@esbuild/openbsd-x64": "0.27.2", "@esbuild/openharmony-arm64": "0.27.2", "@esbuild/sunos-x64": "0.27.2", "@esbuild/win32-arm64": "0.27.2", "@esbuild/win32-ia32": "0.27.2", "@esbuild/win32-x64": "0.27.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], - "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + "escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], @@ -413,7 +431,9 @@ "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], - "figures": ["figures@6.1.0", "", { "dependencies": { "is-unicode-supported": "^2.0.0" } }, "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg=="], + "figures": ["figures@5.0.0", "", { "dependencies": { "escape-string-regexp": "^5.0.0", "is-unicode-supported": "^1.2.0" } }, "sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg=="], + + "fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="], "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], @@ -463,7 +483,7 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "ink": ["ink@6.6.0", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.2.1", "ansi-escapes": "^7.2.0", "ansi-styles": "^6.2.1", "auto-bind": "^5.0.1", "chalk": "^5.6.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^5.1.1", "code-excerpt": "^4.0.0", "es-toolkit": "^1.39.10", "indent-string": "^5.0.0", "is-in-ci": "^2.0.0", "patch-console": "^2.0.0", "react-reconciler": "^0.33.0", "signal-exit": "^3.0.7", "slice-ansi": "^7.1.0", "stack-utils": "^2.0.6", "string-width": "^8.1.0", "type-fest": "^4.27.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0", "ws": "^8.18.0", "yoga-layout": "~3.2.1" }, "peerDependencies": { "@types/react": ">=19.0.0", "react": ">=19.0.0", "react-devtools-core": "^6.1.2" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-QDt6FgJxgmSxAelcOvOHUvFxbIUjVpCH5bx+Slvc5m7IEcpGt3dYwbz/L+oRnqEGeRvwy1tineKK4ect3nW1vQ=="], + "ink": ["ink-cjs@4.4.1", "", { "dependencies": { "@alcalzone/ansi-tokenize": "^0.1.3", "@react-pdf/yoga": "^4.1.2", "ansi-escapes": "^6.0.0", "auto-bind": "^5.0.1", "chalk": "^5.2.0", "cli-boxes": "^3.0.0", "cli-cursor": "^4.0.0", "cli-truncate": "^3.1.0", "code-excerpt": "^4.0.0", "indent-string": "^5.0.0", "is-ci": "^3.0.1", "is-lower-case": "^2.0.2", "is-upper-case": "^2.0.2", "lodash": "^4.17.21", "patch-console": "^2.0.0", "react-reconciler": "^0.29.0", "scheduler": "^0.23.0", "signal-exit": "^3.0.7", "slice-ansi": "^6.0.0", "stack-utils": "^2.0.6", "string-width": "^5.1.2", "type-fest": "^0.12.0", "widest-line": "^4.0.1", "wrap-ansi": "^8.1.0", "ws": "^8.12.0", "yoga-wasm-web": "~0.3.3" }, "peerDependencies": { "@types/react": ">=18.0.0", "react": ">=18.0.0", "react-devtools-core": "^4.19.1" }, "optionalPeers": ["@types/react", "react-devtools-core"] }, "sha512-M6FDbkDr0Qi0CDDEwSN7zDrgr8nO6HddIt8ZlNhLPmtbbTMkgh5/fuazqqVfB64DgRt/ChhbzN8kLuXB7u6LLQ=="], "ink-link": ["ink-link@4.1.0", "", { "dependencies": { "prop-types": "^15.8.1", "terminal-link": "^3.0.0" }, "peerDependencies": { "ink": ">=4" } }, "sha512-3nNyJXum0FJIKAXBK8qat2jEOM41nJ1J60NRivwgK9Xh92R5UMN/k4vbz0A9xFzhJVrlf4BQEmmxMgXkCE1Jeg=="], @@ -471,32 +491,46 @@ "ink-testing-library": ["ink-testing-library@4.0.0", "", { "peerDependencies": { "@types/react": ">=18.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-yF92kj3pmBvk7oKbSq5vEALO//o7Z9Ck/OaLNlkzXNeYdwfpxMQkSowGTFUCS5MSu9bWfSZMewGpp7bFc66D7Q=="], - "ink-text-input": ["ink-text-input@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "type-fest": "^4.18.2" }, "peerDependencies": { "ink": ">=5", "react": ">=18" } }, "sha512-Fw64n7Yha5deb1rHY137zHTAbSTNelUKuB5Kkk2HACXEtwIHBCf9OH2tP/LQ9fRYTl1F0dZgbW0zPnZk6FA9Lw=="], + "ink-text-input": ["ink-text-input@5.0.1", "", { "dependencies": { "chalk": "^5.2.0", "type-fest": "^3.6.1" }, "peerDependencies": { "ink": "^4.0.0", "react": "^18.0.0" } }, "sha512-crnsYJalG4EhneOFnr/q+Kzw1RgmXI2KsBaLFE6mpiIKxAtJLUnvygOF2IUKO8z4nwkSkveGRBMd81RoYdRSag=="], "inquirer": ["inquirer@13.2.1", "", { "dependencies": { "@inquirer/ansi": "^2.0.3", "@inquirer/core": "^11.1.1", "@inquirer/prompts": "^8.2.0", "@inquirer/type": "^4.0.3", "mute-stream": "^3.0.0", "run-async": "^4.0.6", "rxjs": "^7.8.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-kjIN+joqgbSncQJ6GfN7gV9AbDQlMA+hJ96xcwkQUwP9KN/ZIusoJ2mAfdt0LPrZJQsEyk5i/YrgJQTxSgzlPw=="], "into-stream": ["into-stream@6.0.0", "", { "dependencies": { "from2": "^2.3.0", "p-is-promise": "^3.0.0" } }, "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA=="], + "is-ci": ["is-ci@3.0.1", "", { "dependencies": { "ci-info": "^3.2.0" }, "bin": { "is-ci": "bin.js" } }, "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ=="], + "is-core-module": ["is-core-module@2.16.1", "", { "dependencies": { "hasown": "^2.0.2" } }, "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w=="], "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "is-in-ci": ["is-in-ci@2.0.0", "", { "bin": { "is-in-ci": "cli.js" } }, "sha512-cFeerHriAnhrQSbpAxL37W1wcJKUUX07HyLWZCW1URJT/ra3GyUTzBgUnh24TMVfNTV2Hij2HLxkPHFZfOZy5w=="], - "is-interactive": ["is-interactive@2.0.0", "", {}, "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ=="], + "is-lower-case": ["is-lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-bVcMJy4X5Og6VZfdOZstSexlEy20Sr0k/p/b2IlQJlfdKAQuMpiv5w2Ccxb8sKdRUNAG1PnHVHjFSdRDVS6NlQ=="], + "is-unicode-supported": ["is-unicode-supported@2.1.0", "", {}, "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ=="], + "is-upper-case": ["is-upper-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-44pxmxAvnnAOwBg4tHPnkfvgjPwbc5QIsSstNU+YcJ1ovxVzCWpSGosPJOZh/a1tdl81fbgnLc9LLv+x2ywbPQ=="], + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + "little-date": ["little-date@1.2.1", "", { "dependencies": { "date-fns": "^2.0.0" } }, "sha512-O5LUKWMw96qEyS+12mxnRP49MZEEM9fPWzIylgutX6RHf6F1JakvfNeBgteP0CIeEyNFiPM8nohTldtbmyjEqQ=="], + "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + "log-symbols": ["log-symbols@6.0.0", "", { "dependencies": { "chalk": "^5.3.0", "is-unicode-supported": "^1.3.0" } }, "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw=="], "loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="], @@ -521,12 +555,16 @@ "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "multistream": ["multistream@4.1.0", "", { "dependencies": { "once": "^1.4.0", "readable-stream": "^3.6.0" } }, "sha512-J1XDiAmmNpRCBfIWJv+n0ymC4ABcf/Pl+5YvC5B/D2f/2+8PtHvCNxMPKiQcZyi922Hq69J2YOpb1pTywfifyw=="], "mute-stream": ["mute-stream@3.0.0", "", {}, "sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw=="], + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], @@ -565,8 +603,14 @@ "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], + "posthog-node": ["posthog-node@4.18.0", "", { "dependencies": { "axios": "^1.8.2" } }, "sha512-XROs1h+DNatgKh/AlIlCtDxWzwrKdYDb2mOs58n4yN8BkGN9ewqeQwG5ApS4/IzwCb7HPttUkOVulkYatd2PIw=="], "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], @@ -585,18 +629,22 @@ "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "react": ["react@19.2.3", "", {}, "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA=="], + "react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="], "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-reconciler": ["react-reconciler@0.33.0", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.0" } }, "sha512-KetWRytFv1epdpJc3J4G75I4WrplZE5jOL7Yq0p34+OVOKF4Se7WrdIdVC45XsSSmUTlht2FM/fM1FZb1mfQeA=="], + "react-reconciler": ["react-reconciler@0.29.2", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-zZQqIiYgDCTP/f1N/mAR10nJGrPD2ZR+jDSEsKWJHYC7Cm2wodlwbR3upZRdC3cjIjSlTLNVyO7Iu0Yy7t2AYg=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "require-directory": ["require-directory@2.1.1", "", {}, "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q=="], "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], "restore-cursor": ["restore-cursor@4.0.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg=="], @@ -613,7 +661,7 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], - "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], + "scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="], "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -627,7 +675,9 @@ "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - "slice-ansi": ["slice-ansi@7.1.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w=="], + "slice-ansi": ["slice-ansi@6.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-6bn4hRfkTvDfUoEQYkERg0BVF1D0vrX9HEkMl08uDiNWvVvjylLHvZFZWkDo6wjT8tUctbYl1nCOuE66ZTaUtA=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -643,12 +693,14 @@ "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "supports-hyperlinks": ["supports-hyperlinks@2.3.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA=="], @@ -661,6 +713,10 @@ "terminal-link": ["terminal-link@3.0.0", "", { "dependencies": { "ansi-escapes": "^5.0.0", "supports-hyperlinks": "^2.2.0" } }, "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + "tiny-invariant": ["tiny-invariant@1.3.3", "", {}, "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg=="], "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], @@ -677,8 +733,14 @@ "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="], + "tsx": ["tsx@4.21.0", "", { "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], @@ -691,6 +753,8 @@ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -729,9 +793,9 @@ "yn": ["yn@5.1.0", "", {}, "sha512-TfXLvT6eVsBNIm8rAXTwJYdQFtOXaHQ+rA7LU8HL8C/BFfaSfhvFE5T1rHAdBCbAj808HaqjXVkmo8jmeGOqhw=="], - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], + "yoga-wasm-web": ["yoga-wasm-web@0.3.3", "", {}, "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA=="], - "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + "@alcalzone/ansi-tokenize/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], "@inquirer/core/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -743,7 +807,9 @@ "cli-table3/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "cli-truncate/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], + "cli-truncate/slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "cli-truncate/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "cliui/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -751,24 +817,40 @@ "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "figures/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + "from2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "ink/string-width": ["string-width@8.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "strip-ansi": "^7.1.0" } }, "sha512-Kxl3KJGb/gxkaUMOjRsQ8IrXiGW75O4E3RPjFIINOVH8AMl2SQ/yWdTzWwF3FevIX9LcMAjJW+GRwAlAbTSXdg=="], + "ink/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], - "ink-spinner/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "ink/type-fest": ["type-fest@0.12.0", "", {}, "sha512-53RyidyjvkGpnWPMF9bQgFtWp+Sl8O2Rp13VavmJgfAP9WWG6q6TkrKU8iyJdnwnfgHI6k2hTlgqH4aSdjoTbg=="], + + "ink/widest-line": ["widest-line@4.0.1", "", { "dependencies": { "string-width": "^5.0.1" } }, "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig=="], + + "ink/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "ink-text-input/type-fest": ["type-fest@3.13.1", "", {}, "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="], "little-date/date-fns": ["date-fns@2.30.0", "", { "dependencies": { "@babel/runtime": "^7.21.0" } }, "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw=="], "log-symbols/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], + "mlly/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "ora/cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], - "ora/cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], + "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], "stream-meter/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "string_decoder/safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "sucrase/commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + "terminal-link/ansi-escapes": ["ansi-escapes@5.0.0", "", { "dependencies": { "type-fest": "^1.0.2" } }, "sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA=="], "vite/esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], @@ -787,14 +869,24 @@ "cli-table3/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "cli-truncate/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "cliui/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "from2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "ink/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + "ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + "stream-meter/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "terminal-link/ansi-escapes/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], "vite/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], diff --git a/package.json b/package.json index 90978ce1..bf3e4941 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,8 @@ { "name": "sf-cli", "type": "module", - "bin": "src/index.ts", + "bin": "dist/index.cjs", "pkg": { - "scripts": "src/**/*.ts", "assets": [ "package.json" ], @@ -21,7 +20,7 @@ }, "dependencies": { "@commander-js/extra-typings": "^14.0.0", - "@inkjs/ui": "^2.0.0", + "@inkjs/ui": "^1.0.0", "@inquirer/prompts": "^8.2.0", "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", "@types/ms": "^0.7.34", @@ -36,11 +35,11 @@ "date-fns": "^4.1.0", "dayjs": "^1.11.19", "dotenv": "^16.4.5", - "ink": "^6.6.0", + "ink": "npm:ink-cjs@^4.4.1", "ink-link": "^4.1.0", "ink-spinner": "^5.0.0", "ink-testing-library": "^4.0.0", - "ink-text-input": "^6.0.0", + "ink-text-input": "^5.0.1", "inquirer": "^13.2.0", "little-date": "^1.0.0", "ms": "^2.1.3", @@ -50,7 +49,7 @@ "parse-duration": "^2.1.3", "posthog-node": "^4.10.1", "prettier": "^3.5.3", - "react": "^19.2.3", + "react": "^18.3.1", "semver": "^7.6.3", "shescape": "^2.1.1", "tiny-invariant": "^1.3.3", @@ -64,9 +63,10 @@ "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", "@types/node": "^20.0.0", - "@types/react": "^19.2.9", + "@types/react": "^18.3.21", "@types/semver": "^7.5.8", "@yao-pkg/pkg": "^5.12.0", + "tsup": "^8.5.1", "tsx": "^4.19.0", "typescript": "^5.6.0", "vitest": "^2.1.0" From 96e4fb21248dff6cdd484da4f2e42d88b02b48bc Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 19:12:00 -0800 Subject: [PATCH 42/48] fix: handle bugbot review --- src/lib/posthog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/posthog.ts b/src/lib/posthog.ts index 1ff0776a..addd8d4e 100644 --- a/src/lib/posthog.ts +++ b/src/lib/posthog.ts @@ -43,7 +43,7 @@ const trackEvent = ({ const { data } = await client.GET("/v0/me"); if (data?.id) { exchangeAccountId = data.id; - saveConfig({ ...config, account_id: data.id }); + await saveConfig({ ...config, account_id: data.id }); } } From a12a46e437da075455650d5273c810b51e2ce035 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 20:43:19 -0800 Subject: [PATCH 43/48] build: upgrade from `node20` to `node22` --- .tool-versions | 2 +- README.md | 2 +- install.sh | 8 ++++---- package.json | 4 ++-- src/index.ts | 3 ++- src/scripts/release.ts | 9 +++++---- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/.tool-versions b/.tool-versions index 77730a63..a3128f26 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -nodejs 20.19.0 +nodejs 22.22.0 diff --git a/README.md b/README.md index 09086127..b778a2ec 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ sf --version # 0.1.0 ### Setup -- Install [Node.js](https://nodejs.org/) (v20 or later) - used as the runtime +- Install [Node.js](https://nodejs.org/) (v22 or later) - used as the runtime - Install [Bun](https://bun.sh/) - used as the package manager - Install dependencies: `bun install` - Auth your CLI with `bun run prod login` diff --git a/install.sh b/install.sh index 9b045b2f..d2af927a 100755 --- a/install.sh +++ b/install.sh @@ -46,10 +46,10 @@ TARGET_FILE="${TARGET_DIR}/${BINARY_NAME}" if [ "$OS" = "Linux" ]; then case "${ARCH}" in x86_64) - target='node20-linux-x64' + target='node22-linux-x64' ;; aarch64) - target='node20-linux-arm64' + target='node22-linux-arm64' ;; *) echo "Unsupported Linux architecture: ${ARCH}" >&2 @@ -59,10 +59,10 @@ if [ "$OS" = "Linux" ]; then elif [ "$OS" = "Darwin" ]; then case "${ARCH}" in x86_64) - target='node20-macos-x64' + target='node22-macos-x64' ;; arm64) - target='node20-macos-arm64' + target='node22-macos-arm64' ;; *) echo "Unsupported macOS architecture: ${ARCH}" >&2 diff --git a/package.json b/package.json index bf3e4941..34fa2535 100644 --- a/package.json +++ b/package.json @@ -62,10 +62,10 @@ "@biomejs/biome": "^2.3.11", "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", - "@types/node": "^20.0.0", + "@types/node": "^22.0.0", "@types/react": "^18.3.21", "@types/semver": "^7.5.8", - "@yao-pkg/pkg": "^5.12.0", + "@yao-pkg/pkg": "^6.12.0", "tsup": "^8.5.1", "tsx": "^4.19.0", "typescript": "^5.6.0", diff --git a/src/index.ts b/src/index.ts index ed59bc48..7b93ef5a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ -#!/usr/bin/env bun +#!/usr/bin/env node + import * as console from "node:console"; import os from "node:os"; diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 2ddac31e..120df00f 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -1,3 +1,4 @@ +#!/usr/bin/env node import { spawnSync } from "node:child_process"; import * as console from "node:console"; import * as fs from "node:fs"; @@ -50,10 +51,10 @@ function saveVersion(version: string) { } const COMPILE_TARGETS: string[] = [ - "node20-linux-x64", - "node20-linux-arm64", - "node20-macos-x64", - "node20-macos-arm64", + "node22-linux-x64", + "node22-linux-arm64", + "node22-macos-x64", + "node22-macos-arm64", ]; async function compileDistribution() { From 5ff5a539b15fee2cba05526a23131f58eb59d5e9 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 20:45:13 -0800 Subject: [PATCH 44/48] build: migrate from `deno compile` to `tsup` | `pkg` pipeline --- src/scripts/release.ts | 81 +++++++++++++++++++++++++++++++----------- tsup.config.ts | 23 ++++++++++++ 2 files changed, 84 insertions(+), 20 deletions(-) create mode 100644 tsup.config.ts diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 120df00f..4aca5f3d 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -58,18 +58,35 @@ const COMPILE_TARGETS: string[] = [ ]; async function compileDistribution() { - // Create dist directory + // Clean and create dist directory + fs.rmSync("./dist", { recursive: true, force: true }); fs.mkdirSync("./dist", { recursive: true }); + // Bundle with tsup first + console.log("Bundling with tsup..."); + const tsupResult = spawnSync("npx", ["tsup"], { stdio: "inherit" }); + + if (tsupResult.status !== 0) { + logAndError("Failed to bundle with tsup"); + } + console.log("✅ Bundle created at dist/index.cjs"); + + // Compile for each target using @yao-pkg/pkg + // Note: segfaults on macOS arm64 were fixed by polyfilling Intl.Segmenter + // See: https://github.com/yao-pkg/pkg-fetch/issues/134 for (const target of COMPILE_TARGETS) { - const result = spawnSync("npx", [ - "pkg", - ".", - "--target", - target, - "--output", - `dist/sf-${target}`, - ]); + const result = spawnSync( + "npx", + [ + "@yao-pkg/pkg", + "dist/index.cjs", + "--target", + target, + "--output", + `dist/sf-${target}`, + ], + { stdio: "inherit" }, + ); if (result.status !== 0) { console.error(result.stderr?.toString() ?? ""); @@ -178,26 +195,36 @@ program "A github release tool for the project. Valid types are: major, minor, patch, prerelease", ) .addArgument( - new Argument("type").choices([ + new Argument("[type]").choices([ "major", "minor", "patch", "prerelease", ] as const), ) - .action(async (type) => { + .option("--no-commit", "Dry run: build only, skip version bump, git commit, and GitHub release") + .action(async (type, options) => { try { - const ghCheckResult = spawnSync("which", ["gh"]); + const noCommit = !options.commit; + + if (!noCommit && !type) { + console.error("error: type argument is required when not using --no-commit"); + process.exit(1); + } - if (ghCheckResult.status !== 0) { - console.error( - `The 'gh' command is not installed. Please install it. + if (!noCommit) { + const ghCheckResult = spawnSync("which", ["gh"]); + + if (ghCheckResult.status !== 0) { + console.error( + `The 'gh' command is not installed. Please install it. $ brew install gh `, - ); - process.exit(1); + ); + process.exit(1); + } } process.on("SIGINT", () => { @@ -212,10 +239,24 @@ program await cleanDist(); const version = await getLocalVersion(); - const bumpedVersion = bumpVersion(version, type); - await saveVersion(bumpedVersion); + const bumpedVersion = type ? bumpVersion(version, type) : version; + + if (noCommit) { + if (type) { + console.log(`🔍 Dry run: would bump version to ${bumpedVersion}`); + } + } else { + await saveVersion(bumpedVersion); + } + await compileDistribution(); - await createRelease(bumpedVersion); + + if (noCommit) { + console.log("🔍 Dry run: skipping GitHub release and git commit"); + console.log(`✅ Dry run complete. Binaries available in ./dist`); + } else { + await createRelease(bumpedVersion); + } } catch (err) { console.error(err); } diff --git a/tsup.config.ts b/tsup.config.ts new file mode 100644 index 00000000..e0244e43 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["cjs"], + target: "node20", + platform: "node", + outDir: "dist", + clean: true, + shims: true, + treeshake: true, + noExternal: [/.*/], // Bundle ALL dependencies + define: { + "process.env.NODE_ENV": '"production"', + }, + esbuildOptions(options) { + options.bundle = true; + options.alias = { + "react-devtools-core": "./src/stubs/react-devtools-core.ts", + }; + }, +}); + From ba835b0d1d45084068d0e303dfcaba80de076ff4 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 20:47:17 -0800 Subject: [PATCH 45/48] build: polyfill intl segmenter to avoid SEGFAULT --- bun.lock | 86 ++++++++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + src/index.ts | 6 ++++ 3 files changed, 84 insertions(+), 9 deletions(-) diff --git a/bun.lock b/bun.lock index 681baf99..618e037f 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "name": "sf-cli", "dependencies": { "@commander-js/extra-typings": "^14.0.0", + "@formatjs/intl-segmenter": "^12.1.0", "@inkjs/ui": "^1.0.0", "@inquirer/prompts": "^8.2.0", "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", @@ -47,11 +48,10 @@ "@biomejs/biome": "^2.3.11", "@types/async-retry": "^1.4.9", "@types/cli-progress": "^3.11.6", - "@types/node": "^20.0.0", + "@types/node": "^22.0.0", "@types/react": "^18.3.21", "@types/semver": "^7.5.8", - "@yao-pkg/pkg": "^5.12.0", - "esbuild": "^0.27.2", + "@yao-pkg/pkg": "^6.12.0", "tsup": "^8.5.1", "tsx": "^4.19.0", "typescript": "^5.6.0", @@ -151,6 +151,14 @@ "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.2", "", { "os": "win32", "cpu": "x64" }, "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ=="], + "@formatjs/ecma402-abstract": ["@formatjs/ecma402-abstract@3.1.0", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.0", "@formatjs/intl-localematcher": "0.8.0", "decimal.js": "^10.6.0", "tslib": "^2.8.1" } }, "sha512-CjP1sUzM7XiQW6YluDreN+dMvcKZysO/J4ikvuDjDyd6nSOoSqAK9gvD1s75ZFaJVXtYOsz+y3CUXPZ1sKxcxw=="], + + "@formatjs/fast-memoize": ["@formatjs/fast-memoize@3.1.0", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-b5mvSWCI+XVKiz5WhnBCY3RJ4ZwfjAidU0yVlKa3d3MSgKmH1hC3tBGEAtYyN5mqL7N0G5x0BOUYyO8CEupWgg=="], + + "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.8.0", "", { "dependencies": { "@formatjs/fast-memoize": "3.1.0", "tslib": "^2.8.1" } }, "sha512-zgMYWdUlmEZpX2Io+v3LHrfq9xZ6khpQVf9UAw2xYWhGerGgI9XgH1HvL/A34jWiruUJpYlP5pk4g8nIcaDrXQ=="], + + "@formatjs/intl-segmenter": ["@formatjs/intl-segmenter@12.1.0", "", { "dependencies": { "@formatjs/ecma402-abstract": "3.1.0", "@formatjs/intl-localematcher": "0.8.0", "tslib": "^2.8.1" } }, "sha512-Ju9xMHPHo+xjMOYUZPsoAno3Je5ujV+Bh9iZg9V2Nvl/6Fo9nYBOw4HqauzjRqPkNKZPLqfly13PW44A7KCTJA=="], + "@inkjs/ui": ["@inkjs/ui@1.0.0", "", { "dependencies": { "chalk": "^5.2.0", "cli-spinners": "^2.9.0", "deepmerge": "^4.3.1", "figures": "^5.0.0" }, "peerDependencies": { "ink": "^4.2.0" } }, "sha512-JAVX5ntXG3QokXsGelobIc1ORkTQiJU4XiemUoMUzVaZkBpwzOD7NkMm0qzKvysDyrE1nD6keFHRhwsRvhVamw=="], "@inquirer/ansi": ["@inquirer/ansi@2.0.3", "", {}, "sha512-g44zhR3NIKVs0zUesa4iMzExmZpLUdTLRMCStqX3GE5NT6VkPcxQGJ+uC8tDgBUC/vB1rUhUd55cOf++4NZcmw=="], @@ -185,6 +193,8 @@ "@inquirer/type": ["@inquirer/type@4.0.3", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-cKZN7qcXOpj1h+1eTTcGDVLaBIHNMT1Rz9JqJP5MnEJ0JhgVWllx7H/tahUp5YEK1qaByH2Itb8wLG/iScD5kw=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], @@ -255,7 +265,7 @@ "@types/ms": ["@types/ms@0.7.34", "", {}, "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g=="], - "@types/node": ["@types/node@20.19.30", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g=="], + "@types/node": ["@types/node@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], "@types/prop-types": ["@types/prop-types@15.7.15", "", {}, "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw=="], @@ -279,9 +289,9 @@ "@vitest/utils": ["@vitest/utils@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ=="], - "@yao-pkg/pkg": ["@yao-pkg/pkg@5.16.1", "", { "dependencies": { "@babel/generator": "^7.23.0", "@babel/parser": "^7.23.0", "@babel/types": "^7.23.0", "@yao-pkg/pkg-fetch": "3.5.16", "into-stream": "^6.0.0", "minimist": "^1.2.6", "multistream": "^4.1.0", "picocolors": "^1.1.0", "picomatch": "^4.0.2", "prebuild-install": "^7.1.1", "resolve": "^1.22.0", "stream-meter": "^1.0.4", "tinyglobby": "^0.2.9" }, "bin": { "pkg": "lib-es5/bin.js" } }, "sha512-crUlnNFSReFNFuXDc4f3X2ignkFlc9kmEG7Bp/mJMA1jYyqR0lqjZGLgrSDYTYiNsYud8AzgA3RY1DrMdcUZWg=="], + "@yao-pkg/pkg": ["@yao-pkg/pkg@6.12.0", "", { "dependencies": { "@babel/generator": "^7.23.0", "@babel/parser": "^7.23.0", "@babel/types": "^7.23.0", "@yao-pkg/pkg-fetch": "3.5.32", "into-stream": "^6.0.0", "minimist": "^1.2.6", "multistream": "^4.1.0", "picocolors": "^1.1.0", "picomatch": "^4.0.2", "prebuild-install": "^7.1.1", "resolve": "^1.22.10", "stream-meter": "^1.0.4", "tar": "^7.4.3", "tinyglobby": "^0.2.11", "unzipper": "^0.12.3" }, "bin": { "pkg": "lib-es5/bin.js" } }, "sha512-yXdr5XTnEUm+AuBWPvMdv1z6dCcuKLUPYGZKPwb0pS8YE+P/Jspb47QjutcjfA31tIkGU6JTsOhlGxDxrO/A2w=="], - "@yao-pkg/pkg-fetch": ["@yao-pkg/pkg-fetch@3.5.16", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.6", "picocolors": "^1.1.0", "progress": "^2.0.3", "semver": "^7.3.5", "tar-fs": "^2.1.1", "yargs": "^16.2.0" }, "bin": { "pkg-fetch": "lib-es5/bin.js" } }, "sha512-mCnZvZz0/Ylpk4TGyt34pqWJyBGYJM8c3dPoMRV8Knodv2QhcYS4iXb5kB/JNWkrRtCKukGZIKkMLXZ3TQlzPg=="], + "@yao-pkg/pkg-fetch": ["@yao-pkg/pkg-fetch@3.5.32", "", { "dependencies": { "https-proxy-agent": "^5.0.0", "node-fetch": "^2.6.6", "picocolors": "^1.1.0", "progress": "^2.0.3", "semver": "^7.3.5", "tar-fs": "^3.1.1", "yargs": "^16.2.0" }, "bin": { "pkg-fetch": "lib-es5/bin.js" } }, "sha512-hS8zzze5VVyVAciZoNX4q3ZmCdn0gi34P2SElk4PFbzkA4LPs7qBJ3LhUKb4d4KGw1HVkFJJOMgGomH7cMqQ5Q=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -307,10 +317,26 @@ "axios": ["axios@1.13.2", "", { "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", "proxy-from-env": "^1.1.0" } }, "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA=="], + "b4a": ["b4a@1.7.3", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q=="], + + "bare-events": ["bare-events@2.8.2", "", { "peerDependencies": { "bare-abort-controller": "*" }, "optionalPeers": ["bare-abort-controller"] }, "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ=="], + + "bare-fs": ["bare-fs@4.5.2", "", { "dependencies": { "bare-events": "^2.5.4", "bare-path": "^3.0.0", "bare-stream": "^2.6.4", "bare-url": "^2.2.2", "fast-fifo": "^1.3.2" }, "peerDependencies": { "bare-buffer": "*" }, "optionalPeers": ["bare-buffer"] }, "sha512-veTnRzkb6aPHOvSKIOy60KzURfBdUflr5VReI+NSaPL6xf+XLdONQgZgpYvUuZLVQ8dCqxpBAudaOM1+KpAUxw=="], + + "bare-os": ["bare-os@3.6.2", "", {}, "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A=="], + + "bare-path": ["bare-path@3.0.0", "", { "dependencies": { "bare-os": "^3.0.1" } }, "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw=="], + + "bare-stream": ["bare-stream@2.7.0", "", { "dependencies": { "streamx": "^2.21.0" }, "peerDependencies": { "bare-buffer": "*", "bare-events": "*" }, "optionalPeers": ["bare-buffer", "bare-events"] }, "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A=="], + + "bare-url": ["bare-url@2.3.2", "", { "dependencies": { "bare-path": "^3.0.0" } }, "sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw=="], + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "bluebird": ["bluebird@3.7.2", "", {}, "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="], + "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], @@ -333,7 +359,7 @@ "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], "chrono-node": ["chrono-node@2.9.0", "", {}, "sha512-glI4YY2Jy6JII5l3d5FN6rcrIbKSQqKPhWsIRYPK2IK8Mm4Q1ZZFdYIaDqglUNf7gNwG+kWIzTn0omzzE0VkvQ=="], @@ -383,6 +409,8 @@ "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + "decimal.js": ["decimal.js@10.6.0", "", {}, "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg=="], + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], @@ -399,6 +427,8 @@ "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "duplexer2": ["duplexer2@0.1.4", "", { "dependencies": { "readable-stream": "^2.0.2" } }, "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], @@ -423,10 +453,14 @@ "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "events-universal": ["events-universal@1.0.1", "", { "dependencies": { "bare-events": "^2.7.0" } }, "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw=="], + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + "fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="], + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], @@ -445,6 +479,8 @@ "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + "fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -463,6 +499,8 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], @@ -521,6 +559,8 @@ "jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "jsonfile": ["jsonfile@6.2.0", "", { "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg=="], + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], @@ -553,6 +593,10 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], "mlly": ["mlly@1.8.0", "", { "dependencies": { "acorn": "^8.15.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.1" } }, "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g=="], @@ -575,6 +619,8 @@ "node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="], + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], @@ -691,6 +737,8 @@ "stream-meter": ["stream-meter@1.0.4", "", { "dependencies": { "readable-stream": "^2.1.4" } }, "sha512-4sOEtrbgFotXwnEuzzsQBYEV1elAeFSO8rSGeTwabuX1RRn/kEq9JVH7I0MRBhKVRR0sJkr0M0QCH7yOLf9fhQ=="], + "streamx": ["streamx@2.23.0", "", { "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" } }, "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg=="], + "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], @@ -707,12 +755,16 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + "tar": ["tar@7.5.6", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA=="], - "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "tar-fs": ["tar-fs@3.1.1", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg=="], + + "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], "terminal-link": ["terminal-link@3.0.0", "", { "dependencies": { "ansi-escapes": "^5.0.0", "supports-hyperlinks": "^2.2.0" } }, "sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg=="], + "text-decoder": ["text-decoder@1.2.3", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -757,6 +809,10 @@ "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + "universalify": ["universalify@2.0.1", "", {}, "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw=="], + + "unzipper": ["unzipper@0.12.3", "", { "dependencies": { "bluebird": "~3.7.2", "duplexer2": "~0.1.4", "fs-extra": "^11.2.0", "graceful-fs": "^4.2.2", "node-int64": "^0.4.0" } }, "sha512-PZ8hTS+AqcGxsaQntl3IRBw65QrBI6lxzqDEL7IAo/XCEqRTKGfOX56Vea5TH9SZczRVxuzk1re04z/YjuYCJA=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], @@ -785,6 +841,8 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + "yaml": ["yaml@2.6.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg=="], "yargs": ["yargs@16.2.0", "", { "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" } }, "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw=="], @@ -817,6 +875,8 @@ "cliui/wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "duplexer2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + "figures/is-unicode-supported": ["is-unicode-supported@1.3.0", "", {}, "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ=="], "from2/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], @@ -841,6 +901,8 @@ "pkg-types/pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + "prebuild-install/tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], "stack-utils/escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], @@ -879,12 +941,18 @@ "cliui/wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + "duplexer2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + "from2/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], "ink/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "ora/cli-cursor/restore-cursor": ["restore-cursor@5.1.0", "", { "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" } }, "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA=="], + "prebuild-install/tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "prebuild-install/tar-fs/tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "stream-meter/readable-stream/string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], "terminal-link/ansi-escapes/type-fest": ["type-fest@1.4.0", "", {}, "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="], diff --git a/package.json b/package.json index 34fa2535..7140656d 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "dependencies": { "@commander-js/extra-typings": "^14.0.0", + "@formatjs/intl-segmenter": "^12.1.0", "@inkjs/ui": "^1.0.0", "@inquirer/prompts": "^8.2.0", "@sfcompute/nodes-sdk-alpha": "0.1.0-alpha.27", diff --git a/src/index.ts b/src/index.ts index 7b93ef5a..ebfce211 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,11 @@ #!/usr/bin/env node +// Polyfill for Intl.Segmenter to avoid segfaults in pkg builds +// pkg uses small-icu which causes crashes when Intl.Segmenter.segment() is called +// See: https://github.com/yao-pkg/pkg-fetch/issues/134 + +// Use polyfill-force to always replace the native implementation +import "@formatjs/intl-segmenter/polyfill-force.js"; import * as console from "node:console"; import os from "node:os"; From 79de9963ee03285a419c9580833b4a766ba031ac Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Tue, 20 Jan 2026 20:48:20 -0800 Subject: [PATCH 46/48] style: run `biome check` --- src/scripts/release.ts | 9 +++++++-- tsup.config.ts | 1 - 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/scripts/release.ts b/src/scripts/release.ts index 4aca5f3d..c5b9f926 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -202,13 +202,18 @@ program "prerelease", ] as const), ) - .option("--no-commit", "Dry run: build only, skip version bump, git commit, and GitHub release") + .option( + "--no-commit", + "Dry run: build only, skip version bump, git commit, and GitHub release", + ) .action(async (type, options) => { try { const noCommit = !options.commit; if (!noCommit && !type) { - console.error("error: type argument is required when not using --no-commit"); + console.error( + "error: type argument is required when not using --no-commit", + ); process.exit(1); } diff --git a/tsup.config.ts b/tsup.config.ts index e0244e43..60042afa 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -20,4 +20,3 @@ export default defineConfig({ }; }, }); - From b6e81ea3950870dd0a6aa4b1ee9c63ba848ee6ae Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 21 Jan 2026 09:22:49 +0000 Subject: [PATCH 47/48] fix: remove reference to non-existent react-devtools-core stub Co-authored-by: danieltaox --- tsup.config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/tsup.config.ts b/tsup.config.ts index 60042afa..765fb302 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -15,8 +15,5 @@ export default defineConfig({ }, esbuildOptions(options) { options.bundle = true; - options.alias = { - "react-devtools-core": "./src/stubs/react-devtools-core.ts", - }; }, }); From 013440d4b27df2aef7c00aa0b958f7f351b5a4b2 Mon Sep 17 00:00:00 2001 From: Daniel Tao Date: Wed, 21 Jan 2026 12:43:19 -0800 Subject: [PATCH 48/48] fix: typo --- src/lib/orders/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/orders/index.tsx b/src/lib/orders/index.tsx index 9505d027..b00859c1 100644 --- a/src/lib/orders/index.tsx +++ b/src/lib/orders/index.tsx @@ -329,7 +329,7 @@ export async function submitOrderCancellationByIdAction(orderId: string) { method: "DELETE", body: JSON.stringify({}), headers: { - "Content-ype": "application/json", + "Content-Type": "application/json", Authorization: `Bearer ${await getAuthToken()}`, }, });