1- pub mod session;
1+ // pub mod session;
22pub mod token;
33
4- use node_bridge:: bindings:: AbortSignal ;
4+ use std:: future:: IntoFuture ;
5+
6+ use base64:: Engine ;
7+ use futures:: {
8+ future:: { select, Either } ,
9+ StreamExt ,
10+ } ;
11+ use gloo:: timers:: future:: IntervalStream ;
12+ use node_bridge:: { bindings:: AbortSignal , futures:: Defer , http_client:: HttpMethod , prelude:: * } ;
513use rand:: RngCore ;
14+ use sha2:: Digest ;
15+ use uuid:: Uuid ;
616use wasm_bindgen:: prelude:: * ;
17+ use wasm_bindgen_futures:: future_to_promise;
18+
19+ use crate :: {
20+ bindings:: { progress_location:: ProgressLocation , progress_options:: ProgressOptions } ,
21+ context:: get_extension_context,
22+ request:: make_request,
23+ } ;
724
8- use self :: session :: Session ;
25+ use self :: token :: Token ;
926
1027fn random_bytes ( ) -> Vec < u8 > {
1128 let mut rng = rand:: thread_rng ( ) ;
@@ -14,11 +31,128 @@ fn random_bytes() -> Vec<u8> {
1431 bytes
1532}
1633
17- #[ wasm_bindgen]
18- pub fn make_session ( abort_signal : AbortSignal ) -> Session {
19- Session :: new ( abort_signal)
34+ fn base64_encode < T > ( bytes : T ) -> String
35+ where
36+ T : AsRef < [ u8 ] > ,
37+ {
38+ base64:: engine:: general_purpose:: STANDARD
39+ . encode ( bytes)
40+ . replace ( "+" , "-" )
41+ . replace ( "/" , "_" )
42+ . replace ( "=" , "" )
43+ }
44+
45+ fn sha256 < T > ( data : T ) -> Vec < u8 >
46+ where
47+ T : AsRef < [ u8 ] > ,
48+ {
49+ let mut hasher = sha2:: Sha256 :: new ( ) ;
50+ hasher. update ( data) ;
51+ hasher. finalize ( ) . to_vec ( )
2052}
2153
54+ #[ wasm_bindgen( js_name = signIn) ]
55+ pub async fn sign_in ( ) {
56+ let uuid = Uuid :: new_v4 ( ) . to_string ( ) ;
57+ let verifier = base64_encode ( random_bytes ( ) ) ;
58+ let challenge = base64_encode ( sha256 ( verifier. clone ( ) ) ) ;
59+
60+ let login_url = format ! (
61+ "https://cursor.so/loginDeepControl?challenge={challenge}&uuid={}" ,
62+ uuid. clone( )
63+ ) ;
64+
65+ let context = get_extension_context ( ) ;
66+ // The API of VSCode does not allow us to obtain the execution result of the 'vscode.open' command,
67+ // so we cannot determine whether the user has confirmed to open url.
68+ context
69+ . execute_command1 ( "vscode.open" , JsValue :: from_str ( & login_url) )
70+ . await ;
71+
72+ context
73+ . with_progress (
74+ ProgressOptions {
75+ location : ProgressLocation :: Notification ,
76+ title : Some ( "Signing in..." . to_owned ( ) ) ,
77+ cancellable : true ,
78+ } ,
79+ closure ! ( |abort_signal: AbortSignal | {
80+ let uuid = uuid. clone( ) ;
81+ let verifier = verifier. clone( ) ;
82+ future_to_promise( async move {
83+ polling( & uuid, & verifier, abort_signal)
84+ . await
85+ . map( Into :: into)
86+ } )
87+ } )
88+ . into_js_value ( )
89+ . into ( ) ,
90+ )
91+ . await ;
92+ }
93+
94+ async fn polling (
95+ uuid : & str ,
96+ verifier : & str ,
97+ abort_signal : AbortSignal ,
98+ ) -> Result < Option < String > , JsValue > {
99+ let defer_abort = Defer :: new ( ) ;
100+ let defer_abort_clone = defer_abort. clone ( ) ;
101+ abort_signal. add_event_listener (
102+ "abort" ,
103+ closure_once ! ( || {
104+ defer_abort_clone. resolve( JsValue :: null( ) ) ;
105+ } )
106+ . into_js_value ( ) ,
107+ ) ;
108+
109+ let mut interval = IntervalStream :: new ( 2000 ) ;
110+ loop {
111+ let defer_abort_future = defer_abort. clone ( ) . into_future ( ) ;
112+ match select ( defer_abort_future, interval. next ( ) ) . await {
113+ Either :: Left ( _) => {
114+ return Ok ( None ) ;
115+ }
116+ _ => { }
117+ }
118+ let mut response = make_request (
119+ & format ! ( "/auth/poll?uuid={}&verifier={}" , uuid, verifier) ,
120+ HttpMethod :: Get ,
121+ )
122+ . send ( )
123+ . await ?;
124+
125+ if let Some ( chunk) = response. body ( ) . next ( ) . await {
126+ let data = chunk. to_string ( "utf-8" ) ;
127+ #[ cfg( debug_assertions) ]
128+ console:: log_str ( & data) ;
129+ match serde_json:: from_str :: < serde_json:: Value > ( & data) . and_then ( |value| {
130+ if value. is_null ( ) {
131+ Ok ( false )
132+ } else {
133+ serde_json:: from_str :: < Token > ( & data) . map ( |_| true )
134+ }
135+ } ) {
136+ Ok ( flag) => {
137+ if !flag {
138+ continue ;
139+ }
140+ return Ok ( Some ( data) ) ;
141+ }
142+ Err ( err) => {
143+ let js_error = JsError :: new ( & err. to_string ( ) ) ;
144+ let error = js_error. into ( ) ;
145+ #[ cfg( debug_assertions) ]
146+ console:: error1 ( & error) ;
147+ return Err ( error) ;
148+ }
149+ }
150+ }
151+ }
152+ }
153+
154+ pub fn sign_out ( ) { }
155+
22156#[ cfg( test) ]
23157mod test {
24158 #[ test]
0 commit comments