@hellcoder/companion 0.105.1 → 0.105.2-preview.20260701003146.464c71f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{AgentsPage-eLLQNxgP.js → AgentsPage-D1iuUiHX.js} +1 -1
- package/dist/assets/{CronManager-DDwptxks.js → CronManager-CLKlW4pc.js} +1 -1
- package/dist/assets/{IntegrationsPage-BExX5JOb.js → IntegrationsPage-CY3rD93q.js} +1 -1
- package/dist/assets/{LinearOAuthSettingsPage--m6I9icx.js → LinearOAuthSettingsPage-CaNewlOp.js} +1 -1
- package/dist/assets/{LinearSettingsPage-4_3ZJrKS.js → LinearSettingsPage-B-9ZMgYC.js} +1 -1
- package/dist/assets/{Playground-BBt78cYF.js → Playground-BqRkgMBH.js} +1 -1
- package/dist/assets/{PromptsPage-_B61JVQm.js → PromptsPage-BA_qgnJV.js} +1 -1
- package/dist/assets/{RunsPage-CgUwAzRE.js → RunsPage-CBmACxTt.js} +1 -1
- package/dist/assets/{SandboxManager-CX_Zi6K3.js → SandboxManager-eWZ5PosF.js} +1 -1
- package/dist/assets/{SettingsPage-BLax9147.js → SettingsPage-DGA37UhJ.js} +1 -1
- package/dist/assets/{TailscalePage-Buy9PO3F.js → TailscalePage-L4M_MY6O.js} +1 -1
- package/dist/assets/index-D3RdMj2z.js +136 -0
- package/dist/assets/{index-psWeuMrk.css → index-D4AeOLki.css} +1 -1
- package/dist/assets/{sw-register-BPMmALbK.js → sw-register-CWd_l93k.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/package.json +1 -1
- package/server/claude-adapter.test.ts +62 -0
- package/server/claude-adapter.ts +74 -17
- package/dist/assets/index-DeUv1epH.js +0 -136
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":"
|
|
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":"5ec7302990e9f7627b289621960201ec","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-CWd_l93k.js"},{"revision":null,"url":"assets/index-D4AeOLki.css"},{"revision":null,"url":"assets/index-D3RdMj2z.js"},{"revision":null,"url":"assets/TailscalePage-L4M_MY6O.js"},{"revision":null,"url":"assets/SettingsPage-DGA37UhJ.js"},{"revision":null,"url":"assets/SandboxManager-eWZ5PosF.js"},{"revision":null,"url":"assets/RunsPage-CBmACxTt.js"},{"revision":null,"url":"assets/PromptsPage-BA_qgnJV.js"},{"revision":null,"url":"assets/Playground-BqRkgMBH.js"},{"revision":null,"url":"assets/LinearSettingsPage-B-9ZMgYC.js"},{"revision":null,"url":"assets/LinearOAuthSettingsPage-CaNewlOp.js"},{"revision":null,"url":"assets/IntegrationsPage-CY3rD93q.js"},{"revision":null,"url":"assets/CronManager-CLKlW4pc.js"},{"revision":null,"url":"assets/AgentsPage-D1iuUiHX.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.105.
|
|
3
|
+
"version": "0.105.2-preview.20260701003146.464c71f",
|
|
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",
|
|
@@ -1522,6 +1522,7 @@ function createMockProc() {
|
|
|
1522
1522
|
return {
|
|
1523
1523
|
proc: proc as any,
|
|
1524
1524
|
endStdout: () => stdoutController.close(),
|
|
1525
|
+
pushStdout: (line: string) => stdoutController.enqueue(new TextEncoder().encode(line)),
|
|
1525
1526
|
resolveExit: () => {
|
|
1526
1527
|
proc.exitCode = 0;
|
|
1527
1528
|
resolveExit();
|
|
@@ -1578,6 +1579,67 @@ describe("stdio transport disconnect propagation", () => {
|
|
|
1578
1579
|
}
|
|
1579
1580
|
});
|
|
1580
1581
|
|
|
1582
|
+
it("does NOT kill a process that closed stdout after a terminal result, even past the short grace", async () => {
|
|
1583
|
+
// Regression for the dominant 143-exit relaunch churn on busy hosts: after a
|
|
1584
|
+
// turn-ending `result`, the CLI closes stdout but can take longer than the
|
|
1585
|
+
// short (mid-stream) grace to flush teardown (MCP servers, etc.) and exit
|
|
1586
|
+
// code-0. Because the last substantive message was a `result`, the adapter
|
|
1587
|
+
// must treat the EOF as a clean shutdown and wait the longer result-grace
|
|
1588
|
+
// instead of SIGTERM-ing a code-0 exit into a 143 + needless relaunch.
|
|
1589
|
+
vi.useFakeTimers();
|
|
1590
|
+
try {
|
|
1591
|
+
const { proc, endStdout, pushStdout, resolveExit } = createMockProc();
|
|
1592
|
+
adapter.attachStdio(proc);
|
|
1593
|
+
|
|
1594
|
+
// The CLI finishes a turn cleanly, then its stdout EOFs while still alive.
|
|
1595
|
+
pushStdout('{"type":"result","subtype":"success"}\n');
|
|
1596
|
+
await vi.advanceTimersByTimeAsync(0); // let the reader route the result
|
|
1597
|
+
endStdout();
|
|
1598
|
+
|
|
1599
|
+
// Past the SHORT grace (2s) the process is still alive — but the result
|
|
1600
|
+
// marker means we must NOT kill it yet (the mid-stream path would have).
|
|
1601
|
+
await vi.advanceTimersByTimeAsync(2100);
|
|
1602
|
+
expect(proc.kill).not.toHaveBeenCalled();
|
|
1603
|
+
expect(adapter.isConnected()).toBe(true); // still finishing its clean exit
|
|
1604
|
+
|
|
1605
|
+
// It exits on its own within the longer result grace → never killed.
|
|
1606
|
+
resolveExit();
|
|
1607
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
1608
|
+
expect(proc.kill).not.toHaveBeenCalled();
|
|
1609
|
+
expect(adapter.isConnected()).toBe(false);
|
|
1610
|
+
expect(disconnectCb).toHaveBeenCalledTimes(1);
|
|
1611
|
+
} finally {
|
|
1612
|
+
vi.useRealTimers();
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
|
|
1616
|
+
it("kills a process that wedges AFTER a result, once the longer result grace elapses", async () => {
|
|
1617
|
+
// The result-grace is generous but BOUNDED: a turn can end cleanly and the
|
|
1618
|
+
// process then deadlock in teardown and never exit. Recovery must not be
|
|
1619
|
+
// blocked forever — past the longer grace the wedged process is still killed.
|
|
1620
|
+
vi.useFakeTimers();
|
|
1621
|
+
try {
|
|
1622
|
+
const { proc, endStdout, pushStdout } = createMockProc();
|
|
1623
|
+
adapter.attachStdio(proc);
|
|
1624
|
+
|
|
1625
|
+
pushStdout('{"type":"result","subtype":"success"}\n');
|
|
1626
|
+
await vi.advanceTimersByTimeAsync(0);
|
|
1627
|
+
endStdout();
|
|
1628
|
+
|
|
1629
|
+
// Short grace passes without a kill (result path uses the longer grace)...
|
|
1630
|
+
await vi.advanceTimersByTimeAsync(2100);
|
|
1631
|
+
expect(proc.kill).not.toHaveBeenCalled();
|
|
1632
|
+
|
|
1633
|
+
// ...but past the longer result grace (10s) with no self-exit → killed.
|
|
1634
|
+
await vi.advanceTimersByTimeAsync(8000);
|
|
1635
|
+
expect(proc.kill).toHaveBeenCalledTimes(1);
|
|
1636
|
+
expect(adapter.isConnected()).toBe(false);
|
|
1637
|
+
expect(disconnectCb).toHaveBeenCalledTimes(1);
|
|
1638
|
+
} finally {
|
|
1639
|
+
vi.useRealTimers();
|
|
1640
|
+
}
|
|
1641
|
+
});
|
|
1642
|
+
|
|
1581
1643
|
it("fires disconnect on process exit", async () => {
|
|
1582
1644
|
const { proc, resolveExit } = createMockProc();
|
|
1583
1645
|
adapter.attachStdio(proc);
|
package/server/claude-adapter.ts
CHANGED
|
@@ -65,6 +65,15 @@ const CLI_DEDUP_WINDOW = 2000;
|
|
|
65
65
|
*/
|
|
66
66
|
const STDOUT_CLOSE_GRACE_MS = Number(process.env.COMPANION_STDOUT_CLOSE_GRACE_MS) || 2000;
|
|
67
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Longer grace used when stdout EOFs right after a terminal `result` (a clean
|
|
70
|
+
* end-of-turn shutdown). We give the process generous time to flush teardown
|
|
71
|
+
* (MCP servers, etc.) and exit code-0 on its own instead of SIGTERM-ing it.
|
|
72
|
+
* Still bounded so a process that wedges *after* a result (e.g. a teardown
|
|
73
|
+
* deadlock) cannot hang the reader and block recovery indefinitely.
|
|
74
|
+
*/
|
|
75
|
+
const STDOUT_CLOSE_RESULT_GRACE_MS = Number(process.env.COMPANION_STDOUT_CLOSE_RESULT_GRACE_MS) || 10000;
|
|
76
|
+
|
|
68
77
|
// --- Claude Code Adapter ------------------------------------------------------
|
|
69
78
|
|
|
70
79
|
export class ClaudeAdapter implements IBackendAdapter {
|
|
@@ -92,6 +101,14 @@ export class ClaudeAdapter implements IBackendAdapter {
|
|
|
92
101
|
* matter which teardown path (stdout reader end, reader error, or process
|
|
93
102
|
* exit) trips first. Mirrors CodexAdapter.disconnectFired. */
|
|
94
103
|
private disconnectFired = false;
|
|
104
|
+
/** Whether the most recent substantive CLI message was a terminal `result`
|
|
105
|
+
* (end-of-turn). Used by the stdout-EOF handler to distinguish a process
|
|
106
|
+
* that closed stdout while *finishing a turn cleanly* (exiting on its own —
|
|
107
|
+
* do not force-kill) from one that EOF'd mid-stream (a genuine wedge that
|
|
108
|
+
* must be killed so recovery can proceed). Transport-level noise
|
|
109
|
+
* (`keep_alive`, `system` status/keepalive) does not reset it; any
|
|
110
|
+
* substantive turn activity does. */
|
|
111
|
+
private lastInboundWasResult = false;
|
|
95
112
|
|
|
96
113
|
// Callbacks registered by the bridge via on*() methods
|
|
97
114
|
private browserMessageCb: ((msg: BrowserIncomingMessage) => void) | null = null;
|
|
@@ -294,25 +311,54 @@ export class ClaudeAdapter implements IBackendAdapter {
|
|
|
294
311
|
// (b) Wedged — it stopped emitting (e.g. after a fatal API error such
|
|
295
312
|
// as a 429) but will never exit, leaving `handleAutoRelaunch`'s
|
|
296
313
|
// PID-liveness guard satisfied and blocking recovery indefinitely.
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
//
|
|
314
|
+
// We distinguish them two ways and only kill genuine wedges (b):
|
|
315
|
+
// 1. If the last substantive message was a terminal `result`, the CLI
|
|
316
|
+
// finished a turn and is shutting down cleanly — wait a generous
|
|
317
|
+
// (but bounded) grace for it to exit on its own rather than SIGTERM a
|
|
318
|
+
// code-0 exit. This is the dominant source of the 143-exit relaunch
|
|
319
|
+
// churn on busy hosts.
|
|
320
|
+
// 2. Otherwise (mid-stream EOF) give it a short grace; kill only if it
|
|
321
|
+
// is STILL alive afterwards, so a true wedge can't block recovery.
|
|
300
322
|
const proc = this.stdioProc;
|
|
301
323
|
if (proc && proc.exitCode === null && !proc.killed) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
324
|
+
if (this.lastInboundWasResult) {
|
|
325
|
+
// Clean end-of-turn shutdown: let the process flush teardown (MCP
|
|
326
|
+
// servers, etc.) and exit code-0 instead of SIGTERM-ing a code-0 exit.
|
|
327
|
+
// Use a generous grace so we don't clobber a slow-but-clean exit, but
|
|
328
|
+
// keep it bounded — a process that wedges *after* a result must still
|
|
329
|
+
// be killed so recovery isn't blocked indefinitely.
|
|
330
|
+
const exitedOnOwn = await Promise.race([
|
|
331
|
+
proc.exited.then(() => true),
|
|
332
|
+
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), STDOUT_CLOSE_RESULT_GRACE_MS)),
|
|
333
|
+
]);
|
|
334
|
+
if (!exitedOnOwn && proc.exitCode === null && !proc.killed) {
|
|
335
|
+
log.warn("claude-adapter", "stdout closed after result but process did not exit within grace; killing wedged process", {
|
|
336
|
+
sessionId: this.sessionId,
|
|
337
|
+
pid: proc.pid,
|
|
338
|
+
graceMs: STDOUT_CLOSE_RESULT_GRACE_MS,
|
|
339
|
+
});
|
|
340
|
+
try {
|
|
341
|
+
proc.kill();
|
|
342
|
+
} catch {
|
|
343
|
+
// Process may have exited between the check and the kill.
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
const exitedOnOwn = await Promise.race([
|
|
348
|
+
proc.exited.then(() => true),
|
|
349
|
+
new Promise<boolean>((resolve) => setTimeout(() => resolve(false), STDOUT_CLOSE_GRACE_MS)),
|
|
350
|
+
]);
|
|
351
|
+
if (!exitedOnOwn && proc.exitCode === null && !proc.killed) {
|
|
352
|
+
log.warn("claude-adapter", "stdout closed mid-stream and process did not exit within grace; killing wedged process", {
|
|
353
|
+
sessionId: this.sessionId,
|
|
354
|
+
pid: proc.pid,
|
|
355
|
+
graceMs: STDOUT_CLOSE_GRACE_MS,
|
|
356
|
+
});
|
|
357
|
+
try {
|
|
358
|
+
proc.kill();
|
|
359
|
+
} catch {
|
|
360
|
+
// Process may have exited between the check and the kill.
|
|
361
|
+
}
|
|
316
362
|
}
|
|
317
363
|
}
|
|
318
364
|
}
|
|
@@ -743,6 +789,17 @@ export class ClaudeAdapter implements IBackendAdapter {
|
|
|
743
789
|
this.onActivityUpdate?.();
|
|
744
790
|
}
|
|
745
791
|
|
|
792
|
+
// Track whether the last substantive message was a turn-ending `result`, so
|
|
793
|
+
// the stdout-EOF handler can tell a clean end-of-turn shutdown apart from a
|
|
794
|
+
// mid-stream wedge. `result` marks end-of-turn; any other substantive turn
|
|
795
|
+
// activity clears it. Transport-level noise (`keep_alive`, `system`) is
|
|
796
|
+
// ignored so trailing status/keepalive lines after a result don't mask it.
|
|
797
|
+
if (msg.type === "result") {
|
|
798
|
+
this.lastInboundWasResult = true;
|
|
799
|
+
} else if (msg.type !== "keep_alive" && msg.type !== "system") {
|
|
800
|
+
this.lastInboundWasResult = false;
|
|
801
|
+
}
|
|
802
|
+
|
|
746
803
|
switch (msg.type) {
|
|
747
804
|
case "system":
|
|
748
805
|
this.handleSystemMessage(msg);
|