@hellcoder/companion 0.121.1 → 0.123.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.
- package/dist/assets/{AgentsPage-BksCBN61.js → AgentsPage-D_ZGmkrU.js} +5 -5
- package/dist/assets/CronManager-u5KuicVz.js +1 -0
- package/dist/assets/{DashboardPage-DzdjTocu.js → DashboardPage-Dab8XyGT.js} +1 -1
- package/dist/assets/{IntegrationsPage-CeFJD8qq.js → IntegrationsPage-Bc7SgfqX.js} +1 -1
- package/dist/assets/{LinearOAuthSettingsPage-DRlRTVfQ.js → LinearOAuthSettingsPage-CxyO36qY.js} +1 -1
- package/dist/assets/{LinearSettingsPage-6lZfxn4O.js → LinearSettingsPage-BWfwYF05.js} +1 -1
- package/dist/assets/{MagicUIDashboard-DDBNAFVy.js → MagicUIDashboard-CfD7iyXm.js} +1 -1
- package/dist/assets/{MagicUIView-CbB9S_lC.js → MagicUIView-CO6pZB0M.js} +1 -1
- package/dist/assets/{Playground-C-hSwNGZ.js → Playground-BiB_VDIl.js} +1 -1
- package/dist/assets/{PromptsPage-CdmFkzMP.js → PromptsPage-BIE70OT1.js} +1 -1
- package/dist/assets/{RunsPage-bXFgnAju.js → RunsPage-BAblQKAW.js} +1 -1
- package/dist/assets/{SandboxManager-Bl4PXenO.js → SandboxManager-DsR3hQz1.js} +1 -1
- package/dist/assets/SettingsPage-DuzHKBe_.js +1 -0
- package/dist/assets/{TailscalePage-DRdnrf6G.js → TailscalePage-C3y_bGkT.js} +1 -1
- package/dist/assets/index-Cd0HGwTS.js +141 -0
- package/dist/assets/{sw-register-DhsOPkya.js → sw-register-CUhVLAo0.js} +1 -1
- package/dist/index.html +1 -1
- package/dist/sw.js +1 -1
- package/package.json +1 -1
- package/server/claude-auth.test.ts +305 -0
- package/server/claude-auth.ts +367 -0
- package/server/routes/claude-auth-routes.test.ts +147 -0
- package/server/routes/claude-auth-routes.ts +55 -0
- package/server/routes.ts +2 -0
- package/server/settings-manager.ts +1 -1
- package/dist/assets/CronManager-S0nmTpnB.js +0 -1
- package/dist/assets/SettingsPage-lBpx5wZP.js +0 -1
- package/dist/assets/index-C5CzX0bF.js +0 -141
|
@@ -1 +1 @@
|
|
|
1
|
-
import{_ as u}from"./index-
|
|
1
|
+
import{_ as u}from"./index-Cd0HGwTS.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-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-Cd0HGwTS.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-BnF_YIhB.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":"
|
|
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":"a31e7465a039f763a843e69eb0065600","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-CUhVLAo0.js"},{"revision":null,"url":"assets/index-Cd0HGwTS.js"},{"revision":null,"url":"assets/index-BnF_YIhB.css"},{"revision":null,"url":"assets/TailscalePage-C3y_bGkT.js"},{"revision":null,"url":"assets/SettingsPage-DuzHKBe_.js"},{"revision":null,"url":"assets/SandboxManager-DsR3hQz1.js"},{"revision":null,"url":"assets/RunsPage-BAblQKAW.js"},{"revision":null,"url":"assets/PromptsPage-BIE70OT1.js"},{"revision":null,"url":"assets/Playground-BiB_VDIl.js"},{"revision":null,"url":"assets/MagicUIView-CO6pZB0M.js"},{"revision":null,"url":"assets/MagicUIDashboard-CfD7iyXm.js"},{"revision":null,"url":"assets/LinearSettingsPage-BWfwYF05.js"},{"revision":null,"url":"assets/LinearOAuthSettingsPage-CxyO36qY.js"},{"revision":null,"url":"assets/IntegrationsPage-Bc7SgfqX.js"},{"revision":null,"url":"assets/DashboardPage-Dab8XyGT.js"},{"revision":null,"url":"assets/CronManager-u5KuicVz.js"},{"revision":null,"url":"assets/AgentsPage-D_ZGmkrU.js"}]);ne();T(new oe(re("index.html"),{denylist:[/^\/api/,/^\/ws/]}));
|
package/package.json
CHANGED
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { EventEmitter } from "node:events";
|
|
3
|
+
import { PassThrough } from "node:stream";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Tests for the Claude Code paste-code sign-in.
|
|
7
|
+
*
|
|
8
|
+
* The manager drives the real `claude` binary, so these tests stub the spawn
|
|
9
|
+
* boundary. Two kinds of child are simulated: the long-lived
|
|
10
|
+
* `claude auth login` process (emits an authorize URL, then reads a code from
|
|
11
|
+
* stdin) and the short-lived `claude auth status --json` probe.
|
|
12
|
+
*
|
|
13
|
+
* The behaviour that matters and is easy to get wrong:
|
|
14
|
+
* - success is decided by re-reading `auth status`, never by parsing prose
|
|
15
|
+
* - a rejected code is recoverable on the same process (the CLI re-prompts)
|
|
16
|
+
* - CLAUDE_CODE_OAUTH_TOKEN must not leak into these subprocesses, or a stale
|
|
17
|
+
* token would masquerade as a successful login
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const spawnMock = vi.fn();
|
|
21
|
+
const resolveBinaryMock = vi.fn(() => "/usr/bin/claude");
|
|
22
|
+
|
|
23
|
+
vi.mock("node:child_process", () => ({ spawn: spawnMock }));
|
|
24
|
+
vi.mock("./path-resolver.js", () => ({
|
|
25
|
+
resolveBinary: (...a: unknown[]) => resolveBinaryMock(...(a as [])),
|
|
26
|
+
getEnrichedPath: () => "/usr/bin:/bin",
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
const AUTH_URL =
|
|
30
|
+
"https://claude.com/cai/oauth/authorize?code=true&client_id=9d1c250a&redirect_uri=https%3A%2F%2Fplatform.claude.com%2Foauth%2Fcode%2Fcallback";
|
|
31
|
+
|
|
32
|
+
class FakeChild extends EventEmitter {
|
|
33
|
+
stdout = new PassThrough();
|
|
34
|
+
stderr = new PassThrough();
|
|
35
|
+
/** Codes written to stdin by the manager. */
|
|
36
|
+
written: string[] = [];
|
|
37
|
+
stdin = { write: (chunk: string) => { this.written.push(chunk); return true; } };
|
|
38
|
+
killed = false;
|
|
39
|
+
kill(): void { this.killed = true; }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Queue of children handed out by successive spawn() calls, in order. */
|
|
43
|
+
let loginChild: FakeChild;
|
|
44
|
+
let statusResponses: Array<{ loggedIn: boolean; [k: string]: unknown }>;
|
|
45
|
+
let spawnedArgs: string[][];
|
|
46
|
+
let spawnedEnvs: NodeJS.ProcessEnv[];
|
|
47
|
+
let auth: typeof import("./claude-auth.js");
|
|
48
|
+
|
|
49
|
+
function installSpawn(): void {
|
|
50
|
+
spawnMock.mockImplementation((_bin: string, args: string[], opts: { env: NodeJS.ProcessEnv }) => {
|
|
51
|
+
spawnedArgs.push(args);
|
|
52
|
+
spawnedEnvs.push(opts?.env ?? {});
|
|
53
|
+
|
|
54
|
+
// `auth status --json` — short-lived, answers from the queue then exits.
|
|
55
|
+
if (args[1] === "status") {
|
|
56
|
+
const child = new FakeChild();
|
|
57
|
+
const payload = statusResponses.length > 1
|
|
58
|
+
? statusResponses.shift()!
|
|
59
|
+
: statusResponses[0];
|
|
60
|
+
setImmediate(() => {
|
|
61
|
+
child.stdout.write(JSON.stringify(payload));
|
|
62
|
+
child.emit("exit", 0);
|
|
63
|
+
});
|
|
64
|
+
return child;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (args[1] === "logout") {
|
|
68
|
+
const child = new FakeChild();
|
|
69
|
+
setImmediate(() => child.emit("exit", 0));
|
|
70
|
+
return child;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// `auth login` — long-lived; emits the URL shortly after spawn.
|
|
74
|
+
loginChild = new FakeChild();
|
|
75
|
+
setImmediate(() => {
|
|
76
|
+
loginChild.stdout.write(`Opening browser to sign in…\nIf the browser didn't open, visit: ${AUTH_URL}\nPaste code here if prompted > `);
|
|
77
|
+
});
|
|
78
|
+
return loginChild;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const LOGGED_OUT = { loggedIn: false, authMethod: "none" };
|
|
83
|
+
const LOGGED_IN = {
|
|
84
|
+
loggedIn: true, authMethod: "claude.ai", email: "user@example.com",
|
|
85
|
+
orgName: "Acme Inc", subscriptionType: "max",
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
beforeEach(async () => {
|
|
89
|
+
vi.resetModules();
|
|
90
|
+
spawnMock.mockReset();
|
|
91
|
+
resolveBinaryMock.mockReturnValue("/usr/bin/claude");
|
|
92
|
+
spawnedArgs = [];
|
|
93
|
+
spawnedEnvs = [];
|
|
94
|
+
statusResponses = [LOGGED_OUT];
|
|
95
|
+
installSpawn();
|
|
96
|
+
auth = await import("./claude-auth.js");
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
afterEach(() => {
|
|
100
|
+
auth._resetClaudeAuthState();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("extractAuthUrl", () => {
|
|
104
|
+
it("scrapes the authorize URL out of CLI chatter", () => {
|
|
105
|
+
expect(auth.extractAuthUrl(`visit: ${AUTH_URL}\nPaste code >`)).toBe(AUTH_URL);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("ignores output that has no URL yet", () => {
|
|
109
|
+
expect(auth.extractAuthUrl("Opening browser to sign in…")).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it("strips ANSI colouring around the URL", () => {
|
|
113
|
+
expect(auth.extractAuthUrl(`\u001b[36m${AUTH_URL}\u001b[0m`)).toBe(AUTH_URL);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("getAccountStatus", () => {
|
|
118
|
+
it("reports the signed-in account from auth status --json", async () => {
|
|
119
|
+
statusResponses = [LOGGED_IN];
|
|
120
|
+
|
|
121
|
+
const status = await auth.getAccountStatus();
|
|
122
|
+
|
|
123
|
+
expect(status).toMatchObject({
|
|
124
|
+
cliAvailable: true, authenticated: true, method: "claude.ai",
|
|
125
|
+
email: "user@example.com", orgName: "Acme Inc", subscriptionType: "max",
|
|
126
|
+
});
|
|
127
|
+
expect(spawnedArgs[0]).toEqual(["auth", "status", "--json"]);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("reports signed out, normalising authMethod 'none' to null", async () => {
|
|
131
|
+
const status = await auth.getAccountStatus();
|
|
132
|
+
|
|
133
|
+
expect(status.authenticated).toBe(false);
|
|
134
|
+
expect(status.method).toBeNull();
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("never exposes CLAUDE_CODE_OAUTH_TOKEN to the CLI", async () => {
|
|
138
|
+
// A token injected into sessions is a *different* auth mechanism. If it
|
|
139
|
+
// leaked here, status would report that token's identity instead of the
|
|
140
|
+
// stored login this panel manages.
|
|
141
|
+
await auth.getAccountStatus();
|
|
142
|
+
|
|
143
|
+
expect(spawnedEnvs[0]).toHaveProperty("CLAUDE_CODE_OAUTH_TOKEN", undefined);
|
|
144
|
+
expect(spawnedEnvs[0]).toHaveProperty("CLAUDECODE", undefined);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("reports the CLI as unavailable rather than throwing", async () => {
|
|
148
|
+
resolveBinaryMock.mockReturnValue(null as never);
|
|
149
|
+
|
|
150
|
+
const status = await auth.getAccountStatus();
|
|
151
|
+
|
|
152
|
+
expect(status.cliAvailable).toBe(false);
|
|
153
|
+
expect(status.error).toMatch(/not found/i);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("surfaces an error when the CLI prints unparseable output", async () => {
|
|
157
|
+
spawnMock.mockImplementation(() => {
|
|
158
|
+
const child = new FakeChild();
|
|
159
|
+
setImmediate(() => {
|
|
160
|
+
child.stderr.write("something exploded");
|
|
161
|
+
child.emit("exit", 1);
|
|
162
|
+
});
|
|
163
|
+
return child;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const status = await auth.getAccountStatus();
|
|
167
|
+
|
|
168
|
+
expect(status.authenticated).toBe(false);
|
|
169
|
+
expect(status.error).toBe("something exploded");
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe("startLogin", () => {
|
|
174
|
+
it("returns the authorize URL and waits for a code", async () => {
|
|
175
|
+
const status = await auth.startLogin();
|
|
176
|
+
|
|
177
|
+
expect(status.state).toBe("awaiting_code");
|
|
178
|
+
expect(status.authUrl).toBe(AUTH_URL);
|
|
179
|
+
expect(spawnedArgs[0]).toEqual(["auth", "login", "--claudeai"]);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("is idempotent so an already-open link stays valid", async () => {
|
|
183
|
+
const first = await auth.startLogin();
|
|
184
|
+
const second = await auth.startLogin();
|
|
185
|
+
|
|
186
|
+
expect(second.authUrl).toBe(first.authUrl);
|
|
187
|
+
expect(spawnedArgs.filter((a) => a[1] === "login")).toHaveLength(1);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it("throws when the claude binary is missing", async () => {
|
|
191
|
+
resolveBinaryMock.mockReturnValue(null as never);
|
|
192
|
+
await expect(auth.startLogin()).rejects.toThrow(/not found/i);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("fails cleanly when the CLI exits before printing a link", async () => {
|
|
196
|
+
spawnMock.mockImplementation(() => {
|
|
197
|
+
const child = new FakeChild();
|
|
198
|
+
setImmediate(() => child.emit("exit", 1));
|
|
199
|
+
return child;
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const status = await auth.startLogin();
|
|
203
|
+
|
|
204
|
+
expect(status.state).toBe("error");
|
|
205
|
+
expect(status.error).toMatch(/exited/i);
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
describe("submitCode", () => {
|
|
210
|
+
it("writes the code to the CLI and confirms success via auth status", async () => {
|
|
211
|
+
await auth.startLogin();
|
|
212
|
+
// First status call (during verification) reports the new login.
|
|
213
|
+
statusResponses = [LOGGED_IN];
|
|
214
|
+
|
|
215
|
+
const status = await auth.submitCode(" good-code ");
|
|
216
|
+
|
|
217
|
+
expect(status.state).toBe("success");
|
|
218
|
+
// Trimmed and newline-terminated, or the CLI would keep waiting.
|
|
219
|
+
expect(loginChild.written).toEqual(["good-code\n"]);
|
|
220
|
+
expect(loginChild.killed).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("returns to awaiting_code with the URL intact when the code is rejected", async () => {
|
|
224
|
+
await auth.startLogin();
|
|
225
|
+
|
|
226
|
+
const pending = auth.submitCode("bad-code");
|
|
227
|
+
// The CLI re-prompts rather than exiting, so this is recoverable.
|
|
228
|
+
setImmediate(() => loginChild.stdout.write("Invalid code. Please make sure the full code was copied.\n"));
|
|
229
|
+
const status = await pending;
|
|
230
|
+
|
|
231
|
+
expect(status.state).toBe("awaiting_code");
|
|
232
|
+
expect(status.error).toMatch(/wasn't accepted/i);
|
|
233
|
+
expect(status.authUrl).toBe(AUTH_URL);
|
|
234
|
+
// Child stays alive for the retry.
|
|
235
|
+
expect(loginChild.killed).toBe(false);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("accepts a corrected code after a rejection", async () => {
|
|
239
|
+
await auth.startLogin();
|
|
240
|
+
|
|
241
|
+
const firstTry = auth.submitCode("bad-code");
|
|
242
|
+
setImmediate(() => loginChild.stdout.write("Invalid code. Please make sure the full code was copied.\n"));
|
|
243
|
+
await firstTry;
|
|
244
|
+
|
|
245
|
+
statusResponses = [LOGGED_IN];
|
|
246
|
+
const retry = await auth.submitCode("good-code");
|
|
247
|
+
|
|
248
|
+
expect(retry.state).toBe("success");
|
|
249
|
+
expect(loginChild.written).toEqual(["bad-code\n", "good-code\n"]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("rejects an empty code without touching the CLI", async () => {
|
|
253
|
+
await auth.startLogin();
|
|
254
|
+
|
|
255
|
+
const status = await auth.submitCode(" ");
|
|
256
|
+
|
|
257
|
+
expect(status.state).toBe("awaiting_code");
|
|
258
|
+
expect(status.error).toMatch(/enter the code/i);
|
|
259
|
+
expect(loginChild.written).toEqual([]);
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it("reports when no sign-in is in progress", async () => {
|
|
263
|
+
const status = await auth.submitCode("some-code");
|
|
264
|
+
expect(status.error).toMatch(/no sign-in in progress/i);
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
describe("cancelLogin", () => {
|
|
269
|
+
it("abandons the attempt and kills the CLI", async () => {
|
|
270
|
+
await auth.startLogin();
|
|
271
|
+
|
|
272
|
+
const status = auth.cancelLogin();
|
|
273
|
+
|
|
274
|
+
expect(status.state).toBe("canceled");
|
|
275
|
+
expect(loginChild.killed).toBe(true);
|
|
276
|
+
// Terminal result is retained for a poll arriving after cancel.
|
|
277
|
+
expect(auth.getLoginStatus().state).toBe("canceled");
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
it("is a no-op when nothing is in flight", () => {
|
|
281
|
+
expect(auth.cancelLogin().state).toBe("idle");
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe("logout", () => {
|
|
286
|
+
it("shells out to auth logout", async () => {
|
|
287
|
+
const res = await auth.logout();
|
|
288
|
+
|
|
289
|
+
expect(res.ok).toBe(true);
|
|
290
|
+
expect(spawnedArgs[0]).toEqual(["auth", "logout"]);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("reports a logout failure reason", async () => {
|
|
294
|
+
spawnMock.mockImplementation(() => {
|
|
295
|
+
const child = new FakeChild();
|
|
296
|
+
setImmediate(() => {
|
|
297
|
+
child.stderr.write("not logged in");
|
|
298
|
+
child.emit("exit", 1);
|
|
299
|
+
});
|
|
300
|
+
return child;
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
expect(await auth.logout()).toEqual({ ok: false, error: "not logged in" });
|
|
304
|
+
});
|
|
305
|
+
});
|