@bodhiapp/bodhi-js 0.0.29 → 0.0.30
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 +25 -33
- package/dist/direct-client.d.ts +0 -2
- package/dist/ext-client.d.ts +2 -5
- package/dist/facade-client.d.ts +1 -3
- 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 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
|
+
"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,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??"scope_user_user",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(),h=await this.requestAccess(n),{id:c,review_url:a}=r.unwrapResponse(h);e?.onProgress?.("reviewing");let l;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"),l=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 ${l??""}`.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 m=500,w=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)),h=(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([h,n])}catch(n){if(n instanceof r.BodhiApiError||n instanceof r.BodhiError)throw n;if(n instanceof Error){const h=n;throw n.name==="BodhiApiError"&&typeof h.status=="number"&&h.body!=null?new r.BodhiApiError(h.status,h.body,n.message,h.headers):n.name==="BodhiError"&&typeof h.code=="string"?new r.BodhiError(h.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??w,i=e.intervalMs??this.config.initParams?.extension?.intervalMs??m,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??"scope_user_user",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(),h=await this.requestAccess(n),{id:c,review_url:a}=r.unwrapResponse(h);e?.onProgress?.("reviewing");let l;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"),l=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 ${l??""}`.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:l}=await c.read();if(l||a?.done)break;yield a.body}}catch(a){if(a instanceof r.BodhiApiError||a instanceof r.BodhiError)throw a;if(a instanceof Error){const l=a;throw a.name==="BodhiApiError"&&typeof l.status=="number"&&l.body!=null?new r.BodhiApiError(l.status,l.body,a.message,l.headers):a.name==="BodhiError"&&typeof l.code=="string"?new r.BodhiError(l.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 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}}}function y(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 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",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,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,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;
|
package/dist/bodhi-web.esm.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
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
|
|
2
|
-
class
|
|
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 T, 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 R, SERVER_ERROR_CODES as B, Chat as $, Models as V, Embeddings as z, Mcps as H, pollAccessRequestUntilResolved as W, BaseFacadeClient as X } from "@bodhiapp/bodhi-js-core";
|
|
2
|
+
class G extends K {
|
|
3
3
|
constructor(e, t) {
|
|
4
4
|
const s = I(
|
|
5
5
|
e.basePath,
|
|
@@ -7,7 +7,6 @@ class j extends K {
|
|
|
7
7
|
), r = {
|
|
8
8
|
authClientId: e.authClientId,
|
|
9
9
|
authServerUrl: e.authServerUrl,
|
|
10
|
-
userRole: e.userRole,
|
|
11
10
|
storagePrefix: s,
|
|
12
11
|
logLevel: e.logLevel,
|
|
13
12
|
loggerPrefix: "DirectWebClient",
|
|
@@ -22,7 +21,7 @@ class j extends K {
|
|
|
22
21
|
const t = await this.getAuthState();
|
|
23
22
|
if (t.status === "authenticated")
|
|
24
23
|
return t;
|
|
25
|
-
const s = e?.userRole ??
|
|
24
|
+
const s = e?.userRole ?? "scope_user_user", r = e?.flowType ?? "popup";
|
|
26
25
|
e?.onProgress?.("requesting");
|
|
27
26
|
const o = new k(this.authClientId).requestedRole(s).flowType(r);
|
|
28
27
|
if (e?.requested && o.requested(e.requested), r === "redirect") {
|
|
@@ -146,12 +145,12 @@ class j extends K {
|
|
|
146
145
|
return this.redirectUri;
|
|
147
146
|
}
|
|
148
147
|
}
|
|
149
|
-
const
|
|
150
|
-
class
|
|
148
|
+
const j = 500, Q = 5e3, Y = 3e4;
|
|
149
|
+
class J {
|
|
151
150
|
constructor(e, t, s) {
|
|
152
151
|
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
152
|
const r = I(t.basePath, A.WEB_EXT);
|
|
154
|
-
this.storageKeys = N(r), this.apiTimeoutMs = t.apiTimeoutMs ??
|
|
153
|
+
this.storageKeys = N(r), this.apiTimeoutMs = t.apiTimeoutMs ?? Y;
|
|
155
154
|
}
|
|
156
155
|
/**
|
|
157
156
|
* Set client state and notify callback
|
|
@@ -180,7 +179,7 @@ class Z {
|
|
|
180
179
|
*/
|
|
181
180
|
ensureBodhiext() {
|
|
182
181
|
if (!this.bodhiext && window.bodhiext && (this.logger.info("Acquiring window.bodhiext reference"), this.bodhiext = window.bodhiext), !this.bodhiext)
|
|
183
|
-
throw
|
|
182
|
+
throw T("not_initialized", "Client not initialized");
|
|
184
183
|
}
|
|
185
184
|
/**
|
|
186
185
|
* Send extension request via window.bodhiext.sendExtRequest
|
|
@@ -258,7 +257,7 @@ class Z {
|
|
|
258
257
|
if (this.bodhiext && !e.testConnection)
|
|
259
258
|
return this.logger.debug("Already have bodhiext handle, skipping polling"), this.state;
|
|
260
259
|
if (!this.bodhiext) {
|
|
261
|
-
const r = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ??
|
|
260
|
+
const r = e.timeoutMs ?? this.config.initParams?.extension?.timeoutMs ?? Q, o = e.intervalMs ?? this.config.initParams?.extension?.intervalMs ?? j, a = Date.now();
|
|
262
261
|
if (!await new Promise((n) => {
|
|
263
262
|
const i = () => {
|
|
264
263
|
if (window.bodhiext) {
|
|
@@ -307,7 +306,7 @@ class Z {
|
|
|
307
306
|
if (t.status === "authenticated")
|
|
308
307
|
return t;
|
|
309
308
|
this.ensureBodhiext();
|
|
310
|
-
const s = e?.userRole ??
|
|
309
|
+
const s = e?.userRole ?? "scope_user_user", r = e?.flowType ?? "popup";
|
|
311
310
|
e?.onProgress?.("requesting");
|
|
312
311
|
const o = new k(this.authClientId).requestedRole(s).flowType(r);
|
|
313
312
|
if (e?.requested && o.requested(e.requested), r === "redirect") {
|
|
@@ -487,7 +486,7 @@ class Z {
|
|
|
487
486
|
} catch (t) {
|
|
488
487
|
this.logger.warn("Token refresh failed:", t);
|
|
489
488
|
}
|
|
490
|
-
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"),
|
|
489
|
+
throw this.logger.warn("Token refresh failed, keeping tokens for manual retry"), T(
|
|
491
490
|
"auth_error",
|
|
492
491
|
"Access token expired and unable to refresh. Try logging out and logging in again."
|
|
493
492
|
);
|
|
@@ -528,7 +527,7 @@ class Z {
|
|
|
528
527
|
client_id: t.client_id ?? null
|
|
529
528
|
};
|
|
530
529
|
case "setup":
|
|
531
|
-
return
|
|
530
|
+
return R(
|
|
532
531
|
"setup",
|
|
533
532
|
s,
|
|
534
533
|
void 0,
|
|
@@ -536,7 +535,7 @@ class Z {
|
|
|
536
535
|
t.client_id
|
|
537
536
|
);
|
|
538
537
|
case "resource_admin":
|
|
539
|
-
return
|
|
538
|
+
return R(
|
|
540
539
|
"resource_admin",
|
|
541
540
|
s,
|
|
542
541
|
void 0,
|
|
@@ -544,7 +543,7 @@ class Z {
|
|
|
544
543
|
t.client_id
|
|
545
544
|
);
|
|
546
545
|
case "error":
|
|
547
|
-
return
|
|
546
|
+
return R(
|
|
548
547
|
"error",
|
|
549
548
|
s,
|
|
550
549
|
t.error ? { message: t.error.message, type: t.error.type } : B.SERVER_NOT_READY,
|
|
@@ -568,7 +567,7 @@ class Z {
|
|
|
568
567
|
if (o) {
|
|
569
568
|
const i = await this._getAccessTokenRaw();
|
|
570
569
|
if (!i)
|
|
571
|
-
throw
|
|
570
|
+
throw T("auth_error", "Not authenticated. Please log in first.");
|
|
572
571
|
a = {
|
|
573
572
|
...a,
|
|
574
573
|
Authorization: `Bearer ${i}`
|
|
@@ -610,11 +609,8 @@ class Z {
|
|
|
610
609
|
get embeddings() {
|
|
611
610
|
return this._embeddings ??= new z(this);
|
|
612
611
|
}
|
|
613
|
-
get toolsets() {
|
|
614
|
-
return this._toolsets ??= new H(this);
|
|
615
|
-
}
|
|
616
612
|
get mcps() {
|
|
617
|
-
return this._mcps ??= new
|
|
613
|
+
return this._mcps ??= new H(this);
|
|
618
614
|
}
|
|
619
615
|
// ============================================================================
|
|
620
616
|
// Access Request Methods
|
|
@@ -638,7 +634,7 @@ class Z {
|
|
|
638
634
|
);
|
|
639
635
|
}
|
|
640
636
|
async pollAccessRequestStatus(e, t) {
|
|
641
|
-
return
|
|
637
|
+
return W(
|
|
642
638
|
(s) => this.getAccessRequestStatus(s),
|
|
643
639
|
e,
|
|
644
640
|
t
|
|
@@ -684,24 +680,22 @@ class Z {
|
|
|
684
680
|
bodhiextAvailable: this.bodhiext !== null,
|
|
685
681
|
authClientId: this.authClientId,
|
|
686
682
|
authServerUrl: this.config.authServerUrl,
|
|
687
|
-
redirectUri: this.config.redirectUri
|
|
688
|
-
userRole: this.config.userRole
|
|
683
|
+
redirectUri: this.config.redirectUri
|
|
689
684
|
};
|
|
690
685
|
}
|
|
691
686
|
}
|
|
692
|
-
function
|
|
687
|
+
function Z(g) {
|
|
693
688
|
if (typeof window > "u")
|
|
694
689
|
throw new Error("redirectUri required in non-browser environment");
|
|
695
690
|
const e = g === "/" ? "" : g.replace(/\/$/, "");
|
|
696
691
|
return `${window.location.origin}${e}/callback`;
|
|
697
692
|
}
|
|
698
|
-
class
|
|
693
|
+
class te extends X {
|
|
699
694
|
constructor(e, t, s) {
|
|
700
695
|
const r = t || {}, o = {
|
|
701
696
|
basePath: r.basePath || "/",
|
|
702
|
-
redirectUri: r.redirectUri ||
|
|
697
|
+
redirectUri: r.redirectUri || Z(r.basePath || "/"),
|
|
703
698
|
authServerUrl: r.authServerUrl || "https://id.getbodhi.app/realms/bodhi",
|
|
704
|
-
userRole: r.userRole || "scope_user_user",
|
|
705
699
|
logLevel: r.logLevel || "warn",
|
|
706
700
|
apiTimeoutMs: r.apiTimeoutMs,
|
|
707
701
|
initParams: r.initParams
|
|
@@ -715,12 +709,11 @@ class se extends G {
|
|
|
715
709
|
return I(e.basePath, A.WEB);
|
|
716
710
|
}
|
|
717
711
|
createExtClient(e, t) {
|
|
718
|
-
return new
|
|
712
|
+
return new J(
|
|
719
713
|
this.authClientId,
|
|
720
714
|
{
|
|
721
715
|
authServerUrl: e.authServerUrl,
|
|
722
716
|
redirectUri: e.redirectUri,
|
|
723
|
-
userRole: e.userRole,
|
|
724
717
|
basePath: e.basePath,
|
|
725
718
|
logLevel: e.logLevel,
|
|
726
719
|
apiTimeoutMs: e.apiTimeoutMs,
|
|
@@ -730,12 +723,11 @@ class se extends G {
|
|
|
730
723
|
);
|
|
731
724
|
}
|
|
732
725
|
createDirectClient(e, t, s) {
|
|
733
|
-
return new
|
|
726
|
+
return new G(
|
|
734
727
|
{
|
|
735
728
|
authClientId: e,
|
|
736
729
|
authServerUrl: t.authServerUrl,
|
|
737
730
|
redirectUri: t.redirectUri,
|
|
738
|
-
userRole: t.userRole,
|
|
739
731
|
logLevel: t.logLevel,
|
|
740
732
|
basePath: t.basePath,
|
|
741
733
|
apiTimeoutMs: t.apiTimeoutMs
|
|
@@ -753,8 +745,8 @@ class se extends G {
|
|
|
753
745
|
return this.connectionMode === "direct" ? this.directClient.handleAccessRequestCallback(e) : this.extClient.handleAccessRequestCallback(e);
|
|
754
746
|
}
|
|
755
747
|
}
|
|
756
|
-
const
|
|
748
|
+
const se = "production";
|
|
757
749
|
export {
|
|
758
|
-
|
|
759
|
-
|
|
750
|
+
se as WEB_BUILD_MODE,
|
|
751
|
+
te as WebUIClient
|
|
760
752
|
};
|
package/dist/direct-client.d.ts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { DirectClientBase, AuthState, LoginOptions, LogLevel, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
|
|
2
|
-
import { UserScope } from '@bodhiapp/ts-client';
|
|
3
2
|
/**
|
|
4
3
|
* Configuration for DirectWebClient
|
|
5
4
|
*/
|
|
6
5
|
export interface DirectWebClientConfig {
|
|
7
6
|
authClientId: string;
|
|
8
7
|
authServerUrl: string;
|
|
9
|
-
userRole: UserScope;
|
|
10
8
|
basePath: string;
|
|
11
9
|
logLevel: LogLevel;
|
|
12
10
|
redirectUri: string;
|
package/dist/ext-client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, PingResponse
|
|
2
|
-
import { Chat, Models, Embeddings,
|
|
1
|
+
import { AccessRequestStatusResponse, CreateAccessRequest, CreateAccessRequestResponse, PingResponse } from '@bodhiapp/ts-client';
|
|
2
|
+
import { Chat, Models, Embeddings, Mcps, AuthState, BackendServerState, ClientState, ExtensionState, IExtensionClient, InitParams, LoginOptions, LogLevel, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
|
|
3
3
|
import { ApiResponse } from '@bodhiapp/bodhi-browser-types';
|
|
4
4
|
export type SerializedWebExtensionState = {
|
|
5
5
|
extensionId?: string;
|
|
@@ -11,7 +11,6 @@ export type SerializedWebExtensionState = {
|
|
|
11
11
|
export interface WindowBodhiextClientConfig {
|
|
12
12
|
authServerUrl: string;
|
|
13
13
|
redirectUri: string;
|
|
14
|
-
userRole: UserScope;
|
|
15
14
|
basePath: string;
|
|
16
15
|
logLevel: LogLevel;
|
|
17
16
|
apiTimeoutMs?: number;
|
|
@@ -45,7 +44,6 @@ export declare class WindowBodhiextClient implements IExtensionClient {
|
|
|
45
44
|
private _chat;
|
|
46
45
|
private _models;
|
|
47
46
|
private _embeddings;
|
|
48
|
-
private _toolsets;
|
|
49
47
|
private _mcps;
|
|
50
48
|
constructor(authClientId: string, config: WindowBodhiextClientConfig, onStateChange?: StateChangeCallback);
|
|
51
49
|
/**
|
|
@@ -149,7 +147,6 @@ export declare class WindowBodhiextClient implements IExtensionClient {
|
|
|
149
147
|
get chat(): Chat;
|
|
150
148
|
get models(): Models;
|
|
151
149
|
get embeddings(): Embeddings;
|
|
152
|
-
get toolsets(): Toolsets;
|
|
153
150
|
get mcps(): Mcps;
|
|
154
151
|
requestAccess(body: CreateAccessRequest): Promise<ApiResponse<CreateAccessRequestResponse>>;
|
|
155
152
|
getAccessRequestStatus(requestId: string): Promise<ApiResponse<AccessRequestStatusResponse>>;
|
package/dist/facade-client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BaseFacadeClient, Logger, AuthState, IWebUIClient, LogLevel, StateChange, StateChangeCallback
|
|
1
|
+
import { BaseFacadeClient, Logger, AuthState, IWebUIClient, LogLevel, StateChange, StateChangeCallback } from '@bodhiapp/bodhi-js-core';
|
|
2
2
|
import { DirectWebClient } from './direct-client';
|
|
3
3
|
import { WindowBodhiextClient } from './ext-client';
|
|
4
4
|
/**
|
|
@@ -8,7 +8,6 @@ import { WindowBodhiextClient } from './ext-client';
|
|
|
8
8
|
export interface WebClientConfig {
|
|
9
9
|
authServerUrl: string;
|
|
10
10
|
redirectUri: string;
|
|
11
|
-
userRole: UserScope;
|
|
12
11
|
basePath: string;
|
|
13
12
|
logLevel: LogLevel;
|
|
14
13
|
apiTimeoutMs?: number;
|
|
@@ -26,7 +25,6 @@ export interface WebClientConfig {
|
|
|
26
25
|
export interface WebUIClientParams {
|
|
27
26
|
redirectUri?: string;
|
|
28
27
|
authServerUrl?: string;
|
|
29
|
-
userRole?: UserScope;
|
|
30
28
|
basePath?: string;
|
|
31
29
|
logLevel?: LogLevel;
|
|
32
30
|
apiTimeoutMs?: 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.30",
|
|
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.30",
|
|
41
|
+
"@bodhiapp/bodhi-js-core": "0.0.30",
|
|
42
|
+
"@bodhiapp/ts-client": "0.1.24"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@eslint/js": "^9.23.0",
|