@buoy-gg/license 7.0.74 → 7.0.76

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.
Files changed (46) hide show
  1. package/lib/commonjs/LicenseManager.js +2 -2
  2. package/lib/commonjs/cacheSignature.js +1 -1
  3. package/lib/commonjs/config.js +1 -1
  4. package/lib/commonjs/devToken.js +1 -1
  5. package/lib/commonjs/hooks.js +1 -1
  6. package/lib/commonjs/index.js +1 -1
  7. package/lib/commonjs/index.web.js +1 -1
  8. package/lib/commonjs/nodeSession.js +1 -1
  9. package/lib/commonjs/tier.js +1 -1
  10. package/lib/commonjs/weekendPro.js +1 -1
  11. package/lib/module/LicenseManager.js +2 -2
  12. package/lib/module/cacheSignature.js +1 -1
  13. package/lib/module/config.js +1 -1
  14. package/lib/module/devToken.js +1 -1
  15. package/lib/module/hooks.js +1 -1
  16. package/lib/module/index.js +1 -1
  17. package/lib/module/index.web.js +1 -1
  18. package/lib/module/nodeSession.js +1 -1
  19. package/lib/module/tier.js +1 -1
  20. package/lib/module/weekendPro.js +1 -1
  21. package/lib/typescript/commonjs/LicenseManager.d.ts +12 -2
  22. package/lib/typescript/commonjs/cacheSignature.d.ts +1 -1
  23. package/lib/typescript/commonjs/config.d.ts +1 -1
  24. package/lib/typescript/commonjs/devToken.d.ts +1 -1
  25. package/lib/typescript/commonjs/hooks.d.ts +1 -1
  26. package/lib/typescript/commonjs/index.d.ts +2 -2
  27. package/lib/typescript/commonjs/index.web.d.ts +2 -2
  28. package/lib/typescript/commonjs/tier.d.ts +1 -1
  29. package/lib/typescript/commonjs/types.d.ts +21 -0
  30. package/lib/typescript/commonjs/weekendPro.d.ts +1 -1
  31. package/lib/typescript/module/LicenseManager.d.ts +12 -2
  32. package/lib/typescript/module/cacheSignature.d.ts +1 -1
  33. package/lib/typescript/module/config.d.ts +1 -1
  34. package/lib/typescript/module/devToken.d.ts +1 -1
  35. package/lib/typescript/module/hooks.d.ts +1 -1
  36. package/lib/typescript/module/index.d.ts +2 -2
  37. package/lib/typescript/module/index.web.d.ts +2 -2
  38. package/lib/typescript/module/tier.d.ts +1 -1
  39. package/lib/typescript/module/types.d.ts +21 -0
  40. package/lib/typescript/module/weekendPro.d.ts +1 -1
  41. package/lib/web/chunks/LicenseManager-LOBHCMIJ.mjs +1 -0
  42. package/lib/web/chunks/chunk-3RVGU64S.mjs +73 -0
  43. package/lib/web/index.mjs +4 -4
  44. package/package.json +1 -1
  45. package/lib/web/chunks/LicenseManager-G4PF6ILL.mjs +0 -1
  46. package/lib/web/chunks/chunk-OP6QSLZS.mjs +0 -73
@@ -11,6 +11,6 @@
11
11
  * 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
