@@ -2,6 +2,8 @@ import * as path from "path"
2
2
import child_process from "child_process"
3
3
import net from "node:net"
4
4
import http from "http"
5
+ //@ts -ignore
6
+ import SSE from "sse.js"
5
7
6
8
export interface RunOpts {
7
9
gptscriptURL ?: string
@@ -52,6 +54,7 @@ export class Run {
52
54
53
55
private promise ?: Promise < string >
54
56
private process ?: child_process . ChildProcess
57
+ private sse ?: SSE
55
58
private req ?: http . ClientRequest
56
59
private stdout ?: string
57
60
private stderr ?: string
@@ -179,41 +182,34 @@ export class Run {
179
182
const options = this . requestOptions ( this . opts . gptscriptURL , path , postData , tool )
180
183
181
184
this . promise = new Promise < string > ( ( resolve , reject ) => {
182
- // Use frag to keep track of partial object writes.
183
- let frag = ""
184
- this . req = http . request ( options , ( res : http . IncomingMessage ) => {
185
- this . state = RunState . Running
186
- res . on ( "data" , ( chunk : any ) => {
187
- for ( let line of ( chunk . toString ( ) + frag ) . split ( "\n" ) ) {
188
- const c = line . replace ( / ^ ( d a t a : ) / , "" ) . trim ( )
189
- if ( ! c ) {
190
- continue
191
- }
185
+ // This checks that the code is running in a browser. If it is, then we use SSE.
186
+ if ( typeof window !== "undefined" && typeof window . document !== "undefined" ) {
187
+ this . sse = new SSE ( this . opts . gptscriptURL + "/" + path , {
188
+ headers : { "Content-Type" : "application/json" } ,
189
+ payload : postData
190
+ } )
192
191
193
- if ( c === "[DONE]" ) {
194
- return
195
- }
192
+ this . sse . addEventListener ( "open" , ( ) => {
193
+ this . state = RunState . Running
194
+ } )
196
195
197
- let e : any
198
- try {
199
- e = JSON . parse ( c )
200
- } catch {
201
- frag = c
202
- return
203
- }
204
- frag = ""
205
-
206
- if ( e . stderr ) {
207
- this . stderr = ( this . stderr || "" ) + ( typeof e . stderr === "string" ? e . stderr : JSON . stringify ( e . stderr ) )
208
- } else if ( e . stdout ) {
209
- this . stdout = ( this . stdout || "" ) + ( typeof e . stdout === "string" ? e . stdout : JSON . stringify ( e . stdout ) )
210
- } else {
211
- frag = this . emitEvent ( c )
212
- }
196
+ this . sse . addEventListener ( "message" , ( data : any ) => {
197
+ if ( data . data === "[DONE]" ) {
198
+ this . sse . close ( )
199
+ return
200
+ }
201
+
202
+ const e = JSON . parse ( data . data )
203
+ if ( e . stderr ) {
204
+ this . stderr = ( this . stderr || "" ) + ( typeof e . stderr === "string" ? e . stderr : JSON . stringify ( e . stderr ) )
205
+ } else if ( e . stdout ) {
206
+ this . stdout = ( this . stdout || "" ) + ( typeof e . stdout === "string" ? e . stdout : JSON . stringify ( e . stdout ) )
207
+ } else {
208
+ this . emitEvent ( data . data )
213
209
}
214
210
} )
215
211
216
- res . on ( "end ", ( ) => {
212
+ this . sse . addEventListener ( "close ", ( ) => {
217
213
if ( this . state === RunState . Running || this . state === RunState . Finished ) {
218
214
this . state = RunState . Finished
219
215
resolve ( this . stdout || "" )
@@ -222,29 +218,81 @@ export class Run {
222
218
}
223
219
} )
224
220
225
- res . on ( "aborted" , ( ) => {
226
- if ( this . state !== RunState . Finished ) {
221
+ this . sse . addEventListener ( "error" , ( err : any ) => {
222
+ this . state = RunState . Error
223
+ this . err = err
224
+ reject ( err )
225
+ } )
226
+ } else {
227
+ // If not in the browser, then we use HTTP.
228
+
229
+ // Use frag to keep track of partial object writes.
230
+ let frag = ""
231
+ this . req = http . request ( options , ( res : http . IncomingMessage ) => {
232
+ this . state = RunState . Running
233
+ res . on ( "data" , ( chunk : any ) => {
234
+ for ( let line of ( chunk . toString ( ) + frag ) . split ( "\n" ) ) {
235
+ const c = line . replace ( / ^ ( d a t a : ) / , "" ) . trim ( )
236
+ if ( ! c ) {
237
+ continue
238
+ }
239
+
240
+ if ( c === "[DONE]" ) {
241
+ return
242
+ }
243
+
244
+ let e : any
245
+ try {
246
+ e = JSON . parse ( c )
247
+ } catch {
248
+ frag = c
249
+ return
250
+ }
251
+ frag = ""
252
+
253
+ if ( e . stderr ) {
254
+ this . stderr = ( this . stderr || "" ) + ( typeof e . stderr === "string" ? e . stderr : JSON . stringify ( e . stderr ) )
255
+ } else if ( e . stdout ) {
256
+ this . stdout = ( this . stdout || "" ) + ( typeof e . stdout === "string" ? e . stdout : JSON . stringify ( e . stdout ) )
257
+ } else {
258
+ frag = this . emitEvent ( c )
259
+ }
260
+ }
261
+ } )
262
+
263
+ res . on ( "end" , ( ) => {
264
+ if ( this . state === RunState . Running || this . state === RunState . Finished ) {
265
+ this . state = RunState . Finished
266
+ resolve ( this . stdout || "" )
267
+ } else if ( this . state === RunState . Error ) {
268
+ reject ( this . err )
269
+ }
270
+ } )
271
+
272
+ res . on ( "aborted" , ( ) => {
273
+ if ( this . state !== RunState . Finished ) {
274
+ this . state = RunState . Error
275
+ this . err = "Run has been aborted"
276
+ reject ( this . err )
277
+ }
278
+ } )
279
+
280
+ res . on ( "error" , ( error : Error ) => {
227
281
this . state = RunState . Error
228
- this . err = "Run has been aborted "
282
+ this . err = error . message || " "
229
283
reject ( this . err )
230
- }
284
+ } )
231
285
} )
232
286
233
- res . on ( "error" , ( error : Error ) => {
287
+ this . req . on ( "error" , ( error : Error ) => {
234
288
this . state = RunState . Error
235
289
this . err = error . message || ""
236
290
reject ( this . err )
237
291
} )
238
- } )
239
-
240
- this . req . on ( "error" , ( error : Error ) => {
241
- this . state = RunState . Error
242
- this . err = error . message || ""
243
- reject ( this . err )
244
- } )
245
292
246
- this . req . write ( postData )
247
- this . req . end ( )
293
+ this . req . write ( postData )
294
+ this . req . end ( )
295
+ }
248
296
} )
249
297
}
250
298
@@ -297,7 +345,7 @@ export class Run {
297
345
this . state = RunState . Finished
298
346
this . stdout = f . output || ""
299
347
}
300
- } else if ( f . type . startsWith ( "call" ) ) {
348
+ } else if ( ( f . type as string ) . startsWith ( "call" ) ) {
301
349
let call = this . calls ?. find ( ( x ) => x . id === f . callContext . id )
302
350
303
351
if ( ! call ) {
@@ -382,7 +430,7 @@ export class Run {
382
430
return JSON . parse ( await this . text ( ) )
383
431
}
384
432
385
- public abort ( ) : void {
433
+ public close ( ) : void {
386
434
if ( this . process ) {
387
435
if ( this . process . exitCode === null ) {
388
436
this . process . kill ( "SIGKILL" )
@@ -395,6 +443,11 @@ export class Run {
395
443
return
396
444
}
397
445
446
+ if ( this . sse ) {
447
+ this . sse . close ( )
448
+ return
449
+ }
450
+
398
451
throw new Error ( "Run not started" )
399
452
}
400
453
0 commit comments