pi-plus 0.1.32 → 0.1.34
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/anthropic.js +1 -1
- package/bedrock-converse-stream.js +2 -2
- package/github-copilot.js +1 -1
- package/image-resize-worker.js +1 -1
- package/modes/interactive/theme/dark.json +1 -1
- package/modes/interactive/theme/light.json +1 -1
- package/openai-codex.js +1 -1
- package/package.json +7 -3
- package/pipi.js +363 -242
- package/modes/interactive/theme/theme-schema.json +0 -361
package/image-resize-worker.js
CHANGED
|
@@ -6,4 +6,4 @@ import { createRequire as __piCreateRequire } from "node:module"; const require
|
|
|
6
6
|
for (const __key of ["ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "GROQ_API_KEY", "CEREBRAS_API_KEY", "XAI_API_KEY", "OPENROUTER_API_KEY", "ZAI_API_KEY", "MISTRAL_API_KEY", "MINIMAX_API_KEY", "MINIMAX_CN_API_KEY", "AI_GATEWAY_API_KEY", "OPENCODE_API_KEY", "COPILOT_GITHUB_TOKEN", "GH_TOKEN", "GITHUB_TOKEN", "HF_TOKEN", "GOOGLE_APPLICATION_CREDENTIALS", "GOOGLE_CLOUD_PROJECT", "GCLOUD_PROJECT", "GOOGLE_CLOUD_LOCATION", "AWS_PROFILE", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_REGION", "AWS_DEFAULT_REGION", "AWS_BEARER_TOKEN_BEDROCK", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", "AWS_CONTAINER_CREDENTIALS_FULL_URI", "AWS_WEB_IDENTITY_TOKEN_FILE", "AZURE_OPENAI_API_KEY", "AZURE_OPENAI_BASE_URL", "AZURE_OPENAI_RESOURCE_NAME"]) delete process.env[__key];
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
import{parentPort}from"node:worker_threads";function readOrientationFromTiff(bytes,tiffStart){if(tiffStart+8>bytes.length)return 1;let le=(bytes[tiffStart]<<8|bytes[tiffStart+1])===18761,read16=pos=>le?bytes[pos]|bytes[pos+1]<<8:bytes[pos]<<8|bytes[pos+1],ifdOffset=(pos=>le?bytes[pos]|bytes[pos+1]<<8|bytes[pos+2]<<16|bytes[pos+3]<<24:(bytes[pos]<<24|bytes[pos+1]<<16|bytes[pos+2]<<8|bytes[pos+3])>>>0)(tiffStart+4),ifdStart=tiffStart+ifdOffset;if(ifdStart+2>bytes.length)return 1;let entryCount=read16(ifdStart);for(let i=0;i<entryCount;i++){let entryPos=ifdStart+2+i*12;if(entryPos+12>bytes.length)return 1;if(read16(entryPos)===274){let value=read16(entryPos+8);return value>=1&&value<=8?value:1}}return 1}function findJpegTiffOffset(bytes){let offset=2;for(;offset<bytes.length-1;){if(bytes[offset]!==255)return-1;let marker=bytes[offset+1];if(marker===255){offset++;continue}if(marker===225){if(offset+4>=bytes.length)return-1;let segmentStart=offset+4;if(segmentStart+6>bytes.length)return-1;if(hasExifHeader(bytes,segmentStart))return segmentStart+6}if(offset+4>bytes.length)return-1;let length=bytes[offset+2]<<8|bytes[offset+3];offset+=2+length}return-1}function findWebpTiffOffset(bytes){let offset=12;for(;offset+8<=bytes.length;){let chunkId=String.fromCharCode(bytes[offset],bytes[offset+1],bytes[offset+2],bytes[offset+3]),chunkSize=bytes[offset+4]|bytes[offset+5]<<8|bytes[offset+6]<<16|bytes[offset+7]<<24,dataStart=offset+8;if(chunkId==="EXIF")return dataStart+chunkSize>bytes.length?-1:chunkSize>=6&&hasExifHeader(bytes,dataStart)?dataStart+6:dataStart;offset=dataStart+chunkSize+chunkSize%2}return-1}function hasExifHeader(bytes,offset){return bytes[offset]===69&&bytes[offset+1]===120&&bytes[offset+2]===105&&bytes[offset+3]===102&&bytes[offset+4]===0&&bytes[offset+5]===0}function getExifOrientation(bytes){let tiffOffset=-1;return bytes.length>=2&&bytes[0]===255&&bytes[1]===216?tiffOffset=findJpegTiffOffset(bytes):bytes.length>=12&&bytes[0]===82&&bytes[1]===73&&bytes[2]===70&&bytes[3]===70&&bytes[8]===87&&bytes[9]===69&&bytes[10]===66&&bytes[11]===80&&(tiffOffset=findWebpTiffOffset(bytes)),tiffOffset===-1?1:readOrientationFromTiff(bytes,tiffOffset)}function rotate90(photon,image,dstIndex){let w=image.get_width(),h=image.get_height(),src=image.get_raw_pixels(),dst=new Uint8Array(src.length);for(let y=0;y<h;y++)for(let x=0;x<w;x++){let srcIdx=(y*w+x)*4,dstIdx=dstIndex(x,y,w,h)*4;dst[dstIdx]=src[srcIdx],dst[dstIdx+1]=src[srcIdx+1],dst[dstIdx+2]=src[srcIdx+2],dst[dstIdx+3]=src[srcIdx+3]}return new photon.PhotonImage(dst,h,w)}function applyExifOrientation(photon,image,originalBytes){let orientation=getExifOrientation(originalBytes);if(orientation===1)return image;switch(orientation){case 2:return photon.fliph(image),image;case 3:return photon.fliph(image),photon.flipv(image),image;case 4:return photon.flipv(image),image;case 5:{let rotated=rotate90(photon,image,(x,y,_w,h)=>x*h+(h-1-y));return photon.fliph(rotated),rotated}case 6:return rotate90(photon,image,(x,y,_w,h)=>x*h+(h-1-y));case 7:{let rotated=rotate90(photon,image,(x,y,w,h)=>(w-1-x)*h+y);return photon.fliph(rotated),rotated}case 8:return rotate90(photon,image,(x,y,w,h)=>(w-1-x)*h+y);default:return image}}import{createRequire}from"module";import*as path from"path";import{fileURLToPath}from"url";var require2=createRequire(import.meta.url),fs=require2("fs"),WASM_FILENAME="photon_rs_bg.wasm",photonModule=null,loadPromise=null;function pathOrNull(file){return typeof file=="string"?file:file instanceof URL?fileURLToPath(file):null}function getFallbackWasmPaths(){let execDir=path.dirname(process.execPath);return[path.join(execDir,WASM_FILENAME),path.join(execDir,"photon",WASM_FILENAME),path.join(process.cwd(),WASM_FILENAME)]}function patchPhotonWasmRead(){let originalReadFileSync=fs.readFileSync.bind(fs),fallbackPaths=getFallbackWasmPaths(),mutableFs=fs,patchedReadFileSync=((...args)=>{let[file,options]=args;if(pathOrNull(file)?.endsWith(WASM_FILENAME))try{return originalReadFileSync(...args)}catch(error){let err=error;if(err?.code&&err.code!=="ENOENT")throw error;for(let fallbackPath of fallbackPaths)if(fs.existsSync(fallbackPath))return options===void 0?originalReadFileSync(fallbackPath):originalReadFileSync(fallbackPath,options);throw error}return originalReadFileSync(...args)});try{mutableFs.readFileSync=patchedReadFileSync}catch{Object.defineProperty(fs,"readFileSync",{value:patchedReadFileSync,writable:!0,configurable:!0})}return()=>{try{mutableFs.readFileSync=originalReadFileSync}catch{Object.defineProperty(fs,"readFileSync",{value:originalReadFileSync,writable:!0,configurable:!0})}}}async function loadPhoton(){return photonModule||loadPromise||(loadPromise=(async()=>{let restoreReadFileSync=patchPhotonWasmRead();try{return photonModule=await import("@silvia-odwyer/photon-node"),photonModule}catch{return photonModule=null,photonModule}finally{restoreReadFileSync()}})(),loadPromise)}var DEFAULT_MAX_BYTES=4.5*1024*1024,DEFAULT_OPTIONS={maxWidth:2e3,maxHeight:2e3,maxBytes:DEFAULT_MAX_BYTES,jpegQuality:80};function encodeCandidate(buffer,mimeType){let data=Buffer.from(buffer).toString("base64");return{data,encodedSize:Buffer.byteLength(data,"utf-8"),mimeType}}async function resizeImageInProcess(inputBytes,mimeType,options){let opts={...DEFAULT_OPTIONS,...options},inputBase64Size=Math.ceil(inputBytes.byteLength/3)*4,photon=await loadPhoton();if(!photon)return null;let image;try{let tryEncodings=function(width,height,jpegQualities){let resized=photon.resize(image,width,height,photon.SamplingFilter.Lanczos3);try{let candidates=[encodeCandidate(resized.get_bytes(),"image/png")];for(let quality of jpegQualities)candidates.push(encodeCandidate(resized.get_bytes_jpeg(quality),"image/jpeg"));return candidates}finally{resized.free()}},rawImage=photon.PhotonImage.new_from_byteslice(inputBytes);image=applyExifOrientation(photon,rawImage,inputBytes),image!==rawImage&&rawImage.free();let originalWidth=image.get_width(),originalHeight=image.get_height(),format=mimeType.split("/")[1]??"png";if(originalWidth<=opts.maxWidth&&originalHeight<=opts.maxHeight&&inputBase64Size<opts.maxBytes)return{data:Buffer.from(inputBytes).toString("base64"),mimeType:mimeType||`image/${format}`,originalWidth,originalHeight,width:originalWidth,height:originalHeight,wasResized:!1};let targetWidth=originalWidth,targetHeight=originalHeight;targetWidth>opts.maxWidth&&(targetHeight=Math.round(targetHeight*opts.maxWidth/targetWidth),targetWidth=opts.maxWidth),targetHeight>opts.maxHeight&&(targetWidth=Math.round(targetWidth*opts.maxHeight/targetHeight),targetHeight=opts.maxHeight);let qualitySteps=Array.from(new Set([opts.jpegQuality,85,70,55,40])),currentWidth=targetWidth,currentHeight=targetHeight;for(;;){let candidates=tryEncodings(currentWidth,currentHeight,qualitySteps);for(let candidate of candidates)if(candidate.encodedSize<opts.maxBytes)return{data:candidate.data,mimeType:candidate.mimeType,originalWidth,originalHeight,width:currentWidth,height:currentHeight,wasResized:!0};if(currentWidth===1&¤tHeight===1)break;let nextWidth=currentWidth===1?1:Math.max(1,Math.floor(currentWidth*.75)),nextHeight=currentHeight===1?1:Math.max(1,Math.floor(currentHeight*.75));if(nextWidth===currentWidth&&nextHeight===currentHeight)break;currentWidth=nextWidth,currentHeight=nextHeight}return null}catch{return null}finally{image&&image.free()}}function isResizeImageWorkerRequest(value){if(!value||typeof value!="object")return!1;let record=value;return record.inputBytes instanceof Uint8Array&&typeof record.mimeType=="string"}var port=parentPort;if(!port)throw new Error("image resize worker requires parentPort");port.once("message",message=>{(async()=>{try{if(!isResizeImageWorkerRequest(message))throw new Error("Invalid image resize worker request");let
|
|
9
|
+
import{parentPort}from"node:worker_threads";function readOrientationFromTiff(bytes,tiffStart){if(tiffStart+8>bytes.length)return 1;let le=(bytes[tiffStart]<<8|bytes[tiffStart+1])===18761,read16=pos=>le?bytes[pos]|bytes[pos+1]<<8:bytes[pos]<<8|bytes[pos+1],ifdOffset=(pos=>le?bytes[pos]|bytes[pos+1]<<8|bytes[pos+2]<<16|bytes[pos+3]<<24:(bytes[pos]<<24|bytes[pos+1]<<16|bytes[pos+2]<<8|bytes[pos+3])>>>0)(tiffStart+4),ifdStart=tiffStart+ifdOffset;if(ifdStart+2>bytes.length)return 1;let entryCount=read16(ifdStart);for(let i=0;i<entryCount;i++){let entryPos=ifdStart+2+i*12;if(entryPos+12>bytes.length)return 1;if(read16(entryPos)===274){let value=read16(entryPos+8);return value>=1&&value<=8?value:1}}return 1}function findJpegTiffOffset(bytes){let offset=2;for(;offset<bytes.length-1;){if(bytes[offset]!==255)return-1;let marker=bytes[offset+1];if(marker===255){offset++;continue}if(marker===225){if(offset+4>=bytes.length)return-1;let segmentStart=offset+4;if(segmentStart+6>bytes.length)return-1;if(hasExifHeader(bytes,segmentStart))return segmentStart+6}if(offset+4>bytes.length)return-1;let length=bytes[offset+2]<<8|bytes[offset+3];offset+=2+length}return-1}function findWebpTiffOffset(bytes){let offset=12;for(;offset+8<=bytes.length;){let chunkId=String.fromCharCode(bytes[offset],bytes[offset+1],bytes[offset+2],bytes[offset+3]),chunkSize=bytes[offset+4]|bytes[offset+5]<<8|bytes[offset+6]<<16|bytes[offset+7]<<24,dataStart=offset+8;if(chunkId==="EXIF")return dataStart+chunkSize>bytes.length?-1:chunkSize>=6&&hasExifHeader(bytes,dataStart)?dataStart+6:dataStart;offset=dataStart+chunkSize+chunkSize%2}return-1}function hasExifHeader(bytes,offset){return bytes[offset]===69&&bytes[offset+1]===120&&bytes[offset+2]===105&&bytes[offset+3]===102&&bytes[offset+4]===0&&bytes[offset+5]===0}function getExifOrientation(bytes){let tiffOffset=-1;return bytes.length>=2&&bytes[0]===255&&bytes[1]===216?tiffOffset=findJpegTiffOffset(bytes):bytes.length>=12&&bytes[0]===82&&bytes[1]===73&&bytes[2]===70&&bytes[3]===70&&bytes[8]===87&&bytes[9]===69&&bytes[10]===66&&bytes[11]===80&&(tiffOffset=findWebpTiffOffset(bytes)),tiffOffset===-1?1:readOrientationFromTiff(bytes,tiffOffset)}function rotate90(photon,image,dstIndex){let w=image.get_width(),h=image.get_height(),src=image.get_raw_pixels(),dst=new Uint8Array(src.length);for(let y=0;y<h;y++)for(let x=0;x<w;x++){let srcIdx=(y*w+x)*4,dstIdx=dstIndex(x,y,w,h)*4;dst[dstIdx]=src[srcIdx],dst[dstIdx+1]=src[srcIdx+1],dst[dstIdx+2]=src[srcIdx+2],dst[dstIdx+3]=src[srcIdx+3]}return new photon.PhotonImage(dst,h,w)}function applyExifOrientation(photon,image,originalBytes){let orientation=getExifOrientation(originalBytes);if(orientation===1)return image;switch(orientation){case 2:return photon.fliph(image),image;case 3:return photon.fliph(image),photon.flipv(image),image;case 4:return photon.flipv(image),image;case 5:{let rotated=rotate90(photon,image,(x,y,_w,h)=>x*h+(h-1-y));return photon.fliph(rotated),rotated}case 6:return rotate90(photon,image,(x,y,_w,h)=>x*h+(h-1-y));case 7:{let rotated=rotate90(photon,image,(x,y,w,h)=>(w-1-x)*h+y);return photon.fliph(rotated),rotated}case 8:return rotate90(photon,image,(x,y,w,h)=>(w-1-x)*h+y);default:return image}}import{createRequire}from"module";import*as path from"path";import{fileURLToPath}from"url";var require2=createRequire(import.meta.url),fs=require2("fs"),WASM_FILENAME="photon_rs_bg.wasm",photonModule=null,loadPromise=null;function pathOrNull(file){return typeof file=="string"?file:file instanceof URL?fileURLToPath(file):null}function getFallbackWasmPaths(){let execDir=path.dirname(process.execPath);return[path.join(execDir,WASM_FILENAME),path.join(execDir,"photon",WASM_FILENAME),path.join(process.cwd(),WASM_FILENAME)]}function patchPhotonWasmRead(){let originalReadFileSync=fs.readFileSync.bind(fs),fallbackPaths=getFallbackWasmPaths(),mutableFs=fs,patchedReadFileSync=((...args)=>{let[file,options]=args;if(pathOrNull(file)?.endsWith(WASM_FILENAME))try{return originalReadFileSync(...args)}catch(error){let err=error;if(err?.code&&err.code!=="ENOENT")throw error;for(let fallbackPath of fallbackPaths)if(fs.existsSync(fallbackPath))return options===void 0?originalReadFileSync(fallbackPath):originalReadFileSync(fallbackPath,options);throw error}return originalReadFileSync(...args)});try{mutableFs.readFileSync=patchedReadFileSync}catch{Object.defineProperty(fs,"readFileSync",{value:patchedReadFileSync,writable:!0,configurable:!0})}return()=>{try{mutableFs.readFileSync=originalReadFileSync}catch{Object.defineProperty(fs,"readFileSync",{value:originalReadFileSync,writable:!0,configurable:!0})}}}async function loadPhoton(){return photonModule||loadPromise||(loadPromise=(async()=>{let restoreReadFileSync=patchPhotonWasmRead();try{return photonModule=await import("@silvia-odwyer/photon-node"),photonModule}catch{return photonModule=null,photonModule}finally{restoreReadFileSync()}})(),loadPromise)}var IMAGE_RESIZE_WORKER_RESPONSE_TYPE="pi:image-resize-response",DEFAULT_MAX_BYTES=4.5*1024*1024,DEFAULT_OPTIONS={maxWidth:2e3,maxHeight:2e3,maxBytes:DEFAULT_MAX_BYTES,jpegQuality:80};function encodeCandidate(buffer,mimeType){let data=Buffer.from(buffer).toString("base64");return{data,encodedSize:Buffer.byteLength(data,"utf-8"),mimeType}}async function resizeImageInProcess(inputBytes,mimeType,options){let opts={...DEFAULT_OPTIONS,...options},inputBase64Size=Math.ceil(inputBytes.byteLength/3)*4,photon=await loadPhoton();if(!photon)return null;let image;try{let tryEncodings=function(width,height,jpegQualities){let resized=photon.resize(image,width,height,photon.SamplingFilter.Lanczos3);try{let candidates=[encodeCandidate(resized.get_bytes(),"image/png")];for(let quality of jpegQualities)candidates.push(encodeCandidate(resized.get_bytes_jpeg(quality),"image/jpeg"));return candidates}finally{resized.free()}},rawImage=photon.PhotonImage.new_from_byteslice(inputBytes);image=applyExifOrientation(photon,rawImage,inputBytes),image!==rawImage&&rawImage.free();let originalWidth=image.get_width(),originalHeight=image.get_height(),format=mimeType.split("/")[1]??"png";if(originalWidth<=opts.maxWidth&&originalHeight<=opts.maxHeight&&inputBase64Size<opts.maxBytes)return{data:Buffer.from(inputBytes).toString("base64"),mimeType:mimeType||`image/${format}`,originalWidth,originalHeight,width:originalWidth,height:originalHeight,wasResized:!1};let targetWidth=originalWidth,targetHeight=originalHeight;targetWidth>opts.maxWidth&&(targetHeight=Math.round(targetHeight*opts.maxWidth/targetWidth),targetWidth=opts.maxWidth),targetHeight>opts.maxHeight&&(targetWidth=Math.round(targetWidth*opts.maxHeight/targetHeight),targetHeight=opts.maxHeight);let qualitySteps=Array.from(new Set([opts.jpegQuality,85,70,55,40])),currentWidth=targetWidth,currentHeight=targetHeight;for(;;){let candidates=tryEncodings(currentWidth,currentHeight,qualitySteps);for(let candidate of candidates)if(candidate.encodedSize<opts.maxBytes)return{data:candidate.data,mimeType:candidate.mimeType,originalWidth,originalHeight,width:currentWidth,height:currentHeight,wasResized:!0};if(currentWidth===1&¤tHeight===1)break;let nextWidth=currentWidth===1?1:Math.max(1,Math.floor(currentWidth*.75)),nextHeight=currentHeight===1?1:Math.max(1,Math.floor(currentHeight*.75));if(nextWidth===currentWidth&&nextHeight===currentHeight)break;currentWidth=nextWidth,currentHeight=nextHeight}return null}catch{return null}finally{image&&image.free()}}function isResizeImageWorkerRequest(value){if(!value||typeof value!="object")return!1;let record=value;return record.inputBytes instanceof Uint8Array&&typeof record.mimeType=="string"}var port=parentPort;if(!port)throw new Error("image resize worker requires parentPort");port.once("message",message=>{(async()=>{try{if(!isResizeImageWorkerRequest(message))throw new Error("Invalid image resize worker request");let result=await resizeImageInProcess(message.inputBytes,message.mimeType,message.options),response={type:IMAGE_RESIZE_WORKER_RESPONSE_TYPE,result};port.postMessage(response)}catch(error){let response={type:IMAGE_RESIZE_WORKER_RESPONSE_TYPE,error:error instanceof Error?error.message:String(error)};port.postMessage(response)}})()});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/schemas/theme.schema.json",
|
|
3
3
|
"name": "dark",
|
|
4
4
|
"appearance": "dark",
|
|
5
5
|
"vars": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/
|
|
2
|
+
"$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/schemas/theme.schema.json",
|
|
3
3
|
"name": "light",
|
|
4
4
|
"appearance": "light",
|
|
5
5
|
"vars": {
|
package/openai-codex.js
CHANGED
|
@@ -79,4 +79,4 @@ var __require=(x=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(x,{get:(
|
|
|
79
79
|
${details?`<div class="details">${details}</div>`:""}
|
|
80
80
|
</main>
|
|
81
81
|
</body>
|
|
82
|
-
</html>`}function oauthSuccessHtml(message){return renderPage({title:"Authentication successful",heading:"Authentication successful",message})}function oauthErrorHtml(message,details){return renderPage({title:"Authentication failed",heading:"Authentication failed",message,details})}function sendPage(response,status,html){response.writeHead(status,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),response.end(html)}async function startOAuthCallbackServer(options){let{providerName,signal}=options;if(signal?.aborted)throw new Error("Login cancelled");let resolveWait=()=>{},rejectWait=()=>{},waitPromise=new Promise((resolve,reject)=>{resolveWait=resolve,rejectWait=reject});waitPromise.catch(()=>{});let claimed=!1,settled=!1,timer,onAbort=()=>finish({error:new Error("Login cancelled")}),finish=result=>{settled||(settled=!0,timer&&clearTimeout(timer),signal?.removeEventListener("abort",onAbort),"error"in result?rejectWait(result.error):resolveWait(result.value))},server=createServer((request,response)=>{(async()=>{let url=new URL(request.url??"/","http://localhost");if(request.method!=="GET"||url.pathname!==options.path){sendPage(response,404,oauthErrorHtml("Callback route not found."));return}if(options.state!==void 0&&url.searchParams.get("state")!==options.state){sendPage(response,400,oauthErrorHtml("State mismatch."));return}if(claimed||settled){sendPage(response,409,oauthErrorHtml("This sign-in has already been handled."));return}let error=url.searchParams.get("error");if(error){let description=url.searchParams.get("error_description")??error;sendPage(response,400,oauthErrorHtml(`${providerName} authorization failed.`,description)),finish({error:new Error(`${providerName} authorization failed: ${description}`)});return}let code=url.searchParams.get("code");if(!code){sendPage(response,400,oauthErrorHtml("Missing authorization code."));return}claimed=!0;try{let value=await options.complete(code);sendPage(response,200,oauthSuccessHtml(`Signed in to ${providerName}. You may now close this page.`)),finish({value})}catch(error2){let failure=error2 instanceof Error?error2:new Error(String(error2));sendPage(response,502,oauthErrorHtml(`${providerName} sign-in failed.`,failure.message)),finish({error:failure})}})()});await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(options.port,options.host,()=>{server.off("error",reject),resolve()})});let address=server.address();if(!address||typeof address=="string")throw server.close(),new Error("OAuth callback server did not bind to TCP");server.on("error",error=>finish({error})),signal?.addEventListener("abort",onAbort,{once:!0}),options.timeoutMs!==void 0&&(timer=setTimeout(()=>finish({error:new Error(`${providerName} sign-in timed out`)}),options.timeoutMs));let redirectHost=options.redirectHost??options.host;return{redirectUri:`http://${redirectHost.includes(":")?`[${redirectHost}]`:redirectHost}:${address.port}${options.path}`,wait:()=>waitPromise,cancel:()=>{claimed||finish({value:void 0})},close:()=>{finish({error:new Error("OAuth callback server closed")}),server.close()}}}async function waitForCallbackOrManualInput(interaction,callback,prompt){let manualAbort=new AbortController,manualError,manual=interaction.prompt({type:"manual_code",...prompt,signal:manualAbort.signal}).then(input=>(callback?.cancel(),input)).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),callback?.cancel()});try{let value=await callback?.wait();if(manualError)throw manualError;if(value!==void 0)return{type:"callback",value};let input=await manual;if(manualError)throw manualError;return{type:"manual",input:input??""}}finally{manualAbort.abort()}}var CANCEL_MESSAGE="Login cancelled",TIMEOUT_MESSAGE="Device flow timed out",SLOW_DOWN_TIMEOUT_MESSAGE="Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";function abortableSleep(ms,signal,cancelMessage){return new Promise((resolve,reject)=>{if(signal.aborted){reject(new Error(cancelMessage));return}let onAbort=()=>{clearTimeout(timeout),reject(new Error(cancelMessage))},timeout=setTimeout(()=>{signal.removeEventListener("abort",onAbort),resolve()},ms);signal.addEventListener("abort",onAbort,{once:!0})})}async function pollOAuthDeviceCodeFlow(options){let deadline=typeof options.expiresInSeconds=="number"?Date.now()+options.expiresInSeconds*1e3:Number.POSITIVE_INFINITY,intervalMs=Math.max(1e3,Math.floor((options.intervalSeconds??5)*1e3)),slowDownResponses=0;if(options.waitBeforeFirstPoll){let remainingMs=deadline-Date.now();remainingMs>0&&await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}for(;Date.now()<deadline;){if(options.signal.aborted)throw new Error(CANCEL_MESSAGE);let result=await options.poll();if(result.status==="complete")return result.value;if(result.status==="failed")throw new Error(result.message);result.status==="slow_down"&&(slowDownResponses+=1,intervalMs=typeof result.intervalSeconds=="number"&&Number.isFinite(result.intervalSeconds)&&result.intervalSeconds>0?Math.max(1e3,Math.floor(result.intervalSeconds*1e3)):Math.max(1e3,intervalMs+5e3));let remainingMs=deadline-Date.now();if(remainingMs<=0)break;await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}throw new Error(slowDownResponses>0?SLOW_DOWN_TIMEOUT_MESSAGE:TIMEOUT_MESSAGE)}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var _randomBytes=null;typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&import("node:crypto").then(m=>{_randomBytes=m.randomBytes});var CLIENT_ID="app_EMoamEEZ73f0CkXaXp7hrann",AUTH_BASE_URL="https://auth.openai.com",AUTHORIZE_URL=`${AUTH_BASE_URL}/oauth/authorize`,TOKEN_URL=`${AUTH_BASE_URL}/oauth/token`,REDIRECT_URI="http://localhost:1455/auth/callback",DEVICE_USER_CODE_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`,DEVICE_TOKEN_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/token`,DEVICE_VERIFICATION_URI=`${AUTH_BASE_URL}/codex/device`,DEVICE_REDIRECT_URI=`${AUTH_BASE_URL}/deviceauth/callback`,DEVICE_CODE_TIMEOUT_SECONDS=900,OPENAI_CODEX_BROWSER_LOGIN_METHOD="browser",OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD="device_code",SCOPE="openid profile email offline_access",JWT_CLAIM_PATH="https://api.openai.com/auth";function getCallbackHost(){return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1"}function createState(){if(!_randomBytes)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");return _randomBytes(16).toString("hex")}function parseAuthorizationInput(input){let value=input.trim();if(!value)return{};try{let url=new URL(value);return{code:url.searchParams.get("code")??void 0,state:url.searchParams.get("state")??void 0}}catch{}if(value.includes("#")){let[code,state]=value.split("#",2);return{code,state}}if(value.includes("code=")){let params=new URLSearchParams(value);return{code:params.get("code")??void 0,state:params.get("state")??void 0}}return{code:value}}function decodeJwt(token){try{let parts=token.split(".");if(parts.length!==3)return null;let payload=parts[1]??"",decoded=atob(payload);return JSON.parse(decoded)}catch{return null}}async function fetchWithLoginCancellation(input,init){try{return await fetch(input,init)}catch(error){throw init.signal?.aborted?new Error("Login cancelled"):error}}async function readTokenResponse(response,operation){if(!response.ok){let text=await response.text().catch(()=>"");throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text||response.statusText}`)}let json=await response.json();if(!json?.access_token||!json.refresh_token||typeof json.expires_in!="number")throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);return{access:json.access_token,refresh:json.refresh_token,expires:Date.now()+json.expires_in*1e3}}async function exchangeAuthorizationCode(code,verifier,redirectUri,signal){let response=await fetchWithLoginCancellation(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:CLIENT_ID,code,code_verifier:verifier,redirect_uri:redirectUri}),signal});return readTokenResponse(response,"exchange")}async function refreshAccessToken(refreshToken,signal){let response;try{response=await fetch(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:refreshToken,client_id:CLIENT_ID}),signal})}catch(error){throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error?error.message:String(error)}`)}return readTokenResponse(response,"refresh")}async function startOpenAICodexDeviceAuth(signal){let response=await fetchWithLoginCancellation(DEVICE_USER_CODE_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:CLIENT_ID}),signal});if(!response.ok){if(response.status===404)throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");let responseBody=await response.text().catch(()=>"");throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`)}let json=await response.json(),intervalSeconds=typeof json?.interval=="string"?Number(json.interval.trim()):json?.interval;if(!json?.device_auth_id||!json.user_code||typeof intervalSeconds!="number"||!Number.isFinite(intervalSeconds)||intervalSeconds<0)throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);return{deviceAuthId:json.device_auth_id,userCode:json.user_code,intervalSeconds}}async function pollOpenAICodexDeviceAuth(device,signal){return pollOAuthDeviceCodeFlow({intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS,signal,poll:async()=>{let response=await fetchWithLoginCancellation(DEVICE_TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:device.deviceAuthId,user_code:device.userCode}),signal});if(response.ok){let json=await response.json();return!json?.authorization_code||!json.code_verifier?{status:"failed",message:`Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`}:{status:"complete",value:{authorizationCode:json.authorization_code,codeVerifier:json.code_verifier}}}if(response.status===403||response.status===404)return{status:"pending"};let responseBody=await response.text().catch(()=>""),errorCode;try{let error=JSON.parse(responseBody)?.error;errorCode=typeof error=="object"?error?.code:error}catch{}return errorCode==="deviceauth_authorization_pending"?{status:"pending"}:errorCode==="slow_down"?{status:"slow_down"}:{status:"failed",message:`OpenAI Codex device auth failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`}}})}async function createAuthorizationFlow(originator="pi"){let{verifier,challenge}=await generatePKCE(),state=createState(),url=new URL(AUTHORIZE_URL);return url.searchParams.set("response_type","code"),url.searchParams.set("client_id",CLIENT_ID),url.searchParams.set("redirect_uri",REDIRECT_URI),url.searchParams.set("scope",SCOPE),url.searchParams.set("code_challenge",challenge),url.searchParams.set("code_challenge_method","S256"),url.searchParams.set("state",state),url.searchParams.set("id_token_add_organizations","true"),url.searchParams.set("codex_cli_simplified_flow","true"),url.searchParams.set("originator",originator),{verifier,state,url:url.toString()}}function getAccountId(accessToken){let accountId=decodeJwt(accessToken)?.[JWT_CLAIM_PATH]?.chatgpt_account_id;return typeof accountId=="string"&&accountId.length>0?accountId:null}function credentialsFromToken(token){let accountId=getAccountId(token.access);if(!accountId)throw new Error("Failed to extract accountId from token");return{type:"oauth",access:token.access,refresh:token.refresh,expires:token.expires,accountId}}async function exchangeAuthorizationCodeForCredentials(code,verifier,redirectUri,signal){return credentialsFromToken(await exchangeAuthorizationCode(code,verifier,redirectUri,signal))}async function loginOpenAICodexDeviceCode(interaction){let device=await startOpenAICodexDeviceAuth(interaction.signal);interaction.notify({type:"device_code",userCode:device.userCode,verificationUri:DEVICE_VERIFICATION_URI,intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS});let code=await pollOpenAICodexDeviceAuth(device,interaction.signal);return exchangeAuthorizationCodeForCredentials(code.authorizationCode,code.codeVerifier,DEVICE_REDIRECT_URI,interaction.signal)}async function loginOpenAICodex(interaction){let{verifier,state,url}=await createAuthorizationFlow(),callback=await startOAuthCallbackServer({providerName:"OpenAI",host:getCallbackHost(),port:1455,path:"/auth/callback",state,complete:async code=>code,signal:interaction.signal}).catch(()=>{});interaction.notify({type:"auth_url",url,instructions:"A browser window should open. Complete login to finish."});try{let result=await waitForCallbackOrManualInput(interaction,callback,{message:"Complete login in your browser, or paste the authorization code / redirect URL here:",placeholder:REDIRECT_URI}),code;if(result.type==="callback")code=result.value;else{let parsed=parseAuthorizationInput(result.input);if(parsed.state&&parsed.state!==state)throw new Error("State mismatch");code=parsed.code}if(!code)throw new Error("Missing authorization code");return await exchangeAuthorizationCodeForCredentials(code,verifier,REDIRECT_URI,interaction.signal)}finally{callback?.close()}}async function refreshOpenAICodexToken(refreshToken,signal){return credentialsFromToken(await refreshAccessToken(refreshToken,signal))}var openaiCodexOAuth={name:"OpenAI (ChatGPT Plus/Pro)",isSubscription:!0,async login(interaction){let method=await interaction.prompt({type:"select",message:"Select OpenAI Codex login method:",options:[{id:OPENAI_CODEX_BROWSER_LOGIN_METHOD,label:"Browser login (default)"},{id:OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,label:"Device code login (headless)"}]});if(method===OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD)return loginOpenAICodexDeviceCode(interaction);if(method!==OPENAI_CODEX_BROWSER_LOGIN_METHOD)throw new Error(`Unknown OpenAI Codex login method: ${method}`);return loginOpenAICodex(interaction)},refresh:(credential,signal)=>refreshOpenAICodexToken(credential.refresh,signal),async toAuth(credential){return{apiKey:credential.access}}};export{openaiCodexOAuth};
|
|
82
|
+
</html>`}function oauthSuccessHtml(message){return renderPage({title:"Authentication successful",heading:"Authentication successful",message})}function oauthErrorHtml(message,details){return renderPage({title:"Authentication failed",heading:"Authentication failed",message,details})}function sendPage(response,status,html){response.writeHead(status,{"content-type":"text/html; charset=utf-8","cache-control":"no-store"}),response.end(html)}async function startOAuthCallbackServer(options){let{providerName,signal}=options;if(signal?.aborted)throw new Error("Login cancelled");let resolveWait=()=>{},rejectWait=()=>{},waitPromise=new Promise((resolve,reject)=>{resolveWait=resolve,rejectWait=reject});waitPromise.catch(()=>{});let claimed=!1,settled=!1,timer,onAbort=()=>finish({error:new Error("Login cancelled")}),finish=result=>{settled||(settled=!0,timer&&clearTimeout(timer),signal?.removeEventListener("abort",onAbort),"error"in result?rejectWait(result.error):resolveWait(result.value))},server=createServer((request,response)=>{(async()=>{let url=new URL(request.url??"/","http://localhost");if(request.method!=="GET"||url.pathname!==options.path){sendPage(response,404,oauthErrorHtml("Callback route not found."));return}if(options.state!==void 0&&url.searchParams.get("state")!==options.state){sendPage(response,400,oauthErrorHtml("State mismatch."));return}if(claimed||settled){sendPage(response,409,oauthErrorHtml("This sign-in has already been handled."));return}let error=url.searchParams.get("error");if(error){let description=url.searchParams.get("error_description")??error;sendPage(response,400,oauthErrorHtml(`${providerName} authorization failed.`,description)),finish({error:new Error(`${providerName} authorization failed: ${description}`)});return}let code=url.searchParams.get("code");if(!code){sendPage(response,400,oauthErrorHtml("Missing authorization code."));return}claimed=!0;try{let value=await options.complete(code);sendPage(response,200,oauthSuccessHtml(`Signed in to ${providerName}. You may now close this page.`)),finish({value})}catch(error2){let failure=error2 instanceof Error?error2:new Error(String(error2));sendPage(response,502,oauthErrorHtml(`${providerName} sign-in failed.`,failure.message)),finish({error:failure})}})()});await new Promise((resolve,reject)=>{server.once("error",reject),server.listen(options.port,options.host,()=>{server.off("error",reject),resolve()})});let address=server.address();if(!address||typeof address=="string")throw server.close(),new Error("OAuth callback server did not bind to TCP");server.on("error",error=>finish({error})),signal?.addEventListener("abort",onAbort,{once:!0}),options.timeoutMs!==void 0&&(timer=setTimeout(()=>finish({error:new Error(`${providerName} sign-in timed out`)}),options.timeoutMs));let redirectHost=options.redirectHost??options.host;return{redirectUri:`http://${redirectHost.includes(":")?`[${redirectHost}]`:redirectHost}:${address.port}${options.path}`,wait:()=>waitPromise,cancel:()=>{claimed||finish({value:void 0})},close:()=>{finish({error:new Error("OAuth callback server closed")}),server.close()}}}async function waitForCallbackOrManualInput(interaction,callback,prompt){let manualAbort=new AbortController,manualError,manual=interaction.prompt({type:"manual_code",...prompt,signal:manualAbort.signal}).then(input=>(callback?.cancel(),input)).catch(error=>{manualError=error instanceof Error?error:new Error(String(error)),callback?.cancel()});try{let value=await callback?.wait();if(manualError)throw manualError;if(value!==void 0)return{type:"callback",value};let input=await manual;if(manualError)throw manualError;return{type:"manual",input:input??""}}finally{manualAbort.abort()}}var CANCEL_MESSAGE="Login cancelled",TIMEOUT_MESSAGE="Device flow timed out",SLOW_DOWN_TIMEOUT_MESSAGE="Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";function abortableSleep(ms,signal,cancelMessage){return new Promise((resolve,reject)=>{if(signal.aborted){reject(new Error(cancelMessage));return}let onAbort=()=>{clearTimeout(timeout),reject(new Error(cancelMessage))},timeout=setTimeout(()=>{signal.removeEventListener("abort",onAbort),resolve()},ms);signal.addEventListener("abort",onAbort,{once:!0})})}async function pollOAuthDeviceCodeFlow(options){let deadline=typeof options.expiresInSeconds=="number"?Date.now()+options.expiresInSeconds*1e3:Number.POSITIVE_INFINITY,intervalMs=Math.max(1e3,Math.floor((options.intervalSeconds??5)*1e3)),slowDownResponses=0;if(options.waitBeforeFirstPoll){let remainingMs=deadline-Date.now();remainingMs>0&&await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}for(;Date.now()<deadline;){if(options.signal.aborted)throw new Error(CANCEL_MESSAGE);let result=await options.poll();if(result.status==="complete")return result.value;if(result.status==="failed")throw new Error(result.message);result.status==="slow_down"&&(slowDownResponses+=1,intervalMs=typeof result.intervalSeconds=="number"&&Number.isFinite(result.intervalSeconds)&&result.intervalSeconds>0?Math.max(1e3,Math.floor(result.intervalSeconds*1e3)):Math.max(1e3,intervalMs+5e3));let remainingMs=deadline-Date.now();if(remainingMs<=0)break;await abortableSleep(Math.min(intervalMs,remainingMs),options.signal,CANCEL_MESSAGE)}throw new Error(slowDownResponses>0?SLOW_DOWN_TIMEOUT_MESSAGE:TIMEOUT_MESSAGE)}function base64urlEncode(bytes){let binary="";for(let byte of bytes)binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function generatePKCE(){let verifierBytes=new Uint8Array(32);crypto.getRandomValues(verifierBytes);let verifier=base64urlEncode(verifierBytes),data=new TextEncoder().encode(verifier),hashBuffer=await crypto.subtle.digest("SHA-256",data),challenge=base64urlEncode(new Uint8Array(hashBuffer));return{verifier,challenge}}var _randomBytes=null;typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&import("node:crypto").then(m=>{_randomBytes=m.randomBytes});var CLIENT_ID="app_EMoamEEZ73f0CkXaXp7hrann",AUTH_BASE_URL="https://auth.openai.com",AUTHORIZE_URL=`${AUTH_BASE_URL}/oauth/authorize`,TOKEN_URL=`${AUTH_BASE_URL}/oauth/token`,REDIRECT_URI="http://localhost:1455/auth/callback",DEVICE_USER_CODE_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/usercode`,DEVICE_TOKEN_URL=`${AUTH_BASE_URL}/api/accounts/deviceauth/token`,DEVICE_VERIFICATION_URI=`${AUTH_BASE_URL}/codex/device`,DEVICE_REDIRECT_URI=`${AUTH_BASE_URL}/deviceauth/callback`,DEVICE_CODE_TIMEOUT_SECONDS=900,OPENAI_CODEX_BROWSER_LOGIN_METHOD="browser",OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD="device_code",SCOPE="openid profile email offline_access",JWT_CLAIM_PATH="https://api.openai.com/auth";function getCallbackHost(){return getProviderEnvValue("PI_OAUTH_CALLBACK_HOST")||"127.0.0.1"}function createState(){if(!_randomBytes)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");return _randomBytes(16).toString("hex")}function parseAuthorizationInput(input){let value=input.trim();if(!value)return{};try{let url=new URL(value);return{code:url.searchParams.get("code")??void 0,state:url.searchParams.get("state")??void 0}}catch{}if(value.includes("#")){let[code,state]=value.split("#",2);return{code,state}}if(value.includes("code=")){let params=new URLSearchParams(value);return{code:params.get("code")??void 0,state:params.get("state")??void 0}}return{code:value}}function decodeJwt(token){try{let parts=token.split(".");if(parts.length!==3)return null;let payload=parts[1]??"",decoded=atob(payload);return JSON.parse(decoded)}catch{return null}}async function fetchWithLoginCancellation(input,init){try{return await fetch(input,init)}catch(error){throw init.signal?.aborted?new Error("Login cancelled"):error}}async function readTokenResponse(response,operation){if(!response.ok){let text=await response.text().catch(()=>"");throw new Error(`OpenAI Codex token ${operation} failed (${response.status}): ${text||response.statusText}`)}let json=await response.json();if(!json?.access_token||!json.refresh_token||typeof json.expires_in!="number")throw new Error(`OpenAI Codex token ${operation} response missing fields: ${JSON.stringify(json)}`);return{access:json.access_token,refresh:json.refresh_token,expires:Date.now()+json.expires_in*1e3}}async function exchangeAuthorizationCode(code,verifier,redirectUri,signal){let response=await fetchWithLoginCancellation(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:CLIENT_ID,code,code_verifier:verifier,redirect_uri:redirectUri}),signal});return readTokenResponse(response,"exchange")}async function refreshAccessToken(refreshToken,signal){let response;try{response=await fetch(TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:refreshToken,client_id:CLIENT_ID}),signal})}catch(error){throw new Error(`OpenAI Codex token refresh error: ${error instanceof Error?error.message:String(error)}`)}return readTokenResponse(response,"refresh")}async function startOpenAICodexDeviceAuth(signal){let response=await fetchWithLoginCancellation(DEVICE_USER_CODE_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:CLIENT_ID}),signal});if(!response.ok){if(response.status===404)throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");let responseBody=await response.text().catch(()=>"");throw new Error(`OpenAI Codex device code request failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`)}let json=await response.json(),intervalSeconds=typeof json?.interval=="string"?Number(json.interval.trim()):json?.interval;if(!json?.device_auth_id||!json.user_code||typeof intervalSeconds!="number"||!Number.isFinite(intervalSeconds)||intervalSeconds<0)throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(json)}`);return{deviceAuthId:json.device_auth_id,userCode:json.user_code,intervalSeconds}}async function pollOpenAICodexDeviceAuth(device,signal){return pollOAuthDeviceCodeFlow({intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS,signal,poll:async()=>{let response=await fetchWithLoginCancellation(DEVICE_TOKEN_URL,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:device.deviceAuthId,user_code:device.userCode}),signal});if(response.ok){let json=await response.json();return!json?.authorization_code||!json.code_verifier?{status:"failed",message:`Invalid OpenAI Codex device auth token response: ${JSON.stringify(json)}`}:{status:"complete",value:{authorizationCode:json.authorization_code,codeVerifier:json.code_verifier}}}if(response.status===403||response.status===404)return{status:"pending"};let responseBody=await response.text().catch(()=>""),errorCode;try{let error=JSON.parse(responseBody)?.error;errorCode=typeof error=="object"?error?.code:error}catch{}return errorCode==="deviceauth_authorization_pending"?{status:"pending"}:errorCode==="slow_down"?{status:"slow_down"}:{status:"failed",message:`OpenAI Codex device auth failed with status ${response.status}${responseBody?`: ${responseBody}`:""}`}}})}async function createAuthorizationFlow(originator="pi"){let{verifier,challenge}=await generatePKCE(),state=createState(),url=new URL(AUTHORIZE_URL);return url.searchParams.set("response_type","code"),url.searchParams.set("client_id",CLIENT_ID),url.searchParams.set("redirect_uri",REDIRECT_URI),url.searchParams.set("scope",SCOPE),url.searchParams.set("code_challenge",challenge),url.searchParams.set("code_challenge_method","S256"),url.searchParams.set("state",state),url.searchParams.set("id_token_add_organizations","true"),url.searchParams.set("codex_cli_simplified_flow","true"),url.searchParams.set("originator",originator),{verifier,state,url:url.toString()}}function getAccountId(accessToken){let accountId=decodeJwt(accessToken)?.[JWT_CLAIM_PATH]?.chatgpt_account_id;return typeof accountId=="string"&&accountId.length>0?accountId:null}function credentialsFromToken(token){let accountId=getAccountId(token.access);if(!accountId)throw new Error("Failed to extract accountId from token");return{type:"oauth",access:token.access,refresh:token.refresh,expires:token.expires,accountId}}async function exchangeAuthorizationCodeForCredentials(code,verifier,redirectUri,signal){return credentialsFromToken(await exchangeAuthorizationCode(code,verifier,redirectUri,signal))}async function loginOpenAICodexDeviceCode(interaction){let device=await startOpenAICodexDeviceAuth(interaction.signal);interaction.notify({type:"device_code",userCode:device.userCode,verificationUri:DEVICE_VERIFICATION_URI,intervalSeconds:device.intervalSeconds,expiresInSeconds:DEVICE_CODE_TIMEOUT_SECONDS});let code=await pollOpenAICodexDeviceAuth(device,interaction.signal);return exchangeAuthorizationCodeForCredentials(code.authorizationCode,code.codeVerifier,DEVICE_REDIRECT_URI,interaction.signal)}async function loginOpenAICodex(interaction,options){let{verifier,state,url}=await createAuthorizationFlow(options?.agentName),callback=await startOAuthCallbackServer({providerName:"OpenAI",host:getCallbackHost(),port:1455,path:"/auth/callback",state,complete:async code=>code,signal:interaction.signal}).catch(()=>{});interaction.notify({type:"auth_url",url,instructions:"A browser window should open. Complete login to finish."});try{let result=await waitForCallbackOrManualInput(interaction,callback,{message:"Complete login in your browser, or paste the authorization code / redirect URL here:",placeholder:REDIRECT_URI}),code;if(result.type==="callback")code=result.value;else{let parsed=parseAuthorizationInput(result.input);if(parsed.state&&parsed.state!==state)throw new Error("State mismatch");code=parsed.code}if(!code)throw new Error("Missing authorization code");return await exchangeAuthorizationCodeForCredentials(code,verifier,REDIRECT_URI,interaction.signal)}finally{callback?.close()}}async function refreshOpenAICodexToken(refreshToken,signal){return credentialsFromToken(await refreshAccessToken(refreshToken,signal))}var openaiCodexOAuth={name:"OpenAI (ChatGPT Plus/Pro)",isSubscription:!0,async login(interaction,options){let method=await interaction.prompt({type:"select",message:"Select OpenAI Codex login method:",options:[{id:OPENAI_CODEX_BROWSER_LOGIN_METHOD,label:"Browser login (default)"},{id:OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,label:"Device code login (headless)"}]});if(method===OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD)return loginOpenAICodexDeviceCode(interaction);if(method!==OPENAI_CODEX_BROWSER_LOGIN_METHOD)throw new Error(`Unknown OpenAI Codex login method: ${method}`);return loginOpenAICodex(interaction,options)},refresh:(credential,signal)=>refreshOpenAICodexToken(credential.refresh,signal),async toAuth(credential){return{apiKey:credential.access}}};export{openaiCodexOAuth};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-plus",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.34",
|
|
4
4
|
"description": "pi coding agent with the pi-plus override layer (pipi CLI)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"CHANGELOG.md"
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@earendil-works/chord": "^1.0
|
|
25
|
-
"@earendil-works/pi-tui": "^1.0
|
|
24
|
+
"@earendil-works/chord": "^1.1.0",
|
|
25
|
+
"@earendil-works/pi-tui": "^1.1.0",
|
|
26
26
|
"@silvia-odwyer/photon-node": "0.3.4",
|
|
27
27
|
"jiti": "2.7.0"
|
|
28
28
|
},
|
|
@@ -32,5 +32,9 @@
|
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "https://github.com/pi-plus-org/pi-plus"
|
|
38
|
+
},
|
|
35
39
|
"license": "MIT"
|
|
36
40
|
}
|