@@ -18,10 +18,10 @@ export type AuthCoordinator = {
18
18
export async function isPidRunning ( pid : number ) : Promise < boolean > {
19
19
try {
20
20
process . kill ( pid , 0 ) // Doesn't kill the process, just checks if it exists
21
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Process ${ pid } is running` )
21
+ if ( DEBUG ) debugLog ( `Process ${ pid } is running` )
22
22
return true
23
23
} catch ( err ) {
24
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Process ${ pid } is not running` , err )
24
+ if ( DEBUG ) debugLog ( `Process ${ pid } is not running` , err )
25
25
return false
26
26
}
27
27
}
@@ -32,14 +32,14 @@ export async function isPidRunning(pid: number): Promise<boolean> {
32
32
* @returns True if the lockfile is valid, false otherwise
33
33
*/
34
34
export async function isLockValid ( lockData : LockfileData ) : Promise < boolean > {
35
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , 'Checking if lockfile is valid' , lockData )
35
+ if ( DEBUG ) debugLog ( 'Checking if lockfile is valid' , lockData )
36
36
37
37
// Check if the lockfile is too old (over 30 minutes)
38
38
const MAX_LOCK_AGE = 30 * 60 * 1000 // 30 minutes
39
39
if ( Date . now ( ) - lockData . timestamp > MAX_LOCK_AGE ) {
40
40
log ( 'Lockfile is too old' )
41
41
if ( DEBUG )
42
- await debugLog ( global . currentServerUrlHash ! , 'Lockfile is too old' , {
42
+ debugLog ( 'Lockfile is too old' , {
43
43
age : Date . now ( ) - lockData . timestamp ,
44
44
maxAge : MAX_LOCK_AGE ,
45
45
} )
@@ -49,13 +49,13 @@ export async function isLockValid(lockData: LockfileData): Promise<boolean> {
49
49
// Check if the process is still running
50
50
if ( ! ( await isPidRunning ( lockData . pid ) ) ) {
51
51
log ( 'Process from lockfile is not running' )
52
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , 'Process from lockfile is not running' , { pid : lockData . pid } )
52
+ if ( DEBUG ) debugLog ( 'Process from lockfile is not running' , { pid : lockData . pid } )
53
53
return false
54
54
}
55
55
56
56
// Check if the endpoint is accessible
57
57
try {
58
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , 'Checking if endpoint is accessible' , { port : lockData . port } )
58
+ if ( DEBUG ) debugLog ( 'Checking if endpoint is accessible' , { port : lockData . port } )
59
59
60
60
const controller = new AbortController ( )
61
61
const timeout = setTimeout ( ( ) => controller . abort ( ) , 1000 )
@@ -67,12 +67,11 @@ export async function isLockValid(lockData: LockfileData): Promise<boolean> {
67
67
clearTimeout ( timeout )
68
68
69
69
const isValid = response . status === 200 || response . status === 202
70
- if ( DEBUG )
71
- await debugLog ( global . currentServerUrlHash ! , `Endpoint check result: ${ isValid ? 'valid' : 'invalid' } ` , { status : response . status } )
70
+ if ( DEBUG ) debugLog ( `Endpoint check result: ${ isValid ? 'valid' : 'invalid' } ` , { status : response . status } )
72
71
return isValid
73
72
} catch ( error ) {
74
73
log ( `Error connecting to auth server: ${ ( error as Error ) . message } ` )
75
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , 'Error connecting to auth server' , error )
74
+ if ( DEBUG ) debugLog ( 'Error connecting to auth server' , error )
76
75
return false
77
76
}
78
77
}
@@ -84,44 +83,41 @@ export async function isLockValid(lockData: LockfileData): Promise<boolean> {
84
83
*/
85
84
export async function waitForAuthentication ( port : number ) : Promise < boolean > {
86
85
log ( `Waiting for authentication from the server on port ${ port } ...` )
87
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Waiting for authentication from server on port ${ port } ` )
88
86
89
87
try {
90
88
let attempts = 0
91
89
while ( true ) {
92
90
attempts ++
93
91
const url = `http://127.0.0.1:${ port } /wait-for-auth`
94
92
log ( `Querying: ${ url } ` )
95
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Poll attempt ${ attempts } : ${ url } ` )
93
+ if ( DEBUG ) debugLog ( `Poll attempt ${ attempts } ` )
96
94
97
95
try {
98
96
const response = await fetch ( url )
99
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Poll response status: ${ response . status } ` )
97
+ if ( DEBUG ) debugLog ( `Poll response status: ${ response . status } ` )
100
98
101
99
if ( response . status === 200 ) {
102
100
// Auth completed, but we don't return the code anymore
103
101
log ( `Authentication completed by other instance` )
104
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Authentication completed by other instance` )
105
102
return true
106
103
} else if ( response . status === 202 ) {
107
104
// Continue polling
108
105
log ( `Authentication still in progress` )
109
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Authentication still in progress, will retry in 1s`)
106
+ if ( DEBUG ) debugLog ( `Will retry in 1s`)
110
107
await new Promise ( ( resolve ) => setTimeout ( resolve , 1000 ) )
111
108
} else {
112
109
log ( `Unexpected response status: ${ response . status } ` )
113
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Unexpected response status` , { status : response . status } )
114
110
return false
115
111
}
116
112
} catch ( fetchError ) {
117
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Fetch error during poll` , fetchError )
113
+ if ( DEBUG ) debugLog ( `Fetch error during poll` , fetchError )
118
114
// If we can't connect, we'll try again after a delay
119
115
await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) )
120
116
}
121
117
}
122
118
} catch ( error ) {
123
119
log ( `Error waiting for authentication: ${ ( error as Error ) . message } ` )
124
- if ( DEBUG ) await debugLog ( global . currentServerUrlHash ! , `Error waiting for authentication` , error )
120
+ if ( DEBUG ) debugLog ( `Error waiting for authentication` , error )
125
121
return false
126
122
}
127
123
}
@@ -140,16 +136,16 @@ export function createLazyAuthCoordinator(serverUrlHash: string, callbackPort: n
140
136
initializeAuth : async ( ) => {
141
137
// If auth has already been initialized, return the existing state
142
138
if ( authState ) {
143
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Auth already initialized, reusing existing state' )
139
+ if ( DEBUG ) debugLog ( 'Auth already initialized, reusing existing state' )
144
140
return authState
145
141
}
146
142
147
143
log ( 'Initializing auth coordination on-demand' )
148
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Initializing auth coordination on-demand' , { serverUrlHash, callbackPort } )
144
+ if ( DEBUG ) debugLog ( 'Initializing auth coordination on-demand' , { serverUrlHash, callbackPort } )
149
145
150
146
// Initialize auth using the existing coordinateAuth logic
151
147
authState = await coordinateAuth ( serverUrlHash , callbackPort , events )
152
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Auth coordination completed' , { skipBrowserAuth : authState . skipBrowserAuth } )
148
+ if ( DEBUG ) debugLog ( 'Auth coordination completed' , { skipBrowserAuth : authState . skipBrowserAuth } )
153
149
return authState
154
150
} ,
155
151
}
@@ -167,42 +163,39 @@ export async function coordinateAuth(
167
163
callbackPort : number ,
168
164
events : EventEmitter ,
169
165
) : Promise < { server : Server ; waitForAuthCode : ( ) => Promise < string > ; skipBrowserAuth : boolean } > {
170
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Coordinating authentication' , { serverUrlHash, callbackPort } )
166
+ if ( DEBUG ) debugLog ( 'Coordinating authentication' , { serverUrlHash, callbackPort } )
171
167
172
168
// Check for a lockfile (disabled on Windows for the time being)
173
169
const lockData = process . platform === 'win32' ? null : await checkLockfile ( serverUrlHash )
174
170
175
171
if ( DEBUG ) {
176
172
if ( process . platform === 'win32' ) {
177
- await debugLog ( serverUrlHash , 'Skipping lockfile check on Windows' )
173
+ debugLog ( 'Skipping lockfile check on Windows' )
178
174
} else {
179
- await debugLog ( serverUrlHash , 'Lockfile check result' , { found : ! ! lockData , lockData } )
175
+ debugLog ( 'Lockfile check result' , { found : ! ! lockData , lockData } )
180
176
}
181
177
}
182
178
183
179
// If there's a valid lockfile, try to use the existing auth process
184
180
if ( lockData && ( await isLockValid ( lockData ) ) ) {
185
- log ( `Another instance is handling authentication on port ${ lockData . port } ` )
186
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Another instance is handling authentication' , { port : lockData . port , pid : lockData . pid } )
181
+ log ( `Another instance is handling authentication on port ${ lockData . port } (pid: ${ lockData . pid } )` )
187
182
188
183
try {
189
184
// Try to wait for the authentication to complete
190
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Waiting for authentication from other instance' )
185
+ if ( DEBUG ) debugLog ( 'Waiting for authentication from other instance' )
191
186
const authCompleted = await waitForAuthentication ( lockData . port )
192
187
193
188
if ( authCompleted ) {
194
- log ( 'Authentication completed by another instance' )
195
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Authentication completed by another instance, will use tokens from disk' )
189
+ log ( 'Authentication completed by another instance. Using tokens from disk' )
196
190
197
191
// Setup a dummy server - the client will use tokens directly from disk
198
192
const dummyServer = express ( ) . listen ( 0 ) // Listen on any available port
199
193
const dummyPort = ( dummyServer . address ( ) as AddressInfo ) . port
200
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Started dummy server' , { port : dummyPort } )
194
+ if ( DEBUG ) debugLog ( 'Started dummy server' , { port : dummyPort } )
201
195
202
196
// This shouldn't actually be called in normal operation, but provide it for API compatibility
203
197
const dummyWaitForAuthCode = ( ) => {
204
198
log ( 'WARNING: waitForAuthCode called in secondary instance - this is unexpected' )
205
- if ( DEBUG ) debugLog ( serverUrlHash , 'WARNING: waitForAuthCode called in secondary instance - this is unexpected' ) . catch ( ( ) => { } )
206
199
// Return a promise that never resolves - the client should use the tokens from disk instead
207
200
return new Promise < string > ( ( ) => { } )
208
201
}
@@ -214,25 +207,23 @@ export async function coordinateAuth(
214
207
}
215
208
} else {
216
209
log ( 'Taking over authentication process...' )
217
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Taking over authentication process' )
218
210
}
219
211
} catch ( error ) {
220
212
log ( `Error waiting for authentication: ${ error } ` )
221
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Error waiting for authentication' , error )
213
+ if ( DEBUG ) debugLog ( 'Error waiting for authentication' , error )
222
214
}
223
215
224
216
// If we get here, the other process didn't complete auth successfully
225
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Other instance did not complete auth successfully, deleting lockfile' )
217
+ if ( DEBUG ) debugLog ( 'Other instance did not complete auth successfully, deleting lockfile' )
226
218
await deleteLockfile ( serverUrlHash )
227
219
} else if ( lockData ) {
228
220
// Invalid lockfile, delete it
229
221
log ( 'Found invalid lockfile, deleting it' )
230
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Found invalid lockfile, deleting it' )
231
222
await deleteLockfile ( serverUrlHash )
232
223
}
233
224
234
225
// Create our own lockfile
235
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Setting up OAuth callback server' , { port : callbackPort } )
226
+ if ( DEBUG ) debugLog ( 'Setting up OAuth callback server' , { port : callbackPort } )
236
227
const { server, waitForAuthCode, authCompletedPromise } = setupOAuthCallbackServerWithLongPoll ( {
237
228
port : callbackPort ,
238
229
path : '/oauth/callback' ,
@@ -242,21 +233,19 @@ export async function coordinateAuth(
242
233
// Get the actual port the server is running on
243
234
const address = server . address ( ) as AddressInfo
244
235
const actualPort = address . port
245
- if ( DEBUG ) await debugLog ( serverUrlHash , 'OAuth callback server running' , { port : actualPort } )
236
+ if ( DEBUG ) debugLog ( 'OAuth callback server running' , { port : actualPort } )
246
237
247
238
log ( `Creating lockfile for server ${ serverUrlHash } with process ${ process . pid } on port ${ actualPort } ` )
248
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Creating lockfile' , { serverUrlHash, pid : process . pid , port : actualPort } )
249
239
await createLockfile ( serverUrlHash , process . pid , actualPort )
250
240
251
241
// Make sure lockfile is deleted on process exit
252
242
const cleanupHandler = async ( ) => {
253
243
try {
254
244
log ( `Cleaning up lockfile for server ${ serverUrlHash } ` )
255
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Cleaning up lockfile' )
256
245
await deleteLockfile ( serverUrlHash )
257
246
} catch ( error ) {
258
247
log ( `Error cleaning up lockfile: ${ error } ` )
259
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Error cleaning up lockfile' , error )
248
+ if ( DEBUG ) debugLog ( 'Error cleaning up lockfile' , error )
260
249
}
261
250
}
262
251
@@ -273,11 +262,11 @@ export async function coordinateAuth(
273
262
274
263
// Also handle SIGINT separately
275
264
process . once ( 'SIGINT' , async ( ) => {
276
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Received SIGINT signal, cleaning up' )
265
+ if ( DEBUG ) debugLog ( 'Received SIGINT signal, cleaning up' )
277
266
await cleanupHandler ( )
278
267
} )
279
268
280
- if ( DEBUG ) await debugLog ( serverUrlHash , 'Auth coordination complete, returning primary instance handlers' )
269
+ if ( DEBUG ) debugLog ( 'Auth coordination complete, returning primary instance handlers' )
281
270
return {
282
271
server,
283
272
waitForAuthCode,
0 commit comments