@popmelt.com/core 0.5.9 → 0.5.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.mjs CHANGED
@@ -113,7 +113,7 @@ url = "${r}"
113
113
  `)}function Ct(r){let n=r.match(/<review>\s*([\s\S]*?)\s*<\/review>/);if(!(n!=null&&n[1]))return null;try{let t=JSON.parse(n[1]);return typeof t!="object"||t===null||t.verdict!=="pass"&&t.verdict!=="fail"||typeof t.summary!="string"?null:{verdict:t.verdict,summary:t.summary,issues:Array.isArray(t.issues)?t.issues.filter(e=>typeof e=="string"):void 0}}catch(t){return null}}var _e=class{constructor(n=5){this.queue=[];this.activeJobs=new Map;this.activeProcesses=new Map;this.listeners=new Set;this.processor=null;this.maxConcurrency=n}setProcessor(n){this.processor=n}get active(){let n=this.activeJobs.values().next();return n.done?null:n.value}get allActive(){return Array.from(this.activeJobs.values())}get activeCount(){return this.activeJobs.size}get depth(){return this.queue.length}get isRunning(){return this.activeJobs.size>0}setActiveProcess(n,t){t?this.activeProcesses.set(n,t):this.activeProcesses.delete(n)}enqueue(n){return this.queue.push(n),this.processNext(),this.queue.length+this.activeJobs.size}addListener(n){return this.listeners.add(n),()=>this.listeners.delete(n)}broadcast(n,t,e){for(let o of this.listeners)o(n,t,e)}cancelJob(n){let t=this.activeProcesses.get(n),e=this.activeJobs.get(n);return!t||!e?!1:(t.kill("SIGTERM"),this.activeProcesses.delete(n),this.activeJobs.delete(n),e.status="error",e.error="Cancelled by user",this.broadcast({type:"error",jobId:e.id,message:"Cancelled by user",cancelled:!0},e.id,e.sourceId),this.processNext(),!0)}cancelActive(){if(this.activeJobs.size===0)return!1;let n=Array.from(this.activeJobs.keys());for(let t of n)this.cancelJob(t);return!0}destroy(){for(let n of this.activeProcesses.values())n.kill("SIGTERM");this.activeProcesses.clear(),this.activeJobs.clear(),this.queue=[],this.listeners.clear()}async destroyAsync(n=1e4){let t=Array.from(this.activeProcesses.values());if(this.queue=[],this.listeners.clear(),t.length===0){this.activeProcesses.clear(),this.activeJobs.clear();return}for(let e of t)try{e.kill("SIGTERM")}catch(o){}await Promise.all(t.map(e=>new Promise(o=>{let a=!1,i=()=>{a||(a=!0,o())};e.on("exit",i),e.on("error",i),setTimeout(()=>{if(!a){try{e.kill("SIGKILL")}catch(l){}setTimeout(i,500)}},n)}))),this.activeProcesses.clear(),this.activeJobs.clear()}processNext(){for(;this.activeJobs.size<this.maxConcurrency&&this.queue.length>0&&this.processor;){let n=this.queue.shift();this.activeJobs.set(n.id,n),n.status="running",this.broadcast({type:"job_started",jobId:n.id,position:0},n.id,n.sourceId),this.processor(n).catch(t=>{n.status="error",n.error=t instanceof Error?t.message:String(t),this.broadcast({type:"error",jobId:n.id,message:n.error},n.id,n.sourceId)}).finally(()=>{this.activeJobs.delete(n.id),this.activeProcesses.delete(n.id),this.processNext(),this.activeJobs.size===0&&this.queue.length===0&&this.broadcast({type:"queue_drained"},n.id)})}}};import{mkdir as un,readFile as pn,writeFile as hn}from"fs/promises";import{dirname as fn,join as mn}from"path";var gn={version:1,threads:{}},Je=class{constructor(n){this.cache=null;this.writeChain=Promise.resolve();this.filePath=mn(n,".popmelt","threads.json")}async load(){if(this.cache)return this.cache;try{let n=await pn(this.filePath,"utf-8"),t=JSON.parse(n);if(t&&t.version===1&&t.threads)return this.cache=t,this.cache}catch(n){}return this.cache=ce(te({},gn),{threads:{}}),this.cache}async getThread(n){var e;return(e=(await this.load()).threads[n])!=null?e:null}async findContinuationThread(n){if(n.length===0)return null;let t=await this.load(),e=new Set(n);for(let o of Object.values(t.threads))if(o.elementIdentifiers.some(i=>e.has(i)))return o;return null}async createThread(n,t){let e=await this.load(),o={id:n,createdAt:Date.now(),updatedAt:Date.now(),elementIdentifiers:t,messages:[]};return e.threads[n]=o,await this.persist(),o}async appendMessage(n,t){let o=(await this.load()).threads[n];o&&(o.messages.push(t),o.updatedAt=Date.now(),await this.persist())}async addElementIdentifiers(n,t){let o=(await this.load()).threads[n];if(!o)return;let a=new Set(o.elementIdentifiers);for(let i of t)a.has(i)||o.elementIdentifiers.push(i);o.updatedAt=Date.now(),await this.persist()}async getThreadHistory(n,t=6){let e=await this.getThread(n);return!e||e.messages.length===0?[]:e.messages.length<=t?e.messages:[e.messages[0],...e.messages.slice(-(t-1))]}async persist(){this.writeChain=this.writeChain.then(async()=>{if(this.cache)try{await un(fn(this.filePath),{recursive:!0}),await hn(this.filePath,JSON.stringify(this.cache,null,2))}catch(n){console.error("[ThreadStore] Failed to persist:",n)}}),await this.writeChain}};var Jn={};var $n=1111,Mn=["Read","Edit","Write","Glob","Grep","Bash","WebFetch","WebSearch","Bash(curl:*)"],On=1800*1e3,Cn=3600*1e3;function Dt(r){if(!r)return!1;try{let n=new URL(r);return n.hostname==="localhost"||n.hostname==="127.0.0.1"}catch(n){return!1}}function En(r,n){let t=r.headers.origin;Dt(t)&&(n.setHeader("Access-Control-Allow-Origin",t),n.setHeader("Access-Control-Allow-Methods","GET, POST, PATCH, DELETE, OPTIONS"),n.setHeader("Access-Control-Allow-Headers","Content-Type"))}function m(r,n,t){r.writeHead(n,{"Content-Type":"application/json"}),r.end(JSON.stringify(t))}function An(r,n){if(!r)return n;let t=r.match(/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i);if(!t)return n;let[,e,o,a]=t;return`\x1B[38;2;${parseInt(e,16)};${parseInt(o,16)};${parseInt(a,16)}m${n}\x1B[0m`}function Et(r,n){try{r.res.write(`event: ${n.type}
114
114
  data: ${JSON.stringify(n)}
115
115
 
116
- `)}catch(t){}}async function Dn(r){try{let n=new AbortController,t=setTimeout(()=>n.abort(),500),e=await fetch(`http://127.0.0.1:${r}/status`,{signal:n.signal});return clearTimeout(t),e.ok?await e.json():null}catch(n){return null}}function _n(r,n){return new Promise((t,e)=>{let o=i=>{r.removeListener("listening",a),e(i)},a=()=>{r.removeListener("error",o),t()};r.once("error",o),r.once("listening",a),r.listen(n,"127.0.0.1")})}async function _t(r={}){var Qe,qe,Ke,Xe,Ve,Ze,et,tt,nt;let n=(Qe=r.port)!=null?Qe:$n,t=(qe=r.projectRoot)!=null?qe:process.cwd(),e=wn("sha256").update(t).digest("hex").slice(0,12),o=(Ke=r.devOrigin)!=null?Ke:null,a=(Xe=r.tempDir)!=null?Xe:oe(bn(),"popmelt-bridge"),i=(Ve=r.maxTurns)!=null?Ve:40,l=(Ze=r.maxBudgetUsd)!=null?Ze:1,h=(et=r.allowedTools)!=null?et:Mn,f=(tt=r.claudePath)!=null?tt:"claude",J=(nt=r.provider)!=null?nt:"claude",A=r.timeoutMs,O=n,g={};for(let s of["claude","codex"])try{let c=yn("which",[s],{encoding:"utf-8"}).trim();g[s]={available:!0,path:c}}catch(c){g[s]={available:!1,path:null}}let[H,W]=await Promise.all([He(t),Ge(t)]);g.claude&&(g.claude.mcp=H),g.codex&&(g.codex.mcp=W),await vn(a,{recursive:!0}),At(a).catch(()=>{});let v=new _e(1),L=new Set,C=new Je(t),V=new Ee(t),E=new Ae(t,V,{claudePath:f,onEvent:s=>{for(let c of L)Et(c,s)}}),X=new Map,U=20,ne=300*1e3,P=[],x=null,G;v.addListener((s,c,u)=>{for(let d of L)(!u||!d.sourceId||d.sourceId===u)&&Et(d,s)}),v.setProcessor(async s=>{var Z,re,_,se,le,Se,ue;let c=s._replyPrompt,u=s._replyImagePaths,d=(Z=s.provider)!=null?Z:J,p;if(s.threadId){let $=await C.getThread(s.threadId);if($){for(let y=$.messages.length-1;y>=0;y--)if($.messages[y].sessionId){p=$.messages[y].sessionId;break}}}let S;if(p&&c){let $=(re=await C.getThread(s.threadId))==null?void 0:re.messages.filter(M=>M.role==="human").pop();if(S=($==null?void 0:$.replyToQuestion)||($==null?void 0:$.feedbackSummary)||"",u&&u.length>0){S+=`
116
+ `)}catch(t){}}async function Dn(r){try{let n=new AbortController,t=setTimeout(()=>n.abort(),500),e=await fetch(`http://127.0.0.1:${r}/status`,{signal:n.signal});return clearTimeout(t),e.ok?await e.json():null}catch(n){return null}}function _n(r,n){return new Promise((t,e)=>{let o=i=>{r.removeListener("listening",a),e(i)},a=()=>{r.removeListener("error",o),t()};r.once("error",o),r.once("listening",a),r.listen(n,"127.0.0.1")})}async function _t(r={}){var Qe,qe,Ke,Xe,Ve,Ze,et,tt,nt;let n=(Qe=r.port)!=null?Qe:$n,t=(qe=r.projectRoot)!=null?qe:process.cwd(),e=wn("sha256").update(t).digest("hex").slice(0,12),o=(Ke=r.devOrigin)!=null?Ke:process.env.PORT?`http://localhost:${process.env.PORT}`:null,a=(Xe=r.tempDir)!=null?Xe:oe(bn(),"popmelt-bridge"),i=(Ve=r.maxTurns)!=null?Ve:40,l=(Ze=r.maxBudgetUsd)!=null?Ze:1,h=(et=r.allowedTools)!=null?et:Mn,f=(tt=r.claudePath)!=null?tt:"claude",J=(nt=r.provider)!=null?nt:"claude",A=r.timeoutMs,O=n,g={};for(let s of["claude","codex"])try{let c=yn("which",[s],{encoding:"utf-8"}).trim();g[s]={available:!0,path:c}}catch(c){g[s]={available:!1,path:null}}let[H,W]=await Promise.all([He(t),Ge(t)]);g.claude&&(g.claude.mcp=H),g.codex&&(g.codex.mcp=W),await vn(a,{recursive:!0}),At(a).catch(()=>{});let v=new _e(1),L=new Set,C=new Je(t),V=new Ee(t),E=new Ae(t,V,{claudePath:f,onEvent:s=>{for(let c of L)Et(c,s)}}),X=new Map,U=20,ne=300*1e3,P=[],x=null,G;v.addListener((s,c,u)=>{for(let d of L)(!u||!d.sourceId||d.sourceId===u)&&Et(d,s)}),v.setProcessor(async s=>{var Z,re,_,se,le,Se,ue;let c=s._replyPrompt,u=s._replyImagePaths,d=(Z=s.provider)!=null?Z:J,p;if(s.threadId){let $=await C.getThread(s.threadId);if($){for(let y=$.messages.length-1;y>=0;y--)if($.messages[y].sessionId){p=$.messages[y].sessionId;break}}}let S;if(p&&c){let $=(re=await C.getThread(s.threadId))==null?void 0:re.messages.filter(M=>M.role==="human").pop();if(S=($==null?void 0:$.replyToQuestion)||($==null?void 0:$.feedbackSummary)||"",u&&u.length>0){S+=`
117
117
 
118
118
  The developer attached reference images with their reply:`;for(let M of u)S+=`
119
119
  Attached image: use the Read tool to view the image at: ${M}`}S+=`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@popmelt.com/core",
3
- "version": "0.5.9",
3
+ "version": "0.5.10",
4
4
  "description": "The design collaboration layer for AI coding agents",
5
5
  "license": "PolyForm-Shield-1.0.0",
6
6
  "author": "Popmelt <hello@popmelt.com> (https://popmelt.com)",