@@ -10,9 +10,40 @@ 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" ;
1515
16+ // V2 API types (hardcoded to avoid schema.ts coupling)
17+
18+ type V2CreateImageRequest = {
19+ name : string ;
20+ } ;
21+
22+ type V2CreateImageResponse = {
23+ object : "image" ;
24+ id : string ;
25+ upload_status : string ;
26+ } ;
27+
28+ type V2UploadPartRequest = {
29+ part_id : number ;
30+ } ;
31+
32+ type V2UploadPartResponse = {
33+ url : string ;
34+ expires_at : string ;
35+ } ;
36+
37+ type V2CompleteUploadRequest = {
38+ sha256 : string ;
39+ } ;
40+
41+ type V2CompleteUploadResponse = {
42+ object : "image" ;
43+ upload_status : string ;
44+ id : string ;
45+ } ;
46+
1647async function readChunk (
1748 filePath : string ,
1849 start : number ,
@@ -75,23 +106,30 @@ const upload = new Command("upload")
75106 let progressBar : cliProgress . SingleBar | undefined ;
76107
77108 try {
78- const client = await apiClient ( ) ;
109+ const config = await loadConfig ( ) ;
110+ const token = await getAuthToken ( ) ;
111+ const apiHeaders = {
112+ Authorization : `Bearer ${ token } ` ,
113+ "Content-Type" : "application/json" ,
114+ } ;
79115
80116 preparingSpinner = ora ( `Preparing upload for ${ name } ...` ) . start ( ) ;
81117
82118 // Create image via v2 API
83- const startResponse = await client . POST ( "/v2/images" , {
84- body : { name } ,
119+ const startResponse = await fetch ( `${ config . api_url } /v2/images` , {
120+ method : "POST" ,
121+ headers : apiHeaders ,
122+ body : JSON . stringify ( { name } satisfies V2CreateImageRequest ) ,
85123 } ) ;
86124
87- if ( ! startResponse . response . ok || ! startResponse . data ) {
88- const errorText = await startResponse . response . text ( ) . catch ( ( ) => "" ) ;
125+ if ( ! startResponse . ok ) {
89126 throw new Error (
90- `Failed to start upload: ${ startResponse . response . status } ${ startResponse . response . statusText } ${ errorText ? ` - ${ errorText } ` : "" } ` ,
127+ `Failed to start upload: ${ startResponse . status } ${ startResponse . statusText } ` ,
91128 ) ;
92129 }
93130
94- const imageId = startResponse . data . id ;
131+ const startData = ( await startResponse . json ( ) ) as V2CreateImageResponse ;
132+ const imageId = startData . id ;
95133
96134 preparingSpinner . succeed (
97135 `Started upload for image ${ chalk . cyan ( name ) } (${ chalk . blackBright (
@@ -231,16 +269,19 @@ const upload = new Command("upload")
231269 }
232270
233271 // Get presigned URL via v2 API
234- const partResponse = await client . POST ( "/v2/images/{id}/parts" , {
235- params : { path : { id : imageId } } ,
236- body : { part_id : part } ,
237- } ) ;
272+ const partResponse = await fetch (
273+ `${ config . api_url } /v2/images/${ imageId } /parts` ,
274+ {
275+ method : "POST" ,
276+ headers : apiHeaders ,
277+ body : JSON . stringify ( {
278+ part_id : part ,
279+ } satisfies V2UploadPartRequest ) ,
280+ } ,
281+ ) ;
238282
239- if ( ! partResponse . response . ok || ! partResponse . data ) {
240- const status = partResponse . response . status ;
241- const errorText = await partResponse . response
242- . text ( )
243- . catch ( ( ) => "" ) ;
283+ if ( ! partResponse . ok ) {
284+ const status = partResponse . status ;
244285
245286 if (
246287 status >= 400 &&
@@ -250,18 +291,20 @@ const upload = new Command("upload")
250291 ) {
251292 bail (
252293 new Error (
253- `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . response . statusText } - ${ errorText } ` ,
294+ `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . statusText } ` ,
254295 ) ,
255296 ) ;
256297 return ;
257298 }
258299
259300 throw new Error (
260- `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . response . statusText } - ${ errorText } ` ,
301+ `Failed to get upload URL for part ${ part } : ${ status } ${ partResponse . statusText } ` ,
261302 ) ;
262303 }
263304
264- const url = partResponse . data . url ;
305+ const partData =
306+ ( await partResponse . json ( ) ) as V2UploadPartResponse ;
307+ const url = partData . url ;
265308
266309 // Read chunk from disk with progress tracking
267310 const payload = await readChunk (
@@ -353,21 +396,30 @@ const upload = new Command("upload")
353396 const sha256Hash = hash . digest ( "hex" ) ;
354397
355398 // Complete upload via v2 API
356- const completeResponse = await client . POST ( "/v2/images/{id}/complete" , {
357- params : { path : { id : imageId } } ,
358- body : { sha256 : sha256Hash } ,
359- } ) ;
399+ const completeResponse = await fetch (
400+ `${ config . api_url } /v2/images/${ imageId } /complete` ,
401+ {
402+ method : "POST" ,
403+ headers : apiHeaders ,
404+ body : JSON . stringify ( {
405+ sha256 : sha256Hash ,
406+ } satisfies V2CompleteUploadRequest ) ,
407+ } ,
408+ ) ;
360409
361- if ( ! completeResponse . response . ok || ! completeResponse . data ) {
410+ if ( ! completeResponse . ok ) {
362411 throw new Error (
363- `Failed to complete upload: ${ completeResponse . response . status } ${ completeResponse . response . statusText } ${ completeResponse . error ? ` - ${ JSON . stringify ( completeResponse . error ) } ` : "" } ` ,
412+ `Failed to complete upload: ${ completeResponse . status } ${ completeResponse . statusText } ` ,
364413 ) ;
365414 }
366415
416+ const completeData =
417+ ( await completeResponse . json ( ) ) as V2CompleteUploadResponse ;
418+
367419 finalizingSpinner . succeed ( "Image uploaded and verified" ) ;
368420
369421 console . log ( chalk . gray ( "\nNext steps:" ) ) ;
370- console . log ( ` sf images get ${ chalk . cyan ( completeResponse . data . id ) } ` ) ;
422+ console . log ( ` sf images get ${ chalk . cyan ( completeData . id ) } ` ) ;
371423 } catch ( err ) {
372424 if ( spinnerTimer ) {
373425 clearInterval ( spinnerTimer ) ;
0 commit comments