- */async function boundedStorage(h,e){let t;try{return await Promise.race([h,new Promise(i=>{t=setTimeout(()=>i(e),15e3)})])}finally{if(t)clearTimeout(t)}}function tierOfCache(h){if(h.tier==="pro"||h.tier==="free")return h.tier;return h.isPro?"pro":"free"}class LicenseManagerImpl{state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};listeners=new Set;revalidationTimer=null;cachedLicense=null;fingerprintPromise=null;initPromise=null;generation=0;persistence=Promise.resolve();verifiedAt=null;expiryTimer=null;removeForegroundListener=null;sessionUnsubscribe=null;retryTimer=null;retryAttempt=0;deviceRegistrationCallback=null;pendingRegistration=null;async initialize(){if(this.initPromise)return this.initPromise;if(this.state.isInitialized)return;this.initPromise=this._doInitialize();return this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;const e=this.generation;if(!this.sessionUnsubscribe){this.sessionUnsubscribe=_sessionManager.SessionManager.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))}await boundedStorage(_sessionManager.SessionManager.load().catch(()=>void 0),void 0);try{const t=await boundedStorage(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){const i=(0,_cacheSignature.verifyCacheSignature)(t);this.cachedLicense=t;this.state.licenseKey=t.licenseKey;if(i&&this.isCacheValid(t))this.applyTier(tierOfCache(t));else this.lock();this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null;if(i&&this.isCacheValid(t))this.setVerifiedAt(t.cachedAt);if(this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))){this.validateInBackground(t.licenseKey)}}this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer()}catch(t){console.warn("[Buoy]","[React Buoy] License initialization failed:",t);this.state.isInitialized=true;this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0){await this.initialize();return this.hasAccountAccess()}const t=(0,_serviceKey.normalizeLicenseKey)(e);const i=++this.generation;this.cancelRetry();this.configuredKey=t||null;if(this.state.licenseKey!==this.configuredKey)this.lock();this.state.licenseKey=this.configuredKey;this.state.isValidating=!!t;this.emit({type:"validating",isPro:this.isPro()});await this.initialize();if(i!==this.generation)return false;if(!t){await this.clearLicense();return false}return this.setLicenseKey(t)}async setLicenseKey(e,t){if(!e||e.trim().length===0){this.state.error="License key is required";this.state.errorKind="verdict";return false}const i=(0,_serviceKey.normalizeLicenseKey)(e);const s=++this.generation;this.cancelRetry();if(!t?.isRetry)this.retryAttempt=0;if(this.state.licenseKey!==i)this.lock();this.state.licenseKey=i;this.state.isValidating=true;this.state.error=null;this.state.errorKind=null;this.emit({type:"validating",isPro:this.isPro()});if((0,_freeToken.isBuoyToken)(i))return this.admitToken(i,s);if((0,_serviceKey.isServiceKey)(i))return this.admitServiceKey(i,s);if((0,_devToken.isDevToken)(i))return this.admitDevToken(i);if(!this.cachedLicense){const a=await boundedStorage(this.loadCachedLicense(),null);if(s!==this.generation)return false;if(a?.licenseKey===i){this.cachedLicense=a;if((0,_cacheSignature.verifyCacheSignature)(a)&&this.isCacheValid(a)){this.applyTier(tierOfCache(a));this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null;this.setVerifiedAt(a.cachedAt);this.emit({type:"validating",isPro:this.isPro()})}}}const r=this.cachedLicense;if(!t?.force&&r&&r.valid&&r.licenseKey===i&&r.fingerprint&&(0,_cacheSignature.verifyCacheSignature)(r)&&this.isCacheValid(r)&&this.isCacheFresh(r)){this.applyTier(tierOfCache(r));this.state.licenseKey=i;this.state.isValidating=false;this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);this.emit({type:"validated",isPro:this.state.isPro});return true}const c=await boundedStorage(this.loadHold(),null);if(s!==this.generation)return false;const u=(c?.backoffUntil??0)-(0,_realNow.realNow)();if(u>0){return this.standDown(i,s,"License server is rate limited; retrying later.",u)}if(!t?.force&&c?.key===i&&c.verdictAt!==void 0&&(0,_realNow.realNow)()-c.verdictAt>=0&&(0,_realNow.realNow)()-c.verdictAt<_config.LICENSE_VERDICT_CACHE_DURATION){this.lock();this.state.isValidating=false;this.state.error=c.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});return false}let n;try{n=await(0,_api.validateLicense)(i)}catch(a){const o=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;if((0,_api.isTransientLicenseError)(a)){if((0,_api.isRateLimited)(a)){const l=Math.min((0,_api.backoffAfter)(a,_config.LICENSE_QUOTA_COOLDOWN,_config.LICENSE_RATE_LIMIT_DEFAULT_WAIT)*2**this.retryAttempt,_config.LICENSE_QUOTA_COOLDOWN);this.retryAttempt+=1;await this.saveHold({...this.hold,backoffUntil:(0,_realNow.realNow)()+l});if(s!==this.generation)return false;return this.standDown(i,s,o,l)}this.state.isValidating=false;if(this.cachedLicense&&this.cachedLicense.licenseKey===i&&(0,_cacheSignature.verifyCacheSignature)(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)){this.applyTier(tierOfCache(this.cachedLicense));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null;this.setVerifiedAt(this.cachedLicense.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetry(i);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=o;this.state.errorKind="transport";this.scheduleRetry(i);this.emit({type:"error",isPro:this.state.isPro,error:o});return false}this.state.isValidating=false;this.state.error=o;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:o});return false}try{if(s!==this.generation)return false;const a=(0,_api.parseValidationResult)(n);const o=n.data?.relationships?.product?.data?.id;if((!o||o===_config.KEYGEN_CONFIG.productId)&&a.code==="EXPIRED"&&((0,_tier.isRecognizedAccount)(n.data)||this.cachedLicense?.licenseKey===i&&(0,_cacheSignature.verifyCacheSignature)(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){const E=(0,_realNow.realNow)();const L=await this.saveCache({licenseKey:i,licenseId:n.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:E,lastValidatedAt:E,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},s);if(s!==this.generation||!L)return false;this.cachedLicense=L;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(E);this.emit({type:"validated",isPro:false});return true}if((0,_api.isFatalLicenseError)(a.code)||!(0,_api.isAcceptedValidation)(n)){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:(0,_realNow.realNow)()});return false}if(!n.data){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License not found";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const l=(0,_tier.resolveTier)(n.data);const _=n.data.id;const g=(0,_realNow.realNow)();const v=(0,_api.machineCount)(n)>=_config.LICENSE_FLEET_MACHINE_THRESHOLD;const m=(0,_fingerprint.isDevBundle)();const I=m&&!v;const R=l==="free"&&(v||!m);let d=this.cachedLicense?.fingerprint;let f=this.cachedLicense?.deviceName;let A=false;if(!d){d=await boundedStorage(this.getOrCreateDeviceFingerprint(),(0,_fingerprint.generateUUID)());if(s!==this.generation)return false;f=(0,_fingerprint.getDefaultDeviceName)();A=I}let y=false;if(A){if(this.deviceRegistrationCallback){this.pendingRegistration={licenseId:_,licenseKey:i,fingerprint:d};this.deviceRegistrationCallback({fingerprint:d,defaultName:f});y=true}else{this.registerDeviceAsync(_,i,d,f)}}const p={licenseKey:i,licenseId:_,valid:true,isPro:l==="pro",tier:l,expiresAt:n.data.attributes.expiry??null,cachedAt:g,lastValidatedAt:g,fingerprint:y?void 0:d,deviceName:y?void 0:f,quiet:R||void 0};const T=await this.saveCache(p,s);if(s!==this.generation||!T)return false;this.cachedLicense=T;this.applyTier(l);this.state.isValidating=false;this.state.expiresAt=p.expiresAt?new Date(p.expiresAt):null;this.setVerifiedAt(g);if(this.hold)await this.saveHold(null);if(s!==this.generation)return false;this.retryAttempt=0;this.emit({type:"validated",isPro:this.state.isPro});return true}catch(a){const o=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;this.state.isValidating=false;this.state.error=o;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:o});return false}}scheduleRetry(e){this.cancelRetry();const t=_config.LICENSE_TRANSIENT_RETRY_DELAYS[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},t);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}}async admitServiceKey(e,t){const i=await(0,_serviceKey.exchangeServiceKey)(e,{origin:_sessionManager.SessionManager.origin});if(t!==this.generation)return false;if(!i.ok){this.lock();this.state.isValidating=false;this.state.error=i.detail;this.state.errorKind=i.reason==="network"?"transport":"verdict";this.emit({type:"error",isPro:false,error:i.detail});if(i.reason==="network")this.scheduleRetry(e);return false}const s=i.claims.exp*1e3;this.applyTier(i.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(s);this.setVerifiedAt((0,_realNow.realNow)());this.retryAttempt=0;this.scheduleRetryIn(e,Math.max(6e4,s-60*60*1e3-(0,_realNow.realNow)()));this.emit({type:"validated",isPro:this.isPro()});return true}admitDevToken(e){const t=(0,_devToken.checkDevToken)(e);if(!t.ok||!(0,_fingerprint.isDevBundle)()){const s=t.ok?_devToken.DEV_TOKEN_IN_RELEASE:t.message;this.lock();this.state.isValidating=false;this.state.error=s;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:s});return false}const i=(0,_realNow.realNow)();this.applyTier(t.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(t.claims.exp*1e3);this.setVerifiedAt(Math.min(i,t.claims.iat*1e3));this.retryAttempt=0;this.emit({type:"validated",isPro:this.isPro()});return true}async admitToken(e,t){const i=(0,_freeToken.verifyFreeToken)(e);if(t!==this.generation)return false;if(!i){this.lock();this.state.isValidating=false;this.state.error="This key is not valid. Check that it was copied completely.";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const s=(0,_realNow.realNow)();const r=this.cachedLicense?.fingerprint??await boundedStorage(this.getOrCreateDeviceFingerprint(),(0,_fingerprint.generateUUID)());if(t!==this.generation)return false;const c=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:s,lastValidatedAt:s,fingerprint:r,deviceName:this.cachedLicense?.deviceName??(0,_fingerprint.getDefaultDeviceName)(),quiet:true},t);if(t!==this.generation||!c)return false;this.cachedLicense=c;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(s);this.retryAttempt=0;this.emit({type:"validated",isPro:false});return true}standDown(e,t,i,s){if(t!==this.generation)return false;this.state.isValidating=false;const r=this.cachedLicense;if(r&&r.licenseKey===e&&(0,_cacheSignature.verifyCacheSignature)(r)&&this.isCacheValid(r)){this.applyTier(tierOfCache(r));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetryIn(e,s);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=i;this.state.errorKind="transport";this.scheduleRetryIn(e,s);this.emit({type:"error",isPro:this.state.isPro,error:i});return false}scheduleRetryIn(e,t){this.cancelRetry();const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},Math.max(1,Math.min(t,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}cancelRetry(){if(this.retryTimer){clearTimeout(this.retryTimer);this.retryTimer=null}}async clearLicense(){this.generation+=1;this.cancelRetry();this.retryAttempt=0;this.configuredKey=null;this.state.licenseKey=null;this.lock();this.state.expiresAt=null;this.state.error=null;this.state.errorKind=null;this.cachedLicense=null;this.pendingRegistration=null;this.state.isValidating=false;this.emit({type:"deactivated",isPro:false});await this.clearCachedLicense();if(this.hold?.verdictAt!==void 0){await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}}applyTier(e){this.state.tier=e;this.state.tierSource="key";this.state.isPro=e==="pro"}lock(){this.state.tier="free";this.state.tierSource="none";this.state.isPro=false;this.verifiedAt=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null}getTier(){return this.credential().tier}credential(){const e=this.keyTier();const t=this.sessionTier();if(t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")){return{source:"session",tier:t}}return{source:this.state.tierSource,tier:e}}keyTier(){if(!this.hasKeyAccess())return"free";if(this.state.expiresAt&&(0,_realNow.realNow)()>=this.state.expiresAt.getTime())return"free";return this.state.tier}sessionTier(){const e=_sessionManager.SessionManager.getStatus();if(e.state!=="active")return null;return e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){const{source:e,tier:t}=this.credential();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess()}}getAccountAccess(){const e=this.getKeyAccess();if(e.status==="admitted")return e;const t=_sessionManager.SessionManager.getStatus();if(t.state==="active")return{status:"admitted",reason:null,validUntil:t.validUntil};return e}getKeyAccess(){return(0,_accountAccess.resolveAccountAccess)({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e;if(this.expiryTimer)clearTimeout(this.expiryTimer);const t=e+_config.LICENSE_CACHE_DURATION;const i=this.state.expiresAt?.getTime();const s=i&&i>(0,_realNow.realNow)()?Math.min(t,i):t;const r=s-(0,_realNow.realNow)();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null;if(this.verifiedAt!==e)return;this.emit({type:"validated",isPro:this.isPro()});if((0,_realNow.realNow)()<t)this.setVerifiedAt(e)},Math.max(1,Math.min(r,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){this.deviceRegistrationCallback=e;return()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}const{licenseId:t,licenseKey:i,fingerprint:s}=this.pendingRegistration;const r=this.generation;const c=e.trim()||(0,_fingerprint.getDefaultDeviceName)();const u=(0,_fingerprint.getDeviceMetadata)();try{await(0,_api.activateMachine)(t,i,s,c,u)}catch(n){const a=n instanceof Error?n.message:String(n);if(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"){}else{console.warn("[Buoy] Device registration failed:",a);throw n}}if(r!==this.generation)return;if(this.cachedLicense?.licenseKey===i){const n=await this.saveCache({...this.cachedLicense,fingerprint:s,deviceName:c},r);if(r!==this.generation)return;if(n)this.cachedLicense=n}this.pendingRegistration=null}emit(e){const t=[...this.listeners];t.forEach(i=>{try{i(e)}catch(s){console.warn("[Buoy]","[React Buoy] License listener error:",s)}})}isCacheValid(e){const t=(0,_realNow.realNow)();const i=t-e.cachedAt;if(!e.valid||!Number.isFinite(e.cachedAt)||i<0||i>=_config.LICENSE_CACHE_DURATION){return false}return true}isCacheFresh(e){const t=this.isQuiet(e)?_config.LICENSE_QUIET_VALIDATION_TTL:_config.LICENSE_LAUNCH_VALIDATION_TTL;return(0,_realNow.realNow)()-(e.lastValidatedAt??0)<t}isQuiet(e){if(e.quiet!==void 0)return e.quiet;return tierOfCache(e)==="free"&&!(0,_fingerprint.isDevBundle)()}async loadHold(){if(this.hold!==void 0)return this.hold;try{const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.LICENSE_HOLD_STORAGE_KEY);const i=t?JSON.parse(t):null;this.hold=i&&typeof i==="object"?i:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{const t=await(0,_storage.getLicenseStorage)();if(e)await t.setItem(_config.LICENSE_HOLD_STORAGE_KEY,JSON.stringify(e));else await t.removeItem(_config.LICENSE_HOLD_STORAGE_KEY)}catch{}}async persistCache(e){const t=(0,_cacheSignature.signCache)(e);const i=await(0,_storage.getLicenseStorage)();await i.setItem(_config.LICENSE_STORAGE_KEY,JSON.stringify(t));return t}saveCache(e,t){const i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;const s=await this.persistCache(e);return t===this.generation?s:null});this.persistence=i;return boundedStorage(i,(0,_cacheSignature.signCache)(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.LICENSE_STORAGE_KEY);if(!t)return null;const i=JSON.parse(t);return i}catch(e){console.warn("[Buoy] Error loading cached license:",e);return null}}getOrCreateDeviceFingerprint(){if(!this.fingerprintPromise){this.fingerprintPromise=(async()=>{const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY);if(t)return t;const i=this.cachedLicense?.fingerprint||(0,_fingerprint.generateUUID)();await e.setItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY,i);return i})()}return this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;const e=this.persistence.catch(()=>{}).then(async()=>{const t=await(0,_storage.getLicenseStorage)();await t.removeItem(_config.LICENSE_STORAGE_KEY)});this.persistence=e;await boundedStorage(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,s){const r=(0,_fingerprint.getDeviceMetadata)();(0,_api.activateMachine)(e,t,i,s,r).catch(c=>{if(c.message!=="FINGERPRINT_TAKEN"&&c.message!=="MACHINE_LIMIT_EXCEEDED"){console.warn("[Buoy] Device registration failed (non-blocking):",c.message)}})}validateInBackground(e){void this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){if(!this.removeForegroundListener){this.removeForegroundListener=(0,_foreground.subscribeForeground)(()=>this.refreshAccess());if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener}if(this.revalidationTimer){clearInterval(this.revalidationTimer)}this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;const e=this.cachedLicense;if(e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e))return;this.validateInBackground(this.state.licenseKey)},_config.LICENSE_REVALIDATION_INTERVAL);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_TIMER__=this.revalidationTimer}}stopRevalidationTimer(){this.removeForegroundListener?.();this.removeForegroundListener=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null;if(this.revalidationTimer){clearInterval(this.revalidationTimer);this.revalidationTimer=null}this.cancelRetry()}async forceReregister(){if(this.cachedLicense){this.cachedLicense.fingerprint=void 0;this.cachedLicense.deviceName=void 0}this.fingerprintPromise=null;await this.clearCachedLicense();try{const e=await(0,_storage.getLicenseStorage)();await e.removeItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY)}catch{}}}const LicenseManager=exports.LicenseManager=new LicenseManagerImpl;const devTimers=globalThis;if(typeof __DEV__!=="undefined"&&__DEV__){devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__?.();devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0;if(devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__)clearTimeout(devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__);if(devTimers.__LICENSE_MANAGER_TIMER__){clearInterval(devTimers.__LICENSE_MANAGER_TIMER__)}if(devTimers.__LICENSE_MANAGER_RETRY_TIMER__){clearTimeout(devTimers.__LICENSE_MANAGER_RETRY_TIMER__);devTimers.__LICENSE_MANAGER_RETRY_TIMER__=void 0}}
16
+ */const NO_TEAM_ACCESS={role:null,tools:null};function teamAccessOf(h){return{role:h.role??null,tools:h.tools??null}}async function boundedStorage(h,e){let t;try{return await Promise.race([h,new Promise(i=>{t=setTimeout(()=>i(e),15e3)})])}finally{if(t)clearTimeout(t)}}function tierOfCache(h){if(h.tier==="pro"||h.tier==="free")return h.tier;return h.isPro?"pro":"free"}class LicenseManagerImpl{state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};listeners=new Set;revalidationTimer=null;cachedLicense=null;fingerprintPromise=null;initPromise=null;generation=0;persistence=Promise.resolve();verifiedAt=null;expiryTimer=null;removeForegroundListener=null;sessionUnsubscribe=null;devToken=null;retryTimer=null;retryAttempt=0;deviceRegistrationCallback=null;pendingRegistration=null;async initialize(){if(this.initPromise)return this.initPromise;if(this.state.isInitialized)return;this.initPromise=this._doInitialize();return this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;const e=this.generation;if(!this.sessionUnsubscribe){this.sessionUnsubscribe=_sessionManager.SessionManager.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))}await boundedStorage(_sessionManager.SessionManager.load().catch(()=>void 0),void 0);try{const t=await boundedStorage(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){const i=(0,_cacheSignature.verifyCacheSignature)(t);this.cachedLicense=t;this.state.licenseKey=t.licenseKey;if(i&&this.isCacheValid(t))this.applyTier(tierOfCache(t));else this.lock();this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null;if(i&&this.isCacheValid(t))this.setVerifiedAt(t.cachedAt);if(this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))){this.validateInBackground(t.licenseKey)}}this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer()}catch(t){console.warn("[Buoy]","[React Buoy] License initialization failed:",t);this.state.isInitialized=true;this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0){await this.initialize();return this.hasAccountAccess()}const t=(0,_serviceKey.normalizeLicenseKey)(e);const i=++this.generation;this.cancelRetry();this.configuredKey=t||null;if(this.state.licenseKey!==this.configuredKey)this.lock();this.state.licenseKey=this.configuredKey;this.state.isValidating=!!t;this.emit({type:"validating",isPro:this.isPro()});await this.initialize();if(i!==this.generation)return false;if(!t){await this.clearLicense();return false}return this.setLicenseKey(t)}async setLicenseKey(e,t){if(!e||e.trim().length===0){this.state.error="License key is required";this.state.errorKind="verdict";return false}const i=(0,_serviceKey.normalizeLicenseKey)(e);const s=++this.generation;this.cancelRetry();if(!t?.isRetry)this.retryAttempt=0;if(this.state.licenseKey!==i)this.lock();this.state.licenseKey=i;this.state.isValidating=true;this.state.error=null;this.state.errorKind=null;this.emit({type:"validating",isPro:this.isPro()});if((0,_freeToken.isBuoyToken)(i))return this.admitToken(i,s);if((0,_serviceKey.isServiceKey)(i))return this.admitServiceKey(i,s);if((0,_devToken.isDevToken)(i))return this.admitDevToken(i);if(!this.cachedLicense){const a=await boundedStorage(this.loadCachedLicense(),null);if(s!==this.generation)return false;if(a?.licenseKey===i){this.cachedLicense=a;if((0,_cacheSignature.verifyCacheSignature)(a)&&this.isCacheValid(a)){this.applyTier(tierOfCache(a));this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null;this.setVerifiedAt(a.cachedAt);this.emit({type:"validating",isPro:this.isPro()})}}}const r=this.cachedLicense;if(!t?.force&&r&&r.valid&&r.licenseKey===i&&r.fingerprint&&(0,_cacheSignature.verifyCacheSignature)(r)&&this.isCacheValid(r)&&this.isCacheFresh(r)){this.applyTier(tierOfCache(r));this.state.licenseKey=i;this.state.isValidating=false;this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);this.emit({type:"validated",isPro:this.state.isPro});return true}const c=await boundedStorage(this.loadHold(),null);if(s!==this.generation)return false;const u=(c?.backoffUntil??0)-(0,_realNow.realNow)();if(u>0){return this.standDown(i,s,"License server is rate limited; retrying later.",u)}if(!t?.force&&c?.key===i&&c.verdictAt!==void 0&&(0,_realNow.realNow)()-c.verdictAt>=0&&(0,_realNow.realNow)()-c.verdictAt<_config.LICENSE_VERDICT_CACHE_DURATION){this.lock();this.state.isValidating=false;this.state.error=c.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});return false}let n;try{n=await(0,_api.validateLicense)(i)}catch(a){const o=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;if((0,_api.isTransientLicenseError)(a)){if((0,_api.isRateLimited)(a)){const l=Math.min((0,_api.backoffAfter)(a,_config.LICENSE_QUOTA_COOLDOWN,_config.LICENSE_RATE_LIMIT_DEFAULT_WAIT)*2**this.retryAttempt,_config.LICENSE_QUOTA_COOLDOWN);this.retryAttempt+=1;await this.saveHold({...this.hold,backoffUntil:(0,_realNow.realNow)()+l});if(s!==this.generation)return false;return this.standDown(i,s,o,l)}this.state.isValidating=false;if(this.cachedLicense&&this.cachedLicense.licenseKey===i&&(0,_cacheSignature.verifyCacheSignature)(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)){this.applyTier(tierOfCache(this.cachedLicense));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null;this.setVerifiedAt(this.cachedLicense.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetry(i);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=o;this.state.errorKind="transport";this.scheduleRetry(i);this.emit({type:"error",isPro:this.state.isPro,error:o});return false}this.state.isValidating=false;this.state.error=o;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:o});return false}try{if(s!==this.generation)return false;const a=(0,_api.parseValidationResult)(n);const o=n.data?.relationships?.product?.data?.id;if((!o||o===_config.KEYGEN_CONFIG.productId)&&a.code==="EXPIRED"&&((0,_tier.isRecognizedAccount)(n.data)||this.cachedLicense?.licenseKey===i&&(0,_cacheSignature.verifyCacheSignature)(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){const E=(0,_realNow.realNow)();const L=await this.saveCache({licenseKey:i,licenseId:n.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:E,lastValidatedAt:E,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},s);if(s!==this.generation||!L)return false;this.cachedLicense=L;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(E);this.emit({type:"validated",isPro:false});return true}if((0,_api.isFatalLicenseError)(a.code)||!(0,_api.isAcceptedValidation)(n)){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:(0,_realNow.realNow)()});return false}if(!n.data){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License not found";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const l=(0,_tier.resolveTier)(n.data);const _=n.data.id;const g=(0,_realNow.realNow)();const v=(0,_api.machineCount)(n)>=_config.LICENSE_FLEET_MACHINE_THRESHOLD;const m=(0,_fingerprint.isDevBundle)();const I=m&&!v;const R=l==="free"&&(v||!m);let d=this.cachedLicense?.fingerprint;let f=this.cachedLicense?.deviceName;let A=false;if(!d){d=await boundedStorage(this.getOrCreateDeviceFingerprint(),(0,_fingerprint.generateUUID)());if(s!==this.generation)return false;f=(0,_fingerprint.getDefaultDeviceName)();A=I}let y=false;if(A){if(this.deviceRegistrationCallback){this.pendingRegistration={licenseId:_,licenseKey:i,fingerprint:d};this.deviceRegistrationCallback({fingerprint:d,defaultName:f});y=true}else{this.registerDeviceAsync(_,i,d,f)}}const p={licenseKey:i,licenseId:_,valid:true,isPro:l==="pro",tier:l,expiresAt:n.data.attributes.expiry??null,cachedAt:g,lastValidatedAt:g,fingerprint:y?void 0:d,deviceName:y?void 0:f,quiet:R||void 0};const T=await this.saveCache(p,s);if(s!==this.generation||!T)return false;this.cachedLicense=T;this.applyTier(l);this.state.isValidating=false;this.state.expiresAt=p.expiresAt?new Date(p.expiresAt):null;this.setVerifiedAt(g);if(this.hold)await this.saveHold(null);if(s!==this.generation)return false;this.retryAttempt=0;this.emit({type:"validated",isPro:this.state.isPro});return true}catch(a){const o=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;this.state.isValidating=false;this.state.error=o;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:o});return false}}scheduleRetry(e){this.cancelRetry();const t=_config.LICENSE_TRANSIENT_RETRY_DELAYS[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},t);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}}async admitServiceKey(e,t){const i=await(0,_serviceKey.exchangeServiceKey)(e,{origin:_sessionManager.SessionManager.origin});if(t!==this.generation)return false;if(!i.ok){this.lock();this.state.isValidating=false;this.state.error=i.detail;this.state.errorKind=i.reason==="network"?"transport":"verdict";this.emit({type:"error",isPro:false,error:i.detail});if(i.reason==="network")this.scheduleRetry(e);return false}const s=i.claims.exp*1e3;this.applyTier(i.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(s);this.setVerifiedAt((0,_realNow.realNow)());this.retryAttempt=0;this.scheduleRetryIn(e,Math.max(6e4,s-60*60*1e3-(0,_realNow.realNow)()));this.emit({type:"validated",isPro:this.isPro()});return true}admitDevToken(e){const t=(0,_devToken.checkDevToken)(e);if(!t.ok||!(0,_fingerprint.isDevBundle)()){const s=t.ok?_devToken.DEV_TOKEN_IN_RELEASE:t.message;this.lock();this.state.isValidating=false;this.state.error=s;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:s});return false}const i=(0,_realNow.realNow)();this.devToken={token:e,claims:t.claims};this.applyTier(t.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(t.claims.exp*1e3);this.setVerifiedAt(Math.min(i,t.claims.iat*1e3));this.retryAttempt=0;this.emit({type:"validated",isPro:this.isPro()});return true}async admitToken(e,t){const i=(0,_freeToken.verifyFreeToken)(e);if(t!==this.generation)return false;if(!i){this.lock();this.state.isValidating=false;this.state.error="This key is not valid. Check that it was copied completely.";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const s=(0,_realNow.realNow)();const r=this.cachedLicense?.fingerprint??await boundedStorage(this.getOrCreateDeviceFingerprint(),(0,_fingerprint.generateUUID)());if(t!==this.generation)return false;const c=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:s,lastValidatedAt:s,fingerprint:r,deviceName:this.cachedLicense?.deviceName??(0,_fingerprint.getDefaultDeviceName)(),quiet:true},t);if(t!==this.generation||!c)return false;this.cachedLicense=c;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(s);this.retryAttempt=0;this.emit({type:"validated",isPro:false});return true}standDown(e,t,i,s){if(t!==this.generation)return false;this.state.isValidating=false;const r=this.cachedLicense;if(r&&r.licenseKey===e&&(0,_cacheSignature.verifyCacheSignature)(r)&&this.isCacheValid(r)){this.applyTier(tierOfCache(r));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetryIn(e,s);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=i;this.state.errorKind="transport";this.scheduleRetryIn(e,s);this.emit({type:"error",isPro:this.state.isPro,error:i});return false}scheduleRetryIn(e,t){this.cancelRetry();const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},Math.max(1,Math.min(t,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}cancelRetry(){if(this.retryTimer){clearTimeout(this.retryTimer);this.retryTimer=null}}async clearLicense(){this.generation+=1;this.cancelRetry();this.retryAttempt=0;this.configuredKey=null;this.state.licenseKey=null;this.lock();this.state.expiresAt=null;this.state.error=null;this.state.errorKind=null;this.cachedLicense=null;this.pendingRegistration=null;this.state.isValidating=false;this.emit({type:"deactivated",isPro:false});await this.clearCachedLicense();if(this.hold?.verdictAt!==void 0){await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}}applyTier(e){this.state.tier=e;this.state.tierSource="key";this.state.isPro=e==="pro"}lock(){this.state.tier="free";this.state.tierSource="none";this.state.isPro=false;this.verifiedAt=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null}getTier(){return this.credential().tier}credential(){const e=this.keyTier();const t=this.sessionTier();if(t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")){return{source:"session",tier:t}}return{source:this.state.tierSource,tier:e}}keyTier(){if(!this.hasKeyAccess())return"free";if(this.state.expiresAt&&(0,_realNow.realNow)()>=this.state.expiresAt.getTime())return"free";return this.state.tier}sessionTier(){const e=_sessionManager.SessionManager.getStatus();if(e.state!=="active")return null;return e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){const{source:e,tier:t}=this.credential();const i=this.getTeamAccess();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess(),teamRole:i.role,allowedTools:i.tools}}getTeamAccess(){const e=_sessionManager.SessionManager.getStatus();if(e.state==="active")return teamAccessOf(e.claims);const t=this.devToken;if(t&&t.token===this.state.licenseKey&&this.state.tierSource==="key"&&(0,_realNow.realNow)()<t.claims.exp*1e3&&this.hasKeyAccess()){return teamAccessOf(t.claims)}return NO_TEAM_ACCESS}getAccountAccess(){const e=this.getKeyAccess();if(e.status==="admitted")return e;const t=_sessionManager.SessionManager.getStatus();if(t.state==="active")return{status:"admitted",reason:null,validUntil:t.validUntil};return e}getKeyAccess(){return(0,_accountAccess.resolveAccountAccess)({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e;if(this.expiryTimer)clearTimeout(this.expiryTimer);const t=e+_config.LICENSE_CACHE_DURATION;const i=this.state.expiresAt?.getTime();const s=i&&i>(0,_realNow.realNow)()?Math.min(t,i):t;const r=s-(0,_realNow.realNow)();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null;if(this.verifiedAt!==e)return;this.emit({type:"validated",isPro:this.isPro()});if((0,_realNow.realNow)()<t)this.setVerifiedAt(e)},Math.max(1,Math.min(r,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){this.deviceRegistrationCallback=e;return()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}const{licenseId:t,licenseKey:i,fingerprint:s}=this.pendingRegistration;const r=this.generation;const c=e.trim()||(0,_fingerprint.getDefaultDeviceName)();const u=(0,_fingerprint.getDeviceMetadata)();try{await(0,_api.activateMachine)(t,i,s,c,u)}catch(n){const a=n instanceof Error?n.message:String(n);if(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"){}else{console.warn("[Buoy] Device registration failed:",a);throw n}}if(r!==this.generation)return;if(this.cachedLicense?.licenseKey===i){const n=await this.saveCache({...this.cachedLicense,fingerprint:s,deviceName:c},r);if(r!==this.generation)return;if(n)this.cachedLicense=n}this.pendingRegistration=null}emit(e){const t=[...this.listeners];t.forEach(i=>{try{i(e)}catch(s){console.warn("[Buoy]","[React Buoy] License listener error:",s)}})}isCacheValid(e){const t=(0,_realNow.realNow)();const i=t-e.cachedAt;if(!e.valid||!Number.isFinite(e.cachedAt)||i<0||i>=_config.LICENSE_CACHE_DURATION){return false}return true}isCacheFresh(e){const t=this.isQuiet(e)?_config.LICENSE_QUIET_VALIDATION_TTL:_config.LICENSE_LAUNCH_VALIDATION_TTL;return(0,_realNow.realNow)()-(e.lastValidatedAt??0)<t}isQuiet(e){if(e.quiet!==void 0)return e.quiet;return tierOfCache(e)==="free"&&!(0,_fingerprint.isDevBundle)()}async loadHold(){if(this.hold!==void 0)return this.hold;try{const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.LICENSE_HOLD_STORAGE_KEY);const i=t?JSON.parse(t):null;this.hold=i&&typeof i==="object"?i:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{const t=await(0,_storage.getLicenseStorage)();if(e)await t.setItem(_config.LICENSE_HOLD_STORAGE_KEY,JSON.stringify(e));else await t.removeItem(_config.LICENSE_HOLD_STORAGE_KEY)}catch{}}async persistCache(e){const t=(0,_cacheSignature.signCache)(e);const i=await(0,_storage.getLicenseStorage)();await i.setItem(_config.LICENSE_STORAGE_KEY,JSON.stringify(t));return t}saveCache(e,t){const i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;const s=await this.persistCache(e);return t===this.generation?s:null});this.persistence=i;return boundedStorage(i,(0,_cacheSignature.signCache)(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.LICENSE_STORAGE_KEY);if(!t)return null;const i=JSON.parse(t);return i}catch(e){console.warn("[Buoy] Error loading cached license:",e);return null}}getOrCreateDeviceFingerprint(){if(!this.fingerprintPromise){this.fingerprintPromise=(async()=>{const e=await(0,_storage.getLicenseStorage)();const t=await e.getItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY);if(t)return t;const i=this.cachedLicense?.fingerprint||(0,_fingerprint.generateUUID)();await e.setItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY,i);return i})()}return this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;const e=this.persistence.catch(()=>{}).then(async()=>{const t=await(0,_storage.getLicenseStorage)();await t.removeItem(_config.LICENSE_STORAGE_KEY)});this.persistence=e;await boundedStorage(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,s){const r=(0,_fingerprint.getDeviceMetadata)();(0,_api.activateMachine)(e,t,i,s,r).catch(c=>{if(c.message!=="FINGERPRINT_TAKEN"&&c.message!=="MACHINE_LIMIT_EXCEEDED"){console.warn("[Buoy] Device registration failed (non-blocking):",c.message)}})}validateInBackground(e){void this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){if(!this.removeForegroundListener){this.removeForegroundListener=(0,_foreground.subscribeForeground)(()=>this.refreshAccess());if(typeof __DEV__!=="undefined"&&__DEV__)devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener}if(this.revalidationTimer){clearInterval(this.revalidationTimer)}this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;const e=this.cachedLicense;if(e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e))return;this.validateInBackground(this.state.licenseKey)},_config.LICENSE_REVALIDATION_INTERVAL);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_TIMER__=this.revalidationTimer}}stopRevalidationTimer(){this.removeForegroundListener?.();this.removeForegroundListener=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null;if(this.revalidationTimer){clearInterval(this.revalidationTimer);this.revalidationTimer=null}this.cancelRetry()}async forceReregister(){if(this.cachedLicense){this.cachedLicense.fingerprint=void 0;this.cachedLicense.deviceName=void 0}this.fingerprintPromise=null;await this.clearCachedLicense();try{const e=await(0,_storage.getLicenseStorage)();await e.removeItem(_config.DEVICE_FINGERPRINT_STORAGE_KEY)}catch{}}}const LicenseManager=exports.LicenseManager=new LicenseManagerImpl;const devTimers=globalThis;if(typeof __DEV__!=="undefined"&&__DEV__){devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__?.();devTimers.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0;if(devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__)clearTimeout(devTimers.__LICENSE_MANAGER_EXPIRY_TIMER__);if(devTimers.__LICENSE_MANAGER_TIMER__){clearInterval(devTimers.__LICENSE_MANAGER_TIMER__)}if(devTimers.__LICENSE_MANAGER_RETRY_TIMER__){clearTimeout(devTimers.__LICENSE_MANAGER_RETRY_TIMER__);devTimers.__LICENSE_MANAGER_RETRY_TIMER__=void 0}}
@@ -11,6 +11,6 @@
11
11
  * under 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
16
  */const SIGNATURE_VERSION="v2";const LEGACY_VERSIONS=["v1"];const SIGNING_SALT="buoy.gg/pro:9f3c1d7a4b2e:license-cache";const FIELD_SEPARATOR="\0";function fnv1a(e,r){let n=r>>>0;for(let i=0;i<e.length;i++){n^=e.charCodeAt(i);n=Math.imul(n,16777619)>>>0}return n>>>0}function canonicalize(e,r){const n=[r,e.licenseKey,e.licenseId,e.isPro?"1":"0",e.expiresAt??"",String(e.cachedAt)];if(r!=="v1"){n.push(e.tier??"")}n.push(SIGNING_SALT);return n.join(FIELD_SEPARATOR)}function signatureFor(e,r){const n=canonicalize(e,r);const i=fnv1a(n,2166136261);const t=fnv1a(n,2135587861);return`${r}.${i.toString(16)}${t.toString(16)}`}function cacheSignature(e){return signatureFor(e,SIGNATURE_VERSION)}function signCache(e){return{...e,sig:cacheSignature(e)}}function verifyCacheSignature(e){if(!e||typeof e.sig!=="string"||e.sig.length===0){return false}if(e.sig===cacheSignature(e))return true;return LEGACY_VERSIONS.some(r=>e.sig.startsWith(`${r}.`)&&e.sig===signatureFor(e,r))}
@@ -12,6 +12,6 @@
12
12
  * § 1201, which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */const KEYGEN_CONFIG=exports.KEYGEN_CONFIG=Object.freeze({accountId:"b0eba0e8-e904-4906-b644-38f9aab57bed",productId:"bf67cb43-eeef-40ca-b308-e6e1b22c0eaa",apiUrl:"https://buoy.gg/api/license"});const KEYGEN_POLICIES=exports.KEYGEN_POLICIES=Object.freeze({free:"394e3351-1249-4f73-807a-703408604c0e",solo:"fa5d3c8f-5379-40f1-b63e-ab8a974b2f28",business:"690c2212-8e54-426f-b4b4-002ee1c0ae2b"});const LICENSE_CACHE_DURATION=exports.LICENSE_CACHE_DURATION=30*24*60*60*1e3;const LICENSE_REVALIDATION_INTERVAL=exports.LICENSE_REVALIDATION_INTERVAL=24*60*60*1e3;const LICENSE_STORAGE_KEY=exports.LICENSE_STORAGE_KEY="buoy-gg-license";const DEVICE_FINGERPRINT_STORAGE_KEY=exports.DEVICE_FINGERPRINT_STORAGE_KEY="buoy-gg-device-fingerprint";const LICENSE_LAUNCH_VALIDATION_TTL=exports.LICENSE_LAUNCH_VALIDATION_TTL=60*60*1e3;const LICENSE_TRANSIENT_RETRY_DELAYS=exports.LICENSE_TRANSIENT_RETRY_DELAYS=[5*1e3,20*1e3,60*1e3,5*60*1e3,15*60*1e3];const LICENSE_RATE_LIMIT_DEFAULT_WAIT=exports.LICENSE_RATE_LIMIT_DEFAULT_WAIT=60*1e3;const LICENSE_QUOTA_COOLDOWN=exports.LICENSE_QUOTA_COOLDOWN=60*60*1e3;const LICENSE_VERDICT_CACHE_DURATION=exports.LICENSE_VERDICT_CACHE_DURATION=24*60*60*1e3;const LICENSE_FLEET_MACHINE_THRESHOLD=exports.LICENSE_FLEET_MACHINE_THRESHOLD=50;const LICENSE_QUIET_VALIDATION_TTL=exports.LICENSE_QUIET_VALIDATION_TTL=LICENSE_CACHE_DURATION;const LICENSE_HOLD_STORAGE_KEY=exports.LICENSE_HOLD_STORAGE_KEY="buoy-gg-license-hold";
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
5
  * (https://buoy.gg/terms), not an OSS license.
6
- */const DEV_AUDIENCE=exports.DEV_AUDIENCE="dev";function isDevToken(e){return(0,_sessionToken.isSessionToken)(e)}function checkDevToken(e,n=(0,_realNow.realNow)()){const o=(0,_sessionToken.verifySessionToken)(e,{audience:DEV_AUDIENCE,ignoreTime:true});if(!o.ok)return{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx buoy login` again."};if(n>=o.claims.exp*1e3){return{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx buoy login` again."}}return{ok:true,claims:o.claims}}const DEV_TOKEN_IN_RELEASE=exports.DEV_TOKEN_IN_RELEASE="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";
6
+ */const DEV_AUDIENCE=exports.DEV_AUDIENCE="dev";function isDevToken(e){return(0,_sessionToken.isSessionToken)(e)}function checkDevToken(e,n=(0,_realNow.realNow)()){const o=(0,_sessionToken.verifySessionToken)(e,{audience:DEV_AUDIENCE,ignoreTime:true});if(!o.ok)return{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx --package=@buoy-gg/core buoy login` again."};if(n>=o.claims.exp*1e3){return{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx --package=@buoy-gg/core buoy login` again."}}return{ok:true,claims:o.claims}}const DEV_TOKEN_IN_RELEASE=exports.DEV_TOKEN_IN_RELEASE="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";
@@ -12,6 +12,6 @@
12
12
  * which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */function useLicense(){const[t,e]=(0,_react.useState)(_LicenseManager.LicenseManager.getState());(0,_react.useEffect)(()=>{const s=_LicenseManager.LicenseManager.subscribe(a=>{e(_LicenseManager.LicenseManager.getState())});_LicenseManager.LicenseManager.initialize().then(()=>{e(_LicenseManager.LicenseManager.getState())});return s},[]);const r=(0,_react.useCallback)(async s=>{const a=await _LicenseManager.LicenseManager.setLicenseKey(s,{force:true});e(_LicenseManager.LicenseManager.getState());return a},[]);const n=(0,_react.useCallback)(async()=>{await _LicenseManager.LicenseManager.clearLicense();e(_LicenseManager.LicenseManager.getState())},[]);return{...t,setLicenseKey:r,clearLicense:n}}function useAccountAccess(){return useLicense().access}function useTier(){const{tier:t,isInitialized:e}=useLicense();return e?t:"free"}function useFeatureAccess(t){const{isPro:e,isInitialized:r,isValidating:n}=useLicense();const s=e;const a=!r||n;return{hasAccess:s,isPro:e,isLoading:a}}function useIsPro(){const{isPro:t,isInitialized:e,access:r}=useLicense();const n=(0,_weekendPro.weekendPassAppliesTo)(r.status==="admitted",(0,_weekendPro.useWeekendFreePro)());return e&&t||n}function useProAccess(){const{isPro:t,isInitialized:e,access:r}=useLicense();const n=e&&t;const s=(0,_weekendPro.weekendPassAppliesTo)(r.status==="admitted",(0,_weekendPro.useWeekendFreePro)())&&!n;const a=n?"licensed":s?"weekend":null;return{isPro:n||s,isLicensed:n,isWeekendFree:s,reason:a}}function useBuoySession(){const[t,e]=(0,_react.useState)(()=>_sessionManager.SessionManager.getStatus());(0,_react.useEffect)(()=>{const s=()=>e(_sessionManager.SessionManager.getStatus());const a=_sessionManager.SessionManager.subscribe(s);_sessionManager.SessionManager.load().then(s,s);return a},[]);const r=(0,_react.useCallback)(()=>_sessionManager.SessionManager.signIn(),[]);const n=(0,_react.useCallback)(()=>_sessionManager.SessionManager.signOut(),[]);return{status:t,signIn:r,signOut:n}}
@@ -13,6 +13,6 @@
13
13
  * copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */async function setLicenseKey(r){const{LicenseManager:t}=await Promise.resolve().then(()=>_interopRequireWildcard(require("./LicenseManager")));await t.initialize();return t.setLicenseKey(r)}function weekendPassActive(){const{isWeekendFreePro:r,weekendPassAppliesTo:t}=require("./weekendPro");const{LicenseManager:u}=require("./LicenseManager");return t(u.hasAccountAccess(),r())}function isPro(){const{LicenseManager:r}=require("./LicenseManager");if(r.isPro())return true;return weekendPassActive()}function hasEntitlement(r){const{LicenseManager:t}=require("./LicenseManager");if(t.hasEntitlement(r))return true;return weekendPassActive()}
@@ -13,6 +13,6 @@
13
13
  * copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */async function setLicenseKey(n){const{LicenseManager:i}=await Promise.resolve().then(()=>_interopRequireWildcard(require("./LicenseManager")));await i.initialize();return i.setLicenseKey(n)}function weekendPassActive(){return(0,_weekendPro.weekendPassAppliesTo)(_LicenseManager.LicenseManager.hasAccountAccess(),(0,_weekendPro.isWeekendFreePro)())}function isPro(){if(_LicenseManager.LicenseManager.isPro())return true;return weekendPassActive()}function hasEntitlement(n){if(_LicenseManager.LicenseManager.hasEntitlement(n))return true;return weekendPassActive()}
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
5
  * (https://buoy.gg/terms), not an OSS license.
6
- */const DESKTOP_AUDIENCE=exports.DESKTOP_AUDIENCE="app:gg.buoy.desktop";const CLI_AUDIENCE=exports.CLI_AUDIENCE="app:gg.buoy.cli";const WEB_EXTENSION_AUDIENCE=exports.WEB_EXTENSION_AUDIENCE="app:gg.buoy.web";const LOGIN_TIMEOUT_MS=5*60*1e3;const MAX_BODY_BYTES=4096;const LOCK_STALE_MS=6e4;const LOCK_WAIT_MS=3e4;const STATUS_TIMEOUT_MS=8e3;const DEV_TOKEN_TIMEOUT_MS=15e3;const TLS_INTERCEPT_CODES=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","CERT_UNTRUSTED"]);function describeFetchError(e){const r=e?.cause;if(r&&typeof r.code==="string")return r.code;if(e?.name==="TimeoutError")return"timed out";if(r&&typeof r.message==="string")return r.message;return e instanceof Error?e.message:String(e)}function defaultSessionFile(){return process.env.BUOY_SESSION_FILE||(0,_nodePath.join)((0,_nodeOs.homedir)(),".buoy","session.json")}function fileSessionStorage(e){const r=async()=>{try{const o=JSON.parse(await _nodeFs.promises.readFile(e,"utf8"));return o&&typeof o==="object"?o:{}}catch{return{}}};const u=async o=>{await _nodeFs.promises.mkdir((0,_nodePath.dirname)(e),{recursive:true,mode:448});if(Object.keys(o).length===0){await _nodeFs.promises.rm(e,{force:true});return}const i=`${e}.${process.pid}.${(0,_nodeCrypto.randomBytes)(4).toString("hex")}.tmp`;await _nodeFs.promises.writeFile(i,JSON.stringify(o),{mode:384});await _nodeFs.promises.rename(i,e)};return{async getItem(o){return(await r())[o]??null},async setItem(o,i){const d=await r();d[o]=i;await u(d)},async removeItem(o){const i=await r();if(!(o in i))return;delete i[o];await u(i)}}}function fileLock(e,r={}){const u=r.staleMs??LOCK_STALE_MS;const o=r.waitMs??LOCK_WAIT_MS;return async(i,d)=>{const a=`${process.pid}:${(0,_nodeCrypto.randomBytes)(8).toString("hex")}`;const p=Date.now()+o;await _nodeFs.promises.mkdir((0,_nodePath.dirname)(e),{recursive:true,mode:448});for(;;){try{await _nodeFs.promises.writeFile(e,a,{flag:"wx",mode:384});break}catch(t){if(t.code!=="EEXIST")throw t;const s=await _nodeFs.promises.stat(e).catch(()=>null);if(s&&Date.now()-s.mtimeMs>u){await _nodeFs.promises.rm(e,{force:true});continue}if(Date.now()>p)throw new Error("Another Buoy process holds the sign-in lock.");await new Promise(n=>setTimeout(n,50))}}try{return await d()}finally{const t=await _nodeFs.promises.readFile(e,"utf8").catch(()=>null);if(t===a)await _nodeFs.promises.rm(e,{force:true})}}}function safeEqual(e,r){const u=Buffer.from(e);const o=Buffer.from(r);return u.length===o.length&&(0,_nodeCrypto.timingSafeEqual)(u,o)}async function receiveCodeOnLoopback(e,r,u,o={}){let i;let d;const a=new Promise((c,l)=>{i=c;d=l});a.catch(()=>{});const p=_nodeHttp.default.createServer((c,l)=>{if(c.headers.origin===u){l.setHeader("Access-Control-Allow-Origin",u);l.setHeader("Access-Control-Allow-Headers","Content-Type");l.setHeader("Access-Control-Allow-Methods","POST, OPTIONS");l.setHeader("Access-Control-Allow-Private-Network","true")}if(c.method==="OPTIONS")return void l.writeHead(204).end();if(c.method!=="POST")return void l.writeHead(405).end();let g="";c.on("data",E=>{g+=E;if(g.length>MAX_BODY_BYTES)c.destroy()});c.on("end",()=>{let E=null;try{E=JSON.parse(g)}catch{}if(!E||typeof E.state!=="string"||!safeEqual(E.state,r))return void l.writeHead(400).end();l.writeHead(200,{"Content-Type":"application/json"}).end('{"ok":true}');if(typeof E.code==="string"&&E.code)n(()=>i(E.code));else n(()=>d(new _sessionManager.SessionError("access_denied","Sign-in was cancelled.")))})});const t=setTimeout(()=>n(()=>d(new _sessionManager.SessionError("timeout","Sign-in took too long."))),o.timeoutMs??LOGIN_TIMEOUT_MS);t.unref?.();const s=()=>n(()=>d(new _sessionManager.SessionError("popup_closed","Sign-in was cancelled.")));o.signal?.addEventListener("abort",s);function n(c){clearTimeout(t);o.signal?.removeEventListener("abort",s);p.close();c()}await new Promise((c,l)=>{p.once("error",l);p.listen(0,"127.0.0.1",()=>c())});const f=p.address();if(!f||typeof f==="string"){n(()=>d(new Error("Could not open a local port")));throw new Error("Could not open a local port")}return{url:`${e}&port=${f.port}`,code:a}}function createNodeSession(e){const r=defaultSessionFile();const u=e.storage??fileSessionStorage(r);const o=(e.signinOrigin??process.env.BUOY_SITE_URL??"https://buoy.gg").replace(/\/$/,"");const i=e.fetch??((t,s)=>fetch(t,s));const d={fetch:i,storage:async()=>u,now:e.now??(()=>Date.now()),random:t=>new Uint8Array((0,_nodeCrypto.randomBytes)(t)),sha256:async t=>new Uint8Array((0,_nodeCrypto.createHash)("sha256").update(t).digest()),openPopup:()=>null,onMessage:()=>()=>{},withLock:fileLock(e.lockFile??`${r}.lock`),setTimer:(t,s)=>{const n=setTimeout(t,Math.min(s,2147483647));n.unref?.();return n},clearTimer:t=>clearTimeout(t)};const a=new _sessionManager.SessionManagerImpl(d);a.configure({audience:e.audience,signinOrigin:o});const p=async()=>{let t;try{t=await i(`${o}/api/auth/status`,{method:"GET",headers:{},signal:AbortSignal.timeout(STATUS_TIMEOUT_MS)})}catch(n){const f=describeFetchError(n);return TLS_INTERCEPT_CODES.has(f)?{state:"unreachable",detail:f,hint:"Your network may re-sign secure traffic. Point NODE_EXTRA_CA_CERTS at your company's root certificate."}:{state:"unreachable",detail:f}}const s=await t.json().catch(()=>null);if(t.status===200&&s?.enabled===true)return{state:"enabled"};if(t.status===200&&s?.enabled===false)return{state:"disabled"};return{state:"unreachable",detail:s?`HTTP ${t.status}`:`HTTP ${t.status}, not a Buoy answer`}};return{manager:a,async load(){await a.load();return a.getStatus()},getStatus:()=>a.getStatus(),signIn:(t={})=>a.signInWith(async(s,n)=>{const{url:f,code:c}=await receiveCodeOnLoopback(s,n,o,t);await e.openBrowser(f);return c}),signOut:()=>a.signOut(),refresh:t=>a.refresh(t),sync:()=>a.sync(),async devToken(){const t=a.getAccessToken();if(!t)return{ok:false,detail:"This computer is not signed in. Run `npx buoy login`."};try{const s=await i(`${o}/api/auth/dev-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:t}),signal:AbortSignal.timeout(DEV_TOKEN_TIMEOUT_MS)});const n=await s.json().catch(()=>null);if(s.status===200&&typeof n?.dev_token==="string"){return{ok:true,token:n.dev_token,tier:typeof n.tier==="string"?n.tier:"free"}}return{ok:false,detail:typeof n?.error_description==="string"?n.error_description:"buoy.gg did not make a dev token."}}catch{return{ok:false,detail:"Buoy could not be reached. Check your connection and try again."}}},checkAvailability:p,async isAvailable(){return(await p()).state==="enabled"},subscribe:t=>a.subscribe(t)}}
6
+ */const DESKTOP_AUDIENCE=exports.DESKTOP_AUDIENCE="app:gg.buoy.desktop";const CLI_AUDIENCE=exports.CLI_AUDIENCE="app:gg.buoy.cli";const WEB_EXTENSION_AUDIENCE=exports.WEB_EXTENSION_AUDIENCE="app:gg.buoy.web";const LOGIN_TIMEOUT_MS=5*60*1e3;const MAX_BODY_BYTES=4096;const LOCK_STALE_MS=6e4;const LOCK_WAIT_MS=3e4;const STATUS_TIMEOUT_MS=8e3;const DEV_TOKEN_TIMEOUT_MS=15e3;const TLS_INTERCEPT_CODES=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","CERT_UNTRUSTED"]);function describeFetchError(e){const n=e?.cause;if(n&&typeof n.code==="string")return n.code;if(e?.name==="TimeoutError")return"timed out";if(n&&typeof n.message==="string")return n.message;return e instanceof Error?e.message:String(e)}function defaultSessionFile(){return process.env.BUOY_SESSION_FILE||(0,_nodePath.join)((0,_nodeOs.homedir)(),".buoy","session.json")}function fileSessionStorage(e){const n=async()=>{try{const o=JSON.parse(await _nodeFs.promises.readFile(e,"utf8"));return o&&typeof o==="object"?o:{}}catch{return{}}};const u=async o=>{await _nodeFs.promises.mkdir((0,_nodePath.dirname)(e),{recursive:true,mode:448});if(Object.keys(o).length===0){await _nodeFs.promises.rm(e,{force:true});return}const i=`${e}.${process.pid}.${(0,_nodeCrypto.randomBytes)(4).toString("hex")}.tmp`;await _nodeFs.promises.writeFile(i,JSON.stringify(o),{mode:384});await _nodeFs.promises.rename(i,e)};return{async getItem(o){return(await n())[o]??null},async setItem(o,i){const d=await n();d[o]=i;await u(d)},async removeItem(o){const i=await n();if(!(o in i))return;delete i[o];await u(i)}}}function fileLock(e,n={}){const u=n.staleMs??LOCK_STALE_MS;const o=n.waitMs??LOCK_WAIT_MS;return async(i,d)=>{const a=`${process.pid}:${(0,_nodeCrypto.randomBytes)(8).toString("hex")}`;const p=Date.now()+o;await _nodeFs.promises.mkdir((0,_nodePath.dirname)(e),{recursive:true,mode:448});for(;;){try{await _nodeFs.promises.writeFile(e,a,{flag:"wx",mode:384});break}catch(t){if(t.code!=="EEXIST")throw t;const s=await _nodeFs.promises.stat(e).catch(()=>null);if(s&&Date.now()-s.mtimeMs>u){await _nodeFs.promises.rm(e,{force:true});continue}if(Date.now()>p)throw new Error("Another Buoy process holds the sign-in lock.");await new Promise(r=>setTimeout(r,50))}}try{return await d()}finally{const t=await _nodeFs.promises.readFile(e,"utf8").catch(()=>null);if(t===a)await _nodeFs.promises.rm(e,{force:true})}}}function safeEqual(e,n){const u=Buffer.from(e);const o=Buffer.from(n);return u.length===o.length&&(0,_nodeCrypto.timingSafeEqual)(u,o)}async function receiveCodeOnLoopback(e,n,u,o={}){let i;let d;const a=new Promise((c,l)=>{i=c;d=l});a.catch(()=>{});const p=_nodeHttp.default.createServer((c,l)=>{if(c.headers.origin===u){l.setHeader("Access-Control-Allow-Origin",u);l.setHeader("Access-Control-Allow-Headers","Content-Type");l.setHeader("Access-Control-Allow-Methods","POST, OPTIONS");l.setHeader("Access-Control-Allow-Private-Network","true")}if(c.method==="OPTIONS")return void l.writeHead(204).end();if(c.method!=="POST")return void l.writeHead(405).end();let g="";c.on("data",E=>{g+=E;if(g.length>MAX_BODY_BYTES)c.destroy()});c.on("end",()=>{let E=null;try{E=JSON.parse(g)}catch{}if(!E||typeof E.state!=="string"||!safeEqual(E.state,n))return void l.writeHead(400).end();l.writeHead(200,{"Content-Type":"application/json"}).end('{"ok":true}');if(typeof E.code==="string"&&E.code)r(()=>i(E.code));else r(()=>d(new _sessionManager.SessionError("access_denied","Sign-in was cancelled.")))})});const t=setTimeout(()=>r(()=>d(new _sessionManager.SessionError("timeout","Sign-in took too long."))),o.timeoutMs??LOGIN_TIMEOUT_MS);t.unref?.();const s=()=>r(()=>d(new _sessionManager.SessionError("popup_closed","Sign-in was cancelled.")));o.signal?.addEventListener("abort",s);function r(c){clearTimeout(t);o.signal?.removeEventListener("abort",s);p.close();c()}await new Promise((c,l)=>{p.once("error",l);p.listen(0,"127.0.0.1",()=>c())});const f=p.address();if(!f||typeof f==="string"){r(()=>d(new Error("Could not open a local port")));throw new Error("Could not open a local port")}return{url:`${e}&port=${f.port}`,code:a}}function createNodeSession(e){const n=defaultSessionFile();const u=e.storage??fileSessionStorage(n);const o=(e.signinOrigin??process.env.BUOY_SITE_URL??"https://buoy.gg").replace(/\/$/,"");const i=e.fetch??((t,s)=>fetch(t,s));const d={fetch:i,storage:async()=>u,now:e.now??(()=>Date.now()),random:t=>new Uint8Array((0,_nodeCrypto.randomBytes)(t)),sha256:async t=>new Uint8Array((0,_nodeCrypto.createHash)("sha256").update(t).digest()),openPopup:()=>null,onMessage:()=>()=>{},withLock:fileLock(e.lockFile??`${n}.lock`),setTimer:(t,s)=>{const r=setTimeout(t,Math.min(s,2147483647));r.unref?.();return r},clearTimer:t=>clearTimeout(t)};const a=new _sessionManager.SessionManagerImpl(d);a.configure({audience:e.audience,signinOrigin:o});const p=async()=>{let t;try{t=await i(`${o}/api/auth/status`,{method:"GET",headers:{},signal:AbortSignal.timeout(STATUS_TIMEOUT_MS)})}catch(r){const f=describeFetchError(r);return TLS_INTERCEPT_CODES.has(f)?{state:"unreachable",detail:f,hint:"Your network may re-sign secure traffic. Point NODE_EXTRA_CA_CERTS at your company's root certificate."}:{state:"unreachable",detail:f}}const s=await t.json().catch(()=>null);if(t.status===200&&s?.enabled===true)return{state:"enabled"};if(t.status===200&&s?.enabled===false)return{state:"disabled"};return{state:"unreachable",detail:s?`HTTP ${t.status}`:`HTTP ${t.status}, not a Buoy answer`}};return{manager:a,async load(){await a.load();return a.getStatus()},getStatus:()=>a.getStatus(),signIn:(t={})=>a.signInWith(async(s,r)=>{const{url:f,code:c}=await receiveCodeOnLoopback(s,r,o,t);await e.openBrowser(f);return c}),signOut:()=>a.signOut(),refresh:t=>a.refresh(t),sync:()=>a.sync(),async devToken(){const t=a.getAccessToken();if(!t)return{ok:false,detail:"This computer is not signed in. Run `npx --package=@buoy-gg/core buoy login`."};try{const s=await i(`${o}/api/auth/dev-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:t}),signal:AbortSignal.timeout(DEV_TOKEN_TIMEOUT_MS)});const r=await s.json().catch(()=>null);if(s.status===200&&typeof r?.dev_token==="string"){return{ok:true,token:r.dev_token,tier:typeof r.tier==="string"?r.tier:"free"}}return{ok:false,detail:typeof r?.error_description==="string"?r.error_description:"buoy.gg did not make a dev token."}}catch{return{ok:false,detail:"Buoy could not be reached. Check your connection and try again."}}},checkAvailability:p,async isAvailable(){return(await p()).state==="enabled"},subscribe:t=>a.subscribe(t)}}
@@ -12,6 +12,6 @@
12
12
  * applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */function resolveTier(e){const r=e.relationships?.policy?.data?.id;if(r===_config.KEYGEN_POLICIES.free)return"free";if(r===_config.KEYGEN_POLICIES.solo||r===_config.KEYGEN_POLICIES.business){return"pro"}const t=e.attributes?.metadata?.plan;if(t==="free")return"free";return"pro"}function tierAfterFatalError(e){return e==="EXPIRED"?"free":null}function isRecognizedAccount(e){if(!e)return false;const r=e.relationships?.product?.data?.id;if(r)return r===_config.KEYGEN_CONFIG.productId;const t=e.relationships?.policy?.data?.id;return Object.values(_config.KEYGEN_POLICIES).some(o=>o===t)}
@@ -13,6 +13,6 @@
13
13
  * whether or not any code is copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */function isWeekendFreePro(r=new Date((0,_realNow.realNow)())){if(globalThis.__buoyWeekendPassOff===true)return false;const e=r.getDay();return e===0||e===6}function useWeekendFreePro(){const[r,e]=(0,_react.useState)(()=>isWeekendFreePro());(0,_react.useEffect)(()=>{const n=setInterval(()=>{const t=isWeekendFreePro();e(s=>s===t?s:t)},60*1e3);return()=>clearInterval(n)},[]);return r}function weekendPassAppliesTo(r,e){return e&&r}
@@ -11,6 +11,6 @@
11
11
  * 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
- */import{getLicenseStorage as y}from"./storage";import{KEYGEN_CONFIG as B,LICENSE_STORAGE_KEY as N,LICENSE_CACHE_DURATION as b,LICENSE_REVALIDATION_INTERVAL as X,LICENSE_LAUNCH_VALIDATION_TTL as q,LICENSE_TRANSIENT_RETRY_DELAYS as J,LICENSE_RATE_LIMIT_DEFAULT_WAIT as Q,LICENSE_QUOTA_COOLDOWN as F,LICENSE_VERDICT_CACHE_DURATION as W,LICENSE_FLEET_MACHINE_THRESHOLD as j,LICENSE_QUIET_VALIDATION_TTL as $,LICENSE_HOLD_STORAGE_KEY as C,DEVICE_FINGERPRINT_STORAGE_KEY as K}from"./config.js";import{validateLicense as Z,activateMachine as O,parseValidationResult as ee,isFatalLicenseError as te,isTransientLicenseError as ie,isAcceptedValidation as se,isRateLimited as re,backoffAfter as ae,machineCount as ne}from"./api.js";import{generateUUID as P,getDeviceMetadata as G,getDefaultDeviceName as V,isDevBundle as w}from"./fingerprint";import{signCache as U,verifyCacheSignature as p}from"./cacheSignature.js";import{isBuoyToken as ce,verifyFreeToken as he}from"./freeToken.js";import{resolveTier as oe,isRecognizedAccount as le}from"./tier.js";import{resolveAccountAccess as de}from"./accountAccess.js";import{subscribeForeground as fe}from"./foreground";import{realNow as c}from"./realNow.js";import{SessionManager as g}from"./sessionManager.js";import{exchangeServiceKey as ue,isServiceKey as ye,normalizeLicenseKey as z}from"./serviceKey.js";import{checkDevToken as me,isDevToken as pe,DEV_TOKEN_IN_RELEASE as _e}from"./devToken.js";async function d(f,e){let t;try{return await Promise.race([f,new Promise(i=>{t=setTimeout(()=>i(e),15e3)})])}finally{if(t)clearTimeout(t)}}function _(f){if(f.tier==="pro"||f.tier==="free")return f.tier;return f.isPro?"pro":"free"}class ge{state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};listeners=new Set;revalidationTimer=null;cachedLicense=null;fingerprintPromise=null;initPromise=null;generation=0;persistence=Promise.resolve();verifiedAt=null;expiryTimer=null;removeForegroundListener=null;sessionUnsubscribe=null;retryTimer=null;retryAttempt=0;deviceRegistrationCallback=null;pendingRegistration=null;async initialize(){if(this.initPromise)return this.initPromise;if(this.state.isInitialized)return;this.initPromise=this._doInitialize();return this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;const e=this.generation;if(!this.sessionUnsubscribe){this.sessionUnsubscribe=g.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))}await d(g.load().catch(()=>void 0),void 0);try{const t=await d(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){const i=p(t);this.cachedLicense=t;this.state.licenseKey=t.licenseKey;if(i&&this.isCacheValid(t))this.applyTier(_(t));else this.lock();this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null;if(i&&this.isCacheValid(t))this.setVerifiedAt(t.cachedAt);if(this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))){this.validateInBackground(t.licenseKey)}}this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer()}catch(t){console.warn("[Buoy]","[React Buoy] License initialization failed:",t);this.state.isInitialized=true;this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0){await this.initialize();return this.hasAccountAccess()}const t=z(e);const i=++this.generation;this.cancelRetry();this.configuredKey=t||null;if(this.state.licenseKey!==this.configuredKey)this.lock();this.state.licenseKey=this.configuredKey;this.state.isValidating=!!t;this.emit({type:"validating",isPro:this.isPro()});await this.initialize();if(i!==this.generation)return false;if(!t){await this.clearLicense();return false}return this.setLicenseKey(t)}async setLicenseKey(e,t){if(!e||e.trim().length===0){this.state.error="License key is required";this.state.errorKind="verdict";return false}const i=z(e);const s=++this.generation;this.cancelRetry();if(!t?.isRetry)this.retryAttempt=0;if(this.state.licenseKey!==i)this.lock();this.state.licenseKey=i;this.state.isValidating=true;this.state.error=null;this.state.errorKind=null;this.emit({type:"validating",isPro:this.isPro()});if(ce(i))return this.admitToken(i,s);if(ye(i))return this.admitServiceKey(i,s);if(pe(i))return this.admitDevToken(i);if(!this.cachedLicense){const a=await d(this.loadCachedLicense(),null);if(s!==this.generation)return false;if(a?.licenseKey===i){this.cachedLicense=a;if(p(a)&&this.isCacheValid(a)){this.applyTier(_(a));this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null;this.setVerifiedAt(a.cachedAt);this.emit({type:"validating",isPro:this.isPro()})}}}const r=this.cachedLicense;if(!t?.force&&r&&r.valid&&r.licenseKey===i&&r.fingerprint&&p(r)&&this.isCacheValid(r)&&this.isCacheFresh(r)){this.applyTier(_(r));this.state.licenseKey=i;this.state.isValidating=false;this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);this.emit({type:"validated",isPro:this.state.isPro});return true}const h=await d(this.loadHold(),null);if(s!==this.generation)return false;const A=(h?.backoffUntil??0)-c();if(A>0){return this.standDown(i,s,"License server is rate limited; retrying later.",A)}if(!t?.force&&h?.key===i&&h.verdictAt!==void 0&&c()-h.verdictAt>=0&&c()-h.verdictAt<W){this.lock();this.state.isValidating=false;this.state.error=h.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});return false}let n;try{n=await Z(i)}catch(a){const l=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;if(ie(a)){if(re(a)){const u=Math.min(ae(a,F,Q)*2**this.retryAttempt,F);this.retryAttempt+=1;await this.saveHold({...this.hold,backoffUntil:c()+u});if(s!==this.generation)return false;return this.standDown(i,s,l,u)}this.state.isValidating=false;if(this.cachedLicense&&this.cachedLicense.licenseKey===i&&p(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)){this.applyTier(_(this.cachedLicense));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null;this.setVerifiedAt(this.cachedLicense.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetry(i);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=l;this.state.errorKind="transport";this.scheduleRetry(i);this.emit({type:"error",isPro:this.state.isPro,error:l});return false}this.state.isValidating=false;this.state.error=l;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:l});return false}try{if(s!==this.generation)return false;const a=ee(n);const l=n.data?.relationships?.product?.data?.id;if((!l||l===B.productId)&&a.code==="EXPIRED"&&(le(n.data)||this.cachedLicense?.licenseKey===i&&p(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){const R=c();const M=await this.saveCache({licenseKey:i,licenseId:n.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:R,lastValidatedAt:R,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},s);if(s!==this.generation||!M)return false;this.cachedLicense=M;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(R);this.emit({type:"validated",isPro:false});return true}if(te(a.code)||!se(n)){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:c()});return false}if(!n.data){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License not found";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const u=oe(n.data);const v=n.data.id;const T=c();const D=ne(n)>=j;const S=w();const H=S&&!D;const Y=u==="free"&&(D||!S);let m=this.cachedLicense?.fingerprint;let E=this.cachedLicense?.deviceName;let k=false;if(!m){m=await d(this.getOrCreateDeviceFingerprint(),P());if(s!==this.generation)return false;E=V();k=H}let L=false;if(k){if(this.deviceRegistrationCallback){this.pendingRegistration={licenseId:v,licenseKey:i,fingerprint:m};this.deviceRegistrationCallback({fingerprint:m,defaultName:E});L=true}else{this.registerDeviceAsync(v,i,m,E)}}const I={licenseKey:i,licenseId:v,valid:true,isPro:u==="pro",tier:u,expiresAt:n.data.attributes.expiry??null,cachedAt:T,lastValidatedAt:T,fingerprint:L?void 0:m,deviceName:L?void 0:E,quiet:Y||void 0};const x=await this.saveCache(I,s);if(s!==this.generation||!x)return false;this.cachedLicense=x;this.applyTier(u);this.state.isValidating=false;this.state.expiresAt=I.expiresAt?new Date(I.expiresAt):null;this.setVerifiedAt(T);if(this.hold)await this.saveHold(null);if(s!==this.generation)return false;this.retryAttempt=0;this.emit({type:"validated",isPro:this.state.isPro});return true}catch(a){const l=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;this.state.isValidating=false;this.state.error=l;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:l});return false}}scheduleRetry(e){this.cancelRetry();const t=J[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},t);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}}async admitServiceKey(e,t){const i=await ue(e,{origin:g.origin});if(t!==this.generation)return false;if(!i.ok){this.lock();this.state.isValidating=false;this.state.error=i.detail;this.state.errorKind=i.reason==="network"?"transport":"verdict";this.emit({type:"error",isPro:false,error:i.detail});if(i.reason==="network")this.scheduleRetry(e);return false}const s=i.claims.exp*1e3;this.applyTier(i.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(s);this.setVerifiedAt(c());this.retryAttempt=0;this.scheduleRetryIn(e,Math.max(6e4,s-60*60*1e3-c()));this.emit({type:"validated",isPro:this.isPro()});return true}admitDevToken(e){const t=me(e);if(!t.ok||!w()){const s=t.ok?_e:t.message;this.lock();this.state.isValidating=false;this.state.error=s;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:s});return false}const i=c();this.applyTier(t.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(t.claims.exp*1e3);this.setVerifiedAt(Math.min(i,t.claims.iat*1e3));this.retryAttempt=0;this.emit({type:"validated",isPro:this.isPro()});return true}async admitToken(e,t){const i=he(e);if(t!==this.generation)return false;if(!i){this.lock();this.state.isValidating=false;this.state.error="This key is not valid. Check that it was copied completely.";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const s=c();const r=this.cachedLicense?.fingerprint??await d(this.getOrCreateDeviceFingerprint(),P());if(t!==this.generation)return false;const h=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:s,lastValidatedAt:s,fingerprint:r,deviceName:this.cachedLicense?.deviceName??V(),quiet:true},t);if(t!==this.generation||!h)return false;this.cachedLicense=h;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(s);this.retryAttempt=0;this.emit({type:"validated",isPro:false});return true}standDown(e,t,i,s){if(t!==this.generation)return false;this.state.isValidating=false;const r=this.cachedLicense;if(r&&r.licenseKey===e&&p(r)&&this.isCacheValid(r)){this.applyTier(_(r));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetryIn(e,s);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=i;this.state.errorKind="transport";this.scheduleRetryIn(e,s);this.emit({type:"error",isPro:this.state.isPro,error:i});return false}scheduleRetryIn(e,t){this.cancelRetry();const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},Math.max(1,Math.min(t,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)o.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}cancelRetry(){if(this.retryTimer){clearTimeout(this.retryTimer);this.retryTimer=null}}async clearLicense(){this.generation+=1;this.cancelRetry();this.retryAttempt=0;this.configuredKey=null;this.state.licenseKey=null;this.lock();this.state.expiresAt=null;this.state.error=null;this.state.errorKind=null;this.cachedLicense=null;this.pendingRegistration=null;this.state.isValidating=false;this.emit({type:"deactivated",isPro:false});await this.clearCachedLicense();if(this.hold?.verdictAt!==void 0){await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}}applyTier(e){this.state.tier=e;this.state.tierSource="key";this.state.isPro=e==="pro"}lock(){this.state.tier="free";this.state.tierSource="none";this.state.isPro=false;this.verifiedAt=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null}getTier(){return this.credential().tier}credential(){const e=this.keyTier();const t=this.sessionTier();if(t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")){return{source:"session",tier:t}}return{source:this.state.tierSource,tier:e}}keyTier(){if(!this.hasKeyAccess())return"free";if(this.state.expiresAt&&c()>=this.state.expiresAt.getTime())return"free";return this.state.tier}sessionTier(){const e=g.getStatus();if(e.state!=="active")return null;return e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){const{source:e,tier:t}=this.credential();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess()}}getAccountAccess(){const e=this.getKeyAccess();if(e.status==="admitted")return e;const t=g.getStatus();if(t.state==="active")return{status:"admitted",reason:null,validUntil:t.validUntil};return e}getKeyAccess(){return de({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e;if(this.expiryTimer)clearTimeout(this.expiryTimer);const t=e+b;const i=this.state.expiresAt?.getTime();const s=i&&i>c()?Math.min(t,i):t;const r=s-c();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null;if(this.verifiedAt!==e)return;this.emit({type:"validated",isPro:this.isPro()});if(c()<t)this.setVerifiedAt(e)},Math.max(1,Math.min(r,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)o.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){this.deviceRegistrationCallback=e;return()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}const{licenseId:t,licenseKey:i,fingerprint:s}=this.pendingRegistration;const r=this.generation;const h=e.trim()||V();const A=G();try{await O(t,i,s,h,A)}catch(n){const a=n instanceof Error?n.message:String(n);if(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"){}else{console.warn("[Buoy] Device registration failed:",a);throw n}}if(r!==this.generation)return;if(this.cachedLicense?.licenseKey===i){const n=await this.saveCache({...this.cachedLicense,fingerprint:s,deviceName:h},r);if(r!==this.generation)return;if(n)this.cachedLicense=n}this.pendingRegistration=null}emit(e){const t=[...this.listeners];t.forEach(i=>{try{i(e)}catch(s){console.warn("[Buoy]","[React Buoy] License listener error:",s)}})}isCacheValid(e){const t=c();const i=t-e.cachedAt;if(!e.valid||!Number.isFinite(e.cachedAt)||i<0||i>=b){return false}return true}isCacheFresh(e){const t=this.isQuiet(e)?$:q;return c()-(e.lastValidatedAt??0)<t}isQuiet(e){if(e.quiet!==void 0)return e.quiet;return _(e)==="free"&&!w()}async loadHold(){if(this.hold!==void 0)return this.hold;try{const e=await y();const t=await e.getItem(C);const i=t?JSON.parse(t):null;this.hold=i&&typeof i==="object"?i:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{const t=await y();if(e)await t.setItem(C,JSON.stringify(e));else await t.removeItem(C)}catch{}}async persistCache(e){const t=U(e);const i=await y();await i.setItem(N,JSON.stringify(t));return t}saveCache(e,t){const i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;const s=await this.persistCache(e);return t===this.generation?s:null});this.persistence=i;return d(i,U(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});const e=await y();const t=await e.getItem(N);if(!t)return null;const i=JSON.parse(t);return i}catch(e){console.warn("[Buoy] Error loading cached license:",e);return null}}getOrCreateDeviceFingerprint(){if(!this.fingerprintPromise){this.fingerprintPromise=(async()=>{const e=await y();const t=await e.getItem(K);if(t)return t;const i=this.cachedLicense?.fingerprint||P();await e.setItem(K,i);return i})()}return this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;const e=this.persistence.catch(()=>{}).then(async()=>{const t=await y();await t.removeItem(N)});this.persistence=e;await d(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,s){const r=G();O(e,t,i,s,r).catch(h=>{if(h.message!=="FINGERPRINT_TAKEN"&&h.message!=="MACHINE_LIMIT_EXCEEDED"){console.warn("[Buoy] Device registration failed (non-blocking):",h.message)}})}validateInBackground(e){void this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){if(!this.removeForegroundListener){this.removeForegroundListener=fe(()=>this.refreshAccess());if(typeof __DEV__!=="undefined"&&__DEV__)o.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener}if(this.revalidationTimer){clearInterval(this.revalidationTimer)}this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;const e=this.cachedLicense;if(e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e))return;this.validateInBackground(this.state.licenseKey)},X);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_TIMER__=this.revalidationTimer}}stopRevalidationTimer(){this.removeForegroundListener?.();this.removeForegroundListener=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null;if(this.revalidationTimer){clearInterval(this.revalidationTimer);this.revalidationTimer=null}this.cancelRetry()}async forceReregister(){if(this.cachedLicense){this.cachedLicense.fingerprint=void 0;this.cachedLicense.deviceName=void 0}this.fingerprintPromise=null;await this.clearCachedLicense();try{const e=await y();await e.removeItem(K)}catch{}}}export const LicenseManager=new ge;const o=globalThis;if(typeof __DEV__!=="undefined"&&__DEV__){o.__LICENSE_MANAGER_FOREGROUND_OFF__?.();o.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0;if(o.__LICENSE_MANAGER_EXPIRY_TIMER__)clearTimeout(o.__LICENSE_MANAGER_EXPIRY_TIMER__);if(o.__LICENSE_MANAGER_TIMER__){clearInterval(o.__LICENSE_MANAGER_TIMER__)}if(o.__LICENSE_MANAGER_RETRY_TIMER__){clearTimeout(o.__LICENSE_MANAGER_RETRY_TIMER__);o.__LICENSE_MANAGER_RETRY_TIMER__=void 0}}
16
+ */import{getLicenseStorage as y}from"./storage";import{KEYGEN_CONFIG as X,LICENSE_STORAGE_KEY as N,LICENSE_CACHE_DURATION as b,LICENSE_REVALIDATION_INTERVAL as q,LICENSE_LAUNCH_VALIDATION_TTL as J,LICENSE_TRANSIENT_RETRY_DELAYS as Q,LICENSE_RATE_LIMIT_DEFAULT_WAIT as W,LICENSE_QUOTA_COOLDOWN as F,LICENSE_VERDICT_CACHE_DURATION as j,LICENSE_FLEET_MACHINE_THRESHOLD as $,LICENSE_QUIET_VALIDATION_TTL as Z,LICENSE_HOLD_STORAGE_KEY as C,DEVICE_FINGERPRINT_STORAGE_KEY as K}from"./config.js";import{validateLicense as ee,activateMachine as O,parseValidationResult as te,isFatalLicenseError as ie,isTransientLicenseError as se,isAcceptedValidation as re,isRateLimited as ae,backoffAfter as ne,machineCount as ce}from"./api.js";import{generateUUID as P,getDeviceMetadata as G,getDefaultDeviceName as V,isDevBundle as w}from"./fingerprint";import{signCache as U,verifyCacheSignature as p}from"./cacheSignature.js";import{isBuoyToken as oe,verifyFreeToken as he}from"./freeToken.js";import{resolveTier as le,isRecognizedAccount as de}from"./tier.js";import{resolveAccountAccess as ue}from"./accountAccess.js";import{subscribeForeground as fe}from"./foreground";import{realNow as c}from"./realNow.js";import{SessionManager as _}from"./sessionManager.js";import{exchangeServiceKey as ye,isServiceKey as me,normalizeLicenseKey as z}from"./serviceKey.js";import{checkDevToken as pe,isDevToken as _e,DEV_TOKEN_IN_RELEASE as ge}from"./devToken.js";const Ae={role:null,tools:null};function H(d){return{role:d.role??null,tools:d.tools??null}}async function u(d,e){let t;try{return await Promise.race([d,new Promise(i=>{t=setTimeout(()=>i(e),15e3)})])}finally{if(t)clearTimeout(t)}}function g(d){if(d.tier==="pro"||d.tier==="free")return d.tier;return d.isPro?"pro":"free"}class Ee{state={isInitialized:false,isValidating:false,isPro:false,tier:"free",tierSource:"none",licenseKey:null,error:null,errorKind:null,expiresAt:null};listeners=new Set;revalidationTimer=null;cachedLicense=null;fingerprintPromise=null;initPromise=null;generation=0;persistence=Promise.resolve();verifiedAt=null;expiryTimer=null;removeForegroundListener=null;sessionUnsubscribe=null;devToken=null;retryTimer=null;retryAttempt=0;deviceRegistrationCallback=null;pendingRegistration=null;async initialize(){if(this.initPromise)return this.initPromise;if(this.state.isInitialized)return;this.initPromise=this._doInitialize();return this.initPromise}async _doInitialize(){if(this.state.isInitialized)return;const e=this.generation;if(!this.sessionUnsubscribe){this.sessionUnsubscribe=_.subscribe(()=>this.emit({type:"validated",isPro:this.isPro()}))}await u(_.load().catch(()=>void 0),void 0);try{const t=await u(this.loadCachedLicense(),null);if(e!==this.generation){this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer();return}if(t&&(this.configuredKey===void 0||t.licenseKey===this.configuredKey)){const i=p(t);this.cachedLicense=t;this.state.licenseKey=t.licenseKey;if(i&&this.isCacheValid(t))this.applyTier(g(t));else this.lock();this.state.expiresAt=t.expiresAt?new Date(t.expiresAt):null;if(i&&this.isCacheValid(t))this.setVerifiedAt(t.cachedAt);if(this.configuredKey===void 0&&t.licenseKey&&!(i&&this.isCacheValid(t)&&this.isCacheFresh(t))){this.validateInBackground(t.licenseKey)}}this.state.isInitialized=true;this.emit({type:"initialized",isPro:this.state.isPro});this.startRevalidationTimer()}catch(t){console.warn("[Buoy]","[React Buoy] License initialization failed:",t);this.state.isInitialized=true;this.emit({type:"initialized",isPro:false})}}async configure(e){if(e===void 0){await this.initialize();return this.hasAccountAccess()}const t=z(e);const i=++this.generation;this.cancelRetry();this.configuredKey=t||null;if(this.state.licenseKey!==this.configuredKey)this.lock();this.state.licenseKey=this.configuredKey;this.state.isValidating=!!t;this.emit({type:"validating",isPro:this.isPro()});await this.initialize();if(i!==this.generation)return false;if(!t){await this.clearLicense();return false}return this.setLicenseKey(t)}async setLicenseKey(e,t){if(!e||e.trim().length===0){this.state.error="License key is required";this.state.errorKind="verdict";return false}const i=z(e);const s=++this.generation;this.cancelRetry();if(!t?.isRetry)this.retryAttempt=0;if(this.state.licenseKey!==i)this.lock();this.state.licenseKey=i;this.state.isValidating=true;this.state.error=null;this.state.errorKind=null;this.emit({type:"validating",isPro:this.isPro()});if(oe(i))return this.admitToken(i,s);if(me(i))return this.admitServiceKey(i,s);if(_e(i))return this.admitDevToken(i);if(!this.cachedLicense){const a=await u(this.loadCachedLicense(),null);if(s!==this.generation)return false;if(a?.licenseKey===i){this.cachedLicense=a;if(p(a)&&this.isCacheValid(a)){this.applyTier(g(a));this.state.expiresAt=a.expiresAt?new Date(a.expiresAt):null;this.setVerifiedAt(a.cachedAt);this.emit({type:"validating",isPro:this.isPro()})}}}const r=this.cachedLicense;if(!t?.force&&r&&r.valid&&r.licenseKey===i&&r.fingerprint&&p(r)&&this.isCacheValid(r)&&this.isCacheFresh(r)){this.applyTier(g(r));this.state.licenseKey=i;this.state.isValidating=false;this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);this.emit({type:"validated",isPro:this.state.isPro});return true}const o=await u(this.loadHold(),null);if(s!==this.generation)return false;const A=(o?.backoffUntil??0)-c();if(A>0){return this.standDown(i,s,"License server is rate limited; retrying later.",A)}if(!t?.force&&o?.key===i&&o.verdictAt!==void 0&&c()-o.verdictAt>=0&&c()-o.verdictAt<j){this.lock();this.state.isValidating=false;this.state.error=o.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});return false}let n;try{n=await ee(i)}catch(a){const l=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;if(se(a)){if(ae(a)){const f=Math.min(ne(a,F,W)*2**this.retryAttempt,F);this.retryAttempt+=1;await this.saveHold({...this.hold,backoffUntil:c()+f});if(s!==this.generation)return false;return this.standDown(i,s,l,f)}this.state.isValidating=false;if(this.cachedLicense&&this.cachedLicense.licenseKey===i&&p(this.cachedLicense)&&this.isCacheValid(this.cachedLicense)){this.applyTier(g(this.cachedLicense));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=this.cachedLicense.expiresAt?new Date(this.cachedLicense.expiresAt):null;this.setVerifiedAt(this.cachedLicense.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetry(i);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=l;this.state.errorKind="transport";this.scheduleRetry(i);this.emit({type:"error",isPro:this.state.isPro,error:l});return false}this.state.isValidating=false;this.state.error=l;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:l});return false}try{if(s!==this.generation)return false;const a=te(n);const l=n.data?.relationships?.product?.data?.id;if((!l||l===X.productId)&&a.code==="EXPIRED"&&(de(n.data)||this.cachedLicense?.licenseKey===i&&p(this.cachedLicense)&&this.isCacheValid(this.cachedLicense))){const I=c();const M=await this.saveCache({licenseKey:i,licenseId:n.data?.id??"",valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:I,lastValidatedAt:I,fingerprint:this.cachedLicense?.licenseKey===i?this.cachedLicense.fingerprint:void 0},s);if(s!==this.generation||!M)return false;this.cachedLicense=M;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(I);this.emit({type:"validated",isPro:false});return true}if(ie(a.code)||!re(n)){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License could not be verified";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();await this.saveHold({key:i,code:a.code,detail:this.state.error,verdictAt:c()});return false}if(!n.data){this.lock();this.state.isValidating=false;this.state.error=a.detail||"License not found";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const f=le(n.data);const v=n.data.id;const T=c();const D=ce(n)>=$;const k=w();const Y=k&&!D;const B=f==="free"&&(D||!k);let m=this.cachedLicense?.fingerprint;let E=this.cachedLicense?.deviceName;let S=false;if(!m){m=await u(this.getOrCreateDeviceFingerprint(),P());if(s!==this.generation)return false;E=V();S=Y}let L=false;if(S){if(this.deviceRegistrationCallback){this.pendingRegistration={licenseId:v,licenseKey:i,fingerprint:m};this.deviceRegistrationCallback({fingerprint:m,defaultName:E});L=true}else{this.registerDeviceAsync(v,i,m,E)}}const R={licenseKey:i,licenseId:v,valid:true,isPro:f==="pro",tier:f,expiresAt:n.data.attributes.expiry??null,cachedAt:T,lastValidatedAt:T,fingerprint:L?void 0:m,deviceName:L?void 0:E,quiet:B||void 0};const x=await this.saveCache(R,s);if(s!==this.generation||!x)return false;this.cachedLicense=x;this.applyTier(f);this.state.isValidating=false;this.state.expiresAt=R.expiresAt?new Date(R.expiresAt):null;this.setVerifiedAt(T);if(this.hold)await this.saveHold(null);if(s!==this.generation)return false;this.retryAttempt=0;this.emit({type:"validated",isPro:this.state.isPro});return true}catch(a){const l=a instanceof Error?a.message:"Validation failed";if(s!==this.generation)return false;this.state.isValidating=false;this.state.error=l;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:l});return false}}scheduleRetry(e){this.cancelRetry();const t=Q[this.retryAttempt];if(t===void 0)return;this.retryAttempt+=1;const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},t);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}}async admitServiceKey(e,t){const i=await ye(e,{origin:_.origin});if(t!==this.generation)return false;if(!i.ok){this.lock();this.state.isValidating=false;this.state.error=i.detail;this.state.errorKind=i.reason==="network"?"transport":"verdict";this.emit({type:"error",isPro:false,error:i.detail});if(i.reason==="network")this.scheduleRetry(e);return false}const s=i.claims.exp*1e3;this.applyTier(i.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(s);this.setVerifiedAt(c());this.retryAttempt=0;this.scheduleRetryIn(e,Math.max(6e4,s-60*60*1e3-c()));this.emit({type:"validated",isPro:this.isPro()});return true}admitDevToken(e){const t=pe(e);if(!t.ok||!w()){const s=t.ok?ge:t.message;this.lock();this.state.isValidating=false;this.state.error=s;this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:s});return false}const i=c();this.devToken={token:e,claims:t.claims};this.applyTier(t.claims.tier==="free"?"free":"pro");this.state.isValidating=false;this.state.expiresAt=new Date(t.claims.exp*1e3);this.setVerifiedAt(Math.min(i,t.claims.iat*1e3));this.retryAttempt=0;this.emit({type:"validated",isPro:this.isPro()});return true}async admitToken(e,t){const i=he(e);if(t!==this.generation)return false;if(!i){this.lock();this.state.isValidating=false;this.state.error="This key is not valid. Check that it was copied completely.";this.state.errorKind="verdict";this.emit({type:"error",isPro:false,error:this.state.error});await this.clearCachedLicense();return false}const s=c();const r=this.cachedLicense?.fingerprint??await u(this.getOrCreateDeviceFingerprint(),P());if(t!==this.generation)return false;const o=await this.saveCache({licenseKey:e,licenseId:`token:${i.accountHash}`,valid:true,isPro:false,tier:"free",expiresAt:null,cachedAt:s,lastValidatedAt:s,fingerprint:r,deviceName:this.cachedLicense?.deviceName??V(),quiet:true},t);if(t!==this.generation||!o)return false;this.cachedLicense=o;this.applyTier("free");this.state.isValidating=false;this.state.expiresAt=null;this.setVerifiedAt(s);this.retryAttempt=0;this.emit({type:"validated",isPro:false});return true}standDown(e,t,i,s){if(t!==this.generation)return false;this.state.isValidating=false;const r=this.cachedLicense;if(r&&r.licenseKey===e&&p(r)&&this.isCacheValid(r)){this.applyTier(g(r));this.state.error=null;this.state.errorKind=null;this.state.expiresAt=r.expiresAt?new Date(r.expiresAt):null;this.setVerifiedAt(r.cachedAt);if(this.keyTier()!=="pro")this.scheduleRetryIn(e,s);this.emit({type:"validated",isPro:this.state.isPro});return true}this.state.error=i;this.state.errorKind="transport";this.scheduleRetryIn(e,s);this.emit({type:"error",isPro:this.state.isPro,error:i});return false}scheduleRetryIn(e,t){this.cancelRetry();const i=this.generation;this.retryTimer=setTimeout(()=>{this.retryTimer=null;if(i!==this.generation)return;if(this.state.isValidating)return;void this.setLicenseKey(e,{force:true,isRetry:true})},Math.max(1,Math.min(t,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)h.__LICENSE_MANAGER_RETRY_TIMER__=this.retryTimer}cancelRetry(){if(this.retryTimer){clearTimeout(this.retryTimer);this.retryTimer=null}}async clearLicense(){this.generation+=1;this.cancelRetry();this.retryAttempt=0;this.configuredKey=null;this.state.licenseKey=null;this.lock();this.state.expiresAt=null;this.state.error=null;this.state.errorKind=null;this.cachedLicense=null;this.pendingRegistration=null;this.state.isValidating=false;this.emit({type:"deactivated",isPro:false});await this.clearCachedLicense();if(this.hold?.verdictAt!==void 0){await this.saveHold(this.hold.backoffUntil?{backoffUntil:this.hold.backoffUntil}:null)}}applyTier(e){this.state.tier=e;this.state.tierSource="key";this.state.isPro=e==="pro"}lock(){this.state.tier="free";this.state.tierSource="none";this.state.isPro=false;this.verifiedAt=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null}getTier(){return this.credential().tier}credential(){const e=this.keyTier();const t=this.sessionTier();if(t!==null&&(!this.hasKeyAccess()||t==="pro"&&e!=="pro")){return{source:"session",tier:t}}return{source:this.state.tierSource,tier:e}}keyTier(){if(!this.hasKeyAccess())return"free";if(this.state.expiresAt&&c()>=this.state.expiresAt.getTime())return"free";return this.state.tier}sessionTier(){const e=_.getStatus();if(e.state!=="active")return null;return e.claims.tier==="free"?"free":"pro"}isPro(){return this.getTier()==="pro"}hasEntitlement(e){return this.isPro()}getState(){const{source:e,tier:t}=this.credential();const i=this.getTeamAccess();return{...this.state,tier:t,tierSource:e,isPro:t==="pro",access:this.getAccountAccess(),teamRole:i.role,allowedTools:i.tools}}getTeamAccess(){const e=_.getStatus();if(e.state==="active")return H(e.claims);const t=this.devToken;if(t&&t.token===this.state.licenseKey&&this.state.tierSource==="key"&&c()<t.claims.exp*1e3&&this.hasKeyAccess()){return H(t.claims)}return Ae}getAccountAccess(){const e=this.getKeyAccess();if(e.status==="admitted")return e;const t=_.getStatus();if(t.state==="active")return{status:"admitted",reason:null,validUntil:t.validUntil};return e}getKeyAccess(){return ue({initialized:this.state.isInitialized,validating:this.state.isValidating,key:this.state.licenseKey,tierSource:this.state.tierSource,verifiedAt:this.verifiedAt})}hasAccountAccess(){return this.getAccountAccess().status==="admitted"}hasKeyAccess(){return this.getKeyAccess().status==="admitted"}setVerifiedAt(e){this.verifiedAt=e;if(this.expiryTimer)clearTimeout(this.expiryTimer);const t=e+b;const i=this.state.expiresAt?.getTime();const s=i&&i>c()?Math.min(t,i):t;const r=s-c();this.expiryTimer=setTimeout(()=>{this.expiryTimer=null;if(this.verifiedAt!==e)return;this.emit({type:"validated",isPro:this.isPro()});if(c()<t)this.setVerifiedAt(e)},Math.max(1,Math.min(r,2147483647)));if(typeof __DEV__!=="undefined"&&__DEV__)h.__LICENSE_MANAGER_EXPIRY_TIMER__=this.expiryTimer}subscribe(e){this.listeners.add(e);return()=>this.listeners.delete(e)}onDeviceRegistrationNeeded(e){this.deviceRegistrationCallback=e;return()=>{this.deviceRegistrationCallback=null}}async completeDeviceRegistration(e){if(!this.pendingRegistration){console.warn("[Buoy] No pending device registration");return}const{licenseId:t,licenseKey:i,fingerprint:s}=this.pendingRegistration;const r=this.generation;const o=e.trim()||V();const A=G();try{await O(t,i,s,o,A)}catch(n){const a=n instanceof Error?n.message:String(n);if(a==="FINGERPRINT_TAKEN"||a==="MACHINE_LIMIT_EXCEEDED"){}else{console.warn("[Buoy] Device registration failed:",a);throw n}}if(r!==this.generation)return;if(this.cachedLicense?.licenseKey===i){const n=await this.saveCache({...this.cachedLicense,fingerprint:s,deviceName:o},r);if(r!==this.generation)return;if(n)this.cachedLicense=n}this.pendingRegistration=null}emit(e){const t=[...this.listeners];t.forEach(i=>{try{i(e)}catch(s){console.warn("[Buoy]","[React Buoy] License listener error:",s)}})}isCacheValid(e){const t=c();const i=t-e.cachedAt;if(!e.valid||!Number.isFinite(e.cachedAt)||i<0||i>=b){return false}return true}isCacheFresh(e){const t=this.isQuiet(e)?Z:J;return c()-(e.lastValidatedAt??0)<t}isQuiet(e){if(e.quiet!==void 0)return e.quiet;return g(e)==="free"&&!w()}async loadHold(){if(this.hold!==void 0)return this.hold;try{const e=await y();const t=await e.getItem(C);const i=t?JSON.parse(t):null;this.hold=i&&typeof i==="object"?i:null}catch{this.hold=null}return this.hold}async saveHold(e){this.hold=e;try{const t=await y();if(e)await t.setItem(C,JSON.stringify(e));else await t.removeItem(C)}catch{}}async persistCache(e){const t=U(e);const i=await y();await i.setItem(N,JSON.stringify(t));return t}saveCache(e,t){const i=this.persistence.catch(()=>{}).then(async()=>{if(t!==this.generation)return null;const s=await this.persistCache(e);return t===this.generation?s:null});this.persistence=i;return u(i,U(e))}async loadCachedLicense(){try{await this.persistence.catch(()=>{});const e=await y();const t=await e.getItem(N);if(!t)return null;const i=JSON.parse(t);return i}catch(e){console.warn("[Buoy] Error loading cached license:",e);return null}}getOrCreateDeviceFingerprint(){if(!this.fingerprintPromise){this.fingerprintPromise=(async()=>{const e=await y();const t=await e.getItem(K);if(t)return t;const i=this.cachedLicense?.fingerprint||P();await e.setItem(K,i);return i})()}return this.fingerprintPromise}async clearCachedLicense(){this.cachedLicense=null;const e=this.persistence.catch(()=>{}).then(async()=>{const t=await y();await t.removeItem(N)});this.persistence=e;await u(e.catch(()=>{}),void 0)}registerDeviceAsync(e,t,i,s){const r=G();O(e,t,i,s,r).catch(o=>{if(o.message!=="FINGERPRINT_TAKEN"&&o.message!=="MACHINE_LIMIT_EXCEEDED"){console.warn("[Buoy] Device registration failed (non-blocking):",o.message)}})}validateInBackground(e){void this.setLicenseKey(e,{force:true,isRetry:true})}refreshAccess(){this.emit({type:"validated",isPro:this.isPro()})}startRevalidationTimer(){if(!this.removeForegroundListener){this.removeForegroundListener=fe(()=>this.refreshAccess());if(typeof __DEV__!=="undefined"&&__DEV__)h.__LICENSE_MANAGER_FOREGROUND_OFF__=this.removeForegroundListener}if(this.revalidationTimer){clearInterval(this.revalidationTimer)}this.revalidationTimer=setInterval(()=>{if(!this.state.licenseKey||this.state.isValidating)return;const e=this.cachedLicense;if(e&&e.licenseKey===this.state.licenseKey&&this.isCacheFresh(e))return;this.validateInBackground(this.state.licenseKey)},q);if(typeof __DEV__!=="undefined"&&__DEV__){global.__LICENSE_MANAGER_TIMER__=this.revalidationTimer}}stopRevalidationTimer(){this.removeForegroundListener?.();this.removeForegroundListener=null;if(this.expiryTimer)clearTimeout(this.expiryTimer);this.expiryTimer=null;if(this.revalidationTimer){clearInterval(this.revalidationTimer);this.revalidationTimer=null}this.cancelRetry()}async forceReregister(){if(this.cachedLicense){this.cachedLicense.fingerprint=void 0;this.cachedLicense.deviceName=void 0}this.fingerprintPromise=null;await this.clearCachedLicense();try{const e=await y();await e.removeItem(K)}catch{}}}export const LicenseManager=new Ee;const h=globalThis;if(typeof __DEV__!=="undefined"&&__DEV__){h.__LICENSE_MANAGER_FOREGROUND_OFF__?.();h.__LICENSE_MANAGER_FOREGROUND_OFF__=void 0;if(h.__LICENSE_MANAGER_EXPIRY_TIMER__)clearTimeout(h.__LICENSE_MANAGER_EXPIRY_TIMER__);if(h.__LICENSE_MANAGER_TIMER__){clearInterval(h.__LICENSE_MANAGER_TIMER__)}if(h.__LICENSE_MANAGER_RETRY_TIMER__){clearTimeout(h.__LICENSE_MANAGER_RETRY_TIMER__);h.__LICENSE_MANAGER_RETRY_TIMER__=void 0}}
@@ -11,6 +11,6 @@
11
11
  * under 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
16
  */const o="v2";const u=["v1"];const g="buoy.gg/pro:9f3c1d7a4b2e:license-cache";const f="\0";function i(n,r){let e=r>>>0;for(let t=0;t<n.length;t++){e^=n.charCodeAt(t);e=Math.imul(e,16777619)>>>0}return e>>>0}function a(n,r){const e=[r,n.licenseKey,n.licenseId,n.isPro?"1":"0",n.expiresAt??"",String(n.cachedAt)];if(r!=="v1"){e.push(n.tier??"")}e.push(g);return e.join(f)}function s(n,r){const e=a(n,r);const t=i(e,2166136261);const c=i(e,2135587861);return`${r}.${t.toString(16)}${c.toString(16)}`}export function cacheSignature(n){return s(n,o)}export function signCache(n){return{...n,sig:cacheSignature(n)}}export function verifyCacheSignature(n){if(!n||typeof n.sig!=="string"||n.sig.length===0){return false}if(n.sig===cacheSignature(n))return true;return u.some(r=>n.sig.startsWith(`${r}.`)&&n.sig===s(n,r))}
@@ -12,6 +12,6 @@
12
12
  * § 1201, which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */export const KEYGEN_CONFIG=Object.freeze({accountId:"b0eba0e8-e904-4906-b644-38f9aab57bed",productId:"bf67cb43-eeef-40ca-b308-e6e1b22c0eaa",apiUrl:"https://buoy.gg/api/license"});export{FREE_TOKEN_PUBLIC_KEY}from"./freeTokenKey.js";export{SESSION_TOKEN_PUBLIC_KEY}from"./sessionTokenKey.js";export const KEYGEN_POLICIES=Object.freeze({free:"394e3351-1249-4f73-807a-703408604c0e",solo:"fa5d3c8f-5379-40f1-b63e-ab8a974b2f28",business:"690c2212-8e54-426f-b4b4-002ee1c0ae2b"});export const LICENSE_CACHE_DURATION=30*24*60*60*1e3;export const LICENSE_REVALIDATION_INTERVAL=24*60*60*1e3;export const LICENSE_STORAGE_KEY="buoy-gg-license";export const DEVICE_FINGERPRINT_STORAGE_KEY="buoy-gg-device-fingerprint";export const LICENSE_LAUNCH_VALIDATION_TTL=60*60*1e3;export const LICENSE_TRANSIENT_RETRY_DELAYS=[5*1e3,20*1e3,60*1e3,5*60*1e3,15*60*1e3];export const LICENSE_RATE_LIMIT_DEFAULT_WAIT=60*1e3;export const LICENSE_QUOTA_COOLDOWN=60*60*1e3;export const LICENSE_VERDICT_CACHE_DURATION=24*60*60*1e3;export const LICENSE_FLEET_MACHINE_THRESHOLD=50;export const LICENSE_QUIET_VALIDATION_TTL=LICENSE_CACHE_DURATION;export const LICENSE_HOLD_STORAGE_KEY="buoy-gg-license-hold";
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
5
  * (https://buoy.gg/terms), not an OSS license.
6
- */import{isSessionToken as i,verifySessionToken as s}from"./sessionToken.js";import{realNow as r}from"./realNow.js";export const DEV_AUDIENCE="dev";export function isDevToken(e){return i(e)}export function checkDevToken(e,n=r()){const o=s(e,{audience:DEV_AUDIENCE,ignoreTime:true});if(!o.ok)return{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx buoy login` again."};if(n>=o.claims.exp*1e3){return{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx buoy login` again."}}return{ok:true,claims:o.claims}}export const DEV_TOKEN_IN_RELEASE="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";
6
+ */import{isSessionToken as i,verifySessionToken as s}from"./sessionToken.js";import{realNow as r}from"./realNow.js";export const DEV_AUDIENCE="dev";export function isDevToken(e){return i(e)}export function checkDevToken(e,n=r()){const o=s(e,{audience:DEV_AUDIENCE,ignoreTime:true});if(!o.ok)return{ok:false,reason:"invalid",message:"This dev token is not valid. Run `npx --package=@buoy-gg/core buoy login` again."};if(n>=o.claims.exp*1e3){return{ok:false,reason:"expired",message:"Your dev token ran out. Run `npx --package=@buoy-gg/core buoy login` again."}}return{ok:true,claims:o.claims}}export const DEV_TOKEN_IN_RELEASE="This is a dev token. It only works in dev builds. A live build turns on Buoy with Sign in with Buoy.";
@@ -12,6 +12,6 @@
12
12
  * which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */import{useState as u,useEffect as d,useCallback as a}from"react";import{LicenseManager as r}from"./LicenseManager.js";import{useWeekendFreePro as f,weekendPassAppliesTo as l}from"./weekendPro.js";import{SessionManager as o}from"./sessionManager.js";export function useLicense(){const[n,e]=u(r.getState());d(()=>{const s=r.subscribe(c=>{e(r.getState())});r.initialize().then(()=>{e(r.getState())});return s},[]);const i=a(async s=>{const c=await r.setLicenseKey(s,{force:true});e(r.getState());return c},[]);const t=a(async()=>{await r.clearLicense();e(r.getState())},[]);return{...n,setLicenseKey:i,clearLicense:t}}export function useAccountAccess(){return useLicense().access}export function useTier(){const{tier:n,isInitialized:e}=useLicense();return e?n:"free"}export function useFeatureAccess(n){const{isPro:e,isInitialized:i,isValidating:t}=useLicense();const s=e;const c=!i||t;return{hasAccess:s,isPro:e,isLoading:c}}export function useIsPro(){const{isPro:n,isInitialized:e,access:i}=useLicense();const t=l(i.status==="admitted",f());return e&&n||t}export function useProAccess(){const{isPro:n,isInitialized:e,access:i}=useLicense();const t=e&&n;const s=l(i.status==="admitted",f())&&!t;const c=t?"licensed":s?"weekend":null;return{isPro:t||s,isLicensed:t,isWeekendFree:s,reason:c}}export function useBuoySession(){const[n,e]=u(()=>o.getStatus());d(()=>{const s=()=>e(o.getStatus());const c=o.subscribe(s);o.load().then(s,s);return c},[]);const i=a(()=>o.signIn(),[]);const t=a(()=>o.signOut(),[]);return{status:n,signIn:i,signOut:t}}
@@ -13,6 +13,6 @@
13
13
  * copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */export{LicenseManager}from"./LicenseManager.js";export{DeviceRegistrationModal}from"./DeviceRegistrationModal.js";export{useLicense,useFeatureAccess,useIsPro,useProAccess,useTier}from"./hooks.js";export{useAccountAccess}from"./hooks.js";export{resolveAccountAccess}from"./accountAccess.js";export{LicenseTransportError,isTransientLicenseError}from"./api.js";export{SessionManager,SessionError}from"./sessionManager.js";export{useBuoySession}from"./hooks.js";export{isServiceKey,normalizeLicenseKey,sameLicenseKey}from"./serviceKey.js";export{isCapacitorNative,capacitorPlatform,capacitorDebug,resolveDevBuild}from"./capacitorHost.js";export{installCapacitorBridge,isCapacitorBridgeInstalled,interceptCapacitor,emitCapacitorEvent,capacitorListenerCount,hasCapacitorPlugin,capacitorPlugin,callCapacitorNative,callCapacitorAsApp,callCapacitorNativeCallback,listenCapacitorNative,makeCapacitorCallbackId,getCapacitorCalls,clearCapacitorCalls,subscribeCapacitorCalls,loadCapacitorAppHost,getCapacitorAppHost,colorSchemeQueryValue,getColorSchemeQueries}from"./capacitorBridge.js";export{isDevToken}from"./devToken.js";export{getSyncCredential}from"./syncCredential.js";export{isBuoyToken}from"./freeToken.js";export{isWeekendFreePro,useWeekendFreePro}from"./weekendPro.js";export async function setLicenseKey(e){const{LicenseManager:r}=await import("./LicenseManager");await r.initialize();return r.setLicenseKey(e)}function o(){const{isWeekendFreePro:e,weekendPassAppliesTo:r}=require("./weekendPro");const{LicenseManager:a}=require("./LicenseManager");return r(a.hasAccountAccess(),e())}export function isPro(){const{LicenseManager:e}=require("./LicenseManager");if(e.isPro())return true;return o()}export function hasEntitlement(e){const{LicenseManager:r}=require("./LicenseManager");if(r.hasEntitlement(e))return true;return o()}
@@ -13,6 +13,6 @@
13
13
  * copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */import{LicenseManager as r}from"./LicenseManager.js";import{isWeekendFreePro as a,weekendPassAppliesTo as i}from"./weekendPro.js";export{LicenseManager}from"./LicenseManager.js";export{useLicense,useFeatureAccess,useIsPro,useProAccess,useTier}from"./hooks.js";export{useAccountAccess}from"./hooks.js";export{resolveAccountAccess}from"./accountAccess.js";export{LicenseTransportError,isTransientLicenseError}from"./api.js";export{SessionManager,SessionError}from"./sessionManager.js";export{useBuoySession}from"./hooks.js";export{isServiceKey,normalizeLicenseKey,sameLicenseKey}from"./serviceKey.js";export{isCapacitorNative,capacitorPlatform,capacitorDebug,resolveDevBuild}from"./capacitorHost.js";export{installCapacitorBridge,isCapacitorBridgeInstalled,interceptCapacitor,emitCapacitorEvent,capacitorListenerCount,hasCapacitorPlugin,capacitorPlugin,callCapacitorNative,callCapacitorAsApp,callCapacitorNativeCallback,listenCapacitorNative,makeCapacitorCallbackId,getCapacitorCalls,clearCapacitorCalls,subscribeCapacitorCalls,loadCapacitorAppHost,getCapacitorAppHost,colorSchemeQueryValue,getColorSchemeQueries}from"./capacitorBridge.js";export{isDevToken}from"./devToken.js";export{getSyncCredential}from"./syncCredential.js";export{isBuoyToken}from"./freeToken.js";export{isWeekendFreePro,useWeekendFreePro}from"./weekendPro.js";export async function setLicenseKey(e){const{LicenseManager:o}=await import("./LicenseManager");await o.initialize();return o.setLicenseKey(e)}function t(){return i(r.hasAccountAccess(),a())}export function isPro(){if(r.isPro())return true;return t()}export function hasEntitlement(e){if(r.hasEntitlement(e))return true;return t()}
@@ -3,4 +3,4 @@
3
3
  *
4
4
  * Copyright (c) Buoy. All rights reserved. Licensed under the Buoy EULA
5
5
  * (https://buoy.gg/terms), not an OSS license.
6
- */import S from"node:http";import{createHash as T,randomBytes as y,timingSafeEqual as _}from"node:crypto";import{promises as f}from"node:fs";import{dirname as E,join as b}from"node:path";import{homedir as k}from"node:os";import{SessionManagerImpl as O,SessionError as h}from"./sessionManager.js";export const DESKTOP_AUDIENCE="app:gg.buoy.desktop";export const CLI_AUDIENCE="app:gg.buoy.cli";export const WEB_EXTENSION_AUDIENCE="app:gg.buoy.web";const A=5*60*1e3;const I=4096;const C=6e4;const N=3e4;const v=8e3;const L=15e3;const P=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","CERT_UNTRUSTED"]);function U(t){const r=t?.cause;if(r&&typeof r.code==="string")return r.code;if(t?.name==="TimeoutError")return"timed out";if(r&&typeof r.message==="string")return r.message;return t instanceof Error?t.message:String(t)}export function defaultSessionFile(){return process.env.BUOY_SESSION_FILE||b(k(),".buoy","session.json")}export function fileSessionStorage(t){const r=async()=>{try{const n=JSON.parse(await f.readFile(t,"utf8"));return n&&typeof n==="object"?n:{}}catch{return{}}};const u=async n=>{await f.mkdir(E(t),{recursive:true,mode:448});if(Object.keys(n).length===0){await f.rm(t,{force:true});return}const i=`${t}.${process.pid}.${y(4).toString("hex")}.tmp`;await f.writeFile(i,JSON.stringify(n),{mode:384});await f.rename(i,t)};return{async getItem(n){return(await r())[n]??null},async setItem(n,i){const l=await r();l[n]=i;await u(l)},async removeItem(n){const i=await r();if(!(n in i))return;delete i[n];await u(i)}}}export function fileLock(t,r={}){const u=r.staleMs??C;const n=r.waitMs??N;return async(i,l)=>{const s=`${process.pid}:${y(8).toString("hex")}`;const p=Date.now()+n;await f.mkdir(E(t),{recursive:true,mode:448});for(;;){try{await f.writeFile(t,s,{flag:"wx",mode:384});break}catch(e){if(e.code!=="EEXIST")throw e;const a=await f.stat(t).catch(()=>null);if(a&&Date.now()-a.mtimeMs>u){await f.rm(t,{force:true});continue}if(Date.now()>p)throw new Error("Another Buoy process holds the sign-in lock.");await new Promise(o=>setTimeout(o,50))}}try{return await l()}finally{const e=await f.readFile(t,"utf8").catch(()=>null);if(e===s)await f.rm(t,{force:true})}}}function H(t,r){const u=Buffer.from(t);const n=Buffer.from(r);return u.length===n.length&&_(u,n)}export async function receiveCodeOnLoopback(t,r,u,n={}){let i;let l;const s=new Promise((c,d)=>{i=c;l=d});s.catch(()=>{});const p=S.createServer((c,d)=>{if(c.headers.origin===u){d.setHeader("Access-Control-Allow-Origin",u);d.setHeader("Access-Control-Allow-Headers","Content-Type");d.setHeader("Access-Control-Allow-Methods","POST, OPTIONS");d.setHeader("Access-Control-Allow-Private-Network","true")}if(c.method==="OPTIONS")return void d.writeHead(204).end();if(c.method!=="POST")return void d.writeHead(405).end();let w="";c.on("data",g=>{w+=g;if(w.length>I)c.destroy()});c.on("end",()=>{let g=null;try{g=JSON.parse(w)}catch{}if(!g||typeof g.state!=="string"||!H(g.state,r))return void d.writeHead(400).end();d.writeHead(200,{"Content-Type":"application/json"}).end('{"ok":true}');if(typeof g.code==="string"&&g.code)o(()=>i(g.code));else o(()=>l(new h("access_denied","Sign-in was cancelled.")))})});const e=setTimeout(()=>o(()=>l(new h("timeout","Sign-in took too long."))),n.timeoutMs??A);e.unref?.();const a=()=>o(()=>l(new h("popup_closed","Sign-in was cancelled.")));n.signal?.addEventListener("abort",a);function o(c){clearTimeout(e);n.signal?.removeEventListener("abort",a);p.close();c()}await new Promise((c,d)=>{p.once("error",d);p.listen(0,"127.0.0.1",()=>c())});const m=p.address();if(!m||typeof m==="string"){o(()=>l(new Error("Could not open a local port")));throw new Error("Could not open a local port")}return{url:`${t}&port=${m.port}`,code:s}}export function createNodeSession(t){const r=defaultSessionFile();const u=t.storage??fileSessionStorage(r);const n=(t.signinOrigin??process.env.BUOY_SITE_URL??"https://buoy.gg").replace(/\/$/,"");const i=t.fetch??((e,a)=>fetch(e,a));const l={fetch:i,storage:async()=>u,now:t.now??(()=>Date.now()),random:e=>new Uint8Array(y(e)),sha256:async e=>new Uint8Array(T("sha256").update(e).digest()),openPopup:()=>null,onMessage:()=>()=>{},withLock:fileLock(t.lockFile??`${r}.lock`),setTimer:(e,a)=>{const o=setTimeout(e,Math.min(a,2147483647));o.unref?.();return o},clearTimer:e=>clearTimeout(e)};const s=new O(l);s.configure({audience:t.audience,signinOrigin:n});const p=async()=>{let e;try{e=await i(`${n}/api/auth/status`,{method:"GET",headers:{},signal:AbortSignal.timeout(v)})}catch(o){const m=U(o);return P.has(m)?{state:"unreachable",detail:m,hint:"Your network may re-sign secure traffic. Point NODE_EXTRA_CA_CERTS at your company's root certificate."}:{state:"unreachable",detail:m}}const a=await e.json().catch(()=>null);if(e.status===200&&a?.enabled===true)return{state:"enabled"};if(e.status===200&&a?.enabled===false)return{state:"disabled"};return{state:"unreachable",detail:a?`HTTP ${e.status}`:`HTTP ${e.status}, not a Buoy answer`}};return{manager:s,async load(){await s.load();return s.getStatus()},getStatus:()=>s.getStatus(),signIn:(e={})=>s.signInWith(async(a,o)=>{const{url:m,code:c}=await receiveCodeOnLoopback(a,o,n,e);await t.openBrowser(m);return c}),signOut:()=>s.signOut(),refresh:e=>s.refresh(e),sync:()=>s.sync(),async devToken(){const e=s.getAccessToken();if(!e)return{ok:false,detail:"This computer is not signed in. Run `npx buoy login`."};try{const a=await i(`${n}/api/auth/dev-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:e}),signal:AbortSignal.timeout(L)});const o=await a.json().catch(()=>null);if(a.status===200&&typeof o?.dev_token==="string"){return{ok:true,token:o.dev_token,tier:typeof o.tier==="string"?o.tier:"free"}}return{ok:false,detail:typeof o?.error_description==="string"?o.error_description:"buoy.gg did not make a dev token."}}catch{return{ok:false,detail:"Buoy could not be reached. Check your connection and try again."}}},checkAvailability:p,async isAvailable(){return(await p()).state==="enabled"},subscribe:e=>s.subscribe(e)}}
6
+ */import S from"node:http";import{createHash as T,randomBytes as y,timingSafeEqual as _}from"node:crypto";import{promises as f}from"node:fs";import{dirname as E,join as b}from"node:path";import{homedir as k}from"node:os";import{SessionManagerImpl as O,SessionError as h}from"./sessionManager.js";export const DESKTOP_AUDIENCE="app:gg.buoy.desktop";export const CLI_AUDIENCE="app:gg.buoy.cli";export const WEB_EXTENSION_AUDIENCE="app:gg.buoy.web";const A=5*60*1e3;const I=4096;const C=6e4;const N=3e4;const v=8e3;const L=15e3;const P=new Set(["UNABLE_TO_GET_ISSUER_CERT_LOCALLY","SELF_SIGNED_CERT_IN_CHAIN","DEPTH_ZERO_SELF_SIGNED_CERT","UNABLE_TO_VERIFY_LEAF_SIGNATURE","CERT_UNTRUSTED"]);function U(t){const r=t?.cause;if(r&&typeof r.code==="string")return r.code;if(t?.name==="TimeoutError")return"timed out";if(r&&typeof r.message==="string")return r.message;return t instanceof Error?t.message:String(t)}export function defaultSessionFile(){return process.env.BUOY_SESSION_FILE||b(k(),".buoy","session.json")}export function fileSessionStorage(t){const r=async()=>{try{const n=JSON.parse(await f.readFile(t,"utf8"));return n&&typeof n==="object"?n:{}}catch{return{}}};const u=async n=>{await f.mkdir(E(t),{recursive:true,mode:448});if(Object.keys(n).length===0){await f.rm(t,{force:true});return}const i=`${t}.${process.pid}.${y(4).toString("hex")}.tmp`;await f.writeFile(i,JSON.stringify(n),{mode:384});await f.rename(i,t)};return{async getItem(n){return(await r())[n]??null},async setItem(n,i){const l=await r();l[n]=i;await u(l)},async removeItem(n){const i=await r();if(!(n in i))return;delete i[n];await u(i)}}}export function fileLock(t,r={}){const u=r.staleMs??C;const n=r.waitMs??N;return async(i,l)=>{const s=`${process.pid}:${y(8).toString("hex")}`;const p=Date.now()+n;await f.mkdir(E(t),{recursive:true,mode:448});for(;;){try{await f.writeFile(t,s,{flag:"wx",mode:384});break}catch(e){if(e.code!=="EEXIST")throw e;const a=await f.stat(t).catch(()=>null);if(a&&Date.now()-a.mtimeMs>u){await f.rm(t,{force:true});continue}if(Date.now()>p)throw new Error("Another Buoy process holds the sign-in lock.");await new Promise(o=>setTimeout(o,50))}}try{return await l()}finally{const e=await f.readFile(t,"utf8").catch(()=>null);if(e===s)await f.rm(t,{force:true})}}}function H(t,r){const u=Buffer.from(t);const n=Buffer.from(r);return u.length===n.length&&_(u,n)}export async function receiveCodeOnLoopback(t,r,u,n={}){let i;let l;const s=new Promise((c,d)=>{i=c;l=d});s.catch(()=>{});const p=S.createServer((c,d)=>{if(c.headers.origin===u){d.setHeader("Access-Control-Allow-Origin",u);d.setHeader("Access-Control-Allow-Headers","Content-Type");d.setHeader("Access-Control-Allow-Methods","POST, OPTIONS");d.setHeader("Access-Control-Allow-Private-Network","true")}if(c.method==="OPTIONS")return void d.writeHead(204).end();if(c.method!=="POST")return void d.writeHead(405).end();let w="";c.on("data",m=>{w+=m;if(w.length>I)c.destroy()});c.on("end",()=>{let m=null;try{m=JSON.parse(w)}catch{}if(!m||typeof m.state!=="string"||!H(m.state,r))return void d.writeHead(400).end();d.writeHead(200,{"Content-Type":"application/json"}).end('{"ok":true}');if(typeof m.code==="string"&&m.code)o(()=>i(m.code));else o(()=>l(new h("access_denied","Sign-in was cancelled.")))})});const e=setTimeout(()=>o(()=>l(new h("timeout","Sign-in took too long."))),n.timeoutMs??A);e.unref?.();const a=()=>o(()=>l(new h("popup_closed","Sign-in was cancelled.")));n.signal?.addEventListener("abort",a);function o(c){clearTimeout(e);n.signal?.removeEventListener("abort",a);p.close();c()}await new Promise((c,d)=>{p.once("error",d);p.listen(0,"127.0.0.1",()=>c())});const g=p.address();if(!g||typeof g==="string"){o(()=>l(new Error("Could not open a local port")));throw new Error("Could not open a local port")}return{url:`${t}&port=${g.port}`,code:s}}export function createNodeSession(t){const r=defaultSessionFile();const u=t.storage??fileSessionStorage(r);const n=(t.signinOrigin??process.env.BUOY_SITE_URL??"https://buoy.gg").replace(/\/$/,"");const i=t.fetch??((e,a)=>fetch(e,a));const l={fetch:i,storage:async()=>u,now:t.now??(()=>Date.now()),random:e=>new Uint8Array(y(e)),sha256:async e=>new Uint8Array(T("sha256").update(e).digest()),openPopup:()=>null,onMessage:()=>()=>{},withLock:fileLock(t.lockFile??`${r}.lock`),setTimer:(e,a)=>{const o=setTimeout(e,Math.min(a,2147483647));o.unref?.();return o},clearTimer:e=>clearTimeout(e)};const s=new O(l);s.configure({audience:t.audience,signinOrigin:n});const p=async()=>{let e;try{e=await i(`${n}/api/auth/status`,{method:"GET",headers:{},signal:AbortSignal.timeout(v)})}catch(o){const g=U(o);return P.has(g)?{state:"unreachable",detail:g,hint:"Your network may re-sign secure traffic. Point NODE_EXTRA_CA_CERTS at your company's root certificate."}:{state:"unreachable",detail:g}}const a=await e.json().catch(()=>null);if(e.status===200&&a?.enabled===true)return{state:"enabled"};if(e.status===200&&a?.enabled===false)return{state:"disabled"};return{state:"unreachable",detail:a?`HTTP ${e.status}`:`HTTP ${e.status}, not a Buoy answer`}};return{manager:s,async load(){await s.load();return s.getStatus()},getStatus:()=>s.getStatus(),signIn:(e={})=>s.signInWith(async(a,o)=>{const{url:g,code:c}=await receiveCodeOnLoopback(a,o,n,e);await t.openBrowser(g);return c}),signOut:()=>s.signOut(),refresh:e=>s.refresh(e),sync:()=>s.sync(),async devToken(){const e=s.getAccessToken();if(!e)return{ok:false,detail:"This computer is not signed in. Run `npx --package=@buoy-gg/core buoy login`."};try{const a=await i(`${n}/api/auth/dev-token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({access_token:e}),signal:AbortSignal.timeout(L)});const o=await a.json().catch(()=>null);if(a.status===200&&typeof o?.dev_token==="string"){return{ok:true,token:o.dev_token,tier:typeof o.tier==="string"?o.tier:"free"}}return{ok:false,detail:typeof o?.error_description==="string"?o.error_description:"buoy.gg did not make a dev token."}}catch{return{ok:false,detail:"Buoy could not be reached. Check your connection and try again."}}},checkAvailability:p,async isAvailable(){return(await p()).state==="enabled"},subscribe:e=>s.subscribe(e)}}
@@ -12,6 +12,6 @@
12
12
  * applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */import{KEYGEN_CONFIG as n,KEYGEN_POLICIES as r}from"./config.js";export function resolveTier(t){const e=t.relationships?.policy?.data?.id;if(e===r.free)return"free";if(e===r.solo||e===r.business){return"pro"}const o=t.attributes?.metadata?.plan;if(o==="free")return"free";return"pro"}export function tierAfterFatalError(t){return t==="EXPIRED"?"free":null}export function isRecognizedAccount(t){if(!t)return false;const e=t.relationships?.product?.data?.id;if(e)return e===n.productId;const o=t.relationships?.policy?.data?.id;return Object.values(r).some(i=>i===o)}
@@ -13,6 +13,6 @@
13
13
  * whether or not any code is copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */import{useEffect as o,useState as a}from"react";import{realNow as u}from"./realNow.js";export function isWeekendFreePro(t=new Date(u())){if(globalThis.__buoyWeekendPassOff===true)return false;const e=t.getDay();return e===0||e===6}export function useWeekendFreePro(){const[t,e]=a(()=>isWeekendFreePro());o(()=>{const n=setInterval(()=>{const r=isWeekendFreePro();e(s=>s===r?s:r)},60*1e3);return()=>clearInterval(n)},[]);return t}export function weekendPassAppliesTo(t,e){return e&&t}
@@ -11,10 +11,10 @@
11
11
  * 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
16
  */
17
- import type { BuoyTier, TierSource, LicenseListener } from "./types";
17
+ import type { BuoyTier, TierSource, LicenseListener, TeamAccess } from "./types";
18
18
  /** Info passed to device registration callback */
19
19
  export interface DeviceRegistrationInfo {
20
20
  fingerprint: string;
@@ -48,6 +48,8 @@ declare class LicenseManagerImpl {
48
48
  private expiryTimer;
49
49
  private removeForegroundListener;
50
50
  private sessionUnsubscribe;
51
+ /** The dev token admitted last, so reading its role doesn't re-check the signature. */
52
+ private devToken;
51
53
  /**
52
54
  * Pending backoff retry after a transient failure (see
53
55
  * LICENSE_TRANSIENT_RETRY_DELAYS). At most one is ever outstanding, and it
@@ -172,6 +174,8 @@ declare class LicenseManagerImpl {
172
174
  tierSource: TierSource;
173
175
  isPro: boolean;
174
176
  access: import("./accountAccess").AccountAccess;
177
+ teamRole: string | null;
178
+ allowedTools: string[] | null;
175
179
  isInitialized: boolean;
176
180
  isValidating: boolean;
177
181
  licenseKey: string | null;
@@ -179,6 +183,12 @@ declare class LicenseManagerImpl {
179
183
  errorKind: "transport" | "verdict" | null;
180
184
  expiresAt: Date | null;
181
185
  };
186
+ /**
187
+ * What the person's team shows them: the sign-in's role and tool list, or
188
+ * else the dev token's. A key names no person, so it has no list. This
189
+ * only shapes the menu; tier and caps never read it.
190
+ */
191
+ getTeamAccess(): TeamAccess;
182
192
  getAccountAccess(): import("./accountAccess").AccountAccess;
183
193
  private getKeyAccess;
184
194
  hasAccountAccess(): boolean;
@@ -11,7 +11,7 @@
11
11
  * under 17 U.S.C. § 1201 — which applies whether or not any code is copied.
12
12
  *
13
13
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
14
- * including a free one (`npx buoy login`). Buy a key at
14
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
15
15
  * https://buoy.gg or use it free on Saturday and Sunday.
16
16
  */
17
17
  /** The subset of the cache that the checksum covers. */
@@ -12,7 +12,7 @@
12
12
  * § 1201, which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */
18
18
  /**
@@ -5,7 +5,7 @@
5
5
  * (https://buoy.gg/terms), not an OSS license.
6
6
  */
7
7
  /**
8
- * Dev tokens (BUOY_SIGN_IN_PLAN.md 2.3): what `npx buoy login` writes into a
8
+ * Dev tokens (BUOY_SIGN_IN_PLAN.md 2.3): what `npx --package=@buoy-gg/core buoy login` writes into a
9
9
  * project for dev builds and sims. A sign-in token for the audience "dev",
10
10
  * good for 30 days, checked offline. Only dev builds take it, so one that
11
11
  * slips into a shipped app opens nothing there.
@@ -12,7 +12,7 @@
12
12
  * which applies whether or not any code is copied.
13
13
  *
14
14
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
15
- * including a free one (`npx buoy login`). Buy a key at
15
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
16
16
  * https://buoy.gg or use it free on Saturday and Sunday.
17
17
  */
18
18
  import { LicenseManager } from "./LicenseManager";
@@ -13,7 +13,7 @@
13
13
  * copied.
14
14
  *
15
15
  * Pro is $9/mo, and is free every weekend for anyone holding a key —
16
- * including a free one (`npx buoy login`). Buy a key at
16
+ * including a free one (`npx --package=@buoy-gg/core buoy login`). Buy a key at
17
17
  * https://buoy.gg or use it free on Saturday and Sunday.
18
18
  */
19
19
  /**
@@ -46,7 +46,7 @@ export { isDevToken } from "./devToken";
46
46
  export { getSyncCredential } from "./syncCredential";
47
47
  export { isBuoyToken } from "./freeToken";
48
48
  export { isWeekendFreePro, useWeekendFreePro } from "./weekendPro";
49
- export type { BuoyTier, TierSource, LicenseState, LicenseEvent, LicenseListener, LicenseValidationResult, CachedLicense, } from "./types";
49
+ export type { BuoyTier, TierSource, LicenseState, LicenseEvent, LicenseListener, LicenseValidationResult, CachedLicense, TeamAccess, } from "./types";
50
50
  export declare function setLicenseKey(key: string): Promise<boolean>;
51
51
  export declare function isPro(): boolean;
52
52
  export declare function hasEntitlement(code: string): boolean;