@hellcoder/companion 0.112.1-preview.20260728132124.bf82019 → 0.113.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import{_ as u}from"./index-S0nvfWUo.js";function w(r={}){const{immediate:i=!1,onNeedReload:d,onNeedRefresh:_,onOfflineReady:s,onRegistered:n,onRegisteredSW:o,onRegisterError:t}=r;let a,c;const l=async(e=!0)=>{await c};async function f(){if("serviceWorker"in navigator){if(a=await u(async()=>{const{Workbox:e}=await import("./workbox-window.prod.es5-BBnX5xw4.js");return{Workbox:e}},[]).then(({Workbox:e})=>new e("/sw.js",{scope:"/",type:"classic"})).catch(e=>{t==null||t(e)}),!a)return;a.addEventListener("activated",e=>{(e.isUpdate||e.isExternal)&&(d?d():window.location.reload())}),a.addEventListener("installed",e=>{e.isUpdate||s==null||s()}),a.register({immediate:i}).then(e=>{o?o("/sw.js",e):n==null||n(e)}).catch(e=>{t==null||t(e)})}}return c=f(),l}w({onRegisteredSW(r,i){i&&setInterval(()=>{i.update()},3600*1e3)},onOfflineReady(){console.log("[SW] Offline-ready: all assets precached")}});
1
+ import{_ as u}from"./index-B6xqr-v6.js";function w(r={}){const{immediate:i=!1,onNeedReload:d,onNeedRefresh:_,onOfflineReady:s,onRegistered:n,onRegisteredSW:o,onRegisterError:t}=r;let a,c;const l=async(e=!0)=>{await c};async function f(){if("serviceWorker"in navigator){if(a=await u(async()=>{const{Workbox:e}=await import("./workbox-window.prod.es5-BBnX5xw4.js");return{Workbox:e}},[]).then(({Workbox:e})=>new e("/sw.js",{scope:"/",type:"classic"})).catch(e=>{t==null||t(e)}),!a)return;a.addEventListener("activated",e=>{(e.isUpdate||e.isExternal)&&(d?d():window.location.reload())}),a.addEventListener("installed",e=>{e.isUpdate||s==null||s()}),a.register({immediate:i}).then(e=>{o?o("/sw.js",e):n==null||n(e)}).catch(e=>{t==null||t(e)})}}return c=f(),l}w({onRegisteredSW(r,i){i&&setInterval(()=>{i.update()},3600*1e3)},onOfflineReady(){console.log("[SW] Offline-ready: all assets precached")}});
package/dist/index.html CHANGED
@@ -11,7 +11,7 @@
11
11
  <meta name="apple-mobile-web-app-title" content="Companion ME" />
12
12
  <meta name="theme-color" content="#d97757" />
13
13
  <title>The Companion — Moritz Edition</title>
14
- <script type="module" crossorigin src="/assets/index-S0nvfWUo.js"></script>
14
+ <script type="module" crossorigin src="/assets/index-B6xqr-v6.js"></script>
15
15
  <link rel="stylesheet" crossorigin href="/assets/index-C86AT0KJ.css">
16
16
  </head>
17
17
  <body class="bg-cc-bg text-cc-fg">
