diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71cf910e..729c1a30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,17 +7,14 @@ on: branches: [main] jobs: - # run format, lint, and test test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: denoland/setup-deno@v2 - 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 + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: biomejs/setup-biome@v2 + - run: bun install + - run: biome ci . + - run: bun run check + - run: bun run test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6d4d41ed..d653d623 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,22 +29,20 @@ jobs: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Deno - uses: denoland/setup-deno@v2 - with: - deno-version-file: .tool-versions + - name: Setup Bun + uses: oven-sh/setup-bun@v2 - - name: Check formatting - run: deno fmt --check + - name: Install dependencies + run: bun install - - name: Run linter - run: deno lint + - name: Lint + run: bun run lint - name: Type check - run: deno check --config deno.json ./src/index.ts + run: bun run check - name: Run tests - run: deno test --allow-all + run: bun test - name: Configure Git run: | @@ -55,4 +53,4 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - deno run --allow-all src/scripts/release.ts ${{ inputs.version_type }} + bun run src/scripts/release.ts ${{ inputs.version_type }} diff --git a/.tool-versions b/.tool-versions index 2e5aabff..a3128f26 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1 @@ -nodejs 20.19.0 -deno 2.4.2 \ No newline at end of file +nodejs 22.22.0 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..473826ab 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,18 +1,24 @@ { - "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": { + "source.fixAll.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 } diff --git a/README.md b/README.md index e40cfafc..b778a2ec 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/) (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` ### 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 diff --git a/biome.json b/biome.json new file mode 100644 index 00000000..27dc20cb --- /dev/null +++ b/biome.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "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/bun.lock b/bun.lock new file mode 100644 index 00000000..618e037f --- /dev/null +++ b/bun.lock @@ -0,0 +1,1022 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "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", + "@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": "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": "^5.0.1", + "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": "^2.3.11", + "@types/async-retry": "^1.4.9", + "@types/cli-progress": "^3.11.6", + "@types/node": "^22.0.0", + "@types/react": "^18.3.21", + "@types/semver": "^7.5.8", + "@yao-pkg/pkg": "^6.12.0", + "tsup": "^8.5.1", + "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@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@2.3.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-/uXXkBcPKVQY7rc9Ys2CrlirBJYbpESEDme7RKiBD6MmqR2w3j0+ZZXRIL2xiaNPsIMMNhP1YnA+jRRxoOAFrA=="], + + "@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@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@2.3.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-XPSQ+XIPZMLaZ6zveQdwNjbX+QdROEd1zPgMwD47zvHV+tCGB88VH+aynyGxAHdzL+Tm/+DtKST5SECs4iwCLg=="], + + "@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@2.3.11", "", { "os": "linux", "cpu": "x64" }, "sha512-vU7a8wLs5C9yJ4CB8a44r12aXYb8yYgBn+WeyzbMjaCMklzCv1oXr8x+VEyWodgJt9bDmhiaW/I0RHbn7rsNmw=="], + + "@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@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=="], + + "@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=="], + + "@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=="], + + "@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=="], + + "@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=="], + + "@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=="], + + "@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=="], + + "@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.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-dP67MA0cCMHFT2g5XyjtpVOtp7y4UyUxN3dhLdt11at5cPKnSm4lY+EhwNvDXIMzAMIo2KU+mc9wxaAQJTn7sQ=="], + + "@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.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-Ng95wtHVEulRwn7R0tMrlUuiLVL/HXA8Lt/MYVpy88+s5ikpntzZba1qEulTuPnPIZuOPcW9wNEiqvZxZmgmqQ=="], + + "@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.2", "", { "os": "linux", "cpu": "arm" }, "sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q=="], + + "@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.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA=="], + + "@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.2", "", { "os": "linux", "cpu": "none" }, "sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA=="], + + "@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.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ=="], + + "@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.2", "", { "os": "linux", "cpu": "none" }, "sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q=="], + + "@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.2", "", { "os": "linux", "cpu": "x64" }, "sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw=="], + + "@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.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw=="], + + "@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.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-sDpFbenhmWjNcEbBcoTV0PWvW5rPJFvu+P7XoTY0YLGRupgLbFY0XPfwIbJOObzO7QgkRDANh65RjhPmgSaAjQ=="], + + "@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.2", "", { "os": "win32", "cpu": "x64" }, "sha512-KvXsBvp13oZz9JGe5NYS7FNizLe99Ny+W8ETsuCyjXiKdiGrcz2/J/N8qxZ/RSwivqjQguug07NLHqrIHrqfYw=="], + + "@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=="], + + "@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@22.19.7", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw=="], + + "@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@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.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=="], + + "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@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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "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@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@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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], + + "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=="], + + "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@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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-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=="], + + "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@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-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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], + + "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=="], + + "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@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=="], + + "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=="], + + "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=="], + + "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=="], + + "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], + + "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-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=="], + + "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=="], + + "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=="], + + "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=="], + + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + + "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=="], + + "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=="], + + "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=="], + + "yargs-parser": ["yargs-parser@20.2.9", "", {}, "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w=="], + + "yn": ["yn@5.1.0", "", {}, "sha512-TfXLvT6eVsBNIm8rAXTwJYdQFtOXaHQ+rA7LU8HL8C/BFfaSfhvFE5T1rHAdBCbAj808HaqjXVkmo8jmeGOqhw=="], + + "yoga-wasm-web": ["yoga-wasm-web@0.3.3", "", {}, "sha512-N+d4UJSJbt/R3wqY7Coqs5pcV0aUj2j9IaQ3rNj9bVCLld8tTGKRa2USARjnvZJWVx1NDmQev8EknoczaOQDOA=="], + + "@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=="], + + "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=="], + + "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=="], + + "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=="], + + "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/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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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=="], + + "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/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" - ] - } - } -} diff --git a/install.sh b/install.sh index 942f3e7b..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='x86_64-unknown-linux-gnu' + target='node22-linux-x64' ;; aarch64) - target='aarch64-unknown-linux-gnu' + 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='x86_64-apple-darwin' + target='node22-macos-x64' ;; arm64) - target='aarch64-apple-darwin' + target='node22-macos-arm64' ;; *) echo "Unsupported macOS architecture: ${ARCH}" >&2 diff --git a/package.json b/package.json index 023e683c..7140656d 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,34 @@ { + "name": "sf-cli", + "type": "module", + "bin": "dist/index.cjs", + "pkg": { + "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", + "@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", "@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", @@ -22,12 +36,12 @@ "date-fns": "^4.1.0", "dayjs": "^1.11.19", "dotenv": "^16.4.5", - "ink": "^5.2.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", - "inquirer": "^10.1.2", + "ink-text-input": "^5.0.1", + "inquirer": "^13.2.0", "little-date": "^1.0.0", "ms": "^2.1.3", "node-fetch": "^3.3.2", @@ -42,17 +56,24 @@ "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": { - "@types/react": "^18.3.20", - "@types/semver": "^7.5.8" + "@biomejs/biome": "^2.3.11", + "@types/async-retry": "^1.4.9", + "@types/cli-progress": "^3.11.6", + "@types/node": "^22.0.0", + "@types/react": "^18.3.21", + "@types/semver": "^7.5.8", + "@yao-pkg/pkg": "^6.12.0", + "tsup": "^8.5.1", + "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" } 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/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/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/errors.ts b/src/helpers/errors.ts index d3663a52..c3d7107e 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 { @@ -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/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/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/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/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..591db31b 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,22 @@ 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 + // biome-ignore lint/suspicious/noExplicitAny: these are invalid inputs and will not typecheck 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 +88,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/helpers/units.ts b/src/helpers/units.ts index 62cf7ebc..9d366874 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"); } @@ -91,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 }; } @@ -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/index.ts b/src/index.ts index 5272c947..ebfce211 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,164 +1,164 @@ -#!/usr/bin/env bun +#!/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 { 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 { apiClient } from "./apiClient.ts"; import { checkVersion } from "./checkVersion.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"; -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 { 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(); +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}`, - }, - }); + }; - // 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; - saveConfig({ ...config, account_id: data.id }); - } - } + process.on("beforeExit", ensureAnalyticsShutdown); + process.on("SIGINT", async () => { + await ensureAnalyticsShutdown(); + process.exit(130); + }); + process.on("SIGTERM", async () => { + await ensureAnalyticsShutdown(); + process.exit(0); + }); - program.exitOverride((error) => { - let isError = true; + const config = await loadConfig(); + let exchangeAccountId = config.account_id; - switch (error.code) { - case "commander.helpDisplayed": - case "commander.help": - case "commander.version": - isError = false; - break; + if (!exchangeAccountId) { + 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 }); + } } - 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; - } + program.exitOverride((error) => { + let isError = 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] = + Number.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(); 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/Quote.tsx b/src/lib/Quote.tsx index 2e353a1c..517bd3c1 100644 --- a/src/lib/Quote.tsx +++ b/src/lib/Quote.tsx @@ -1,7 +1,6 @@ -import React from "react"; 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) { @@ -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/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..f82bdd29 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, render, Text, 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,14 +28,14 @@ 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"; -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"; +import type { Quote } from "../Quote.tsx"; +import QuoteDisplay from "../Quote.tsx"; +import { Row } from "../Row.tsx"; 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 }) { @@ -291,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, @@ -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(); @@ -486,7 +480,7 @@ function VMWarning(props: BuyOrderProps) { } equivalentCommand += ` -d ${realDurationString}`; if (props.yes) { - equivalentCommand += ` -y`; + equivalentCommand += " -y"; } if (props.cluster) { equivalentCommand += ` -z ${props.cluster}`; @@ -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,12 +990,16 @@ 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 } }, }); 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 48708e23..05e78b71 100644 --- a/src/lib/contracts/ContractDisplay.tsx +++ b/src/lib/contracts/ContractDisplay.tsx @@ -2,10 +2,9 @@ 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"; +import { Row } from "../Row.tsx"; import type { ActiveContract, Contract } from "./types.ts"; import { type ContractState, @@ -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/dev.ts b/src/lib/dev.ts index e8b84eda..6e098963 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); @@ -112,13 +112,11 @@ 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]} | ${ - 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..50e6f1fb 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 ${ @@ -86,26 +84,28 @@ ${ 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(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/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 746427c3..4f0fb751 100644 --- a/src/lib/login.ts +++ b/src/lib/login.ts @@ -1,20 +1,19 @@ -import type { Command } from "@commander-js/extra-typings"; 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 ora from "ora"; -import { saveConfig } from "../helpers/config.ts"; -import { clearScreen } from "../helpers/prompt.ts"; -import { getWebAppUrl } from "../helpers/urls.ts"; - +import type { Command } from "@commander-js/extra-typings"; // 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"; -import { randomInt } from "node:crypto"; export function registerLogin(program: Command) { program 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/nodes/create.ts b/src/lib/nodes/create.ts index c0f56184..7294dcd6 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,8 +58,8 @@ function formatNodeDescription( * @throws CommanderError if invalid */ function validateCount(val: string): number { - const parsed = parseInt(val, 10); - if (isNaN(parsed) || parsed <= 0) { + const parsed = Number.parseInt(val, 10); + if (Number.isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, "INVALID_COUNT", @@ -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,29 +402,29 @@ 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`; + 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..704bd1ad 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,29 +95,29 @@ 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( - `\nTo configure auto reserved nodes, use the \`sf nodes set\` command.`, + 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/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/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..40479cce 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, render, Text } 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/image/upload.ts b/src/lib/nodes/image/upload.ts index ac75574b..fba97497 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,8 +61,8 @@ const upload = new Command("upload") "-c, --concurrency ", "Number of parts to upload concurrently", (value) => { - const parsed = parseInt(value, 10); - if (isNaN(parsed) || parsed < 1) { + const parsed = Number.parseInt(value, 10); + if (Number.isNaN(parsed) || parsed < 1) { throw new Error("Concurrency must be a positive integer"); } return parsed; @@ -88,20 +94,22 @@ 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 +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); @@ -152,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, @@ -185,7 +194,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 +222,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 +264,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 +299,15 @@ const upload = new Command("upload") headers: { "Content-Type": "application/octet-stream", }, - body: payload, + body: payload 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 +339,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,19 +358,19 @@ 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), }); 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"); - 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); } @@ -382,11 +395,11 @@ const upload = new Command("upload") ); } - finalizingSpinner.succeed(`Image uploaded and verified`); + 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 +429,12 @@ 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/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/nodes/list.tsx b/src/lib/nodes/list.tsx index 5a28b8f3..caf5630d 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 utc from "dayjs/plugin/utc"; import { Box, render, Text } from "ink"; -import type { SFCNodes } from "@sfcompute/nodes-sdk-alpha"; -import { formatDuration, intervalToDuration } from "date-fns"; +import ora from "ora"; import { getAuthToken } from "../../helpers/config.ts"; import { logAndQuit } from "../../helpers/errors.ts"; @@ -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,66 +454,62 @@ 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" && ( - <> - - + )} {node.node_type !== "autoreserved" && ( <> @@ -513,9 +520,9 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { )} @@ -527,12 +534,14 @@ function NodeVerboseDisplay({ node }: { node: SFCNodes.Node }) { {nodeActions.length > 0 && ( <> - Actions: + + Actions: + - {nodeActions.map((action, index) => ( + {nodeActions.map((action) => ( @@ -568,11 +577,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 +590,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 +602,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 +635,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 +658,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..bff3ddb9 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))); } @@ -178,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)", ); } @@ -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..98ac9a14 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. @@ -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) @@ -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."); @@ -178,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({ @@ -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..0af8da46 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,17 +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); - case "unknown": + return chalk.gray(statusText); default: return statusText; } @@ -63,15 +55,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 +81,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 +105,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 +141,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 +155,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 +168,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,8 +179,8 @@ export function pluralizeNodes(count: number) { * @throws CommanderError if invalid */ export function validatePrice(val: string, minimum = 0): number { - const parsed = parseFloat(val); - if (isNaN(parsed) || parsed <= 0) { + const parsed = Number.parseFloat(val); + if (Number.isNaN(parsed) || parsed <= 0) { throw new CommanderError( 1, "INVALID_PRICE", @@ -209,8 +205,8 @@ 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); - if (isNaN(parsed)) { + const parsed = Number.parseInt(val, 10); + if (Number.isNaN(parsed)) { throw new CommanderError( 1, "INVALID_DURATION", @@ -221,9 +217,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; @@ -248,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; @@ -277,10 +273,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 +281,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 +291,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 +317,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/orders/OrderDisplay.tsx b/src/lib/orders/OrderDisplay.tsx index 5d75b3c1..71011cc8 100644 --- a/src/lib/orders/OrderDisplay.tsx +++ b/src/lib/orders/OrderDisplay.tsx @@ -1,17 +1,17 @@ -import { Box, measureElement, Text, useInput } from "ink"; import process from "node:process"; import dayjs from "dayjs"; +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"; 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 + + )} @@ -175,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 ( @@ -188,52 +195,36 @@ export function OrderDisplay(props: { ); } - 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, - }; - }, [props.orders]); + const orders = + activeTab === "all" + ? props.orders + : props.orders.filter((order) => order.side === activeTab); 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. + + + )} + ); } @@ -330,9 +321,7 @@ export function ScrollArea({ sellOrdersCount: number; buyOrdersCount: number; }) { - const [state, dispatch] = React.useReducer< - React.Reducer - >(reducer, { + const [state, dispatch] = React.useReducer(reducer, { height, scrollTop: 0, innerHeight: 0, @@ -341,23 +330,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(() => { @@ -373,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/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); }); diff --git a/src/lib/orders/index.tsx b/src/lib/orders/index.tsx index 3bdf53d7..b00859c1 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 { @@ -330,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()}`, }, }); @@ -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..addd8d4e 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,18 +39,11 @@ 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}`, - }, - }); - // 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) { + 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 }); + await saveConfig({ ...config, account_id: data.id }); } } @@ -69,10 +62,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/scale/ConfirmationMessage.tsx b/src/lib/scale/ConfirmationMessage.tsx index ac38d065..dd1b0cb2 100644 --- a/src/lib/scale/ConfirmationMessage.tsx +++ b/src/lib/scale/ConfirmationMessage.tsx @@ -1,15 +1,13 @@ -import React from "react"; 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, - Procurement, + type Procurement, } from "./utils.ts"; export default function ConfirmationMessage(props: { @@ -24,8 +22,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 +38,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 && ( - {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 +61,16 @@ export default function ProcurementDisplay( {typeLabel} - - ({instance_type}) - + ({instance_type}) + ) : ( + instance_type ) - : instance_type} + } /> & { 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), @@ -165,6 +161,8 @@ function CreateProcurementCommand(props: CreateProcurementCommandProps) { const [isQuoting, setIsQuoting] = useState(false); 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. useEffect(() => { (async function init() { try { @@ -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"), + "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"), ) .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.") @@ -464,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, @@ -483,10 +468,7 @@ $ sf scale create -n 8 --horizon '30m' } const { waitUntilExit } = render( - , + , ); await waitUntilExit(); }); diff --git a/src/lib/scale/index.tsx b/src/lib/scale/index.tsx index 5f95462c..b403c5f7 100644 --- a/src/lib/scale/index.tsx +++ b/src/lib/scale/index.tsx @@ -1,9 +1,9 @@ -import { Command } from "@commander-js/extra-typings"; +import type { Command } from "@commander-js/extra-typings"; import { isFeatureEnabled } from "../posthog.ts"; import create from "./create.tsx"; -import update from "./update.tsx"; import list from "./list.tsx"; +import update from "./update.tsx"; export async function registerScale(program: Command) { const isEnabled = await isFeatureEnabled("procurements"); diff --git a/src/lib/scale/list.tsx b/src/lib/scale/list.tsx index c0c3a118..df27bf3c 100644 --- a/src/lib/scale/list.tsx +++ b/src/lib/scale/list.tsx @@ -1,28 +1,29 @@ -import React, { useEffect, useState } from "react"; +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 { Command } from "@commander-js/extra-typings"; -import { setTimeout } from "node:timers"; +import { useEffect, useState } from "react"; import { apiClient } from "../../apiClient.ts"; -import { getProcurement, parseIds, type Procurement } from "./utils.ts"; import ProcurementDisplay from "./ProcurementDisplay.tsx"; +import { getProcurement, type Procurement, parseIds } from "./utils.ts"; async function listProcurements() { const client = await apiClient(); const procurements: Procurement[] = []; let hasMore = true; while (hasMore) { - const { response, data: listObject, error } = await client.GET( - "/v0/procurements", - { - query: { - limit: 100, - offset: procurements.length, - }, + const { + response, + data: listObject, + error, + } = await client.GET("/v0/procurements", { + query: { + limit: 100, + offset: procurements.length, }, - ); + }); if (!response.ok) { throw new Error(error?.message || "Failed to list procurements"); @@ -45,6 +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. useEffect(() => { async function fetchInfo() { try { @@ -59,20 +61,21 @@ 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 { failed.push({ id: ids[idx], - message: result.status === "rejected" - ? (result.reason instanceof Error - ? result.reason.message - : String(result.reason)) - : "Unknown error", + message: + result.status === "rejected" + ? result.reason instanceof Error + ? result.reason.message + : String(result.reason) + : "Unknown error", }); } - }); + } setFailedFetches(failed); } else { @@ -95,7 +98,7 @@ function ProcurementsList(props: { type?: string; ids?: string[] }) { } } 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 f3c5a70a..311bd730 100644 --- a/src/lib/scale/update.tsx +++ b/src/lib/scale/update.tsx @@ -1,33 +1,32 @@ -import React, { +import console from "node:console"; +import { setTimeout } from "node:timers"; +import { Command } from "@commander-js/extra-typings"; +import chalk from "chalk"; +import { Box, render, Text, 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 { 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 { acceleratorsToNodes, getProcurement, + type Procurement, 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, @@ -62,46 +61,57 @@ 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< - 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 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"); - if (failures.length > 0) { - setError(`Failed to update ${failures.length} procurement(s)`); + const failures = settledResults.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 +127,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,37 +144,41 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { props.accelerators !== undefined ? acceleratorsToNodes(props.accelerators) : undefined, - [ - props.accelerators, - ], + [props.accelerators], ); 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. useEffect(() => { - (async function init() { + (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 + .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 +212,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 +265,30 @@ 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, + exit, + updateProcurements, + ], + ); if (error && !results) { return Error: {error}; @@ -282,16 +304,27 @@ 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 UpdateResult & { + result: PromiseFulfilledResult; + } => r.result.status === "fulfilled" && r.result.value != null, + ) + .map((r) => r.result.value); + const failedProcurements = results + .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 && ( @@ -304,17 +337,16 @@ function UpdateProcurementCommand(props: UpdateProcurementCommandProps) { Failed to update {failedProcurements.length} procurement(s): - {failedProcurements.map((f, i) => ( - - - {f} + {failedProcurements.map((f) => ( + + - {f.id}: {f.error} ))} )} - {successfulProcurements && - successfulProcurements.map((s, i) => ( - - ))} + {successfulProcurements?.map((s) => ( + + ))} ); } @@ -382,7 +414,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 +422,7 @@ $ sf scale update -p 1.50 return; } const { waitUntilExit } = render( - , + , ); await waitUntilExit(); }); diff --git a/src/lib/scale/utils.ts b/src/lib/scale/utils.ts index b71c88b8..9b5170de 100644 --- a/src/lib/scale/utils.ts +++ b/src/lib/scale/utils.ts @@ -1,15 +1,13 @@ import parseDuration from "parse-duration"; +import { apiClient } from "../../apiClient.ts"; import { logAndQuit } from "../../helpers/errors.ts"; import { dollarsToCents } from "../../helpers/units.ts"; -import { apiClient } from "../../apiClient.ts"; import type { paths } from "../../schema.ts"; import { GPUS_PER_NODE } from "../constants.ts"; export type Procurement = - paths["/v0/procurements"]["get"]["responses"]["200"]["content"][ - "application/json" - ]["data"][number]; + paths["/v0/procurements"]["get"]["responses"]["200"]["content"]["application/json"]["data"][number]; export type ColocationStrategyName = Procurement["colocation_strategy"]["type"]; export const DEFAULT_PRICE_PER_GPU_HOUR_IN_CENTS = 265 as const; // Example default price @@ -42,13 +40,13 @@ 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); } 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.`); } @@ -60,20 +58,14 @@ 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 } }, }); if (!res.response.ok) { - throw new Error( - res.error?.message || "Failed to get procurement", - ); + throw new Error(res.error?.message || "Failed to get procurement"); } return res.data ?? null; diff --git a/src/lib/sell.ts b/src/lib/sell.ts index dc4b1985..3de430f0 100644 --- a/src/lib/sell.ts +++ b/src/lib/sell.ts @@ -62,8 +62,7 @@ export function registerSell(program: Command) { } if (options.accelerators % GPUS_PER_NODE !== 0) { - const exampleCommand = - `sf sell -n ${GPUS_PER_NODE} -c ${options.contractId}`; + const exampleCommand = `sf sell -n ${GPUS_PER_NODE} -c ${options.contractId}`; return logAndQuit( `At the moment, only entire-nodes are available, so you must have a multiple of ${GPUS_PER_NODE} GPUs. Example command:\n\n${exampleCommand}`, ); @@ -119,9 +118,8 @@ export function registerSell(program: Command) { quantity: forceAsNumber(options.accelerators) / GPUS_PER_NODE, price: totalPrice, contract_id: options.contractId, - start_at: roundedStartDate === "NOW" - ? "NOW" - : roundedStartDate.toISOString(), + start_at: + roundedStartDate === "NOW" ? "NOW" : roundedStartDate.toISOString(), end_at: endDate.toISOString(), }; @@ -134,13 +132,11 @@ export function registerSell(program: Command) { switch (response.status) { case 400: return logAndQuit( - `Bad Request: ${error?.message}: ${ - JSON.stringify( - error?.details, - null, - 2, - ) - }`, + `Bad Request: ${error?.message}: ${JSON.stringify( + error?.details, + null, + 2, + )}`, ); // return logAndQuit(`Bad Request: ${error?.message}`); case 401: diff --git a/src/lib/sell/index.tsx b/src/lib/sell/index.tsx index 5660a891..815b5baf 100644 --- a/src/lib/sell/index.tsx +++ b/src/lib/sell/index.tsx @@ -1,29 +1,27 @@ -import type { Command } from "@commander-js/extra-typings"; import { clearInterval, setInterval, setTimeout } from "node:timers"; +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, 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 invariant from "tiny-invariant"; import { apiClient } from "../../apiClient.ts"; -import { components } from "../../schema.ts"; +import { isLoggedIn } from "../../helpers/config.ts"; import { logAndQuit, logLoginMessageAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; -import parseDurationFromLibrary from "parse-duration"; -import { Box, render, useApp } from "ink"; +import { getContract } from "../../helpers/fetchers.ts"; import { parseStartDate } from "../../helpers/units.ts"; -import { GPUS_PER_NODE } from "../constants.ts"; -import { useCallback, useEffect, useState } from "react"; -import { Text } from "ink"; +import type { components } from "../../schema.ts"; import ConfirmInput from "../ConfirmInput.tsx"; -import React from "react"; +import { GPUS_PER_NODE } from "../constants.ts"; import { Row } from "../Row.tsx"; -import ms from "ms"; -import Spinner from "ink-spinner"; -import invariant from "tiny-invariant"; -import { getContract } from "../../helpers/fetchers.ts"; -import { isLoggedIn } from "../../helpers/config.ts"; type SellOrderFlags = components["schemas"]["market-api_OrderFlags"]; @@ -65,7 +63,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}`, ); @@ -113,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) { @@ -168,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) { @@ -184,7 +183,7 @@ function SellOrder(props: { async function submitOrder() { setIsLoading(true); // Place the sell order - const order = await placeSellOrder({ + const placedOrder = await placeSellOrder({ price: props.price, contractId: props.contractId, quantity: props.size, @@ -192,15 +191,17 @@ function SellOrder(props: { endsAt: props.endsAt, flags: props.flags, }); - setOrder(order); + 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]); + // 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) { @@ -213,7 +214,7 @@ function SellOrder(props: { return; } - const o = await getOrder(order!.id); + const o = await getOrder(order?.id); setOrder(o); if (o) { setTimeout(exit, 0); @@ -226,7 +227,7 @@ function SellOrder(props: { clearInterval(interval); } }; - }, [isLoading, exit, order]); + }, [isLoading]); return ( @@ -236,10 +237,7 @@ function SellOrder(props: { Place order? (y/n) - + )} @@ -287,21 +285,19 @@ 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 const startFromNow = startDate.fromNow(); const endDate = roundEndDate(props.endsAt); const end = endDate.format("MMM D h:mm a").toLowerCase(); - // @ts-ignore fromNow not typed const endFromNow = endDate.fromNow(); const realDuration = endDate.diff(startDate); 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( @@ -362,7 +359,7 @@ export async function placeSellOrder(options: { realDurationHours, ); invariant( - totalPrice == Math.ceil(totalPrice), + totalPrice === Math.ceil(totalPrice), "totalPrice must be a whole number", ); @@ -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,12 +402,16 @@ 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 } }, }); 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/tokens.ts b/src/lib/tokens.ts index b0b4e886..cbfd3d7c 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()); @@ -260,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(); @@ -274,27 +258,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 +300,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 +314,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/upgrade.ts b/src/lib/upgrade.ts index 0760b26e..9b4fade0 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) => { + stdout += data.toString(); + }); - const { code, stdout, stderr } = await bashProcess.output(); + bashProcess.stderr.on("data", (data) => { + 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); } diff --git a/src/lib/vm/index.ts b/src/lib/vm/index.ts index e8ded6b6..60b69e66 100644 --- a/src/lib/vm/index.ts +++ b/src/lib/vm/index.ts @@ -1,39 +1,42 @@ -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(); 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 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/vm/logs.ts b/src/lib/vm/logs.ts index d0063fcc..96f90b88 100644 --- a/src/lib/vm/logs.ts +++ b/src/lib/vm/logs.ts @@ -1,6 +1,6 @@ -import { Command, CommanderError } from "@commander-js/extra-typings"; import console from "node:console"; import { setTimeout } from "node:timers"; +import { Command, CommanderError } from "@commander-js/extra-typings"; import dayjs from "dayjs"; import utc from "dayjs/plugin/utc"; @@ -10,15 +10,13 @@ import { logAndQuit, logSessionTokenExpiredAndQuit, } from "../../helpers/errors.ts"; -import { paths } from "../../schema.ts"; +import type { paths } from "../../schema.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); @@ -41,7 +39,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( 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(); 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..ed489e1c 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 @@ -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 { @@ -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; @@ -104,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({ @@ -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", diff --git a/src/lib/zones.tsx b/src/lib/zones.tsx index cd09b616..bbaf3b47 100644 --- a/src/lib/zones.tsx +++ b/src/lib/zones.tsx @@ -1,26 +1,25 @@ +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 { 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"; 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(); @@ -149,12 +148,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: [], @@ -164,9 +163,10 @@ function displayZonesTable(zones: ZoneInfo[]) { sortedZones.forEach((zone) => { const available = getCurrentAvailableCapacity(zone); - const availableNodesText = available > 0 - ? green(available.toString()) - : red(available.toString()); + const availableNodesText = + available > 0 + ? chalk.green(available.toString()) + : chalk.red(available.toString()); table.push([ zone.name, @@ -178,17 +178,19 @@ 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()); 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(chalk.gray("Examples:")); + console.log(` sf buy --zone ${chalk.green(availableZoneName)}`); + console.log( + ` sf scale create -n 16 --zone ${chalk.green(availableZoneName)}`, ); - console.log(gray("Examples:")); - console.log(` sf buy --zone ${green(availableZoneName)}`); - console.log(` sf scale create -n 16 --zone ${green(availableZoneName)}`); } function EmptyZonesDisplay() { 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"]; + }; + }; }; + }; } diff --git a/src/scripts/release.ts b/src/scripts/release.ts index ff3346ce..c5b9f926 100644 --- a/src/scripts/release.ts +++ b/src/scripts/release.ts @@ -1,6 +1,9 @@ -import { Argument, Command } from "@commander-js/extra-typings"; +#!/usr/bin/env node +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 +20,8 @@ function bumpVersion( Number.parseInt( // Remove everything after the - if there is one v.includes("-") ? v.split("-")[0] : v, - ) + 10, + ), ); switch (type) { case "major": @@ -33,56 +37,72 @@ function bumpVersion( } } -async function getLocalVersion() { - const packageJson = await Deno.readTextFile("package.json"); +function getLocalVersion() { + 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"); +function saveVersion(version: string) { + 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", + "node22-linux-x64", + "node22-linux-arm64", + "node22-macos-x64", + "node22-macos-arm64", ]; async function compileDistribution() { + // 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 = await new Deno.Command("deno", { - args: [ - "compile", - "-A", + const result = spawnSync( + "npx", + [ + "@yao-pkg/pkg", + "dist/index.cjs", "--target", target, "--output", `dist/sf-${target}`, - "./src/index.ts", ], - }).output(); + { stdio: "inherit" }, + ); - if (!result.success) { - console.error(new TextDecoder().decode(result.stderr)); + if (result.status !== 0) { + console.error(result.stderr?.toString() ?? ""); logAndError(`Failed to compile for ${target}`); } 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(); + const zipResult = spawnSync("zip", [ + "-j", + zipFileName, + `dist/sf-${target}`, + ]); - if (!zipResult.success) { - console.error(zipResult.stderr); + if (zipResult.status !== 0) { + console.error(zipResult.stderr?.toString() ?? ""); logAndError(`Failed to zip the binary for ${target}`); } console.log(`✅ Zipped binary for ${target}`); @@ -91,19 +111,17 @@ async function compileDistribution() { 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 +129,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}`); @@ -169,14 +185,8 @@ async function createRelease(version: string) { console.log("✅ Pushed to origin main"); } -async function cleanDist() { - try { - await Deno.remove("./dist", { recursive: true }); - } catch (error) { - if (!(error instanceof Deno.errors.NotFound)) { - throw error; - } - } +function cleanDist() { + fs.rmSync("./dist", { recursive: true, force: true }); } program @@ -185,30 +195,41 @@ 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) => { + .option( + "--no-commit", + "Dry run: build only, skip version bump, git commit, and GitHub release", + ) + .action(async (type, options) => { try { - const ghCheckResult = await new Deno.Command("which", { - args: ["gh"], - }).output(); + const noCommit = !options.commit; - if (!ghCheckResult.success) { + if (!noCommit && !type) { console.error( - `The 'gh' command is not installed. Please install it. + "error: type argument is required when not using --no-commit", + ); + process.exit(1); + } + + 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", () => { @@ -223,10 +244,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/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/tsup.config.ts b/tsup.config.ts new file mode 100644 index 00000000..765fb302 --- /dev/null +++ b/tsup.config.ts @@ -0,0 +1,19 @@ +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; + }, +}); 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", + }, + }, +});