|
| 1 | +"use strict"; |
| 2 | + |
| 3 | +const IDENT = "[A-Za-z_$][\\w$]*"; |
| 4 | + |
| 5 | +function findTransportSymbols(source) { |
| 6 | + const classMatch = source.match(new RegExp(`var (${IDENT})=class\\{kind=\\\`websocket\\\``)); |
| 7 | + const selectionLogIndex = source.indexOf("selected app-server transport"); |
| 8 | + if (classMatch == null || selectionLogIndex < 0 || classMatch.index >= selectionLogIndex) return null; |
| 9 | + |
| 10 | + const sshClassSource = source.slice(classMatch.index, selectionLogIndex); |
| 11 | + const webSocketMatch = sshClassSource.match( |
| 12 | + new RegExp(`new (${IDENT})\\.(${IDENT})\\((${IDENT}),\\{perMessageDeflate:!1,createConnection:`), |
| 13 | + ); |
| 14 | + if (webSocketMatch == null) return null; |
| 15 | + const [, namespace, webSocketClass, webSocketUrl] = webSocketMatch; |
| 16 | + const lifecycleMatch = sshClassSource.match( |
| 17 | + new RegExp( |
| 18 | + `return ${namespace}\\.(${IDENT})\\((${IDENT}),\\{onPongTimeout:[\\s\\S]{0,160}?\\}\\),new ${namespace}\\.(${IDENT})\\(\\2\\)`, |
| 19 | + ), |
| 20 | + ); |
| 21 | + if (lifecycleMatch == null) return null; |
| 22 | + |
| 23 | + return { |
| 24 | + namespace, |
| 25 | + webSocketClass, |
| 26 | + webSocketUrl, |
| 27 | + adapterClass: lifecycleMatch[3], |
| 28 | + keepAlive: lifecycleMatch[1], |
| 29 | + }; |
| 30 | +} |
| 31 | + |
| 32 | +function applySharedAppServerSocketPatch(source) { |
| 33 | + if (source.includes("class CodexLinuxSharedAppServerSocketTransport")) return source; |
| 34 | + |
| 35 | + const symbols = findTransportSymbols(source); |
| 36 | + if (symbols == null) { |
| 37 | + console.warn("WARN: Could not find SSH WebSocket transport for shared app-server socket patch"); |
| 38 | + return source; |
| 39 | + } |
| 40 | + |
| 41 | + const selectionLogIndex = source.indexOf("selected app-server transport"); |
| 42 | + const factoryStart = source.lastIndexOf("function ", selectionLogIndex); |
| 43 | + const factoryEnd = source.indexOf("function ", selectionLogIndex + 1); |
| 44 | + if (selectionLogIndex < 0 || factoryStart < 0 || factoryEnd < 0) { |
| 45 | + console.warn("WARN: Could not find local transport factory for shared app-server socket patch"); |
| 46 | + return source; |
| 47 | + } |
| 48 | + const factorySource = source.slice(factoryStart, factoryEnd); |
| 49 | + const localFallbackPattern = new RegExp( |
| 50 | + `(if\\(${symbols.namespace}\\.(${IDENT})\\(e\\.hostConfig\\)\\)return [^;]+;)(let (${IDENT})=(${IDENT})\\(e\\.hostConfig\\);return \\4\\?)`, |
| 51 | + ); |
| 52 | + const localFallbackMatch = factorySource.match(localFallbackPattern); |
| 53 | + if (localFallbackMatch == null) { |
| 54 | + console.warn("WARN: Could not find local transport fallback for shared app-server socket patch"); |
| 55 | + return source; |
| 56 | + } |
| 57 | + |
| 58 | + const classSource = |
| 59 | + "class CodexLinuxSharedAppServerSocketTransport{" + |
| 60 | + "kind=`websocket`;proxyStreams=new Set;authority=null;" + |
| 61 | + "constructor(e){this.socketPath=e}" + |
| 62 | + "supportsReconnect(){return!0}" + |
| 63 | + "dispose(){for(let e of this.proxyStreams)e.destroy();this.proxyStreams.clear();this.authority?.kill();this.authority=null;try{require(`node:fs`).unlinkSync(this.socketPath)}catch(e){if(e?.code!==`ENOENT`)throw e}}" + |
| 64 | + "async ensureAuthority(){if(this.authority&&this.authority.exitCode==null)return;let e=process.env.CODEX_CLI_PATH;if(!e)throw Error(`shared app-server socket requires CODEX_CLI_PATH`);let t=require(`node:fs`),n=require(`node:path`);t.mkdirSync(n.dirname(this.socketPath),{recursive:!0,mode:448});try{t.unlinkSync(this.socketPath)}catch(e){if(e?.code!==`ENOENT`)throw e}this.authority=require(`node:child_process`).spawn(e,[`app-server`,`--listen`,`unix://${this.socketPath}`],{env:process.env,stdio:`ignore`});for(let e=0;e<100;e++){if(this.authority.exitCode!=null)throw Error(`shared app-server authority exited before socket creation`);try{if(t.statSync(this.socketPath).isSocket())return}catch(e){if(e?.code!==`ENOENT`)throw e}await new Promise(e=>setTimeout(e,100))}this.authority.kill();this.authority=null;throw Error(`shared app-server socket creation timed out`)}" + |
| 65 | + "createProxyStream(){let c=process.env.CODEX_CLI_PATH;if(!c)throw Error(`shared app-server socket requires CODEX_CLI_PATH`);let e=require(`node:child_process`).spawn(c,[`app-server`,`proxy`,`--sock`,this.socketPath],{env:process.env,stdio:[`pipe`,`pipe`,`pipe`]}),t=e.stdin,n=e.stdout,r=e.stderr;if(t==null||n==null||r==null)throw e.kill(),Error(`shared app-server proxy stdio was unavailable`);let i=``;r.on(`data`,e=>{i=`${i}${e.toString(`utf8`)}`.slice(-4000)});let a=new(require(`node:stream`).Duplex)({read(){n.resume()},write(e,n,r){t.write(e,n,r)},final(e){t.end(),e()},destroy(t,n){e.kill(),n(t)}});Object.assign(a,{setKeepAlive:()=>a,setNoDelay:()=>a,setTimeout:()=>a});let o=e=>a.destroy(e);t.on(`error`,o),n.on(`data`,e=>{a.push(e)||n.pause()}),n.on(`end`,()=>a.push(null)),e.on(`error`,o),e.on(`close`,(e,n)=>{t.removeListener(`error`,o),e===0?a.push(null):a.destroy(Error(`shared app-server proxy exited (${e??n??`unknown`}): ${i.trim()}`))}),this.proxyStreams.add(a),a.once(`close`,()=>this.proxyStreams.delete(a));return a}" + |
| 66 | + `async connect(){await this.ensureAuthority();let e={current:null},t=new ${symbols.namespace}.${symbols.webSocketClass}(${symbols.webSocketUrl},{perMessageDeflate:!1,createConnection:()=>(e.current=this.createProxyStream(),e.current)});t.once(\`close\`,()=>e.current?.destroy());await new Promise((n,r)=>{let i=setTimeout(()=>{r(Error(\`shared app-server websocket open timed out\`))},3e4);i.unref();let a=()=>{clearTimeout(i),t.off(\`error\`,o),t.off(\`close\`,s)},o=e=>{a(),r(e)},s=()=>{a(),r(Error(\`shared app-server websocket closed before opening\`))};t.once(\`open\`,()=>{a(),n()}),t.once(\`error\`,o),t.once(\`close\`,s)});${symbols.namespace}.${symbols.keepAlive}(t,{onPongTimeout:()=>t.terminate()});return new ${symbols.namespace}.${symbols.adapterClass}(t)}}`; |
| 67 | + |
| 68 | + const patchedFactory = factorySource.replace( |
| 69 | + localFallbackPattern, |
| 70 | + `$1if(process.env.CODEX_LINUX_APP_SERVER_BRIDGE_SOCKET&&e.hostConfig.kind===\`local\`)return new CodexLinuxSharedAppServerSocketTransport(process.env.CODEX_LINUX_APP_SERVER_BRIDGE_SOCKET);$3`, |
| 71 | + ); |
| 72 | + return source.slice(0, factoryStart) + classSource + patchedFactory + source.slice(factoryEnd); |
| 73 | +} |
| 74 | + |
| 75 | +const descriptors = [ |
| 76 | + { |
| 77 | + id: "main-process-shared-app-server-socket", |
| 78 | + phase: "main-bundle", |
| 79 | + order: 140, |
| 80 | + ciPolicy: "optional", |
| 81 | + apply: applySharedAppServerSocketPatch, |
| 82 | + }, |
| 83 | +]; |
| 84 | + |
| 85 | +module.exports = { |
| 86 | + applySharedAppServerSocketPatch, |
| 87 | + descriptors, |
| 88 | + findTransportSymbols, |
| 89 | +}; |
0 commit comments