@hienlh/ppm 0.8.89 → 0.8.91
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 +20 -0
- package/dist/web/assets/{browser-tab-DSWumOSG.js → browser-tab-Bt91e0v_.js} +1 -1
- package/dist/web/assets/chat-tab-BY1ovPns.js +8 -0
- package/dist/web/assets/{code-editor-DLTcPb55.js → code-editor-CAHcH0N-.js} +1 -1
- package/dist/web/assets/{database-viewer-BrpPlYG7.js → database-viewer-DzEoA-r6.js} +1 -1
- package/dist/web/assets/{diff-viewer-Dx96kcTu.js → diff-viewer-Co7JUnvw.js} +1 -1
- package/dist/web/assets/{git-graph-CoN6voTp.js → git-graph-B139k04F.js} +1 -1
- package/dist/web/assets/{index-DRdx_Wqn.js → index-CXJneRo7.js} +10 -10
- package/dist/web/assets/index-CqhIj4Ko.css +2 -0
- package/dist/web/assets/keybindings-store-C8WA_lZu.js +1 -0
- package/dist/web/assets/{markdown-renderer-BqsXIW9n.js → markdown-renderer-C6phS0NU.js} +1 -1
- package/dist/web/assets/{postgres-viewer-Lw8xaGfc.js → postgres-viewer-Bb1N6-J2.js} +1 -1
- package/dist/web/assets/{settings-tab-DDCC58we.js → settings-tab-CZp_PyJ9.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-DECA802J.js → sqlite-viewer-Bzgj_M05.js} +1 -1
- package/dist/web/assets/{terminal-tab-DneNM6WP.js → terminal-tab-Bi4qWzTP.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-CrtYAJMR.js → use-monaco-theme-D7s2hmIL.js} +1 -1
- package/dist/web/index.html +2 -2
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
- package/src/providers/claude-agent-sdk.ts +59 -1
- package/src/providers/cli-provider-base.ts +6 -0
- package/src/server/routes/chat.ts +31 -10
- package/src/server/routes/settings.ts +27 -0
- package/src/server/ws/chat.ts +7 -1
- package/src/services/cloud-ws.service.ts +1 -0
- package/src/services/cloud.service.ts +1 -0
- package/src/services/db.service.ts +8 -0
- package/src/services/supervisor.ts +22 -2
- package/src/types/api.ts +1 -0
- package/src/types/chat.ts +2 -0
- package/src/web/app.tsx +3 -2
- package/src/web/components/chat/chat-history-bar.tsx +21 -7
- package/src/web/components/chat/chat-tab.tsx +4 -1
- package/src/web/components/chat/message-list.tsx +2 -2
- package/src/web/components/chat/session-picker.tsx +1 -0
- package/src/web/components/layout/upgrade-banner.tsx +15 -5
- package/src/web/components/settings/change-password-section.tsx +128 -0
- package/src/web/components/settings/settings-tab.tsx +4 -0
- package/src/web/hooks/use-chat.ts +17 -0
- package/test-session-ops.mjs +444 -0
- package/dist/web/assets/chat-tab-Ccwf-c6M.js +0 -8
- package/dist/web/assets/index-CtbNK_ih.css +0 -2
- package/dist/web/assets/keybindings-store-DHGoLYnP.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":"8d3ea9bac3eb342673fb75bf8da6c0be","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","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-CrtYAJMR.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-DneNM6WP.js"},{"revision":null,"url":"assets/terminal-tab-BrP-ENHg.css"},{"revision":null,"url":"assets/tag-Q2dZiSPX.js"},{"revision":null,"url":"assets/table-CQVQM2SB.js"},{"revision":null,"url":"assets/tab-store--SlERlDs.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/sqlite-viewer-DECA802J.js"},{"revision":null,"url":"assets/settings-tab-DDCC58we.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-Lw8xaGfc.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-BqsXIW9n.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-BeaDXEkP.js"},{"revision":null,"url":"assets/keybindings-store-DHGoLYnP.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-BRW_vwa9.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-DRdx_Wqn.js"},{"revision":null,"url":"assets/index-CtbNK_ih.css"},{"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-CoN6voTp.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DGDPTxs1.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/dist-CALwEtco.js"},{"revision":null,"url":"assets/diff-viewer-Dx96kcTu.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-BrpPlYG7.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-DUbHtTAS.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-DbesTfa7.js"},{"revision":null,"url":"assets/code-editor-DLTcPb55.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-CHnjJt4E.js"},{"revision":null,"url":"assets/chat-tab-Ccwf-c6M.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab-DSWumOSG.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up--LjUXLEt.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":"583c98c2d25ebf1f3adaebbf961a8347","url":"index.html"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-512.svg"},{"revision":"a0fb34fc84eb148d51812cd62669f20d","url":"icon-192.svg"},{"revision":"eb9818b9094675c0c5d303168f273345","url":"monacoeditorwork/ts.worker.bundle.js"},{"revision":"9af0be92dcefdc1f1290441cb5ff5d9b","url":"monacoeditorwork/json.worker.bundle.js"},{"revision":"a261b429c39dbb75ae97972d7d005e6d","url":"monacoeditorwork/html.worker.bundle.js"},{"revision":"79953d804e1bbacecfd79b85fd679016","url":"monacoeditorwork/editor.worker.bundle.js"},{"revision":"fdcba0d09aac31df7a0bc652f6e739bd","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-D7s2hmIL.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-BrP-ENHg.css"},{"revision":null,"url":"assets/terminal-tab-Bi4qWzTP.js"},{"revision":null,"url":"assets/tag-Q2dZiSPX.js"},{"revision":null,"url":"assets/table-CQVQM2SB.js"},{"revision":null,"url":"assets/tab-store--SlERlDs.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/sqlite-viewer-Bzgj_M05.js"},{"revision":null,"url":"assets/settings-tab-CZp_PyJ9.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-Bb1N6-J2.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-C6phS0NU.js"},{"revision":null,"url":"assets/linear-Bcjv9FQt.js"},{"revision":null,"url":"assets/line-B75-Rx70.js"},{"revision":null,"url":"assets/lib-BeaDXEkP.js"},{"revision":null,"url":"assets/keybindings-store-C8WA_lZu.js"},{"revision":null,"url":"assets/katex-DzXRfQ_m.js"},{"revision":null,"url":"assets/kanban-definition-K7BYSVSG-h4g10UHL.js"},{"revision":null,"url":"assets/jsx-runtime-BRW_vwa9.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-CqhIj4Ko.css"},{"revision":null,"url":"assets/index-CXJneRo7.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-B139k04F.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DGDPTxs1.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/dist-CALwEtco.js"},{"revision":null,"url":"assets/diff-viewer-Co7JUnvw.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-DzEoA-r6.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-DUbHtTAS.js"},{"revision":null,"url":"assets/cose-bilkent-S5V4N54A-qudEiMCT.js"},{"revision":null,"url":"assets/columns-2-DbesTfa7.js"},{"revision":null,"url":"assets/code-editor-CAHcH0N-.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-CHnjJt4E.js"},{"revision":null,"url":"assets/chat-tab-BY1ovPns.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab-Bt91e0v_.js"},{"revision":null,"url":"assets/blockDiagram-WCTKOSBZ-h3cDF2vI.js"},{"revision":null,"url":"assets/arrow-up--LjUXLEt.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||`/`)}))});
|
package/package.json
CHANGED
|
@@ -19,9 +19,11 @@ import { getSessionMapping, setSessionMapping, getSessionTitles } from "../servi
|
|
|
19
19
|
import { accountSelector } from "../services/account-selector.service.ts";
|
|
20
20
|
import { accountService } from "../services/account.service.ts";
|
|
21
21
|
import { resolve } from "node:path";
|
|
22
|
-
import { existsSync } from "node:fs";
|
|
22
|
+
import { existsSync, readdirSync, unlinkSync } from "node:fs";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
24
|
|
|
25
|
+
const CLAUDE_PROJECTS_DIR = resolve(homedir(), ".claude/projects");
|
|
26
|
+
|
|
25
27
|
function getSdkSessionId(ppmId: string): string {
|
|
26
28
|
return getSessionMapping(ppmId) ?? ppmId;
|
|
27
29
|
}
|
|
@@ -317,6 +319,21 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
317
319
|
this.closeStreamingSession(sessionId);
|
|
318
320
|
this.activeSessions.delete(sessionId);
|
|
319
321
|
this.messageCount.delete(sessionId);
|
|
322
|
+
this.pendingApprovals.delete(sessionId);
|
|
323
|
+
this.forkSources.delete(sessionId);
|
|
324
|
+
|
|
325
|
+
// Best-effort: delete JSONL from ~/.claude/projects/
|
|
326
|
+
const sdkId = getSessionMapping(sessionId) ?? sessionId;
|
|
327
|
+
try {
|
|
328
|
+
if (existsSync(CLAUDE_PROJECTS_DIR)) {
|
|
329
|
+
const projectDirs = readdirSync(CLAUDE_PROJECTS_DIR);
|
|
330
|
+
for (const dir of projectDirs) {
|
|
331
|
+
if (dir.includes("..") || dir.includes("/")) continue; // safety
|
|
332
|
+
const jsonlPath = resolve(CLAUDE_PROJECTS_DIR, dir, `${sdkId}.jsonl`);
|
|
333
|
+
if (existsSync(jsonlPath)) { unlinkSync(jsonlPath); break; }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch { /* best-effort */ }
|
|
320
337
|
}
|
|
321
338
|
|
|
322
339
|
/**
|
|
@@ -335,6 +352,29 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
335
352
|
this.forkSources.set(sessionId, sourceSessionId);
|
|
336
353
|
}
|
|
337
354
|
|
|
355
|
+
/** Fork a session at a specific message using SDK forkSession() */
|
|
356
|
+
async forkAtMessage(
|
|
357
|
+
sessionId: string,
|
|
358
|
+
messageId: string,
|
|
359
|
+
opts?: { title?: string; dir?: string },
|
|
360
|
+
): Promise<{ sessionId: string }> {
|
|
361
|
+
const sdkId = getSessionMapping(sessionId) ?? sessionId;
|
|
362
|
+
// Dynamic import: Bun's ESM linker fails to resolve forkSession as a static named export
|
|
363
|
+
// in certain test configurations. Lazy import avoids the module linking issue.
|
|
364
|
+
const { forkSession } = await import("@anthropic-ai/claude-agent-sdk");
|
|
365
|
+
const result = await forkSession(sdkId, {
|
|
366
|
+
upToMessageId: messageId,
|
|
367
|
+
title: opts?.title,
|
|
368
|
+
dir: opts?.dir,
|
|
369
|
+
});
|
|
370
|
+
return { sessionId: result.sessionId };
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/** Mark session as resumed so next sendMessage uses resume path */
|
|
374
|
+
markAsResumed(sessionId: string): void {
|
|
375
|
+
this.messageCount.set(sessionId, 1);
|
|
376
|
+
}
|
|
377
|
+
|
|
338
378
|
async listModels(): Promise<ModelOption[]> {
|
|
339
379
|
return [
|
|
340
380
|
{ value: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
|
@@ -698,6 +738,24 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
698
738
|
}
|
|
699
739
|
}
|
|
700
740
|
|
|
741
|
+
// Detect compacting status
|
|
742
|
+
if (subtype === "status") {
|
|
743
|
+
const status = (msg as any).status;
|
|
744
|
+
if (status === "compacting") {
|
|
745
|
+
console.log(`[sdk] session=${sessionId} COMPACTING`);
|
|
746
|
+
yield { type: "system" as const, subtype: "compacting" } as ChatEvent;
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
// Detect compact boundary (compact finished, messages replaced in JSONL)
|
|
752
|
+
if (subtype === "compact_boundary") {
|
|
753
|
+
const meta = (msg as any).compact_metadata;
|
|
754
|
+
console.log(`[sdk] session=${sessionId} COMPACT_BOUNDARY trigger=${meta?.trigger} pre_tokens=${meta?.pre_tokens}`);
|
|
755
|
+
yield { type: "system" as const, subtype: "compact_done" } as ChatEvent;
|
|
756
|
+
continue;
|
|
757
|
+
}
|
|
758
|
+
|
|
701
759
|
// Yield system events so streaming loop can transition phases
|
|
702
760
|
// (e.g. connecting → thinking when hooks/init arrive)
|
|
703
761
|
yield { type: "system" as any, subtype } as any;
|
|
@@ -84,7 +84,13 @@ export abstract class CliProvider implements AIProvider {
|
|
|
84
84
|
}));
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
markAsResumed(sessionId: string): void {
|
|
88
|
+
this.messageCount.set(sessionId, 1);
|
|
89
|
+
}
|
|
90
|
+
|
|
87
91
|
async deleteSession(sessionId: string): Promise<void> {
|
|
92
|
+
const proc = this.activeProcesses.get(sessionId);
|
|
93
|
+
if (proc) { proc.kill(); this.activeProcesses.delete(sessionId); }
|
|
88
94
|
this.sessions.delete(sessionId);
|
|
89
95
|
this.messageCount.delete(sessionId);
|
|
90
96
|
}
|
|
@@ -8,7 +8,7 @@ import { renameSession as sdkRenameSession } from "@anthropic-ai/claude-agent-sd
|
|
|
8
8
|
import { listSlashItems } from "../../services/slash-items.service.ts";
|
|
9
9
|
import { getCachedUsage, refreshUsageNow } from "../../services/claude-usage.service.ts";
|
|
10
10
|
import { getSessionLog } from "../../services/session-log.service.ts";
|
|
11
|
-
import { getSessionMapping, setSessionTitle, getPinnedSessionIds, pinSession, unpinSession } from "../../services/db.service.ts";
|
|
11
|
+
import { getSessionMapping, setSessionMapping, setSessionTitle, getPinnedSessionIds, pinSession, unpinSession, deleteSessionMapping, deleteSessionTitle } from "../../services/db.service.ts";
|
|
12
12
|
import { ok, err } from "../../types/api.ts";
|
|
13
13
|
|
|
14
14
|
type Env = { Variables: { projectPath: string; projectName: string } };
|
|
@@ -125,7 +125,13 @@ chatRoutes.delete("/sessions/:id", async (c) => {
|
|
|
125
125
|
try {
|
|
126
126
|
const id = c.req.param("id");
|
|
127
127
|
const providerId = c.req.query("providerId") ?? "claude";
|
|
128
|
+
const sdkId = getSessionMapping(id) ?? id;
|
|
129
|
+
// Provider-specific cleanup (JSONL, process, etc.)
|
|
128
130
|
await chatService.deleteSession(providerId, id);
|
|
131
|
+
// Shared DB cleanup
|
|
132
|
+
deleteSessionMapping(id);
|
|
133
|
+
deleteSessionTitle(sdkId);
|
|
134
|
+
unpinSession(sdkId);
|
|
129
135
|
return c.json(ok({ deleted: id }));
|
|
130
136
|
} catch (e) {
|
|
131
137
|
return c.json(err((e as Error).message), 404);
|
|
@@ -184,16 +190,31 @@ chatRoutes.post("/sessions/:id/fork", async (c) => {
|
|
|
184
190
|
const projectName = c.get("projectName");
|
|
185
191
|
const projectPath = c.get("projectPath");
|
|
186
192
|
const providerId = c.req.query("providerId") ?? "claude";
|
|
187
|
-
|
|
188
|
-
const session = await chatService.createSession(providerId, {
|
|
189
|
-
projectName,
|
|
190
|
-
projectPath,
|
|
191
|
-
title: "Forked Chat",
|
|
192
|
-
});
|
|
193
|
-
// Store fork source so WS handler knows to use forkSession on first message
|
|
193
|
+
const body = await c.req.json<{ messageId?: string }>().catch(() => ({} as { messageId?: string }));
|
|
194
194
|
const provider = providerRegistry.get(providerId);
|
|
195
|
-
provider
|
|
196
|
-
|
|
195
|
+
if (!provider) return c.json(err("Provider not found"), 404);
|
|
196
|
+
|
|
197
|
+
if (body.messageId && provider.forkAtMessage) {
|
|
198
|
+
// Mid-fork: SDK fork first, then create PPM session only on success
|
|
199
|
+
const result = await provider.forkAtMessage(sourceId, body.messageId, {
|
|
200
|
+
title: "Forked Chat", dir: projectPath,
|
|
201
|
+
});
|
|
202
|
+
const session = await chatService.createSession(providerId, {
|
|
203
|
+
projectName, projectPath, title: "Forked Chat",
|
|
204
|
+
});
|
|
205
|
+
setSessionMapping(session.id, result.sessionId);
|
|
206
|
+
provider.markAsResumed?.(session.id);
|
|
207
|
+
return c.json(ok({ ...session, forkedFrom: sourceId }), 201);
|
|
208
|
+
} else if (provider.setForkSource) {
|
|
209
|
+
// Deferred fork from end (full history copy on first msg)
|
|
210
|
+
const session = await chatService.createSession(providerId, {
|
|
211
|
+
projectName, projectPath, title: "Forked Chat",
|
|
212
|
+
});
|
|
213
|
+
provider.setForkSource(session.id, sourceId);
|
|
214
|
+
return c.json(ok({ ...session, forkedFrom: sourceId }), 201);
|
|
215
|
+
} else {
|
|
216
|
+
return c.json(err("Provider does not support forking"), 400);
|
|
217
|
+
}
|
|
197
218
|
} catch (e) {
|
|
198
219
|
return c.json(err((e as Error).message), 500);
|
|
199
220
|
}
|
|
@@ -252,6 +252,33 @@ settingsRoutes.post("/telegram/test", async (c) => {
|
|
|
252
252
|
}
|
|
253
253
|
});
|
|
254
254
|
|
|
255
|
+
// ── Auth / Password ──────────────────────────────────────────────────
|
|
256
|
+
|
|
257
|
+
/** PUT /settings/auth/password — change the access password (token) */
|
|
258
|
+
settingsRoutes.put("/auth/password", async (c) => {
|
|
259
|
+
try {
|
|
260
|
+
const { password, confirm } = await c.req.json<{ password: string; confirm: string }>();
|
|
261
|
+
if (typeof password !== "string" || !password.trim()) {
|
|
262
|
+
return c.json(err("Password is required"), 400);
|
|
263
|
+
}
|
|
264
|
+
if (password !== confirm) {
|
|
265
|
+
return c.json(err("Passwords do not match"), 400);
|
|
266
|
+
}
|
|
267
|
+
const trimmed = password.trim();
|
|
268
|
+
if (trimmed.length < 4) {
|
|
269
|
+
return c.json(err("Password must be at least 4 characters"), 400);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const auth = configService.get("auth");
|
|
273
|
+
configService.set("auth", { ...auth, token: trimmed });
|
|
274
|
+
configService.save();
|
|
275
|
+
|
|
276
|
+
return c.json(ok({ token: trimmed }));
|
|
277
|
+
} catch (e) {
|
|
278
|
+
return c.json(err((e as Error).message), 400);
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
255
282
|
// ── Proxy ────────────────────────────────────────────────────────────
|
|
256
283
|
|
|
257
284
|
/** GET /settings/proxy — proxy status */
|
package/src/server/ws/chat.ts
CHANGED
|
@@ -218,8 +218,14 @@ async function startSessionConsumer(sessionId: string, providerId: string, conte
|
|
|
218
218
|
continue;
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
// System events → transition connecting → thinking
|
|
221
|
+
// System events → transition connecting → thinking, forward compact events
|
|
222
222
|
if (evType === "system") {
|
|
223
|
+
const sub = (ev as any).subtype;
|
|
224
|
+
if (sub === "compacting") {
|
|
225
|
+
broadcast(sessionId, { type: "compact_status", status: "compacting" });
|
|
226
|
+
} else if (sub === "compact_done") {
|
|
227
|
+
broadcast(sessionId, { type: "compact_status", status: "done" });
|
|
228
|
+
}
|
|
223
229
|
if (!firstEventReceived) {
|
|
224
230
|
if (heartbeat) clearInterval(heartbeat);
|
|
225
231
|
setPhase(sessionId, "thinking");
|
|
@@ -424,6 +424,14 @@ export function getPinnedSessionIds(): Set<string> {
|
|
|
424
424
|
return new Set(rows.map((r) => r.session_id));
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
export function deleteSessionMapping(ppmId: string): void {
|
|
428
|
+
getDb().query("DELETE FROM session_map WHERE ppm_id = ?").run(ppmId);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function deleteSessionTitle(sessionId: string): void {
|
|
432
|
+
getDb().query("DELETE FROM session_titles WHERE session_id = ?").run(sessionId);
|
|
433
|
+
}
|
|
434
|
+
|
|
427
435
|
// ---------------------------------------------------------------------------
|
|
428
436
|
// Push subscription helpers
|
|
429
437
|
// ---------------------------------------------------------------------------
|
|
@@ -378,17 +378,33 @@ function adoptTunnel(): boolean {
|
|
|
378
378
|
const status = readStatus();
|
|
379
379
|
const pid = status.tunnelPid as number;
|
|
380
380
|
const url = status.shareUrl as string;
|
|
381
|
-
if (!pid || !url)
|
|
381
|
+
if (!pid || !url) {
|
|
382
|
+
log("DEBUG", `adoptTunnel: missing tunnelPid(${pid}) or shareUrl(${url}) in status`);
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
382
385
|
process.kill(pid, 0); // throws if process is dead
|
|
383
386
|
adoptedTunnelPid = pid;
|
|
384
387
|
tunnelUrl = url;
|
|
385
388
|
log("INFO", `Adopted existing tunnel (PID: ${pid}, URL: ${url})`);
|
|
386
389
|
return true;
|
|
387
|
-
} catch {
|
|
390
|
+
} catch (e) {
|
|
391
|
+
log("WARN", `adoptTunnel: tunnel PID ${(readStatus().tunnelPid)} unreachable: ${e}`);
|
|
388
392
|
return false;
|
|
389
393
|
}
|
|
390
394
|
}
|
|
391
395
|
|
|
396
|
+
/** Kill stale tunnel PID from status.json (cleanup after failed adoption) */
|
|
397
|
+
function killStaleTunnel() {
|
|
398
|
+
try {
|
|
399
|
+
const status = readStatus();
|
|
400
|
+
const pid = status.tunnelPid as number;
|
|
401
|
+
if (!pid) return;
|
|
402
|
+
try { process.kill(pid, "SIGTERM"); } catch {}
|
|
403
|
+
log("INFO", `Killed stale tunnel (PID: ${pid})`);
|
|
404
|
+
} catch {}
|
|
405
|
+
updateStatus({ tunnelPid: null, shareUrl: null });
|
|
406
|
+
}
|
|
407
|
+
|
|
392
408
|
/** Spawn new supervisor from updated code, wait for it to be healthy, then exit */
|
|
393
409
|
async function selfReplace(): Promise<{ success: boolean; error?: string }> {
|
|
394
410
|
log("INFO", "Starting self-replace for upgrade");
|
|
@@ -490,6 +506,8 @@ async function connectCloud(opts: { port: number }, serverArgs: string[], logFd:
|
|
|
490
506
|
secretKey: device.secret_key,
|
|
491
507
|
heartbeatFn: () => {
|
|
492
508
|
const status = readStatus();
|
|
509
|
+
// Re-read device file each heartbeat to pick up name changes
|
|
510
|
+
const currentDevice = getCloudDevice();
|
|
493
511
|
return {
|
|
494
512
|
type: "heartbeat" as const,
|
|
495
513
|
tunnelUrl,
|
|
@@ -499,6 +517,7 @@ async function connectCloud(opts: { port: number }, serverArgs: string[], logFd:
|
|
|
499
517
|
availableVersion: (status.availableVersion as string) || null,
|
|
500
518
|
serverPid: serverChild?.pid ?? null,
|
|
501
519
|
uptime: Math.floor((Date.now() - startTime) / 1000),
|
|
520
|
+
deviceName: currentDevice?.name ?? device.name,
|
|
502
521
|
timestamp: new Date().toISOString(),
|
|
503
522
|
};
|
|
504
523
|
},
|
|
@@ -706,6 +725,7 @@ export async function runSupervisor(opts: {
|
|
|
706
725
|
startTunnelProbe(opts.port);
|
|
707
726
|
// Try adopting tunnel kept alive from previous upgrade; spawn new if dead
|
|
708
727
|
if (!adoptTunnel()) {
|
|
728
|
+
killStaleTunnel(); // kill orphaned tunnel before spawning new one
|
|
709
729
|
promises.push(spawnTunnel(opts.port));
|
|
710
730
|
}
|
|
711
731
|
}
|
package/src/types/api.ts
CHANGED
|
@@ -44,4 +44,5 @@ export type ChatWsServerMessage =
|
|
|
44
44
|
| { type: "session_state"; sessionId: string; phase: SessionPhase; pendingApproval: { requestId: string; tool: string; input: unknown } | null; sessionTitle: string | null }
|
|
45
45
|
| { type: "turn_events"; events: unknown[] }
|
|
46
46
|
| { type: "title_updated"; title: string }
|
|
47
|
+
| { type: "compact_status"; status: "compacting" | "done" }
|
|
47
48
|
| { type: "ping" };
|
package/src/types/chat.ts
CHANGED
|
@@ -29,6 +29,8 @@ export interface AIProvider {
|
|
|
29
29
|
listSessionsByDir?(dir: string): Promise<SessionInfo[]>;
|
|
30
30
|
ensureProjectPath?(sessionId: string, path: string): void;
|
|
31
31
|
setForkSource?(sessionId: string, sourceSessionId: string): void;
|
|
32
|
+
forkAtMessage?(sessionId: string, messageId: string, opts?: { title?: string; dir?: string }): Promise<{ sessionId: string }>;
|
|
33
|
+
markAsResumed?(sessionId: string): void;
|
|
32
34
|
isAvailable?(): Promise<boolean>;
|
|
33
35
|
listModels?(): Promise<ModelOption[]>;
|
|
34
36
|
}
|
package/src/web/app.tsx
CHANGED
|
@@ -37,6 +37,7 @@ type AuthState = "checking" | "authenticated" | "unauthenticated";
|
|
|
37
37
|
|
|
38
38
|
export function App() {
|
|
39
39
|
const [authState, setAuthState] = useState<AuthState>("checking");
|
|
40
|
+
const [upgradeBannerVisible, setUpgradeBannerVisible] = useState(false);
|
|
40
41
|
const [drawerOpen, setDrawerOpen] = useState(false);
|
|
41
42
|
const [drawerTab, setDrawerTab] = useState<"explorer" | "git" | "settings" | undefined>();
|
|
42
43
|
const [projectSheetOpen, setProjectSheetOpen] = useState(false);
|
|
@@ -229,11 +230,11 @@ export function App() {
|
|
|
229
230
|
<TooltipProvider>
|
|
230
231
|
<div className="h-dvh flex flex-col bg-background text-foreground overflow-hidden relative">
|
|
231
232
|
{/* Upgrade banner — shown when new version available */}
|
|
232
|
-
<UpgradeBanner />
|
|
233
|
+
<UpgradeBanner onVisibilityChange={setUpgradeBannerVisible} />
|
|
233
234
|
|
|
234
235
|
{/* Mobile device name badge — floating top-left */}
|
|
235
236
|
{deviceName && (
|
|
236
|
-
<div className="md:hidden fixed
|
|
237
|
+
<div className={cn("md:hidden fixed left-0 z-50 px-2 py-0.5 bg-primary/80 text-primary-foreground text-[10px] font-medium rounded-br transition-[top]", upgradeBannerVisible ? "top-7" : "top-0")}>
|
|
237
238
|
{deviceName}
|
|
238
239
|
</div>
|
|
239
240
|
)}
|
|
@@ -16,6 +16,7 @@ interface ChatHistoryBarProps {
|
|
|
16
16
|
projectName: string;
|
|
17
17
|
usageInfo: UsageInfo;
|
|
18
18
|
contextWindowPct?: number | null;
|
|
19
|
+
compactStatus?: "compacting" | null;
|
|
19
20
|
usageLoading?: boolean;
|
|
20
21
|
refreshUsage?: () => void;
|
|
21
22
|
lastFetchedAt?: string | null;
|
|
@@ -79,7 +80,7 @@ function DebugCopyButton({ sessionId, projectName }: { sessionId: string; projec
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
export function ChatHistoryBar({
|
|
82
|
-
projectName, usageInfo, contextWindowPct, usageLoading, refreshUsage, lastFetchedAt,
|
|
83
|
+
projectName, usageInfo, contextWindowPct, compactStatus, usageLoading, refreshUsage, lastFetchedAt,
|
|
83
84
|
sessionId, providerId, onSelectSession, onBugReport, isConnected, onReconnect,
|
|
84
85
|
}: ChatHistoryBarProps) {
|
|
85
86
|
const [activePanel, setActivePanel] = useState<PanelType>(null);
|
|
@@ -240,14 +241,27 @@ export function ChatHistoryBar({
|
|
|
240
241
|
<span className={pctColor(contextWindowPct)}>Ctx:{contextWindowPct}%</span>
|
|
241
242
|
</>
|
|
242
243
|
)}
|
|
244
|
+
{compactStatus === "compacting" && (
|
|
245
|
+
<>
|
|
246
|
+
<span className="text-text-subtle">·</span>
|
|
247
|
+
<span className="text-blue-400 animate-pulse">compacting...</span>
|
|
248
|
+
</>
|
|
249
|
+
)}
|
|
243
250
|
</button>
|
|
244
251
|
) : (
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
<
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
252
|
+
<>
|
|
253
|
+
{contextWindowPct != null && (
|
|
254
|
+
<span className={`flex items-center gap-1 px-1.5 py-0.5 text-[11px] font-medium tabular-nums ${pctColor(contextWindowPct)}`}>
|
|
255
|
+
<Activity className="size-3" />
|
|
256
|
+
<span>Ctx:{contextWindowPct}%</span>
|
|
257
|
+
</span>
|
|
258
|
+
)}
|
|
259
|
+
{compactStatus === "compacting" && (
|
|
260
|
+
<span className="text-[11px] px-1.5 py-0.5 text-blue-400 animate-pulse">
|
|
261
|
+
compacting...
|
|
262
|
+
</span>
|
|
263
|
+
)}
|
|
264
|
+
</>
|
|
251
265
|
)}
|
|
252
266
|
|
|
253
267
|
{/* Spacer */}
|
|
@@ -89,6 +89,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
89
89
|
connectingElapsed,
|
|
90
90
|
pendingApproval,
|
|
91
91
|
contextWindowPct,
|
|
92
|
+
compactStatus,
|
|
92
93
|
sessionTitle,
|
|
93
94
|
migratedSessionId,
|
|
94
95
|
sendMessage,
|
|
@@ -162,12 +163,13 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
162
163
|
}, [tabId, updateTab]);
|
|
163
164
|
|
|
164
165
|
/** Fork current session and open new tab with the forked session, resending userMessage */
|
|
165
|
-
const handleFork = useCallback(async (userMessage: string) => {
|
|
166
|
+
const handleFork = useCallback(async (userMessage: string, messageId?: string) => {
|
|
166
167
|
if (!sessionId || !projectName) return;
|
|
167
168
|
try {
|
|
168
169
|
const { api, projectUrl } = await import("@/lib/api-client");
|
|
169
170
|
const forked = await api.post<{ id: string; forkedFrom: string }>(
|
|
170
171
|
`${projectUrl(projectName)}/chat/sessions/${sessionId}/fork?providerId=${providerId}`,
|
|
172
|
+
{ messageId },
|
|
171
173
|
);
|
|
172
174
|
// Open new chat tab with forked session — it will send userMessage on connect
|
|
173
175
|
useTabStore.getState().openTab({
|
|
@@ -350,6 +352,7 @@ export function ChatTab({ metadata, tabId }: ChatTabProps) {
|
|
|
350
352
|
projectName={projectName}
|
|
351
353
|
usageInfo={usageInfo}
|
|
352
354
|
contextWindowPct={contextWindowPct}
|
|
355
|
+
compactStatus={compactStatus}
|
|
353
356
|
usageLoading={usageLoading}
|
|
354
357
|
refreshUsage={refreshUsage}
|
|
355
358
|
lastFetchedAt={lastFetchedAt}
|
|
@@ -43,7 +43,7 @@ interface MessageListProps {
|
|
|
43
43
|
connectingElapsed?: number;
|
|
44
44
|
projectName?: string;
|
|
45
45
|
/** Called when user clicks Fork/Rewind — opens new forked chat tab */
|
|
46
|
-
onFork?: (userMessage: string) => void;
|
|
46
|
+
onFork?: (userMessage: string, messageId?: string) => void;
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export function MessageList({
|
|
@@ -96,7 +96,7 @@ export function MessageList({
|
|
|
96
96
|
message={msg}
|
|
97
97
|
isStreaming={isStreaming && msg.id.startsWith("streaming-")}
|
|
98
98
|
projectName={projectName}
|
|
99
|
-
onFork={msg.role === "user" && onFork ? () => onFork(msg.content) : undefined}
|
|
99
|
+
onFork={msg.role === "user" && onFork ? () => onFork(msg.content, msg.id) : undefined}
|
|
100
100
|
/>
|
|
101
101
|
))}
|
|
102
102
|
|
|
@@ -47,6 +47,7 @@ export function SessionPicker({
|
|
|
47
47
|
|
|
48
48
|
const handleDelete = async (e: React.MouseEvent, session: SessionInfo) => {
|
|
49
49
|
e.stopPropagation();
|
|
50
|
+
if (!window.confirm("Delete this session? This cannot be undone.")) return;
|
|
50
51
|
try {
|
|
51
52
|
if (!projectName) return;
|
|
52
53
|
await api.del(
|