@hienlh/ppm 0.9.39 → 0.9.41
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 +3 -50
- package/dist/web/assets/browser-tab--V6I70pH.js +1 -0
- package/dist/web/assets/chat-tab-CrkhvVjF.js +10 -0
- package/dist/web/assets/code-editor-BfMyExLp.js +2 -0
- package/dist/web/assets/{database-viewer-TjRo2b8_.js → database-viewer-CeRUrZKj.js} +1 -1
- package/dist/web/assets/{diff-viewer-BMhCz0xk.js → diff-viewer-D2p3WTMS.js} +1 -1
- package/dist/web/assets/{extension-webview-DiVdlE2r.js → extension-webview-DQWAHMlR.js} +1 -1
- package/dist/web/assets/git-graph-BWRMlCdK.js +1 -0
- package/dist/web/assets/index-C7esr4gM.css +2 -0
- package/dist/web/assets/index-DU6UVgQY.js +30 -0
- package/dist/web/assets/keybindings-store-BE2T8jM9.js +1 -0
- package/dist/web/assets/{markdown-renderer-IyEzLrC6.js → markdown-renderer-C7lKs47M.js} +4 -4
- package/dist/web/assets/{postgres-viewer-CSynGGkJ.js → postgres-viewer-Cr9jpBNd.js} +1 -1
- package/dist/web/assets/{settings-tab-BdI4HhRa.js → settings-tab-DKy-YDg2.js} +1 -1
- package/dist/web/assets/{sqlite-viewer-C5mviyU5.js → sqlite-viewer-9AmeF-Zs.js} +1 -1
- package/dist/web/assets/square-oPKIkJiw.js +1 -0
- package/dist/web/assets/{terminal-tab-CDyC1grg.js → terminal-tab-DFhB4Rxh.js} +1 -1
- package/dist/web/assets/{use-monaco-theme-DcVicB_i.js → use-monaco-theme-B7XLw-OX.js} +1 -1
- package/dist/web/index.html +2 -3
- package/dist/web/sw.js +1 -1
- package/docs/codebase-summary.md +3 -33
- package/docs/project-changelog.md +0 -47
- package/docs/project-roadmap.md +7 -14
- package/docs/system-architecture.md +2 -65
- package/package.json +1 -1
- package/src/server/index.ts +0 -7
- package/src/server/routes/settings.ts +1 -72
- package/src/services/config.service.ts +1 -1
- package/src/services/db.service.ts +1 -279
- package/src/services/git.service.ts +2 -2
- package/src/types/config.ts +0 -26
- package/src/web/components/browser/browser-tab.tsx +128 -97
- package/src/web/components/chat/chat-history-bar.tsx +3 -8
- package/src/web/components/layout/command-palette.tsx +1 -1
- package/src/web/components/settings/settings-tab.tsx +1 -4
- package/src/web/hooks/use-url-sync.ts +1 -1
- package/dist/web/assets/browser-tab-DnIsHiCc.js +0 -1
- package/dist/web/assets/chat-tab-il6D4jql.js +0 -10
- package/dist/web/assets/code-editor-BUc1jBqm.js +0 -2
- package/dist/web/assets/git-graph-4eGJ8B1A.js +0 -1
- package/dist/web/assets/index-BmcV1di6.js +0 -30
- package/dist/web/assets/index-CcFDEPCo.css +0 -2
- package/dist/web/assets/keybindings-store--5T5hsAj.js +0 -1
- package/dist/web/assets/tab-store-BXMIUvsE.js +0 -1
- package/docs/streaming-input-guide.md +0 -267
- package/snapshot-state.md +0 -1526
- package/src/services/ppmbot/ppmbot-formatter.ts +0 -88
- package/src/services/ppmbot/ppmbot-memory.ts +0 -333
- package/src/services/ppmbot/ppmbot-service.ts +0 -545
- package/src/services/ppmbot/ppmbot-session.ts +0 -199
- package/src/services/ppmbot/ppmbot-streamer.ts +0 -288
- package/src/services/ppmbot/ppmbot-telegram.ts +0 -279
- package/src/types/ppmbot.ts +0 -103
- package/src/web/components/settings/ppmbot-settings-section.tsx +0 -270
- package/test-session-ops.mjs +0 -444
- package/test-tokens.mjs +0 -212
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":"9f50533cb3ab66547532f7b18f4f74c1","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-DcVicB_i.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-CDyC1grg.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/tab-store-BXMIUvsE.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-C5mviyU5.js"},{"revision":null,"url":"assets/settings-tab-BdI4HhRa.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-CSynGGkJ.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-IyEzLrC6.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--5T5hsAj.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-CcFDEPCo.css"},{"revision":null,"url":"assets/index-BmcV1di6.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-4eGJ8B1A.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-DiVdlE2r.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-DKlZwvf8.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/diff-viewer-BMhCz0xk.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-TjRo2b8_.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--ZSEumXf.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-BUc1jBqm.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-il6D4jql.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab-DnIsHiCc.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":"c3c62c20ca227632c659fcfddaf3386f","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-B7XLw-OX.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-DFhB4Rxh.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-9AmeF-Zs.js"},{"revision":null,"url":"assets/settings-tab-DKy-YDg2.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-Cr9jpBNd.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-C7lKs47M.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-BE2T8jM9.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-DU6UVgQY.js"},{"revision":null,"url":"assets/index-C7esr4gM.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-BWRMlCdK.js"},{"revision":null,"url":"assets/ganttDiagram-A5KZAMGK-BdjmoMLS.js"},{"revision":null,"url":"assets/flowDiagram-PKNHOUZH-CRxlE9Sr.js"},{"revision":null,"url":"assets/extension-webview-DQWAHMlR.js"},{"revision":null,"url":"assets/erDiagram-INFDFZHY-DLeYhAAT.js"},{"revision":null,"url":"assets/dist-DRTW9IWi.js"},{"revision":null,"url":"assets/dist-DKlZwvf8.js"},{"revision":null,"url":"assets/dist-Cep75xXf.js"},{"revision":null,"url":"assets/diff-viewer-D2p3WTMS.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-CeRUrZKj.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--ZSEumXf.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-BfMyExLp.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-CrkhvVjF.js"},{"revision":null,"url":"assets/channel-C2fMafck.js"},{"revision":null,"url":"assets/c4Diagram-IC4MRINW--pF1r5lr.js"},{"revision":null,"url":"assets/browser-tab--V6I70pH.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||`/`)}))});
|
package/docs/codebase-summary.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# PPM Codebase Summary
|
|
2
2
|
|
|
3
|
-
**Last Updated:** 2026-
|
|
4
|
-
**Version:** 0.
|
|
3
|
+
**Last Updated:** 2026-03-26
|
|
4
|
+
**Version:** 0.8.60
|
|
5
5
|
**Repository:** PPM (Project & Process Manager) — Multi-provider web IDE/project manager with Claude Agent SDK
|
|
6
6
|
|
|
7
7
|
**Core Statistics:**
|
|
@@ -66,13 +66,6 @@ src/
|
|
|
66
66
|
│ ├── extension-rpc.ts # RPC channel (request/response/events)
|
|
67
67
|
│ ├── extension-host-worker.ts # Worker-side extension loading
|
|
68
68
|
│ ├── contribution-registry.ts # Central registry for commands, views, config
|
|
69
|
-
│ ├── clawbot/ # Telegram bot service layer
|
|
70
|
-
│ │ ├── clawbot.service.ts # Main orchestrator (poller lifecycle, routing)
|
|
71
|
-
│ │ ├── clawbot-telegram.ts # Telegram API (long-polling, send, edit, typing)
|
|
72
|
-
│ │ ├── clawbot-session.ts # Session mapping (chatID → PPM sessionID)
|
|
73
|
-
│ │ ├── clawbot-memory.ts # FTS5 memory (save, recall, decay, supersede)
|
|
74
|
-
│ │ ├── clawbot-formatter.ts # Markdown → Telegram HTML, chunking
|
|
75
|
-
│ │ └── clawbot-streamer.ts # ChatEvent → progressive message edits
|
|
76
69
|
│ ├── database/
|
|
77
70
|
│ │ ├── adapter-registry.ts # SQLite/Postgres adapter registry
|
|
78
71
|
│ │ ├── sqlite-adapter.ts
|
|
@@ -276,12 +269,6 @@ src/
|
|
|
276
269
|
- **DatabaseAdapterRegistry** — Register/retrieve DatabaseAdapter implementations (extensible pattern)
|
|
277
270
|
- **SQLiteAdapter** — SQLite connection/query execution with readonly checks
|
|
278
271
|
- **PostgresAdapter** — PostgreSQL connection/query execution with readonly checks
|
|
279
|
-
- **ClawBotService** — Telegram bot orchestrator (startup, shutdown, message routing)
|
|
280
|
-
- **ClawBotTelegramService** — Telegram API (long-polling, send, edit, typing, command handling)
|
|
281
|
-
- **ClawBotSessionService** — chatID → PPM sessionID mapping, session state tracking
|
|
282
|
-
- **ClawBotMemoryService** — FTS5 persistent memory (save, recall, decay, project-aware search)
|
|
283
|
-
- **ClawBotFormatterService** — Markdown → Telegram HTML, message chunking (4096 char limit)
|
|
284
|
-
- **ClawBotStreamerService** — ChatEvent streaming → progressive Telegram message editing
|
|
285
272
|
- **Pattern:** Singleton services, dependency injection via imports, adapter registry for extensibility
|
|
286
273
|
|
|
287
274
|
### Provider Layer (src/providers/)
|
|
@@ -604,24 +591,7 @@ ppm ext dev /path/to/src # Dev symlink
|
|
|
604
591
|
|
|
605
592
|
---
|
|
606
593
|
|
|
607
|
-
## Recent Changes (v0.
|
|
608
|
-
|
|
609
|
-
### v0.9.10 (ClawBot Telegram Integration)
|
|
610
|
-
- **Telegram Bot Service** — Long-polling Telegram bot with message routing
|
|
611
|
-
- Session mapping: chatID → PPM sessionID (per-user thread isolation)
|
|
612
|
-
- Pairing system: Code-based device pairing with owner approval in web UI
|
|
613
|
-
- Message queue: Handle concurrent Telegram messages without race conditions
|
|
614
|
-
- **Memory System** — FTS5 persistent conversation memory
|
|
615
|
-
- Hybrid extraction: AI extraction (primary) + regex fallback
|
|
616
|
-
- Cross-project search: Auto-detect project name mentions → include memories
|
|
617
|
-
- Decay/supersede: Memory relevance based on age + custom decay factors
|
|
618
|
-
- **Response Streaming** — Progressive Telegram message editing
|
|
619
|
-
- ChatEvent streaming with 1s throttle
|
|
620
|
-
- Markdown → Telegram HTML formatting with chunking (4096 char limit)
|
|
621
|
-
- **Settings & History**
|
|
622
|
-
- Settings UI: Enable/disable, paired devices, default project, system prompt, display toggles, debounce config
|
|
623
|
-
- Chat history: [Claw] prefix sessions with robot icon for easy identification
|
|
624
|
-
- **Database Schema v13** — `clawbot_sessions`, `clawbot_memories` (FTS5), `clawbot_paired_chats` tables
|
|
594
|
+
## Recent Changes (v0.8.60+)
|
|
625
595
|
|
|
626
596
|
### v0.9.0 (Extension System Phase 1)
|
|
627
597
|
- **Extension Framework** — VSCode-compatible npm-installable extensions
|
|
@@ -6,53 +6,6 @@ All notable changes to PPM are documented here. Format follows [Keep a Changelog
|
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
## [0.9.10] — 2026-04-06
|
|
10
|
-
|
|
11
|
-
### Added
|
|
12
|
-
- **ClawBot Telegram Integration** — Telegram bot service layer for AI-powered messaging
|
|
13
|
-
- Telegram long-polling: receive messages via polling (no webhooks needed for self-hosted)
|
|
14
|
-
- Session routing: chatID → PPM session mapping with per-user thread isolation
|
|
15
|
-
- Memory system: SQLite FTS5 for persistent conversation history + recall with decay/supersede
|
|
16
|
-
- Response streaming: ChatEvent → progressive Telegram message editing (1s throttle)
|
|
17
|
-
- Message formatting: Markdown → Telegram HTML with 4096-character chunking
|
|
18
|
-
- Pairing system: Code-based device pairing for security (owner approves in web UI)
|
|
19
|
-
- Message queue: Handle concurrent Telegram messages without race conditions
|
|
20
|
-
- Settings UI: Enable/disable, paired devices, default project, system prompt, display toggles, debounce config
|
|
21
|
-
- Chat history: [Claw] prefix sessions with robot icon for easy identification
|
|
22
|
-
- Cross-project memory: Auto-detect project name mentions → include that project's memories in context
|
|
23
|
-
|
|
24
|
-
### Technical Details
|
|
25
|
-
- **Database Migration (v13):**
|
|
26
|
-
- `clawbot_sessions` — chatID, sessionID, pairedAt, lastUsed
|
|
27
|
-
- `clawbot_memories` — sessionID, content, role, created, decay_factor (FTS5)
|
|
28
|
-
- `clawbot_paired_chats` — chatID, pairingCode, approvedAt, approvedBy
|
|
29
|
-
- **Files Created:**
|
|
30
|
-
- `src/services/clawbot/clawbot.service.ts` — Main orchestrator
|
|
31
|
-
- `src/services/clawbot/clawbot-telegram.ts` — Telegram API polling
|
|
32
|
-
- `src/services/clawbot/clawbot-session.ts` — Session mapping
|
|
33
|
-
- `src/services/clawbot/clawbot-memory.ts` — FTS5 memory CRUD + hybrid extraction (AI + regex)
|
|
34
|
-
- `src/services/clawbot/clawbot-formatter.ts` — Markdown → Telegram HTML
|
|
35
|
-
- `src/services/clawbot/clawbot-streamer.ts` — ChatEvent → progressive message edits
|
|
36
|
-
- `src/types/clawbot.ts` — Type definitions
|
|
37
|
-
- `src/web/components/settings/clawbot-settings-section.tsx` — Settings UI
|
|
38
|
-
- **Files Modified:**
|
|
39
|
-
- `src/services/db.service.ts` — Schema v13 migration
|
|
40
|
-
- `src/types/config.ts` — ClawBotConfig interface
|
|
41
|
-
- `src/server/index.ts` — ClawBot poller startup
|
|
42
|
-
- `src/server/routes/settings.ts` — ClawBot settings endpoints (GET/PUT)
|
|
43
|
-
- `src/web/components/settings/settings-tab.tsx` — ClawBot category
|
|
44
|
-
- `src/web/components/chat/chat-history-bar.tsx` — [Claw] prefix + icon tagging
|
|
45
|
-
|
|
46
|
-
### Key Design Principles
|
|
47
|
-
- **Long-polling over webhooks** — Simpler self-hosted setup, no public URL required
|
|
48
|
-
- **bypassPermissions by default** — Headless bot, no manual tool approvals needed
|
|
49
|
-
- **Hybrid memory extraction** — AI extraction (primary) + regex fallback (fallback)
|
|
50
|
-
- **Progressive message editing** — 1s throttle balances UX with Telegram rate limits
|
|
51
|
-
- **Message debouncing** — 2s default, configurable per session
|
|
52
|
-
- **Pairing-based security** — Replace allowlists with owner-approved pairing codes
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
9
|
## [0.9.9] — 2026-04-04
|
|
57
10
|
|
|
58
11
|
### Added
|
package/docs/project-roadmap.md
CHANGED
|
@@ -109,15 +109,14 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
109
109
|
|
|
110
110
|
### v0.11.0 — "Intelligence" (Q3–Q4 2026)
|
|
111
111
|
|
|
112
|
-
**Theme:** Event system + PPM's own AI layer
|
|
112
|
+
**Theme:** Event system + PPM's own AI layer. Hooks → Skills API → Clawbot dependency chain.
|
|
113
113
|
|
|
114
|
-
| Feature | Priority |
|
|
115
|
-
|
|
116
|
-
| **Hooks system** | High |
|
|
117
|
-
| **PPM Skills API** | High |
|
|
118
|
-
| **
|
|
119
|
-
| **
|
|
120
|
-
| **More providers** | Medium | — | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
|
|
114
|
+
| Feature | Priority | Description |
|
|
115
|
+
|---------|----------|-------------|
|
|
116
|
+
| **Hooks system** | High | Event hooks for PPM lifecycle (file save, git commit, chat message, etc.). Foundation for Skills API and deeper extension integration. |
|
|
117
|
+
| **PPM Skills API** | High | Stable internal API for AI to control PPM: file.read/write/search, terminal.run, git.status/commit/diff, db.query, editor.open/goto, project.switch. Skills are the bridge between AI and PPM features. |
|
|
118
|
+
| **Built-in Clawbot** | High | Lightweight AI agent built into PPM using Anthropic Messages API (not Agent SDK). Uses Skills API + MCP tools. Instant response, no external CLI deps. For quick tasks: file search, code explanation, simple refactors. |
|
|
119
|
+
| **More providers** | Medium | Gemini CLI (Tier 2), OpenAI Codex (Tier 2), Tier 3 chat-only (any OpenAI-compatible API). Provider interface already clean from v0.9. |
|
|
121
120
|
|
|
122
121
|
**Built-in Clawbot — why it matters:**
|
|
123
122
|
- Claude Agent SDK spawns subprocess — heavy, slow startup, requires CLI installed
|
|
@@ -125,12 +124,6 @@ PPM is the **lightest path from phone to code** — a self-hosted, BYOK, multi-d
|
|
|
125
124
|
- Users can use Clawbot to create extensions → zero-friction extension authoring
|
|
126
125
|
- Foundation for "AI creates extensions on demand" vision
|
|
127
126
|
|
|
128
|
-
**Telegram Bot — why it matters:**
|
|
129
|
-
- Extends PPM beyond the web UI → reach users on mobile, messaging apps
|
|
130
|
-
- Long-polling avoids webhooks — simpler for self-hosted, no public URL needed
|
|
131
|
-
- Paired devices enable secure multi-user access — owner controls who can use the bot
|
|
132
|
-
- Persistent memory in FTS5 — bot learns conversation context over time
|
|
133
|
-
|
|
134
127
|
---
|
|
135
128
|
|
|
136
129
|
### v1.0.0 — "Production Ready" (Q4 2026)
|
|
@@ -193,14 +193,8 @@ Tab IDs are deterministic: `{type}:{identifier}` (e.g., `editor:src/index.ts`, `
|
|
|
193
193
|
| **AccountService** | Account CRUD, token encryption/decryption | getAccounts, createAccount, updateAccount, deleteAccount |
|
|
194
194
|
| **AccountSelectorService** | Select active account based on config | getActiveAccount, setActiveAccount, selectByProject |
|
|
195
195
|
| **UpgradeService** | Version checking, installation, self-replace signaling | checkForUpdate, applyUpgrade, getInstallMethod, compareSemver |
|
|
196
|
-
| **ClawBotService** | Telegram bot orchestrator | start, stop, handleMessage, routeToChat |
|
|
197
|
-
| **ClawBotTelegramService** | Telegram long-polling, message ops | getUpdates, sendMessage, editMessage, setTyping, handleCommands |
|
|
198
|
-
| **ClawBotSessionService** | chatID → PPM sessionID mapping | mapSession, getSession, deleteSession |
|
|
199
|
-
| **ClawBotMemoryService** | FTS5 memory persistence | saveMemory, recallMemories, decayMemories, searchByProject |
|
|
200
|
-
| **ClawBotFormatterService** | Markdown → Telegram HTML + chunking | formatMarkdown, chunkMessage |
|
|
201
|
-
| **ClawBotStreamerService** | ChatEvent → progressive Telegram edits | streamMessageEdits |
|
|
202
196
|
|
|
203
|
-
**Key Files:** `src/services/*.service.ts
|
|
197
|
+
**Key Files:** `src/services/*.service.ts`
|
|
204
198
|
|
|
205
199
|
---
|
|
206
200
|
|
|
@@ -269,63 +263,6 @@ PPM supports multiple AI providers through a generic `AIProvider` interface and
|
|
|
269
263
|
|
|
270
264
|
---
|
|
271
265
|
|
|
272
|
-
### ClawBot Service Layer (Telegram Bot Integration)
|
|
273
|
-
**Component:** Telegram bot service + subsidiary services
|
|
274
|
-
|
|
275
|
-
**Responsibilities:**
|
|
276
|
-
- Receive Telegram messages via long-polling (no webhooks needed)
|
|
277
|
-
- Route Telegram user (chatID) to PPM session with pairing-based security
|
|
278
|
-
- Persist session state + conversation memory in SQLite (FTS5)
|
|
279
|
-
- Stream AI responses back to Telegram with progressive message editing
|
|
280
|
-
- Format responses as Telegram HTML with proper chunking (4096 char limit)
|
|
281
|
-
|
|
282
|
-
**Architecture:**
|
|
283
|
-
```
|
|
284
|
-
Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
|
|
285
|
-
↓
|
|
286
|
-
ClawBotSessionService (chatID→sessionID)
|
|
287
|
-
ClawBotMemoryService (FTS5 recall)
|
|
288
|
-
ChatService + ProviderRegistry
|
|
289
|
-
ClawBotStreamerService (ChatEvent→edits)
|
|
290
|
-
ClawBotFormatterService (Markdown→HTML)
|
|
291
|
-
```
|
|
292
|
-
|
|
293
|
-
**Services (src/services/clawbot/):**
|
|
294
|
-
- **ClawBotService** — Lifecycle management (start/stop), message queue, routing logic
|
|
295
|
-
- **ClawBotTelegramService** — Telegram Bot API wrapper (getUpdates long-polling, sendMessage, editMessage, setTyping, command handlers)
|
|
296
|
-
- **ClawBotSessionService** — chatID ↔ PPM sessionID bidirectional mapping, session state tracking
|
|
297
|
-
- **ClawBotMemoryService** — FTS5 persistent memory (save, recall with relevance, decay factor, supersede logic, cross-project search by name mention)
|
|
298
|
-
- **ClawBotFormatterService** — Markdown → Telegram HTML conversion, message chunking (respects 4096 char limit), code block formatting
|
|
299
|
-
- **ClawBotStreamerService** — ChatEvent async generator → progressive Telegram message edits (1s throttle for rate limiting)
|
|
300
|
-
|
|
301
|
-
**Security Model:**
|
|
302
|
-
- **Pairing System** — Replace allowlists with code-based pairing: User requests pairing → receives code → owner approves in web UI → chatID registered in `clawbot_paired_chats`
|
|
303
|
-
- **Per-User Sessions** — Each Telegram chatID maps to isolated PPM session (no cross-user interference)
|
|
304
|
-
- **bypassPermissions** — ClawBot bot runs headless, auto-approves tools (no manual approval flow)
|
|
305
|
-
|
|
306
|
-
**Database Schema (v13):**
|
|
307
|
-
- `clawbot_sessions` — chatID (PK), sessionID (FK chat_sessions), pairedAt, lastUsed
|
|
308
|
-
- `clawbot_memories` — id (PK), sessionID (FK), content (text), role (user|assistant), created, decay_factor (FTS5 full-text index)
|
|
309
|
-
- `clawbot_paired_chats` — chatID (PK), pairingCode (unique, 6 chars), approvedAt, approvedBy (user ID)
|
|
310
|
-
|
|
311
|
-
**Key Design Decisions:**
|
|
312
|
-
1. **Long-polling** — No webhooks = no public URL required, simpler for self-hosted
|
|
313
|
-
2. **Message queue** — Concurrent Telegram messages queued FIFO, prevents race conditions
|
|
314
|
-
3. **Progressive edits** — Edit same message for long responses, reduce Telegram API calls, better UX
|
|
315
|
-
4. **Memory system** — Hybrid extraction (AI primary + regex fallback), supports cross-project search by project name mention
|
|
316
|
-
5. **Config reuse** — Shares existing Telegram bot_token with notifications, separate ClawBotConfig section
|
|
317
|
-
6. **Session tagging** — [Claw] prefix visible in web UI without schema changes, robot icon for identification
|
|
318
|
-
|
|
319
|
-
**Settings Endpoints:**
|
|
320
|
-
- `GET /api/settings/clawbot` — Fetch config (enabled, bot token, default project, system prompt, debounce, display toggles)
|
|
321
|
-
- `PUT /api/settings/clawbot` — Update config
|
|
322
|
-
- `GET /api/clawbot/paired-chats` — List paired Telegram chatIDs
|
|
323
|
-
- `POST /api/clawbot/pairing` — Request pairing code (returns code)
|
|
324
|
-
- `POST /api/clawbot/pairing/:code/approve` — Approve pairing code (owner only)
|
|
325
|
-
- `DELETE /api/clawbot/paired-chats/:chatId` — Revoke pairing
|
|
326
|
-
|
|
327
|
-
---
|
|
328
|
-
|
|
329
266
|
### Data Access Layer (SQLite + Filesystem + Git)
|
|
330
267
|
**Components:** SQLite via bun:sqlite, direct filesystem access, simple-git wrapper
|
|
331
268
|
|
|
@@ -337,7 +274,7 @@ Telegram → ClawBotTelegramService (polling) → ClawBotService (orchestrator)
|
|
|
337
274
|
- Enforce security (no parent directory access)
|
|
338
275
|
|
|
339
276
|
**Key Patterns:**
|
|
340
|
-
- SQLite: WAL mode, foreign keys, lazy init, schema
|
|
277
|
+
- SQLite: WAL mode, foreign keys, lazy init, schema v10 (10 tables: config, connections, accounts, usage_history, session_logs, push_subscriptions, session_map, table_metadata, session_logs, workspace_state)
|
|
341
278
|
- Path validation: `projectPath/relativePath` only, reject `..`
|
|
342
279
|
- Caching: Directory trees cached with TTL
|
|
343
280
|
- Error handling: Descriptive messages (file not found, permission denied)
|
package/package.json
CHANGED
package/src/server/index.ts
CHANGED
|
@@ -477,12 +477,5 @@ if (process.argv.includes("__serve__")) {
|
|
|
477
477
|
console.error("[ExtService] Startup error:", e);
|
|
478
478
|
});
|
|
479
479
|
|
|
480
|
-
// Start PPMBot Telegram poller (if enabled)
|
|
481
|
-
import("../services/ppmbot/ppmbot-service.ts")
|
|
482
|
-
.then(({ ppmbotService }) => ppmbotService.start())
|
|
483
|
-
.catch((e) => {
|
|
484
|
-
console.error("[ppmbot] Startup error:", e);
|
|
485
|
-
});
|
|
486
|
-
|
|
487
480
|
console.log(`Server child ready on port ${port}`);
|
|
488
481
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import { configService } from "../../services/config.service.ts";
|
|
3
|
-
import { getConfigValue, setConfigValue
|
|
3
|
+
import { getConfigValue, setConfigValue } from "../../services/db.service.ts";
|
|
4
4
|
import {
|
|
5
5
|
validateAIProviderConfig,
|
|
6
6
|
validateDefaultProvider,
|
|
7
7
|
VALID_PROVIDERS,
|
|
8
|
-
DEFAULT_CONFIG,
|
|
9
8
|
type AIProviderConfig,
|
|
10
9
|
type TelegramConfig,
|
|
11
|
-
type PPMBotConfig,
|
|
12
10
|
type ThemeConfig,
|
|
13
11
|
} from "../../types/config.ts";
|
|
14
12
|
import { ok, err } from "../../types/api.ts";
|
|
@@ -318,72 +316,3 @@ settingsRoutes.put("/proxy", async (c) => {
|
|
|
318
316
|
return c.json(err((e as Error).message), 400);
|
|
319
317
|
}
|
|
320
318
|
});
|
|
321
|
-
|
|
322
|
-
// ── PPMBot ─────────────────────────────────────────────────────
|
|
323
|
-
|
|
324
|
-
/** GET /settings/clawbot — return current clawbot config */
|
|
325
|
-
settingsRoutes.get("/clawbot", (c) => {
|
|
326
|
-
const config = configService.get("clawbot") as PPMBotConfig | undefined;
|
|
327
|
-
if (!config) return c.json(ok(DEFAULT_CONFIG.clawbot));
|
|
328
|
-
return c.json(ok(config));
|
|
329
|
-
});
|
|
330
|
-
|
|
331
|
-
/** PUT /settings/clawbot — update clawbot config */
|
|
332
|
-
settingsRoutes.put("/clawbot", async (c) => {
|
|
333
|
-
try {
|
|
334
|
-
const body = await c.req.json<Partial<PPMBotConfig>>();
|
|
335
|
-
const current = (configService.get("clawbot") as PPMBotConfig | undefined)
|
|
336
|
-
?? structuredClone(DEFAULT_CONFIG.clawbot!);
|
|
337
|
-
const updated: PPMBotConfig = { ...current, ...body };
|
|
338
|
-
|
|
339
|
-
if (updated.debounce_ms < 0 || updated.debounce_ms > 30000) {
|
|
340
|
-
return c.json(err("debounce_ms must be 0-30000"), 400);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
configService.set("clawbot", updated);
|
|
344
|
-
configService.save();
|
|
345
|
-
|
|
346
|
-
// Restart clawbot if running state changed
|
|
347
|
-
try {
|
|
348
|
-
const { ppmbotService } = await import("../../services/ppmbot/ppmbot-service.ts");
|
|
349
|
-
if (updated.enabled && !ppmbotService.isRunning) {
|
|
350
|
-
await ppmbotService.start();
|
|
351
|
-
} else if (!updated.enabled && ppmbotService.isRunning) {
|
|
352
|
-
ppmbotService.stop();
|
|
353
|
-
}
|
|
354
|
-
} catch { /* PPMBot module not loaded yet — OK */ }
|
|
355
|
-
|
|
356
|
-
return c.json(ok(updated));
|
|
357
|
-
} catch (e) {
|
|
358
|
-
return c.json(err((e as Error).message), 400);
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
|
|
362
|
-
/** GET /settings/clawbot/paired — list paired devices */
|
|
363
|
-
settingsRoutes.get("/clawbot/paired", (c) => {
|
|
364
|
-
return c.json(ok(listPairedChats()));
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
/** POST /settings/clawbot/paired/approve — approve pairing by code */
|
|
368
|
-
settingsRoutes.post("/clawbot/paired/approve", async (c) => {
|
|
369
|
-
try {
|
|
370
|
-
const { code } = await c.req.json<{ code: string }>();
|
|
371
|
-
const pairing = getPairingByCode(code);
|
|
372
|
-
if (!pairing) return c.json(err("Invalid pairing code"), 404);
|
|
373
|
-
approvePairing(pairing.telegram_chat_id);
|
|
374
|
-
// Notify user on Telegram
|
|
375
|
-
try {
|
|
376
|
-
const { ppmbotService } = await import("../../services/ppmbot/ppmbot-service.ts");
|
|
377
|
-
await ppmbotService.notifyPairingApproved(pairing.telegram_chat_id);
|
|
378
|
-
} catch { /* OK */ }
|
|
379
|
-
return c.json(ok({ approved: pairing.telegram_chat_id }));
|
|
380
|
-
} catch (e) {
|
|
381
|
-
return c.json(err((e as Error).message), 400);
|
|
382
|
-
}
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
/** DELETE /settings/clawbot/paired/:chatId — revoke pairing */
|
|
386
|
-
settingsRoutes.delete("/clawbot/paired/:chatId", (c) => {
|
|
387
|
-
revokePairing(c.req.param("chatId"));
|
|
388
|
-
return c.json(ok({ revoked: true }));
|
|
389
|
-
});
|
|
@@ -19,7 +19,7 @@ const PPM_DIR = resolve(homedir(), ".ppm");
|
|
|
19
19
|
|
|
20
20
|
/** Top-level config keys stored in the config table (not projects) */
|
|
21
21
|
const CONFIG_TABLE_KEYS: (keyof PpmConfig)[] = [
|
|
22
|
-
"device_name", "port", "host", "theme", "auth", "ai", "push", "telegram",
|
|
22
|
+
"device_name", "port", "host", "theme", "auth", "ai", "push", "telegram",
|
|
23
23
|
];
|
|
24
24
|
|
|
25
25
|
class ConfigService {
|