@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.
- package/dist/bodhi-web.cjs.js +1 -1
- package/dist/bodhi-web.esm.js +157 -191
- package/dist/ext-client.d.ts +9 -10
- package/package.json +4 -4
package/dist/bodhi-web.cjs.js
CHANGED
|
@@ -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;
|
package/dist/bodhi-web.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DirectClientBase as
|
|
2
|
-
class j extends
|
|
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 =
|
|
4
|
+
const s = I(
|
|
5
5
|
e.basePath,
|
|
6
|
-
|
|
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
|
|
27
|
+
const o = new k(this.authClientId).requestedRole(s).flowType(r);
|
|
28
28
|
if (e?.requested && o.requested(e.requested), r === "redirect") {
|
|
29
|
-
const
|
|
30
|
-
o.redirectUrl(
|
|
29
|
+
const f = e?.redirectUrl ?? this.redirectUri;
|
|
30
|
+
o.redirectUrl(f);
|
|
31
31
|
}
|
|
32
|
-
const a = o.build(),
|
|
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
|
|
34
|
+
let l;
|
|
43
35
|
if (r === "popup") {
|
|
44
|
-
const
|
|
45
|
-
const S = await this.getAccessRequestStatus(
|
|
46
|
-
if (
|
|
47
|
-
const { status:
|
|
48
|
-
return
|
|
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 ??
|
|
51
|
-
timeoutMs: e?.pollTimeoutMs ??
|
|
42
|
+
intervalMs: e?.pollIntervalMs ?? P,
|
|
43
|
+
timeoutMs: e?.pollTimeoutMs ?? x
|
|
52
44
|
});
|
|
53
|
-
|
|
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,
|
|
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 ${
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
164
|
-
class
|
|
149
|
+
const Q = 500, Y = 5e3, J = 3e4;
|
|
150
|
+
class Z {
|
|
165
151
|
constructor(e, t, s) {
|
|
166
|
-
this.state =
|
|
167
|
-
const r =
|
|
168
|
-
this.storageKeys =
|
|
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
|
|
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
|
|
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
|
-
*
|
|
193
|
+
* @throws BodhiError on operational errors (extension not ready, auth, network, timeout)
|
|
208
194
|
*/
|
|
209
195
|
async sendApiRequest(e, t, s, r, o) {
|
|
210
|
-
|
|
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
|
-
(
|
|
199
|
+
(n, i) => setTimeout(
|
|
223
200
|
() => i(
|
|
224
|
-
new
|
|
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
|
-
),
|
|
231
|
-
let
|
|
208
|
+
), c = (async () => {
|
|
209
|
+
let n = r || {};
|
|
232
210
|
if (o) {
|
|
233
211
|
const i = await this._getAccessTokenRaw();
|
|
234
212
|
if (!i)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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,
|
|
219
|
+
return this.bodhiext.sendApiRequest(e, t, s, n);
|
|
247
220
|
})();
|
|
248
|
-
return await Promise.race([
|
|
221
|
+
return await Promise.race([c, a]);
|
|
249
222
|
} catch (a) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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"),
|
|
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 ??
|
|
285
|
-
if (!await new Promise((
|
|
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,
|
|
265
|
+
this.bodhiext = window.bodhiext, n(!0);
|
|
289
266
|
return;
|
|
290
267
|
}
|
|
291
268
|
if (Date.now() - a >= r) {
|
|
292
|
-
|
|
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(
|
|
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:
|
|
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:
|
|
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
|
|
312
|
+
const o = new k(this.authClientId).requestedRole(s).flowType(r);
|
|
336
313
|
if (e?.requested && o.requested(e.requested), r === "redirect") {
|
|
337
|
-
const
|
|
338
|
-
o.redirectUrl(
|
|
314
|
+
const f = e?.redirectUrl ?? this.config.redirectUri;
|
|
315
|
+
o.redirectUrl(f);
|
|
339
316
|
}
|
|
340
|
-
const a = o.build(),
|
|
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
|
|
319
|
+
let l;
|
|
351
320
|
if (r === "popup") {
|
|
352
|
-
const
|
|
353
|
-
const S = await this.getAccessRequestStatus(
|
|
354
|
-
if (
|
|
355
|
-
const { status:
|
|
356
|
-
return
|
|
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 ??
|
|
359
|
-
timeoutMs: e?.pollTimeoutMs ??
|
|
327
|
+
intervalMs: e?.pollIntervalMs ?? P,
|
|
328
|
+
timeoutMs: e?.pollTimeoutMs ?? x
|
|
360
329
|
});
|
|
361
|
-
|
|
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,
|
|
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 ${
|
|
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:
|
|
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
|
|
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 =
|
|
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"),
|
|
524
|
-
"
|
|
525
|
-
"
|
|
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
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
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
|
|
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
|
|
577
|
+
const n = this.bodhiext.sendStreamRequest(e, t, s, a).getReader();
|
|
611
578
|
try {
|
|
612
579
|
for (; ; ) {
|
|
613
|
-
const { value: i, done:
|
|
614
|
-
if (
|
|
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
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
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
|
-
|
|
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
|
|
605
|
+
return this._chat ??= new $(this);
|
|
636
606
|
}
|
|
637
607
|
get models() {
|
|
638
|
-
return this._models ??= new
|
|
608
|
+
return this._models ??= new V(this);
|
|
639
609
|
}
|
|
640
610
|
get embeddings() {
|
|
641
|
-
return this._embeddings ??= new
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
692
|
+
function ee(g) {
|
|
727
693
|
if (typeof window > "u")
|
|
728
694
|
throw new Error("redirectUri required in non-browser environment");
|
|
729
|
-
const e =
|
|
695
|
+
const e = g === "/" ? "" : g.replace(/\/$/, "");
|
|
730
696
|
return `${window.location.origin}${e}/callback`;
|
|
731
697
|
}
|
|
732
|
-
class
|
|
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 ||
|
|
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
|
|
712
|
+
return new U("WebUIClient", e.logLevel);
|
|
747
713
|
}
|
|
748
714
|
createStoragePrefix(e) {
|
|
749
|
-
return
|
|
715
|
+
return I(e.basePath, A.WEB);
|
|
750
716
|
}
|
|
751
717
|
createExtClient(e, t) {
|
|
752
|
-
return new
|
|
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
|
|
756
|
+
const re = "production";
|
|
791
757
|
export {
|
|
792
|
-
|
|
793
|
-
|
|
758
|
+
re as WEB_BUILD_MODE,
|
|
759
|
+
se as WebUIClient
|
|
794
760
|
};
|
package/dist/ext-client.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, UserScope } from '@bodhiapp/ts-client';
|
|
2
|
-
import { Chat, Models, Embeddings, Toolsets, Mcps,
|
|
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
|
|
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
|
-
*
|
|
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<
|
|
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<
|
|
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<
|
|
156
|
-
getAccessRequestStatus(requestId: string): Promise<
|
|
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.
|
|
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.
|
|
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",
|