@hienlh/ppm 0.9.57 → 0.9.59
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/CHANGELOG.md +13 -0
- package/dist/web/assets/api-settings-CgBII8jW.js +1 -0
- package/dist/web/assets/chat-tab-GSn-Itse.js +10 -0
- package/dist/web/assets/{code-editor-DAZvtAlT.js → code-editor-Bjh4LrGQ.js} +1 -1
- package/dist/web/assets/{database-viewer-C5fco1jm.js → database-viewer-v3PVSVgo.js} +1 -1
- package/dist/web/assets/{diff-viewer-ShRSPvsf.js → diff-viewer-BGSt0dzB.js} +1 -1
- package/dist/web/assets/{extension-webview-CWJRMPfV.js → extension-webview-aIzLDxoc.js} +1 -1
- package/dist/web/assets/{git-graph-h0QmXMdZ.js → git-graph-D_7WjkSI.js} +1 -1
- package/dist/web/assets/{index-CDlrGSwd.js → index-B23bElOE.js} +6 -6
- package/dist/web/assets/index-r64nXcCm.css +2 -0
- package/dist/web/assets/keybindings-store-DbtSlUnk.js +1 -0
- package/dist/web/assets/{markdown-renderer-CSEmmMWt.js → markdown-renderer-DoqEXzyK.js} +1 -1
- package/dist/web/assets/{port-forwarding-tab-Cts6tMFn.js → port-forwarding-tab-DCfENtZd.js} +1 -1
- package/dist/web/assets/{postgres-viewer-CiQC1sf9.js → postgres-viewer-E0_ojaz2.js} +1 -1
- package/dist/web/assets/{settings-tab-CQx6aHtO.js → settings-tab-Ba9P0f9D.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-FQfCkjU6.js → sqlite-viewer-DvlMWCLX.js} +1 -1
- package/dist/web/assets/{terminal-tab-C2SnOqxn.js → terminal-tab-wFhiLTfY.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-VPgvhMpB.js → use-monaco-theme-Dk_fE15d.js} +1 -1
- package/dist/web/index.html +3 -3
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/providers/claude-agent-sdk.ts +28 -2
- package/src/server/routes/accounts.ts +9 -1
- package/src/server/routes/chat.ts +32 -5
- package/src/services/account.service.ts +34 -0
- package/src/services/chat.service.ts +3 -3
- package/src/services/db.service.ts +9 -0
- package/src/types/chat.ts +6 -1
- package/src/web/components/chat/chat-history-bar.tsx +110 -74
- package/src/web/components/chat/chat-history-panel.tsx +2 -2
- package/src/web/components/chat/chat-welcome.tsx +2 -2
- package/src/web/components/chat/session-picker.tsx +2 -2
- package/src/web/components/chat/usage-badge.tsx +12 -10
- package/src/web/components/chat/usage-pattern-chart.tsx +203 -0
- package/src/web/components/layout/editor-panel.tsx +2 -2
- package/src/web/lib/api-settings.ts +14 -0
- package/dist/web/assets/api-settings-Bid0NHuI.js +0 -1
- package/dist/web/assets/chat-tab-SfXtOm9d.js +0 -10
- package/dist/web/assets/index-DVuSY0BZ.css +0 -2
- package/dist/web/assets/keybindings-store-wbHg-S_v.js +0 -1
package/dist/web/sw.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),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),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});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:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(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 t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"67b9002128a70eb318e875811eac8a3b","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-DylHYNtJ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-ywK7LMaH.js"},{"revision":null,"url":"assets/utils-DMiycH3O.js"},{"revision":null,"url":"assets/use-monaco-theme-VPgvhMpB.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C9TYRE0k.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-A4PN_Efm.js"},{"revision":null,"url":"assets/terminal-tab-C2SnOqxn.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-CXMT0QB6.js"},{"revision":null,"url":"assets/table-DFevCOMd.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-C4EMl6jf.js"},{"revision":null,"url":"assets/src-Dw4QhedI.js"},{"revision":null,"url":"assets/square-oPKIkJiw.js"},{"revision":null,"url":"assets/sqlite-viewer-FQfCkjU6.js"},{"revision":null,"url":"assets/settings-tab-CQx6aHtO.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-DM-tMAhx.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-RolPi8bU.js"},{"revision":null,"url":"assets/rough.esm-nHaDi0Kw.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-B9F_Cx_p.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-SKk5z-bm.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-Z-Tr5wtS.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-CdjGIDfw.js"},{"revision":null,"url":"assets/preload-helper-Bf_JiD2A.js"},{"revision":null,"url":"assets/postgres-viewer-CiQC1sf9.js"},{"revision":null,"url":"assets/port-forwarding-tab-Cts6tMFn.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-At5Kz0KK.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CvXHKAzp.js"},{"revision":null,"url":"assets/path-DIKpVbHL.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-IVa5F-go.js"},{"revision":null,"url":"assets/ordinal-LFEjVtwQ.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BaOBwb-W.js"},{"revision":null,"url":"assets/mermaid-parser.core-8u2leTXI.js"},{"revision":null,"url":"assets/math-y9zN1W-N.js"},{"revision":null,"url":"assets/markdown-renderer-CSEmmMWt.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-mag4ySk-.js"},{"revision":null,"url":"assets/keybindings-store-wbHg-S_v.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-kMwlnEGE.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-CgDI-UG4.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js"},{"revision":null,"url":"assets/isEmpty-B9L-Ge-H.js"},{"revision":null,"url":"assets/isArrayLikeObject-CGBoxvCD.js"},{"revision":null,"url":"assets/init-C0r9Gk5G.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BDzTLc11.js"},{"revision":null,"url":"assets/index-DVuSY0BZ.css"},{"revision":null,"url":"assets/index-CDlrGSwd.js"},{"revision":null,"url":"assets/graphlib-Duh_bWLa.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CNlas3Rz.js"},{"revision":null,"url":"assets/git-graph-h0QmXMdZ.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-CWJRMPfV.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/dist-C40JmyoH.js"},{"revision":null,"url":"assets/diff-viewer-ShRSPvsf.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-hzmp0GHK.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-sqTog_XV.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-DxPjK7_c.js"},{"revision":null,"url":"assets/defaultLocale-CrJzLgRD.js"},{"revision":null,"url":"assets/database-viewer-C5fco1jm.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-C3O-MTLf.js"},{"revision":null,"url":"assets/dagre-BFcnKyBF.js"},{"revision":null,"url":"assets/cytoscape.esm-CWPXKqbJ.js"},{"revision":null,"url":"assets/csv-preview-sx6DC51G.js"},{"revision":null,"url":"assets/createLucideIcon-PuMiQgHl.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-cEVJHYd7.js"},{"revision":null,"url":"assets/code-editor-DAZvtAlT.js"},{"revision":null,"url":"assets/clone-B2hUek6n.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-av5aeHLq.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-Cb0iqycX.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPEX8KhL.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-DRJEb7Zb.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-CXuQvlyu.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CMY0PkRK.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-DFKFM_C1.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-C7Gry6md.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-DX0xW7kO.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-rG0P22U9.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-BsUWb9d0.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-DXUTQ-BL.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-C2UEioMs.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-HG_eMj_C.js"},{"revision":null,"url":"assets/chunk-KYZI473N-Djw13C-3.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-DP36BDiU.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-BBmymCjA.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-BBw_z0fW.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-DBdWQ3zy.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-tDjHsAUs.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-D23YVTOU.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-Cpr87sBR.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-CtqKiH4q.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-Dvbyu4Zw.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CkFGv6Zs.js"},{"revision":null,"url":"assets/chunk-55IACEB6-D5cABeB9.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-C3aZvW7B.js"},{"revision":null,"url":"assets/chevron-right-5HgK6l7K.js"},{"revision":null,"url":"assets/chat-tab-SfXtOm9d.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up-BYhx9ckd.js"},{"revision":null,"url":"assets/array-DqLCdDFv.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DqAZP_F6.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CFzkFKEL.js"},{"revision":null,"url":"assets/arc-B9n1Gvb5.js"},{"revision":null,"url":"assets/api-settings-Bid0NHuI.js"},{"revision":null,"url":"assets/api-client-BKIT_Qeg.js"},{"revision":null,"url":"assets/_baseUniq-Yy35llnn.js"},{"revision":null,"url":"assets/_basePickBy-3Xe18azI.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
|
1
|
+
try{self[`workbox:core:7.3.0`]&&_()}catch{}var e=(e,...t)=>{let n=e;return t.length>0&&(n+=` :: ${JSON.stringify(t)}`),n},t=class extends Error{constructor(t,n){let r=e(t,n);super(r),this.name=t,this.details=n}},n={googleAnalytics:`googleAnalytics`,precache:`precache-v2`,prefix:`workbox`,runtime:`runtime`,suffix:typeof registration<`u`?registration.scope:``},r=e=>[n.prefix,e,n.suffix].filter(e=>e&&e.length>0).join(`-`),i=e=>{for(let t of Object.keys(n))e(t)},a={updateDetails:e=>{i(t=>{typeof e[t]==`string`&&(n[t]=e[t])})},getGoogleAnalyticsName:e=>e||r(n.googleAnalytics),getPrecacheName:e=>e||r(n.precache),getPrefix:()=>n.prefix,getRuntimeName:e=>e||r(n.runtime),getSuffix:()=>n.suffix};function o(e,t){let n=t();return e.waitUntil(n),n}try{self[`workbox:precaching:7.3.0`]&&_()}catch{}var s=`__WB_REVISION__`;function c(e){if(!e)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(typeof e==`string`){let t=new URL(e,location.href);return{cacheKey:t.href,url:t.href}}let{revision:n,url:r}=e;if(!r)throw new t(`add-to-cache-list-unexpected-type`,{entry:e});if(!n){let e=new URL(r,location.href);return{cacheKey:e.href,url:e.href}}let i=new URL(r,location.href),a=new URL(r,location.href);return i.searchParams.set(s,n),{cacheKey:i.href,url:a.href}}var l=class{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:e,state:t})=>{t&&(t.originalRequest=e)},this.cachedResponseWillBeUsed=async({event:e,state:t,cachedResponse:n})=>{if(e.type===`install`&&t&&t.originalRequest&&t.originalRequest instanceof Request){let e=t.originalRequest.url;n?this.notUpdatedURLs.push(e):this.updatedURLs.push(e)}return n}}},u=class{constructor({precacheController:e}){this.cacheKeyWillBeUsed=async({request:e,params:t})=>{let n=t?.cacheKey||this._precacheController.getCacheKeyForURL(e.url);return n?new Request(n,{headers:e.headers}):e},this._precacheController=e}},d;function f(){if(d===void 0){let e=new Response(``);if(`body`in e)try{new Response(e.body),d=!0}catch{d=!1}d=!1}return d}async function p(e,n){let r=null;if(e.url&&(r=new URL(e.url).origin),r!==self.location.origin)throw new t(`cross-origin-copy-response`,{origin:r});let i=e.clone(),a={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},o=n?n(a):a,s=f()?i.body:await i.blob();return new Response(s,o)}var m=e=>new URL(String(e),location.href).href.replace(RegExp(`^${location.origin}`),``);function h(e,t){let n=new URL(e);for(let e of t)n.searchParams.delete(e);return n.href}async function g(e,t,n,r){let i=h(t.url,n);if(t.url===i)return e.match(t,r);let a=Object.assign(Object.assign({},r),{ignoreSearch:!0}),o=await e.keys(t,a);for(let t of o)if(i===h(t.url,n))return e.match(t,r)}var v=class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},y=new Set;async function b(){for(let e of y)await e()}function x(e){return new Promise(t=>setTimeout(t,e))}try{self[`workbox:strategies:7.3.0`]&&_()}catch{}function S(e){return typeof e==`string`?new Request(e):e}var C=class{constructor(e,t){this._cacheKeys={},Object.assign(this,t),this.event=t.event,this._strategy=e,this._handlerDeferred=new v,this._extendLifetimePromises=[],this._plugins=[...e.plugins],this._pluginStateMap=new Map;for(let e of this._plugins)this._pluginStateMap.set(e,{});this.event.waitUntil(this._handlerDeferred.promise)}async fetch(e){let{event:n}=this,r=S(e);if(r.mode===`navigate`&&n instanceof FetchEvent&&n.preloadResponse){let e=await n.preloadResponse;if(e)return e}let i=this.hasCallback(`fetchDidFail`)?r.clone():null;try{for(let e of this.iterateCallbacks(`requestWillFetch`))r=await e({request:r.clone(),event:n})}catch(e){if(e instanceof Error)throw new t(`plugin-error-request-will-fetch`,{thrownErrorMessage:e.message})}let a=r.clone();try{let e;e=await fetch(r,r.mode===`navigate`?void 0:this._strategy.fetchOptions);for(let t of this.iterateCallbacks(`fetchDidSucceed`))e=await t({event:n,request:a,response:e});return e}catch(e){throw i&&await this.runCallbacks(`fetchDidFail`,{error:e,event:n,originalRequest:i.clone(),request:a.clone()}),e}}async fetchAndCachePut(e){let t=await this.fetch(e),n=t.clone();return this.waitUntil(this.cachePut(e,n)),t}async cacheMatch(e){let t=S(e),n,{cacheName:r,matchOptions:i}=this._strategy,a=await this.getCacheKey(t,`read`),o=Object.assign(Object.assign({},i),{cacheName:r});n=await caches.match(a,o);for(let e of this.iterateCallbacks(`cachedResponseWillBeUsed`))n=await e({cacheName:r,matchOptions:i,cachedResponse:n,request:a,event:this.event})||void 0;return n}async cachePut(e,n){let r=S(e);await x(0);let i=await this.getCacheKey(r,`write`);if(!n)throw new t(`cache-put-with-no-response`,{url:m(i.url)});let a=await this._ensureResponseSafeToCache(n);if(!a)return!1;let{cacheName:o,matchOptions:s}=this._strategy,c=await self.caches.open(o),l=this.hasCallback(`cacheDidUpdate`),u=l?await g(c,i.clone(),[`__WB_REVISION__`],s):null;try{await c.put(i,l?a.clone():a)}catch(e){if(e instanceof Error)throw e.name===`QuotaExceededError`&&await b(),e}for(let e of this.iterateCallbacks(`cacheDidUpdate`))await e({cacheName:o,oldResponse:u,newResponse:a.clone(),request:i,event:this.event});return!0}async getCacheKey(e,t){let n=`${e.url} | ${t}`;if(!this._cacheKeys[n]){let r=e;for(let e of this.iterateCallbacks(`cacheKeyWillBeUsed`))r=S(await e({mode:t,request:r,event:this.event,params:this.params}));this._cacheKeys[n]=r}return this._cacheKeys[n]}hasCallback(e){for(let t of this._strategy.plugins)if(e in t)return!0;return!1}async runCallbacks(e,t){for(let n of this.iterateCallbacks(e))await n(t)}*iterateCallbacks(e){for(let t of this._strategy.plugins)if(typeof t[e]==`function`){let n=this._pluginStateMap.get(t);yield r=>{let i=Object.assign(Object.assign({},r),{state:n});return t[e](i)}}}waitUntil(e){return this._extendLifetimePromises.push(e),e}async doneWaiting(){for(;this._extendLifetimePromises.length;){let e=this._extendLifetimePromises.splice(0),t=(await Promise.allSettled(e)).find(e=>e.status===`rejected`);if(t)throw t.reason}}destroy(){this._handlerDeferred.resolve(null)}async _ensureResponseSafeToCache(e){let t=e,n=!1;for(let e of this.iterateCallbacks(`cacheWillUpdate`))if(t=await e({request:this.request,response:t,event:this.event})||void 0,n=!0,!t)break;return n||t&&t.status!==200&&(t=void 0),t}},w=class{constructor(e={}){this.cacheName=a.getRuntimeName(e.cacheName),this.plugins=e.plugins||[],this.fetchOptions=e.fetchOptions,this.matchOptions=e.matchOptions}handle(e){let[t]=this.handleAll(e);return t}handleAll(e){e instanceof FetchEvent&&(e={event:e,request:e.request});let t=e.event,n=typeof e.request==`string`?new Request(e.request):e.request,r=`params`in e?e.params:void 0,i=new C(this,{event:t,request:n,params:r}),a=this._getResponse(i,n,t);return[a,this._awaitComplete(a,i,n,t)]}async _getResponse(e,n,r){await e.runCallbacks(`handlerWillStart`,{event:r,request:n});let i;try{if(i=await this._handle(n,e),!i||i.type===`error`)throw new t(`no-response`,{url:n.url})}catch(t){if(t instanceof Error){for(let a of e.iterateCallbacks(`handlerDidError`))if(i=await a({error:t,event:r,request:n}),i)break}if(!i)throw t}for(let t of e.iterateCallbacks(`handlerWillRespond`))i=await t({event:r,request:n,response:i});return i}async _awaitComplete(e,t,n,r){let i,a;try{i=await e}catch{}try{await t.runCallbacks(`handlerDidRespond`,{event:r,request:n,response:i}),await t.doneWaiting()}catch(e){e instanceof Error&&(a=e)}if(await t.runCallbacks(`handlerDidComplete`,{event:r,request:n,response:i,error:a}),t.destroy(),a)throw a}},T=class e extends w{constructor(t={}){t.cacheName=a.getPrecacheName(t.cacheName),super(t),this._fallbackToNetwork=t.fallbackToNetwork!==!1,this.plugins.push(e.copyRedirectedCacheableResponsesPlugin)}async _handle(e,t){return await t.cacheMatch(e)||(t.event&&t.event.type===`install`?await this._handleInstall(e,t):await this._handleFetch(e,t))}async _handleFetch(e,n){let r,i=n.params||{};if(this._fallbackToNetwork){let t=i.integrity,a=e.integrity,o=!a||a===t;r=await n.fetch(new Request(e,{integrity:e.mode===`no-cors`?void 0:a||t})),t&&o&&e.mode!==`no-cors`&&(this._useDefaultCacheabilityPluginIfNeeded(),await n.cachePut(e,r.clone()))}else throw new t(`missing-precache-entry`,{cacheName:this.cacheName,url:e.url});return r}async _handleInstall(e,n){this._useDefaultCacheabilityPluginIfNeeded();let r=await n.fetch(e);if(!await n.cachePut(e,r.clone()))throw new t(`bad-precaching-response`,{url:e.url,status:r.status});return r}_useDefaultCacheabilityPluginIfNeeded(){let t=null,n=0;for(let[r,i]of this.plugins.entries())i!==e.copyRedirectedCacheableResponsesPlugin&&(i===e.defaultPrecacheCacheabilityPlugin&&(t=r),i.cacheWillUpdate&&n++);n===0?this.plugins.push(e.defaultPrecacheCacheabilityPlugin):n>1&&t!==null&&this.plugins.splice(t,1)}};T.defaultPrecacheCacheabilityPlugin={async cacheWillUpdate({response:e}){return!e||e.status>=400?null:e}},T.copyRedirectedCacheableResponsesPlugin={async cacheWillUpdate({response:e}){return e.redirected?await p(e):e}};var E=class{constructor({cacheName:e,plugins:t=[],fallbackToNetwork:n=!0}={}){this._urlsToCacheKeys=new Map,this._urlsToCacheModes=new Map,this._cacheKeysToIntegrities=new Map,this._strategy=new T({cacheName:a.getPrecacheName(e),plugins:[...t,new u({precacheController:this})],fallbackToNetwork:n}),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),!0)}addToCacheList(e){let n=[];for(let r of e){typeof r==`string`?n.push(r):r&&r.revision===void 0&&n.push(r.url);let{cacheKey:e,url:i}=c(r),a=typeof r!=`string`&&r.revision?`reload`:`default`;if(this._urlsToCacheKeys.has(i)&&this._urlsToCacheKeys.get(i)!==e)throw new t(`add-to-cache-list-conflicting-entries`,{firstEntry:this._urlsToCacheKeys.get(i),secondEntry:e});if(typeof r!=`string`&&r.integrity){if(this._cacheKeysToIntegrities.has(e)&&this._cacheKeysToIntegrities.get(e)!==r.integrity)throw new t(`add-to-cache-list-conflicting-integrities`,{url:i});this._cacheKeysToIntegrities.set(e,r.integrity)}if(this._urlsToCacheKeys.set(i,e),this._urlsToCacheModes.set(i,a),n.length>0){let e=`Workbox is precaching URLs without revision info: ${n.join(`, `)}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(e)}}}install(e){return o(e,async()=>{let t=new l;this.strategy.plugins.push(t);for(let[t,n]of this._urlsToCacheKeys){let r=this._cacheKeysToIntegrities.get(n),i=this._urlsToCacheModes.get(t),a=new Request(t,{integrity:r,cache:i,credentials:`same-origin`});await Promise.all(this.strategy.handleAll({params:{cacheKey:n},request:a,event:e}))}let{updatedURLs:n,notUpdatedURLs:r}=t;return{updatedURLs:n,notUpdatedURLs:r}})}activate(e){return o(e,async()=>{let e=await self.caches.open(this.strategy.cacheName),t=await e.keys(),n=new Set(this._urlsToCacheKeys.values()),r=[];for(let i of t)n.has(i.url)||(await e.delete(i),r.push(i.url));return{deletedURLs:r}})}getURLsToCacheKeys(){return this._urlsToCacheKeys}getCachedURLs(){return[...this._urlsToCacheKeys.keys()]}getCacheKeyForURL(e){let t=new URL(e,location.href);return this._urlsToCacheKeys.get(t.href)}getIntegrityForCacheKey(e){return this._cacheKeysToIntegrities.get(e)}async matchPrecache(e){let t=e instanceof Request?e.url:e,n=this.getCacheKeyForURL(t);if(n)return(await self.caches.open(this.strategy.cacheName)).match(n)}createHandlerBoundToURL(e){let n=this.getCacheKeyForURL(e);if(!n)throw new t(`non-precached-url`,{url:e});return t=>(t.request=new Request(e),t.params=Object.assign({cacheKey:n},t.params),this.strategy.handle(t))}},D,O=()=>(D||=new E,D);try{self[`workbox:routing:7.3.0`]&&_()}catch{}var k=e=>e&&typeof e==`object`?e:{handle:e},A=class{constructor(e,t,n=`GET`){this.handler=k(t),this.match=e,this.method=n}setCatchHandler(e){this.catchHandler=k(e)}},j=class extends A{constructor(e,t,n){super(({url:t})=>{let n=e.exec(t.href);if(n&&!(t.origin!==location.origin&&n.index!==0))return n.slice(1)},t,n)}},M=class{constructor(){this._routes=new Map,this._defaultHandlerMap=new Map}get routes(){return this._routes}addFetchListener(){self.addEventListener(`fetch`,(e=>{let{request:t}=e,n=this.handleRequest({request:t,event:e});n&&e.respondWith(n)}))}addCacheListener(){self.addEventListener(`message`,(e=>{if(e.data&&e.data.type===`CACHE_URLS`){let{payload:t}=e.data,n=Promise.all(t.urlsToCache.map(t=>{typeof t==`string`&&(t=[t]);let n=new Request(...t);return this.handleRequest({request:n,event:e})}));e.waitUntil(n),e.ports&&e.ports[0]&&n.then(()=>e.ports[0].postMessage(!0))}}))}handleRequest({request:e,event:t}){let n=new URL(e.url,location.href);if(!n.protocol.startsWith(`http`))return;let r=n.origin===location.origin,{params:i,route:a}=this.findMatchingRoute({event:t,request:e,sameOrigin:r,url:n}),o=a&&a.handler,s=e.method;if(!o&&this._defaultHandlerMap.has(s)&&(o=this._defaultHandlerMap.get(s)),!o)return;let c;try{c=o.handle({url:n,request:e,event:t,params:i})}catch(e){c=Promise.reject(e)}let l=a&&a.catchHandler;return c instanceof Promise&&(this._catchHandler||l)&&(c=c.catch(async r=>{if(l)try{return await l.handle({url:n,request:e,event:t,params:i})}catch(e){e instanceof Error&&(r=e)}if(this._catchHandler)return this._catchHandler.handle({url:n,request:e,event:t});throw r})),c}findMatchingRoute({url:e,sameOrigin:t,request:n,event:r}){let i=this._routes.get(n.method)||[];for(let a of i){let i,o=a.match({url:e,sameOrigin:t,request:n,event:r});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:a,params:i}}return{}}setDefaultHandler(e,t=`GET`){this._defaultHandlerMap.set(t,k(e))}setCatchHandler(e){this._catchHandler=k(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 t(`unregister-route-but-not-found-with-method`,{method:e.method});let n=this._routes.get(e.method).indexOf(e);if(n>-1)this._routes.get(e.method).splice(n,1);else throw new t(`unregister-route-route-not-registered`)}},N,P=()=>(N||(N=new M,N.addFetchListener(),N.addCacheListener()),N);function F(e,n,r){let i;if(typeof e==`string`){let t=new URL(e,location.href);i=new A(({url:e})=>e.href===t.href,n,r)}else if(e instanceof RegExp)i=new j(e,n,r);else if(typeof e==`function`)i=new A(e,n,r);else if(e instanceof A)i=e;else throw new t(`unsupported-route-type`,{moduleName:`workbox-routing`,funcName:`registerRoute`,paramName:`capture`});return P().registerRoute(i),i}function I(e,t=[]){for(let n of[...e.searchParams.keys()])t.some(e=>e.test(n))&&e.searchParams.delete(n);return e}function*L(e,{ignoreURLParametersMatching:t=[/^utm_/,/^fbclid$/],directoryIndex:n=`index.html`,cleanURLs:r=!0,urlManipulation:i}={}){let a=new URL(e,location.href);a.hash=``,yield a.href;let o=I(a,t);if(yield o.href,n&&o.pathname.endsWith(`/`)){let e=new URL(o.href);e.pathname+=n,yield e.href}if(r){let e=new URL(o.href);e.pathname+=`.html`,yield e.href}if(i){let e=i({url:a});for(let t of e)yield t.href}}var R=class extends A{constructor(e,t){super(({request:n})=>{let r=e.getURLsToCacheKeys();for(let i of L(n.url,t)){let t=r.get(i);if(t)return{cacheKey:t,integrity:e.getIntegrityForCacheKey(t)}}},e.strategy)}};function z(e){F(new R(O(),e))}function B(e){O().precache(e)}function V(e,t){B(e),z(t)}V([{"revision":"1872c500de691dce40960bb85481de07","url":"registerSW.js"},{"revision":"fe6ac64860fe6c13aa77616771054d58","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"948e060affb598c339be40d69e1f6f9c","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"a5d8a1acfc29c2a4c882a54ffc93def3","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"d0f94ce046cf8cf09605ee7664dac557","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"a424156a79b9c1b907db93aa3180585a","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"b3a7f967560c9816492a1567b3f7f0dc","url":"monacoeditorwork/css.worker.bundle.js"},{"revision":null,"url":"assets/xychartDiagram-JWTSCODW-DylHYNtJ.js"},{"revision":null,"url":"assets/vennDiagram-LZ73GAT5-ywK7LMaH.js"},{"revision":null,"url":"assets/utils-DMiycH3O.js"},{"revision":null,"url":"assets/use-monaco-theme-Dk_fE15d.js"},{"revision":null,"url":"assets/treemap-KZPCXAKY-C9TYRE0k.js"},{"revision":null,"url":"assets/timeline-definition-YZTLITO2-A4PN_Efm.js"},{"revision":null,"url":"assets/terminal-tab-wFhiLTfY.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-CXMT0QB6.js"},{"revision":null,"url":"assets/table-DFevCOMd.js"},{"revision":null,"url":"assets/stateDiagram-v2-FVOUBMTO-B-UjZch3.js"},{"revision":null,"url":"assets/stateDiagram-RAJIS63D-C4EMl6jf.js"},{"revision":null,"url":"assets/src-Dw4QhedI.js"},{"revision":null,"url":"assets/square-oPKIkJiw.js"},{"revision":null,"url":"assets/sqlite-viewer-DvlMWCLX.js"},{"revision":null,"url":"assets/settings-tab-Ba9P0f9D.js"},{"revision":null,"url":"assets/sequenceDiagram-2WXFIKYE-DM-tMAhx.js"},{"revision":null,"url":"assets/sankeyDiagram-WA2Y5GQK-RolPi8bU.js"},{"revision":null,"url":"assets/rough.esm-nHaDi0Kw.js"},{"revision":null,"url":"assets/requirementDiagram-Z7DCOOCP-B9F_Cx_p.js"},{"revision":null,"url":"assets/react-nm2Ru1Pt.js"},{"revision":null,"url":"assets/react-dom-Bpkvzu3U.js"},{"revision":null,"url":"assets/react-SKk5z-bm.js"},{"revision":null,"url":"assets/radar-KQ55EAFF-Z-Tr5wtS.js"},{"revision":null,"url":"assets/quadrantDiagram-337W2JSQ-CdjGIDfw.js"},{"revision":null,"url":"assets/preload-helper-Bf_JiD2A.js"},{"revision":null,"url":"assets/postgres-viewer-E0_ojaz2.js"},{"revision":null,"url":"assets/port-forwarding-tab-DCfENtZd.js"},{"revision":null,"url":"assets/pieDiagram-SKSYHLDU-At5Kz0KK.js"},{"revision":null,"url":"assets/pie-UPGHQEXC-CvXHKAzp.js"},{"revision":null,"url":"assets/path-DIKpVbHL.js"},{"revision":null,"url":"assets/packet-RMMSAZCW-IVa5F-go.js"},{"revision":null,"url":"assets/ordinal-LFEjVtwQ.js"},{"revision":null,"url":"assets/mindmap-definition-YRQLILUH-BaOBwb-W.js"},{"revision":null,"url":"assets/mermaid-parser.core-8u2leTXI.js"},{"revision":null,"url":"assets/math-y9zN1W-N.js"},{"revision":null,"url":"assets/markdown-renderer-DoqEXzyK.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-mag4ySk-.js"},{"revision":null,"url":"assets/keybindings-store-DbtSlUnk.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-kMwlnEGE.js"},{"revision":null,"url":"assets/journeyDiagram-4ABVD52K-CgDI-UG4.js"},{"revision":null,"url":"assets/ishikawaDiagram-PHBUUO56-Cu0Rt1Ok.js"},{"revision":null,"url":"assets/isEmpty-B9L-Ge-H.js"},{"revision":null,"url":"assets/isArrayLikeObject-CGBoxvCD.js"},{"revision":null,"url":"assets/init-C0r9Gk5G.js"},{"revision":null,"url":"assets/infoDiagram-LFFYTUFH-ZZmpgc6t.js"},{"revision":null,"url":"assets/info-3K5VOQVL-BDzTLc11.js"},{"revision":null,"url":"assets/index-r64nXcCm.css"},{"revision":null,"url":"assets/index-B23bElOE.js"},{"revision":null,"url":"assets/graphlib-Duh_bWLa.js"},{"revision":null,"url":"assets/gitGraphDiagram-K3NZZRJ6-BeHSX7kk.js"},{"revision":null,"url":"assets/gitGraph-HDMCJU4V-CNlas3Rz.js"},{"revision":null,"url":"assets/git-graph-D_7WjkSI.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-aIzLDxoc.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/dist-C40JmyoH.js"},{"revision":null,"url":"assets/diff-viewer-BGSt0dzB.js"},{"revision":null,"url":"assets/diagram-P4PSJMXO-hzmp0GHK.js"},{"revision":null,"url":"assets/diagram-IFDJBPK2-sqTog_XV.js"},{"revision":null,"url":"assets/diagram-E7M64L7V-DxPjK7_c.js"},{"revision":null,"url":"assets/defaultLocale-CrJzLgRD.js"},{"revision":null,"url":"assets/database-viewer-v3PVSVgo.js"},{"revision":null,"url":"assets/dagre-KLK3FWXG-C3O-MTLf.js"},{"revision":null,"url":"assets/dagre-BFcnKyBF.js"},{"revision":null,"url":"assets/cytoscape.esm-CWPXKqbJ.js"},{"revision":null,"url":"assets/csv-preview-sx6DC51G.js"},{"revision":null,"url":"assets/createLucideIcon-PuMiQgHl.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-cEVJHYd7.js"},{"revision":null,"url":"assets/code-editor-Bjh4LrGQ.js"},{"revision":null,"url":"assets/clone-B2hUek6n.js"},{"revision":null,"url":"assets/classDiagram-v2-RAHNMMFH-D8IvcV_B.js"},{"revision":null,"url":"assets/classDiagram-VBA2DB6C-Dp4Kk3Yb.js"},{"revision":null,"url":"assets/chunk-YBOYWFTD-av5aeHLq.js"},{"revision":null,"url":"assets/chunk-XZSTWKYB-Cb0iqycX.js"},{"revision":null,"url":"assets/chunk-XPW4576I-BPEX8KhL.js"},{"revision":null,"url":"assets/chunk-XIRO2GV7-DRJEb7Zb.js"},{"revision":null,"url":"assets/chunk-WL4C6EOR-CXuQvlyu.js"},{"revision":null,"url":"assets/chunk-R5LLSJPH-CMY0PkRK.js"},{"revision":null,"url":"assets/chunk-QZHKN3VN-DFKFM_C1.js"},{"revision":null,"url":"assets/chunk-PU5JKC2W-C7Gry6md.js"},{"revision":null,"url":"assets/chunk-PQ6SQG4A-DX0xW7kO.js"},{"revision":null,"url":"assets/chunk-OZEHJAEY-rG0P22U9.js"},{"revision":null,"url":"assets/chunk-O4XLMI2P-BsUWb9d0.js"},{"revision":null,"url":"assets/chunk-NQ4KR5QH-DXUTQ-BL.js"},{"revision":null,"url":"assets/chunk-MX3YWQON-C2UEioMs.js"},{"revision":null,"url":"assets/chunk-L3YUKLVL-HG_eMj_C.js"},{"revision":null,"url":"assets/chunk-KYZI473N-Djw13C-3.js"},{"revision":null,"url":"assets/chunk-KX2RTZJC-DP36BDiU.js"},{"revision":null,"url":"assets/chunk-JSJVCQXG-BBmymCjA.js"},{"revision":null,"url":"assets/chunk-HHEYEP7N-BBw_z0fW.js"},{"revision":null,"url":"assets/chunk-GLR3WWYH-DBdWQ3zy.js"},{"revision":null,"url":"assets/chunk-GEFDOKGD-tDjHsAUs.js"},{"revision":null,"url":"assets/chunk-FMBD7UC4-D23YVTOU.js"},{"revision":null,"url":"assets/chunk-EGIJ26TM-Cpr87sBR.js"},{"revision":null,"url":"assets/chunk-CFjPhJqf.js"},{"revision":null,"url":"assets/chunk-C72U2L5F-CtqKiH4q.js"},{"revision":null,"url":"assets/chunk-7R4GIKGN-Dvbyu4Zw.js"},{"revision":null,"url":"assets/chunk-7E7YKBS2-CkFGv6Zs.js"},{"revision":null,"url":"assets/chunk-55IACEB6-D5cABeB9.js"},{"revision":null,"url":"assets/chunk-4BX2VUAB-C3aZvW7B.js"},{"revision":null,"url":"assets/chevron-right-5HgK6l7K.js"},{"revision":null,"url":"assets/chat-tab-GSn-Itse.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up-BYhx9ckd.js"},{"revision":null,"url":"assets/array-DqLCdDFv.js"},{"revision":null,"url":"assets/architectureDiagram-2XIMDMQ5-DqAZP_F6.js"},{"revision":null,"url":"assets/architecture-PBZL5I3N-CFzkFKEL.js"},{"revision":null,"url":"assets/arc-B9n1Gvb5.js"},{"revision":null,"url":"assets/api-settings-CgBII8jW.js"},{"revision":null,"url":"assets/api-client-BKIT_Qeg.js"},{"revision":null,"url":"assets/_baseUniq-Yy35llnn.js"},{"revision":null,"url":"assets/_basePickBy-3Xe18azI.js"},{"revision":"79c8870653c8f419f2e3323085e1f4be","url":"manifest.webmanifest"}]),self.addEventListener(`push`,e=>{e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{if(t.some(e=>e.visibilityState===`visible`))return;let n=e.data?.json()??{title:`PPM`,body:`Chat completed`};return self.registration.showNotification(n.title,{body:n.body,icon:`/icon-192.png`,badge:`/icon-192.png`,tag:`ppm-chat-done`,silent:!1,data:{url:self.location.origin}})}))}),self.addEventListener(`notificationclick`,e=>{e.notification.close(),e.waitUntil(self.clients.matchAll({type:`window`,includeUncontrolled:!0}).then(t=>{for(let e of t)if(e.url.includes(self.location.origin)&&`focus`in e)return e.focus();return self.clients.openWindow(e.notification.data?.url||`/`)}))});
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
query,
|
|
3
3
|
listSessions as sdkListSessions,
|
|
4
|
+
getSessionInfo as sdkGetSessionInfo,
|
|
4
5
|
getSessionMessages,
|
|
5
6
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
6
7
|
import type {
|
|
@@ -318,9 +319,11 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
318
319
|
return this.listSessionsByDir();
|
|
319
320
|
}
|
|
320
321
|
|
|
321
|
-
async listSessionsByDir(dir?: string): Promise<SessionInfo[]> {
|
|
322
|
+
async listSessionsByDir(dir?: string, opts?: { limit?: number; offset?: number }): Promise<SessionInfo[]> {
|
|
322
323
|
try {
|
|
323
|
-
const
|
|
324
|
+
const limit = opts?.limit ?? 50;
|
|
325
|
+
const offset = opts?.offset ?? 0;
|
|
326
|
+
const sdkSessions = await sdkListSessions({ dir, limit, offset });
|
|
324
327
|
// Overlay DB titles (user-set) over SDK titles
|
|
325
328
|
const ids = sdkSessions.map((s) => s.sessionId);
|
|
326
329
|
const dbTitles = getSessionTitles(ids);
|
|
@@ -342,6 +345,23 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
342
345
|
}
|
|
343
346
|
}
|
|
344
347
|
|
|
348
|
+
async getSessionInfoById(sessionId: string, dir?: string): Promise<SessionInfo | null> {
|
|
349
|
+
try {
|
|
350
|
+
const info = await sdkGetSessionInfo(sessionId, { dir });
|
|
351
|
+
if (!info) return null;
|
|
352
|
+
const dbTitles = getSessionTitles([info.sessionId]);
|
|
353
|
+
return {
|
|
354
|
+
id: info.sessionId,
|
|
355
|
+
providerId: this.id,
|
|
356
|
+
title: dbTitles[info.sessionId] ?? info.customTitle ?? info.summary ?? info.firstPrompt ?? "Chat",
|
|
357
|
+
createdAt: new Date(info.lastModified).toISOString(),
|
|
358
|
+
updatedAt: new Date(info.lastModified).toISOString(),
|
|
359
|
+
};
|
|
360
|
+
} catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
345
365
|
async deleteSession(sessionId: string): Promise<void> {
|
|
346
366
|
this.closeStreamingSession(sessionId);
|
|
347
367
|
this.activeSessions.delete(sessionId);
|
|
@@ -813,6 +833,8 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
813
833
|
if (subtype === "api_retry" && (msg as any).error_status === 401 && account && !authRetried) {
|
|
814
834
|
authRetried = true;
|
|
815
835
|
try {
|
|
836
|
+
// refreshAccessToken has mutex + skip-if-fresh: if another session already
|
|
837
|
+
// refreshed, it returns immediately without calling OAuth again.
|
|
816
838
|
await accountService.refreshAccessToken(account.id, false);
|
|
817
839
|
const refreshedAccount = accountService.getWithTokens(account.id);
|
|
818
840
|
if (refreshedAccount) {
|
|
@@ -988,6 +1010,8 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
988
1010
|
if (assistantError === "authentication_failed" && account && !authRetried) {
|
|
989
1011
|
authRetried = true;
|
|
990
1012
|
try {
|
|
1013
|
+
// refreshAccessToken has mutex + skip-if-fresh: if another session already
|
|
1014
|
+
// refreshed, it returns immediately without calling OAuth again.
|
|
991
1015
|
await accountService.refreshAccessToken(account.id, false);
|
|
992
1016
|
const refreshedAccount = accountService.getWithTokens(account.id);
|
|
993
1017
|
if (refreshedAccount) {
|
|
@@ -1176,6 +1200,8 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
1176
1200
|
if (!authRetried) {
|
|
1177
1201
|
authRetried = true;
|
|
1178
1202
|
try {
|
|
1203
|
+
// refreshAccessToken has mutex + skip-if-fresh: if another session already
|
|
1204
|
+
// refreshed, it returns immediately without calling OAuth again.
|
|
1179
1205
|
await accountService.refreshAccessToken(account.id, false);
|
|
1180
1206
|
const refreshedAccount = accountService.getWithTokens(account.id);
|
|
1181
1207
|
if (refreshedAccount) {
|
|
@@ -2,7 +2,7 @@ import { Hono } from "hono";
|
|
|
2
2
|
import type { Context } from "hono";
|
|
3
3
|
import { accountService } from "../../services/account.service.ts";
|
|
4
4
|
import { accountSelector } from "../../services/account-selector.service.ts";
|
|
5
|
-
import { updateAccount } from "../../services/db.service.ts";
|
|
5
|
+
import { updateAccount, getSnapshotHistory } from "../../services/db.service.ts";
|
|
6
6
|
import { getAllAccountUsages, getUsageForAccount } from "../../services/claude-usage.service.ts";
|
|
7
7
|
import { ok, err } from "../../types/api.ts";
|
|
8
8
|
|
|
@@ -193,6 +193,14 @@ accountsRoutes.get("/:id/usage", (c) => {
|
|
|
193
193
|
return c.json(ok(getUsageForAccount(id)));
|
|
194
194
|
});
|
|
195
195
|
|
|
196
|
+
/** GET /api/accounts/:id/usage-history — snapshot history for charts */
|
|
197
|
+
accountsRoutes.get("/:id/usage-history", (c) => {
|
|
198
|
+
const { id } = c.req.param();
|
|
199
|
+
const hours = Math.min(parseInt(c.req.query("hours") ?? "168", 10) || 168, 168);
|
|
200
|
+
const snapshots = getSnapshotHistory(id, hours);
|
|
201
|
+
return c.json(ok(snapshots));
|
|
202
|
+
});
|
|
203
|
+
|
|
196
204
|
/** POST /api/accounts/:id/verify — re-verify token & refresh profile */
|
|
197
205
|
accountsRoutes.post("/:id/verify", async (c) => {
|
|
198
206
|
const { id } = c.req.param();
|
|
@@ -75,17 +75,44 @@ chatRoutes.get("/sessions", async (c) => {
|
|
|
75
75
|
try {
|
|
76
76
|
const projectPath = c.get("projectPath");
|
|
77
77
|
const providerId = c.req.query("providerId");
|
|
78
|
-
const
|
|
79
|
-
|
|
78
|
+
const limit = Math.min(parseInt(c.req.query("limit") ?? "50", 10) || 50, 200);
|
|
79
|
+
const offset = parseInt(c.req.query("offset") ?? "0", 10) || 0;
|
|
80
|
+
|
|
81
|
+
const sessions = await chatService.listSessions(providerId, projectPath, { limit, offset });
|
|
80
82
|
const pinnedIds = getPinnedSessionIds();
|
|
81
|
-
|
|
82
|
-
//
|
|
83
|
+
|
|
84
|
+
// On first page, fetch pinned sessions that may be outside the current page
|
|
85
|
+
let pinnedSessions: typeof sessions = [];
|
|
86
|
+
if (offset === 0 && pinnedIds.size > 0) {
|
|
87
|
+
const pageIds = new Set(sessions.map((s) => s.id));
|
|
88
|
+
const missingPinnedIds = [...pinnedIds].filter((id) => !pageIds.has(id));
|
|
89
|
+
if (missingPinnedIds.length > 0) {
|
|
90
|
+
// Fetch individual pinned sessions by ID via SDK
|
|
91
|
+
const claudeProvider = providerRegistry.get("claude") as any;
|
|
92
|
+
if (claudeProvider?.getSessionInfoById) {
|
|
93
|
+
const results = await Promise.all(
|
|
94
|
+
missingPinnedIds.map((id) => claudeProvider.getSessionInfoById(id, projectPath)),
|
|
95
|
+
);
|
|
96
|
+
pinnedSessions = results.filter((s: any): s is NonNullable<typeof s> => s != null);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Merge and enrich with pin status
|
|
102
|
+
const merged = [...pinnedSessions, ...sessions];
|
|
103
|
+
const seen = new Set<string>();
|
|
104
|
+
const deduped = merged.filter((s) => { if (seen.has(s.id)) return false; seen.add(s.id); return true; });
|
|
105
|
+
const enriched = deduped.map((s) => ({ ...s, pinned: pinnedIds.has(s.id) }));
|
|
106
|
+
|
|
107
|
+
// Sort: pinned first, then by createdAt desc
|
|
83
108
|
enriched.sort((a, b) => {
|
|
84
109
|
if (a.pinned && !b.pinned) return -1;
|
|
85
110
|
if (!a.pinned && b.pinned) return 1;
|
|
86
111
|
return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
|
|
87
112
|
});
|
|
88
|
-
|
|
113
|
+
|
|
114
|
+
const hasMore = sessions.length >= limit;
|
|
115
|
+
return c.json(ok({ sessions: enriched, hasMore }));
|
|
89
116
|
} catch (e) {
|
|
90
117
|
return c.json(err((e as Error).message), 500);
|
|
91
118
|
}
|
|
@@ -77,6 +77,8 @@ const acctHotState = ((globalThis as any)[ACCT_HOT_KEY] ??= {
|
|
|
77
77
|
|
|
78
78
|
class AccountService {
|
|
79
79
|
private pendingStates = new Map<string, { verifier: string; createdAt: number }>();
|
|
80
|
+
/** Per-account mutex: dedup concurrent refresh calls so only one OAuth request fires at a time. */
|
|
81
|
+
private pendingRefreshes = new Map<string, Promise<void>>();
|
|
80
82
|
|
|
81
83
|
private toAccount(row: AccountRow): Account {
|
|
82
84
|
let profileData: OAuthProfileData | null = null;
|
|
@@ -517,16 +519,42 @@ class AccountService {
|
|
|
517
519
|
|
|
518
520
|
/**
|
|
519
521
|
* Refresh an OAuth access token using the stored refresh token.
|
|
522
|
+
* Uses a per-account mutex to prevent concurrent refresh calls from racing
|
|
523
|
+
* (Anthropic rotates refresh tokens — only one call per token is valid).
|
|
524
|
+
* Also skips the OAuth call if the DB token was already refreshed by another session.
|
|
520
525
|
* @param disableOnFail - if true, disable the account when refresh fails (default: true).
|
|
521
526
|
* Background/startup refresh should pass false to avoid disabling accounts prematurely.
|
|
522
527
|
*/
|
|
523
528
|
async refreshAccessToken(accountId: string, disableOnFail = true): Promise<void> {
|
|
529
|
+
// Dedup: if a refresh is already in progress for this account, wait for it instead of racing
|
|
530
|
+
const pending = this.pendingRefreshes.get(accountId);
|
|
531
|
+
if (pending) {
|
|
532
|
+
console.log(`[accounts] Refresh already in progress for ${accountId} — waiting for it`);
|
|
533
|
+
return pending;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const promise = this._doRefreshAccessToken(accountId, disableOnFail);
|
|
537
|
+
this.pendingRefreshes.set(accountId, promise);
|
|
538
|
+
try {
|
|
539
|
+
await promise;
|
|
540
|
+
} finally {
|
|
541
|
+
this.pendingRefreshes.delete(accountId);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
private async _doRefreshAccessToken(accountId: string, disableOnFail: boolean): Promise<void> {
|
|
524
546
|
const account = this.getWithTokens(accountId);
|
|
525
547
|
if (!account) throw new Error(`Account ${accountId} not found`);
|
|
526
548
|
// Skip refresh for temporary accounts (no refresh token)
|
|
527
549
|
if (!account.refreshToken || account.refreshToken === "") {
|
|
528
550
|
throw new Error(`Account ${accountId} has no refresh token (temporary account)`);
|
|
529
551
|
}
|
|
552
|
+
// Skip if token was already refreshed by another session (still fresh)
|
|
553
|
+
const nowS = Math.floor(Date.now() / 1000);
|
|
554
|
+
if (account.expiresAt && account.expiresAt - nowS > 60) {
|
|
555
|
+
console.log(`[accounts] Token for ${account.email ?? accountId} is already fresh (expires in ${account.expiresAt - nowS}s) — skipping OAuth refresh`);
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
530
558
|
const res = await fetch(OAUTH_TOKEN_URL, {
|
|
531
559
|
method: "POST",
|
|
532
560
|
headers: { "Content-Type": "application/json" },
|
|
@@ -542,6 +570,12 @@ class AccountService {
|
|
|
542
570
|
console.error(`[accounts] Refresh failed for ${accountId}: ${res.status} ${errorBody}`);
|
|
543
571
|
// invalid_grant or invalid_request = refresh token permanently dead → clear it so account becomes temporary
|
|
544
572
|
if (errorBody.includes("invalid_grant") || errorBody.includes("invalid_request")) {
|
|
573
|
+
// Double-check: another session might have already refreshed between our read and the OAuth call
|
|
574
|
+
const recheckAccount = this.getWithTokens(accountId);
|
|
575
|
+
if (recheckAccount?.expiresAt && recheckAccount.expiresAt - Math.floor(Date.now() / 1000) > 60) {
|
|
576
|
+
console.log(`[accounts] Refresh failed with invalid_grant but DB token is now fresh — another session refreshed it`);
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
545
579
|
console.log(`[accounts] Clearing invalid refresh token for ${account.email ?? accountId} — account is now temporary`);
|
|
546
580
|
updateAccount(accountId, { refresh_token: encrypt("") });
|
|
547
581
|
}
|
|
@@ -29,12 +29,12 @@ class ChatService {
|
|
|
29
29
|
return provider.resumeSession(sessionId);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async listSessions(providerId?: string, dir?: string): Promise<SessionInfo[]> {
|
|
32
|
+
async listSessions(providerId?: string, dir?: string, opts?: { limit?: number; offset?: number }): Promise<SessionInfo[]> {
|
|
33
33
|
if (providerId) {
|
|
34
34
|
const provider = providerRegistry.get(providerId);
|
|
35
35
|
if (!provider) throw new Error(`Provider "${providerId}" not found`);
|
|
36
36
|
if (dir && provider.listSessionsByDir) {
|
|
37
|
-
return provider.listSessionsByDir(dir);
|
|
37
|
+
return provider.listSessionsByDir(dir, opts);
|
|
38
38
|
}
|
|
39
39
|
return provider.listSessions();
|
|
40
40
|
}
|
|
@@ -44,7 +44,7 @@ class ChatService {
|
|
|
44
44
|
const provider = providerRegistry.get(info.id);
|
|
45
45
|
if (provider) {
|
|
46
46
|
if (dir && provider.listSessionsByDir) {
|
|
47
|
-
all.push(...await provider.listSessionsByDir(dir));
|
|
47
|
+
all.push(...await provider.listSessionsByDir(dir, opts));
|
|
48
48
|
} else {
|
|
49
49
|
all.push(...await provider.listSessions());
|
|
50
50
|
}
|
|
@@ -758,6 +758,15 @@ export function deleteSnapshotsForAccount(accountId: string): void {
|
|
|
758
758
|
getDb().query("DELETE FROM claude_limit_snapshots WHERE account_id = ?").run(accountId);
|
|
759
759
|
}
|
|
760
760
|
|
|
761
|
+
/** Get all snapshots for an account within the last N hours (default 7 days = 168h) */
|
|
762
|
+
export function getSnapshotHistory(accountId: string, hours = 168): LimitSnapshotRow[] {
|
|
763
|
+
return getDb().query(
|
|
764
|
+
`SELECT * FROM claude_limit_snapshots
|
|
765
|
+
WHERE account_id = ? AND recorded_at > datetime('now', '-' || ? || ' hours')
|
|
766
|
+
ORDER BY recorded_at ASC`,
|
|
767
|
+
).all(accountId, hours) as LimitSnapshotRow[];
|
|
768
|
+
}
|
|
769
|
+
|
|
761
770
|
export function cleanupOldLimitSnapshots(): void {
|
|
762
771
|
getDb().query(
|
|
763
772
|
"DELETE FROM claude_limit_snapshots WHERE recorded_at < datetime('now', '-7 days')",
|
package/src/types/chat.ts
CHANGED
|
@@ -26,7 +26,7 @@ export interface AIProvider {
|
|
|
26
26
|
onToolApproval?: (callback: ToolApprovalHandler) => void;
|
|
27
27
|
abortQuery?(sessionId: string): void;
|
|
28
28
|
getMessages?(sessionId: string): Promise<ChatMessage[]>;
|
|
29
|
-
listSessionsByDir?(dir: string): Promise<SessionInfo[]>;
|
|
29
|
+
listSessionsByDir?(dir: string, opts?: { limit?: number; offset?: number }): Promise<SessionInfo[]>;
|
|
30
30
|
ensureProjectPath?(sessionId: string, path: string): void;
|
|
31
31
|
setForkSource?(sessionId: string, sourceSessionId: string): void;
|
|
32
32
|
forkAtMessage?(sessionId: string, messageId: string, opts?: { title?: string; dir?: string }): Promise<{ sessionId: string }>;
|
|
@@ -66,6 +66,11 @@ export interface SessionInfo {
|
|
|
66
66
|
pinned?: boolean;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
export interface SessionListResponse {
|
|
70
|
+
sessions: SessionInfo[];
|
|
71
|
+
hasMore: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
69
74
|
export interface LimitBucket {
|
|
70
75
|
utilization: number;
|
|
71
76
|
resetsAt: string;
|