@openadapter/koda-ai 1.0.0-beta.3
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/README.md +1385 -0
- package/dist/api-registry.d.ts +19 -0
- package/dist/api-registry.js +1 -0
- package/dist/bedrock-provider.d.ts +4 -0
- package/dist/bedrock-provider.js +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +24 -0
- package/dist/env-api-keys.d.ts +17 -0
- package/dist/env-api-keys.js +1 -0
- package/dist/image-models.d.ts +9 -0
- package/dist/image-models.generated.d.ts +454 -0
- package/dist/image-models.generated.js +1 -0
- package/dist/image-models.js +1 -0
- package/dist/images-api-registry.d.ts +13 -0
- package/dist/images-api-registry.js +1 -0
- package/dist/images.d.ts +3 -0
- package/dist/images.js +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.js +1 -0
- package/dist/models.d.ts +17 -0
- package/dist/models.generated.d.ts +18136 -0
- package/dist/models.generated.js +1 -0
- package/dist/models.js +1 -0
- package/dist/oauth.d.ts +1 -0
- package/dist/oauth.js +1 -0
- package/dist/providers/amazon-bedrock.d.ts +37 -0
- package/dist/providers/amazon-bedrock.js +1 -0
- package/dist/providers/anthropic.d.ts +70 -0
- package/dist/providers/anthropic.js +5 -0
- package/dist/providers/azure-openai-responses.d.ts +14 -0
- package/dist/providers/azure-openai-responses.js +1 -0
- package/dist/providers/cloudflare.d.ts +12 -0
- package/dist/providers/cloudflare.js +1 -0
- package/dist/providers/faux.d.ts +55 -0
- package/dist/providers/faux.js +6 -0
- package/dist/providers/github-copilot-headers.d.ts +7 -0
- package/dist/providers/github-copilot-headers.js +1 -0
- package/dist/providers/google-shared.d.ts +69 -0
- package/dist/providers/google-shared.js +2 -0
- package/dist/providers/google-vertex.d.ts +14 -0
- package/dist/providers/google-vertex.js +1 -0
- package/dist/providers/google.d.ts +12 -0
- package/dist/providers/google.js +1 -0
- package/dist/providers/images/openrouter.d.ts +2 -0
- package/dist/providers/images/openrouter.js +1 -0
- package/dist/providers/images/register-builtins.d.ts +3 -0
- package/dist/providers/images/register-builtins.js +1 -0
- package/dist/providers/mistral.d.ts +24 -0
- package/dist/providers/mistral.js +3 -0
- package/dist/providers/openai-codex-responses.d.ts +29 -0
- package/dist/providers/openai-codex-responses.js +7 -0
- package/dist/providers/openai-completions.d.ts +18 -0
- package/dist/providers/openai-completions.js +6 -0
- package/dist/providers/openai-prompt-cache.d.ts +2 -0
- package/dist/providers/openai-prompt-cache.js +1 -0
- package/dist/providers/openai-responses-shared.d.ts +17 -0
- package/dist/providers/openai-responses-shared.js +12 -0
- package/dist/providers/openai-responses.d.ts +12 -0
- package/dist/providers/openai-responses.js +1 -0
- package/dist/providers/register-builtins.d.ts +34 -0
- package/dist/providers/register-builtins.js +1 -0
- package/dist/providers/simple-options.d.ts +7 -0
- package/dist/providers/simple-options.js +1 -0
- package/dist/providers/transform-messages.d.ts +7 -0
- package/dist/providers/transform-messages.js +1 -0
- package/dist/session-resources.d.ts +3 -0
- package/dist/session-resources.js +1 -0
- package/dist/stream.d.ts +7 -0
- package/dist/stream.js +1 -0
- package/dist/types.d.ts +513 -0
- package/dist/types.js +0 -0
- package/dist/utils/abort-signals.d.ts +5 -0
- package/dist/utils/abort-signals.js +1 -0
- package/dist/utils/diagnostics.d.ts +18 -0
- package/dist/utils/diagnostics.js +1 -0
- package/dist/utils/event-stream.d.ts +20 -0
- package/dist/utils/event-stream.js +1 -0
- package/dist/utils/hash.d.ts +2 -0
- package/dist/utils/hash.js +1 -0
- package/dist/utils/headers.d.ts +1 -0
- package/dist/utils/headers.js +1 -0
- package/dist/utils/json-parse.d.ts +15 -0
- package/dist/utils/json-parse.js +2 -0
- package/dist/utils/node-http-proxy.d.ts +9 -0
- package/dist/utils/node-http-proxy.js +1 -0
- package/dist/utils/oauth/anthropic.d.ts +24 -0
- package/dist/utils/oauth/anthropic.js +1 -0
- package/dist/utils/oauth/device-code.d.ts +20 -0
- package/dist/utils/oauth/device-code.js +1 -0
- package/dist/utils/oauth/github-copilot.d.ts +29 -0
- package/dist/utils/oauth/github-copilot.js +1 -0
- package/dist/utils/oauth/index.d.ts +57 -0
- package/dist/utils/oauth/index.js +1 -0
- package/dist/utils/oauth/oauth-page.d.ts +2 -0
- package/dist/utils/oauth/oauth-page.js +74 -0
- package/dist/utils/oauth/openai-codex.d.ts +42 -0
- package/dist/utils/oauth/openai-codex.js +1 -0
- package/dist/utils/oauth/pkce.d.ts +12 -0
- package/dist/utils/oauth/pkce.js +1 -0
- package/dist/utils/oauth/types.d.ts +63 -0
- package/dist/utils/oauth/types.js +0 -0
- package/dist/utils/overflow.d.ts +56 -0
- package/dist/utils/overflow.js +1 -0
- package/dist/utils/sanitize-unicode.d.ts +21 -0
- package/dist/utils/sanitize-unicode.js +1 -0
- package/dist/utils/typebox-helpers.d.ts +16 -0
- package/dist/utils/typebox-helpers.js +1 -0
- package/dist/utils/validation.d.ts +17 -0
- package/dist/utils/validation.js +6 -0
- package/package.json +114 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=Object.defineProperty;var u=(e,t)=>S(e,"name",{value:t,configurable:!0});import{oauthErrorHtml as y,oauthSuccessHtml as $}from"./oauth-page.js";import{generatePKCE as x}from"./pkce.js";let g=null,_=null;const k=u(e=>atob(e),"decode"),A=k("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl"),O="https://claude.ai/oauth/authorize",p="https://platform.claude.com/v1/oauth/token",v=process.env.KODA_OAUTH_CALLBACK_HOST||"127.0.0.1",E=53692,P="/callback",f=`http://localhost:${E}${P}`,L="org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";async function H(){if(g)return g;if(!_){if(typeof process>"u"||!process.versions?.node&&!process.versions?.bun)throw new Error("Anthropic OAuth is only available in Node.js environments");_=import("node:http").then(e=>({createServer:e.createServer}))}return g=await _,g}u(H,"getNodeApis");function C(e){const t=e.trim();if(!t)return{};try{const r=new URL(t);return{code:r.searchParams.get("code")??void 0,state:r.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[r,o]=t.split("#",2);return{code:r,state:o}}if(t.includes("code=")){const r=new URLSearchParams(t);return{code:r.get("code")??void 0,state:r.get("state")??void 0}}return{code:t}}u(C,"parseAuthorizationInput");function m(e){if(e instanceof Error){const t=[`${e.name}: ${e.message}`],r=e;return r.code&&t.push(`code=${r.code}`),typeof r.errno<"u"&&t.push(`errno=${String(r.errno)}`),typeof e.cause<"u"&&t.push(`cause=${m(e.cause)}`),e.stack&&t.push(`stack=${e.stack}`),t.join("; ")}return String(e)}u(m,"formatErrorDetails");async function I(e){const{createServer:t}=await H();return new Promise((r,o)=>{let s;const c=new Promise(h=>{let n=!1;s=u(i=>{n||(n=!0,h(i))},"settleWait")}),d=t((h,n)=>{try{const i=new URL(h.url||"","http://localhost");if(i.pathname!==P){n.writeHead(404,{"Content-Type":"text/html; charset=utf-8"}),n.end(y("Callback route not found."));return}const w=i.searchParams.get("code"),l=i.searchParams.get("state"),a=i.searchParams.get("error");if(a){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end(y("Anthropic authentication did not complete.",`Error: ${a}`));return}if(!w||!l){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end(y("Missing code or state parameter."));return}if(l!==e){n.writeHead(400,{"Content-Type":"text/html; charset=utf-8"}),n.end(y("State mismatch."));return}n.writeHead(200,{"Content-Type":"text/html; charset=utf-8"}),n.end($("Anthropic authentication completed. You can close this window.")),s?.({code:w,state:l})}catch{n.writeHead(500,{"Content-Type":"text/plain; charset=utf-8"}),n.end("Internal error")}});d.on("error",h=>{o(h)}),d.listen(E,v,()=>{r({server:d,redirectUri:f,cancelWait:u(()=>{s?.(null)},"cancelWait"),waitForCode:u(()=>c,"waitForCode")})})})}u(I,"startCallbackServer");async function T(e,t){const r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify(t),signal:AbortSignal.timeout(3e4)}),o=await r.text();if(!r.ok)throw new Error(`HTTP request failed. status=${r.status}; url=${e}; body=${o}`);return o}u(T,"postJson");async function R(e,t,r,o){let s;try{s=await T(p,{grant_type:"authorization_code",client_id:A,code:e,state:t,redirect_uri:o,code_verifier:r})}catch(d){throw new Error(`Token exchange request failed. url=${p}; redirect_uri=${o}; response_type=authorization_code; details=${m(d)}`)}let c;try{c=JSON.parse(s)}catch(d){throw new Error(`Token exchange returned invalid JSON. url=${p}; body=${s}; details=${m(d)}`)}return{refresh:c.refresh_token,access:c.access_token,expires:Date.now()+c.expires_in*1e3-300*1e3}}u(R,"exchangeAuthorizationCode");async function N(e){const{verifier:t,challenge:r}=await x(),o=await I(t);let s,c,d=f;try{const h=new URLSearchParams({code:"true",client_id:A,response_type:"code",redirect_uri:f,scope:L,code_challenge:r,code_challenge_method:"S256",state:t});if(e.onAuth({url:`${O}?${h.toString()}`,instructions:"Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."}),e.onManualCodeInput){let n,i;const w=e.onManualCodeInput().then(a=>{n=a,o.cancelWait()}).catch(a=>{i=a instanceof Error?a:new Error(String(a)),o.cancelWait()}),l=await o.waitForCode();if(i)throw i;if(l?.code)s=l.code,c=l.state,d=f;else if(n){const a=C(n);if(a.state&&a.state!==t)throw new Error("OAuth state mismatch");s=a.code,c=a.state??t}if(!s){if(await w,i)throw i;if(n){const a=C(n);if(a.state&&a.state!==t)throw new Error("OAuth state mismatch");s=a.code,c=a.state??t}}}else{const n=await o.waitForCode();n?.code&&(s=n.code,c=n.state,d=f)}if(!s){const n=await e.onPrompt({message:"Paste the authorization code or full redirect URL:",placeholder:f}),i=C(n);if(i.state&&i.state!==t)throw new Error("OAuth state mismatch");s=i.code,c=i.state??t}if(!s)throw new Error("Missing authorization code");if(!c)throw new Error("Missing OAuth state");return e.onProgress?.("Exchanging authorization code for tokens..."),R(s,c,t,d)}finally{o.server.close()}}u(N,"loginAnthropic");async function U(e){let t;try{t=await T(p,{grant_type:"refresh_token",client_id:A,refresh_token:e})}catch(o){throw new Error(`Anthropic token refresh request failed. url=${p}; details=${m(o)}`)}let r;try{r=JSON.parse(t)}catch(o){throw new Error(`Anthropic token refresh returned invalid JSON. url=${p}; body=${t}; details=${m(o)}`)}return{refresh:r.refresh_token,access:r.access_token,expires:Date.now()+r.expires_in*1e3-300*1e3}}u(U,"refreshAnthropicToken");const K={id:"anthropic",name:"Anthropic (Claude Pro/Max)",usesCallbackServer:!0,async login(e){return N({onAuth:e.onAuth,onPrompt:e.onPrompt,onProgress:e.onProgress,onManualCodeInput:e.onManualCodeInput})},async refreshToken(e){return U(e.refresh)},getApiKey(e){return e.access}};export{K as anthropicOAuthProvider,N as loginAnthropic,U as refreshAnthropicToken};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
type OAuthDeviceCodeIncompletePollResult = {
|
|
2
|
+
status: "pending";
|
|
3
|
+
} | {
|
|
4
|
+
status: "slow_down";
|
|
5
|
+
} | {
|
|
6
|
+
status: "failed";
|
|
7
|
+
message: string;
|
|
8
|
+
};
|
|
9
|
+
export type OAuthDeviceCodePollResult<T> = OAuthDeviceCodeIncompletePollResult | {
|
|
10
|
+
status: "complete";
|
|
11
|
+
value: T;
|
|
12
|
+
};
|
|
13
|
+
export type OAuthDeviceCodePollOptions<T> = {
|
|
14
|
+
intervalSeconds?: number;
|
|
15
|
+
expiresInSeconds?: number;
|
|
16
|
+
poll: () => Promise<OAuthDeviceCodePollResult<T>>;
|
|
17
|
+
signal?: AbortSignal;
|
|
18
|
+
};
|
|
19
|
+
export declare function pollOAuthDeviceCodeFlow<T>(options: OAuthDeviceCodePollOptions<T>): Promise<T>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=Object.defineProperty;var a=(e,t)=>c(e,"name",{value:t,configurable:!0});const i="Login cancelled",l="Device flow timed out",M="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.",I=1e3,S=5,N=5e3;function _(e,t,r){return new Promise((s,o)=>{if(t?.aborted){o(new Error(r));return}const n=a(()=>{clearTimeout(E),o(new Error(r))},"onAbort"),E=setTimeout(()=>{t?.removeEventListener("abort",n),s()},e);t?.addEventListener("abort",n,{once:!0})})}a(_,"abortableSleep");async function L(e){const t=typeof e.expiresInSeconds=="number"?Date.now()+e.expiresInSeconds*1e3:Number.POSITIVE_INFINITY;let r=Math.max(1e3,Math.floor((e.intervalSeconds??5)*1e3)),s=0;for(;Date.now()<t;){if(e.signal?.aborted)throw new Error(i);const o=await e.poll();if(o.status==="complete")return o.value;if(o.status==="failed")throw new Error(o.message);o.status==="slow_down"&&(s+=1,r=Math.max(1e3,r+5e3));const n=t-Date.now();if(n<=0)break;await _(Math.min(r,n),e.signal,i)}throw new Error(s>0?M:l)}a(L,"pollOAuthDeviceCodeFlow");export{L as pollOAuthDeviceCodeFlow};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Copilot OAuth flow
|
|
3
|
+
*/
|
|
4
|
+
import type { OAuthCredentials, OAuthDeviceCodeInfo, OAuthProviderInterface } from "./types.ts";
|
|
5
|
+
export declare function normalizeDomain(input: string): string | null;
|
|
6
|
+
export declare function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Refresh GitHub Copilot token
|
|
9
|
+
*/
|
|
10
|
+
export declare function refreshGitHubCopilotToken(refreshToken: string, enterpriseDomain?: string): Promise<OAuthCredentials>;
|
|
11
|
+
/**
|
|
12
|
+
* Login with GitHub Copilot OAuth (device code flow)
|
|
13
|
+
*
|
|
14
|
+
* @param options.onDeviceCode - Callback with URL and user code
|
|
15
|
+
* @param options.onPrompt - Callback to prompt user for input
|
|
16
|
+
* @param options.onProgress - Optional progress callback
|
|
17
|
+
* @param options.signal - Optional AbortSignal for cancellation
|
|
18
|
+
*/
|
|
19
|
+
export declare function loginGitHubCopilot(options: {
|
|
20
|
+
onDeviceCode: (info: OAuthDeviceCodeInfo) => void;
|
|
21
|
+
onPrompt: (prompt: {
|
|
22
|
+
message: string;
|
|
23
|
+
placeholder?: string;
|
|
24
|
+
allowEmpty?: boolean;
|
|
25
|
+
}) => Promise<string>;
|
|
26
|
+
onProgress?: (message: string) => void;
|
|
27
|
+
signal?: AbortSignal;
|
|
28
|
+
}): Promise<OAuthCredentials>;
|
|
29
|
+
export declare const githubCopilotOAuthProvider: OAuthProviderInterface;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var g=Object.defineProperty;var s=(e,t)=>g(e,"name",{value:t,configurable:!0});import{getModels as w}from"../../models.js";import{pollOAuthDeviceCodeFlow as y}from"./device-code.js";const v=s(e=>atob(e),"decode"),d=v("SXYxLmI1MDdhMDhjODdlY2ZlOTg="),u={"User-Agent":"GitHubCopilotChat/0.35.0","Editor-Version":"vscode/1.107.0","Editor-Plugin-Version":"copilot-chat/0.35.0","Copilot-Integration-Id":"vscode-chat"};function f(e){const t=e.trim();if(!t)return null;try{return(t.includes("://")?new URL(t):new URL(`https://${t}`)).hostname}catch{return null}}s(f,"normalizeDomain");function l(e){return{deviceCodeUrl:`https://${e}/login/device/code`,accessTokenUrl:`https://${e}/login/oauth/access_token`,copilotTokenUrl:`https://api.${e}/copilot_internal/v2/token`}}s(l,"getUrls");function b(e){const t=e.match(/proxy-ep=([^;]+)/);return t?`https://${t[1].replace(/^proxy\./,"api.")}`:null}s(b,"getBaseUrlFromToken");function h(e,t){if(e){const o=b(e);if(o)return o}return t?`https://copilot-api.${t}`:"https://api.individual.githubcopilot.com"}s(h,"getGitHubCopilotBaseUrl");async function p(e,t){const o=await fetch(e,t);if(!o.ok){const i=await o.text();throw new Error(`${o.status} ${o.statusText}: ${i}`)}return o.json()}s(p,"fetchJson");async function C(e){const t=l(e),o=await p(t.deviceCodeUrl,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded","User-Agent":"GitHubCopilotChat/0.35.0"},body:new URLSearchParams({client_id:d,scope:"read:user"})});if(!o||typeof o!="object")throw new Error("Invalid device code response");const i=o.device_code,n=o.user_code,r=o.verification_uri,c=o.interval,a=o.expires_in;if(typeof i!="string"||typeof n!="string"||typeof r!="string"||c!==void 0&&typeof c!="number"||typeof a!="number")throw new Error("Invalid device code response fields");return{device_code:i,user_code:n,verification_uri:r,interval:c,expires_in:a}}s(C,"startDeviceFlow");async function _(e,t,o){const i=l(e);return y({intervalSeconds:t.interval,expiresInSeconds:t.expires_in,signal:o,poll:s(async()=>{const n=await p(i.accessTokenUrl,{method:"POST",headers:{Accept:"application/json","Content-Type":"application/x-www-form-urlencoded","User-Agent":"GitHubCopilotChat/0.35.0"},body:new URLSearchParams({client_id:d,device_code:t.device_code,grant_type:"urn:ietf:params:oauth:grant-type:device_code"})});if(n&&typeof n=="object"&&typeof n.access_token=="string")return{status:"complete",value:n.access_token};if(n&&typeof n=="object"&&typeof n.error=="string"){const{error:r,error_description:c}=n;if(r==="authorization_pending")return{status:"pending"};if(r==="slow_down")return{status:"slow_down"};const a=c?`: ${c}`:"";return{status:"failed",message:`Device flow failed: ${r}${a}`}}return{status:"failed",message:"Invalid device token response"}},"poll")})}s(_,"pollForGitHubAccessToken");async function m(e,t){const i=l(t||"github.com"),n=await p(i.copilotTokenUrl,{headers:{Accept:"application/json",Authorization:`Bearer ${e}`,...u}});if(!n||typeof n!="object")throw new Error("Invalid Copilot token response");const r=n.token,c=n.expires_at;if(typeof r!="string"||typeof c!="number")throw new Error("Invalid Copilot token response fields");return{refresh:e,access:r,expires:c*1e3-300*1e3,enterpriseUrl:t}}s(m,"refreshGitHubCopilotToken");async function x(e,t,o){const n=`${h(e,o)}/models/${t}/policy`;try{return(await fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${e}`,...u,"openai-intent":"chat-policy","x-interaction-type":"chat-policy"},body:JSON.stringify({state:"enabled"})})).ok}catch{return!1}}s(x,"enableGitHubCopilotModel");async function U(e,t,o){const i=w("github-copilot");await Promise.all(i.map(async n=>{const r=await x(e,n.id,t);o?.(n.id,r)}))}s(U,"enableAllGitHubCopilotModels");async function T(e){const t=await e.onPrompt({message:"GitHub Enterprise URL/domain (blank for github.com)",placeholder:"company.ghe.com",allowEmpty:!0});if(e.signal?.aborted)throw new Error("Login cancelled");const o=t.trim(),i=f(t);if(o&&!i)throw new Error("Invalid GitHub Enterprise URL/domain");const n=i||"github.com",r=await C(n);e.onDeviceCode({userCode:r.user_code,verificationUri:r.verification_uri,intervalSeconds:r.interval,expiresInSeconds:r.expires_in});const c=await _(n,r,e.signal),a=await m(c,i??void 0);return e.onProgress?.("Enabling models..."),await U(a.access,i??void 0),a}s(T,"loginGitHubCopilot");const E={id:"github-copilot",name:"GitHub Copilot",async login(e){return T({onDeviceCode:e.onDeviceCode,onPrompt:e.onPrompt,onProgress:e.onProgress,signal:e.signal})},async refreshToken(e){const t=e;return m(t.refresh,t.enterpriseUrl)},getApiKey(e){return e.access},modifyModels(e,t){const o=t,i=o.enterpriseUrl?f(o.enterpriseUrl)??void 0:void 0,n=h(o.access,i);return e.map(r=>r.provider==="github-copilot"?{...r,baseUrl:n}:r)}};export{h as getGitHubCopilotBaseUrl,E as githubCopilotOAuthProvider,T as loginGitHubCopilot,f as normalizeDomain,m as refreshGitHubCopilotToken};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth credential management for AI providers.
|
|
3
|
+
*
|
|
4
|
+
* This module handles login, token refresh, and credential storage
|
|
5
|
+
* for OAuth-based providers:
|
|
6
|
+
* - Anthropic (Claude Pro/Max)
|
|
7
|
+
* - GitHub Copilot
|
|
8
|
+
*/
|
|
9
|
+
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.ts";
|
|
10
|
+
export * from "./device-code.ts";
|
|
11
|
+
export { getGitHubCopilotBaseUrl, githubCopilotOAuthProvider, loginGitHubCopilot, normalizeDomain, refreshGitHubCopilotToken, } from "./github-copilot.ts";
|
|
12
|
+
export { loginOpenAICodex, loginOpenAICodexDeviceCode, OPENAI_CODEX_BROWSER_LOGIN_METHOD, OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD, openaiCodexOAuthProvider, refreshOpenAICodexToken, } from "./openai-codex.ts";
|
|
13
|
+
export * from "./types.ts";
|
|
14
|
+
import type { OAuthCredentials, OAuthProviderId, OAuthProviderInfo, OAuthProviderInterface } from "./types.ts";
|
|
15
|
+
/**
|
|
16
|
+
* Get an OAuth provider by ID
|
|
17
|
+
*/
|
|
18
|
+
export declare function getOAuthProvider(id: OAuthProviderId): OAuthProviderInterface | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Register a custom OAuth provider
|
|
21
|
+
*/
|
|
22
|
+
export declare function registerOAuthProvider(provider: OAuthProviderInterface): void;
|
|
23
|
+
/**
|
|
24
|
+
* Unregister an OAuth provider.
|
|
25
|
+
*
|
|
26
|
+
* If the provider is built-in, restores the built-in implementation.
|
|
27
|
+
* Custom providers are removed completely.
|
|
28
|
+
*/
|
|
29
|
+
export declare function unregisterOAuthProvider(id: string): void;
|
|
30
|
+
/**
|
|
31
|
+
* Reset OAuth providers to built-ins.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resetOAuthProviders(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Get all registered OAuth providers
|
|
36
|
+
*/
|
|
37
|
+
export declare function getOAuthProviders(): OAuthProviderInterface[];
|
|
38
|
+
/**
|
|
39
|
+
* @deprecated Use getOAuthProviders() which returns OAuthProviderInterface[]
|
|
40
|
+
*/
|
|
41
|
+
export declare function getOAuthProviderInfoList(): OAuthProviderInfo[];
|
|
42
|
+
/**
|
|
43
|
+
* Refresh token for any OAuth provider.
|
|
44
|
+
* @deprecated Use getOAuthProvider(id).refreshToken() instead
|
|
45
|
+
*/
|
|
46
|
+
export declare function refreshOAuthToken(providerId: OAuthProviderId, credentials: OAuthCredentials): Promise<OAuthCredentials>;
|
|
47
|
+
/**
|
|
48
|
+
* Get API key for a provider from OAuth credentials.
|
|
49
|
+
* Automatically refreshes expired tokens.
|
|
50
|
+
*
|
|
51
|
+
* @returns API key string and updated credentials, or null if no credentials
|
|
52
|
+
* @throws Error if refresh fails
|
|
53
|
+
*/
|
|
54
|
+
export declare function getOAuthApiKey(providerId: OAuthProviderId, credentials: Record<string, OAuthCredentials>): Promise<{
|
|
55
|
+
newCredentials: OAuthCredentials;
|
|
56
|
+
apiKey: string;
|
|
57
|
+
} | null>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var f=Object.defineProperty;var e=(r,o)=>f(r,"name",{value:o,configurable:!0});import{anthropicOAuthProvider as T,loginAnthropic as k,refreshAnthropicToken as I}from"./anthropic.js";export*from"./device-code.js";import{getGitHubCopilotBaseUrl as H,githubCopilotOAuthProvider as G,loginGitHubCopilot as N,normalizeDomain as R,refreshGitHubCopilotToken as U}from"./github-copilot.js";import{loginOpenAICodex as B,loginOpenAICodexDeviceCode as K,OPENAI_CODEX_BROWSER_LOGIN_METHOD as M,OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD as $,openaiCodexOAuthProvider as S,refreshOpenAICodexToken as V}from"./openai-codex.js";export*from"./types.js";import{anthropicOAuthProvider as O}from"./anthropic.js";import{githubCopilotOAuthProvider as s}from"./github-copilot.js";import{openaiCodexOAuthProvider as a}from"./openai-codex.js";const u=[O,s,a],n=new Map(u.map(r=>[r.id,r]));function h(r){return n.get(r)}e(h,"getOAuthProvider");function P(r){n.set(r.id,r)}e(P,"registerOAuthProvider");function g(r){const o=u.find(t=>t.id===r);if(o){n.set(r,o);return}n.delete(r)}e(g,"unregisterOAuthProvider");function v(){n.clear();for(const r of u)n.set(r.id,r)}e(v,"resetOAuthProviders");function A(){return Array.from(n.values())}e(A,"getOAuthProviders");function C(){return A().map(r=>({id:r.id,name:r.name,available:!0}))}e(C,"getOAuthProviderInfoList");async function E(r,o){const t=h(r);if(!t)throw new Error(`Unknown OAuth provider: ${r}`);return t.refreshToken(o)}e(E,"refreshOAuthToken");async function _(r,o){const t=h(r);if(!t)throw new Error(`Unknown OAuth provider: ${r}`);let i=o[r];if(!i)return null;if(Date.now()>=i.expires)try{i=await t.refreshToken(i)}catch{throw new Error(`Failed to refresh OAuth token for ${r}`)}const p=t.getApiKey(i);return{newCredentials:i,apiKey:p}}e(_,"getOAuthApiKey");export{M as OPENAI_CODEX_BROWSER_LOGIN_METHOD,$ as OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,T as anthropicOAuthProvider,H as getGitHubCopilotBaseUrl,_ as getOAuthApiKey,h as getOAuthProvider,C as getOAuthProviderInfoList,A as getOAuthProviders,G as githubCopilotOAuthProvider,k as loginAnthropic,N as loginGitHubCopilot,B as loginOpenAICodex,K as loginOpenAICodexDeviceCode,R as normalizeDomain,S as openaiCodexOAuthProvider,I as refreshAnthropicToken,U as refreshGitHubCopilotToken,E as refreshOAuthToken,V as refreshOpenAICodexToken,P as registerOAuthProvider,v as resetOAuthProviders,g as unregisterOAuthProvider};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var s=Object.defineProperty;var i=(e,t)=>s(e,"name",{value:t,configurable:!0});const c='<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" aria-hidden="true"><path fill="#fff" fill-rule="evenodd" d="M165.29 165.29 H517.36 V400 H400 V517.36 H282.65 V634.72 H165.29 Z M282.65 282.65 V400 H400 V282.65 Z"/><path fill="#fff" d="M517.36 400 H634.72 V634.72 H517.36 Z"/></svg>';function o(e){return e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'")}i(o,"escapeHtml");function n(e){const t=o(e.title),l=o(e.heading),r=o(e.message),a=e.details?o(e.details):void 0;return`<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<title>${t}</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--text: #fafafa;
|
|
10
|
+
--text-dim: #a1a1aa;
|
|
11
|
+
--page-bg: #09090b;
|
|
12
|
+
--font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
|
13
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
14
|
+
}
|
|
15
|
+
* { box-sizing: border-box; }
|
|
16
|
+
html { color-scheme: dark; }
|
|
17
|
+
body {
|
|
18
|
+
margin: 0;
|
|
19
|
+
min-height: 100vh;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
padding: 24px;
|
|
24
|
+
background: var(--page-bg);
|
|
25
|
+
color: var(--text);
|
|
26
|
+
font-family: var(--font-sans);
|
|
27
|
+
text-align: center;
|
|
28
|
+
}
|
|
29
|
+
main {
|
|
30
|
+
width: 100%;
|
|
31
|
+
max-width: 560px;
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
align-items: center;
|
|
35
|
+
justify-content: center;
|
|
36
|
+
}
|
|
37
|
+
.logo {
|
|
38
|
+
width: 72px;
|
|
39
|
+
height: 72px;
|
|
40
|
+
display: block;
|
|
41
|
+
margin-bottom: 24px;
|
|
42
|
+
}
|
|
43
|
+
h1 {
|
|
44
|
+
margin: 0 0 10px;
|
|
45
|
+
font-size: 28px;
|
|
46
|
+
line-height: 1.15;
|
|
47
|
+
font-weight: 650;
|
|
48
|
+
color: var(--text);
|
|
49
|
+
}
|
|
50
|
+
p {
|
|
51
|
+
margin: 0;
|
|
52
|
+
line-height: 1.7;
|
|
53
|
+
color: var(--text-dim);
|
|
54
|
+
font-size: 15px;
|
|
55
|
+
}
|
|
56
|
+
.details {
|
|
57
|
+
margin-top: 16px;
|
|
58
|
+
font-family: var(--font-mono);
|
|
59
|
+
font-size: 13px;
|
|
60
|
+
color: var(--text-dim);
|
|
61
|
+
white-space: pre-wrap;
|
|
62
|
+
word-break: break-word;
|
|
63
|
+
}
|
|
64
|
+
</style>
|
|
65
|
+
</head>
|
|
66
|
+
<body>
|
|
67
|
+
<main>
|
|
68
|
+
<div class="logo">${c}</div>
|
|
69
|
+
<h1>${l}</h1>
|
|
70
|
+
<p>${r}</p>
|
|
71
|
+
${a?`<div class="details">${a}</div>`:""}
|
|
72
|
+
</main>
|
|
73
|
+
</body>
|
|
74
|
+
</html>`}i(n,"renderPage");function m(e){return n({title:"Authentication successful",heading:"Authentication successful",message:e})}i(m,"oauthSuccessHtml");function f(e,t){return n({title:"Authentication failed",heading:"Authentication failed",message:e,details:t})}i(f,"oauthErrorHtml");export{f as oauthErrorHtml,m as oauthSuccessHtml};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI Codex (ChatGPT OAuth) flow
|
|
3
|
+
*
|
|
4
|
+
* NOTE: This module uses Node.js crypto and http for the OAuth callback.
|
|
5
|
+
* It is only intended for CLI use, not browser environments.
|
|
6
|
+
*/
|
|
7
|
+
import type { OAuthCredentials, OAuthDeviceCodeInfo, OAuthPrompt, OAuthProviderInterface } from "./types.ts";
|
|
8
|
+
export declare const OPENAI_CODEX_BROWSER_LOGIN_METHOD = "browser";
|
|
9
|
+
export declare const OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD = "device_code";
|
|
10
|
+
/**
|
|
11
|
+
* Login with OpenAI Codex OAuth using the Codex device-code flow.
|
|
12
|
+
*/
|
|
13
|
+
export declare function loginOpenAICodexDeviceCode(options: {
|
|
14
|
+
onDeviceCode: (info: OAuthDeviceCodeInfo) => void;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}): Promise<OAuthCredentials>;
|
|
17
|
+
/**
|
|
18
|
+
* Login with OpenAI Codex OAuth
|
|
19
|
+
*
|
|
20
|
+
* @param options.onAuth - Called with URL and instructions when auth starts
|
|
21
|
+
* @param options.onPrompt - Called to prompt user for manual code paste (fallback if no onManualCodeInput)
|
|
22
|
+
* @param options.onProgress - Optional progress messages
|
|
23
|
+
* @param options.onManualCodeInput - Optional promise that resolves with user-pasted code.
|
|
24
|
+
* Races with browser callback - whichever completes first wins.
|
|
25
|
+
* Useful for showing paste input immediately alongside browser flow.
|
|
26
|
+
* @param options.originator - OAuth originator parameter (defaults to "pi")
|
|
27
|
+
*/
|
|
28
|
+
export declare function loginOpenAICodex(options: {
|
|
29
|
+
onAuth: (info: {
|
|
30
|
+
url: string;
|
|
31
|
+
instructions?: string;
|
|
32
|
+
}) => void;
|
|
33
|
+
onPrompt: (prompt: OAuthPrompt) => Promise<string>;
|
|
34
|
+
onProgress?: (message: string) => void;
|
|
35
|
+
onManualCodeInput?: () => Promise<string>;
|
|
36
|
+
originator?: string;
|
|
37
|
+
}): Promise<OAuthCredentials>;
|
|
38
|
+
/**
|
|
39
|
+
* Refresh OpenAI Codex OAuth token
|
|
40
|
+
*/
|
|
41
|
+
export declare function refreshOpenAICodexToken(refreshToken: string): Promise<OAuthCredentials>;
|
|
42
|
+
export declare const openaiCodexOAuthProvider: OAuthProviderInterface;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=Object.defineProperty;var i=(e,t)=>S(e,"name",{value:t,configurable:!0});let f=null,p=null;typeof process<"u"&&(process.versions?.node||process.versions?.bun)&&(import("node:crypto").then(e=>{f=e.randomBytes}),import("node:http").then(e=>{p=e}));import{pollOAuthDeviceCodeFlow as P}from"./device-code.js";import{oauthErrorHtml as l,oauthSuccessHtml as T}from"./oauth-page.js";import{generatePKCE as D}from"./pkce.js";const R=process.env.KODA_OAUTH_CALLBACK_HOST||"127.0.0.1",h="app_EMoamEEZ73f0CkXaXp7hrann",u="https://auth.openai.com",L=`${u}/oauth/authorize`,g=`${u}/oauth/token`,w="http://localhost:1455/auth/callback",U=`${u}/api/accounts/deviceauth/usercode`,b=`${u}/api/accounts/deviceauth/token`,k=`${u}/codex/device`,$=`${u}/deviceauth/callback`,y=900,E="browser",O="device_code",N="openid profile email offline_access",z="https://api.openai.com/auth";function H(){if(!f)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");return f(16).toString("hex")}i(H,"createState");function m(e){const t=e.trim();if(!t)return{};try{const o=new URL(t);return{code:o.searchParams.get("code")??void 0,state:o.searchParams.get("state")??void 0}}catch{}if(t.includes("#")){const[o,n]=t.split("#",2);return{code:o,state:n}}if(t.includes("code=")){const o=new URLSearchParams(t);return{code:o.get("code")??void 0,state:o.get("state")??void 0}}return{code:t}}i(m,"parseAuthorizationInput");function J(e){try{const t=e.split(".");if(t.length!==3)return null;const o=t[1]??"",n=atob(o);return JSON.parse(n)}catch{return null}}i(J,"decodeJwt");async function C(e,t){try{return await fetch(e,t)}catch(o){throw t.signal?.aborted?new Error("Login cancelled"):o}}i(C,"fetchWithLoginCancellation");async function I(e,t){if(!e.ok){const r=await e.text().catch(()=>"");throw new Error(`OpenAI Codex token ${t} failed (${e.status}): ${r||e.statusText}`)}const n=await e.json();if(!n?.access_token||!n.refresh_token||typeof n.expires_in!="number")throw new Error(`OpenAI Codex token ${t} response missing fields: ${JSON.stringify(n)}`);return{access:n.access_token,refresh:n.refresh_token,expires:Date.now()+n.expires_in*1e3}}i(I,"readTokenResponse");async function j(e,t,o=w,n){const r=await C(g,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",client_id:h,code:e,code_verifier:t,redirect_uri:o}),signal:n});return I(r,"exchange")}i(j,"exchangeAuthorizationCode");async function F(e){let t;try{t=await fetch(g,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:h})})}catch(o){throw new Error(`OpenAI Codex token refresh error: ${o instanceof Error?o.message:String(o)}`)}return I(t,"refresh")}i(F,"refreshAccessToken");async function M(e){const t=await C(U,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({client_id:h}),signal:e});if(!t.ok){if(t.status===404)throw new Error("OpenAI Codex device code login is not enabled for this server. Use browser login or verify the server URL.");const a=await t.text().catch(()=>"");throw new Error(`OpenAI Codex device code request failed with status ${t.status}${a?`: ${a}`:""}`)}const n=await t.json(),r=typeof n?.interval=="string"?Number(n.interval.trim()):n?.interval;if(!n?.device_auth_id||!n.user_code||typeof r!="number"||!Number.isFinite(r)||r<0)throw new Error(`Invalid OpenAI Codex device code response: ${JSON.stringify(n)}`);return{deviceAuthId:n.device_auth_id,userCode:n.user_code,intervalSeconds:r}}i(M,"startOpenAICodexDeviceAuth");async function B(e,t){return P({intervalSeconds:e.intervalSeconds,expiresInSeconds:y,signal:t,poll:i(async()=>{const o=await C(b,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({device_auth_id:e.deviceAuthId,user_code:e.userCode}),signal:t});if(o.ok){const s=await o.json();return!s?.authorization_code||!s.code_verifier?{status:"failed",message:`Invalid OpenAI Codex device auth token response: ${JSON.stringify(s)}`}:{status:"complete",value:{authorizationCode:s.authorization_code,codeVerifier:s.code_verifier}}}if(o.status===403||o.status===404)return{status:"pending"};const n=await o.text().catch(()=>"");let r;try{const s=JSON.parse(n)?.error;r=typeof s=="object"?s?.code:s}catch{}return r==="deviceauth_authorization_pending"?{status:"pending"}:r==="slow_down"?{status:"slow_down"}:{status:"failed",message:`OpenAI Codex device auth failed with status ${o.status}${n?`: ${n}`:""}`}},"poll")})}i(B,"pollOpenAICodexDeviceAuth");async function V(e="pi"){const{verifier:t,challenge:o}=await D(),n=H(),r=new URL(L);return r.searchParams.set("response_type","code"),r.searchParams.set("client_id",h),r.searchParams.set("redirect_uri",w),r.searchParams.set("scope",N),r.searchParams.set("code_challenge",o),r.searchParams.set("code_challenge_method","S256"),r.searchParams.set("state",n),r.searchParams.set("id_token_add_organizations","true"),r.searchParams.set("codex_cli_simplified_flow","true"),r.searchParams.set("originator",e),{verifier:t,state:n,url:r.toString()}}i(V,"createAuthorizationFlow");function W(e){if(!p)throw new Error("OpenAI Codex OAuth is only available in Node.js environments");let t;const o=new Promise(r=>{let a=!1;t=i(s=>{a||(a=!0,r(s))},"settleWait")}),n=p.createServer((r,a)=>{try{const s=new URL(r.url||"","http://localhost");if(s.pathname!=="/auth/callback"){a.statusCode=404,a.setHeader("Content-Type","text/html; charset=utf-8"),a.end(l("Callback route not found."));return}if(s.searchParams.get("state")!==e){a.statusCode=400,a.setHeader("Content-Type","text/html; charset=utf-8"),a.end(l("State mismatch."));return}const d=s.searchParams.get("code");if(!d){a.statusCode=400,a.setHeader("Content-Type","text/html; charset=utf-8"),a.end(l("Missing authorization code."));return}a.statusCode=200,a.setHeader("Content-Type","text/html; charset=utf-8"),a.end(T("OpenAI authentication completed. You can close this window.")),t?.({code:d})}catch{a.statusCode=500,a.setHeader("Content-Type","text/html; charset=utf-8"),a.end(l("Internal error while processing OAuth callback."))}});return new Promise(r=>{n.listen(1455,R,()=>{r({close:i(()=>n.close(),"close"),cancelWait:i(()=>{t?.(null)},"cancelWait"),waitForCode:i(()=>o,"waitForCode")})}).on("error",a=>{t?.(null),r({close:i(()=>{try{n.close()}catch{}},"close"),cancelWait:i(()=>{},"cancelWait"),waitForCode:i(async()=>null,"waitForCode")})})})}i(W,"startLocalOAuthServer");function K(e){const n=J(e)?.[z]?.chatgpt_account_id;return typeof n=="string"&&n.length>0?n:null}i(K,"getAccountId");function v(e){const t=K(e.access);if(!t)throw new Error("Failed to extract accountId from token");return{access:e.access,refresh:e.refresh,expires:e.expires,accountId:t}}i(v,"credentialsFromToken");async function A(e,t,o,n){return v(await j(e,t,o,n))}i(A,"exchangeAuthorizationCodeForCredentials");async function X(e){const t=await M(e.signal);e.onDeviceCode({userCode:t.userCode,verificationUri:k,intervalSeconds:t.intervalSeconds,expiresInSeconds:y});const o=await B(t,e.signal);return A(o.authorizationCode,o.codeVerifier,$,e.signal)}i(X,"loginOpenAICodexDeviceCode");async function G(e){const{verifier:t,state:o,url:n}=await V(e.originator),r=await W(o);e.onAuth({url:n,instructions:"A browser window should open. Complete login to finish."});let a;try{if(e.onManualCodeInput){let s,d;const x=e.onManualCodeInput().then(c=>{s=c,r.cancelWait()}).catch(c=>{d=c instanceof Error?c:new Error(String(c)),r.cancelWait()}),_=await r.waitForCode();if(d)throw d;if(_?.code)a=_.code;else if(s){const c=m(s);if(c.state&&c.state!==o)throw new Error("State mismatch");a=c.code}if(!a){if(await x,d)throw d;if(s){const c=m(s);if(c.state&&c.state!==o)throw new Error("State mismatch");a=c.code}}}else{const s=await r.waitForCode();s?.code&&(a=s.code)}if(!a){const s=await e.onPrompt({message:"Paste the authorization code (or full redirect URL):"}),d=m(s);if(d.state&&d.state!==o)throw new Error("State mismatch");a=d.code}if(!a)throw new Error("Missing authorization code");return A(a,t,w)}finally{r.close()}}i(G,"loginOpenAICodex");async function Z(e){return v(await F(e))}i(Z,"refreshOpenAICodexToken");const te={id:"openai-codex",name:"ChatGPT Plus/Pro (Codex Subscription)",usesCallbackServer:!0,async login(e){const t=await e.onSelect({message:"Select OpenAI Codex login method:",options:[{id:E,label:"Browser login (default)"},{id:O,label:"Device code login (headless)"}]});if(!t)throw new Error("Login cancelled");if(t===O)return X({onDeviceCode:e.onDeviceCode,signal:e.signal});if(t!==E)throw new Error(`Unknown OpenAI Codex login method: ${t}`);return G({onAuth:e.onAuth,onPrompt:e.onPrompt,onProgress:e.onProgress,onManualCodeInput:e.onManualCodeInput})},async refreshToken(e){return Z(e.refresh)},getApiKey(e){return e.access}};export{E as OPENAI_CODEX_BROWSER_LOGIN_METHOD,O as OPENAI_CODEX_DEVICE_CODE_LOGIN_METHOD,G as loginOpenAICodex,X as loginOpenAICodexDeviceCode,te as openaiCodexOAuthProvider,Z as refreshOpenAICodexToken};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PKCE utilities using Web Crypto API.
|
|
3
|
+
* Works in both Node.js 20+ and browsers.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generate PKCE code verifier and challenge.
|
|
7
|
+
* Uses Web Crypto API for cross-platform compatibility.
|
|
8
|
+
*/
|
|
9
|
+
export declare function generatePKCE(): Promise<{
|
|
10
|
+
verifier: string;
|
|
11
|
+
challenge: string;
|
|
12
|
+
}>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var i=Object.defineProperty;var r=(n,e)=>i(n,"name",{value:e,configurable:!0});function o(n){let e="";for(const t of n)e+=String.fromCharCode(t);return btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}r(o,"base64urlEncode");async function f(){const n=new Uint8Array(32);crypto.getRandomValues(n);const e=o(n),c=new TextEncoder().encode(e),a=await crypto.subtle.digest("SHA-256",c),s=o(new Uint8Array(a));return{verifier:e,challenge:s}}r(f,"generatePKCE");export{f as generatePKCE};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Api, Model } from "../../types.ts";
|
|
2
|
+
export type OAuthCredentials = {
|
|
3
|
+
refresh: string;
|
|
4
|
+
access: string;
|
|
5
|
+
expires: number;
|
|
6
|
+
[key: string]: unknown;
|
|
7
|
+
};
|
|
8
|
+
export type OAuthProviderId = string;
|
|
9
|
+
/** @deprecated Use OAuthProviderId instead */
|
|
10
|
+
export type OAuthProvider = OAuthProviderId;
|
|
11
|
+
export type OAuthPrompt = {
|
|
12
|
+
message: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
allowEmpty?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type OAuthAuthInfo = {
|
|
17
|
+
url: string;
|
|
18
|
+
instructions?: string;
|
|
19
|
+
};
|
|
20
|
+
export type OAuthDeviceCodeInfo = {
|
|
21
|
+
userCode: string;
|
|
22
|
+
verificationUri: string;
|
|
23
|
+
intervalSeconds?: number;
|
|
24
|
+
expiresInSeconds?: number;
|
|
25
|
+
};
|
|
26
|
+
export type OAuthSelectOption = {
|
|
27
|
+
id: string;
|
|
28
|
+
label: string;
|
|
29
|
+
};
|
|
30
|
+
export type OAuthSelectPrompt = {
|
|
31
|
+
message: string;
|
|
32
|
+
options: OAuthSelectOption[];
|
|
33
|
+
};
|
|
34
|
+
export interface OAuthLoginCallbacks {
|
|
35
|
+
onAuth: (info: OAuthAuthInfo) => void;
|
|
36
|
+
onDeviceCode: (info: OAuthDeviceCodeInfo) => void;
|
|
37
|
+
onPrompt: (prompt: OAuthPrompt) => Promise<string>;
|
|
38
|
+
onProgress?: (message: string) => void;
|
|
39
|
+
onManualCodeInput?: () => Promise<string>;
|
|
40
|
+
/** Show an interactive selector and return the selected option id, or undefined on cancel. */
|
|
41
|
+
onSelect: (prompt: OAuthSelectPrompt) => Promise<string | undefined>;
|
|
42
|
+
signal?: AbortSignal;
|
|
43
|
+
}
|
|
44
|
+
export interface OAuthProviderInterface {
|
|
45
|
+
readonly id: OAuthProviderId;
|
|
46
|
+
readonly name: string;
|
|
47
|
+
/** Run the login flow, return credentials to persist */
|
|
48
|
+
login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials>;
|
|
49
|
+
/** Whether login uses a local callback server and supports manual code input. */
|
|
50
|
+
usesCallbackServer?: boolean;
|
|
51
|
+
/** Refresh expired credentials, return updated credentials to persist */
|
|
52
|
+
refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials>;
|
|
53
|
+
/** Convert credentials to API key string for the provider */
|
|
54
|
+
getApiKey(credentials: OAuthCredentials): string;
|
|
55
|
+
/** Optional: modify models for this provider (e.g., update baseUrl) */
|
|
56
|
+
modifyModels?(models: Model<Api>[], credentials: OAuthCredentials): Model<Api>[];
|
|
57
|
+
}
|
|
58
|
+
/** @deprecated Use OAuthProviderInterface instead */
|
|
59
|
+
export interface OAuthProviderInfo {
|
|
60
|
+
id: OAuthProviderId;
|
|
61
|
+
name: string;
|
|
62
|
+
available: boolean;
|
|
63
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { AssistantMessage } from "../types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Check if an assistant message represents a context overflow error.
|
|
4
|
+
*
|
|
5
|
+
* This handles two cases:
|
|
6
|
+
* 1. Error-based overflow: Most providers return stopReason "error" with a
|
|
7
|
+
* specific error message pattern.
|
|
8
|
+
* 2. Silent overflow: Some providers accept overflow requests and return
|
|
9
|
+
* successfully. For these, we check if usage.input exceeds the context window.
|
|
10
|
+
*
|
|
11
|
+
* ## Reliability by Provider
|
|
12
|
+
*
|
|
13
|
+
* **Reliable detection (returns error with detectable message):**
|
|
14
|
+
* - Anthropic: "prompt is too long: X tokens > Y maximum" or "request_too_large"
|
|
15
|
+
* - OpenAI (Completions & Responses): "exceeds the context window" or "exceeds the model's maximum context length of X tokens"
|
|
16
|
+
* - Google Gemini: "input token count exceeds the maximum"
|
|
17
|
+
* - xAI (Grok): "maximum prompt length is X but request contains Y"
|
|
18
|
+
* - Groq: "reduce the length of the messages"
|
|
19
|
+
* - Cerebras: 400/413 status code (no body)
|
|
20
|
+
* - Mistral: "Prompt contains X tokens ... too large for model with Y maximum context length"
|
|
21
|
+
* - OpenRouter (most backends): "maximum context length is X tokens"
|
|
22
|
+
* - OpenRouter/Poolside: "Input length X exceeds the maximum allowed input length of Y tokens."
|
|
23
|
+
* - Together AI: "The input (X tokens) is longer than the model's context length (Y tokens)."
|
|
24
|
+
* - llama.cpp: "exceeds the available context size"
|
|
25
|
+
* - LM Studio: "greater than the context length"
|
|
26
|
+
* - Kimi For Coding: "exceeded model token limit: X (requested: Y)"
|
|
27
|
+
*
|
|
28
|
+
* **Unreliable detection:**
|
|
29
|
+
* - z.ai: Sometimes accepts overflow silently (detectable via usage.input > contextWindow),
|
|
30
|
+
* sometimes returns rate limit errors. Pass contextWindow param to detect silent overflow.
|
|
31
|
+
* - Xiaomi MiMo: Truncates input to fit contextWindow then returns stopReason "length" with
|
|
32
|
+
* output=0. Pass contextWindow param to detect via the "filled context + zero output" signal.
|
|
33
|
+
* - Ollama: May truncate input silently for some setups, but may also return explicit
|
|
34
|
+
* overflow errors that match the patterns above. Silent truncation still cannot be
|
|
35
|
+
* detected here because we do not know the expected token count.
|
|
36
|
+
*
|
|
37
|
+
* ## Custom Providers
|
|
38
|
+
*
|
|
39
|
+
* If you've added custom models via settings.json, this function may not detect
|
|
40
|
+
* overflow errors from those providers. To add support:
|
|
41
|
+
*
|
|
42
|
+
* 1. Send a request that exceeds the model's context window
|
|
43
|
+
* 2. Check the errorMessage in the response
|
|
44
|
+
* 3. Create a regex pattern that matches the error
|
|
45
|
+
* 4. The pattern should be added to OVERFLOW_PATTERNS in this file, or
|
|
46
|
+
* check the errorMessage yourself before calling this function
|
|
47
|
+
*
|
|
48
|
+
* @param message - The assistant message to check
|
|
49
|
+
* @param contextWindow - Optional context window size for detecting silent overflow (z.ai)
|
|
50
|
+
* @returns true if the message indicates a context overflow
|
|
51
|
+
*/
|
|
52
|
+
export declare function isContextOverflow(message: AssistantMessage, contextWindow?: number): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Get the overflow patterns for testing purposes.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getOverflowPatterns(): RegExp[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=Object.defineProperty;var n=(e,t)=>s(e,"name",{value:t,configurable:!0});const r=[/prompt is too long/i,/request_too_large/i,/input is too long for requested model/i,/exceeds the context window/i,/exceeds (?:the )?(?:model'?s )?maximum context length of [\d,]+ tokens?/i,/input token count.*exceeds the maximum/i,/maximum prompt length is \d+/i,/reduce the length of the messages/i,/maximum context length is \d+ tokens/i,/exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,/input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,/exceeds the limit of \d+/i,/exceeds the available context size/i,/greater than the context length/i,/context window exceeds limit/i,/exceeded model token limit/i,/too large for model with \d+ maximum context length/i,/model_context_window_exceeded/i,/prompt too long; exceeded (?:max )?context length/i,/context[_ ]length[_ ]exceeded/i,/too many tokens/i,/token limit exceeded/i,/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i],l=[/^(Throttling error|Service unavailable):/i,/rate limit/i,/too many requests/i];function d(e,t){return!!(e.stopReason==="error"&&e.errorMessage&&!l.some(o=>o.test(e.errorMessage))&&r.some(o=>o.test(e.errorMessage))||t&&e.stopReason==="stop"&&e.usage.input+e.usage.cacheRead>t||t&&e.stopReason==="length"&&e.usage.output===0&&e.usage.input+e.usage.cacheRead>=t*.99)}n(d,"isContextOverflow");function c(){return[...r]}n(c,"getOverflowPatterns");export{c as getOverflowPatterns,d as isContextOverflow};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Removes unpaired Unicode surrogate characters from a string.
|
|
3
|
+
*
|
|
4
|
+
* Unpaired surrogates (high surrogates 0xD800-0xDBFF without matching low surrogates 0xDC00-0xDFFF,
|
|
5
|
+
* or vice versa) cause JSON serialization errors in many API providers.
|
|
6
|
+
*
|
|
7
|
+
* Valid emoji and other characters outside the Basic Multilingual Plane use properly paired
|
|
8
|
+
* surrogates and will NOT be affected by this function.
|
|
9
|
+
*
|
|
10
|
+
* @param text - The text to sanitize
|
|
11
|
+
* @returns The sanitized text with unpaired surrogates removed
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Valid emoji (properly paired surrogates) are preserved
|
|
15
|
+
* sanitizeSurrogates("Hello 🙈 World") // => "Hello 🙈 World"
|
|
16
|
+
*
|
|
17
|
+
* // Unpaired high surrogate is removed
|
|
18
|
+
* const unpaired = String.fromCharCode(0xD83D); // high surrogate without low
|
|
19
|
+
* sanitizeSurrogates(`Text ${unpaired} here`) // => "Text here"
|
|
20
|
+
*/
|
|
21
|
+
export declare function sanitizeSurrogates(text: string): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=Object.defineProperty;var F=(u,D)=>e(u,"name",{value:D,configurable:!0});function t(u){return u.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"")}F(t,"sanitizeSurrogates");export{t as sanitizeSurrogates};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type TUnsafe } from "typebox";
|
|
2
|
+
/**
|
|
3
|
+
* Creates a string enum schema compatible with Google's API and other providers
|
|
4
|
+
* that don't support anyOf/const patterns.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* const OperationSchema = StringEnum(["add", "subtract", "multiply", "divide"], {
|
|
8
|
+
* description: "The operation to perform"
|
|
9
|
+
* });
|
|
10
|
+
*
|
|
11
|
+
* type Operation = Static<typeof OperationSchema>; // "add" | "subtract" | "multiply" | "divide"
|
|
12
|
+
*/
|
|
13
|
+
export declare function StringEnum<T extends readonly string[]>(values: T, options?: {
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: T[number];
|
|
16
|
+
}): TUnsafe<T[number]>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var n=Object.defineProperty;var t=(r,e)=>n(r,"name",{value:e,configurable:!0});import{Type as i}from"typebox";function f(r,e){return i.Unsafe({type:"string",enum:r,...e?.description&&{description:e.description},...e?.default&&{default:e.default}})}t(f,"StringEnum");export{f as StringEnum};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Tool, ToolCall } from "../types.ts";
|
|
2
|
+
/**
|
|
3
|
+
* Finds a tool by name and validates the tool call arguments against its TypeBox schema
|
|
4
|
+
* @param tools Array of tool definitions
|
|
5
|
+
* @param toolCall The tool call from the LLM
|
|
6
|
+
* @returns The validated arguments
|
|
7
|
+
* @throws Error if tool is not found or validation fails
|
|
8
|
+
*/
|
|
9
|
+
export declare function validateToolCall(tools: Tool[], toolCall: ToolCall): any;
|
|
10
|
+
/**
|
|
11
|
+
* Validates tool call arguments against the tool's TypeBox schema
|
|
12
|
+
* @param tool The tool definition with TypeBox schema
|
|
13
|
+
* @param toolCall The tool call from the LLM
|
|
14
|
+
* @returns The validated (and potentially coerced) arguments
|
|
15
|
+
* @throws Error with formatted message if validation fails
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateToolArguments(tool: Tool, toolCall: ToolCall): any;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
var b=Object.defineProperty;var i=(r,t)=>b(r,"name",{value:t,configurable:!0});import{Compile as g}from"typebox/compile";import{Value as l}from"typebox/value";const p=new WeakMap,A=Symbol.for("TypeBox.Kind");function c(r){return typeof r=="object"&&r!==null}i(c,"isRecord");function y(r){return c(r)}i(y,"isJsonSchemaObject");function h(r){return c(r)&&Object.getOwnPropertySymbols(r).includes(A)}i(h,"hasTypeBoxMetadata");function O(r){return typeof r.type=="string"?[r.type]:Array.isArray(r.type)?r.type.filter(t=>typeof t=="string"):[]}i(O,"getSchemaTypes");function S(r,t){switch(t){case"number":return typeof r=="number";case"integer":return typeof r=="number"&&Number.isInteger(r);case"boolean":return typeof r=="boolean";case"string":return typeof r=="string";case"null":return r===null;case"array":return Array.isArray(r);case"object":return c(r)&&!Array.isArray(r);default:return!1}}i(S,"matchesJsonType");function P(r){return c(r)}i(P,"isValidatorSchema");function k(r){if(P(r))try{return d(r)}catch{return}}i(k,"getSubSchemaValidator");function j(r,t){switch(t){case"number":{if(r===null)return 0;if(typeof r=="string"&&r.trim()!==""){const e=Number(r);if(Number.isFinite(e))return e}return typeof r=="boolean"?r?1:0:r}case"integer":{if(r===null)return 0;if(typeof r=="string"&&r.trim()!==""){const e=Number(r);if(Number.isInteger(e))return e}return typeof r=="boolean"?r?1:0:r}case"boolean":{if(r===null)return!1;if(typeof r=="string"){if(r==="true")return!0;if(r==="false")return!1}if(typeof r=="number"){if(r===1)return!0;if(r===0)return!1}return r}case"string":return r===null?"":typeof r=="number"||typeof r=="boolean"?String(r):r;case"null":return r===""||r===0||r===!1?null:r;default:return r}}i(j,"coercePrimitiveByType");function w(r,t){const e=t.properties,n=new Set(e?Object.keys(e):[]);if(e)for(const[o,s]of Object.entries(e))o in r&&(r[o]=a(r[o],s));if(t.additionalProperties&&y(t.additionalProperties))for(const[o,s]of Object.entries(r))n.has(o)||(r[o]=a(s,t.additionalProperties))}i(w,"applySchemaObjectCoercion");function T(r,t){if(Array.isArray(t.items)){for(let e=0;e<r.length;e++){const n=t.items[e];n&&(r[e]=a(r[e],n))}return}if(y(t.items))for(let e=0;e<r.length;e++)r[e]=a(r[e],t.items)}i(T,"applySchemaArrayCoercion");function u(r,t){for(const e of t){const n=structuredClone(r),o=a(n,e);if(k(e)?.Check(o))return o}return r}i(u,"coerceWithUnionSchema");function a(r,t){let e=r;if(Array.isArray(t.allOf))for(const s of t.allOf)e=a(e,s);Array.isArray(t.anyOf)&&(e=u(e,t.anyOf)),Array.isArray(t.oneOf)&&(e=u(e,t.oneOf));const n=O(t),o=n.length>1&&n.some(s=>S(e,s));if(n.length>0&&!o)for(const s of n){const f=j(e,s);if(f!==e){e=f;break}}return n.includes("object")&&c(e)&&!Array.isArray(e)&&w(e,t),n.includes("array")&&Array.isArray(e)&&T(e,t),e}i(a,"coerceWithJsonSchema");function d(r){const t=r,e=p.get(t);if(e)return e;const n=g(r);return p.set(t,n),n}i(d,"getValidator");function V(r){if(r.keyword==="required"){const n=r.params.requiredProperties?.[0];if(n){const o=r.instancePath.replace(/^\//,"").replace(/\//g,".");return o?`${o}.${n}`:n}}return r.instancePath.replace(/^\//,"").replace(/\//g,".")||"root"}i(V,"formatValidationPath");function q(r,t){const e=r.find(n=>n.name===t.name);if(!e)throw new Error(`Tool "${t.name}" not found`);return $(e,t)}i(q,"validateToolCall");function $(r,t){const e=structuredClone(t.arguments);l.Convert(r.parameters,e);const n=d(r.parameters);if(!h(r.parameters)&&y(r.parameters)){const f=a(e,r.parameters);if(f!==e)if(c(e)&&c(f)){for(const m of Object.keys(e))delete e[m];Object.assign(e,f)}else return n.Check(f)?f:e}if(n.Check(e))return e;const o=n.Errors(e).map(f=>` - ${V(f)}: ${f.message}`).join(`
|
|
2
|
+
`)||"Unknown validation error",s=`Validation failed for tool "${t.name}":
|
|
3
|
+
${o}
|
|
4
|
+
|
|
5
|
+
Received arguments:
|
|
6
|
+
${JSON.stringify(t.arguments,null,2)}`;throw new Error(s)}i($,"validateToolArguments");export{$ as validateToolArguments,q as validateToolCall};
|