@@ -10,8 +10,9 @@ import chalk from "chalk";
1010import cliProgress from "cli-progress" ;
1111import cliSpinners from "cli-spinners" ;
1212import ora , { type Ora } from "ora" ;
13- import { apiClient } from "../../../apiClient .ts" ;
13+ import { getAuthToken , loadConfig } from "../../../helpers/config .ts" ;
1414import { logAndQuit } from "../../../helpers/errors.ts" ;
15+ import { getDefaultWorkspace } from "../../images/utils.ts" ;
1516
1617async function readChunk (
1718 filePath : string ,
@@ -76,23 +77,34 @@ const upload = new Command("upload")
7677 let progressBar : cliProgress . SingleBar | undefined ;
7778
7879 try {
80+ const config = await loadConfig ( ) ;
81+ const token = await getAuthToken ( ) ;
82+ const apiHeaders = {
83+ Authorization : `Bearer ${ token } ` ,
84+ "Content-Type" : "application/json" ,
85+ } ;
86+
7987 preparingSpinner = ora ( `Preparing upload for ${ name } ...` ) . start ( ) ;
80- const client = await apiClient ( ) ;
88+
89+ const workspace = await getDefaultWorkspace ( ) ;
8190
8291 // Start upload
83- const startResponse = await client . POST ( "/v1/vms /images/start_upload" , {
84- body : {
85- name ,
86- } ,
92+ const startResponse = await fetch ( ` ${ config . api_url } /preview/v2 /images` , {
93+ method : "POST" ,
94+ headers : apiHeaders ,
95+ body : JSON . stringify ( { name , workspace } ) ,
8796 } ) ;
8897
89- if ( ! startResponse . data ) {
98+ if ( ! startResponse . ok ) {
9099 throw new Error (
91- `Failed to start upload: ${ startResponse . response . status } ${ startResponse . response . statusText } ` ,
100+ `Failed to start upload: ${ startResponse . status } ${ startResponse . statusText } ` ,
92101 ) ;
93102 }
94103
95- const imageId = startResponse . data . image_id ;
104+ const startData : { object : "image" ; id : string ; upload_status : string } =
105+ await startResponse . json ( ) ;
106+ const imageId = startData . id ;
107+
96108 preparingSpinner . succeed (
97109 `Started upload for image ${ chalk . cyan ( name ) } (${ chalk . blackBright (
98110 imageId ,
@@ -242,25 +254,18 @@ const upload = new Command("upload")
242254 }
243255
244256 // Fetch fresh upload URL for this attempt
245- const response = await client . POST (
246- "/v1/vms /images/{image_id}/upload" ,
257+ const partResponse = await fetch (
258+ ` ${ config . api_url } /preview/v2 /images/${ imageId } /parts` ,
247259 {
248- params : {
249- path : {
250- image_id : imageId ,
251- } ,
252- } ,
253- body : {
254- part_id : part ,
255- } ,
260+ method : "POST" ,
261+ headers : apiHeaders ,
262+ body : JSON . stringify ( { part_id : part } ) ,
256263 } ,
257264 ) ;
258265
259- if ( ! response . response . ok || ! response . data ) {
260- const status = response . response . status ;
261- const errorText = response . response . ok
262- ? "No data in response"
263- : await response . response . text ( ) . catch ( ( ) => "" ) ;
266+ if ( ! partResponse . ok ) {
267+ const status = partResponse . status ;
268+ const errorText = await partResponse . text ( ) . catch ( ( ) => "" ) ;
264269
265270 // Bail on non-transient 4xx errors (except 408 Request Timeout and 429 Too Many Requests)
266271 if (
@@ -271,18 +276,20 @@ const upload = new Command("upload")
271276 ) {
272277 bail (
273278 new Error (
274- `Failed to get upload URL for part ${ part } : ${ status } ${ response . response . statusText } - ${ errorText } ` ,
279+ `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . statusText } - ${ errorText } ` ,
275280 ) ,
276281 ) ;
277282 return ;
278283 }
279284
280285 throw new Error (
281- `Failed to get upload URL for part ${ part } : ${ status } ${ response . response . statusText } - ${ errorText } ` ,
286+ `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . statusText } - ${ errorText } ` ,
282287 ) ;
283288 }
284289
285- const url = response . data . upload_url ;
290+ const partData : { url : string ; expires_at : string } =
291+ await partResponse . json ( ) ;
292+ const url = partData . url ;
286293
287294 // Read chunk from disk with progress tracking
288295 const payload = await readChunk (
@@ -375,31 +382,33 @@ const upload = new Command("upload")
375382 }
376383
377384 const sha256Hash = hash . digest ( "hex" ) ;
378- const completeResponse = await client . PUT (
379- "/v1/vms/images/{image_id}/complete_upload" ,
385+
386+ // Complete upload via preview/v2 API
387+ const completeResponse = await fetch (
388+ `${ config . api_url } /preview/v2/images/${ imageId } /complete` ,
380389 {
381- params : {
382- path : {
383- image_id : imageId ,
384- } ,
385- } ,
386- body : {
387- sha256_hash : sha256Hash ,
388- } ,
390+ method : "POST" ,
391+ headers : apiHeaders ,
392+ body : JSON . stringify ( { sha256 : sha256Hash } ) ,
389393 } ,
390394 ) ;
391395
392- if ( ! completeResponse . data ) {
396+ if ( ! completeResponse . ok ) {
393397 throw new Error (
394- `Failed to complete upload: ${ completeResponse . response . status } ${ completeResponse . response . statusText } ` ,
398+ `Failed to complete upload: ${ completeResponse . status } ${ completeResponse . statusText } ` ,
395399 ) ;
396400 }
397401
402+ const completeData : {
403+ object : "image" ;
404+ upload_status : string ;
405+ id : string ;
406+ } = await completeResponse . json ( ) ;
407+
398408 finalizingSpinner . succeed ( "Image uploaded and verified" ) ;
399409
400- const object = completeResponse . data ;
401410 console . log ( chalk . gray ( "\nNext steps:" ) ) ;
402- console . log ( ` sf nodes images show ${ chalk . cyan ( object . id ) } ` ) ;
411+ console . log ( ` sf nodes images show ${ chalk . cyan ( completeData . id ) } ` ) ;
403412 } catch ( err ) {
404413 // Clean up spinner timer
405414 if ( spinnerTimer ) {
0 commit comments