@hellcoder/companion 0.102.0 → 0.102.1
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-uXDPdxng.js → AgentsPage-DMd0B7DV.js} +1 -1
- package/dist/assets/{CronManager-BHTueSuv.js → CronManager-Cc1g1uc_.js} +1 -1
- package/dist/assets/{IntegrationsPage-BIAuw3fS.js → IntegrationsPage-CmhW-5Oz.js} +1 -1
- package/dist/assets/{LinearOAuthSettingsPage-CTlEsf3M.js → LinearOAuthSettingsPage-Zj44jouA.js} +1 -1
- package/dist/assets/{LinearSettingsPage-BErRFO3Z.js → LinearSettingsPage-Berx9B0o.js} +1 -1
- package/dist/assets/{Playground-Bj6JwBpM.js → Playground-Cv0nNeBH.js} +1 -1
- package/dist/assets/{PromptsPage-BEvGho2w.js → PromptsPage-CEVBQ8dD.js} +1 -1
- package/dist/assets/{RunsPage-CLUH_HTH.js → RunsPage-CAOTAlme.js} +1 -1
- package/dist/assets/SandboxManager-K86fOiMy.js +1 -0
- package/dist/assets/{SettingsPage-CreWaJD6.js → SettingsPage-CNeDZmJh.js} +1 -1
- package/dist/assets/{TailscalePage-BqCQBn1C.js → TailscalePage-DHn8CkNO.js} +1 -1
- package/dist/assets/index-DToro0X1.js +134 -0
- package/dist/assets/{index-BVs_mQXt.css → index-P1hpzChp.css} +1 -1
- package/dist/assets/{sw-register-CeyFsabK.js → sw-register-CDS-EAiG.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +1 -1
- package/package.json +1 -1
- package/server/feature-flags.test.ts +31 -0
- package/server/feature-flags.ts +18 -0
- package/server/image-pull-manager.test.ts +17 -0
- package/server/image-pull-manager.ts +12 -0
- package/server/routes/env-routes.test.ts +2 -0
- package/server/routes/env-routes.ts +4 -0
- package/server/routes/sandbox-routes.test.ts +2 -0
- package/server/routes/sandbox-routes.ts +5 -0
- package/server/routes/system-routes.ts +4 -2
- package/server/session-creation-service.test.ts +16 -0
- package/server/session-creation-service.ts +5 -2
- package/server/session-orchestrator.test.ts +2 -0
- package/server/session-orchestrator.ts +5 -2
- package/dist/assets/SandboxManager-BbBqapwZ.js +0 -8
- package/dist/assets/index-CO5E0NEG.js +0 -134
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":"0c3c541750418120b23599c83babb648","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-CDS-EAiG.js"},{"revision":null,"url":"assets/index-P1hpzChp.css"},{"revision":null,"url":"assets/index-DToro0X1.js"},{"revision":null,"url":"assets/TailscalePage-DHn8CkNO.js"},{"revision":null,"url":"assets/SettingsPage-CNeDZmJh.js"},{"revision":null,"url":"assets/SandboxManager-K86fOiMy.js"},{"revision":null,"url":"assets/RunsPage-CAOTAlme.js"},{"revision":null,"url":"assets/PromptsPage-CEVBQ8dD.js"},{"revision":null,"url":"assets/Playground-Cv0nNeBH.js"},{"revision":null,"url":"assets/LinearSettingsPage-Berx9B0o.js"},{"revision":null,"url":"assets/LinearOAuthSettingsPage-Zj44jouA.js"},{"revision":null,"url":"assets/IntegrationsPage-CmhW-5Oz.js"},{"revision":null,"url":"assets/CronManager-Cc1g1uc_.js"},{"revision":null,"url":"assets/AgentsPage-DMd0B7DV.js"}]);ne();T(new oe(re("index.html"),{denylist:[/^\/api/,/^\/ws/]}));
|
package/package.json
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
2
|
+
import { isSandboxEnabled } from "./feature-flags.js";
|
|
3
|
+
|
|
4
|
+
// The sandbox kill switch defaults OFF (the upstream image is unmaintained) and
|
|
5
|
+
// is opt-in via COMPANION_SANDBOX_ENABLED. It must read the env live so runtime
|
|
6
|
+
// toggles take effect.
|
|
7
|
+
describe("isSandboxEnabled", () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
delete process.env.COMPANION_SANDBOX_ENABLED;
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("is disabled by default", () => {
|
|
13
|
+
delete process.env.COMPANION_SANDBOX_ENABLED;
|
|
14
|
+
expect(isSandboxEnabled()).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('is enabled when set to "1"', () => {
|
|
18
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
19
|
+
expect(isSandboxEnabled()).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('is enabled when set to "true"', () => {
|
|
23
|
+
process.env.COMPANION_SANDBOX_ENABLED = "true";
|
|
24
|
+
expect(isSandboxEnabled()).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("is disabled for any other value", () => {
|
|
28
|
+
process.env.COMPANION_SANDBOX_ENABLED = "yes";
|
|
29
|
+
expect(isSandboxEnabled()).toBe(false);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature flags for the fork.
|
|
3
|
+
*
|
|
4
|
+
* SANDBOX / container support is DISABLED by default because it depends on the
|
|
5
|
+
* upstream, no-longer-maintained `docker.io/stangirard/the-companion` image.
|
|
6
|
+
* Running an unmaintained image (privileged, no less) is a security risk, so the
|
|
7
|
+
* fork ships with it off.
|
|
8
|
+
*
|
|
9
|
+
* The code is intentionally kept intact: set COMPANION_SANDBOX_ENABLED=1 (or
|
|
10
|
+
* "true") to re-enable it — e.g. once we publish and point at our own image.
|
|
11
|
+
*
|
|
12
|
+
* Read as a function (not a cached const) so tests and runtime env changes take
|
|
13
|
+
* effect without reimporting the module.
|
|
14
|
+
*/
|
|
15
|
+
export function isSandboxEnabled(): boolean {
|
|
16
|
+
const v = process.env.COMPANION_SANDBOX_ENABLED;
|
|
17
|
+
return v === "1" || v === "true";
|
|
18
|
+
}
|
|
@@ -44,6 +44,8 @@ async function createManager() {
|
|
|
44
44
|
|
|
45
45
|
describe("ImagePullManager", () => {
|
|
46
46
|
beforeEach(() => {
|
|
47
|
+
// Sandbox is force-disabled by default; enable it so the pull paths run.
|
|
48
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
47
49
|
vi.clearAllMocks();
|
|
48
50
|
mockImageExists.mockReturnValue(false);
|
|
49
51
|
mockPullImage.mockResolvedValue(true);
|
|
@@ -125,6 +127,21 @@ describe("ImagePullManager", () => {
|
|
|
125
127
|
|
|
126
128
|
expect(mockPullImage).toHaveBeenCalledOnce();
|
|
127
129
|
});
|
|
130
|
+
|
|
131
|
+
it("refuses to pull the upstream image when sandbox support is disabled (hard backstop)", async () => {
|
|
132
|
+
// Security backstop: with the flag off, the unmaintained upstream image must
|
|
133
|
+
// never be pulled, regardless of which call path requested it.
|
|
134
|
+
delete process.env.COMPANION_SANDBOX_ENABLED;
|
|
135
|
+
mockImageExists.mockReturnValue(false);
|
|
136
|
+
const manager = await createManager();
|
|
137
|
+
|
|
138
|
+
manager.ensureImage("the-companion:latest");
|
|
139
|
+
|
|
140
|
+
const state = manager.getState("the-companion:latest");
|
|
141
|
+
expect(state.status).toBe("error");
|
|
142
|
+
expect(state.error).toMatch(/disabled/i);
|
|
143
|
+
expect(mockPullImage).not.toHaveBeenCalled();
|
|
144
|
+
});
|
|
128
145
|
});
|
|
129
146
|
|
|
130
147
|
describe("waitForReady", () => {
|
|
@@ -2,6 +2,7 @@ import { join, dirname } from "node:path";
|
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { containerManager, ContainerManager } from "./container-manager.js";
|
|
5
|
+
import { isSandboxEnabled } from "./feature-flags.js";
|
|
5
6
|
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// Types
|
|
@@ -171,6 +172,17 @@ class ImagePullManager {
|
|
|
171
172
|
// Determine if we can pull from registry
|
|
172
173
|
const registryImage = ContainerManager.getRegistryImage(image);
|
|
173
174
|
|
|
175
|
+
// Hard backstop: while sandbox support is disabled, never pull the upstream
|
|
176
|
+
// (unmaintained) registry image, regardless of which call path asked. Local
|
|
177
|
+
// Dockerfile builds are still allowed so a future self-built image works.
|
|
178
|
+
if (registryImage && !isSandboxEnabled()) {
|
|
179
|
+
this.markError(
|
|
180
|
+
image,
|
|
181
|
+
"Sandbox/container support is disabled in this build. Set COMPANION_SANDBOX_ENABLED=1 to re-enable.",
|
|
182
|
+
);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
174
186
|
if (registryImage) {
|
|
175
187
|
this.doPullFromRegistry(image, registryImage);
|
|
176
188
|
} else {
|
|
@@ -42,6 +42,8 @@ import { registerEnvRoutes } from "./env-routes.js";
|
|
|
42
42
|
let app: Hono;
|
|
43
43
|
|
|
44
44
|
beforeEach(() => {
|
|
45
|
+
// Sandbox is force-disabled by default; enable it so build-base runs.
|
|
46
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
45
47
|
vi.clearAllMocks();
|
|
46
48
|
|
|
47
49
|
app = new Hono();
|
|
@@ -4,6 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import * as envManager from "../env-manager.js";
|
|
5
5
|
import { containerManager } from "../container-manager.js";
|
|
6
6
|
import { imagePullManager } from "../image-pull-manager.js";
|
|
7
|
+
import { isSandboxEnabled } from "../feature-flags.js";
|
|
7
8
|
|
|
8
9
|
export function registerEnvRoutes(
|
|
9
10
|
api: Hono,
|
|
@@ -59,6 +60,9 @@ export function registerEnvRoutes(
|
|
|
59
60
|
});
|
|
60
61
|
|
|
61
62
|
api.post("/docker/build-base", async (c) => {
|
|
63
|
+
if (!isSandboxEnabled()) {
|
|
64
|
+
return c.json({ error: "Sandbox support is disabled in this build." }, 403);
|
|
65
|
+
}
|
|
62
66
|
if (!containerManager.checkDocker()) return c.json({ error: "Docker is not available" }, 503);
|
|
63
67
|
const dockerfilePath = join(options.webDir, "docker", "Dockerfile.the-companion");
|
|
64
68
|
if (!existsSync(dockerfilePath)) {
|
|
@@ -38,6 +38,8 @@ import { registerSandboxRoutes } from "./sandbox-routes.js";
|
|
|
38
38
|
let app: Hono;
|
|
39
39
|
|
|
40
40
|
beforeEach(() => {
|
|
41
|
+
// Sandbox is force-disabled by default; enable it so test-init runs.
|
|
42
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
41
43
|
vi.clearAllMocks();
|
|
42
44
|
|
|
43
45
|
app = new Hono().basePath("/api");
|
|
@@ -3,6 +3,7 @@ import type { Hono } from "hono";
|
|
|
3
3
|
import * as sandboxManager from "../sandbox-manager.js";
|
|
4
4
|
import { containerManager, type ContainerConfig } from "../container-manager.js";
|
|
5
5
|
import { imagePullManager } from "../image-pull-manager.js";
|
|
6
|
+
import { isSandboxEnabled } from "../feature-flags.js";
|
|
6
7
|
|
|
7
8
|
export function registerSandboxRoutes(
|
|
8
9
|
api: Hono,
|
|
@@ -62,6 +63,10 @@ export function registerSandboxRoutes(
|
|
|
62
63
|
// Accepts an optional `initScript` body param to test unsaved content
|
|
63
64
|
// without persisting it first. Falls back to the stored script.
|
|
64
65
|
api.post("/sandboxes/:slug/test-init", async (c) => {
|
|
66
|
+
// Sandbox containers use the unmaintained upstream image; refuse when disabled.
|
|
67
|
+
if (!isSandboxEnabled()) {
|
|
68
|
+
return c.json({ error: "Sandbox support is disabled in this build." }, 403);
|
|
69
|
+
}
|
|
65
70
|
const slug = c.req.param("slug");
|
|
66
71
|
const body = await c.req.json().catch(() => ({}));
|
|
67
72
|
const rawCwd = body.cwd;
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { refreshServiceDefinition } from "../service.js";
|
|
13
13
|
import { getSettings, updateSettings } from "../settings-manager.js";
|
|
14
14
|
import { imagePullManager } from "../image-pull-manager.js";
|
|
15
|
+
import { isSandboxEnabled } from "../feature-flags.js";
|
|
15
16
|
import { checkCompat, getCompatState } from "../claude-compat-checker.js";
|
|
16
17
|
import { pinToVersion, patchBinary, unpatch } from "../claude-patcher.js";
|
|
17
18
|
import {
|
|
@@ -145,8 +146,9 @@ export function registerSystemRoutes(
|
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
// Re-pull Docker image if auto-update is enabled
|
|
149
|
-
|
|
149
|
+
// Re-pull Docker image if auto-update is enabled (and sandbox isn't
|
|
150
|
+
// disabled — we never refresh the unmaintained upstream image).
|
|
151
|
+
if (isSandboxEnabled() && getSettings().dockerAutoUpdate) {
|
|
150
152
|
try {
|
|
151
153
|
console.log("[update] Re-pulling Docker image (dockerAutoUpdate enabled)...");
|
|
152
154
|
imagePullManager.pull("the-companion:latest");
|
|
@@ -150,6 +150,8 @@ describe("executeSessionCreation", () => {
|
|
|
150
150
|
let deps: SessionCreationDeps;
|
|
151
151
|
|
|
152
152
|
beforeEach(() => {
|
|
153
|
+
// Sandbox is force-disabled by default; enable it so sandbox paths run.
|
|
154
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
153
155
|
vi.clearAllMocks();
|
|
154
156
|
deps = makeDeps();
|
|
155
157
|
});
|
|
@@ -215,6 +217,20 @@ describe("executeSessionCreation", () => {
|
|
|
215
217
|
}
|
|
216
218
|
});
|
|
217
219
|
|
|
220
|
+
it("ignores sandboxEnabled entirely when the feature flag is off (kill switch)", async () => {
|
|
221
|
+
// Security kill switch: with sandbox disabled, a request asking for a sandbox
|
|
222
|
+
// (even an invalid one) is treated as a plain session — no 404, no image
|
|
223
|
+
// pull, no container creation.
|
|
224
|
+
delete process.env.COMPANION_SANDBOX_ENABLED;
|
|
225
|
+
const result = await executeSessionCreation(
|
|
226
|
+
{ cwd: "/workspace", sandboxEnabled: true, sandboxSlug: "missing" },
|
|
227
|
+
deps,
|
|
228
|
+
);
|
|
229
|
+
expect(result.session).toBeDefined();
|
|
230
|
+
expect(imagePullManager.ensureImage).not.toHaveBeenCalled();
|
|
231
|
+
expect(containerManager.createContainer).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
218
234
|
// -- Branch validation --
|
|
219
235
|
it("rejects invalid branch names", async () => {
|
|
220
236
|
try {
|
|
@@ -10,6 +10,7 @@ import { containerManager } from "./container-manager.js";
|
|
|
10
10
|
import { hasContainerClaudeAuth } from "./claude-container-auth.js";
|
|
11
11
|
import { hasContainerCodexAuth } from "./codex-container-auth.js";
|
|
12
12
|
import { imagePullManager } from "./image-pull-manager.js";
|
|
13
|
+
import { isSandboxEnabled } from "./feature-flags.js";
|
|
13
14
|
import { getConnection } from "./linear-connections.js";
|
|
14
15
|
import { buildLinearSystemPrompt } from "./linear-prompt-builder.js";
|
|
15
16
|
import { discoverCommandsAndSkills } from "./commands-discovery.js";
|
|
@@ -100,8 +101,10 @@ export async function executeSessionCreation(
|
|
|
100
101
|
console.warn(`[session-creation] Environment "${body.envSlug}" not found, ignoring`);
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
// Resolve sandbox configuration
|
|
104
|
-
|
|
104
|
+
// Resolve sandbox configuration. Sandbox is force-disabled when the feature
|
|
105
|
+
// flag is off (the upstream image is unmaintained), so no image is pulled and
|
|
106
|
+
// no privileged container is launched regardless of the request.
|
|
107
|
+
const sandboxEnabled = isSandboxEnabled() && body.sandboxEnabled === true;
|
|
105
108
|
const companionSandbox = body.sandboxSlug ? sandboxManager.getSandbox(body.sandboxSlug as string) : null;
|
|
106
109
|
if (sandboxEnabled && body.sandboxSlug && !companionSandbox) {
|
|
107
110
|
throw new SessionCreationError(`Sandbox "${body.sandboxSlug}" not found`, 404, "resolving_env");
|
|
@@ -220,6 +220,8 @@ describe("SessionOrchestrator", () => {
|
|
|
220
220
|
let orchestrator: SessionOrchestrator;
|
|
221
221
|
|
|
222
222
|
beforeEach(() => {
|
|
223
|
+
// Sandbox is force-disabled by default; enable it so sandbox paths run.
|
|
224
|
+
process.env.COMPANION_SANDBOX_ENABLED = "1";
|
|
223
225
|
vi.clearAllMocks();
|
|
224
226
|
companionBus.clear();
|
|
225
227
|
mockImagePullIsReady.mockReturnValue(true);
|
|
@@ -7,6 +7,7 @@ import type { BackendType, CreationStepId } from "./session-types.js";
|
|
|
7
7
|
import type { ContainerConfig, ContainerInfo } from "./container-manager.js";
|
|
8
8
|
import { containerManager } from "./container-manager.js";
|
|
9
9
|
import { imagePullManager } from "./image-pull-manager.js";
|
|
10
|
+
import { isSandboxEnabled } from "./feature-flags.js";
|
|
10
11
|
import * as envManager from "./env-manager.js";
|
|
11
12
|
import * as sandboxManager from "./sandbox-manager.js";
|
|
12
13
|
import * as gitUtils from "./git-utils.js";
|
|
@@ -314,8 +315,10 @@ export class SessionOrchestrator {
|
|
|
314
315
|
envVars = { ...envVars, OPENAI_API_KEY: globalSettings.openaiApiKey };
|
|
315
316
|
}
|
|
316
317
|
|
|
317
|
-
// Resolve sandbox configuration
|
|
318
|
-
|
|
318
|
+
// Resolve sandbox configuration. Sandbox is force-disabled when the feature
|
|
319
|
+
// flag is off (the upstream image is unmaintained), so no image is pulled
|
|
320
|
+
// and no privileged container is launched regardless of the request.
|
|
321
|
+
const sandboxEnabled = isSandboxEnabled() && body.sandboxEnabled === true;
|
|
319
322
|
const companionSandbox = body.sandboxSlug ? sandboxManager.getSandbox(body.sandboxSlug) : null;
|
|
320
323
|
if (sandboxEnabled && body.sandboxSlug && !companionSandbox) {
|
|
321
324
|
return { ok: false, error: `Sandbox "${body.sandboxSlug}" not found`, status: 404 };
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import{r as n,a as c,j as e}from"./index-CO5E0NEG.js";function X({embedded:f=!1}){const[u,C]=n.useState([]),[V,W]=n.useState(!0),[v,l]=n.useState(""),[o,M]=n.useState(!1),[k,R]=n.useState(!1),[b,B]=n.useState(null),[s,E]=n.useState(null),i=n.useRef(null),[p,I]=n.useState(""),[T,D]=n.useState(""),[j,y]=n.useState(null),[P,$]=n.useState(""),[N,H]=n.useState(""),[h,w]=n.useState(null),[m,d]=n.useState(null),x=n.useRef({}),[S,z]=n.useState(""),g=n.useCallback(()=>{c.listSandboxes().then(C).catch(()=>{}).finally(()=>W(!1))},[]);n.useEffect(()=>{g(),c.getContainerStatus().then(t=>B(t.available)).catch(()=>B(!1)),c.getImageStatus("the-companion:latest").then(t=>E(t)).catch(()=>{}),c.getHome().then(({cwd:t})=>z(t)).catch(()=>{})},[g]),n.useEffect(()=>{if(!s||s.status!=="pulling"){i.current&&(clearInterval(i.current),i.current=null);return}return i.current||(i.current=setInterval(()=>{c.getImageStatus("the-companion:latest").then(t=>E(t)).catch(()=>{})},2e3)),()=>{i.current&&(clearInterval(i.current),i.current=null)}},[s]);function L(){c.pullImage("the-companion:latest").then(t=>{t.state&&E(t.state)}).catch(()=>{})}async function A(){const t=p.trim();if(t){R(!0);try{await c.createSandbox(t,{initScript:T||void 0}),I(""),D(""),M(!1),l(""),g()}catch(r){l(r instanceof Error?r.message:String(r))}finally{R(!1)}}}function Z(t){x.current={},y(t.slug),$(t.name),H(t.initScript||""),d(null),l("")}function q(){x.current={},y(null),d(null),w(null),l("")}async function K(){if(j){x.current={},w(null);try{await c.updateSandbox(j,{name:P.trim()||void 0,initScript:N||void 0}),y(null),d(null),l(""),g()}catch(t){l(t instanceof Error?t.message:String(t))}}}async function U(t){try{await c.deleteSandbox(t),j===t&&y(null),g()}catch(r){l(r instanceof Error?r.message:String(r))}}async function F(t){if(!S)return;const r={};x.current=r,w(t),d(null);try{const a=await c.testInitScript(t,S,N);if(x.current!==r)return;d(a)}catch(a){if(x.current!==r)return;d({success:!1,exitCode:-1,output:a instanceof Error?a.message:String(a)})}finally{x.current===r&&w(null)}}const G=b===null?null:b?e.jsx("span",{className:"text-[10px] px-2 py-1 rounded-md bg-green-500/10 text-green-500 font-medium",children:"Docker"}):e.jsx("span",{className:"text-[10px] px-2 py-1 rounded-md bg-amber-500/10 text-amber-500 font-medium",children:"No Docker"});function J(){if(!b)return null;const t=(s==null?void 0:s.status)==="pulling";return e.jsxs("div",{className:"rounded-xl bg-cc-card p-3 sm:p-4 mb-5",children:[e.jsxs("div",{className:"flex items-center justify-between gap-2",children:[e.jsxs("div",{className:"flex items-center gap-2 min-w-0",children:[e.jsx("span",{className:"text-[11px] font-medium text-cc-muted",children:"Base Image"}),e.jsx("code",{className:"text-[10px] font-mono-code text-cc-fg",children:"the-companion:latest"}),(s==null?void 0:s.status)==="ready"&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-green-500/10 text-green-500",children:"Ready"}),(s==null?void 0:s.status)==="pulling"&&e.jsxs("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-amber-500/10 text-amber-500 flex items-center gap-1",children:[e.jsx("span",{className:"w-2.5 h-2.5 border border-amber-500/30 border-t-amber-500 rounded-full animate-spin"}),"Pulling..."]}),(s==null?void 0:s.status)==="error"&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-cc-error/10 text-cc-error",children:"Pull failed"}),(!s||s.status==="idle")&&e.jsx("span",{className:"text-[10px] px-1.5 py-0.5 rounded bg-cc-hover text-cc-muted",children:"Not downloaded"})]}),(s==null?void 0:s.status)!=="ready"&&e.jsx("button",{onClick:L,disabled:t,className:`text-xs px-3 py-2 min-h-[36px] rounded-lg transition-colors shrink-0 ${t?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 cursor-pointer"}`,children:t?"Pulling...":"Pull"})]}),t&&(s==null?void 0:s.progress)&&s.progress.length>0&&e.jsx("pre",{className:"mt-2 px-3 py-2 text-[10px] font-mono-code bg-cc-code-bg rounded-lg text-cc-muted max-h-[120px] overflow-auto whitespace-pre-wrap",children:s.progress.slice(-20).join(`
|
|
2
|
-
`)}),(s==null?void 0:s.status)==="error"&&s.error&&e.jsx("p",{className:"mt-2 text-[10px] text-cc-error",children:s.error})]})}return f?e.jsx("div",{className:"h-full bg-cc-bg text-cc-fg font-sans-ui antialiased overflow-y-auto overflow-x-hidden",children:e.jsxs("div",{className:"max-w-2xl mx-auto px-4 sm:px-6 py-6 sm:py-10 pb-safe",children:[e.jsxs("div",{className:"flex items-start justify-between gap-3 mb-2",children:[e.jsxs("div",{className:"min-w-0",children:[e.jsx("h1",{className:"text-lg font-semibold text-cc-fg",children:"Sandboxes"}),e.jsx("p",{className:"mt-0.5 text-[13px] text-cc-muted leading-relaxed",children:"Reusable sandbox configurations for containerized sessions."})]}),e.jsx("div",{className:"flex items-center gap-2 shrink-0",children:G})]}),J(),e.jsxs("div",{className:"flex items-center gap-2 mt-4 mb-5",children:[e.jsx("div",{className:"flex-1"}),e.jsxs("button",{onClick:()=>M(!o),className:`flex items-center gap-1.5 px-3.5 py-2.5 min-h-[44px] rounded-lg text-sm font-medium transition-colors cursor-pointer shrink-0 ${o?"bg-cc-active text-cc-fg":"bg-cc-primary hover:bg-cc-primary-hover text-white"}`,children:[e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",className:"w-4 h-4",children:o?e.jsx("path",{d:"M18 6 6 18M6 6l12 12"}):e.jsx("path",{d:"M12 5v14M5 12h14"})}),e.jsx("span",{className:"hidden sm:inline",children:o?"Cancel":"New Sandbox"})]})]}),o&&e.jsxs("div",{className:"mb-6 rounded-xl bg-cc-card p-4 sm:p-5 space-y-3",style:{animation:"fadeSlideIn 150ms ease-out"},children:[e.jsx("input",{type:"text",value:p,onChange:t=>I(t.target.value),placeholder:"Sandbox name (e.g. node-project)",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow",onKeyDown:t=>{t.key==="Enter"&&p.trim()&&A()}}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-[11px] font-medium text-cc-muted mb-1",children:"Init Script (optional)"}),e.jsx("textarea",{value:T,onChange:t=>D(t.target.value),placeholder:`# Runs inside the container before Claude starts
|
|
3
|
-
# Example:
|
|
4
|
-
bun install
|
|
5
|
-
pip install -r requirements.txt`,rows:5,className:"w-full px-3 py-2.5 text-[11px] font-mono-code bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 resize-y transition-shadow",style:{minHeight:"80px"}})]}),v&&o&&e.jsx("div",{className:"px-3 py-2 rounded-lg bg-cc-error/10 text-xs text-cc-error",children:v}),e.jsxs("div",{className:"flex items-center justify-between pt-1",children:[e.jsxs("p",{className:"text-[11px] text-cc-muted",children:["Stored in ",e.jsx("code",{className:"text-[10px]",children:"~/.companion/sandboxes/"})]}),e.jsx("button",{onClick:A,disabled:!p.trim()||k,className:`px-4 py-2.5 min-h-[44px] rounded-lg text-sm font-medium transition-colors ${p.trim()&&!k?"bg-cc-primary hover:bg-cc-primary-hover text-white cursor-pointer":"bg-cc-hover text-cc-muted cursor-not-allowed"}`,children:k?"Creating...":"Create"})]})]}),e.jsx("div",{className:"flex items-center gap-2 mb-3 text-[12px] text-cc-muted",children:e.jsxs("span",{children:[u.length," sandbox",u.length!==1?"es":""]})}),V?e.jsx("div",{className:"py-12 text-center text-sm text-cc-muted",children:"Loading sandboxes..."}):u.length===0?e.jsx("div",{className:"py-12 text-center text-sm text-cc-muted",children:"No sandboxes yet."}):e.jsx("div",{className:"space-y-1",children:u.map(t=>j===t.slug?e.jsxs("div",{className:"rounded-xl bg-cc-card p-4 space-y-3",style:{animation:"fadeSlideIn 150ms ease-out"},children:[e.jsx("input",{type:"text",value:P,onChange:a=>$(a.target.value),placeholder:"Sandbox name",className:"w-full px-3 py-2.5 min-h-[44px] text-sm bg-cc-bg rounded-lg text-cc-fg focus:outline-none focus:ring-1 focus:ring-cc-primary/40 transition-shadow"}),e.jsxs("div",{children:[e.jsx("div",{className:"flex items-center justify-between mb-1",children:e.jsx("div",{className:"text-[11px] font-medium text-cc-muted",children:"Init Script (optional)"})}),e.jsx("textarea",{value:N,onChange:a=>{H(a.target.value),d(null)},placeholder:`# Runs inside the container before Claude starts
|
|
6
|
-
# Example:
|
|
7
|
-
bun install
|
|
8
|
-
pip install -r requirements.txt`,rows:5,className:"w-full px-3 py-2.5 text-[11px] font-mono-code bg-cc-bg rounded-lg text-cc-fg placeholder:text-cc-muted focus:outline-none focus:ring-1 focus:ring-cc-primary/40 resize-y transition-shadow",style:{minHeight:"80px"}})]}),h===t.slug&&e.jsxs("div",{className:"flex items-center gap-2 px-3 py-2 rounded-lg bg-amber-500/10 text-xs text-amber-500",children:[e.jsx("span",{className:"w-3 h-3 border-2 border-amber-500/30 border-t-amber-500 rounded-full animate-spin"}),"Testing init script..."]}),m&&h!==t.slug&&e.jsxs("div",{className:"space-y-2",children:[e.jsx("div",{className:`flex items-center gap-2 px-3 py-2 rounded-lg text-xs ${m.success?"bg-green-500/10 text-green-500":"bg-cc-error/10 text-cc-error"}`,children:m.success?"Test passed":`Test failed (exit ${m.exitCode})`}),m.output&&e.jsx("pre",{className:"px-3 py-2 text-[10px] font-mono-code bg-cc-code-bg rounded-lg text-cc-muted max-h-[200px] overflow-auto whitespace-pre-wrap",children:m.output})]}),e.jsxs("div",{className:"flex justify-end gap-2",children:[N.trim()&&b&&e.jsx("button",{onClick:()=>void F(t.slug),disabled:h===t.slug||!S,className:`px-3 py-2.5 min-h-[44px] text-sm rounded-lg font-medium transition-colors ${h===t.slug||!S?"bg-cc-hover text-cc-muted cursor-not-allowed":"bg-blue-500/10 text-blue-400 hover:bg-blue-500/20 cursor-pointer"}`,children:h===t.slug?"Testing...":"Test Init Script"}),e.jsx("button",{onClick:q,className:"px-3 py-2.5 min-h-[44px] text-sm rounded-lg text-cc-muted hover:text-cc-fg hover:bg-cc-hover transition-colors cursor-pointer",children:"Cancel"}),e.jsx("button",{onClick:()=>void K(),className:"px-4 py-2.5 min-h-[44px] text-sm rounded-lg font-medium bg-cc-primary hover:bg-cc-primary-hover text-white transition-colors cursor-pointer",children:"Save"})]})]},t.slug):e.jsx(O,{sandbox:t,onStartEdit:()=>Z(t),onDelete:()=>void U(t.slug)},t.slug))}),v&&!o&&e.jsx("div",{className:"mt-4 px-3 py-2 rounded-lg bg-cc-error/10 text-xs text-cc-error",children:v})]})}):e.jsx("div",{className:"h-full bg-cc-bg text-cc-fg font-sans-ui antialiased overflow-y-auto overflow-x-hidden",children:e.jsx("div",{className:"max-w-2xl mx-auto px-4 sm:px-6 py-6 sm:py-10 pb-safe",children:e.jsx("div",{className:"py-12 text-center text-sm text-cc-muted",children:"Use embedded mode to view sandboxes."})})})}function O({sandbox:f,onStartEdit:u,onDelete:C}){return e.jsxs("div",{className:"group flex items-start gap-3 px-3 py-3 min-h-[44px] rounded-lg hover:bg-cc-hover/60 transition-colors",children:[e.jsx("div",{className:"shrink-0 mt-0.5 w-7 h-7 rounded-md bg-cc-primary/10 flex items-center justify-center",children:e.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",className:"w-3.5 h-3.5 text-cc-primary",children:[e.jsx("path",{d:"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16Z"}),e.jsx("path",{d:"M3.27 6.96 12 12.01l8.73-5.05M12 22.08V12"})]})}),e.jsxs("div",{className:"flex-1 min-w-0",children:[e.jsx("div",{className:"flex items-center gap-2",children:e.jsx("span",{className:"text-sm font-medium text-cc-fg truncate",children:f.name})}),e.jsx("p",{className:"mt-0.5 text-xs text-cc-muted",children:f.initScript?"init script":"no init script"})]}),e.jsxs("div",{className:"shrink-0 flex items-center gap-0.5 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity",children:[e.jsx("button",{onClick:u,className:"p-2 min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 sm:p-1.5 rounded-md text-cc-muted hover:text-cc-fg hover:bg-cc-active transition-colors cursor-pointer","aria-label":"Edit",children:e.jsxs("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",className:"w-3.5 h-3.5",children:[e.jsx("path",{d:"M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"}),e.jsx("path",{d:"M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5Z"})]})}),e.jsx("button",{onClick:C,className:"p-2 min-h-[44px] min-w-[44px] sm:min-h-0 sm:min-w-0 sm:p-1.5 rounded-md text-cc-muted hover:text-cc-error hover:bg-cc-error/10 transition-colors cursor-pointer","aria-label":"Delete",children:e.jsx("svg",{viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"1.5",className:"w-3.5 h-3.5",children:e.jsx("path",{d:"M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2m3 0v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6h14Z"})})})]})]})}export{X as SandboxManager};
|