diff --git a/bun.lock b/bun.lock index fd69efe..4246f95 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "sf-cli", @@ -63,6 +62,9 @@ }, }, }, + "overrides": { + "is-fullwidth-code-point": "3.0.0", + }, "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=="], @@ -854,8 +856,6 @@ "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=="], @@ -904,8 +904,6 @@ "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=="], @@ -932,8 +930,6 @@ "cli-table3/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "cli-truncate/slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], - "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=="], diff --git a/package.json b/package.json index 753111d..1b065ed 100644 --- a/package.json +++ b/package.json @@ -75,5 +75,8 @@ "peerDependencies": { "typescript": "^5.6.2" }, - "version": "0.30.0" + "version": "0.30.0", + "overrides": { + "is-fullwidth-code-point": "3.0.0" + } } diff --git a/src/lib/images/upload.ts b/src/lib/images/upload.ts index 2a1c563..deb2a41 100644 --- a/src/lib/images/upload.ts +++ b/src/lib/images/upload.ts @@ -10,7 +10,7 @@ import chalk from "chalk"; import cliProgress from "cli-progress"; import cliSpinners from "cli-spinners"; import ora, { type Ora } from "ora"; -import { apiClient } from "../../apiClient.ts"; +import { getAuthToken, loadConfig } from "../../helpers/config.ts"; import { logAndQuit } from "../../helpers/errors.ts"; async function readChunk( @@ -75,23 +75,31 @@ const upload = new Command("upload") let progressBar: cliProgress.SingleBar | undefined; try { - const client = await apiClient(); + const config = await loadConfig(); + const token = await getAuthToken(); + const apiHeaders = { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }; preparingSpinner = ora(`Preparing upload for ${name}...`).start(); // Create image via v2 API - const startResponse = await client.POST("/v2/images", { - body: { name }, + const startResponse = await fetch(`${config.api_url}/v2/images`, { + method: "POST", + headers: apiHeaders, + body: JSON.stringify({ name }), }); - if (!startResponse.response.ok || !startResponse.data) { - const errorText = await startResponse.response.text().catch(() => ""); + if (!startResponse.ok) { throw new Error( - `Failed to start upload: ${startResponse.response.status} ${startResponse.response.statusText}${errorText ? ` - ${errorText}` : ""}`, + `Failed to start upload: ${startResponse.status} ${startResponse.statusText}`, ); } - const imageId = startResponse.data.id; + const startData: { object: "image"; id: string; upload_status: string } = + await startResponse.json(); + const imageId = startData.id; preparingSpinner.succeed( `Started upload for image ${chalk.cyan(name)} (${chalk.blackBright( @@ -231,16 +239,17 @@ const upload = new Command("upload") } // Get presigned URL via v2 API - const partResponse = await client.POST("/v2/images/{id}/parts", { - params: { path: { id: imageId } }, - body: { part_id: part }, - }); + const partResponse = await fetch( + `${config.api_url}/v2/images/${imageId}/parts`, + { + method: "POST", + headers: apiHeaders, + body: JSON.stringify({ part_id: part }), + }, + ); - if (!partResponse.response.ok || !partResponse.data) { - const status = partResponse.response.status; - const errorText = await partResponse.response - .text() - .catch(() => ""); + if (!partResponse.ok) { + const status = partResponse.status; if ( status >= 400 && @@ -250,18 +259,20 @@ const upload = new Command("upload") ) { bail( new Error( - `Failed to get upload URL for part ${part}: ${status} ${partResponse.response.statusText} - ${errorText}`, + `Failed to get upload URL for part ${part}: ${status} ${partResponse.statusText}`, ), ); return; } throw new Error( - `Failed to get upload URL for part ${part}: ${status} ${partResponse.response.statusText} - ${errorText}`, + `Failed to get upload URL for part ${part}: ${status} ${partResponse.statusText}`, ); } - const url = partResponse.data.upload_url; + const partData: { url: string; expires_at: string } = + await partResponse.json(); + const url = partData.url; // Read chunk from disk with progress tracking const payload = await readChunk( @@ -353,24 +364,31 @@ const upload = new Command("upload") const sha256Hash = hash.digest("hex"); // Complete upload via v2 API - const completeResponse = await client.POST("/v2/images/{id}/complete", { - params: { path: { id: imageId } }, - body: { sha256_hash: sha256Hash }, - }); + const completeResponse = await fetch( + `${config.api_url}/v2/images/${imageId}/complete`, + { + method: "POST", + headers: apiHeaders, + body: JSON.stringify({ sha256: sha256Hash }), + }, + ); - if (!completeResponse.response.ok || !completeResponse.data) { - const errorText = await completeResponse.response - .text() - .catch(() => ""); + if (!completeResponse.ok) { throw new Error( - `Failed to complete upload: ${completeResponse.response.status} ${completeResponse.response.statusText}${errorText ? ` - ${errorText}` : ""}`, + `Failed to complete upload: ${completeResponse.status} ${completeResponse.statusText}`, ); } + const completeData: { + object: "image"; + upload_status: string; + id: string; + } = await completeResponse.json(); + finalizingSpinner.succeed("Image uploaded and verified"); console.log(chalk.gray("\nNext steps:")); - console.log(` sf images get ${chalk.cyan(completeResponse.data.id)}`); + console.log(` sf images get ${chalk.cyan(completeData.id)}`); } catch (err) { if (spinnerTimer) { clearInterval(spinnerTimer);