@bodhiapp/bodhi-js 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.
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("@bodhiapp/bodhi-js-core");class _ extends r.DirectClientBase{constructor(e,t){const s=r.createStoragePrefixWithBasePath(e.basePath,r.STORAGE_PREFIXES.WEB_DIRECT),o={authClientId:e.authClientId,authServerUrl:e.authServerUrl,userRole:e.userRole,storagePrefix:s,logLevel:e.logLevel,loggerPrefix:"DirectWebClient",apiTimeoutMs:e.apiTimeoutMs};super(o,t),this.redirectUri=e.redirectUri}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;const s=e?.userRole??this.userRole,o=e?.flowType??"popup";e?.onProgress?.("requesting");const i=new r.AccessRequestBuilder(this.authClientId).requestedRole(s).flowType(o);if(e?.requested&&i.requested(e.requested),o==="redirect"){const u=e?.redirectUrl??this.redirectUri;i.redirectUrl(u)}const n=i.build(),c=await this.requestAccess(n);if(r.isApiResultOperationError(c))throw r.createOperationError(c.error.message,c.error.type);if(!r.isApiResultSuccess(c))throw r.createOperationError(`Access request failed: HTTP ${c.status}`,"auth_error");const{id:l,review_url:a}=c.body;e?.onProgress?.("reviewing");let h;if(o==="popup"){const u=async()=>{const p=await this.getAccessRequestStatus(l);if(!r.isApiResultSuccess(p))return null;const{status:E,access_request_scope:S}=p.body;return E==="approved"?{approved:!0,accessRequestScope:S??void 0}:["denied","failed","expired"].includes(E)?{approved:!1}:null},g=await r.openPopupReview(a,u,{intervalMs:e?.pollIntervalMs??r.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??r.DEFAULT_POLL_TIMEOUT_MS});if(!g.approved)throw r.createOperationError("Access request was denied or expired","auth_error");h=g.accessRequestScope}else return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID,l),window.location.href=a,new Promise(()=>{});return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${h??""}`.trim())}async performOAuthPkce(e){const t=r.generateCodeVerifier(),s=await r.generateCodeChallenge(t),o=r.generateCodeVerifier();localStorage.setItem(this.storageKeys.CODE_VERIFIER,t),localStorage.setItem(this.storageKeys.STATE,o);const i=new URL(this.authEndpoints.authorize);throw i.searchParams.set("client_id",this.authClientId),i.searchParams.set("response_type","code"),i.searchParams.set("redirect_uri",this.redirectUri),i.searchParams.set("scope",e),i.searchParams.set("code_challenge",s),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("state",o),window.location.href=i.toString(),new Error("Redirect initiated")}async handleOAuthCallback(e,t){const s=localStorage.getItem(this.storageKeys.STATE);if(!s||s!==t)throw new Error("Invalid state parameter - possible CSRF attack");await this.exchangeCodeForTokens(e),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const o=await this.getAuthState();if(o.status!=="authenticated")throw new Error("Login failed");return this.setAuthState(o),o}async handleAccessRequestCallback(e){const t=await this.getAccessRequestStatus(e);if(!r.isApiResultSuccess(t))throw r.createOperationError("Failed to get access request status","auth_error");const{status:s,access_request_scope:o}=t.body;if(s!=="approved")throw r.createOperationError(`Access request is not approved: ${s}`,"auth_error");const i=`openid profile email roles ${o??""}`.trim();return localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID),this.performOAuthPkce(i)}async logout(){const e=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);if(e)try{const s=new URLSearchParams({token:e,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:s})}catch(s){this.logger.warn("Token revocation failed:",s)}localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT);const t={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(t),t}async exchangeCodeForTokens(e){const t=localStorage.getItem(this.storageKeys.CODE_VERIFIER);if(!t)throw new Error("Code verifier not found");const s=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:this.redirectUri,client_id:this.authClientId,code_verifier:t})});if(!s.ok){const i=await s.text();throw new Error(`Token exchange failed: ${s.status} ${i}`)}const o=await s.json();if(localStorage.setItem(this.storageKeys.ACCESS_TOKEN,o.access_token),o.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,o.refresh_token),o.expires_in){const i=Date.now()+o.expires_in*1e3;localStorage.setItem(this.storageKeys.EXPIRES_AT,i.toString())}}async _storageGet(e){return localStorage.getItem(e)}async _storageSet(e){Object.entries(e).forEach(([t,s])=>{localStorage.setItem(t,String(s))})}async _storageRemove(e){e.forEach(t=>localStorage.removeItem(t))}_getRedirectUri(){return this.redirectUri}}const f=500,m=5e3,R=3e4;class w{constructor(e,t,s){this.state=r.EXTENSION_STATE_NOT_INITIALIZED,this.bodhiext=null,this.refreshPromise=null,this.logger=new r.Logger("WindowBodhiextClient",t.logLevel),this.authClientId=e,this.config=t,this.authEndpoints=r.createOAuthEndpoints(this.config.authServerUrl),this.onStateChange=s??r.NOOP_STATE_CALLBACK;const o=r.createStoragePrefixWithBasePath(t.basePath,r.STORAGE_PREFIXES.WEB_EXT);this.storageKeys=r.createStorageKeys(o),this.apiTimeoutMs=t.apiTimeoutMs??R}setState(e){this.state=e,this.logger.info(`{state: ${JSON.stringify(e)}} - Setting client state`),this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}ensureBodhiext(){if(!this.bodhiext&&window.bodhiext&&(this.logger.info("Acquiring window.bodhiext reference"),this.bodhiext=window.bodhiext),!this.bodhiext)throw r.createOperationError("Client not initialized","extension_error")}async sendExtRequest(e,t){return this.ensureBodhiext(),this.bodhiext.sendExtRequest(e,t)}async sendApiRequest(e,t,s,o,i){try{this.ensureBodhiext()}catch(n){return{error:{message:n instanceof Error?n.message:String(n),type:"extension_error"}}}try{const n=new Promise((l,a)=>setTimeout(()=>a(new Error(`[bodhi-js-sdk/web] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`)),this.apiTimeoutMs)),c=(async()=>{let l=o||{};if(i){const a=await this._getAccessTokenRaw();if(!a)return{error:{message:"Not authenticated. Please log in first.",type:"extension_error"}};l={...l,Authorization:`Bearer ${a}`}}return this.bodhiext.sendApiRequest(e,t,s,l)})();return await Promise.race([c,n])}catch(n){const c=n?.error,l=c?.message??(n instanceof Error?n.message:String(n)),a=c?.type||"network_error";return{error:{message:l,type:a}}}}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)return this.logger.info("No testConnection or selectedConnection, returning not-initialized state"),r.EXTENSION_STATE_NOT_INITIALIZED;if(this.bodhiext&&!e.testConnection)return this.logger.debug("Already have bodhiext handle, skipping polling"),this.state;if(!this.bodhiext){const o=e.timeoutMs??this.config.initParams?.extension?.timeoutMs??m,i=e.intervalMs??this.config.initParams?.extension?.intervalMs??f,n=Date.now();if(!await new Promise(l=>{const a=()=>{if(window.bodhiext){this.bodhiext=window.bodhiext,l(!0);return}if(Date.now()-n>=o){l(!1);return}setTimeout(a,i)};a()}))return this.logger.warn("Extension discovery timed out"),this.setState(r.EXTENSION_STATE_NOT_FOUND),this.state}const t=await this.bodhiext.getExtensionId();this.logger.info(`Extension discovered: ${t}`);const s={type:"extension",extension:"ready",extensionId:t,server:r.PENDING_EXTENSION_READY};if(e.testConnection)try{const o=await this.getServerState();this.setState({...s,server:o}),this.logger.info(`Server connectivity tested: ${o.status}`)}catch(o){this.logger.error("Failed to get server state:",o),this.setState({...s,server:r.BACKEND_SERVER_NOT_REACHABLE})}else this.setState(s);return this.state}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;this.ensureBodhiext();const s=e?.userRole??this.config.userRole,o=e?.flowType??"popup";e?.onProgress?.("requesting");const i=new r.AccessRequestBuilder(this.authClientId).requestedRole(s).flowType(o);if(e?.requested&&i.requested(e.requested),o==="redirect"){const u=e?.redirectUrl??this.config.redirectUri;i.redirectUrl(u)}const n=i.build(),c=await this.requestAccess(n);if(r.isApiResultOperationError(c))throw r.createOperationError(c.error.message,c.error.type);if(!r.isApiResultSuccess(c))throw r.createOperationError(`Access request failed: HTTP ${c.status}`,"auth_error");const{id:l,review_url:a}=c.body;e?.onProgress?.("reviewing");let h;if(o==="popup"){const u=async()=>{const p=await this.getAccessRequestStatus(l);if(!r.isApiResultSuccess(p))return null;const{status:E,access_request_scope:S}=p.body;return E==="approved"?{approved:!0,accessRequestScope:S??void 0}:["denied","failed","expired"].includes(E)?{approved:!1}:null},g=await r.openPopupReview(a,u,{intervalMs:e?.pollIntervalMs??r.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??r.DEFAULT_POLL_TIMEOUT_MS});if(!g.approved)throw r.createOperationError("Access request was denied or expired","auth_error");h=g.accessRequestScope}else return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID,l),window.location.href=a,new Promise(()=>{});return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${h??""}`.trim())}async handleOAuthCallback(e,t){const s=localStorage.getItem(this.storageKeys.STATE);if(!s||s!==t)throw new Error("Invalid state parameter - possible CSRF attack");await this.exchangeCodeForTokens(e),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const o=await this.getAuthState();if(o.status!=="authenticated")throw new Error("Login failed");return this.setAuthState(o),o}async exchangeCodeForTokens(e){const t=localStorage.getItem(this.storageKeys.CODE_VERIFIER);if(!t)throw new Error("Code verifier not found");const s=new URLSearchParams({grant_type:"authorization_code",client_id:this.authClientId,code:e,redirect_uri:this.config.redirectUri,code_verifier:t}),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s});if(!o.ok){const n=await o.text();throw new Error(`Token exchange failed: ${o.status} ${n}`)}const i=await o.json();if(!i.access_token)throw new Error("No access token received");if(localStorage.setItem(this.storageKeys.ACCESS_TOKEN,i.access_token),i.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,i.refresh_token),i.expires_in){const n=Date.now()+i.expires_in*1e3;localStorage.setItem(this.storageKeys.EXPIRES_AT,n.toString())}}async logout(){const e=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);if(e)try{const s=new URLSearchParams({token:e,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:s})}catch(s){this.logger.warn("Token revocation failed:",s)}localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const t={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(t),t}async getAuthState(){const e=await this._getAccessTokenRaw();if(!e)return{status:"unauthenticated",user:null,accessToken:null,error:null};try{return{status:"authenticated",user:r.extractUserInfo(e),accessToken:e,error:null}}catch(t){return this.logger.error("Failed to parse token:",t),{status:"unauthenticated",user:null,accessToken:null,error:null}}}async _getAccessTokenRaw(){const e=localStorage.getItem(this.storageKeys.ACCESS_TOKEN),t=localStorage.getItem(this.storageKeys.EXPIRES_AT);if(!e)return null;if(t){const s=parseInt(t,10);if(Date.now()>=s-5*1e3){const o=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);return o?this._tryRefreshToken(o):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 r.refreshAccessToken(this.authEndpoints.token,e,this.authClientId);if(t.success){this._storeRefreshedTokens(t.tokens);const s=r.extractUserInfo(t.tokens.access_token);return this.setAuthState({status:"authenticated",user:s,accessToken:t.tokens.access_token,error:null}),this.logger.info("Token refreshed successfully"),t.tokens.access_token}if(t.error==="invalid_grant")return this.logger.warn("Refresh token expired or revoked, clearing tokens and logging out"),this.clearAuthStorage(),this.setAuthState({status:"unauthenticated",user:null,accessToken:null,error:null}),null}catch(t){this.logger.warn("Token refresh failed:",t)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),r.createOperationError("Access token expired and unable to refresh. Try logging out and logging in again.","token_refresh_failed")}clearAuthStorage(){localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT)}_storeRefreshedTokens(e){const t=Date.now()+e.expires_in*1e3;localStorage.setItem(this.storageKeys.ACCESS_TOKEN,e.access_token),localStorage.setItem(this.storageKeys.EXPIRES_AT,String(t)),e.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,e.refresh_token)}async pingApi(){return this.sendApiRequest("GET","/ping")}async getServerState(){const e=await this.sendApiRequest("GET","/bodhi/v1/info");if(r.isApiResultOperationError(e)||!r.isApiResultSuccess(e))return r.BACKEND_SERVER_NOT_REACHABLE;const t=e.body,s=t.version||"unknown";switch(t.status){case"ready":return{status:"ready",version:s,error:null,deployment:t.deployment??null,client_id:t.client_id??null};case"setup":return r.backendServerNotReady("setup",s,void 0,t.deployment,t.client_id);case"resource_admin":return r.backendServerNotReady("resource_admin",s,void 0,t.deployment,t.client_id);case"tenant_selection":return{status:"tenant_selection",version:s,error:null,deployment:t.deployment??null,client_id:t.client_id??null};case"error":return r.backendServerNotReady("error",s,t.error?{message:t.error.message,type:t.error.type}:r.SERVER_ERROR_CODES.SERVER_NOT_READY,t.deployment,t.client_id);default:return r.BACKEND_SERVER_NOT_REACHABLE}}async*stream(e,t,s,o,i=!0){this.ensureBodhiext();let n=o||{};if(i){const a=await this._getAccessTokenRaw();if(!a)throw r.createOperationError("Not authenticated. Please log in first.","auth_error");n={...n,Authorization:`Bearer ${a}`}}const l=this.bodhiext.sendStreamRequest(e,t,s,n).getReader();try{for(;;){const{value:a,done:h}=await l.read();if(h||a?.done)break;yield a.body}}catch(a){if(a instanceof Error){if("response"in a){const h=a;throw r.createApiError(a.message,h.response.status,h.response.body)}throw"error"in a,r.createOperationError(a.message,"extension_error")}throw a}finally{l.releaseLock()}}get chat(){return this._chat??=new r.Chat(this)}get models(){return this._models??=new r.Models(this)}get embeddings(){return this._embeddings??=new r.Embeddings(this)}get toolsets(){return this._toolsets??=new r.Toolsets(this)}get mcps(){return this._mcps??=new r.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 r.pollAccessRequestUntilResolved(s=>this.getAccessRequestStatus(s),e,t)}async performOAuthPkce(e){const t=r.generateCodeVerifier(),s=await r.generateCodeChallenge(t),o=r.generateCodeVerifier();localStorage.setItem(this.storageKeys.CODE_VERIFIER,t),localStorage.setItem(this.storageKeys.STATE,o);const i=e.split(" ").filter(Boolean),n=new URLSearchParams({response_type:"code",client_id:this.authClientId,redirect_uri:this.config.redirectUri,scope:i.join(" "),state:o,code_challenge:s,code_challenge_method:"S256"});return window.location.href=`${this.authEndpoints.authorize}?${n}`,new Promise(()=>{})}async handleAccessRequestCallback(e){const t=await this.getAccessRequestStatus(e);if(!r.isApiResultSuccess(t))throw r.createOperationError("Failed to get access request status","auth_error");const{status:s,access_request_scope:o}=t.body;if(s!=="approved")throw r.createOperationError(`Access request is not approved: ${s}`,"auth_error");const i=`openid profile email roles ${o??""}`.trim();return localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID),this.performOAuthPkce(i)}serialize(){return{extensionId:this.state.type==="extension"&&this.state.extension==="ready"?this.state.extensionId:void 0}}async debug(){return{type:"WindowBodhiextClient",state:this.state,authState:await this.getAuthState(),bodhiextAvailable:this.bodhiext!==null,authClientId:this.authClientId,authServerUrl:this.config.authServerUrl,redirectUri:this.config.redirectUri,userRole:this.config.userRole}}}function y(d){if(typeof window>"u")throw new Error("redirectUri required in non-browser environment");const e=d==="/"?"":d.replace(/\/$/,"");return`${window.location.origin}${e}/callback`}class T extends r.BaseFacadeClient{constructor(e,t,s){const o=t||{},i={basePath:o.basePath||"/",redirectUri:o.redirectUri||y(o.basePath||"/"),authServerUrl:o.authServerUrl||"https://id.getbodhi.app/realms/bodhi",userRole:o.userRole||"scope_user_user",logLevel:o.logLevel||"warn",apiTimeoutMs:o.apiTimeoutMs,initParams:o.initParams};super(e,i,s)}createLogger(e){return new r.Logger("WebUIClient",e.logLevel)}createStoragePrefix(e){return r.createStoragePrefixWithBasePath(e.basePath,r.STORAGE_PREFIXES.WEB)}createExtClient(e,t){return new w(this.authClientId,{authServerUrl:e.authServerUrl,redirectUri:e.redirectUri,userRole:e.userRole,basePath:e.basePath,logLevel:e.logLevel,apiTimeoutMs:e.apiTimeoutMs,initParams:e.initParams},t)}createDirectClient(e,t,s){return new _({authClientId:e,authServerUrl:t.authServerUrl,redirectUri:t.redirectUri,userRole:t.userRole,logLevel:t.logLevel,basePath:t.basePath,apiTimeoutMs:t.apiTimeoutMs},s)}async handleOAuthCallback(e,t){return this.connectionMode==="direct"?this.directClient.handleOAuthCallback(e,t):this.extClient.handleOAuthCallback(e,t)}async handleAccessRequestCallback(e){return this.connectionMode==="direct"?this.directClient.handleAccessRequestCallback(e):this.extClient.handleAccessRequestCallback(e)}}const A="production";exports.WEB_BUILD_MODE=A;exports.WebUIClient=T;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const r=require("@bodhiapp/bodhi-js-core");class p extends r.DirectClientBase{constructor(e,t){const s=r.createStoragePrefixWithBasePath(e.basePath,r.STORAGE_PREFIXES.WEB_DIRECT),o={authClientId:e.authClientId,authServerUrl:e.authServerUrl,userRole:e.userRole,storagePrefix:s,logLevel:e.logLevel,loggerPrefix:"DirectWebClient",apiTimeoutMs:e.apiTimeoutMs};super(o,t),this.redirectUri=e.redirectUri}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;const s=e?.userRole??this.userRole,o=e?.flowType??"popup";e?.onProgress?.("requesting");const i=new r.AccessRequestBuilder(this.authClientId).requestedRole(s).flowType(o);if(e?.requested&&i.requested(e.requested),o==="redirect"){const u=e?.redirectUrl??this.redirectUri;i.redirectUrl(u)}const n=i.build(),l=await this.requestAccess(n),{id:c,review_url:a}=r.unwrapResponse(l);e?.onProgress?.("reviewing");let h;if(o==="popup"){const u=async()=>{const S=await this.getAccessRequestStatus(c);if(S.status>=400)return null;const{status:g,access_request_scope:f}=S.body;return g==="approved"?{approved:!0,accessRequestScope:f??void 0}:["denied","failed","expired"].includes(g)?{approved:!1,status:g}:null},d=await r.openPopupReview(a,u,{intervalMs:e?.pollIntervalMs??r.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??r.DEFAULT_POLL_TIMEOUT_MS});d.approved||r.throwAccessRequestDenialError(d.status??"unknown"),h=d.accessRequestScope}else return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID,c),window.location.href=a,new Promise(()=>{});return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${h??""}`.trim())}async performOAuthPkce(e){const t=r.generateCodeVerifier(),s=await r.generateCodeChallenge(t),o=r.generateCodeVerifier();localStorage.setItem(this.storageKeys.CODE_VERIFIER,t),localStorage.setItem(this.storageKeys.STATE,o);const i=new URL(this.authEndpoints.authorize);throw i.searchParams.set("client_id",this.authClientId),i.searchParams.set("response_type","code"),i.searchParams.set("redirect_uri",this.redirectUri),i.searchParams.set("scope",e),i.searchParams.set("code_challenge",s),i.searchParams.set("code_challenge_method","S256"),i.searchParams.set("state",o),window.location.href=i.toString(),new Error("Redirect initiated")}async handleOAuthCallback(e,t){const s=localStorage.getItem(this.storageKeys.STATE);if(!s||s!==t)throw new Error("Invalid state parameter - possible CSRF attack");await this.exchangeCodeForTokens(e),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const o=await this.getAuthState();if(o.status!=="authenticated")throw new Error("Login failed");return this.setAuthState(o),o}async handleAccessRequestCallback(e){const t=await this.getAccessRequestStatus(e),{status:s,access_request_scope:o}=r.unwrapResponse(t);localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID),s!=="approved"&&r.throwAccessRequestDenialError(s);const i=`openid profile email roles ${o??""}`.trim();return this.performOAuthPkce(i)}async logout(){const e=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);if(e)try{const s=new URLSearchParams({token:e,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:s})}catch(s){this.logger.warn("Token revocation failed:",s)}localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT);const t={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(t),t}async exchangeCodeForTokens(e){const t=localStorage.getItem(this.storageKeys.CODE_VERIFIER);if(!t)throw new Error("Code verifier not found");const s=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:this.redirectUri,client_id:this.authClientId,code_verifier:t})});if(!s.ok){const i=await s.text();throw new Error(`Token exchange failed: ${s.status} ${i}`)}const o=await s.json();if(localStorage.setItem(this.storageKeys.ACCESS_TOKEN,o.access_token),o.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,o.refresh_token),o.expires_in){const i=Date.now()+o.expires_in*1e3;localStorage.setItem(this.storageKeys.EXPIRES_AT,i.toString())}}async _storageGet(e){return localStorage.getItem(e)}async _storageSet(e){Object.entries(e).forEach(([t,s])=>{localStorage.setItem(t,String(s))})}async _storageRemove(e){e.forEach(t=>localStorage.removeItem(t))}_getRedirectUri(){return this.redirectUri}}const w=500,m=5e3,_=3e4;class R{constructor(e,t,s){this.state=r.EXTENSION_STATE_NOT_INITIALIZED,this.bodhiext=null,this.refreshPromise=null,this.logger=new r.Logger("WindowBodhiextClient",t.logLevel),this.authClientId=e,this.config=t,this.authEndpoints=r.createOAuthEndpoints(this.config.authServerUrl),this.onStateChange=s??r.NOOP_STATE_CALLBACK;const o=r.createStoragePrefixWithBasePath(t.basePath,r.STORAGE_PREFIXES.WEB_EXT);this.storageKeys=r.createStorageKeys(o),this.apiTimeoutMs=t.apiTimeoutMs??_}setState(e){this.state=e,this.logger.info(`{state: ${JSON.stringify(e)}} - Setting client state`),this.onStateChange({type:"client-state",state:e})}setAuthState(e){this.onStateChange({type:"auth-state",state:e})}setStateCallback(e){this.onStateChange=e}ensureBodhiext(){if(!this.bodhiext&&window.bodhiext&&(this.logger.info("Acquiring window.bodhiext reference"),this.bodhiext=window.bodhiext),!this.bodhiext)throw r.createOperationError("not_initialized","Client not initialized")}async sendExtRequest(e,t){return this.ensureBodhiext(),this.bodhiext.sendExtRequest(e,t)}async sendApiRequest(e,t,s,o,i){this.ensureBodhiext();try{const n=new Promise((c,a)=>setTimeout(()=>a(new r.BodhiError("timeout_error",`[bodhi-js-sdk/web] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`)),this.apiTimeoutMs)),l=(async()=>{let c=o||{};if(i){const a=await this._getAccessTokenRaw();if(!a)throw new r.BodhiError("auth_error","Not authenticated. Please log in first.");c={...c,Authorization:`Bearer ${a}`}}return this.bodhiext.sendApiRequest(e,t,s,c)})();return await Promise.race([l,n])}catch(n){if(n instanceof r.BodhiApiError||n instanceof r.BodhiError)throw n;if(n instanceof Error){const l=n;throw n.name==="BodhiApiError"&&typeof l.status=="number"&&l.body!=null?new r.BodhiApiError(l.status,l.body,n.message,l.headers):n.name==="BodhiError"&&typeof l.code=="string"?new r.BodhiError(l.code,n.message):new r.BodhiError("network_error",n.message)}throw new r.BodhiError("network_error",String(n))}}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)return this.logger.info("No testConnection or selectedConnection, returning not-initialized state"),r.EXTENSION_STATE_NOT_INITIALIZED;if(this.bodhiext&&!e.testConnection)return this.logger.debug("Already have bodhiext handle, skipping polling"),this.state;if(!this.bodhiext){const o=e.timeoutMs??this.config.initParams?.extension?.timeoutMs??m,i=e.intervalMs??this.config.initParams?.extension?.intervalMs??w,n=Date.now();if(!await new Promise(c=>{const a=()=>{if(window.bodhiext){this.bodhiext=window.bodhiext,c(!0);return}if(Date.now()-n>=o){c(!1);return}setTimeout(a,i)};a()}))return this.logger.warn("Extension discovery timed out"),this.setState(r.EXTENSION_STATE_NOT_FOUND),this.state}const t=await this.bodhiext.getExtensionId();this.logger.info(`Extension discovered: ${t}`);const s={type:"extension",extension:"ready",extensionId:t,server:r.PENDING_EXTENSION_READY};if(e.testConnection)try{const o=await this.getServerState();this.setState({...s,server:o}),this.logger.info(`Server connectivity tested: ${o.status}`)}catch(o){this.logger.error("Failed to get server state:",o),this.setState({...s,server:r.BACKEND_SERVER_NOT_REACHABLE})}else this.setState(s);return this.state}async login(e){const t=await this.getAuthState();if(t.status==="authenticated")return t;this.ensureBodhiext();const s=e?.userRole??this.config.userRole,o=e?.flowType??"popup";e?.onProgress?.("requesting");const i=new r.AccessRequestBuilder(this.authClientId).requestedRole(s).flowType(o);if(e?.requested&&i.requested(e.requested),o==="redirect"){const u=e?.redirectUrl??this.config.redirectUri;i.redirectUrl(u)}const n=i.build(),l=await this.requestAccess(n),{id:c,review_url:a}=r.unwrapResponse(l);e?.onProgress?.("reviewing");let h;if(o==="popup"){const u=async()=>{const S=await this.getAccessRequestStatus(c);if(S.status>=400)return null;const{status:g,access_request_scope:f}=S.body;return g==="approved"?{approved:!0,accessRequestScope:f??void 0}:["denied","failed","expired"].includes(g)?{approved:!1,status:g}:null},d=await r.openPopupReview(a,u,{intervalMs:e?.pollIntervalMs??r.DEFAULT_POLL_INTERVAL_MS,timeoutMs:e?.pollTimeoutMs??r.DEFAULT_POLL_TIMEOUT_MS});d.approved||r.throwAccessRequestDenialError(d.status??"unknown"),h=d.accessRequestScope}else return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID,c),window.location.href=a,new Promise(()=>{});return e?.onProgress?.("authenticating"),this.performOAuthPkce(`openid profile email roles ${h??""}`.trim())}async handleOAuthCallback(e,t){const s=localStorage.getItem(this.storageKeys.STATE);if(!s||s!==t)throw new Error("Invalid state parameter - possible CSRF attack");await this.exchangeCodeForTokens(e),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const o=await this.getAuthState();if(o.status!=="authenticated")throw new Error("Login failed");return this.setAuthState(o),o}async exchangeCodeForTokens(e){const t=localStorage.getItem(this.storageKeys.CODE_VERIFIER);if(!t)throw new Error("Code verifier not found");const s=new URLSearchParams({grant_type:"authorization_code",client_id:this.authClientId,code:e,redirect_uri:this.config.redirectUri,code_verifier:t}),o=await fetch(this.authEndpoints.token,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s});if(!o.ok){const n=await o.text();throw new Error(`Token exchange failed: ${o.status} ${n}`)}const i=await o.json();if(!i.access_token)throw new Error("No access token received");if(localStorage.setItem(this.storageKeys.ACCESS_TOKEN,i.access_token),i.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,i.refresh_token),i.expires_in){const n=Date.now()+i.expires_in*1e3;localStorage.setItem(this.storageKeys.EXPIRES_AT,n.toString())}}async logout(){const e=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);if(e)try{const s=new URLSearchParams({token:e,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:s})}catch(s){this.logger.warn("Token revocation failed:",s)}localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT),localStorage.removeItem(this.storageKeys.CODE_VERIFIER),localStorage.removeItem(this.storageKeys.STATE);const t={status:"unauthenticated",user:null,accessToken:null,error:null};return this.setAuthState(t),t}async getAuthState(){const e=await this._getAccessTokenRaw();if(!e)return{status:"unauthenticated",user:null,accessToken:null,error:null};try{return{status:"authenticated",user:r.extractUserInfo(e),accessToken:e,error:null}}catch(t){return this.logger.error("Failed to parse token:",t),{status:"unauthenticated",user:null,accessToken:null,error:null}}}async _getAccessTokenRaw(){const e=localStorage.getItem(this.storageKeys.ACCESS_TOKEN),t=localStorage.getItem(this.storageKeys.EXPIRES_AT);if(!e)return null;if(t){const s=parseInt(t,10);if(Date.now()>=s-5*1e3){const o=localStorage.getItem(this.storageKeys.REFRESH_TOKEN);return o?this._tryRefreshToken(o):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 r.refreshAccessToken(this.authEndpoints.token,e,this.authClientId);if(t.success){this._storeRefreshedTokens(t.tokens);const s=r.extractUserInfo(t.tokens.access_token);return this.setAuthState({status:"authenticated",user:s,accessToken:t.tokens.access_token,error:null}),this.logger.info("Token refreshed successfully"),t.tokens.access_token}if(t.error==="invalid_grant")return this.logger.warn("Refresh token expired or revoked, clearing tokens and logging out"),this.clearAuthStorage(),this.setAuthState({status:"unauthenticated",user:null,accessToken:null,error:null}),null}catch(t){this.logger.warn("Token refresh failed:",t)}throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),r.createOperationError("auth_error","Access token expired and unable to refresh. Try logging out and logging in again.")}clearAuthStorage(){localStorage.removeItem(this.storageKeys.ACCESS_TOKEN),localStorage.removeItem(this.storageKeys.REFRESH_TOKEN),localStorage.removeItem(this.storageKeys.EXPIRES_AT)}_storeRefreshedTokens(e){const t=Date.now()+e.expires_in*1e3;localStorage.setItem(this.storageKeys.ACCESS_TOKEN,e.access_token),localStorage.setItem(this.storageKeys.EXPIRES_AT,String(t)),e.refresh_token&&localStorage.setItem(this.storageKeys.REFRESH_TOKEN,e.refresh_token)}async pingApi(){return this.sendApiRequest("GET","/ping")}async getServerState(){try{const e=await this.sendApiRequest("GET","/bodhi/v1/info");if(e.status>=400)return r.BACKEND_SERVER_NOT_REACHABLE;const t=e.body,s=t.version||"unknown";switch(t.status){case"ready":return{status:"ready",version:s,error:null,deployment:t.deployment??null,client_id:t.client_id??null};case"setup":return r.backendServerNotReady("setup",s,void 0,t.deployment,t.client_id);case"resource_admin":return r.backendServerNotReady("resource_admin",s,void 0,t.deployment,t.client_id);case"error":return r.backendServerNotReady("error",s,t.error?{message:t.error.message,type:t.error.type}:r.SERVER_ERROR_CODES.SERVER_NOT_READY,t.deployment,t.client_id);default:return r.BACKEND_SERVER_NOT_REACHABLE}}catch{return r.BACKEND_SERVER_NOT_REACHABLE}}async*stream(e,t,s,o,i=!0){this.ensureBodhiext();let n=o||{};if(i){const a=await this._getAccessTokenRaw();if(!a)throw r.createOperationError("auth_error","Not authenticated. Please log in first.");n={...n,Authorization:`Bearer ${a}`}}const c=this.bodhiext.sendStreamRequest(e,t,s,n).getReader();try{for(;;){const{value:a,done:h}=await c.read();if(h||a?.done)break;yield a.body}}catch(a){if(a instanceof r.BodhiApiError||a instanceof r.BodhiError)throw a;if(a instanceof Error){const h=a;throw a.name==="BodhiApiError"&&typeof h.status=="number"&&h.body!=null?new r.BodhiApiError(h.status,h.body,a.message,h.headers):a.name==="BodhiError"&&typeof h.code=="string"?new r.BodhiError(h.code,a.message):new r.BodhiError("extension_error",a.message)}throw a}finally{c.releaseLock()}}get chat(){return this._chat??=new r.Chat(this)}get models(){return this._models??=new r.Models(this)}get embeddings(){return this._embeddings??=new r.Embeddings(this)}get toolsets(){return this._toolsets??=new r.Toolsets(this)}get mcps(){return this._mcps??=new r.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 r.pollAccessRequestUntilResolved(s=>this.getAccessRequestStatus(s),e,t)}async performOAuthPkce(e){const t=r.generateCodeVerifier(),s=await r.generateCodeChallenge(t),o=r.generateCodeVerifier();localStorage.setItem(this.storageKeys.CODE_VERIFIER,t),localStorage.setItem(this.storageKeys.STATE,o);const i=e.split(" ").filter(Boolean),n=new URLSearchParams({response_type:"code",client_id:this.authClientId,redirect_uri:this.config.redirectUri,scope:i.join(" "),state:o,code_challenge:s,code_challenge_method:"S256"});return window.location.href=`${this.authEndpoints.authorize}?${n}`,new Promise(()=>{})}async handleAccessRequestCallback(e){const t=await this.getAccessRequestStatus(e),{status:s,access_request_scope:o}=r.unwrapResponse(t);localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID),s!=="approved"&&r.throwAccessRequestDenialError(s);const i=`openid profile email roles ${o??""}`.trim();return this.performOAuthPkce(i)}serialize(){return{extensionId:this.state.type==="extension"&&this.state.extension==="ready"?this.state.extensionId:void 0}}async debug(){return{type:"WindowBodhiextClient",state:this.state,authState:await this.getAuthState(),bodhiextAvailable:this.bodhiext!==null,authClientId:this.authClientId,authServerUrl:this.config.authServerUrl,redirectUri:this.config.redirectUri,userRole:this.config.userRole}}}function T(E){if(typeof window>"u")throw new Error("redirectUri required in non-browser environment");const e=E==="/"?"":E.replace(/\/$/,"");return`${window.location.origin}${e}/callback`}class y extends r.BaseFacadeClient{constructor(e,t,s){const o=t||{},i={basePath:o.basePath||"/",redirectUri:o.redirectUri||T(o.basePath||"/"),authServerUrl:o.authServerUrl||"https://id.getbodhi.app/realms/bodhi",userRole:o.userRole||"scope_user_user",logLevel:o.logLevel||"warn",apiTimeoutMs:o.apiTimeoutMs,initParams:o.initParams};super(e,i,s)}createLogger(e){return new r.Logger("WebUIClient",e.logLevel)}createStoragePrefix(e){return r.createStoragePrefixWithBasePath(e.basePath,r.STORAGE_PREFIXES.WEB)}createExtClient(e,t){return new R(this.authClientId,{authServerUrl:e.authServerUrl,redirectUri:e.redirectUri,userRole:e.userRole,basePath:e.basePath,logLevel:e.logLevel,apiTimeoutMs:e.apiTimeoutMs,initParams:e.initParams},t)}createDirectClient(e,t,s){return new p({authClientId:e,authServerUrl:t.authServerUrl,redirectUri:t.redirectUri,userRole:t.userRole,logLevel:t.logLevel,basePath:t.basePath,apiTimeoutMs:t.apiTimeoutMs},s)}async handleOAuthCallback(e,t){return this.connectionMode==="direct"?this.directClient.handleOAuthCallback(e,t):this.extClient.handleOAuthCallback(e,t)}async handleAccessRequestCallback(e){return this.connectionMode==="direct"?this.directClient.handleAccessRequestCallback(e):this.extClient.handleAccessRequestCallback(e)}}const A="production";exports.WEB_BUILD_MODE=A;exports.WebUIClient=y;
@@ -1,9 +1,9 @@
1
- import { DirectClientBase as O, createStoragePrefixWithBasePath as R, STORAGE_PREFIXES as T, AccessRequestBuilder as C, isApiResultOperationError as y, createOperationError as l, isApiResultSuccess as u, openPopupReview as v, DEFAULT_POLL_TIMEOUT_MS as k, DEFAULT_POLL_INTERVAL_MS as x, generateCodeVerifier as _, generateCodeChallenge as b, EXTENSION_STATE_NOT_INITIALIZED as I, Logger as P, createOAuthEndpoints as q, NOOP_STATE_CALLBACK as U, createStorageKeys as K, EXTENSION_STATE_NOT_FOUND as L, PENDING_EXTENSION_READY as N, BACKEND_SERVER_NOT_REACHABLE as m, extractUserInfo as A, refreshAccessToken as D, backendServerNotReady as w, SERVER_ERROR_CODES as M, createApiError as F, Chat as B, Models as $, Embeddings as V, Toolsets as H, Mcps as z, pollAccessRequestUntilResolved as W, BaseFacadeClient as X } from "@bodhiapp/bodhi-js-core";
2
- class j extends O {
1
+ import { DirectClientBase as K, createStoragePrefixWithBasePath as I, STORAGE_PREFIXES as A, AccessRequestBuilder as k, unwrapResponse as w, openPopupReview as b, DEFAULT_POLL_TIMEOUT_MS as x, DEFAULT_POLL_INTERVAL_MS as P, throwAccessRequestDenialError as E, generateCodeVerifier as _, generateCodeChallenge as O, EXTENSION_STATE_NOT_INITIALIZED as C, Logger as U, createOAuthEndpoints as q, NOOP_STATE_CALLBACK as L, createStorageKeys as N, createOperationError as R, BodhiError as h, BodhiApiError as m, EXTENSION_STATE_NOT_FOUND as D, PENDING_EXTENSION_READY as M, BACKEND_SERVER_NOT_REACHABLE as p, extractUserInfo as v, refreshAccessToken as F, backendServerNotReady as T, SERVER_ERROR_CODES as B, Chat as $, Models as V, Embeddings as z, Toolsets as H, Mcps as W, pollAccessRequestUntilResolved as X, BaseFacadeClient as G } from "@bodhiapp/bodhi-js-core";
2
+ class j extends K {
3
3
  constructor(e, t) {
4
- const s = R(
4
+ const s = I(
5
5
  e.basePath,
6
- T.WEB_DIRECT
6
+ A.WEB_DIRECT
7
7
  ), r = {
8
8
  authClientId: e.authClientId,
9
9
  authServerUrl: e.authServerUrl,
@@ -24,42 +24,32 @@ class j extends O {
24
24
  return t;
25
25
  const s = e?.userRole ?? this.userRole, r = e?.flowType ?? "popup";
26
26
  e?.onProgress?.("requesting");
27
- const o = new C(this.authClientId).requestedRole(s).flowType(r);
27
+ const o = new k(this.authClientId).requestedRole(s).flowType(r);
28
28
  if (e?.requested && o.requested(e.requested), r === "redirect") {
29
- const g = e?.redirectUrl ?? this.redirectUri;
30
- o.redirectUrl(g);
29
+ const f = e?.redirectUrl ?? this.redirectUri;
30
+ o.redirectUrl(f);
31
31
  }
32
- const a = o.build(), n = await this.requestAccess(a);
33
- if (y(n))
34
- throw l(n.error.message, n.error.type);
35
- if (!u(n))
36
- throw l(
37
- `Access request failed: HTTP ${n.status}`,
38
- "auth_error"
39
- );
40
- const { id: c, review_url: i } = n.body;
32
+ const a = o.build(), c = await this.requestAccess(a), { id: n, review_url: i } = w(c);
41
33
  e?.onProgress?.("reviewing");
42
- let h;
34
+ let l;
43
35
  if (r === "popup") {
44
- const p = await v(i, async () => {
45
- const S = await this.getAccessRequestStatus(c);
46
- if (!u(S)) return null;
47
- const { status: f, access_request_scope: E } = S.body;
48
- return f === "approved" ? { approved: !0, accessRequestScope: E ?? void 0 } : ["denied", "failed", "expired"].includes(f) ? { approved: !1 } : null;
36
+ const u = await b(i, async () => {
37
+ const S = await this.getAccessRequestStatus(n);
38
+ if (S.status >= 400) return null;
39
+ const { status: d, access_request_scope: y } = S.body;
40
+ return d === "approved" ? { approved: !0, accessRequestScope: y ?? void 0 } : ["denied", "failed", "expired"].includes(d) ? { approved: !1, status: d } : null;
49
41
  }, {
50
- intervalMs: e?.pollIntervalMs ?? x,
51
- timeoutMs: e?.pollTimeoutMs ?? k
42
+ intervalMs: e?.pollIntervalMs ?? P,
43
+ timeoutMs: e?.pollTimeoutMs ?? x
52
44
  });
53
- if (!p.approved)
54
- throw l("Access request was denied or expired", "auth_error");
55
- h = p.accessRequestScope;
45
+ u.approved || E(u.status ?? "unknown"), l = u.accessRequestScope;
56
46
  } else
57
- return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID, c), window.location.href = i, new Promise(() => {
47
+ return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID, n), window.location.href = i, new Promise(() => {
58
48
  });
59
- return e?.onProgress?.("authenticating"), this.performOAuthPkce(`openid profile email roles ${h ?? ""}`.trim());
49
+ return e?.onProgress?.("authenticating"), this.performOAuthPkce(`openid profile email roles ${l ?? ""}`.trim());
60
50
  }
61
51
  async performOAuthPkce(e) {
62
- const t = _(), s = await b(t), r = _();
52
+ const t = _(), s = await O(t), r = _();
63
53
  localStorage.setItem(this.storageKeys.CODE_VERIFIER, t), localStorage.setItem(this.storageKeys.STATE, r);
64
54
  const o = new URL(this.authEndpoints.authorize);
65
55
  throw o.searchParams.set("client_id", this.authClientId), o.searchParams.set("response_type", "code"), o.searchParams.set("redirect_uri", this.redirectUri), o.searchParams.set("scope", e), o.searchParams.set("code_challenge", s), o.searchParams.set("code_challenge_method", "S256"), o.searchParams.set("state", r), window.location.href = o.toString(), new Error("Redirect initiated");
@@ -75,14 +65,10 @@ class j extends O {
75
65
  return this.setAuthState(r), r;
76
66
  }
77
67
  async handleAccessRequestCallback(e) {
78
- const t = await this.getAccessRequestStatus(e);
79
- if (!u(t))
80
- throw l("Failed to get access request status", "auth_error");
81
- const { status: s, access_request_scope: r } = t.body;
82
- if (s !== "approved")
83
- throw l(`Access request is not approved: ${s}`, "auth_error");
68
+ const t = await this.getAccessRequestStatus(e), { status: s, access_request_scope: r } = w(t);
69
+ localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID), s !== "approved" && E(s);
84
70
  const o = `openid profile email roles ${r ?? ""}`.trim();
85
- return localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID), this.performOAuthPkce(o);
71
+ return this.performOAuthPkce(o);
86
72
  }
87
73
  async logout() {
88
74
  const e = localStorage.getItem(this.storageKeys.REFRESH_TOKEN);
@@ -160,12 +146,12 @@ class j extends O {
160
146
  return this.redirectUri;
161
147
  }
162
148
  }
163
- const G = 500, Q = 5e3, Y = 3e4;
164
- class J {
149
+ const Q = 500, Y = 5e3, J = 3e4;
150
+ class Z {
165
151
  constructor(e, t, s) {
166
- this.state = I, this.bodhiext = null, this.refreshPromise = null, this.logger = new P("WindowBodhiextClient", t.logLevel), this.authClientId = e, this.config = t, this.authEndpoints = q(this.config.authServerUrl), this.onStateChange = s ?? U;
167
- const r = R(t.basePath, T.WEB_EXT);
168
- this.storageKeys = K(r), this.apiTimeoutMs = t.apiTimeoutMs ?? Y;
152
+ this.state = C, this.bodhiext = null, this.refreshPromise = null, this.logger = new U("WindowBodhiextClient", t.logLevel), this.authClientId = e, this.config = t, this.authEndpoints = q(this.config.authServerUrl), this.onStateChange = s ?? L;
153
+ const r = I(t.basePath, A.WEB_EXT);
154
+ this.storageKeys = N(r), this.apiTimeoutMs = t.apiTimeoutMs ?? J;
169
155
  }
170
156
  /**
171
157
  * Set client state and notify callback
@@ -190,11 +176,11 @@ class J {
190
176
  // ============================================================================
191
177
  /**
192
178
  * Ensure bodhiext is available, attempting to acquire it if not already set
193
- * @throws OperationError if client not initialized
179
+ * @throws BodhiError if client not initialized
194
180
  */
195
181
  ensureBodhiext() {
196
182
  if (!this.bodhiext && window.bodhiext && (this.logger.info("Acquiring window.bodhiext reference"), this.bodhiext = window.bodhiext), !this.bodhiext)
197
- throw l("Client not initialized", "extension_error");
183
+ throw R("not_initialized", "Client not initialized");
198
184
  }
199
185
  /**
200
186
  * Send extension request via window.bodhiext.sendExtRequest
@@ -204,56 +190,47 @@ class J {
204
190
  }
205
191
  /**
206
192
  * Send API message via window.bodhiext.sendApiRequest
207
- * Converts ApiResponse to ApiResponseResult
193
+ * @throws BodhiError on operational errors (extension not ready, auth, network, timeout)
208
194
  */
209
195
  async sendApiRequest(e, t, s, r, o) {
210
- try {
211
- this.ensureBodhiext();
212
- } catch (a) {
213
- return {
214
- error: {
215
- message: a instanceof Error ? a.message : String(a),
216
- type: "extension_error"
217
- }
218
- };
219
- }
196
+ this.ensureBodhiext();
220
197
  try {
221
198
  const a = new Promise(
222
- (c, i) => setTimeout(
199
+ (n, i) => setTimeout(
223
200
  () => i(
224
- new Error(
201
+ new h(
202
+ "timeout_error",
225
203
  `[bodhi-js-sdk/web] network timeout: api request not completed within configured/default timeout of ${this.apiTimeoutMs}ms`
226
204
  )
227
205
  ),
228
206
  this.apiTimeoutMs
229
207
  )
230
- ), n = (async () => {
231
- let c = r || {};
208
+ ), c = (async () => {
209
+ let n = r || {};
232
210
  if (o) {
233
211
  const i = await this._getAccessTokenRaw();
234
212
  if (!i)
235
- return {
236
- error: {
237
- message: "Not authenticated. Please log in first.",
238
- type: "extension_error"
239
- }
240
- };
241
- c = {
242
- ...c,
213
+ throw new h("auth_error", "Not authenticated. Please log in first.");
214
+ n = {
215
+ ...n,
243
216
  Authorization: `Bearer ${i}`
244
217
  };
245
218
  }
246
- return this.bodhiext.sendApiRequest(e, t, s, c);
219
+ return this.bodhiext.sendApiRequest(e, t, s, n);
247
220
  })();
248
- return await Promise.race([n, a]);
221
+ return await Promise.race([c, a]);
249
222
  } catch (a) {
250
- const n = a?.error, c = n?.message ?? (a instanceof Error ? a.message : String(a)), i = n?.type || "network_error";
251
- return {
252
- error: {
253
- message: c,
254
- type: i
255
- }
256
- };
223
+ if (a instanceof m || a instanceof h) throw a;
224
+ if (a instanceof Error) {
225
+ const c = a;
226
+ throw a.name === "BodhiApiError" && typeof c.status == "number" && c.body != null ? new m(
227
+ c.status,
228
+ c.body,
229
+ a.message,
230
+ c.headers
231
+ ) : a.name === "BodhiError" && typeof c.code == "string" ? new h(c.code, a.message) : new h("network_error", a.message);
232
+ }
233
+ throw new h("network_error", String(a));
257
234
  }
258
235
  }
259
236
  /**
@@ -277,26 +254,26 @@ class J {
277
254
  */
278
255
  async init(e = {}) {
279
256
  if (!e.testConnection && !e.selectedConnection)
280
- return this.logger.info("No testConnection or selectedConnection, returning not-initialized state"), I;
257
+ return this.logger.info("No testConnection or selectedConnection, returning not-initialized state"), C;
281
258
  if (this.bodhiext && !e.testConnection)
282
259
  return this.logger.debug("Already have bodhiext handle, skipping polling"), this.state;
283
260
  if (!this.bodhiext) {
284
- const r = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ?? Q, o = e.intervalMs ?? this.config.initParams?.extension?.intervalMs ?? G, a = Date.now();
285
- if (!await new Promise((c) => {
261
+ const r = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ?? Y, o = e.intervalMs ?? this.config.initParams?.extension?.intervalMs ?? Q, a = Date.now();
262
+ if (!await new Promise((n) => {
286
263
  const i = () => {
287
264
  if (window.bodhiext) {
288
- this.bodhiext = window.bodhiext, c(!0);
265
+ this.bodhiext = window.bodhiext, n(!0);
289
266
  return;
290
267
  }
291
268
  if (Date.now() - a >= r) {
292
- c(!1);
269
+ n(!1);
293
270
  return;
294
271
  }
295
272
  setTimeout(i, o);
296
273
  };
297
274
  i();
298
275
  }))
299
- return this.logger.warn("Extension discovery timed out"), this.setState(L), this.state;
276
+ return this.logger.warn("Extension discovery timed out"), this.setState(D), this.state;
300
277
  }
301
278
  const t = await this.bodhiext.getExtensionId();
302
279
  this.logger.info(`Extension discovered: ${t}`);
@@ -304,14 +281,14 @@ class J {
304
281
  type: "extension",
305
282
  extension: "ready",
306
283
  extensionId: t,
307
- server: N
284
+ server: M
308
285
  };
309
286
  if (e.testConnection)
310
287
  try {
311
288
  const r = await this.getServerState();
312
289
  this.setState({ ...s, server: r }), this.logger.info(`Server connectivity tested: ${r.status}`);
313
290
  } catch (r) {
314
- this.logger.error("Failed to get server state:", r), this.setState({ ...s, server: m });
291
+ this.logger.error("Failed to get server state:", r), this.setState({ ...s, server: p });
315
292
  }
316
293
  else
317
294
  this.setState(s);
@@ -332,39 +309,29 @@ class J {
332
309
  this.ensureBodhiext();
333
310
  const s = e?.userRole ?? this.config.userRole, r = e?.flowType ?? "popup";
334
311
  e?.onProgress?.("requesting");
335
- const o = new C(this.authClientId).requestedRole(s).flowType(r);
312
+ const o = new k(this.authClientId).requestedRole(s).flowType(r);
336
313
  if (e?.requested && o.requested(e.requested), r === "redirect") {
337
- const g = e?.redirectUrl ?? this.config.redirectUri;
338
- o.redirectUrl(g);
314
+ const f = e?.redirectUrl ?? this.config.redirectUri;
315
+ o.redirectUrl(f);
339
316
  }
340
- const a = o.build(), n = await this.requestAccess(a);
341
- if (y(n))
342
- throw l(n.error.message, n.error.type);
343
- if (!u(n))
344
- throw l(
345
- `Access request failed: HTTP ${n.status}`,
346
- "auth_error"
347
- );
348
- const { id: c, review_url: i } = n.body;
317
+ const a = o.build(), c = await this.requestAccess(a), { id: n, review_url: i } = w(c);
349
318
  e?.onProgress?.("reviewing");
350
- let h;
319
+ let l;
351
320
  if (r === "popup") {
352
- const p = await v(i, async () => {
353
- const S = await this.getAccessRequestStatus(c);
354
- if (!u(S)) return null;
355
- const { status: f, access_request_scope: E } = S.body;
356
- return f === "approved" ? { approved: !0, accessRequestScope: E ?? void 0 } : ["denied", "failed", "expired"].includes(f) ? { approved: !1 } : null;
321
+ const u = await b(i, async () => {
322
+ const S = await this.getAccessRequestStatus(n);
323
+ if (S.status >= 400) return null;
324
+ const { status: d, access_request_scope: y } = S.body;
325
+ return d === "approved" ? { approved: !0, accessRequestScope: y ?? void 0 } : ["denied", "failed", "expired"].includes(d) ? { approved: !1, status: d } : null;
357
326
  }, {
358
- intervalMs: e?.pollIntervalMs ?? x,
359
- timeoutMs: e?.pollTimeoutMs ?? k
327
+ intervalMs: e?.pollIntervalMs ?? P,
328
+ timeoutMs: e?.pollTimeoutMs ?? x
360
329
  });
361
- if (!p.approved)
362
- throw l("Access request was denied or expired", "auth_error");
363
- h = p.accessRequestScope;
330
+ u.approved || E(u.status ?? "unknown"), l = u.accessRequestScope;
364
331
  } else
365
- return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID, c), window.location.href = i, new Promise(() => {
332
+ return localStorage.setItem(this.storageKeys.ACCESS_REQUEST_ID, n), window.location.href = i, new Promise(() => {
366
333
  });
367
- return e?.onProgress?.("authenticating"), this.performOAuthPkce(`openid profile email roles ${h ?? ""}`.trim());
334
+ return e?.onProgress?.("authenticating"), this.performOAuthPkce(`openid profile email roles ${l ?? ""}`.trim());
368
335
  }
369
336
  /**
370
337
  * Handle OAuth callback with authorization code
@@ -453,7 +420,7 @@ class J {
453
420
  if (!e)
454
421
  return { status: "unauthenticated", user: null, accessToken: null, error: null };
455
422
  try {
456
- return { status: "authenticated", user: A(e), accessToken: e, error: null };
423
+ return { status: "authenticated", user: v(e), accessToken: e, error: null };
457
424
  } catch (t) {
458
425
  return this.logger.error("Failed to parse token:", t), { status: "unauthenticated", user: null, accessToken: null, error: null };
459
426
  }
@@ -495,14 +462,14 @@ class J {
495
462
  async _doRefreshToken(e) {
496
463
  this.logger.debug("Refreshing access token");
497
464
  try {
498
- const t = await D(
465
+ const t = await F(
499
466
  this.authEndpoints.token,
500
467
  e,
501
468
  this.authClientId
502
469
  );
503
470
  if (t.success) {
504
471
  this._storeRefreshedTokens(t.tokens);
505
- const s = A(t.tokens.access_token);
472
+ const s = v(t.tokens.access_token);
506
473
  return this.setAuthState({
507
474
  status: "authenticated",
508
475
  user: s,
@@ -520,9 +487,9 @@ class J {
520
487
  } catch (t) {
521
488
  this.logger.warn("Token refresh failed:", t);
522
489
  }
523
- throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"), l(
524
- "Access token expired and unable to refresh. Try logging out and logging in again.",
525
- "token_refresh_failed"
490
+ throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"), R(
491
+ "auth_error",
492
+ "Access token expired and unable to refresh. Try logging out and logging in again."
526
493
  );
527
494
  }
528
495
  clearAuthStorage() {
@@ -546,49 +513,49 @@ class J {
546
513
  * Calls /bodhi/v1/info and returns structured server state
547
514
  */
548
515
  async getServerState() {
549
- const e = await this.sendApiRequest("GET", "/bodhi/v1/info");
550
- if (y(e))
551
- return m;
552
- if (!u(e))
553
- return m;
554
- const t = e.body, s = t.version || "unknown";
555
- switch (t.status) {
556
- case "ready":
557
- return {
558
- status: "ready",
559
- version: s,
560
- error: null,
561
- deployment: t.deployment ?? null,
562
- client_id: t.client_id ?? null
563
- };
564
- case "setup":
565
- return w("setup", s, void 0, t.deployment, t.client_id);
566
- case "resource_admin":
567
- return w(
568
- "resource_admin",
569
- s,
570
- void 0,
571
- t.deployment,
572
- t.client_id
573
- );
574
- case "tenant_selection":
575
- return {
576
- status: "tenant_selection",
577
- version: s,
578
- error: null,
579
- deployment: t.deployment ?? null,
580
- client_id: t.client_id ?? null
581
- };
582
- case "error":
583
- return w(
584
- "error",
585
- s,
586
- t.error ? { message: t.error.message, type: t.error.type } : M.SERVER_NOT_READY,
587
- t.deployment,
588
- t.client_id
589
- );
590
- default:
591
- return m;
516
+ try {
517
+ const e = await this.sendApiRequest("GET", "/bodhi/v1/info");
518
+ if (e.status >= 400)
519
+ return p;
520
+ const t = e.body, s = t.version || "unknown";
521
+ switch (t.status) {
522
+ case "ready":
523
+ return {
524
+ status: "ready",
525
+ version: s,
526
+ error: null,
527
+ deployment: t.deployment ?? null,
528
+ client_id: t.client_id ?? null
529
+ };
530
+ case "setup":
531
+ return T(
532
+ "setup",
533
+ s,
534
+ void 0,
535
+ t.deployment,
536
+ t.client_id
537
+ );
538
+ case "resource_admin":
539
+ return T(
540
+ "resource_admin",
541
+ s,
542
+ void 0,
543
+ t.deployment,
544
+ t.client_id
545
+ );
546
+ case "error":
547
+ return T(
548
+ "error",
549
+ s,
550
+ t.error ? { message: t.error.message, type: t.error.type } : B.SERVER_NOT_READY,
551
+ t.deployment,
552
+ t.client_id
553
+ );
554
+ default:
555
+ return p;
556
+ }
557
+ } catch {
558
+ return p;
592
559
  }
593
560
  }
594
561
  /**
@@ -601,50 +568,53 @@ class J {
601
568
  if (o) {
602
569
  const i = await this._getAccessTokenRaw();
603
570
  if (!i)
604
- throw l("Not authenticated. Please log in first.", "auth_error");
571
+ throw R("auth_error", "Not authenticated. Please log in first.");
605
572
  a = {
606
573
  ...a,
607
574
  Authorization: `Bearer ${i}`
608
575
  };
609
576
  }
610
- const c = this.bodhiext.sendStreamRequest(e, t, s, a).getReader();
577
+ const n = this.bodhiext.sendStreamRequest(e, t, s, a).getReader();
611
578
  try {
612
579
  for (; ; ) {
613
- const { value: i, done: h } = await c.read();
614
- if (h || i?.done)
580
+ const { value: i, done: l } = await n.read();
581
+ if (l || i?.done)
615
582
  break;
616
583
  yield i.body;
617
584
  }
618
585
  } catch (i) {
586
+ if (i instanceof m || i instanceof h) throw i;
619
587
  if (i instanceof Error) {
620
- if ("response" in i) {
621
- const h = i;
622
- throw F(i.message, h.response.status, h.response.body);
623
- }
624
- throw "error" in i ? l(i.message, "extension_error") : l(i.message, "extension_error");
588
+ const l = i;
589
+ throw i.name === "BodhiApiError" && typeof l.status == "number" && l.body != null ? new m(
590
+ l.status,
591
+ l.body,
592
+ i.message,
593
+ l.headers
594
+ ) : i.name === "BodhiError" && typeof l.code == "string" ? new h(l.code, i.message) : new h("extension_error", i.message);
625
595
  }
626
596
  throw i;
627
597
  } finally {
628
- c.releaseLock();
598
+ n.releaseLock();
629
599
  }
630
600
  }
631
601
  // ============================================================================
632
602
  // OpenAI-Compatible Namespaced API
633
603
  // ============================================================================
634
604
  get chat() {
635
- return this._chat ??= new B(this);
605
+ return this._chat ??= new $(this);
636
606
  }
637
607
  get models() {
638
- return this._models ??= new $(this);
608
+ return this._models ??= new V(this);
639
609
  }
640
610
  get embeddings() {
641
- return this._embeddings ??= new V(this);
611
+ return this._embeddings ??= new z(this);
642
612
  }
643
613
  get toolsets() {
644
614
  return this._toolsets ??= new H(this);
645
615
  }
646
616
  get mcps() {
647
- return this._mcps ??= new z(this);
617
+ return this._mcps ??= new W(this);
648
618
  }
649
619
  // ============================================================================
650
620
  // Access Request Methods
@@ -668,14 +638,14 @@ class J {
668
638
  );
669
639
  }
670
640
  async pollAccessRequestStatus(e, t) {
671
- return W(
641
+ return X(
672
642
  (s) => this.getAccessRequestStatus(s),
673
643
  e,
674
644
  t
675
645
  );
676
646
  }
677
647
  async performOAuthPkce(e) {
678
- const t = _(), s = await b(t), r = _();
648
+ const t = _(), s = await O(t), r = _();
679
649
  localStorage.setItem(this.storageKeys.CODE_VERIFIER, t), localStorage.setItem(this.storageKeys.STATE, r);
680
650
  const o = e.split(" ").filter(Boolean), a = new URLSearchParams({
681
651
  response_type: "code",
@@ -690,14 +660,10 @@ class J {
690
660
  });
691
661
  }
692
662
  async handleAccessRequestCallback(e) {
693
- const t = await this.getAccessRequestStatus(e);
694
- if (!u(t))
695
- throw l("Failed to get access request status", "auth_error");
696
- const { status: s, access_request_scope: r } = t.body;
697
- if (s !== "approved")
698
- throw l(`Access request is not approved: ${s}`, "auth_error");
663
+ const t = await this.getAccessRequestStatus(e), { status: s, access_request_scope: r } = w(t);
664
+ localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID), s !== "approved" && E(s);
699
665
  const o = `openid profile email roles ${r ?? ""}`.trim();
700
- return localStorage.removeItem(this.storageKeys.ACCESS_REQUEST_ID), this.performOAuthPkce(o);
666
+ return this.performOAuthPkce(o);
701
667
  }
702
668
  /**
703
669
  * Serialize web extension client state (all transient, nothing to persist)
@@ -723,17 +689,17 @@ class J {
723
689
  };
724
690
  }
725
691
  }
726
- function Z(d) {
692
+ function ee(g) {
727
693
  if (typeof window > "u")
728
694
  throw new Error("redirectUri required in non-browser environment");
729
- const e = d === "/" ? "" : d.replace(/\/$/, "");
695
+ const e = g === "/" ? "" : g.replace(/\/$/, "");
730
696
  return `${window.location.origin}${e}/callback`;
731
697
  }
732
- class te extends X {
698
+ class se extends G {
733
699
  constructor(e, t, s) {
734
700
  const r = t || {}, o = {
735
701
  basePath: r.basePath || "/",
736
- redirectUri: r.redirectUri || Z(r.basePath || "/"),
702
+ redirectUri: r.redirectUri || ee(r.basePath || "/"),
737
703
  authServerUrl: r.authServerUrl || "https://id.getbodhi.app/realms/bodhi",
738
704
  userRole: r.userRole || "scope_user_user",
739
705
  logLevel: r.logLevel || "warn",
@@ -743,13 +709,13 @@ class te extends X {
743
709
  super(e, o, s);
744
710
  }
745
711
  createLogger(e) {
746
- return new P("WebUIClient", e.logLevel);
712
+ return new U("WebUIClient", e.logLevel);
747
713
  }
748
714
  createStoragePrefix(e) {
749
- return R(e.basePath, T.WEB);
715
+ return I(e.basePath, A.WEB);
750
716
  }
751
717
  createExtClient(e, t) {
752
- return new J(
718
+ return new Z(
753
719
  this.authClientId,
754
720
  {
755
721
  authServerUrl: e.authServerUrl,
@@ -787,8 +753,8 @@ class te extends X {
787
753
  return this.connectionMode === "direct" ? this.directClient.handleAccessRequestCallback(e) : this.extClient.handleAccessRequestCallback(e);
788
754
  }
789
755
  }
790
- const se = "production";
756
+ const re = "production";
791
757
  export {
792
- se as WEB_BUILD_MODE,
793
- te as WebUIClient
758
+ re as WEB_BUILD_MODE,
759
+ se as WebUIClient
794
760
  };
@@ -1,5 +1,6 @@
1
- import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, UserScope } from '@bodhiapp/ts-client';
2
- import { Chat, Models, Embeddings, Toolsets, Mcps, ApiResponseResult, AuthState, BackendServerState, ClientState, ExtensionState, IExtensionClient, InitParams, LoginOptions, LogLevel, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
1
+ import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, PingResponse, UserScope } from '@bodhiapp/ts-client';
2
+ import { Chat, Models, Embeddings, Toolsets, Mcps, AuthState, BackendServerState, ClientState, ExtensionState, IExtensionClient, InitParams, LoginOptions, LogLevel, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
3
+ import { ApiResponse } from '@bodhiapp/bodhi-browser-types';
3
4
  export type SerializedWebExtensionState = {
4
5
  extensionId?: string;
5
6
  };
@@ -61,7 +62,7 @@ export declare class WindowBodhiextClient implements IExtensionClient {
61
62
  setStateCallback(callback: StateChangeCallback): void;
62
63
  /**
63
64
  * Ensure bodhiext is available, attempting to acquire it if not already set
64
- * @throws OperationError if client not initialized
65
+ * @throws BodhiError if client not initialized
65
66
  */
66
67
  private ensureBodhiext;
67
68
  /**
@@ -70,9 +71,9 @@ export declare class WindowBodhiextClient implements IExtensionClient {
70
71
  sendExtRequest<TParams = void, TRes = unknown>(action: string, params?: TParams): Promise<TRes>;
71
72
  /**
72
73
  * Send API message via window.bodhiext.sendApiRequest
73
- * Converts ApiResponse to ApiResponseResult
74
+ * @throws BodhiError on operational errors (extension not ready, auth, network, timeout)
74
75
  */
75
- sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<ApiResponseResult<TRes>>;
76
+ sendApiRequest<TReq = void, TRes = unknown>(method: string, endpoint: string, body?: TReq, headers?: Record<string, string>, authenticated?: boolean): Promise<ApiResponse<TRes>>;
76
77
  /**
77
78
  * Get current client state
78
79
  */
@@ -134,9 +135,7 @@ export declare class WindowBodhiextClient implements IExtensionClient {
134
135
  /**
135
136
  * Ping API
136
137
  */
137
- pingApi(): Promise<ApiResponseResult<{
138
- message: string;
139
- }>>;
138
+ pingApi(): Promise<ApiResponse<PingResponse>>;
140
139
  /**
141
140
  * Get backend server state
142
141
  * Calls /bodhi/v1/info and returns structured server state
@@ -152,8 +151,8 @@ export declare class WindowBodhiextClient implements IExtensionClient {
152
151
  get embeddings(): Embeddings;
153
152
  get toolsets(): Toolsets;
154
153
  get mcps(): Mcps;
155
- requestAccess(body: CreateAccessRequest): Promise<ApiResponseResult<CreateAccessRequestResponse>>;
156
- getAccessRequestStatus(requestId: string): Promise<ApiResponseResult<AccessRequestStatusResponse>>;
154
+ requestAccess(body: CreateAccessRequest): Promise<ApiResponse<CreateAccessRequestResponse>>;
155
+ getAccessRequestStatus(requestId: string): Promise<ApiResponse<AccessRequestStatusResponse>>;
157
156
  pollAccessRequestStatus(requestId: string, options?: {
158
157
  intervalMs?: number;
159
158
  timeoutMs?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bodhiapp/bodhi-js",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "Web SDK for Bodhi Browser - window.bodhiext communication",
5
5
  "type": "module",
6
6
  "main": "dist/bodhi-web.cjs.js",
@@ -37,9 +37,9 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "dependencies": {
40
- "@bodhiapp/bodhi-browser-types": "0.0.27",
41
- "@bodhiapp/bodhi-js-core": "0.0.27",
42
- "@bodhiapp/ts-client": "0.1.20"
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",