package/dist/sw.js CHANGED
@@ -1,2 +1,2 @@
1
1
  try{self["workbox:core:7.4.0"]&&_()}catch{}const x=(a,...e)=>{let t=a;return e.length>0&&(t+=` :: ${JSON.stringify(e)}`),t},N=x;class l extends Error{constructor(e,t){const s=N(e,t);super(s),this.name=e,this.details=t}}const E=new Set,f={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:typeof registration<"u"?registration.scope:""},b=a=>[f.prefix,a,f.suffix].filter(e=>e&&e.length>0).join("-"),O=a=>{for(const e of Object.keys(f))a(e)},C={updateDetails:a=>{O(e=>{typeof a[e]=="string"&&(f[e]=a[e])})},getGoogleAnalyticsName:a=>a||b(f.googleAnalytics),getPrecacheName:a=>a||b(f.precache),getPrefix:()=>f.prefix,getRuntimeName:a=>a||b(f.runtime),getSuffix:()=>f.suffix};function P(a,e){const t=new URL(a);for(const s of e)t.searchParams.delete(s);return t.href}async function I(a,e,t,s){const n=P(e.url,t);if(e.url===n)return a.match(e,s);const r=Object.assign(Object.assign({},s),{ignoreSearch:!0}),c=await a.keys(e,r);for(const i of c){const o=P(i.url,t);if(n===o)return a.match(i,s)}}let y;function M(){if(y===void 0){const a=new Response("");if("body"in a)try{new Response(a.body),y=!0}catch{y=!1}y=!1}return y}class D{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}}async function S(){for(const a of E)await a()}const W=a=>new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),"");function A(a){return new Promise(e=>setTimeout(e,a))}function K(a,e){const t=e();return a.waitUntil(t),t}async function q(a,e){let t=null;if(a.url&&(t=new URL(a.url).origin),t!==self.location.origin)throw new l("cross-origin-copy-response",{origin:t});const s=a.clone(),r={headers:new Headers(s.headers),status:s.status,statusText:s.statusText},c=M()?s.body:await s.blob();return new Response(c,r)}function j(){self.addEventListener("activate",()=>self.clients.claim())}try{self["workbox:precaching:7.4.0"]&&_()}catch{}const H="__WB_REVISION__";function F(a){if(!a)throw new l("add-to-cache-list-unexpected-type",{entry:a});if(typeof a=="string"){const r=new URL(a,location.href);return{cacheKey:r.href,url:r.href}}const{revision:e,url:t}=a;if(!t)throw new l("add-to-cache-list-unexpected-type",{entry:a});if(!e){const r=new URL(t,location.href);return{cacheKey:r.href,url:r.href}}const s=new URL(t,location.href),n=new URL(t,location.href);return s.searchParams.set(H,e),{cacheKey:s.href,url:n.href}}class B{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:s})=>{if(e.type==="install"&&t&&t.originalRequest&&t.originalRequest instanceof Request){const n=t.originalRequest.url;s?this.notUpdatedURLs.push(n):this.updatedURLs.push(n)}return s}}}class ${constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:t,params:s})=>{const n=(s==null?void 0:s.cacheKey)||this._precacheController.getCacheKeyForURL(t.url);return n?new Request(n,{headers:t.headers}):t},this._precacheController=e}}try{self["workbox:strategies:7.4.0"]&&_()}catch{}function m(a){return typeof a=="string"?new Request(a):a}class G{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new D,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(const s of this._plugins)this._pluginStateMap.set(s,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){const{event:t}=this;let s=m(e);if(s.mode==="navigate"&&t instanceof FetchEvent&&t.preloadResponse){const c=await t.preloadResponse;if(c)return c}const n=this.hasCallback("fetchDidFail")?s.clone():null;try{for(const c of this.iterateCallbacks("requestWillFetch"))s=await c({request:s.clone(),event:t})}catch(c){if(c instanceof Error)throw new l("plugin-error-request-will-fetch",{thrownErrorMessage:c.message})}const r=s.clone();try{let c;c=await fetch(s,s.mode==="navigate"?void 0:this._strategy.fetchOptions);for(const i of this.iterateCallbacks("fetchDidSucceed"))c=await i({event:t,request:r,response:c});return c}catch(c){throw n&&await this.runCallbacks("fetchDidFail",{error:c,event:t,originalRequest:n.clone(),request:r.clone()}),c}}async fetchAndCachePut(e){const t=await this.fetch(e),s=t.clone();return this.waitUntil(this.cachePut(e,s)),t}async cacheMatch(e){const t=m(e);let s;const{cacheName:n,matchOptions:r}=this._strategy,c=await this.getCacheKey(t,"read"),i=Object.assign(Object.assign({},r),{cacheName:n});s=await caches.match(c,i);for(const o of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await o({cacheName:n,matchOptions:r,cachedResponse:s,request:c,event:this.event})||void 0;return s}async cachePut(e,t){const s=m(e);await A(0);const n=await this.getCacheKey(s,"write");if(!t)throw new l("cache-put-with-no-response",{url:W(n.url)});const r=await this._ensureResponseSafeToCache(t);if(!r)return!1;const{cacheName:c,matchOptions:i}=this._strategy,o=await self.caches.open(c),h=this.hasCallback("cacheDidUpdate"),p=h?await I(o,n.clone(),["__WB_REVISION__"],i):null;try{await o.put(n,h?r.clone():r)}catch(u){if(u instanceof Error)throw u.name==="QuotaExceededError"&&await S(),u}for(const u of this.iterateCallbacks("cacheDidUpdate"))await u({cacheName:c,oldResponse:p,newResponse:r.clone(),request:n,event:this.event});return!0}async getCacheKey(e,t){const s=`${e.url} | ${t}`;if(!this._cacheKeys[s]){let n=e;for(const r of this.iterateCallbacks("cacheKeyWillBeUsed"))n=m(await r({mode:t,request:n,event:this.event,params:this.params}));this._cacheKeys[s]=n}return this._cacheKeys[s]}hasCallback(e){for(const t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(const s of this.iterateCallbacks(e))await s(t)}*iterateCallbacks(e){for(const t of this._strategy.plugins)if(typeof t[e]=="function"){const s=this._pluginStateMap.get(t);yield r=>{const c=Object.assign(Object.assign({},r),{state:s});return t[e](c)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){const e=this._extendLifetimePromises.splice(0),s=(await Promise.allSettled(e)).find(n=>n.status==="rejected");if(s)throw s.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,s=!1;for(const n of this.iterateCallbacks("cacheWillUpdate"))if(t=await n({request:this.request,response:t,event:this.event})||void 0,s=!0,!t)break;return s||t&&t.status!==200&&(t=void 0),t}}class V{constructor(e={}){this.cacheName=C.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){const[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});const t=e.event,s=typeof e.request=="string"?new Request(e.request):e.request,n="params"in e?e.params:void 0,r=new G(this,{event:t,request:s,params:n}),c=this._getResponse(r,s,t),i=this._awaitComplete(c,r,s,t);return[c,i]}async _getResponse(e,t,s){await e.runCallbacks("handlerWillStart",{event:s,request:t});let n;try{if(n=await this._handle(t,e),!n||n.type==="error")throw new l("no-response",{url:t.url})}catch(r){if(r instanceof Error){for(const c of e.iterateCallbacks("handlerDidError"))if(n=await c({error:r,event:s,request:t}),n)break}if(!n)throw r}for(const r of e.iterateCallbacks("handlerWillRespond"))n=await r({event:s,request:t,response:n});return n}async _awaitComplete(e,t,s,n){let r,c;try{r=await e}catch{}try{await t.runCallbacks("handlerDidRespond",{event:n,request:s,response:r}),await t.doneWaiting()}catch(i){i instanceof Error&&(c=i)}if(await t.runCallbacks("handlerDidComplete",{event:n,request:s,response:r,error:c}),t.destroy(),c)throw c}}class d extends V{constructor(e={}){e.cacheName=C.getPrecacheName(e.cacheName),super(e),this._fallbackToNetwork=e.fallbackToNetwork!==!1,this.plugins.push(d.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){const s=await t.cacheMatch(e);return s||(t.event&&t.event.type==="install"?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,t){let s;const n=t.params||{};if(this._fallbackToNetwork){const r=n.integrity,c=e.integrity,i=!c||c===r;s=await t.fetch(new Request(e,{integrity:e.mode!=="no-cors"?c||r:void 0})),r&&i&&e.mode!=="no-cors"&&(this._useDefaultCacheabilityPluginIfNeeded(),await t.cachePut(e,s.clone()))}else throw new l("missing-precache-entry",{cacheName:this.cacheName,url:e.url});return s}async _handleInstall(e,t){this._useDefaultCacheabilityPluginIfNeeded();const s=await t.fetch(e);if(!await t.cachePut(e,s.clone()))throw new l("bad-precaching-response",{url:e.url,status:s.status});return s}_useDefaultCacheabilityPluginIfNeeded(){let e=null,t=0;for(const[s,n]of this.plugins.entries())n!==d.copyRedirectedCacheableResponsesPlugin&&(n===d.defaultPrecacheCacheabilityPlugin&&(e=s),n.cacheWillUpdate&&t++);t===0?this.plugins.push(d.defaultPrecacheCacheabilityPlugin):t>1&&e!==null&&this.plugins.splice(e,1)}}d.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:a}){return!a||a.status>=400?null:a}};d.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:a}){return a.redirected?await q(a):a}};class Q{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:s=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new d({cacheName:C.getPrecacheName(e),plugins:[...t,new $({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this._strategy}precache(e){this.addToCacheList(e),this._installAndActiveListenersAdded||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this._installAndActiveListenersAdded=!0)}addToCacheList(e){const t=[];for(const s of e){typeof s=="string"?t.push(s):s&&s.revision===void 0&&t.push(s.url);const{cacheKey:n,url:r}=F(s),c=typeof s!="string"&&s.revision?"reload":"default";if(this._urlsToCacheKeys.has(r)&&this._urlsToCacheKeys.get(r)!==n)throw new l("add-to-cache-list-conflicting-entries",{firstEntry:this._urlsToCacheKeys.get(r),secondEntry:n});if(typeof s!="string"&&s.integrity){if(this._cacheKeysToIntegrities.has(n)&&this._cacheKeysToIntegrities.get(n)!==s.integrity)throw new l("add-to-cache-list-conflicting-integrities",{url:r});this._cacheKeysToIntegrities.set(n,s.integrity)}if(this._urlsToCacheKeys.set(r,n),this._urlsToCacheModes.set(r,c),t.length>0){const i=`Workbox is precaching URLs without revision info: ${t.join(", ")}
2
- This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(i)}}}install(e){return K(e,async()=>{const t=new B;this.strategy.plugins.push(t);for(const[r,c]of this._urlsToCacheKeys){const i=this._cacheKeysToIntegrities.get(c),o=this._urlsToCacheModes.get(r),h=new Request(r,{integrity:i,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:c},request:h,event:e}))}const{updatedURLs:s,notUpdatedURLs:n}=t;return{updatedURLs:s,notUpdatedURLs:n}})}activate(e){return K(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),s=await t.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(const c of s)n.has(c.url)||(await t.delete(c),r.push(c.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s)return(await self.caches.open(this.strategy.cacheName)).match(s)}createHandlerBoundToURL(e){const t=this.getCacheKeyForURL(e);if(!t)throw new l("non-precached-url",{url:e});return s=>(s.request=new Request(e),s.params=Object.assign({cacheKey:t},s.params),this.strategy.handle(s))}}let U;const L=()=>(U||(U=new Q),U);try{self["workbox:routing:7.4.0"]&&_()}catch{}const v="GET",R=a=>a&&typeof a=="object"?a:{handle:a};class g{constructor(e,t,s=v){this.handler=R(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=R(e)}}class z extends g{constructor(e,t,s){const n=({url:r})=>{const c=e.exec(r.href);if(c&&!(r.origin!==location.origin&&c.index!==0))return c.slice(1)};super(n,t,s)}}class J{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&e.data.type==="CACHE_URLS"){const{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(n=>{typeof n=="string"&&(n=[n]);const r=new Request(...n);return this.handleRequest({request:r,event:e})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:c}=this.findMatchingRoute({event:t,request:e,sameOrigin:n,url:s});let i=c&&c.handler;const o=e.method;if(!i&&this._defaultHandlerMap.has(o)&&(i=this._defaultHandlerMap.get(o)),!i)return;let h;try{h=i.handle({url:s,request:e,event:t,params:r})}catch(u){h=Promise.reject(u)}const p=c&&c.catchHandler;return h instanceof Promise&&(this._catchHandler||p)&&(h=h.catch(async u=>{if(p)try{return await p.handle({url:s,request:e,event:t,params:r})}catch(k){k instanceof Error&&(u=k)}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw u})),h}findMatchingRoute({url:e,sameOrigin:t,request:s,event:n}){const r=this._routes.get(s.method)||[];for(const c of r){let i;const o=c.match({url:e,sameOrigin:t,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o=="boolean")&&(i=void 0),{route:c,params:i}}return{}}setDefaultHandler(e,t=v){this._defaultHandlerMap.set(t,R(e))}setCatchHandler(e){this._catchHandler=R(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});const t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}}let w;const X=()=>(w||(w=new J,w.addFetchListener(),w.addCacheListener()),w);function T(a,e,t){let s;if(typeof a=="string"){const r=new URL(a,location.href),c=({url:i})=>i.href===r.href;s=new g(c,e,t)}else if(a instanceof RegExp)s=new z(a,e,t);else if(typeof a=="function")s=new g(a,e,t);else if(a instanceof g)s=a;else throw new l("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return X().registerRoute(s),s}function Y(a,e=[]){for(const t of[...a.searchParams.keys()])e.some(s=>s.test(t))&&a.searchParams.delete(t);return a}function*Z(a,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:t="index.html",cleanURLs:s=!0,urlManipulation:n}={}){const r=new URL(a,location.href);r.hash="",yield r.href;const c=Y(r,e);if(yield c.href,t&&c.pathname.endsWith("/")){const i=new URL(c.href);i.pathname+=t,yield i.href}if(s){const i=new URL(c.href);i.pathname+=".html",yield i.href}if(n){const i=n({url:r});for(const o of i)yield o.href}}class ee extends g{constructor(e,t){const s=({request:n})=>{const r=e.getURLsToCacheKeys();for(const c of Z(n.url,t)){const i=r.get(c);if(i){const o=e.getIntegrityForCacheKey(i);return{cacheKey:i,integrity:o}}}};super(s,e.strategy)}}function te(a){const e=L(),t=new ee(e,a);T(t)}const se="-precache-",ae=async(a,e=se)=>{const s=(await self.caches.keys()).filter(n=>n.includes(e)&&n.includes(self.registration.scope)&&n!==a);return await Promise.all(s.map(n=>self.caches.delete(n))),s};function ne(){self.addEventListener("activate",(a=>{const e=C.getPrecacheName();a.waitUntil(ae(e).then(t=>{}))}))}function re(a){return L().createHandlerBoundToURL(a)}function ce(a){L().precache(a)}function ie(a,e){ce(a),te(e)}class oe extends g{constructor(e,{allowlist:t=[/./],denylist:s=[]}={}){super(n=>this._match(n),e),this._allowlist=t,this._denylist=s}_match({url:e,request:t}){if(t&&t.mode!=="navigate")return!1;const s=e.pathname+e.search;for(const n of this._denylist)if(n.test(s))return!1;return!!this._allowlist.some(n=>n.test(s))}}self.skipWaiting();j();ie([{"revision":"fab346dacb0b9b9af12554e9a59f004d","url":"logo.svg"},{"revision":"623840baec64a3438663496bb1050d30","url":"logo-docker.svg"},{"revision":"c5c8611b432190ec28522f1a2dfcbb7f","url":"logo-codex.svg"},{"revision":"d25d768699a108499b7a2b21180c1be0","url":"index.html"},{"revision":"1421e9c6cad8218ef5689b85034d3a95","url":"icon-512.png"},{"revision":"b96da5a0b6636012517f54b0e69c304d","url":"icon-192.png"},{"revision":"c64ca8ae8596d4ffe13d0f591beb2274","url":"favicon.svg"},{"revision":"6481face04bd08e6f0bfc2a633840b57","url":"apple-touch-icon.png"},{"revision":"83304194a3c8be2b5b61242eeb1c1046","url":"fonts/MesloLGSNerdFontMono-Regular.woff2"},{"revision":"60fafe18cbcb717c51cdabf87f9490f0","url":"fonts/MesloLGSNerdFontMono-Bold.woff2"},{"revision":null,"url":"assets/workbox-window.prod.es5-BBnX5xw4.js"},{"revision":null,"url":"assets/sw-register-CZ9DmSD-.js"},{"revision":null,"url":"assets/index-S0nvfWUo.js"},{"revision":null,"url":"assets/index-C86AT0KJ.css"},{"revision":null,"url":"assets/TailscalePage-DvlPwchP.js"},{"revision":null,"url":"assets/SettingsPage-BL8vCPyR.js"},{"revision":null,"url":"assets/SandboxManager-VUkfrmv0.js"},{"revision":null,"url":"assets/RunsPage-CxlLKCM8.js"},{"revision":null,"url":"assets/PromptsPage-Bz9Ml49y.js"},{"revision":null,"url":"assets/Playground-Bq0Tks1d.js"},{"revision":null,"url":"assets/LinearSettingsPage-BQSZm4Bq.js"},{"revision":null,"url":"assets/LinearOAuthSettingsPage-6OAQZdqq.js"},{"revision":null,"url":"assets/IntegrationsPage-C5rUwiN9.js"},{"revision":null,"url":"assets/DashboardPage-ukAmlbn5.js"},{"revision":null,"url":"assets/CronManager-YyyPK6hb.js"},{"revision":null,"url":"assets/AgentsPage-D17hofXo.js"}]);ne();T(new oe(re("index.html"),{denylist:[/^\/api/,/^\/ws/]}));
2
+ This is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(i)}}}install(e){return K(e,async()=>{const t=new B;this.strategy.plugins.push(t);for(const[r,c]of this._urlsToCacheKeys){const i=this._cacheKeysToIntegrities.get(c),o=this._urlsToCacheModes.get(r),h=new Request(r,{integrity:i,cache:o,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:c},request:h,event:e}))}const{updatedURLs:s,notUpdatedURLs:n}=t;return{updatedURLs:s,notUpdatedURLs:n}})}activate(e){return K(e,async()=>{const t=await self.caches.open(this.strategy.cacheName),s=await t.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(const c of s)n.has(c.url)||(await t.delete(c),r.push(c.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){const t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){const t=e instanceof Request?e.url:e,s=this.getCacheKeyForURL(t);if(s)return(await self.caches.open(this.strategy.cacheName)).match(s)}createHandlerBoundToURL(e){const t=this.getCacheKeyForURL(e);if(!t)throw new l("non-precached-url",{url:e});return s=>(s.request=new Request(e),s.params=Object.assign({cacheKey:t},s.params),this.strategy.handle(s))}}let U;const L=()=>(U||(U=new Q),U);try{self["workbox:routing:7.4.0"]&&_()}catch{}const v="GET",R=a=>a&&typeof a=="object"?a:{handle:a};class g{constructor(e,t,s=v){this.handler=R(t),this.match=e,this.method=s}setCatchHandler(e){this.catchHandler=R(e)}}class z extends g{constructor(e,t,s){const n=({url:r})=>{const c=e.exec(r.href);if(c&&!(r.origin!==location.origin&&c.index!==0))return c.slice(1)};super(n,t,s)}}class J{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener("fetch",(e=>{const{request:t}=e,s=this.handleRequest({request:t,event:e});s&&e.respondWith(s)}))}addCacheListener(){self.addEventListener("message",(e=>{if(e.data&&e.data.type==="CACHE_URLS"){const{payload:t}=e.data,s=Promise.all(t.urlsToCache.map(n=>{typeof n=="string"&&(n=[n]);const r=new Request(...n);return this.handleRequest({request:r,event:e})}));e.waitUntil(s),e.ports&&e.ports[0]&&s.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){const s=new URL(e.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:r,route:c}=this.findMatchingRoute({event:t,request:e,sameOrigin:n,url:s});let i=c&&c.handler;const o=e.method;if(!i&&this._defaultHandlerMap.has(o)&&(i=this._defaultHandlerMap.get(o)),!i)return;let h;try{h=i.handle({url:s,request:e,event:t,params:r})}catch(u){h=Promise.reject(u)}const p=c&&c.catchHandler;return h instanceof Promise&&(this._catchHandler||p)&&(h=h.catch(async u=>{if(p)try{return await p.handle({url:s,request:e,event:t,params:r})}catch(k){k instanceof Error&&(u=k)}if(this._catchHandler)return this._catchHandler.handle({url:s,request:e,event:t});throw u})),h}findMatchingRoute({url:e,sameOrigin:t,request:s,event:n}){const r=this._routes.get(s.method)||[];for(const c of r){let i;const o=c.match({url:e,sameOrigin:t,request:s,event:n});if(o)return i=o,(Array.isArray(i)&&i.length===0||o.constructor===Object&&Object.keys(o).length===0||typeof o=="boolean")&&(i=void 0),{route:c,params:i}}return{}}setDefaultHandler(e,t=v){this._defaultHandlerMap.set(t,R(e))}setCatchHandler(e){this._catchHandler=R(e)}registerRoute(e){this._routes.has(e.method)||this._routes.set(e.method,[]),this._routes.get(e.method).push(e)}unregisterRoute(e){if(!this._routes.has(e.method))throw new l("unregister-route-but-not-found-with-method",{method:e.method});const t=this._routes.get(e.method).indexOf(e);if(t>-1)this._routes.get(e.method).splice(t,1);else throw new l("unregister-route-route-not-registered")}}let w;const X=()=>(w||(w=new J,w.addFetchListener(),w.addCacheListener()),w);function T(a,e,t){let s;if(typeof a=="string"){const r=new URL(a,location.href),c=({url:i})=>i.href===r.href;s=new g(c,e,t)}else if(a instanceof RegExp)s=new z(a,e,t);else if(typeof a=="function")s=new g(a,e,t);else if(a instanceof g)s=a;else throw new l("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});return X().registerRoute(s),s}function Y(a,e=[]){for(const t of[...a.searchParams.keys()])e.some(s=>s.test(t))&&a.searchParams.delete(t);return a}function*Z(a,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:t="index.html",cleanURLs:s=!0,urlManipulation:n}={}){const r=new URL(a,location.href);r.hash="",yield r.href;const c=Y(r,e);if(yield c.href,t&&c.pathname.endsWith("/")){const i=new URL(c.href);i.pathname+=t,yield i.href}if(s){const i=new URL(c.href);i.pathname+=".html",yield i.href}if(n){const i=n({url:r});for(const o of i)yield o.href}}class ee extends g{constructor(e,t){const s=({request:n})=>{const r=e.getURLsToCacheKeys();for(const c of Z(n.url,t)){const i=r.get(c);if(i){const o=e.getIntegrityForCacheKey(i);return{cacheKey:i,integrity:o}}}};super(s,e.strategy)}}function te(a){const e=L(),t=new ee(e,a);T(t)}const se="-precache-",ae=async(a,e=se)=>{const s=(await self.caches.keys()).filter(n=>n.includes(e)&&n.includes(self.registration.scope)&&n!==a);return await Promise.all(s.map(n=>self.caches.delete(n))),s};function ne(){self.addEventListener("activate",(a=>{const e=C.getPrecacheName();a.waitUntil(ae(e).then(t=>{}))}))}function re(a){return L().createHandlerBoundToURL(a)}function ce(a){L().precache(a)}function ie(a,e){ce(a),te(e)}class oe extends g{constructor(e,{allowlist:t=[/./],denylist:s=[]}={}){super(n=>this._match(n),e),this._allowlist=t,this._denylist=s}_match({url:e,request:t}){if(t&&t.mode!=="navigate")return!1;const s=e.pathname+e.search;for(const n of this._denylist)if(n.test(s))return!1;return!!this._allowlist.some(n=>n.test(s))}}self.skipWaiting();j();ie([{"revision":"fab346dacb0b9b9af12554e9a59f004d","url":"logo.svg"},{"revision":"623840baec64a3438663496bb1050d30","url":"logo-docker.svg"},{"revision":"c5c8611b432190ec28522f1a2dfcbb7f","url":"logo-codex.svg"},{"revision":"4f8bb842832a3076ea5fd36837194001","url":"index.html"},{"revision":"1421e9c6cad8218ef5689b85034d3a95","url":"icon-512.png"},{"revision":"b96da5a0b6636012517f54b0e69c304d","url":"icon-192.png"},{"revision":"c64ca8ae8596d4ffe13d0f591beb2274","url":"favicon.svg"},{"revision":"6481face04bd08e6f0bfc2a633840b57","url":"apple-touch-icon.png"},{"revision":"83304194a3c8be2b5b61242eeb1c1046","url":"fonts/MesloLGSNerdFontMono-Regular.woff2"},{"revision":"60fafe18cbcb717c51cdabf87f9490f0","url":"fonts/MesloLGSNerdFontMono-Bold.woff2"},{"revision":null,"url":"assets/workbox-window.prod.es5-BBnX5xw4.js"},{"revision":null,"url":"assets/sw-register-A0FMc-CY.js"},{"revision":null,"url":"assets/index-C86AT0KJ.css"},{"revision":null,"url":"assets/index-B6xqr-v6.js"},{"revision":null,"url":"assets/TailscalePage-DPJGhCUO.js"},{"revision":null,"url":"assets/SettingsPage-CFe6_dW9.js"},{"revision":null,"url":"assets/SandboxManager-BNIPWLHz.js"},{"revision":null,"url":"assets/RunsPage-Bn7SIAEx.js"},{"revision":null,"url":"assets/PromptsPage-BF5QRCM7.js"},{"revision":null,"url":"assets/Playground-Z8gfp_Pg.js"},{"revision":null,"url":"assets/LinearSettingsPage-DZbV5ES0.js"},{"revision":null,"url":"assets/LinearOAuthSettingsPage-aKHb8Kgv.js"},{"revision":null,"url":"assets/IntegrationsPage-ClHcXTNR.js"},{"revision":null,"url":"assets/DashboardPage-DFgvPALP.js"},{"revision":null,"url":"assets/CronManager-B1s3pGHK.js"},{"revision":null,"url":"assets/AgentsPage-BBi5x0sS.js"}]);ne();T(new oe(re("index.html"),{denylist:[/^\/api/,/^\/ws/]}));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hellcoder/companion",
3
- "version": "0.112.1-preview.20260728132124.bf82019",
3
+ "version": "0.113.0",
4
4
  "type": "module",
5
5
  "description": "Web UI for launching and interacting with Claude Code agents — Moritz Edition (fork of the-companion)",
6
6
  "license": "MIT",
@@ -55,6 +55,7 @@ vi.mock("./settings-manager.js", () => ({
55
55
  }));
56
56
 
57
57
  import { ClaudeAdapter } from "./claude-adapter.js";
58
+ import type { BrowserIncomingMessage } from "./session-types.js";
58
59
  import { log } from "./logger.js";
59
60
 
60
61
  // ─── Mock socket factory ────────────────────────────────────────────────────
@@ -2319,3 +2320,73 @@ describe("stderr tail on kill warnings", () => {
2319
2320
  expect(wedgeWarning()).toBeDefined();
2320
2321
  });
2321
2322
  });
2323
+
2324
+ /**
2325
+ * rate_limit_event was previously discarded with the comment "no user-facing
2326
+ * action needed". Recorded traffic disagrees: across 200 events there were 14
2327
+ * `allowed_warning` and 3 `rejected`, none of which reached the UI or the logs.
2328
+ * It is also the only signal we get about plan-window usage.
2329
+ */
2330
+ describe("rate_limit_event surfacing", () => {
2331
+ it("forwards the info to the browser", async () => {
2332
+ const emitted: BrowserIncomingMessage[] = [];
2333
+ const a = new ClaudeAdapter("rl-1");
2334
+ a.onBrowserMessage((m) => emitted.push(m));
2335
+ const { proc, pushStdout } = createMockProc();
2336
+ a.attachStdio(proc);
2337
+
2338
+ pushStdout(JSON.stringify({
2339
+ type: "rate_limit_event",
2340
+ rate_limit_info: {
2341
+ status: "allowed_warning",
2342
+ utilization: 0.95,
2343
+ rateLimitType: "five_hour",
2344
+ resetsAt: 1784441400,
2345
+ },
2346
+ uuid: "rl-uuid-1",
2347
+ session_id: "s",
2348
+ }) + "\n");
2349
+ await new Promise((r) => setTimeout(r, 20));
2350
+
2351
+ const msg = emitted.find((m) => m.type === "rate_limit") as
2352
+ | { type: "rate_limit"; info: Record<string, unknown> }
2353
+ | undefined;
2354
+ expect(msg).toBeDefined();
2355
+ expect(msg?.info.status).toBe("allowed_warning");
2356
+ // Utilization must survive intact — it drives the meter fill.
2357
+ expect(msg?.info.utilization).toBe(0.95);
2358
+ });
2359
+
2360
+ it("still forwards allowed events so the UI can clear a stale warning", async () => {
2361
+ // If only warnings were forwarded, a session would stay visually throttled
2362
+ // after recovering.
2363
+ const emitted: BrowserIncomingMessage[] = [];
2364
+ const a = new ClaudeAdapter("rl-2");
2365
+ a.onBrowserMessage((m) => emitted.push(m));
2366
+ const { proc, pushStdout } = createMockProc();
2367
+ a.attachStdio(proc);
2368
+
2369
+ pushStdout(JSON.stringify({
2370
+ type: "rate_limit_event",
2371
+ rate_limit_info: { status: "allowed", rateLimitType: "five_hour" },
2372
+ session_id: "s",
2373
+ }) + "\n");
2374
+ await new Promise((r) => setTimeout(r, 20));
2375
+
2376
+ expect(emitted.some((m) => m.type === "rate_limit")).toBe(true);
2377
+ });
2378
+
2379
+ it("tolerates a missing rate_limit_info without throwing", async () => {
2380
+ // A throw here would kill the stdout reader for the life of the process —
2381
+ // the exact failure the e2e harness hit with Bun.hash.
2382
+ const a = new ClaudeAdapter("rl-3");
2383
+ const { proc, pushStdout } = createMockProc();
2384
+ a.attachStdio(proc);
2385
+
2386
+ expect(() => {
2387
+ pushStdout(JSON.stringify({ type: "rate_limit_event", session_id: "s" }) + "\n");
2388
+ }).not.toThrow();
2389
+ await new Promise((r) => setTimeout(r, 20));
2390
+ expect(a.isConnected()).toBe(true);
2391
+ });
2392
+ });
@@ -30,6 +30,7 @@ import type {
30
30
  CLIControlResponseMessage,
31
31
  CLIAuthStatusMessage,
32
32
  CLIControlCancelRequestMessage,
33
+ CLIRateLimitEventMessage,
33
34
  CLIStreamlinedTextMessage,
34
35
  CLIStreamlinedToolUseSummaryMessage,
35
36
  CLIPromptSuggestionMessage,
@@ -1394,10 +1395,28 @@ export class ClaudeAdapter implements IBackendAdapter {
1394
1395
  // to avoid rendering raw tool_result JSON in the chat UI.
1395
1396
  break;
1396
1397
 
1397
- case "rate_limit_event":
1398
- // Rate-limit status from Claude API (allowed/throttled). Silently
1399
- // consumed no user-facing action needed.
1398
+ case "rate_limit_event": {
1399
+ // Previously discarded. It is the only signal we get about plan-window
1400
+ // usage, and recorded traffic shows it does fire in anger (14 warning
1401
+ // and 3 rejected events across 200 samples) — while the UI said nothing
1402
+ // and the logs kept no record.
1403
+ const info = (msg as CLIRateLimitEventMessage).rate_limit_info ?? {};
1404
+ // Only non-`allowed` states are logged: `allowed` arrives constantly and
1405
+ // would drown the log, whereas a warning or rejection is exactly what a
1406
+ // later investigation needs to find.
1407
+ if (info.status && info.status !== "allowed") {
1408
+ log.warn("claude-adapter", "API rate limit", {
1409
+ sessionId: this.sessionId,
1410
+ status: info.status,
1411
+ utilization: info.utilization,
1412
+ rateLimitType: info.rateLimitType,
1413
+ resetsAt: info.resetsAt,
1414
+ isUsingOverage: info.isUsingOverage,
1415
+ });
1416
+ }
1417
+ this.browserMessageCb?.({ type: "rate_limit", info });
1400
1418
  break;
1419
+ }
1401
1420
 
1402
1421
  case "control_cancel_request":
1403
1422
  this.handleControlCancelRequest(msg as CLIControlCancelRequestMessage);
@@ -297,10 +297,34 @@ export interface CLIUserEchoMessage {
297
297
  session_id?: string;
298
298
  }
299
299
 
300
+ /**
301
+ * Rate-limit status from the Claude API.
302
+ *
303
+ * Field shapes verified against 200 recorded events: status was `allowed`
304
+ * (183), `allowed_warning` (14) and `rejected` (3); `utilization` and
305
+ * `surpassedThreshold` appear on warning events, and `rateLimitType` was
306
+ * always `five_hour` — the plan window, not the per-minute RPM/TPM cap.
307
+ */
308
+ export interface RateLimitInfo {
309
+ /** `allowed` | `allowed_warning` | `rejected`, per observed traffic. */
310
+ status?: string;
311
+ /** Fraction of the window consumed, 0-1 (e.g. 0.95). Warning events only. */
312
+ utilization?: number;
313
+ /** The threshold that tripped this warning, 0-1 (e.g. 0.9). */
314
+ surpassedThreshold?: number;
315
+ /** Unix seconds at which the window resets. */
316
+ resetsAt?: number;
317
+ /** e.g. "five_hour". */
318
+ rateLimitType?: string;
319
+ overageStatus?: string;
320
+ overageDisabledReason?: string;
321
+ isUsingOverage?: boolean;
322
+ }
323
+
300
324
  /** Rate-limit status from Claude API (allowed/throttled). */
301
325
  export interface CLIRateLimitEventMessage {
302
326
  type: "rate_limit_event";
303
- rate_limit_info: Record<string, unknown>;
327
+ rate_limit_info: RateLimitInfo & Record<string, unknown>;
304
328
  uuid?: string;
305
329
  }
306
330
 
@@ -414,6 +438,7 @@ export type BrowserIncomingMessageBase =
414
438
  | { type: "session_name_update"; name: string }
415
439
  | { type: "pr_status_update"; pr: import("./github-pr.js").GitHubPRInfo | null; available: boolean }
416
440
  | { type: "mcp_status"; servers: McpServerDetail[] }
441
+ | { type: "rate_limit"; info: RateLimitInfo }
417
442
  | { type: "session_phase"; phase: SessionPhase; previousPhase: SessionPhase }
418
443
  | { type: "prompt_suggestion"; suggestions: string[] }
419
444
  | { type: "streamlined_text"; text: string }
@@ -1996,6 +1996,50 @@ describe("CLI message routing", () => {
1996
1996
  }
1997
1997
  });
1998
1998
 
1999
+ /**
2000
+ * Regression: the server-owned-adapter disconnect path serves BOTH stdio
2001
+ * Claude and Codex, but every label on it said "Codex" — so a Claude
2002
+ * session's disconnects were filed under the wrong backend and any forensic
2003
+ * search for them looked under the wrong term. Observed live:
2004
+ * reason=codex_disconnect_confirmed backendType=claude
2005
+ */
2006
+ it("labels adapter disconnects with the session's actual backend", async () => {
2007
+ vi.useFakeTimers();
2008
+ try {
2009
+ const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
2010
+ const session = bridge.getOrCreateSession("s1");
2011
+ expect(session.backendType).toBe("claude");
2012
+
2013
+ const browser = makeBrowserSocket("s1");
2014
+ bridge.handleBrowserOpen(browser, "s1");
2015
+
2016
+ let disconnectCb: (() => void) | undefined;
2017
+ const adapter = {
2018
+ isConnected: () => false,
2019
+ send: () => true,
2020
+ disconnect: async () => {},
2021
+ onBrowserMessage: () => {},
2022
+ onSessionMeta: () => {},
2023
+ onDisconnect: (cb: () => void) => { disconnectCb = cb; },
2024
+ onInitError: () => {},
2025
+ };
2026
+ bridge.attachBackendAdapter("s1", adapter as never, "claude");
2027
+
2028
+ disconnectCb!();
2029
+ await vi.advanceTimersByTimeAsync(10_000);
2030
+
2031
+ const banner = warnSpy.mock.calls
2032
+ .map(([a]) => String(a))
2033
+ .find((l) => l.includes("UI: CLI disconnected banner shown"));
2034
+ expect(banner).toBeDefined();
2035
+ // A Claude session must never be filed under codex_*.
2036
+ expect(banner).toContain("claude_disconnect_confirmed");
2037
+ expect(banner).not.toContain("codex_disconnect_confirmed");
2038
+ } finally {
2039
+ vi.useRealTimers();
2040
+ }
2041
+ });
2042
+
1999
2043
  it("tool_progress: broadcasts", async () => {
2000
2044
  const msg = JSON.stringify({
2001
2045
  type: "tool_progress",
@@ -669,9 +669,17 @@ export class WsBridge {
669
669
  // "reconnecting" with a short debounce (5s vs 15s for the legacy WS
670
670
  // Claude transport, since there's no WebSocket cycling).
671
671
  session.backendAdapter = null;
672
- session.stateMachine.transition("reconnecting", "codex_adapter_disconnected");
672
+ // This path serves BOTH stdio Claude and Codex, but every label here used
673
+ // to say "Codex" — so a Claude session's disconnects were filed under the
674
+ // wrong backend in the logs, and forensics silently searched the wrong
675
+ // term. Label from the session's actual backend instead.
676
+ const backend = session.backendType;
677
+ session.stateMachine.transition("reconnecting", `${backend}_adapter_disconnected`);
673
678
  this.persistSession(session);
674
- log.info("ws-bridge", "Codex adapter disconnected, starting debounce", { sessionId });
679
+ log.info("ws-bridge", "Backend adapter disconnected, starting debounce", {
680
+ sessionId,
681
+ backendType: backend,
682
+ });
675
683
 
676
684
  const existing = this.disconnectTimers.get(sessionId);
677
685
  if (existing) clearTimeout(existing);
@@ -680,14 +688,14 @@ export class WsBridge {
680
688
  // Check if a new adapter reconnected during the grace period
681
689
  if (session.backendAdapter?.isConnected()) return;
682
690
 
683
- log.warn("ws-bridge", "Codex disconnect confirmed", { sessionId });
691
+ log.warn("ws-bridge", "Backend disconnect confirmed", { sessionId, backendType: backend });
684
692
  for (const [reqId] of session.pendingPermissions) {
685
693
  this.broadcastToBrowsers(session, { type: "permission_cancelled", request_id: reqId });
686
694
  }
687
695
  session.pendingPermissions.clear();
688
696
  session.stateMachine.transition("terminated", "disconnect_confirmed");
689
697
  this.persistSession(session);
690
- this.broadcastCliDisconnected(session, "codex_disconnect_confirmed");
698
+ this.broadcastCliDisconnected(session, `${backend}_disconnect_confirmed`);
691
699
 
692
700
  // Request auto-relaunch regardless of browser state — proactive
693
701
  // keepalive in the orchestrator ensures headless sessions stay alive.