@@ -2,6 +2,7 @@ import http from "http"
22import path from "path"
33import child_process from "child_process"
44import { fileURLToPath } from "url"
5+ import net from "net"
56
67export interface RunOpts {
78 input ?: string
@@ -31,35 +32,45 @@ export enum RunEventType {
3132 Prompt = "prompt"
3233}
3334
34- let serverProcess : child_process . ChildProcess
35- let clientCount : number = 0
35+ export class GPTScript {
36+ private static serverURL : string = ""
37+ private static serverProcess : child_process . ChildProcess
38+ private static instanceCount : number = 0
3639
37- export class Client {
38- private readonly gptscriptURL : string
39- private clientReady : boolean
4040
41- constructor ( ) {
42- this . clientReady = false
43- this . gptscriptURL = "http://" + ( process . env . GPTSCRIPT_URL || "127.0.0.1:9090" )
44- clientCount ++
45- if ( clientCount === 1 && process . env . GPTSCRIPT_DISABLE_SERVER !== "true" ) {
46- serverProcess = child_process . spawn ( getCmdPath ( ) , [ "--listen-address" , this . gptscriptURL . replace ( "http://" , "" ) . replace ( "https://" , "" ) , "sdkserver" ] , {
47- env : process . env ,
48- stdio : [ "pipe" ]
49- } )
41+ private ready : boolean
5042
51- process . on ( "exit" , ( code ) => {
52- serverProcess . stdin ?. end ( )
53- serverProcess . kill ( code )
54- } )
43+ constructor ( ) {
44+ this . ready = false
45+ GPTScript . instanceCount ++
46+ if ( GPTScript . instanceCount === 1 && process . env . GPTSCRIPT_DISABLE_SERVER !== "true" ) {
47+ GPTScript . serverURL = process . env . GPTSCRIPT_URL || "http://127.0.0.1:0"
48+ const u = new URL ( GPTScript . serverURL )
49+ if ( u . port === "0" ) {
50+ const srv = net . createServer ( )
51+ const s = srv . listen ( 0 , ( ) => {
52+ GPTScript . serverURL = "http://" + u . hostname + ":" + String ( ( s . address ( ) as net . AddressInfo ) . port )
53+ srv . close ( )
54+
55+ GPTScript . serverProcess = child_process . spawn ( getCmdPath ( ) , [ "--listen-address" , GPTScript . serverURL . replace ( "http://" , "" ) , "sdkserver" ] , {
56+ env : process . env ,
57+ stdio : [ "pipe" ]
58+ } )
59+
60+ process . on ( "exit" , ( code ) => {
61+ GPTScript . serverProcess . stdin ?. end ( )
62+ GPTScript . serverProcess . kill ( code )
63+ } )
64+ } )
65+ }
5566 }
5667 }
5768
5869 close ( ) : void {
59- clientCount --
60- if ( clientCount === 0 && serverProcess ) {
61- serverProcess . kill ( "SIGTERM" )
62- serverProcess . stdin ?. end ( )
70+ GPTScript . instanceCount --
71+ if ( GPTScript . instanceCount === 0 && GPTScript . serverProcess ) {
72+ GPTScript . serverProcess . kill ( "SIGTERM" )
73+ GPTScript . serverProcess . stdin ?. end ( )
6374 }
6475 }
6576
@@ -76,10 +87,10 @@ export class Client {
7687 }
7788
7889 async runBasicCommand ( cmd : string ) : Promise < string > {
79- if ( ! this . clientReady ) {
80- this . clientReady = await this . testGPTScriptURL ( 20 )
90+ if ( ! this . ready ) {
91+ this . ready = await this . testGPTScriptURL ( 20 )
8192 }
82- const r = new RunSubcommand ( cmd , "" , "" , { } , this . gptscriptURL )
93+ const r = new RunSubcommand ( cmd , "" , "" , { } , GPTScript . serverURL )
8394 r . requestNoStream ( null )
8495 return r . text ( )
8596 }
@@ -92,10 +103,10 @@ export class Client {
92103 * @return {Run } The Run object representing the running tool.
93104 */
94105 async run ( toolName : string , opts : RunOpts = { } ) : Promise < Run > {
95- if ( ! this . clientReady ) {
96- this . clientReady = await this . testGPTScriptURL ( 20 )
106+ if ( ! this . ready ) {
107+ this . ready = await this . testGPTScriptURL ( 20 )
97108 }
98- return ( new Run ( "run" , toolName , "" , opts , this . gptscriptURL ) ) . nextChat ( opts . input )
109+ return ( new Run ( "run" , toolName , "" , opts , GPTScript . serverURL ) ) . nextChat ( opts . input )
99110 }
100111
101112 /**
@@ -106,8 +117,8 @@ export class Client {
106117 * @return {Run } The Run object representing the evaluation.
107118 */
108119 async evaluate ( tool : ToolDef | ToolDef [ ] | string , opts : RunOpts = { } ) : Promise < Run > {
109- if ( ! this . clientReady ) {
110- this . clientReady = await this . testGPTScriptURL ( 20 )
120+ if ( ! this . ready ) {
121+ this . ready = await this . testGPTScriptURL ( 20 )
111122 }
112123 let toolString : string = ""
113124
@@ -119,30 +130,30 @@ export class Client {
119130 toolString = toolDefToString ( tool )
120131 }
121132
122- return ( new Run ( "evaluate" , "" , toolString , opts , this . gptscriptURL ) ) . nextChat ( opts . input )
133+ return ( new Run ( "evaluate" , "" , toolString , opts , GPTScript . serverURL ) ) . nextChat ( opts . input )
123134 }
124135
125136 async parse ( fileName : string ) : Promise < Block [ ] > {
126- if ( ! this . clientReady ) {
127- this . clientReady = await this . testGPTScriptURL ( 20 )
137+ if ( ! this . ready ) {
138+ this . ready = await this . testGPTScriptURL ( 20 )
128139 }
129- const r : Run = new RunSubcommand ( "parse" , fileName , "" , { } , this . gptscriptURL )
140+ const r : Run = new RunSubcommand ( "parse" , fileName , "" , { } , GPTScript . serverURL )
130141 r . request ( { file : fileName } )
131142 return parseBlocksFromNodes ( ( await r . json ( ) ) . nodes )
132143 }
133144
134145 async parseTool ( toolContent : string ) : Promise < Block [ ] > {
135- if ( ! this . clientReady ) {
136- this . clientReady = await this . testGPTScriptURL ( 20 )
146+ if ( ! this . ready ) {
147+ this . ready = await this . testGPTScriptURL ( 20 )
137148 }
138- const r : Run = new RunSubcommand ( "parse" , "" , toolContent , { } , this . gptscriptURL )
149+ const r : Run = new RunSubcommand ( "parse" , "" , toolContent , { } , GPTScript . serverURL )
139150 r . request ( { content : toolContent } )
140151 return parseBlocksFromNodes ( ( await r . json ( ) ) . nodes )
141152 }
142153
143154 async stringify ( blocks : Block [ ] ) : Promise < string > {
144- if ( ! this . clientReady ) {
145- this . clientReady = await this . testGPTScriptURL ( 20 )
155+ if ( ! this . ready ) {
156+ this . ready = await this . testGPTScriptURL ( 20 )
146157 }
147158 const nodes : any [ ] = [ ]
148159
@@ -162,16 +173,16 @@ export class Client {
162173 }
163174 }
164175
165- const r : Run = new RunSubcommand ( "fmt" , "" , JSON . stringify ( { nodes : nodes } ) , { } , this . gptscriptURL )
176+ const r : Run = new RunSubcommand ( "fmt" , "" , JSON . stringify ( { nodes : nodes } ) , { } , GPTScript . serverURL )
166177 r . request ( { nodes : nodes } )
167178 return r . text ( )
168179 }
169180
170181 async confirm ( response : AuthResponse ) : Promise < void > {
171- if ( ! this . clientReady ) {
172- this . clientReady = await this . testGPTScriptURL ( 20 )
182+ if ( ! this . ready ) {
183+ this . ready = await this . testGPTScriptURL ( 20 )
173184 }
174- const resp = await fetch ( `${ this . gptscriptURL } /confirm/${ response . id } ` , {
185+ const resp = await fetch ( `${ GPTScript . serverURL } /confirm/${ response . id } ` , {
175186 method : "POST" ,
176187 body : JSON . stringify ( response )
177188 } )
@@ -182,10 +193,10 @@ export class Client {
182193 }
183194
184195 async promptResponse ( response : PromptResponse ) : Promise < void > {
185- if ( ! this . clientReady ) {
186- this . clientReady = await this . testGPTScriptURL ( 20 )
196+ if ( ! this . ready ) {
197+ this . ready = await this . testGPTScriptURL ( 20 )
187198 }
188- const resp = await fetch ( `${ this . gptscriptURL } /prompt-response/${ response . id } ` , {
199+ const resp = await fetch ( `${ GPTScript . serverURL } /prompt-response/${ response . id } ` , {
189200 method : "POST" ,
190201 body : JSON . stringify ( response . responses )
191202 } )
@@ -197,7 +208,7 @@ export class Client {
197208
198209 private async testGPTScriptURL ( count : number ) : Promise < boolean > {
199210 try {
200- await fetch ( `${ this . gptscriptURL } /healthz` )
211+ await fetch ( `${ GPTScript . serverURL } /healthz` )
201212 return true
202213 } catch {
203214 if ( count === 0 ) {
0 commit comments