@bodhiapp/bodhi-js-ext 0.0.27 → 0.0.29
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/dist/bodhi-ext.cjs.js +1 -1
- package/dist/bodhi-ext.esm.js +173 -213
- package/dist/ext-client.d.ts +9 -9
- package/dist/ext2ext-client.d.ts +4 -3
- package/package.json +4 -4
package/dist/bodhi-ext.cjs.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("@bodhiapp/bodhi-js-core"),g=require("@bodhiapp/bodhi-browser-types"),h={EXT2EXT_CLIENT_REQUEST:"EXT2EXT_CLIENT_REQUEST",EXT2EXT_CLIENT_RESPONSE:"EXT2EXT_CLIENT_RESPONSE",EXT2EXT_CLIENT_BROADCAST:"EXT2EXT_CLIENT_BROADCAST",EXT2EXT_CLIENT_API_REQUEST:"EXT2EXT_CLIENT_API_REQUEST",EXT2EXT_CLIENT_API_RESPONSE:"EXT2EXT_CLIENT_API_RESPONSE",EXT2EXT_CLIENT_STREAM_REQUEST:"EXT2EXT_CLIENT_STREAM_REQUEST",EXT2EXT_CLIENT_STREAM_CHUNK:"EXT2EXT_CLIENT_STREAM_CHUNK",EXT2EXT_CLIENT_STREAM_ERROR:"EXT2EXT_CLIENT_STREAM_ERROR",EXT2EXT_CLIENT_STREAM_API_ERROR:"EXT2EXT_CLIENT_STREAM_API_ERROR",EXT2EXT_CLIENT_STREAM_DONE:"EXT2EXT_CLIENT_STREAM_DONE"},f="ext2ext-client-stream",p={LOGIN:"login",LOGOUT:"logout",GET_AUTH_STATE:"getAuthState",DISCOVER_EXTENSION:"discoverBodhiExtension",GET_EXTENSION_ID:"get_extension_id",SET_EXTENSION_ID:"setExtensionId"},x=5e3,C=3,w=500,A=500,N=3e4;function O(S){return"error"in S}class v extends n.DirectClientBase{constructor(e,t){const r=n.createStoragePrefixWithBasePath(e.basePath,n.STORAGE_PREFIXES.EXT_DIRECT),i={authClientId:e.authClientId,authServerUrl:e.authServerUrl,userRole:e.userRole,storagePrefix:r,logLevel:e.logLevel,loggerPrefix:"DirectExtClient",apiTimeoutMs:e.apiTimeoutMs};super(i,t)}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;e?.flowType==="redirect"&&this.logger.warn("Extension mode does not support redirect flow type; using popup instead");const r=e?.userRole??this.userRole;e?.onProgress?.("requesting");const i=new n.AccessRequestBuilder(this.authClientId).requestedRole(r).flowType("popup");e?.requested&&i.requested(e.requested);const o=i.build(),s=await this.requestAccess(o);if(n.isApiResultOperationError(s))throw n.createOperationError(s.error.message,s.error.type);if(!n.isApiResultSuccess(s))throw n.createOperationError(`Access request failed: HTTP ${s.status}`,"auth_error");const{id:a,review_url:E}=s.body;e?.onProgress?.("reviewing"),await chrome.tabs.create({url:E});const c=await this.pollAccessRequestStatus(a,{intervalMs:e?.pollIntervalMs??n.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??n.DEFAULT_POLL_TIMEOUT_MS});if(c.status!=="approved")throw n.createOperationError(`Access request ${c.status}`,"auth_error");const u=c.access_request_scope;return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${u??""}`.trim())}async performOAuthPkce(e){const t=n.generateCodeVerifier(),r=await n.generateCodeChallenge(t),i=n.generateCodeVerifier();await chrome.storage.session.set({[this.storageKeys.CODE_VERIFIER]:t,[this.storageKeys.STATE]:i});const o=chrome.identity.getRedirectURL("callback"),s=new URL(this.authEndpoints.authorize);return s.searchParams.set("client_id",this.authClientId),s.searchParams.set("response_type","code"),s.searchParams.set("redirect_uri",o),s.searchParams.set("scope",e),s.searchParams.set("code_challenge",r),s.searchParams.set("code_challenge_method","S256"),s.searchParams.set("state",i),new Promise((a,E)=>{chrome.identity.launchWebAuthFlow({url:s.toString(),interactive:!0},async c=>{if(chrome.runtime.lastError){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(chrome.runtime.lastError);return}if(!c){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("No redirect URL received","oauth-error"));return}try{const u=new URL(c),d=u.searchParams.get("code"),T=u.searchParams.get("state"),m=await chrome.storage.session.get(this.storageKeys.STATE);if(T!==m[this.storageKeys.STATE]){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("State mismatch","oauth-error"));return}if(!d){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("No authorization code","oauth-error"));return}await this.exchangeCodeForTokens(d);const l=await this.getAuthState();if(l.status!=="authenticated")throw n.createOperationError("Login failed","oauth-error");this.setAuthState(l),await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),a(l)}catch(u){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(u)}})})}async logout(){const t=(await chrome.storage.session.get(this.storageKeys.REFRESH_TOKEN))[this.storageKeys.REFRESH_TOKEN];if(t)try{const i=new URLSearchParams({token:t,client_id:this.authClientId,token_type_hint:"refresh_token"});await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i})}catch(i){this.logger.warn("Token revocation failed:",i)}await chrome.storage.session.remove([this.storageKeys.ACCESS_TOKEN,this.storageKeys.REFRESH_TOKEN,this.storageKeys.EXPIRES_AT]);const r={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(r),r}async exchangeCodeForTokens(e){const r=(await chrome.storage.session.get(this.storageKeys.CODE_VERIFIER))[this.storageKeys.CODE_VERIFIER],i=chrome.identity.getRedirectURL("callback"),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:i,client_id:this.authClientId,code_verifier:r})});if(!o.ok){const E=await o.text();throw new Error(`Token exchange failed: ${o.status} ${E}`)}const s=await o.json(),a=Date.now()+(s.expires_in||3600)*1e3;await chrome.storage.session.set({[this.storageKeys.ACCESS_TOKEN]:s.access_token,[this.storageKeys.REFRESH_TOKEN]:s.refresh_token,[this.storageKeys.EXPIRES_AT]:a}),await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE])}async _storageGet(e){const r=(await chrome.storage.session.get(e))[e];return r!==void 0?String(r):null}async _storageSet(e){await chrome.storage.session.set(e)}async _storageRemove(e){await chrome.storage.session.remove(e)}_getRedirectUri(){return chrome.identity.getRedirectURL("callback")}}class X{constructor(e={},t){this.state={type:"extension",extension:"not-initialized",extensionId:null,server:n.PENDING_EXTENSION_READY},this.extensionId=null,this.broadcastListenerActive=!1,this.config=e,this.logger=new n.Logger("ExtClient",e?.logLevel||"warn"),this.onStateChange=t??n.NOOP_STATE_CALLBACK,this.apiTimeoutMs=e.apiTimeoutMs??N,this.authClientId=e.authClientId??""}setState(e){this.state=e,this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}setupBroadcastListener(){this.broadcastListenerActive||(this.broadcastListenerActive=!0,chrome.runtime.onMessage.addListener(e=>{const t=e;return t?.type===h.EXT2EXT_CLIENT_BROADCAST&&t.event==="authStateChanged"&&this.handleAuthStateChangedBroadcast(),!1}),this.logger.debug("Broadcast listener setup complete"))}async handleAuthStateChangedBroadcast(){this.logger.debug("Received authStateChanged broadcast, refreshing auth state");const e=await this.getAuthState();this.setAuthState(e)}generateRequestId(){return crypto.randomUUID()}getState(){return this.state}isClientInitialized(){return this.state.extension==="ready"}isServerReady(){return this.isClientInitialized()&&this.state.server.status==="ready"}async init(e={}){if(!e.testConnection&&!e.selectedConnection){this.logger.info("No testConnection or selectedConnection, returning not-initialized state");const i=n.createExtensionStateNotInitialized();return this.setState(i),i}if(this.extensionId&&!e.testConnection)return this.logger.debug("Already initialized with extensionId, skipping discovery"),this.state;const t=e.timeoutMs??this.config.initParams?.extension?.timeoutMs??x,r=e.savedState?.extensionId;try{if(!this.extensionId){if(r)this.logger.info("Restoring with known extensionId:",r),await this.sendExtMessageWithTimeout(p.SET_EXTENSION_ID,{extensionId:r},t),this.extensionId=r;else{this.logger.info("Discovering bodhi-browser extension...");const s={attempts:this.config.initParams?.extension?.attempts,attemptWaitMs:this.config.initParams?.extension?.attemptWaitMs,attemptTimeout:this.config.initParams?.extension?.attemptTimeout},a=await this.sendExtMessageWithTimeout(p.DISCOVER_EXTENSION,s,t);this.extensionId=a.extensionId,this.logger.info("Extension discovered:",this.extensionId)}this.setupBroadcastListener()}const i={type:"extension",extension:"ready",extensionId:this.extensionId,server:n.PENDING_EXTENSION_READY};let o=n.PENDING_EXTENSION_READY;if(e.testConnection)try{o=await this.getServerState(),this.logger.info("Server connectivity tested, state:",o.status)}catch(s){this.logger.error("Failed to get server state:",s),o=n.BACKEND_SERVER_NOT_REACHABLE}return this.setState({...i,server:o}),this.state}catch(i){this.logger.error("Failed to initialize extension:",i),this.extensionId=null;const o=n.createExtensionStateNotFound();return this.setState(o),this.state}}async sendExtMessageWithTimeout(e,t,r=1e4){const i=new Promise((o,s)=>setTimeout(()=>s(new Error("Timeout")),r));return Promise.race([this.sendExtRequest(e,t),i])}async sendExtRequest(e,t){try{const r=this.generateRequestId(),i=await chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_REQUEST,requestId:r,request:{action:e,params:t}});if(!i)throw n.createOperationError("No response from background script","extension_error");if(i.type!==h.EXT2EXT_CLIENT_RESPONSE)throw n.createOperationError("Invalid response type from background script","extension_error");const o=i.response;if(g.isExtError(o)){const s=o.error.type||"extension_error";throw n.createOperationError(o.error.message,s)}return o}catch(r){throw g.isOperationError(r)?r:n.createOperationError(r instanceof Error?r.message:"Unknown error occurred","extension_error")}}async sendRawApiMessage(e,t,r,i,o){const s=this.generateRequestId();return await chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_API_REQUEST,requestId:s,request:{method:e,endpoint:t,body:r,headers:i,authenticated:o}})}async sendApiRequest(e,t,r,i,o){try{const s=new Promise((E,c)=>setTimeout(()=>c(new Error(`[bodhi-js-sdk/ext] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`)),this.apiTimeoutMs)),a=await Promise.race([this.sendRawApiMessage(e,t,r,i,o),s]);if(O(a)){const E=a.error.type||"extension_error";return{error:{message:a.error.message,type:E}}}return a.response}catch(s){return{error:{message:s instanceof Error?s.message:String(s),type:"network_error"}}}}async login(e){return new Promise((t,r)=>{const i=async o=>{if(o&&typeof o=="object"&&"type"in o&&o.type==="EXT2EXT_CLIENT_BROADCAST"&&"event"in o&&o.event==="authStateChanged"){chrome.runtime.onMessage.removeListener(i);try{const s=await this.getAuthState();if(n.isAuthError(s)){r(n.createOperationError(`Login failed: ${s.error?.message}`,"auth-error"));return}if(s.status!=="authenticated"){r(n.createOperationError("Login failed: User is not logged in","auth-error"));return}this.setAuthState(s),t(s)}catch(s){r(s)}}};chrome.runtime.onMessage.addListener(i),this.sendExtRequest(p.LOGIN,e).catch(o=>{chrome.runtime.onMessage.removeListener(i),r(o)})})}async logout(){await this.sendExtRequest(p.LOGOUT);const e={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(e),e}async getAuthState(){return this.isClientInitialized()?(await this.sendExtRequest(p.GET_AUTH_STATE)).authState:n.INITIAL_AUTH_STATE}async pingApi(){return this.sendApiRequest("GET","/ping")}async getServerState(){const e=await this.sendApiRequest("GET","/bodhi/v1/info");if(n.isApiResultOperationError(e))return{status:"not-reachable",version:null,error:e.error};if(!n.isApiResultSuccess(e))return{status:"not-reachable",version:null,error:{message:"API error from server",type:"extension_error"}};const t=e.body,r=t.version||"unknown",i={deployment:t.deployment??null,client_id:t.client_id??null};switch(t.status){case"ready":return{status:"ready",version:r,error:null,...i};case"setup":return{status:"setup",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Setup required",type:"extension_error"},...i};case"resource_admin":return{status:"resource_admin",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Resource admin required",type:"extension_error"},...i};case"tenant_selection":return{status:"tenant_selection",version:r,error:null,...i};case"error":return{status:"error",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Server error",type:"extension_error"},...i};default:return{status:"not-reachable",version:null,error:{message:"Unknown server status",type:"extension_error"}}}}async*stream(e,t,r,i,o=!0){const s=this.generateRequestId();this.logger.debug("Starting stream",{method:e,endpoint:t,requestId:s});const a=chrome.runtime.connect({name:f}),c=new ReadableStream({start:u=>{a.onMessage.addListener(d=>{if(d.requestId===s)switch(d.type){case h.EXT2EXT_CLIENT_STREAM_DONE:this.logger.debug("Stream complete",{requestId:s}),u.close(),a.disconnect();break;case h.EXT2EXT_CLIENT_STREAM_ERROR:this.logger.error("Stream error",{requestId:s,error:JSON.stringify(d.error)}),u.error(n.createOperationError(d.error.message,"extension_error")),a.disconnect();break;case h.EXT2EXT_CLIENT_STREAM_API_ERROR:{const T=d;this.logger.error("Stream API error",{requestId:s,error:T.response.body?.error}),u.error(n.createApiError(T.response.body?.error?.message||"API error",T.response.status,T.response.body)),a.disconnect();break}case h.EXT2EXT_CLIENT_STREAM_CHUNK:{const T=d;g.isApiSuccessResponse(T.response)&&u.enqueue(T.response.body);break}}}),a.onDisconnect.addListener(()=>{this.logger.debug("Port disconnected",{requestId:s});try{u.error(n.createOperationError("Connection closed unexpectedly","extension_error"))}catch{}}),a.postMessage({type:h.EXT2EXT_CLIENT_STREAM_REQUEST,requestId:s,request:{method:e,endpoint:t,body:r,headers:i,authenticated:o}})}}).getReader();try{for(;;){const{done:u,value:d}=await c.read();if(u){this.logger.debug("Stream iteration complete");break}yield d}}finally{c.releaseLock()}}get chat(){return this._chat??=new n.Chat(this)}get models(){return this._models??=new n.Models(this)}get embeddings(){return this._embeddings??=new n.Embeddings(this)}get toolsets(){return this._toolsets??=new n.Toolsets(this)}get mcps(){return this._mcps??=new n.Mcps(this)}async requestAccess(e){return this.sendApiRequest("POST","/bodhi/v1/apps/request-access",e,{},!1)}async getAccessRequestStatus(e){return this.sendApiRequest("GET",`/bodhi/v1/apps/access-requests/${e}?app_client_id=${encodeURIComponent(this.authClientId)}`,void 0,{},!1)}async pollAccessRequestStatus(e,t){return n.pollAccessRequestUntilResolved(r=>this.getAccessRequestStatus(r),e,t)}serialize(){return{extensionId:this.extensionId??void 0}}async debug(){return{type:"ExtClient",state:this.state,authState:await this.getAuthState()}}}class b extends n.BaseFacadeClient{constructor(e,t,r){const i=t||{},o={basePath:i.basePath||"/",authServerUrl:i.authServerUrl||"https://id.getbodhi.app/realms/bodhi",userRole:i.userRole||"scope_user_user",logLevel:i.logLevel||"warn",apiTimeoutMs:i.apiTimeoutMs,initParams:i.initParams};super(e,o,r)}createLogger(e){return new n.Logger("ExtUIClient",e.logLevel)}createStoragePrefix(e){return n.createStoragePrefixWithBasePath(e.basePath,n.STORAGE_PREFIXES.EXT)}createExtClient(e,t){return new X({authClientId:this.authClientId,logLevel:e.logLevel,apiTimeoutMs:e.apiTimeoutMs,initParams:e.initParams},t)}createDirectClient(e,t,r){return new v({authClientId:e,authServerUrl:t.authServerUrl,userRole:t.userRole,logLevel:t.logLevel,basePath:t.basePath,apiTimeoutMs:t.apiTimeoutMs},r)}}const L=["ggedphdcbekjlomjaidbajglgihbeaon"],M=["bjdjhiombmfbcoeojijpfckljjghmjbf"],y="production",_=class _{constructor(e,t){this.isAuthenticating=!1,this.state="setup",this.listenersInitialized=!1,this.refreshPromise=null,this.activeStreamPorts=new Map,this.authClientId=e,this.authServerUrl=t?.authServerUrl||"https://id.getbodhi.app/realms/bodhi",this.userRole=t?.userRole||"scope_user_user",this.extensionId=t?.extensionId,this.logger=new n.Logger("BodhiExtClient",t?.logLevel||"warn"),this.attempts=t?.attempts??C,this.attemptWaitMs=t?.attemptWaitMs??w,this.attemptTimeout=t?.attemptTimeout??A,this.authEndpoints={authorize:`${this.authServerUrl}/protocol/openid-connect/auth`,token:`${this.authServerUrl}/protocol/openid-connect/token`,userinfo:`${this.authServerUrl}/protocol/openid-connect/userinfo`,logout:`${this.authServerUrl}/protocol/openid-connect/logout`,revoke:`${this.authServerUrl}/protocol/openid-connect/revoke`},this.extensionId?this.logger.info(`[BodhiExtClient] Created client for extension: ${this.extensionId}`):this.logger.info("[BodhiExtClient] Created client without extension ID (call init() to discover)")}static base64UrlEncode(e){return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}static generateCodeVerifier(){const e=new Uint8Array(32);return crypto.getRandomValues(e),_.base64UrlEncode(e.buffer)}static async generateCodeChallenge(e){const r=new TextEncoder().encode(e),i=await crypto.subtle.digest("SHA-256",r);return _.base64UrlEncode(i)}getState(){return this.state}getExtensionIdsForEnvironment(){const t=y!=="production"?L:M;return this.logger.info("[Ext2Ext/Registry] Environment: production"),this.logger.debug("[Ext2Ext/Registry] Using extension IDs:",t),t}async pingExtension(e){return this.logger.debug(`[Ext2Ext/Discovery] Pinging extension: ${e} with timeout ${this.attemptTimeout}ms`),new Promise((t,r)=>{const i=setTimeout(()=>{this.logger.debug(`[Ext2Ext/Discovery] Timeout waiting for extension ${e}`),r(new Error("Timeout"))},this.attemptTimeout);try{const o={type:g.MESSAGE_TYPES.EXT_REQUEST,requestId:crypto.randomUUID(),request:{action:"get_extension_id"}};this.logger.debug(`[Ext2Ext/Discovery] Sending message to ${e}:`,o),chrome.runtime.sendMessage(e,o,s=>{if(clearTimeout(i),chrome.runtime.lastError){this.logger.error(`[Ext2Ext/Discovery] Error from extension ${e}:`,chrome.runtime.lastError.message),r(new Error(chrome.runtime.lastError.message));return}this.logger.debug(`[Ext2Ext/Discovery] Response from ${e}:`,s);const a=s;a&&a.type===g.MESSAGE_TYPES.EXT_RESPONSE?(this.logger.debug(`[Ext2Ext/Discovery] ✓ Extension ${e} responded`),t(!0)):(this.logger.error(`[Ext2Ext/Discovery] Invalid response from ${e}:`,s),r(new Error("Invalid response")))})}catch(o){this.logger.error(`[Ext2Ext/Discovery] Exception pinging ${e}:`,o),clearTimeout(i),r(o)}})}sleep(e){return new Promise(t=>setTimeout(t,e))}async discoverBodhiExtension(e){const{attempts:t,attemptWaitMs:r,attemptTimeout:i}=e;this.logger.info(`[Ext2Ext/Discovery] Starting discovery: ${t} attempts per ID, ${i}ms timeout, ${r}ms between attempts`);const o=this.getExtensionIdsForEnvironment();this.logger.debug(`[Ext2Ext/Discovery] Will try ${o.length} extension(s):`,o);for(const E of o){for(let c=1;c<=t;c++){this.logger.debug(`[Ext2Ext/Discovery] Trying ${E} - attempt ${c}/${t}`);try{return await this.pingExtension(E),this.logger.info(`[Ext2Ext/Discovery] ✓ Found: ${E} on attempt ${c}`),{success:!0,extensionId:E}}catch(u){this.logger.debug(`[Ext2Ext/Discovery] Attempt ${c} failed for ${E}: ${u instanceof Error?u.message:"Unknown error"}`),c<t&&await this.sleep(r)}}this.logger.warn(`[Ext2Ext/Discovery] ✗ Not found: ${E} after ${t} attempts`)}const s=o.join(", "),a=`Extension not found. Tried ${o.length} IDs with ${t} attempts each: ${s}`;return this.logger.error(`[Ext2Ext/Discovery] ${a}`),{success:!1,error:a}}setupListeners(){if(this.listenersInitialized){this.logger.debug("[BodhiExtClient] Listeners already initialized, skipping");return}this.listenersInitialized=!0,chrome.runtime.onMessage.addListener((e,t,r)=>e.type!==h.EXT2EXT_CLIENT_REQUEST&&e.type!==h.EXT2EXT_CLIENT_API_REQUEST?!1:this.state!=="ready"&&!(e.type===h.EXT2EXT_CLIENT_REQUEST&&e.request.action===p.DISCOVER_EXTENSION)?(e.type===h.EXT2EXT_CLIENT_REQUEST?r({type:h.EXT2EXT_CLIENT_RESPONSE,requestId:e.requestId,response:{error:{message:this.createErrorClientNotInitialized(e),type:"NOT_INITIALIZED"}}}):r({type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:e.requestId,error:{message:`Client not initialized. Extension discovery not complete, cannot handle type:${e.type}, message:${JSON.stringify(e)}`,type:"NOT_INITIALIZED"}}),!0):(this.logger.debug(`[BodhiExtClient] Processing message.type=${e.type}`),(async()=>{const i=await this.handleAction(e);r(i)})(),!0)),chrome.runtime.onConnect.addListener(e=>{if(e.name!==f){this.logger.debug("[BodhiExtClient] Ignoring port with name:",e.name);return}this.logger.info("[BodhiExtClient] Streaming port connected"),e.onMessage.addListener(async t=>{if(t.type!==h.EXT2EXT_CLIENT_STREAM_REQUEST){this.logger.warn("[BodhiExtClient] Unknown stream message type:",t.type),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:t.requestId,error:{message:"Unknown stream message type",type:"extension_error"}});return}await this.handleStreamRequest(e,t)}),e.onDisconnect.addListener(()=>{this.logger.info("[BodhiExtClient] Streaming port disconnected")})}),this.logger.info("[BodhiExtClient] Streaming listeners initialized")}async init(e){if(this.setupListeners(),this.extensionId){this.state="ready",this.logger.warn(`[BodhiExtClient] Already initialized with extension ID: ${this.extensionId}`);return}this.logger.info("[BodhiExtClient] Starting discovery");const t={attempts:e?.attempts??this.attempts,attemptWaitMs:e?.attemptWaitMs??this.attemptWaitMs,attemptTimeout:e?.attemptTimeout??this.attemptTimeout},r=await this.discoverBodhiExtension(t);if(!r.success||!r.extensionId)throw new Error(r.error||"Discovery failed");this.extensionId=r.extensionId,this.state="ready",this.logger.info(`[BodhiExtClient] ✓ Initialized: ${this.extensionId}`)}broadcastAuthStateChange(){chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_BROADCAST,event:"authStateChanged"}).catch(e=>{this.logger.debug("[BodhiExtClient] No listeners for broadcast:",e.message)})}async getExtensionIdFromExt(){this.logger.debug("[BodhiExtClient] Getting extension ID from bodhi-browser-ext");const e=await this.sendExtRequest("get_extension_id");return this.logger.debug("[BodhiExtClient] Extension ID response:",e),e.extension_id}async handleApiRequest(e){const{requestId:t}=e;this.logger.debug("[BodhiExtClient] Handling API request:",e.request);try{let r=e.request.headers||{};if(e.request.authenticated){const o=await this._getAccessTokenRaw();if(!o)return{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,error:{message:"Not authenticated. Please log in first.",type:"auth_error"}};r={...r,Authorization:`Bearer ${o}`},this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request")}const i=await this.sendApiRequest(e.request.method,e.request.endpoint,e.request.body,r);return{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,response:i}}catch(r){return this.logger.error("[BodhiExtClient] API request failed:",r),{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,error:{message:r instanceof Error?r.message:"Unknown error",type:"network_error"}}}}async handleExtClientRequest(e){const{requestId:t,request:r}=e,{action:i,params:o}=r;this.logger.debug(`[BodhiExtClient] Handling action: ${i}`);try{let s={};switch(i){case p.DISCOVER_EXTENSION:{const a=o;await this.init(a),this.logger.info("[BodhiExtClient] Discovery successful:",{extensionId:this.extensionId,environment:y}),s={extensionId:this.extensionId,environment:y};break}case p.SET_EXTENSION_ID:{const{extensionId:a}=o;this.extensionId=a,this.state="ready",this.logger.info("[BodhiExtClient] Extension ID set:",{extensionId:a}),s={success:!0};break}case p.GET_EXTENSION_ID:{const a=await this.sendExtRequestRaw(g.EXT_ACTIONS.GET_EXTENSION_ID,o);return g.isExtError(a.response)?{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:a.response.error.message||`Extension request failed to get extension ID: ${JSON.stringify(a.response)}`,type:a.response.error.type}}}:{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:a.response}}case p.LOGIN:{const a=o;await this.login(a),this.broadcastAuthStateChange();break}case p.LOGOUT:await this.logout(),this.broadcastAuthStateChange();break;case p.GET_AUTH_STATE:s={authState:await this.getAuthState()};break;default:return{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:`Unknown action: ${i}`,type:"UNKNOWN_ACTION"}}}}return{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:s}}catch(s){return this.logger.error("[BodhiExtClient] Unexpected error:",s),{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:s instanceof Error?s.message:`Unexpected error: ${JSON.stringify(s)}`}}}}}async handleAction(e){switch(e.type){case h.EXT2EXT_CLIENT_API_REQUEST:return this.handleApiRequest(e);case h.EXT2EXT_CLIENT_REQUEST:return this.handleExtClientRequest(e);default:{const{requestId:t}=e;return this.logger.error("[BodhiExtClient] Unknown message type:",e.type),{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:`Unknown message type: ${e.type}`,type:"UNKNOWN_MESSAGE_TYPE"}}}}}}async login(e){if(!(this.isAuthenticating||(await this.getAuthState()).status==="authenticated")){this.isAuthenticating=!0;try{if(!this.extensionId)throw new Error("Extension not discovered. Please detect Bodhi extension before login.");e?.flowType==="redirect"&&this.logger.warn("Extension mode does not support redirect flow type; using popup instead");const r=e?.userRole??this.userRole,i=new n.AccessRequestBuilder(this.authClientId).requestedRole(r).flowType("popup");e?.requested&&i.requested(e.requested);const o=i.build(),s=await this.requestAccess(o);if(n.isApiResultOperationError(s))throw n.createOperationError(s.error.message,s.error.type);if(!n.isApiResultSuccess(s))throw n.createOperationError(`Access request failed: HTTP ${s.status}`,"auth_error");const{id:a,review_url:E}=s.body;await chrome.tabs.create({url:E});const c=await this.pollAccessRequestStatus(a,{intervalMs:e?.pollIntervalMs??n.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??n.DEFAULT_POLL_TIMEOUT_MS});if(c.status!=="approved")throw n.createOperationError(`Access request ${c.status}`,"auth_error");const d=`openid profile email roles ${c.access_request_scope??""}`.trim();await this.performOAuthPkce(d)}finally{this.isAuthenticating=!1}}}async exchangeCodeForTokens(e){if((await this.getAuthState()).status==="authenticated")return;const{codeVerifier:r}=await chrome.storage.session.get("codeVerifier"),i=chrome.identity.getRedirectURL("callback"),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:i,client_id:this.authClientId,code_verifier:r})});if(!o.ok){const a=await o.text();throw new Error(`Token exchange failed: ${o.status} ${a}`)}const s=await o.json();await this.storeTokens({accessToken:s.access_token,refreshToken:s.refresh_token,idToken:s.id_token,expiresIn:s.expires_in})}async getAuthState(){const e=await this._getAccessTokenRaw();if(!e)return{status:"unauthenticated",user:null,accessToken:null,error:null};try{const t=this.parseJwt(e);return{status:"authenticated",user:{sub:t.sub,email:t.email,name:t.name,given_name:t.given_name,family_name:t.family_name,preferred_username:t.preferred_username},accessToken:e,error:null}}catch(t){return this.logger.error("Failed to parse token:",t),{status:"unauthenticated",user:null,accessToken:null,error:null}}}async logout(){const{refreshToken:e}=await chrome.storage.session.get("refreshToken");if(e)try{await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:e,client_id:this.authClientId,token_type_hint:"refresh_token"})})}catch(t){this.logger.warn("[OAuth] Token revocation failed:",t)}await this.clearTokens()}async requestAccess(e){try{const t=await this.sendApiRequest("POST","/bodhi/v1/apps/request-access",e);return{body:t.body,status:t.status,headers:t.headers}}catch(t){return{error:{message:t instanceof Error?t.message:String(t),type:"extension_error"}}}}async getAccessRequestStatus(e){try{const t=await this.sendApiRequest("GET",`/bodhi/v1/apps/access-requests/${e}?app_client_id=${encodeURIComponent(this.authClientId)}`);return{body:t.body,status:t.status,headers:t.headers}}catch(t){return{error:{message:t instanceof Error?t.message:String(t),type:"extension_error"}}}}async pollAccessRequestStatus(e,t){return n.pollAccessRequestUntilResolved(r=>this.getAccessRequestStatus(r),e,t)}async performOAuthPkce(e){const t=_.generateCodeVerifier(),r=await _.generateCodeChallenge(t),i=_.generateCodeVerifier();await chrome.storage.session.set({codeVerifier:t,state:i,authInProgress:!0});const o=chrome.identity.getRedirectURL("callback"),s=new URL(this.authEndpoints.authorize);return s.searchParams.set("client_id",this.authClientId),s.searchParams.set("response_type","code"),s.searchParams.set("redirect_uri",o),s.searchParams.set("scope",e),s.searchParams.set("code_challenge",r),s.searchParams.set("code_challenge_method","S256"),s.searchParams.set("state",i),new Promise((a,E)=>{chrome.identity.launchWebAuthFlow({url:s.toString(),interactive:!0},async c=>{if(await chrome.storage.session.set({authInProgress:!1}),chrome.runtime.lastError){await chrome.storage.session.remove(["codeVerifier","state"]),E(chrome.runtime.lastError);return}if(!c){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("No redirect URL received","oauth-error"));return}try{const u=new URL(c),d=u.searchParams.get("code"),T=u.searchParams.get("state"),{state:m}=await chrome.storage.session.get("state");if(T!==m){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("State mismatch","oauth-error"));return}if(!d){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("No authorization code","oauth-error"));return}await this.exchangeCodeForTokens(d),await chrome.storage.session.remove(["codeVerifier","state"]);const l=await this.getAuthState();if(l.status!=="authenticated")throw n.createOperationError("Login failed","oauth-error");a(l)}catch(u){await chrome.storage.session.remove(["codeVerifier","state"]),E(u)}})})}async sendExtRequest(e,t){const r=await this.sendExtRequestRaw(e,t);if(g.isExtError(r.response))throw this.logger.error("[BodhiExtClient] Extension error:",r.response.error),new Error(r.response.error.message||`Extension request failed: ${JSON.stringify(r.response)}`);return r.response}async sendApiRequest(e,t,r,i){if(!this.extensionId)throw new Error(this.createErrorClientNotInitialized({type:"api",method:e,endpoint:t}));this.logger.debug(`[BodhiExtClient] Sending API_REQUEST: method=${e}, endpoint=${t}`,r?{body:r}:"");const o=crypto.randomUUID(),s={type:g.MESSAGE_TYPES.API_REQUEST,requestId:o,request:{method:e,endpoint:t,body:r,headers:i}};return this.logger.debug(`[BodhiExtClient] Request ID: ${o}, Extension: ${this.extensionId}`),new Promise((a,E)=>{try{chrome.runtime.sendMessage(this.extensionId,s,c=>{if(chrome.runtime.lastError){this.logger.error(`[BodhiExtClient] Chrome runtime error for request ${o}:`,chrome.runtime.lastError),E(new Error(chrome.runtime.lastError.message));return}if(this.logger.debug(`[BodhiExtClient] Response for request ${o}:`,c),!c){this.logger.error(`[BodhiExtClient] No response received for request ${o}`),E(new Error("No response from extension"));return}c.type===g.MESSAGE_TYPES.API_RESPONSE&&c.requestId===o?"error"in c?(this.logger.error(`[BodhiExtClient] API error for ${o}:`,c.error),E(new Error(c.error.message))):(this.logger.debug(`[BodhiExtClient] ✓ Valid API_RESPONSE for ${o}`),a(c.response)):(this.logger.error(`[BodhiExtClient] Invalid response format for ${o}:`,c),E(new Error("Invalid response format")))})}catch(c){this.logger.error(`[BodhiExtClient] Exception sending message for ${o}:`,c),E(c)}})}async sendExtRequestRaw(e,t){if(!this.extensionId)throw new Error(this.createErrorClientNotInitialized({type:"ext",action:e,params:t}));this.logger.debug(`[BodhiExtClient] Sending EXT_REQUEST (raw): action=${e}`,t?{params:t}:"");const r=crypto.randomUUID(),i={type:g.MESSAGE_TYPES.EXT_REQUEST,requestId:r,request:{action:e,params:t}};return this.logger.debug(`[BodhiExtClient] Request ID: ${r}, Extension: ${this.extensionId}`),new Promise((o,s)=>{try{chrome.runtime.sendMessage(this.extensionId,i,a=>{if(chrome.runtime.lastError){this.logger.error(`[BodhiExtClient] Chrome runtime error for request ${r}:`,chrome.runtime.lastError),s(new Error(chrome.runtime.lastError.message));return}if(this.logger.debug(`[BodhiExtClient] Response for request ${r}:`,a),!a){this.logger.error(`[BodhiExtClient] No response received for request ${r}`),s(new Error("No response from extension"));return}a.type===g.MESSAGE_TYPES.EXT_RESPONSE&&a.requestId===r?(this.logger.debug(`[BodhiExtClient] ✓ Valid EXT_RESPONSE for ${r}`),o(a)):(this.logger.error(`[BodhiExtClient] Invalid response format for ${r}:`,a),s(new Error("Invalid response format")))})}catch(a){this.logger.error(`[BodhiExtClient] Exception sending message for ${r}:`,a),s(a)}})}async handleStreamRequest(e,t){const{requestId:r,request:i}=t,{method:o,endpoint:s,body:a,headers:E,authenticated:c}=i;this.logger.debug("[BodhiExtClient] Processing stream request:",{requestId:r,method:o,endpoint:s,authenticated:c}),this.extensionId||e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:this.createErrorClientNotInitialized(t),type:"extension_error"}});try{let u={...E};if(c!==!1){const l=await this._getAccessTokenRaw();if(!l){e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Not authenticated. Please log in first.",type:"extension_error"}});return}u={...u,Authorization:`Bearer ${l}`},this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request")}const d=chrome.runtime.connect(this.extensionId,{name:g.BODHI_STREAM_PORT});this.activeStreamPorts.set(r,d);const T=setTimeout(()=>{this.activeStreamPorts.has(r)&&(this.logger.error(`[BodhiExtClient] Stream timeout for ${r}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Stream request timed out",type:"timeout_error"}}),this.cleanupStreamPort(r))},_.STREAM_TIMEOUT);d.onMessage.addListener(l=>{if(g.isStreamChunk(l)){const I=l.response,P=I.body;I.status>=400?e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_API_ERROR,requestId:r,response:I}):P?.done?(e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_DONE,requestId:r}),this.logger.info(`[BodhiExtClient] Stream complete for ${r}`),clearTimeout(T),this.cleanupStreamPort(r)):e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_CHUNK,requestId:r,response:I})}else g.isStreamApiError(l)?(this.logger.error(`[BodhiExtClient] Stream API error for ${r}: ${l.response.status}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_API_ERROR,requestId:r,response:l.response})):g.isStreamError(l)&&(this.logger.error(`[BodhiExtClient] Stream error for ${r}:`,l.error.message),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:`stream error: ${JSON.stringify(l)}`,type:"extension_error"}}),clearTimeout(T),this.cleanupStreamPort(r))}),d.onDisconnect.addListener(()=>{clearTimeout(T),this.activeStreamPorts.has(r)&&(this.logger.error(`[BodhiExtClient] Bodhi port disconnected for ${r}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Connection to Bodhi extension closed unexpectedly",type:"network_error"}}),this.activeStreamPorts.delete(r))});const m={type:g.MESSAGE_TYPES.STREAM_REQUEST,requestId:r,request:{method:o,endpoint:s,body:a,headers:u}};this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:",m),d.postMessage(m)}catch(u){const d=u;this.logger.error("[BodhiExtClient] Stream error:",JSON.stringify(d.message)),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:`uncaught error: ${JSON.stringify({error:d,message:d.message})}`,type:"extension_error"}})}}cleanupStreamPort(e){const t=this.activeStreamPorts.get(e);if(t){try{t.disconnect()}catch{}this.activeStreamPorts.delete(e)}}async storeTokens(e){const t=Date.now()+(e.expiresIn||3600)*1e3;await chrome.storage.session.set({accessToken:e.accessToken,refreshToken:e.refreshToken,idToken:e.idToken,expiresAt:t})}async _getAccessTokenRaw(){const{accessToken:e,expiresAt:t}=await chrome.storage.session.get(["accessToken","expiresAt"]);if(!e||!t)return null;if(Date.now()>=t-5*1e3){const{refreshToken:r}=await chrome.storage.session.get("refreshToken");return r?this._tryRefreshToken(r):null}return e}async _tryRefreshToken(e){if(this.refreshPromise)return this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise;this.refreshPromise=this._doRefreshToken(e);try{return await this.refreshPromise}finally{this.refreshPromise=null}}async _doRefreshToken(e){this.logger.debug("Refreshing access token");try{const t=await n.refreshAccessToken(this.authEndpoints.token,e,this.authClientId);if(t.success)return await this._storeRefreshedTokens(t.tokens),this.logger.info("Token refreshed successfully"),this.broadcastAuthStateChange(),t.tokens.access_token;if(t.error==="invalid_grant")return this.logger.warn("Refresh token expired or revoked, clearing tokens and logging out"),await this.clearTokens(),this.broadcastAuthStateChange(),null}catch(t){this.logger.warn("Token refresh failed:",t)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),n.createOperationError("Access token expired and unable to refresh. Try logging out and logging in again.","token_refresh_failed")}async _storeRefreshedTokens(e){const t=Date.now()+e.expires_in*1e3,r={accessToken:e.access_token,expiresAt:t};e.refresh_token&&(r.refreshToken=e.refresh_token),e.id_token&&(r.idToken=e.id_token),await chrome.storage.session.set(r)}async clearTokens(){await chrome.storage.session.remove(["accessToken","refreshToken","idToken","expiresAt","codeVerifier","state","authInProgress","bodhiUserInfo"])}parseJwt(e){const r=e.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),i=decodeURIComponent(atob(r).split("").map(o=>"%"+("00"+o.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(i)}createErrorClientNotInitialized(e){return`Client not initialized. Extension discovery not triggered nor extensionId set, cannot handle request: ${JSON.stringify(e)}`}};_.STREAM_TIMEOUT=6e4;let R=_;const k="production";exports.BodhiExtClient=R;exports.DEFAULT_API_TIMEOUT_MS=N;exports.DISCOVERY_ATTEMPTS=C;exports.DISCOVERY_ATTEMPT_TIMEOUT=A;exports.DISCOVERY_ATTEMPT_WAIT_MS=w;exports.DISCOVERY_TIMEOUT_MS=x;exports.EXT2EXT_CLIENT_ACTIONS=p;exports.EXT2EXT_CLIENT_MESSAGE_TYPES=h;exports.EXT2EXT_CLIENT_STREAM_PORT=f;exports.EXT_BUILD_MODE=k;exports.ExtUIClient=b;exports.isExtClientApiError=O;
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const n=require("@bodhiapp/bodhi-js-core"),g=require("@bodhiapp/bodhi-browser-types"),h={EXT2EXT_CLIENT_REQUEST:"EXT2EXT_CLIENT_REQUEST",EXT2EXT_CLIENT_RESPONSE:"EXT2EXT_CLIENT_RESPONSE",EXT2EXT_CLIENT_BROADCAST:"EXT2EXT_CLIENT_BROADCAST",EXT2EXT_CLIENT_API_REQUEST:"EXT2EXT_CLIENT_API_REQUEST",EXT2EXT_CLIENT_API_RESPONSE:"EXT2EXT_CLIENT_API_RESPONSE",EXT2EXT_CLIENT_STREAM_REQUEST:"EXT2EXT_CLIENT_STREAM_REQUEST",EXT2EXT_CLIENT_STREAM_CHUNK:"EXT2EXT_CLIENT_STREAM_CHUNK",EXT2EXT_CLIENT_STREAM_ERROR:"EXT2EXT_CLIENT_STREAM_ERROR",EXT2EXT_CLIENT_STREAM_API_ERROR:"EXT2EXT_CLIENT_STREAM_API_ERROR",EXT2EXT_CLIENT_STREAM_DONE:"EXT2EXT_CLIENT_STREAM_DONE"},f="ext2ext-client-stream",_={LOGIN:"login",LOGOUT:"logout",GET_AUTH_STATE:"getAuthState",DISCOVER_EXTENSION:"discoverBodhiExtension",GET_EXTENSION_ID:"get_extension_id",SET_EXTENSION_ID:"setExtensionId"},x=5e3,C=3,w=500,A=500,N=3e4;function P(S){return"error"in S}class v extends n.DirectClientBase{constructor(e,t){const r=n.createStoragePrefixWithBasePath(e.basePath,n.STORAGE_PREFIXES.EXT_DIRECT),i={authClientId:e.authClientId,authServerUrl:e.authServerUrl,userRole:e.userRole,storagePrefix:r,logLevel:e.logLevel,loggerPrefix:"DirectExtClient",apiTimeoutMs:e.apiTimeoutMs};super(i,t)}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;e?.flowType==="redirect"&&this.logger.warn("Extension mode does not support redirect flow type; using popup instead");const r=e?.userRole??this.userRole;e?.onProgress?.("requesting");const i=new n.AccessRequestBuilder(this.authClientId).requestedRole(r).flowType("popup");e?.requested&&i.requested(e.requested);const o=i.build(),s=await this.requestAccess(o),{id:a,review_url:E}=n.unwrapResponse(s);e?.onProgress?.("reviewing"),await chrome.tabs.create({url:E});const c=await this.pollAccessRequestStatus(a,{intervalMs:e?.pollIntervalMs??n.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??n.DEFAULT_POLL_TIMEOUT_MS});if(c.status!=="approved")throw n.createOperationError("auth_error",`Access request ${c.status}`);const u=c.access_request_scope;return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${u??""}`.trim())}async performOAuthPkce(e){const t=n.generateCodeVerifier(),r=await n.generateCodeChallenge(t),i=n.generateCodeVerifier();await chrome.storage.session.set({[this.storageKeys.CODE_VERIFIER]:t,[this.storageKeys.STATE]:i});const o=chrome.identity.getRedirectURL("callback"),s=new URL(this.authEndpoints.authorize);return s.searchParams.set("client_id",this.authClientId),s.searchParams.set("response_type","code"),s.searchParams.set("redirect_uri",o),s.searchParams.set("scope",e),s.searchParams.set("code_challenge",r),s.searchParams.set("code_challenge_method","S256"),s.searchParams.set("state",i),new Promise((a,E)=>{chrome.identity.launchWebAuthFlow({url:s.toString(),interactive:!0},async c=>{if(chrome.runtime.lastError){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(chrome.runtime.lastError);return}if(!c){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("oauth_error","No redirect URL received"));return}try{const u=new URL(c),d=u.searchParams.get("code"),T=u.searchParams.get("state"),m=await chrome.storage.session.get(this.storageKeys.STATE);if(T!==m[this.storageKeys.STATE]){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("oauth_error","State mismatch"));return}if(!d){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(n.createOperationError("oauth_error","No authorization code"));return}await this.exchangeCodeForTokens(d);const l=await this.getAuthState();if(l.status!=="authenticated")throw n.createOperationError("oauth_error","Login failed");this.setAuthState(l),await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),a(l)}catch(u){await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE]),E(u)}})})}async logout(){const t=(await chrome.storage.session.get(this.storageKeys.REFRESH_TOKEN))[this.storageKeys.REFRESH_TOKEN];if(t)try{const i=new URLSearchParams({token:t,client_id:this.authClientId,token_type_hint:"refresh_token"});await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:i})}catch(i){this.logger.warn("Token revocation failed:",i)}await chrome.storage.session.remove([this.storageKeys.ACCESS_TOKEN,this.storageKeys.REFRESH_TOKEN,this.storageKeys.EXPIRES_AT]);const r={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(r),r}async exchangeCodeForTokens(e){const r=(await chrome.storage.session.get(this.storageKeys.CODE_VERIFIER))[this.storageKeys.CODE_VERIFIER],i=chrome.identity.getRedirectURL("callback"),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:i,client_id:this.authClientId,code_verifier:r})});if(!o.ok){const E=await o.text();throw new Error(`Token exchange failed: ${o.status} ${E}`)}const s=await o.json(),a=Date.now()+(s.expires_in||3600)*1e3;await chrome.storage.session.set({[this.storageKeys.ACCESS_TOKEN]:s.access_token,[this.storageKeys.REFRESH_TOKEN]:s.refresh_token,[this.storageKeys.EXPIRES_AT]:a}),await chrome.storage.session.remove([this.storageKeys.CODE_VERIFIER,this.storageKeys.STATE])}async _storageGet(e){const r=(await chrome.storage.session.get(e))[e];return r!==void 0?String(r):null}async _storageSet(e){await chrome.storage.session.set(e)}async _storageRemove(e){await chrome.storage.session.remove(e)}_getRedirectUri(){return chrome.identity.getRedirectURL("callback")}}class X{constructor(e={},t){this.state={type:"extension",extension:"not-initialized",extensionId:null,server:n.PENDING_EXTENSION_READY},this.extensionId=null,this.broadcastListenerActive=!1,this.config=e,this.logger=new n.Logger("ExtClient",e?.logLevel||"warn"),this.onStateChange=t??n.NOOP_STATE_CALLBACK,this.apiTimeoutMs=e.apiTimeoutMs??N,this.authClientId=e.authClientId??""}setState(e){this.state=e,this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}setupBroadcastListener(){this.broadcastListenerActive||(this.broadcastListenerActive=!0,chrome.runtime.onMessage.addListener(e=>{const t=e;return t?.type===h.EXT2EXT_CLIENT_BROADCAST&&t.event==="authStateChanged"&&this.handleAuthStateChangedBroadcast(),!1}),this.logger.debug("Broadcast listener setup complete"))}async handleAuthStateChangedBroadcast(){this.logger.debug("Received authStateChanged broadcast, refreshing auth state");const e=await this.getAuthState();this.setAuthState(e)}generateRequestId(){return crypto.randomUUID()}getState(){return this.state}isClientInitialized(){return this.state.extension==="ready"}isServerReady(){return this.isClientInitialized()&&this.state.server.status==="ready"}async init(e={}){if(!e.testConnection&&!e.selectedConnection){this.logger.info("No testConnection or selectedConnection, returning not-initialized state");const i=n.createExtensionStateNotInitialized();return this.setState(i),i}if(this.extensionId&&!e.testConnection)return this.logger.debug("Already initialized with extensionId, skipping discovery"),this.state;const t=e.timeoutMs??this.config.initParams?.extension?.timeoutMs??x,r=e.savedState?.extensionId;try{if(!this.extensionId){if(r)this.logger.info("Restoring with known extensionId:",r),await this.sendExtMessageWithTimeout(_.SET_EXTENSION_ID,{extensionId:r},t),this.extensionId=r;else{this.logger.info("Discovering bodhi-browser extension...");const s={attempts:this.config.initParams?.extension?.attempts,attemptWaitMs:this.config.initParams?.extension?.attemptWaitMs,attemptTimeout:this.config.initParams?.extension?.attemptTimeout},a=await this.sendExtMessageWithTimeout(_.DISCOVER_EXTENSION,s,t);this.extensionId=a.extensionId,this.logger.info("Extension discovered:",this.extensionId)}this.setupBroadcastListener()}const i={type:"extension",extension:"ready",extensionId:this.extensionId,server:n.PENDING_EXTENSION_READY};let o=n.PENDING_EXTENSION_READY;if(e.testConnection)try{o=await this.getServerState(),this.logger.info("Server connectivity tested, state:",o.status)}catch(s){this.logger.error("Failed to get server state:",s),o=n.BACKEND_SERVER_NOT_REACHABLE}return this.setState({...i,server:o}),this.state}catch(i){this.logger.error("Failed to initialize extension:",i),this.extensionId=null;const o=n.createExtensionStateNotFound();return this.setState(o),this.state}}async sendExtMessageWithTimeout(e,t,r=1e4){const i=new Promise((o,s)=>setTimeout(()=>s(new Error("Timeout")),r));return Promise.race([this.sendExtRequest(e,t),i])}async sendExtRequest(e,t){try{const r=this.generateRequestId(),i=await chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_REQUEST,requestId:r,request:{action:e,params:t}});if(!i)throw n.createOperationError("extension_error","No response from background script");if(i.type!==h.EXT2EXT_CLIENT_RESPONSE)throw n.createOperationError("extension_error","Invalid response type from background script");const o=i.response;if(g.isExtError(o)){const s=o.error.type||"extension_error";throw n.createOperationError(s,o.error.message)}return o}catch(r){throw r instanceof n.BodhiError?r:n.createOperationError("extension_error",r instanceof Error?r.message:"Unknown error occurred")}}async sendRawApiMessage(e,t,r,i,o){const s=this.generateRequestId();return await chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_API_REQUEST,requestId:s,request:{method:e,endpoint:t,body:r,headers:i,authenticated:o}})}async sendApiRequest(e,t,r,i,o){try{const s=new Promise((E,c)=>setTimeout(()=>c(new Error(`[bodhi-js-sdk/ext] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`)),this.apiTimeoutMs)),a=await Promise.race([this.sendRawApiMessage(e,t,r,i,o),s]);if(P(a)){const E=a.error.type||"extension_error";throw new n.BodhiError(E,a.error.message)}return a.response}catch(s){if(s instanceof n.BodhiError)throw s;const a=s instanceof Error?s.message:String(s);throw new n.BodhiError("network_error",a)}}async login(e){return new Promise((t,r)=>{const i=async o=>{if(o&&typeof o=="object"&&"type"in o&&o.type==="EXT2EXT_CLIENT_BROADCAST"&&"event"in o&&o.event==="authStateChanged"){chrome.runtime.onMessage.removeListener(i);try{const s=await this.getAuthState();if(n.isAuthError(s)){r(n.createOperationError("auth_error",`Login failed: ${s.error?.message}`));return}if(s.status!=="authenticated"){r(n.createOperationError("auth_error","Login failed: User is not logged in"));return}this.setAuthState(s),t(s)}catch(s){r(s)}}};chrome.runtime.onMessage.addListener(i),this.sendExtRequest(_.LOGIN,e).catch(o=>{chrome.runtime.onMessage.removeListener(i),r(o)})})}async logout(){await this.sendExtRequest(_.LOGOUT);const e={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(e),e}async getAuthState(){return this.isClientInitialized()?(await this.sendExtRequest(_.GET_AUTH_STATE)).authState:n.INITIAL_AUTH_STATE}async pingApi(){return this.sendApiRequest("GET","/ping")}async getServerState(){let e;try{e=await this.sendApiRequest("GET","/bodhi/v1/info")}catch(o){const s=o instanceof Error?o.message:"Connection failed",a=o instanceof n.BodhiError?o.code:"extension_error";return{status:"not-reachable",version:null,error:{message:s,type:a}}}if(e.status>=400)return{status:"not-reachable",version:null,error:{message:"API error from server",type:"extension_error"}};const t=e.body,r=t.version||"unknown",i={deployment:t.deployment??null,client_id:t.client_id??null};switch(t.status){case"ready":return{status:"ready",version:r,error:null,...i};case"setup":return{status:"setup",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Setup required",type:"extension_error"},...i};case"resource_admin":return{status:"resource_admin",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Resource admin required",type:"extension_error"},...i};case"error":return{status:"error",version:r,error:t.error?{message:t.error.message,type:t.error.type}:{message:"Server error",type:"extension_error"},...i};default:return{status:"not-reachable",version:null,error:{message:"Unknown server status",type:"extension_error"}}}}async*stream(e,t,r,i,o=!0){const s=this.generateRequestId();this.logger.debug("Starting stream",{method:e,endpoint:t,requestId:s});const a=chrome.runtime.connect({name:f}),c=new ReadableStream({start:u=>{a.onMessage.addListener(d=>{if(d.requestId===s)switch(d.type){case h.EXT2EXT_CLIENT_STREAM_DONE:this.logger.debug("Stream complete",{requestId:s}),u.close(),a.disconnect();break;case h.EXT2EXT_CLIENT_STREAM_ERROR:this.logger.error("Stream error",{requestId:s,error:JSON.stringify(d.error)}),u.error(new n.BodhiError("extension_error",d.error.message)),a.disconnect();break;case h.EXT2EXT_CLIENT_STREAM_API_ERROR:{const T=d;this.logger.error("Stream API error",{requestId:s,error:T.response.body?.error}),u.error(new n.BodhiApiError(T.response.status,T.response.body,T.response.body?.error?.message||"API error")),a.disconnect();break}case h.EXT2EXT_CLIENT_STREAM_CHUNK:{const T=d;g.isApiSuccessResponse(T.response)&&u.enqueue(T.response.body);break}}}),a.onDisconnect.addListener(()=>{this.logger.debug("Port disconnected",{requestId:s});try{u.error(new n.BodhiError("connection_closed","Connection closed unexpectedly"))}catch{}}),a.postMessage({type:h.EXT2EXT_CLIENT_STREAM_REQUEST,requestId:s,request:{method:e,endpoint:t,body:r,headers:i,authenticated:o}})}}).getReader();try{for(;;){const{done:u,value:d}=await c.read();if(u){this.logger.debug("Stream iteration complete");break}yield d}}finally{c.releaseLock()}}get chat(){return this._chat??=new n.Chat(this)}get models(){return this._models??=new n.Models(this)}get embeddings(){return this._embeddings??=new n.Embeddings(this)}get toolsets(){return this._toolsets??=new n.Toolsets(this)}get mcps(){return this._mcps??=new n.Mcps(this)}async requestAccess(e){return this.sendApiRequest("POST","/bodhi/v1/apps/request-access",e,{},!1)}async getAccessRequestStatus(e){return this.sendApiRequest("GET",`/bodhi/v1/apps/access-requests/${e}?app_client_id=${encodeURIComponent(this.authClientId)}`,void 0,{},!1)}async pollAccessRequestStatus(e,t){return n.pollAccessRequestUntilResolved(r=>this.getAccessRequestStatus(r),e,t)}serialize(){return{extensionId:this.extensionId??void 0}}async debug(){return{type:"ExtClient",state:this.state,authState:await this.getAuthState()}}}class L extends n.BaseFacadeClient{constructor(e,t,r){const i=t||{},o={basePath:i.basePath||"/",authServerUrl:i.authServerUrl||"https://id.getbodhi.app/realms/bodhi",userRole:i.userRole||"scope_user_user",logLevel:i.logLevel||"warn",apiTimeoutMs:i.apiTimeoutMs,initParams:i.initParams};super(e,o,r)}createLogger(e){return new n.Logger("ExtUIClient",e.logLevel)}createStoragePrefix(e){return n.createStoragePrefixWithBasePath(e.basePath,n.STORAGE_PREFIXES.EXT)}createExtClient(e,t){return new X({authClientId:this.authClientId,logLevel:e.logLevel,apiTimeoutMs:e.apiTimeoutMs,initParams:e.initParams},t)}createDirectClient(e,t,r){return new v({authClientId:e,authServerUrl:t.authServerUrl,userRole:t.userRole,logLevel:t.logLevel,basePath:t.basePath,apiTimeoutMs:t.apiTimeoutMs},r)}}const b=["ggedphdcbekjlomjaidbajglgihbeaon"],M=["bjdjhiombmfbcoeojijpfckljjghmjbf"],y="production",p=class p{constructor(e,t){this.isAuthenticating=!1,this.state="setup",this.listenersInitialized=!1,this.refreshPromise=null,this.activeStreamPorts=new Map,this.authClientId=e,this.authServerUrl=t?.authServerUrl||"https://id.getbodhi.app/realms/bodhi",this.userRole=t?.userRole||"scope_user_user",this.extensionId=t?.extensionId,this.logger=new n.Logger("BodhiExtClient",t?.logLevel||"warn"),this.attempts=t?.attempts??C,this.attemptWaitMs=t?.attemptWaitMs??w,this.attemptTimeout=t?.attemptTimeout??A,this.authEndpoints={authorize:`${this.authServerUrl}/protocol/openid-connect/auth`,token:`${this.authServerUrl}/protocol/openid-connect/token`,userinfo:`${this.authServerUrl}/protocol/openid-connect/userinfo`,logout:`${this.authServerUrl}/protocol/openid-connect/logout`,revoke:`${this.authServerUrl}/protocol/openid-connect/revoke`},this.extensionId?this.logger.info(`[BodhiExtClient] Created client for extension: ${this.extensionId}`):this.logger.info("[BodhiExtClient] Created client without extension ID (call init() to discover)")}static base64UrlEncode(e){return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}static generateCodeVerifier(){const e=new Uint8Array(32);return crypto.getRandomValues(e),p.base64UrlEncode(e.buffer)}static async generateCodeChallenge(e){const r=new TextEncoder().encode(e),i=await crypto.subtle.digest("SHA-256",r);return p.base64UrlEncode(i)}getState(){return this.state}getExtensionIdsForEnvironment(){const t=y!=="production"?b:M;return this.logger.info("[Ext2Ext/Registry] Environment: production"),this.logger.debug("[Ext2Ext/Registry] Using extension IDs:",t),t}async pingExtension(e){return this.logger.debug(`[Ext2Ext/Discovery] Pinging extension: ${e} with timeout ${this.attemptTimeout}ms`),new Promise((t,r)=>{const i=setTimeout(()=>{this.logger.debug(`[Ext2Ext/Discovery] Timeout waiting for extension ${e}`),r(new Error("Timeout"))},this.attemptTimeout);try{const o={type:g.MESSAGE_TYPES.EXT_REQUEST,requestId:crypto.randomUUID(),request:{action:"get_extension_id"}};this.logger.debug(`[Ext2Ext/Discovery] Sending message to ${e}:`,o),chrome.runtime.sendMessage(e,o,s=>{if(clearTimeout(i),chrome.runtime.lastError){this.logger.error(`[Ext2Ext/Discovery] Error from extension ${e}:`,chrome.runtime.lastError.message),r(new Error(chrome.runtime.lastError.message));return}this.logger.debug(`[Ext2Ext/Discovery] Response from ${e}:`,s);const a=s;a&&a.type===g.MESSAGE_TYPES.EXT_RESPONSE?(this.logger.debug(`[Ext2Ext/Discovery] ✓ Extension ${e} responded`),t(!0)):(this.logger.error(`[Ext2Ext/Discovery] Invalid response from ${e}:`,s),r(new Error("Invalid response")))})}catch(o){this.logger.error(`[Ext2Ext/Discovery] Exception pinging ${e}:`,o),clearTimeout(i),r(o)}})}sleep(e){return new Promise(t=>setTimeout(t,e))}async discoverBodhiExtension(e){const{attempts:t,attemptWaitMs:r,attemptTimeout:i}=e;this.logger.info(`[Ext2Ext/Discovery] Starting discovery: ${t} attempts per ID, ${i}ms timeout, ${r}ms between attempts`);const o=this.getExtensionIdsForEnvironment();this.logger.debug(`[Ext2Ext/Discovery] Will try ${o.length} extension(s):`,o);for(const E of o){for(let c=1;c<=t;c++){this.logger.debug(`[Ext2Ext/Discovery] Trying ${E} - attempt ${c}/${t}`);try{return await this.pingExtension(E),this.logger.info(`[Ext2Ext/Discovery] ✓ Found: ${E} on attempt ${c}`),{success:!0,extensionId:E}}catch(u){this.logger.debug(`[Ext2Ext/Discovery] Attempt ${c} failed for ${E}: ${u instanceof Error?u.message:"Unknown error"}`),c<t&&await this.sleep(r)}}this.logger.warn(`[Ext2Ext/Discovery] ✗ Not found: ${E} after ${t} attempts`)}const s=o.join(", "),a=`Extension not found. Tried ${o.length} IDs with ${t} attempts each: ${s}`;return this.logger.error(`[Ext2Ext/Discovery] ${a}`),{success:!1,error:a}}setupListeners(){if(this.listenersInitialized){this.logger.debug("[BodhiExtClient] Listeners already initialized, skipping");return}this.listenersInitialized=!0,chrome.runtime.onMessage.addListener((e,t,r)=>e.type!==h.EXT2EXT_CLIENT_REQUEST&&e.type!==h.EXT2EXT_CLIENT_API_REQUEST?!1:this.state!=="ready"&&!(e.type===h.EXT2EXT_CLIENT_REQUEST&&e.request.action===_.DISCOVER_EXTENSION)?(e.type===h.EXT2EXT_CLIENT_REQUEST?r({type:h.EXT2EXT_CLIENT_RESPONSE,requestId:e.requestId,response:{error:{message:this.createErrorClientNotInitialized(e),type:"NOT_INITIALIZED"}}}):r({type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:e.requestId,error:{message:`Client not initialized. Extension discovery not complete, cannot handle type:${e.type}, message:${JSON.stringify(e)}`,type:"NOT_INITIALIZED"}}),!0):(this.logger.debug(`[BodhiExtClient] Processing message.type=${e.type}`),(async()=>{const i=await this.handleAction(e);r(i)})(),!0)),chrome.runtime.onConnect.addListener(e=>{if(e.name!==f){this.logger.debug("[BodhiExtClient] Ignoring port with name:",e.name);return}this.logger.info("[BodhiExtClient] Streaming port connected"),e.onMessage.addListener(async t=>{if(t.type!==h.EXT2EXT_CLIENT_STREAM_REQUEST){this.logger.warn("[BodhiExtClient] Unknown stream message type:",t.type),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:t.requestId,error:{message:"Unknown stream message type",type:"extension_error"}});return}await this.handleStreamRequest(e,t)}),e.onDisconnect.addListener(()=>{this.logger.info("[BodhiExtClient] Streaming port disconnected")})}),this.logger.info("[BodhiExtClient] Streaming listeners initialized")}async init(e){if(this.setupListeners(),this.extensionId){this.state="ready",this.logger.warn(`[BodhiExtClient] Already initialized with extension ID: ${this.extensionId}`);return}this.logger.info("[BodhiExtClient] Starting discovery");const t={attempts:e?.attempts??this.attempts,attemptWaitMs:e?.attemptWaitMs??this.attemptWaitMs,attemptTimeout:e?.attemptTimeout??this.attemptTimeout},r=await this.discoverBodhiExtension(t);if(!r.success||!r.extensionId)throw new Error(r.error||"Discovery failed");this.extensionId=r.extensionId,this.state="ready",this.logger.info(`[BodhiExtClient] ✓ Initialized: ${this.extensionId}`)}broadcastAuthStateChange(){chrome.runtime.sendMessage({type:h.EXT2EXT_CLIENT_BROADCAST,event:"authStateChanged"}).catch(e=>{this.logger.debug("[BodhiExtClient] No listeners for broadcast:",e.message)})}async getExtensionIdFromExt(){this.logger.debug("[BodhiExtClient] Getting extension ID from bodhi-browser-ext");const e=await this.sendExtRequest("get_extension_id");return this.logger.debug("[BodhiExtClient] Extension ID response:",e),e.extension_id}async handleApiRequest(e){const{requestId:t}=e;this.logger.debug("[BodhiExtClient] Handling API request:",e.request);try{let r=e.request.headers||{};if(e.request.authenticated){const o=await this._getAccessTokenRaw();if(!o)return{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,error:{message:"Not authenticated. Please log in first.",type:"auth_error"}};r={...r,Authorization:`Bearer ${o}`},this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request")}const i=await this.sendApiRequest(e.request.method,e.request.endpoint,e.request.body,r);return{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,response:i}}catch(r){return this.logger.error("[BodhiExtClient] API request failed:",r),{type:h.EXT2EXT_CLIENT_API_RESPONSE,requestId:t,error:{message:r instanceof Error?r.message:"Unknown error",type:"network_error"}}}}async handleExtClientRequest(e){const{requestId:t,request:r}=e,{action:i,params:o}=r;this.logger.debug(`[BodhiExtClient] Handling action: ${i}`);try{let s={};switch(i){case _.DISCOVER_EXTENSION:{const a=o;await this.init(a),this.logger.info("[BodhiExtClient] Discovery successful:",{extensionId:this.extensionId,environment:y}),s={extensionId:this.extensionId,environment:y};break}case _.SET_EXTENSION_ID:{const{extensionId:a}=o;this.extensionId=a,this.state="ready",this.logger.info("[BodhiExtClient] Extension ID set:",{extensionId:a}),s={success:!0};break}case _.GET_EXTENSION_ID:{const a=await this.sendExtRequestRaw(g.EXT_ACTIONS.GET_EXTENSION_ID,o);return g.isExtError(a.response)?{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:a.response.error.message||`Extension request failed to get extension ID: ${JSON.stringify(a.response)}`,type:a.response.error.type}}}:{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:a.response}}case _.LOGIN:{const a=o;await this.login(a),this.broadcastAuthStateChange();break}case _.LOGOUT:await this.logout(),this.broadcastAuthStateChange();break;case _.GET_AUTH_STATE:s={authState:await this.getAuthState()};break;default:return{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:`Unknown action: ${i}`,type:"UNKNOWN_ACTION"}}}}return{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:s}}catch(s){return this.logger.error("[BodhiExtClient] Unexpected error:",s),{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:s instanceof Error?s.message:`Unexpected error: ${JSON.stringify(s)}`}}}}}async handleAction(e){switch(e.type){case h.EXT2EXT_CLIENT_API_REQUEST:return this.handleApiRequest(e);case h.EXT2EXT_CLIENT_REQUEST:return this.handleExtClientRequest(e);default:{const{requestId:t}=e;return this.logger.error("[BodhiExtClient] Unknown message type:",e.type),{type:h.EXT2EXT_CLIENT_RESPONSE,requestId:t,response:{error:{message:`Unknown message type: ${e.type}`,type:"UNKNOWN_MESSAGE_TYPE"}}}}}}async login(e){if(!(this.isAuthenticating||(await this.getAuthState()).status==="authenticated")){this.isAuthenticating=!0;try{if(!this.extensionId)throw new Error("Extension not discovered. Please detect Bodhi extension before login.");e?.flowType==="redirect"&&this.logger.warn("Extension mode does not support redirect flow type; using popup instead");const r=e?.userRole??this.userRole,i=new n.AccessRequestBuilder(this.authClientId).requestedRole(r).flowType("popup");e?.requested&&i.requested(e.requested);const o=i.build(),s=await this.requestAccess(o),{id:a,review_url:E}=n.unwrapResponse(s);await chrome.tabs.create({url:E});const c=await this.pollAccessRequestStatus(a,{intervalMs:e?.pollIntervalMs??n.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??n.DEFAULT_POLL_TIMEOUT_MS});if(c.status!=="approved")throw n.createOperationError("auth_error",`Access request ${c.status}`);const d=`openid profile email roles ${c.access_request_scope??""}`.trim();await this.performOAuthPkce(d)}finally{this.isAuthenticating=!1}}}async exchangeCodeForTokens(e){if((await this.getAuthState()).status==="authenticated")return;const{codeVerifier:r}=await chrome.storage.session.get("codeVerifier"),i=chrome.identity.getRedirectURL("callback"),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:i,client_id:this.authClientId,code_verifier:r})});if(!o.ok){const a=await o.text();throw new Error(`Token exchange failed: ${o.status} ${a}`)}const s=await o.json();await this.storeTokens({accessToken:s.access_token,refreshToken:s.refresh_token,idToken:s.id_token,expiresIn:s.expires_in})}async getAuthState(){const e=await this._getAccessTokenRaw();if(!e)return{status:"unauthenticated",user:null,accessToken:null,error:null};try{const t=this.parseJwt(e);return{status:"authenticated",user:{sub:t.sub,email:t.email,name:t.name,given_name:t.given_name,family_name:t.family_name,preferred_username:t.preferred_username},accessToken:e,error:null}}catch(t){return this.logger.error("Failed to parse token:",t),{status:"unauthenticated",user:null,accessToken:null,error:null}}}async logout(){const{refreshToken:e}=await chrome.storage.session.get("refreshToken");if(e)try{await fetch(this.authEndpoints.revoke,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({token:e,client_id:this.authClientId,token_type_hint:"refresh_token"})})}catch(t){this.logger.warn("[OAuth] Token revocation failed:",t)}await this.clearTokens()}async requestAccess(e){return this.sendApiRequest("POST","/bodhi/v1/apps/request-access",e)}async getAccessRequestStatus(e){return this.sendApiRequest("GET",`/bodhi/v1/apps/access-requests/${e}?app_client_id=${encodeURIComponent(this.authClientId)}`)}async pollAccessRequestStatus(e,t){return n.pollAccessRequestUntilResolved(r=>this.getAccessRequestStatus(r),e,t)}async performOAuthPkce(e){const t=p.generateCodeVerifier(),r=await p.generateCodeChallenge(t),i=p.generateCodeVerifier();await chrome.storage.session.set({codeVerifier:t,state:i,authInProgress:!0});const o=chrome.identity.getRedirectURL("callback"),s=new URL(this.authEndpoints.authorize);return s.searchParams.set("client_id",this.authClientId),s.searchParams.set("response_type","code"),s.searchParams.set("redirect_uri",o),s.searchParams.set("scope",e),s.searchParams.set("code_challenge",r),s.searchParams.set("code_challenge_method","S256"),s.searchParams.set("state",i),new Promise((a,E)=>{chrome.identity.launchWebAuthFlow({url:s.toString(),interactive:!0},async c=>{if(await chrome.storage.session.set({authInProgress:!1}),chrome.runtime.lastError){await chrome.storage.session.remove(["codeVerifier","state"]),E(chrome.runtime.lastError);return}if(!c){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("oauth_error","No redirect URL received"));return}try{const u=new URL(c),d=u.searchParams.get("code"),T=u.searchParams.get("state"),{state:m}=await chrome.storage.session.get("state");if(T!==m){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("oauth_error","State mismatch"));return}if(!d){await chrome.storage.session.remove(["codeVerifier","state"]),E(n.createOperationError("oauth_error","No authorization code"));return}await this.exchangeCodeForTokens(d),await chrome.storage.session.remove(["codeVerifier","state"]);const l=await this.getAuthState();if(l.status!=="authenticated")throw n.createOperationError("oauth_error","Login failed");a(l)}catch(u){await chrome.storage.session.remove(["codeVerifier","state"]),E(u)}})})}async sendExtRequest(e,t){const r=await this.sendExtRequestRaw(e,t);if(g.isExtError(r.response))throw this.logger.error("[BodhiExtClient] Extension error:",r.response.error),new Error(r.response.error.message||`Extension request failed: ${JSON.stringify(r.response)}`);return r.response}async sendApiRequest(e,t,r,i){if(!this.extensionId)throw new n.BodhiError("not_initialized",this.createErrorClientNotInitialized({type:"api",method:e,endpoint:t}));this.logger.debug(`[BodhiExtClient] Sending API_REQUEST: method=${e}, endpoint=${t}`,r?{body:r}:"");const o=crypto.randomUUID(),s={type:g.MESSAGE_TYPES.API_REQUEST,requestId:o,request:{method:e,endpoint:t,body:r,headers:i}};return this.logger.debug(`[BodhiExtClient] Request ID: ${o}, Extension: ${this.extensionId}`),new Promise((a,E)=>{try{chrome.runtime.sendMessage(this.extensionId,s,c=>{if(chrome.runtime.lastError){this.logger.error(`[BodhiExtClient] Chrome runtime error for request ${o}:`,chrome.runtime.lastError),E(new n.BodhiError("extension_error",chrome.runtime.lastError.message??"Chrome runtime error"));return}if(this.logger.debug(`[BodhiExtClient] Response for request ${o}:`,c),!c){this.logger.error(`[BodhiExtClient] No response received for request ${o}`),E(new n.BodhiError("extension_error","No response from extension"));return}c.type===g.MESSAGE_TYPES.API_RESPONSE&&c.requestId===o?"error"in c?(this.logger.error(`[BodhiExtClient] API error for ${o}:`,c.error),E(new n.BodhiError(c.error.type||"extension_error",c.error.message))):(this.logger.debug(`[BodhiExtClient] ✓ Valid API_RESPONSE for ${o}`),a(c.response)):(this.logger.error(`[BodhiExtClient] Invalid response format for ${o}:`,c),E(new n.BodhiError("extension_error","Invalid response format")))})}catch(c){this.logger.error(`[BodhiExtClient] Exception sending message for ${o}:`,c),E(c)}})}async sendExtRequestRaw(e,t){if(!this.extensionId)throw new n.BodhiError("not_initialized",this.createErrorClientNotInitialized({type:"ext",action:e,params:t}));this.logger.debug(`[BodhiExtClient] Sending EXT_REQUEST (raw): action=${e}`,t?{params:t}:"");const r=crypto.randomUUID(),i={type:g.MESSAGE_TYPES.EXT_REQUEST,requestId:r,request:{action:e,params:t}};return this.logger.debug(`[BodhiExtClient] Request ID: ${r}, Extension: ${this.extensionId}`),new Promise((o,s)=>{try{chrome.runtime.sendMessage(this.extensionId,i,a=>{if(chrome.runtime.lastError){this.logger.error(`[BodhiExtClient] Chrome runtime error for request ${r}:`,chrome.runtime.lastError),s(new Error(chrome.runtime.lastError.message));return}if(this.logger.debug(`[BodhiExtClient] Response for request ${r}:`,a),!a){this.logger.error(`[BodhiExtClient] No response received for request ${r}`),s(new Error("No response from extension"));return}a.type===g.MESSAGE_TYPES.EXT_RESPONSE&&a.requestId===r?(this.logger.debug(`[BodhiExtClient] ✓ Valid EXT_RESPONSE for ${r}`),o(a)):(this.logger.error(`[BodhiExtClient] Invalid response format for ${r}:`,a),s(new n.BodhiError("extension_error","Invalid response format")))})}catch(a){this.logger.error(`[BodhiExtClient] Exception sending message for ${r}:`,a),s(a)}})}async handleStreamRequest(e,t){const{requestId:r,request:i}=t,{method:o,endpoint:s,body:a,headers:E,authenticated:c}=i;this.logger.debug("[BodhiExtClient] Processing stream request:",{requestId:r,method:o,endpoint:s,authenticated:c}),this.extensionId||e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:this.createErrorClientNotInitialized(t),type:"extension_error"}});try{let u={...E};if(c!==!1){const l=await this._getAccessTokenRaw();if(!l){e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Not authenticated. Please log in first.",type:"extension_error"}});return}u={...u,Authorization:`Bearer ${l}`},this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request")}const d=chrome.runtime.connect(this.extensionId,{name:g.BODHI_STREAM_PORT});this.activeStreamPorts.set(r,d);const T=setTimeout(()=>{this.activeStreamPorts.has(r)&&(this.logger.error(`[BodhiExtClient] Stream timeout for ${r}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Stream request timed out",type:"timeout_error"}}),this.cleanupStreamPort(r))},p.STREAM_TIMEOUT);d.onMessage.addListener(l=>{if(g.isStreamChunk(l)){const I=l.response,O=I.body;I.status>=400?e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_API_ERROR,requestId:r,response:I}):O?.done?(e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_DONE,requestId:r}),this.logger.info(`[BodhiExtClient] Stream complete for ${r}`),clearTimeout(T),this.cleanupStreamPort(r)):e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_CHUNK,requestId:r,response:I})}else g.isStreamApiError(l)?(this.logger.error(`[BodhiExtClient] Stream API error for ${r}: ${l.response.status}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_API_ERROR,requestId:r,response:l.response})):g.isStreamError(l)&&(this.logger.error(`[BodhiExtClient] Stream error for ${r}:`,l.error.message),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:`stream error: ${JSON.stringify(l)}`,type:"extension_error"}}),clearTimeout(T),this.cleanupStreamPort(r))}),d.onDisconnect.addListener(()=>{clearTimeout(T),this.activeStreamPorts.has(r)&&(this.logger.error(`[BodhiExtClient] Bodhi port disconnected for ${r}`),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:"Connection to Bodhi extension closed unexpectedly",type:"network_error"}}),this.activeStreamPorts.delete(r))});const m={type:g.MESSAGE_TYPES.STREAM_REQUEST,requestId:r,request:{method:o,endpoint:s,body:a,headers:u}};this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:",m),d.postMessage(m)}catch(u){const d=u;this.logger.error("[BodhiExtClient] Stream error:",JSON.stringify(d.message)),e.postMessage({type:h.EXT2EXT_CLIENT_STREAM_ERROR,requestId:r,error:{message:`uncaught error: ${JSON.stringify({error:d,message:d.message})}`,type:"extension_error"}})}}cleanupStreamPort(e){const t=this.activeStreamPorts.get(e);if(t){try{t.disconnect()}catch{}this.activeStreamPorts.delete(e)}}async storeTokens(e){const t=Date.now()+(e.expiresIn||3600)*1e3;await chrome.storage.session.set({accessToken:e.accessToken,refreshToken:e.refreshToken,idToken:e.idToken,expiresAt:t})}async _getAccessTokenRaw(){const{accessToken:e,expiresAt:t}=await chrome.storage.session.get(["accessToken","expiresAt"]);if(!e||!t)return null;if(Date.now()>=t-5*1e3){const{refreshToken:r}=await chrome.storage.session.get("refreshToken");return r?this._tryRefreshToken(r):null}return e}async _tryRefreshToken(e){if(this.refreshPromise)return this.logger.debug("Refresh already in progress, returning existing promise"),this.refreshPromise;this.refreshPromise=this._doRefreshToken(e);try{return await this.refreshPromise}finally{this.refreshPromise=null}}async _doRefreshToken(e){this.logger.debug("Refreshing access token");try{const t=await n.refreshAccessToken(this.authEndpoints.token,e,this.authClientId);if(t.success)return await this._storeRefreshedTokens(t.tokens),this.logger.info("Token refreshed successfully"),this.broadcastAuthStateChange(),t.tokens.access_token;if(t.error==="invalid_grant")return this.logger.warn("Refresh token expired or revoked, clearing tokens and logging out"),await this.clearTokens(),this.broadcastAuthStateChange(),null}catch(t){this.logger.warn("Token refresh failed:",t)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),n.createOperationError("auth_error","Access token expired and unable to refresh. Try logging out and logging in again.")}async _storeRefreshedTokens(e){const t=Date.now()+e.expires_in*1e3,r={accessToken:e.access_token,expiresAt:t};e.refresh_token&&(r.refreshToken=e.refresh_token),e.id_token&&(r.idToken=e.id_token),await chrome.storage.session.set(r)}async clearTokens(){await chrome.storage.session.remove(["accessToken","refreshToken","idToken","expiresAt","codeVerifier","state","authInProgress","bodhiUserInfo"])}parseJwt(e){const r=e.split(".")[1].replace(/-/g,"+").replace(/_/g,"/"),i=decodeURIComponent(atob(r).split("").map(o=>"%"+("00"+o.charCodeAt(0).toString(16)).slice(-2)).join(""));return JSON.parse(i)}createErrorClientNotInitialized(e){return`Client not initialized. Extension discovery not triggered nor extensionId set, cannot handle request: ${JSON.stringify(e)}`}};p.STREAM_TIMEOUT=6e4;let R=p;const k="production";exports.BodhiExtClient=R;exports.DEFAULT_API_TIMEOUT_MS=N;exports.DISCOVERY_ATTEMPTS=C;exports.DISCOVERY_ATTEMPT_TIMEOUT=A;exports.DISCOVERY_ATTEMPT_WAIT_MS=w;exports.DISCOVERY_TIMEOUT_MS=x;exports.EXT2EXT_CLIENT_ACTIONS=_;exports.EXT2EXT_CLIENT_MESSAGE_TYPES=h;exports.EXT2EXT_CLIENT_STREAM_PORT=f;exports.EXT_BUILD_MODE=k;exports.ExtUIClient=L;exports.isExtClientApiError=P;
|
package/dist/bodhi-ext.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { DirectClientBase as M, createStoragePrefixWithBasePath as
|
|
2
|
-
import { isExtError as
|
|
1
|
+
import { DirectClientBase as M, createStoragePrefixWithBasePath as N, STORAGE_PREFIXES as v, AccessRequestBuilder as P, unwrapResponse as X, DEFAULT_POLL_TIMEOUT_MS as b, DEFAULT_POLL_INTERVAL_MS as O, createOperationError as l, generateCodeVerifier as C, generateCodeChallenge as U, PENDING_EXTENSION_READY as R, Logger as w, NOOP_STATE_CALLBACK as D, createExtensionStateNotInitialized as $, BACKEND_SERVER_NOT_REACHABLE as B, createExtensionStateNotFound as K, BodhiError as T, INITIAL_AUTH_STATE as V, BodhiApiError as z, Chat as F, Models as G, Embeddings as Q, Toolsets as H, Mcps as W, pollAccessRequestUntilResolved as L, isAuthError as J, BaseFacadeClient as Y, refreshAccessToken as Z } from "@bodhiapp/bodhi-js-core";
|
|
2
|
+
import { isExtError as x, isApiSuccessResponse as j, MESSAGE_TYPES as _, EXT_ACTIONS as ee, BODHI_STREAM_PORT as te, isStreamChunk as re, isStreamApiError as se, isStreamError as oe } from "@bodhiapp/bodhi-browser-types";
|
|
3
3
|
const c = {
|
|
4
4
|
EXT2EXT_CLIENT_REQUEST: "EXT2EXT_CLIENT_REQUEST",
|
|
5
5
|
EXT2EXT_CLIENT_RESPONSE: "EXT2EXT_CLIENT_RESPONSE",
|
|
@@ -12,22 +12,22 @@ const c = {
|
|
|
12
12
|
EXT2EXT_CLIENT_STREAM_ERROR: "EXT2EXT_CLIENT_STREAM_ERROR",
|
|
13
13
|
EXT2EXT_CLIENT_STREAM_API_ERROR: "EXT2EXT_CLIENT_STREAM_API_ERROR",
|
|
14
14
|
EXT2EXT_CLIENT_STREAM_DONE: "EXT2EXT_CLIENT_STREAM_DONE"
|
|
15
|
-
}, k = "ext2ext-client-stream",
|
|
15
|
+
}, k = "ext2ext-client-stream", m = {
|
|
16
16
|
LOGIN: "login",
|
|
17
17
|
LOGOUT: "logout",
|
|
18
18
|
GET_AUTH_STATE: "getAuthState",
|
|
19
19
|
DISCOVER_EXTENSION: "discoverBodhiExtension",
|
|
20
20
|
GET_EXTENSION_ID: "get_extension_id",
|
|
21
21
|
SET_EXTENSION_ID: "setExtensionId"
|
|
22
|
-
},
|
|
23
|
-
function
|
|
24
|
-
return "error" in
|
|
22
|
+
}, ie = 5e3, ne = 3, ae = 500, ce = 500, he = 3e4;
|
|
23
|
+
function ue(y) {
|
|
24
|
+
return "error" in y;
|
|
25
25
|
}
|
|
26
|
-
class
|
|
26
|
+
class de extends M {
|
|
27
27
|
constructor(e, t) {
|
|
28
|
-
const r =
|
|
28
|
+
const r = N(
|
|
29
29
|
e.basePath,
|
|
30
|
-
|
|
30
|
+
v.EXT_DIRECT
|
|
31
31
|
), i = {
|
|
32
32
|
authClientId: e.authClientId,
|
|
33
33
|
authServerUrl: e.authServerUrl,
|
|
@@ -49,29 +49,21 @@ class Ee extends M {
|
|
|
49
49
|
e?.flowType === "redirect" && this.logger.warn("Extension mode does not support redirect flow type; using popup instead");
|
|
50
50
|
const r = e?.userRole ?? this.userRole;
|
|
51
51
|
e?.onProgress?.("requesting");
|
|
52
|
-
const i = new
|
|
52
|
+
const i = new P(this.authClientId).requestedRole(r).flowType("popup");
|
|
53
53
|
e?.requested && i.requested(e.requested);
|
|
54
|
-
const o = i.build(), s = await this.requestAccess(o);
|
|
55
|
-
if (x(s))
|
|
56
|
-
throw E(s.error.message, s.error.type);
|
|
57
|
-
if (!w(s))
|
|
58
|
-
throw E(
|
|
59
|
-
`Access request failed: HTTP ${s.status}`,
|
|
60
|
-
"auth_error"
|
|
61
|
-
);
|
|
62
|
-
const { id: n, review_url: h } = s.body;
|
|
54
|
+
const o = i.build(), s = await this.requestAccess(o), { id: n, review_url: h } = X(s);
|
|
63
55
|
e?.onProgress?.("reviewing"), await chrome.tabs.create({ url: h });
|
|
64
56
|
const a = await this.pollAccessRequestStatus(n, {
|
|
65
57
|
intervalMs: e?.pollIntervalMs ?? O,
|
|
66
58
|
timeoutMs: e?.pollTimeoutMs ?? b
|
|
67
59
|
});
|
|
68
60
|
if (a.status !== "approved")
|
|
69
|
-
throw
|
|
61
|
+
throw l("auth_error", `Access request ${a.status}`);
|
|
70
62
|
const u = a.access_request_scope;
|
|
71
63
|
return e?.onProgress?.("authenticating"), this.performOAuthPkce(`openid profile email roles ${u ?? ""}`.trim());
|
|
72
64
|
}
|
|
73
65
|
async performOAuthPkce(e) {
|
|
74
|
-
const t =
|
|
66
|
+
const t = C(), r = await U(t), i = C();
|
|
75
67
|
await chrome.storage.session.set({
|
|
76
68
|
[this.storageKeys.CODE_VERIFIER]: t,
|
|
77
69
|
[this.storageKeys.STATE]: i
|
|
@@ -92,33 +84,33 @@ class Ee extends M {
|
|
|
92
84
|
await chrome.storage.session.remove([
|
|
93
85
|
this.storageKeys.CODE_VERIFIER,
|
|
94
86
|
this.storageKeys.STATE
|
|
95
|
-
]), h(
|
|
87
|
+
]), h(l("oauth_error", "No redirect URL received"));
|
|
96
88
|
return;
|
|
97
89
|
}
|
|
98
90
|
try {
|
|
99
|
-
const u = new URL(a), d = u.searchParams.get("code"), g = u.searchParams.get("state"),
|
|
100
|
-
if (g !==
|
|
91
|
+
const u = new URL(a), d = u.searchParams.get("code"), g = u.searchParams.get("state"), S = await chrome.storage.session.get(this.storageKeys.STATE);
|
|
92
|
+
if (g !== S[this.storageKeys.STATE]) {
|
|
101
93
|
await chrome.storage.session.remove([
|
|
102
94
|
this.storageKeys.CODE_VERIFIER,
|
|
103
95
|
this.storageKeys.STATE
|
|
104
|
-
]), h(
|
|
96
|
+
]), h(l("oauth_error", "State mismatch"));
|
|
105
97
|
return;
|
|
106
98
|
}
|
|
107
99
|
if (!d) {
|
|
108
100
|
await chrome.storage.session.remove([
|
|
109
101
|
this.storageKeys.CODE_VERIFIER,
|
|
110
102
|
this.storageKeys.STATE
|
|
111
|
-
]), h(
|
|
103
|
+
]), h(l("oauth_error", "No authorization code"));
|
|
112
104
|
return;
|
|
113
105
|
}
|
|
114
106
|
await this.exchangeCodeForTokens(d);
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
throw
|
|
118
|
-
this.setAuthState(
|
|
107
|
+
const E = await this.getAuthState();
|
|
108
|
+
if (E.status !== "authenticated")
|
|
109
|
+
throw l("oauth_error", "Login failed");
|
|
110
|
+
this.setAuthState(E), await chrome.storage.session.remove([
|
|
119
111
|
this.storageKeys.CODE_VERIFIER,
|
|
120
112
|
this.storageKeys.STATE
|
|
121
|
-
]), n(
|
|
113
|
+
]), n(E);
|
|
122
114
|
} catch (u) {
|
|
123
115
|
await chrome.storage.session.remove([
|
|
124
116
|
this.storageKeys.CODE_VERIFIER,
|
|
@@ -206,14 +198,14 @@ class Ee extends M {
|
|
|
206
198
|
return chrome.identity.getRedirectURL("callback");
|
|
207
199
|
}
|
|
208
200
|
}
|
|
209
|
-
class
|
|
201
|
+
class Ee {
|
|
210
202
|
constructor(e = {}, t) {
|
|
211
203
|
this.state = {
|
|
212
204
|
type: "extension",
|
|
213
205
|
extension: "not-initialized",
|
|
214
206
|
extensionId: null,
|
|
215
|
-
server:
|
|
216
|
-
}, this.extensionId = null, this.broadcastListenerActive = !1, this.config = e, this.logger = new
|
|
207
|
+
server: R
|
|
208
|
+
}, this.extensionId = null, this.broadcastListenerActive = !1, this.config = e, this.logger = new w("ExtClient", e?.logLevel || "warn"), this.onStateChange = t ?? D, this.apiTimeoutMs = e.apiTimeoutMs ?? he, this.authClientId = e.authClientId ?? "";
|
|
217
209
|
}
|
|
218
210
|
/**
|
|
219
211
|
* Set client state and notify callback
|
|
@@ -286,12 +278,12 @@ class le {
|
|
|
286
278
|
}
|
|
287
279
|
if (this.extensionId && !e.testConnection)
|
|
288
280
|
return this.logger.debug("Already initialized with extensionId, skipping discovery"), this.state;
|
|
289
|
-
const t = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ??
|
|
281
|
+
const t = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ?? ie, r = e.savedState?.extensionId;
|
|
290
282
|
try {
|
|
291
283
|
if (!this.extensionId) {
|
|
292
284
|
if (r)
|
|
293
285
|
this.logger.info("Restoring with known extensionId:", r), await this.sendExtMessageWithTimeout(
|
|
294
|
-
|
|
286
|
+
m.SET_EXTENSION_ID,
|
|
295
287
|
{ extensionId: r },
|
|
296
288
|
t
|
|
297
289
|
), this.extensionId = r;
|
|
@@ -302,7 +294,7 @@ class le {
|
|
|
302
294
|
attemptWaitMs: this.config.initParams?.extension?.attemptWaitMs,
|
|
303
295
|
attemptTimeout: this.config.initParams?.extension?.attemptTimeout
|
|
304
296
|
}, n = await this.sendExtMessageWithTimeout(
|
|
305
|
-
|
|
297
|
+
m.DISCOVER_EXTENSION,
|
|
306
298
|
s,
|
|
307
299
|
t
|
|
308
300
|
);
|
|
@@ -314,9 +306,9 @@ class le {
|
|
|
314
306
|
type: "extension",
|
|
315
307
|
extension: "ready",
|
|
316
308
|
extensionId: this.extensionId,
|
|
317
|
-
server:
|
|
309
|
+
server: R
|
|
318
310
|
};
|
|
319
|
-
let o =
|
|
311
|
+
let o = R;
|
|
320
312
|
if (e.testConnection)
|
|
321
313
|
try {
|
|
322
314
|
o = await this.getServerState(), this.logger.info("Server connectivity tested, state:", o.status);
|
|
@@ -354,22 +346,22 @@ class le {
|
|
|
354
346
|
}
|
|
355
347
|
});
|
|
356
348
|
if (!i)
|
|
357
|
-
throw
|
|
349
|
+
throw l("extension_error", "No response from background script");
|
|
358
350
|
if (i.type !== c.EXT2EXT_CLIENT_RESPONSE)
|
|
359
|
-
throw
|
|
360
|
-
"
|
|
361
|
-
"
|
|
351
|
+
throw l(
|
|
352
|
+
"extension_error",
|
|
353
|
+
"Invalid response type from background script"
|
|
362
354
|
);
|
|
363
355
|
const o = i.response;
|
|
364
|
-
if (
|
|
356
|
+
if (x(o)) {
|
|
365
357
|
const s = o.error.type || "extension_error";
|
|
366
|
-
throw
|
|
358
|
+
throw l(s, o.error.message);
|
|
367
359
|
}
|
|
368
360
|
return o;
|
|
369
361
|
} catch (r) {
|
|
370
|
-
throw
|
|
371
|
-
|
|
372
|
-
"
|
|
362
|
+
throw r instanceof T ? r : l(
|
|
363
|
+
"extension_error",
|
|
364
|
+
r instanceof Error ? r.message : "Unknown error occurred"
|
|
373
365
|
);
|
|
374
366
|
}
|
|
375
367
|
}
|
|
@@ -392,7 +384,8 @@ class le {
|
|
|
392
384
|
});
|
|
393
385
|
}
|
|
394
386
|
/**
|
|
395
|
-
* Send an API message and
|
|
387
|
+
* Send an API message and return ApiResponse
|
|
388
|
+
* @throws BodhiError on operational errors (network, timeout, extension-level)
|
|
396
389
|
*/
|
|
397
390
|
async sendApiRequest(e, t, r, i, o) {
|
|
398
391
|
try {
|
|
@@ -409,23 +402,16 @@ class le {
|
|
|
409
402
|
this.sendRawApiMessage(e, t, r, i, o),
|
|
410
403
|
s
|
|
411
404
|
]);
|
|
412
|
-
if (
|
|
405
|
+
if (ue(n)) {
|
|
413
406
|
const h = n.error.type || "extension_error";
|
|
414
|
-
|
|
415
|
-
error: {
|
|
416
|
-
message: n.error.message,
|
|
417
|
-
type: h
|
|
418
|
-
}
|
|
419
|
-
};
|
|
407
|
+
throw new T(h, n.error.message);
|
|
420
408
|
}
|
|
421
409
|
return n.response;
|
|
422
410
|
} catch (s) {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
}
|
|
428
|
-
};
|
|
411
|
+
if (s instanceof T)
|
|
412
|
+
throw s;
|
|
413
|
+
const n = s instanceof Error ? s.message : String(s);
|
|
414
|
+
throw new T("network_error", n);
|
|
429
415
|
}
|
|
430
416
|
}
|
|
431
417
|
/**
|
|
@@ -443,12 +429,12 @@ class le {
|
|
|
443
429
|
const s = await this.getAuthState();
|
|
444
430
|
if (J(s)) {
|
|
445
431
|
r(
|
|
446
|
-
|
|
432
|
+
l("auth_error", `Login failed: ${s.error?.message}`)
|
|
447
433
|
);
|
|
448
434
|
return;
|
|
449
435
|
}
|
|
450
436
|
if (s.status !== "authenticated") {
|
|
451
|
-
r(
|
|
437
|
+
r(l("auth_error", "Login failed: User is not logged in"));
|
|
452
438
|
return;
|
|
453
439
|
}
|
|
454
440
|
this.setAuthState(s), t(s);
|
|
@@ -457,7 +443,7 @@ class le {
|
|
|
457
443
|
}
|
|
458
444
|
}
|
|
459
445
|
};
|
|
460
|
-
chrome.runtime.onMessage.addListener(i), this.sendExtRequest(
|
|
446
|
+
chrome.runtime.onMessage.addListener(i), this.sendExtRequest(m.LOGIN, e).catch((o) => {
|
|
461
447
|
chrome.runtime.onMessage.removeListener(i), r(o);
|
|
462
448
|
});
|
|
463
449
|
});
|
|
@@ -468,7 +454,7 @@ class le {
|
|
|
468
454
|
* @returns AuthLoggedOut with logged out state
|
|
469
455
|
*/
|
|
470
456
|
async logout() {
|
|
471
|
-
await this.sendExtRequest(
|
|
457
|
+
await this.sendExtRequest(m.LOGOUT);
|
|
472
458
|
const e = {
|
|
473
459
|
status: "unauthenticated",
|
|
474
460
|
user: null,
|
|
@@ -484,7 +470,7 @@ class le {
|
|
|
484
470
|
*/
|
|
485
471
|
async getAuthState() {
|
|
486
472
|
return this.isClientInitialized() ? (await this.sendExtRequest(
|
|
487
|
-
|
|
473
|
+
m.GET_AUTH_STATE
|
|
488
474
|
)).authState : V;
|
|
489
475
|
}
|
|
490
476
|
/**
|
|
@@ -498,14 +484,18 @@ class le {
|
|
|
498
484
|
* Calls /bodhi/v1/info and returns structured server state
|
|
499
485
|
*/
|
|
500
486
|
async getServerState() {
|
|
501
|
-
|
|
502
|
-
|
|
487
|
+
let e;
|
|
488
|
+
try {
|
|
489
|
+
e = await this.sendApiRequest("GET", "/bodhi/v1/info");
|
|
490
|
+
} catch (o) {
|
|
491
|
+
const s = o instanceof Error ? o.message : "Connection failed", n = o instanceof T ? o.code : "extension_error";
|
|
503
492
|
return {
|
|
504
493
|
status: "not-reachable",
|
|
505
494
|
version: null,
|
|
506
|
-
error:
|
|
495
|
+
error: { message: s, type: n }
|
|
507
496
|
};
|
|
508
|
-
|
|
497
|
+
}
|
|
498
|
+
if (e.status >= 400)
|
|
509
499
|
return {
|
|
510
500
|
status: "not-reachable",
|
|
511
501
|
version: null,
|
|
@@ -529,8 +519,6 @@ class le {
|
|
|
529
519
|
error: t.error ? { message: t.error.message, type: t.error.type } : { message: "Resource admin required", type: "extension_error" },
|
|
530
520
|
...i
|
|
531
521
|
};
|
|
532
|
-
case "tenant_selection":
|
|
533
|
-
return { status: "tenant_selection", version: r, error: null, ...i };
|
|
534
522
|
case "error":
|
|
535
523
|
return {
|
|
536
524
|
status: "error",
|
|
@@ -572,9 +560,9 @@ class le {
|
|
|
572
560
|
requestId: s,
|
|
573
561
|
error: JSON.stringify(d.error)
|
|
574
562
|
}), u.error(
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
563
|
+
new T(
|
|
564
|
+
"extension_error",
|
|
565
|
+
d.error.message
|
|
578
566
|
)
|
|
579
567
|
), n.disconnect();
|
|
580
568
|
break;
|
|
@@ -584,26 +572,24 @@ class le {
|
|
|
584
572
|
requestId: s,
|
|
585
573
|
error: g.response.body?.error
|
|
586
574
|
}), u.error(
|
|
587
|
-
|
|
588
|
-
g.response.body?.error?.message || "API error",
|
|
575
|
+
new z(
|
|
589
576
|
g.response.status,
|
|
590
|
-
g.response.body
|
|
577
|
+
g.response.body,
|
|
578
|
+
g.response.body?.error?.message || "API error"
|
|
591
579
|
)
|
|
592
580
|
), n.disconnect();
|
|
593
581
|
break;
|
|
594
582
|
}
|
|
595
583
|
case c.EXT2EXT_CLIENT_STREAM_CHUNK: {
|
|
596
584
|
const g = d;
|
|
597
|
-
|
|
585
|
+
j(g.response) && u.enqueue(g.response.body);
|
|
598
586
|
break;
|
|
599
587
|
}
|
|
600
588
|
}
|
|
601
589
|
}), n.onDisconnect.addListener(() => {
|
|
602
590
|
this.logger.debug("Port disconnected", { requestId: s });
|
|
603
591
|
try {
|
|
604
|
-
u.error(
|
|
605
|
-
E("Connection closed unexpectedly", "extension_error")
|
|
606
|
-
);
|
|
592
|
+
u.error(new T("connection_closed", "Connection closed unexpectedly"));
|
|
607
593
|
} catch {
|
|
608
594
|
}
|
|
609
595
|
}), n.postMessage({
|
|
@@ -630,7 +616,7 @@ class le {
|
|
|
630
616
|
// OpenAI-Compatible Namespaced API
|
|
631
617
|
// ============================================================================
|
|
632
618
|
get chat() {
|
|
633
|
-
return this._chat ??= new
|
|
619
|
+
return this._chat ??= new F(this);
|
|
634
620
|
}
|
|
635
621
|
get models() {
|
|
636
622
|
return this._models ??= new G(this);
|
|
@@ -689,7 +675,7 @@ class le {
|
|
|
689
675
|
};
|
|
690
676
|
}
|
|
691
677
|
}
|
|
692
|
-
class
|
|
678
|
+
class pe extends Y {
|
|
693
679
|
constructor(e, t, r) {
|
|
694
680
|
const i = t || {}, o = {
|
|
695
681
|
basePath: i.basePath || "/",
|
|
@@ -702,13 +688,13 @@ class _e extends Y {
|
|
|
702
688
|
super(e, o, r);
|
|
703
689
|
}
|
|
704
690
|
createLogger(e) {
|
|
705
|
-
return new
|
|
691
|
+
return new w("ExtUIClient", e.logLevel);
|
|
706
692
|
}
|
|
707
693
|
createStoragePrefix(e) {
|
|
708
|
-
return
|
|
694
|
+
return N(e.basePath, v.EXT);
|
|
709
695
|
}
|
|
710
696
|
createExtClient(e, t) {
|
|
711
|
-
return new
|
|
697
|
+
return new Ee(
|
|
712
698
|
{
|
|
713
699
|
authClientId: this.authClientId,
|
|
714
700
|
logLevel: e.logLevel,
|
|
@@ -719,7 +705,7 @@ class _e extends Y {
|
|
|
719
705
|
);
|
|
720
706
|
}
|
|
721
707
|
createDirectClient(e, t, r) {
|
|
722
|
-
return new
|
|
708
|
+
return new de(
|
|
723
709
|
{
|
|
724
710
|
authClientId: e,
|
|
725
711
|
authServerUrl: t.authServerUrl,
|
|
@@ -732,12 +718,12 @@ class _e extends Y {
|
|
|
732
718
|
);
|
|
733
719
|
}
|
|
734
720
|
}
|
|
735
|
-
const
|
|
721
|
+
const le = ["ggedphdcbekjlomjaidbajglgihbeaon"], ge = ["bjdjhiombmfbcoeojijpfckljjghmjbf"], f = "production", p = class p {
|
|
736
722
|
// ============================================================================
|
|
737
723
|
// Constructor
|
|
738
724
|
// ============================================================================
|
|
739
725
|
constructor(e, t) {
|
|
740
|
-
this.isAuthenticating = !1, this.state = "setup", this.listenersInitialized = !1, this.refreshPromise = null, this.activeStreamPorts = /* @__PURE__ */ new Map(), this.authClientId = e, this.authServerUrl = t?.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userRole = t?.userRole || "scope_user_user", this.extensionId = t?.extensionId, this.logger = new
|
|
726
|
+
this.isAuthenticating = !1, this.state = "setup", this.listenersInitialized = !1, this.refreshPromise = null, this.activeStreamPorts = /* @__PURE__ */ new Map(), this.authClientId = e, this.authServerUrl = t?.authServerUrl || "https://id.getbodhi.app/realms/bodhi", this.userRole = t?.userRole || "scope_user_user", this.extensionId = t?.extensionId, this.logger = new w("BodhiExtClient", t?.logLevel || "warn"), this.attempts = t?.attempts ?? ne, this.attemptWaitMs = t?.attemptWaitMs ?? ae, this.attemptTimeout = t?.attemptTimeout ?? ce, this.authEndpoints = {
|
|
741
727
|
authorize: `${this.authServerUrl}/protocol/openid-connect/auth`,
|
|
742
728
|
token: `${this.authServerUrl}/protocol/openid-connect/token`,
|
|
743
729
|
userinfo: `${this.authServerUrl}/protocol/openid-connect/userinfo`,
|
|
@@ -755,11 +741,11 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
755
741
|
}
|
|
756
742
|
static generateCodeVerifier() {
|
|
757
743
|
const e = new Uint8Array(32);
|
|
758
|
-
return crypto.getRandomValues(e),
|
|
744
|
+
return crypto.getRandomValues(e), p.base64UrlEncode(e.buffer);
|
|
759
745
|
}
|
|
760
746
|
static async generateCodeChallenge(e) {
|
|
761
747
|
const r = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", r);
|
|
762
|
-
return
|
|
748
|
+
return p.base64UrlEncode(i);
|
|
763
749
|
}
|
|
764
750
|
// ============================================================================
|
|
765
751
|
// State Management
|
|
@@ -778,7 +764,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
778
764
|
* Get extension IDs for current environment
|
|
779
765
|
*/
|
|
780
766
|
getExtensionIdsForEnvironment() {
|
|
781
|
-
const t = f !== "production" ?
|
|
767
|
+
const t = f !== "production" ? le : ge;
|
|
782
768
|
return this.logger.info("[Ext2Ext/Registry] Environment: production"), this.logger.debug("[Ext2Ext/Registry] Using extension IDs:", t), t;
|
|
783
769
|
}
|
|
784
770
|
/**
|
|
@@ -793,7 +779,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
793
779
|
}, this.attemptTimeout);
|
|
794
780
|
try {
|
|
795
781
|
const o = {
|
|
796
|
-
type:
|
|
782
|
+
type: _.EXT_REQUEST,
|
|
797
783
|
requestId: crypto.randomUUID(),
|
|
798
784
|
request: {
|
|
799
785
|
action: "get_extension_id"
|
|
@@ -809,7 +795,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
809
795
|
}
|
|
810
796
|
this.logger.debug(`[Ext2Ext/Discovery] Response from ${e}:`, s);
|
|
811
797
|
const n = s;
|
|
812
|
-
n && n.type ===
|
|
798
|
+
n && n.type === _.EXT_RESPONSE ? (this.logger.debug(`[Ext2Ext/Discovery] ✓ Extension ${e} responded`), t(!0)) : (this.logger.error(
|
|
813
799
|
`[Ext2Ext/Discovery] Invalid response from ${e}:`,
|
|
814
800
|
s
|
|
815
801
|
), r(new Error("Invalid response")));
|
|
@@ -876,7 +862,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
876
862
|
this.logger.debug("[BodhiExtClient] Listeners already initialized, skipping");
|
|
877
863
|
return;
|
|
878
864
|
}
|
|
879
|
-
this.listenersInitialized = !0, chrome.runtime.onMessage.addListener((e, t, r) => e.type !== c.EXT2EXT_CLIENT_REQUEST && e.type !== c.EXT2EXT_CLIENT_API_REQUEST ? !1 : this.state !== "ready" && !(e.type === c.EXT2EXT_CLIENT_REQUEST && e.request.action ===
|
|
865
|
+
this.listenersInitialized = !0, chrome.runtime.onMessage.addListener((e, t, r) => e.type !== c.EXT2EXT_CLIENT_REQUEST && e.type !== c.EXT2EXT_CLIENT_API_REQUEST ? !1 : this.state !== "ready" && !(e.type === c.EXT2EXT_CLIENT_REQUEST && e.request.action === m.DISCOVER_EXTENSION) ? (e.type === c.EXT2EXT_CLIENT_REQUEST ? r({
|
|
880
866
|
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
881
867
|
requestId: e.requestId,
|
|
882
868
|
response: {
|
|
@@ -1022,7 +1008,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1022
1008
|
try {
|
|
1023
1009
|
let s = {};
|
|
1024
1010
|
switch (i) {
|
|
1025
|
-
case
|
|
1011
|
+
case m.DISCOVER_EXTENSION: {
|
|
1026
1012
|
const n = o;
|
|
1027
1013
|
await this.init(n), this.logger.info("[BodhiExtClient] Discovery successful:", {
|
|
1028
1014
|
extensionId: this.extensionId,
|
|
@@ -1033,14 +1019,14 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1033
1019
|
};
|
|
1034
1020
|
break;
|
|
1035
1021
|
}
|
|
1036
|
-
case
|
|
1022
|
+
case m.SET_EXTENSION_ID: {
|
|
1037
1023
|
const { extensionId: n } = o;
|
|
1038
1024
|
this.extensionId = n, this.state = "ready", this.logger.info("[BodhiExtClient] Extension ID set:", { extensionId: n }), s = { success: !0 };
|
|
1039
1025
|
break;
|
|
1040
1026
|
}
|
|
1041
|
-
case
|
|
1042
|
-
const n = await this.sendExtRequestRaw(
|
|
1043
|
-
return
|
|
1027
|
+
case m.GET_EXTENSION_ID: {
|
|
1028
|
+
const n = await this.sendExtRequestRaw(ee.GET_EXTENSION_ID, o);
|
|
1029
|
+
return x(n.response) ? {
|
|
1044
1030
|
type: c.EXT2EXT_CLIENT_RESPONSE,
|
|
1045
1031
|
requestId: t,
|
|
1046
1032
|
response: {
|
|
@@ -1055,15 +1041,15 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1055
1041
|
response: n.response
|
|
1056
1042
|
};
|
|
1057
1043
|
}
|
|
1058
|
-
case
|
|
1044
|
+
case m.LOGIN: {
|
|
1059
1045
|
const n = o;
|
|
1060
1046
|
await this.login(n), this.broadcastAuthStateChange();
|
|
1061
1047
|
break;
|
|
1062
1048
|
}
|
|
1063
|
-
case
|
|
1049
|
+
case m.LOGOUT:
|
|
1064
1050
|
await this.logout(), this.broadcastAuthStateChange();
|
|
1065
1051
|
break;
|
|
1066
|
-
case
|
|
1052
|
+
case m.GET_AUTH_STATE:
|
|
1067
1053
|
s = { authState: await this.getAuthState() };
|
|
1068
1054
|
break;
|
|
1069
1055
|
default:
|
|
@@ -1129,27 +1115,16 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1129
1115
|
if (!this.extensionId)
|
|
1130
1116
|
throw new Error("Extension not discovered. Please detect Bodhi extension before login.");
|
|
1131
1117
|
e?.flowType === "redirect" && this.logger.warn("Extension mode does not support redirect flow type; using popup instead");
|
|
1132
|
-
const r = e?.userRole ?? this.userRole, i = new
|
|
1118
|
+
const r = e?.userRole ?? this.userRole, i = new P(this.authClientId).requestedRole(r).flowType("popup");
|
|
1133
1119
|
e?.requested && i.requested(e.requested);
|
|
1134
|
-
const o = i.build(), s = await this.requestAccess(o);
|
|
1135
|
-
if (x(s))
|
|
1136
|
-
throw E(
|
|
1137
|
-
s.error.message,
|
|
1138
|
-
s.error.type
|
|
1139
|
-
);
|
|
1140
|
-
if (!w(s))
|
|
1141
|
-
throw E(
|
|
1142
|
-
`Access request failed: HTTP ${s.status}`,
|
|
1143
|
-
"auth_error"
|
|
1144
|
-
);
|
|
1145
|
-
const { id: n, review_url: h } = s.body;
|
|
1120
|
+
const o = i.build(), s = await this.requestAccess(o), { id: n, review_url: h } = X(s);
|
|
1146
1121
|
await chrome.tabs.create({ url: h });
|
|
1147
1122
|
const a = await this.pollAccessRequestStatus(n, {
|
|
1148
1123
|
intervalMs: e?.pollIntervalMs ?? O,
|
|
1149
1124
|
timeoutMs: e?.pollTimeoutMs ?? b
|
|
1150
1125
|
});
|
|
1151
1126
|
if (a.status !== "approved")
|
|
1152
|
-
throw
|
|
1127
|
+
throw l("auth_error", `Access request ${a.status}`);
|
|
1153
1128
|
const d = `openid profile email roles ${a.access_request_scope ?? ""}`.trim();
|
|
1154
1129
|
await this.performOAuthPkce(d);
|
|
1155
1130
|
} finally {
|
|
@@ -1244,45 +1219,17 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1244
1219
|
// Access Request Methods
|
|
1245
1220
|
// ============================================================================
|
|
1246
1221
|
async requestAccess(e) {
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
);
|
|
1253
|
-
return {
|
|
1254
|
-
body: t.body,
|
|
1255
|
-
status: t.status,
|
|
1256
|
-
headers: t.headers
|
|
1257
|
-
};
|
|
1258
|
-
} catch (t) {
|
|
1259
|
-
return {
|
|
1260
|
-
error: {
|
|
1261
|
-
message: t instanceof Error ? t.message : String(t),
|
|
1262
|
-
type: "extension_error"
|
|
1263
|
-
}
|
|
1264
|
-
};
|
|
1265
|
-
}
|
|
1222
|
+
return this.sendApiRequest(
|
|
1223
|
+
"POST",
|
|
1224
|
+
"/bodhi/v1/apps/request-access",
|
|
1225
|
+
e
|
|
1226
|
+
);
|
|
1266
1227
|
}
|
|
1267
1228
|
async getAccessRequestStatus(e) {
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
);
|
|
1273
|
-
return {
|
|
1274
|
-
body: t.body,
|
|
1275
|
-
status: t.status,
|
|
1276
|
-
headers: t.headers
|
|
1277
|
-
};
|
|
1278
|
-
} catch (t) {
|
|
1279
|
-
return {
|
|
1280
|
-
error: {
|
|
1281
|
-
message: t instanceof Error ? t.message : String(t),
|
|
1282
|
-
type: "extension_error"
|
|
1283
|
-
}
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1229
|
+
return this.sendApiRequest(
|
|
1230
|
+
"GET",
|
|
1231
|
+
`/bodhi/v1/apps/access-requests/${e}?app_client_id=${encodeURIComponent(this.authClientId)}`
|
|
1232
|
+
);
|
|
1286
1233
|
}
|
|
1287
1234
|
async pollAccessRequestStatus(e, t) {
|
|
1288
1235
|
return L(
|
|
@@ -1292,7 +1239,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1292
1239
|
);
|
|
1293
1240
|
}
|
|
1294
1241
|
async performOAuthPkce(e) {
|
|
1295
|
-
const t =
|
|
1242
|
+
const t = p.generateCodeVerifier(), r = await p.generateCodeChallenge(t), i = p.generateCodeVerifier();
|
|
1296
1243
|
await chrome.storage.session.set({
|
|
1297
1244
|
codeVerifier: t,
|
|
1298
1245
|
state: i,
|
|
@@ -1308,24 +1255,24 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1308
1255
|
return;
|
|
1309
1256
|
}
|
|
1310
1257
|
if (!a) {
|
|
1311
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]), h(
|
|
1258
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), h(l("oauth_error", "No redirect URL received"));
|
|
1312
1259
|
return;
|
|
1313
1260
|
}
|
|
1314
1261
|
try {
|
|
1315
|
-
const u = new URL(a), d = u.searchParams.get("code"), g = u.searchParams.get("state"), { state:
|
|
1316
|
-
if (g !==
|
|
1317
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]), h(
|
|
1262
|
+
const u = new URL(a), d = u.searchParams.get("code"), g = u.searchParams.get("state"), { state: S } = await chrome.storage.session.get("state");
|
|
1263
|
+
if (g !== S) {
|
|
1264
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), h(l("oauth_error", "State mismatch"));
|
|
1318
1265
|
return;
|
|
1319
1266
|
}
|
|
1320
1267
|
if (!d) {
|
|
1321
|
-
await chrome.storage.session.remove(["codeVerifier", "state"]), h(
|
|
1268
|
+
await chrome.storage.session.remove(["codeVerifier", "state"]), h(l("oauth_error", "No authorization code"));
|
|
1322
1269
|
return;
|
|
1323
1270
|
}
|
|
1324
1271
|
await this.exchangeCodeForTokens(d), await chrome.storage.session.remove(["codeVerifier", "state"]);
|
|
1325
|
-
const
|
|
1326
|
-
if (
|
|
1327
|
-
throw
|
|
1328
|
-
n(
|
|
1272
|
+
const E = await this.getAuthState();
|
|
1273
|
+
if (E.status !== "authenticated")
|
|
1274
|
+
throw l("oauth_error", "Login failed");
|
|
1275
|
+
n(E);
|
|
1329
1276
|
} catch (u) {
|
|
1330
1277
|
await chrome.storage.session.remove(["codeVerifier", "state"]), h(u);
|
|
1331
1278
|
}
|
|
@@ -1343,7 +1290,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1343
1290
|
*/
|
|
1344
1291
|
async sendExtRequest(e, t) {
|
|
1345
1292
|
const r = await this.sendExtRequestRaw(e, t);
|
|
1346
|
-
if (
|
|
1293
|
+
if (x(r.response))
|
|
1347
1294
|
throw this.logger.error("[BodhiExtClient] Extension error:", r.response.error), new Error(
|
|
1348
1295
|
r.response.error.message || `Extension request failed: ${JSON.stringify(r.response)}`
|
|
1349
1296
|
);
|
|
@@ -1359,13 +1306,16 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1359
1306
|
*/
|
|
1360
1307
|
async sendApiRequest(e, t, r, i) {
|
|
1361
1308
|
if (!this.extensionId)
|
|
1362
|
-
throw new
|
|
1309
|
+
throw new T(
|
|
1310
|
+
"not_initialized",
|
|
1311
|
+
this.createErrorClientNotInitialized({ type: "api", method: e, endpoint: t })
|
|
1312
|
+
);
|
|
1363
1313
|
this.logger.debug(
|
|
1364
1314
|
`[BodhiExtClient] Sending API_REQUEST: method=${e}, endpoint=${t}`,
|
|
1365
1315
|
r ? { body: r } : ""
|
|
1366
1316
|
);
|
|
1367
1317
|
const o = crypto.randomUUID(), s = {
|
|
1368
|
-
type:
|
|
1318
|
+
type: _.API_REQUEST,
|
|
1369
1319
|
requestId: o,
|
|
1370
1320
|
request: {
|
|
1371
1321
|
method: e,
|
|
@@ -1381,17 +1331,24 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1381
1331
|
this.logger.error(
|
|
1382
1332
|
`[BodhiExtClient] Chrome runtime error for request ${o}:`,
|
|
1383
1333
|
chrome.runtime.lastError
|
|
1384
|
-
), h(
|
|
1334
|
+
), h(
|
|
1335
|
+
new T(
|
|
1336
|
+
"extension_error",
|
|
1337
|
+
chrome.runtime.lastError.message ?? "Chrome runtime error"
|
|
1338
|
+
)
|
|
1339
|
+
);
|
|
1385
1340
|
return;
|
|
1386
1341
|
}
|
|
1387
1342
|
if (this.logger.debug(`[BodhiExtClient] Response for request ${o}:`, a), !a) {
|
|
1388
|
-
this.logger.error(`[BodhiExtClient] No response received for request ${o}`), h(new
|
|
1343
|
+
this.logger.error(`[BodhiExtClient] No response received for request ${o}`), h(new T("extension_error", "No response from extension"));
|
|
1389
1344
|
return;
|
|
1390
1345
|
}
|
|
1391
|
-
a.type ===
|
|
1346
|
+
a.type === _.API_RESPONSE && a.requestId === o ? "error" in a ? (this.logger.error(`[BodhiExtClient] API error for ${o}:`, a.error), h(
|
|
1347
|
+
new T(a.error.type || "extension_error", a.error.message)
|
|
1348
|
+
)) : (this.logger.debug(`[BodhiExtClient] ✓ Valid API_RESPONSE for ${o}`), n(a.response)) : (this.logger.error(
|
|
1392
1349
|
`[BodhiExtClient] Invalid response format for ${o}:`,
|
|
1393
1350
|
a
|
|
1394
|
-
), h(new
|
|
1351
|
+
), h(new T("extension_error", "Invalid response format")));
|
|
1395
1352
|
});
|
|
1396
1353
|
} catch (a) {
|
|
1397
1354
|
this.logger.error(`[BodhiExtClient] Exception sending message for ${o}:`, a), h(a);
|
|
@@ -1406,13 +1363,16 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1406
1363
|
*/
|
|
1407
1364
|
async sendExtRequestRaw(e, t) {
|
|
1408
1365
|
if (!this.extensionId)
|
|
1409
|
-
throw new
|
|
1366
|
+
throw new T(
|
|
1367
|
+
"not_initialized",
|
|
1368
|
+
this.createErrorClientNotInitialized({ type: "ext", action: e, params: t })
|
|
1369
|
+
);
|
|
1410
1370
|
this.logger.debug(
|
|
1411
1371
|
`[BodhiExtClient] Sending EXT_REQUEST (raw): action=${e}`,
|
|
1412
1372
|
t ? { params: t } : ""
|
|
1413
1373
|
);
|
|
1414
1374
|
const r = crypto.randomUUID(), i = {
|
|
1415
|
-
type:
|
|
1375
|
+
type: _.EXT_REQUEST,
|
|
1416
1376
|
requestId: r,
|
|
1417
1377
|
request: {
|
|
1418
1378
|
action: e,
|
|
@@ -1433,10 +1393,10 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1433
1393
|
this.logger.error(`[BodhiExtClient] No response received for request ${r}`), s(new Error("No response from extension"));
|
|
1434
1394
|
return;
|
|
1435
1395
|
}
|
|
1436
|
-
n.type ===
|
|
1396
|
+
n.type === _.EXT_RESPONSE && n.requestId === r ? (this.logger.debug(`[BodhiExtClient] ✓ Valid EXT_RESPONSE for ${r}`), o(n)) : (this.logger.error(
|
|
1437
1397
|
`[BodhiExtClient] Invalid response format for ${r}:`,
|
|
1438
1398
|
n
|
|
1439
|
-
), s(new
|
|
1399
|
+
), s(new T("extension_error", "Invalid response format")));
|
|
1440
1400
|
});
|
|
1441
1401
|
} catch (n) {
|
|
1442
1402
|
this.logger.error(`[BodhiExtClient] Exception sending message for ${r}:`, n), s(n);
|
|
@@ -1467,8 +1427,8 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1467
1427
|
try {
|
|
1468
1428
|
let u = { ...h };
|
|
1469
1429
|
if (a !== !1) {
|
|
1470
|
-
const
|
|
1471
|
-
if (!
|
|
1430
|
+
const E = await this._getAccessTokenRaw();
|
|
1431
|
+
if (!E) {
|
|
1472
1432
|
e.postMessage({
|
|
1473
1433
|
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1474
1434
|
requestId: r,
|
|
@@ -1481,11 +1441,11 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1481
1441
|
}
|
|
1482
1442
|
u = {
|
|
1483
1443
|
...u,
|
|
1484
|
-
Authorization: `Bearer ${
|
|
1444
|
+
Authorization: `Bearer ${E}`
|
|
1485
1445
|
}, this.logger.debug("[BodhiExtClient] Injected auth token for authenticated request");
|
|
1486
1446
|
}
|
|
1487
1447
|
const d = chrome.runtime.connect(this.extensionId, {
|
|
1488
|
-
name:
|
|
1448
|
+
name: te
|
|
1489
1449
|
});
|
|
1490
1450
|
this.activeStreamPorts.set(r, d);
|
|
1491
1451
|
const g = setTimeout(() => {
|
|
@@ -1497,36 +1457,36 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1497
1457
|
type: "timeout_error"
|
|
1498
1458
|
}
|
|
1499
1459
|
}), this.cleanupStreamPort(r));
|
|
1500
|
-
},
|
|
1501
|
-
d.onMessage.addListener((
|
|
1502
|
-
if (
|
|
1503
|
-
const
|
|
1504
|
-
|
|
1460
|
+
}, p.STREAM_TIMEOUT);
|
|
1461
|
+
d.onMessage.addListener((E) => {
|
|
1462
|
+
if (re(E)) {
|
|
1463
|
+
const I = E.response, q = I.body;
|
|
1464
|
+
I.status >= 400 ? e.postMessage({
|
|
1505
1465
|
type: c.EXT2EXT_CLIENT_STREAM_API_ERROR,
|
|
1506
1466
|
requestId: r,
|
|
1507
|
-
response:
|
|
1467
|
+
response: I
|
|
1508
1468
|
}) : q?.done ? (e.postMessage({
|
|
1509
1469
|
type: c.EXT2EXT_CLIENT_STREAM_DONE,
|
|
1510
1470
|
requestId: r
|
|
1511
1471
|
}), this.logger.info(`[BodhiExtClient] Stream complete for ${r}`), clearTimeout(g), this.cleanupStreamPort(r)) : e.postMessage({
|
|
1512
1472
|
type: c.EXT2EXT_CLIENT_STREAM_CHUNK,
|
|
1513
1473
|
requestId: r,
|
|
1514
|
-
response:
|
|
1474
|
+
response: I
|
|
1515
1475
|
});
|
|
1516
|
-
} else
|
|
1517
|
-
`[BodhiExtClient] Stream API error for ${r}: ${
|
|
1476
|
+
} else se(E) ? (this.logger.error(
|
|
1477
|
+
`[BodhiExtClient] Stream API error for ${r}: ${E.response.status}`
|
|
1518
1478
|
), e.postMessage({
|
|
1519
1479
|
type: c.EXT2EXT_CLIENT_STREAM_API_ERROR,
|
|
1520
1480
|
requestId: r,
|
|
1521
|
-
response:
|
|
1522
|
-
})) :
|
|
1481
|
+
response: E.response
|
|
1482
|
+
})) : oe(E) && (this.logger.error(
|
|
1523
1483
|
`[BodhiExtClient] Stream error for ${r}:`,
|
|
1524
|
-
|
|
1484
|
+
E.error.message
|
|
1525
1485
|
), e.postMessage({
|
|
1526
1486
|
type: c.EXT2EXT_CLIENT_STREAM_ERROR,
|
|
1527
1487
|
requestId: r,
|
|
1528
1488
|
error: {
|
|
1529
|
-
message: `stream error: ${JSON.stringify(
|
|
1489
|
+
message: `stream error: ${JSON.stringify(E)}`,
|
|
1530
1490
|
type: "extension_error"
|
|
1531
1491
|
}
|
|
1532
1492
|
}), clearTimeout(g), this.cleanupStreamPort(r));
|
|
@@ -1540,8 +1500,8 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1540
1500
|
}
|
|
1541
1501
|
}), this.activeStreamPorts.delete(r));
|
|
1542
1502
|
});
|
|
1543
|
-
const
|
|
1544
|
-
type:
|
|
1503
|
+
const S = {
|
|
1504
|
+
type: _.STREAM_REQUEST,
|
|
1545
1505
|
requestId: r,
|
|
1546
1506
|
request: {
|
|
1547
1507
|
method: o,
|
|
@@ -1550,7 +1510,7 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1550
1510
|
headers: u
|
|
1551
1511
|
}
|
|
1552
1512
|
};
|
|
1553
|
-
this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:",
|
|
1513
|
+
this.logger.debug("[BodhiExtClient] Sending stream request to bodhi port:", S), d.postMessage(S);
|
|
1554
1514
|
} catch (u) {
|
|
1555
1515
|
const d = u;
|
|
1556
1516
|
this.logger.error("[BodhiExtClient] Stream error:", JSON.stringify(d.message)), e.postMessage({
|
|
@@ -1633,9 +1593,9 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1633
1593
|
} catch (t) {
|
|
1634
1594
|
this.logger.warn("Token refresh failed:", t);
|
|
1635
1595
|
}
|
|
1636
|
-
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),
|
|
1637
|
-
"
|
|
1638
|
-
"
|
|
1596
|
+
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"), l(
|
|
1597
|
+
"auth_error",
|
|
1598
|
+
"Access token expired and unable to refresh. Try logging out and logging in again."
|
|
1639
1599
|
);
|
|
1640
1600
|
}
|
|
1641
1601
|
/**
|
|
@@ -1670,20 +1630,20 @@ const ge = ["ggedphdcbekjlomjaidbajglgihbeaon"], Te = ["bjdjhiombmfbcoeojijpfckl
|
|
|
1670
1630
|
return `Client not initialized. Extension discovery not triggered nor extensionId set, cannot handle request: ${JSON.stringify(e)}`;
|
|
1671
1631
|
}
|
|
1672
1632
|
};
|
|
1673
|
-
|
|
1674
|
-
let
|
|
1675
|
-
const
|
|
1633
|
+
p.STREAM_TIMEOUT = 6e4;
|
|
1634
|
+
let A = p;
|
|
1635
|
+
const _e = "production";
|
|
1676
1636
|
export {
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1637
|
+
A as BodhiExtClient,
|
|
1638
|
+
he as DEFAULT_API_TIMEOUT_MS,
|
|
1639
|
+
ne as DISCOVERY_ATTEMPTS,
|
|
1640
|
+
ce as DISCOVERY_ATTEMPT_TIMEOUT,
|
|
1641
|
+
ae as DISCOVERY_ATTEMPT_WAIT_MS,
|
|
1642
|
+
ie as DISCOVERY_TIMEOUT_MS,
|
|
1643
|
+
m as EXT2EXT_CLIENT_ACTIONS,
|
|
1684
1644
|
c as EXT2EXT_CLIENT_MESSAGE_TYPES,
|
|
1685
1645
|
k as EXT2EXT_CLIENT_STREAM_PORT,
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1646
|
+
_e as EXT_BUILD_MODE,
|
|
1647
|
+
pe as ExtUIClient,
|
|
1648
|
+
ue as isExtClientApiError
|
|
1689
1649
|
};
|
package/dist/ext-client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse } from '@bodhiapp/ts-client';
|
|
2
|
-
import { IExtensionClient, Chat, Models, Embeddings, Toolsets, Mcps,
|
|
1
|
+
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, PingResponse } from '@bodhiapp/ts-client';
|
|
2
|
+
import { IExtensionClient, Chat, Models, Embeddings, Toolsets, Mcps, AuthState, BackendServerState, ClientState, ExtensionState, InitParams, LoginOptions, LogLevel, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
|
|
3
|
+
import { ApiResponse } from '@bodhiapp/bodhi-browser-types';
|
|
3
4
|
export type SerializedExt2ExtState = {
|
|
4
5
|
extensionId?: string;
|
|
5
6
|
};
|
|
@@ -99,9 +100,10 @@ export declare class ExtClient implements IExtensionClient {
|
|
|
99
100
|
*/
|
|
100
101
|
private sendRawApiMessage;
|
|
101
102
|
/**
|
|
102
|
-
* Send an API message and
|
|
103
|
+
* Send an API message and return ApiResponse
|
|
104
|
+
* @throws BodhiError on operational errors (network, timeout, extension-level)
|
|
103
105
|
*/
|
|
104
|
-
sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<
|
|
106
|
+
sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<ApiResponse<TRes>>;
|
|
105
107
|
/**
|
|
106
108
|
* Login user via OAuth
|
|
107
109
|
* @param options - Optional login parameters including toolsetScopeIds
|
|
@@ -124,9 +126,7 @@ export declare class ExtClient implements IExtensionClient {
|
|
|
124
126
|
/**
|
|
125
127
|
* Ping bodhi-browser-ext API via /ping endpoint
|
|
126
128
|
*/
|
|
127
|
-
pingApi(): Promise<
|
|
128
|
-
message: string;
|
|
129
|
-
}>>;
|
|
129
|
+
pingApi(): Promise<ApiResponse<PingResponse>>;
|
|
130
130
|
/**
|
|
131
131
|
* Get backend server state
|
|
132
132
|
* Calls /bodhi/v1/info and returns structured server state
|
|
@@ -141,8 +141,8 @@ export declare class ExtClient implements IExtensionClient {
|
|
|
141
141
|
get embeddings(): Embeddings;
|
|
142
142
|
get toolsets(): Toolsets;
|
|
143
143
|
get mcps(): Mcps;
|
|
144
|
-
requestAccess(body: CreateAccessRequest): Promise<
|
|
145
|
-
getAccessRequestStatus(requestId: string): Promise<
|
|
144
|
+
requestAccess(body: CreateAccessRequest): Promise<ApiResponse<CreateAccessRequestResponse>>;
|
|
145
|
+
getAccessRequestStatus(requestId: string): Promise<ApiResponse<AccessRequestStatusResponse>>;
|
|
146
146
|
pollAccessRequestStatus(requestId: string, options?: {
|
|
147
147
|
intervalMs?: number;
|
|
148
148
|
timeoutMs?: number;
|
package/dist/ext2ext-client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse } from '@bodhiapp/ts-client';
|
|
2
2
|
import { ExtClientApiRequestMessage, ExtClientApiResponseMessage, ExtClientRequestMessage, ExtClientResponseMessage } from './messages';
|
|
3
|
-
import {
|
|
3
|
+
import { ApiResponse } from '@bodhiapp/bodhi-browser-types';
|
|
4
|
+
import { AuthState, LoginOptions, LogLevel, UserScope } from '@bodhiapp/bodhi-js-core';
|
|
4
5
|
export type ClientExtState = 'setup' | 'ready' | 'error';
|
|
5
6
|
export interface BodhiExtClientConfig {
|
|
6
7
|
authServerUrl?: string;
|
|
@@ -121,8 +122,8 @@ export declare class BodhiExtClient {
|
|
|
121
122
|
* Logout current user and revoke tokens
|
|
122
123
|
*/
|
|
123
124
|
logout(): Promise<void>;
|
|
124
|
-
requestAccess(body: CreateAccessRequest): Promise<
|
|
125
|
-
getAccessRequestStatus(requestId: string): Promise<
|
|
125
|
+
requestAccess(body: CreateAccessRequest): Promise<ApiResponse<CreateAccessRequestResponse>>;
|
|
126
|
+
getAccessRequestStatus(requestId: string): Promise<ApiResponse<AccessRequestStatusResponse>>;
|
|
126
127
|
pollAccessRequestStatus(requestId: string, options?: {
|
|
127
128
|
intervalMs?: number;
|
|
128
129
|
timeoutMs?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bodhiapp/bodhi-js-ext",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.29",
|
|
4
4
|
"description": "Extension SDK for Bodhi Browser - chrome.runtime communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/bodhi-ext.cjs.js",
|
|
@@ -37,9 +37,9 @@
|
|
|
37
37
|
"typecheck": "tsc --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@bodhiapp/bodhi-browser-types": "0.0.
|
|
41
|
-
"@bodhiapp/bodhi-js-core": "0.0.
|
|
42
|
-
"@bodhiapp/ts-client": "0.1.
|
|
40
|
+
"@bodhiapp/bodhi-browser-types": "0.0.29",
|
|
41
|
+
"@bodhiapp/bodhi-js-core": "0.0.29",
|
|
42
|
+
"@bodhiapp/ts-client": "0.1.23"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@eslint/js": "^9.23.0",
|