typebulb 0.20.2 → 0.20.4
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/README.md
CHANGED
|
@@ -224,7 +224,7 @@ The agent mirror turns that block into a live, sandboxed app, with a *breakout
|
|
|
224
224
|
|
|
225
225
|
`typebulb wait` turns a background task into a subscription. It blocks until the target server logs a new line (`--match <substr>` filters), prints it, and exits — and since an agent harness re-invokes the agent when a background task finishes, the exit *is* the wake-up. It resumes where your last `wait` or `call` on that target left off, so an event that lands while you're acting — or before the wait attaches — still fires it immediately; arm order doesn't matter. It parks until the event. Exit `2` means it gave up before any event arrived (re-arm if you still care, or move on); exit `3` means the server died.
|
|
226
226
|
|
|
227
|
-
**The turn-based loop** (a game, an approval flow): a bulb whose `server.ts` does `console.log` on each user action is the event channel. Per turn — act via `typebulb call`, arm `wait <file> --match <tag>` in the background, end your turn; on wake, read state with `typebulb call <file> <getState>` (never parse it from the log line) and repeat. A bulb's uncaught browser errors land in the same log as `[runtime error] …`, so the wake channel also catches your bulb breaking. For embeds, the same subscription is `typebulb wait agent` on the mirror — see [Emitting an embedded bulb](#emitting-an-embedded-bulb).
|
|
227
|
+
**The turn-based loop** (a game, an approval flow): a bulb whose `server.ts` does `console.log` on each user action is the event channel. Per turn — act via `typebulb call`, arm `wait <file> --match <tag>` in the background, end your turn; on wake, read state with `typebulb call <file> <getState>` (never parse it from the log line) and repeat. **`call` always boots a fresh `server.ts` instance** — it never attaches to the running bulb's server — so any state shared between the page and your calls must live on disk (load/save it in each export), not in `server.ts` module memory. A bulb's uncaught browser errors land in the same log as `[runtime error] …`, so the wake channel also catches your bulb breaking. For embeds, the same subscription is `typebulb wait agent` on the mirror — see [Emitting an embedded bulb](#emitting-an-embedded-bulb).
|
|
228
228
|
|
|
229
229
|
**Keep every loop command argument-stable.** A harness that permission-matches exact command strings prompts the user on *every* event if varying data (a move, a payload) rides the command line. Keep it off: write the args to a fixed file and pipe them — `cat <bulb-folder>/args.json | typebulb call <file> <fn> --args -` — so each of the loop's commands is one constant string, approved once. `wait` and a `getState` call are constant already.
|
|
230
230
|
|
|
@@ -1376,7 +1376,7 @@ ${ZEt(m)}`};let v=VEt(d.code,{jsxImportSource:g.ts?.jsxImportSource});if(v.error
|
|
|
1376
1376
|
|
|
1377
1377
|
`)}stickToBottomNextRender(){this.#n=!0}#u(o,a){o.segments=[...o.segments??[o.text],a],o.text=o.segments.join(`
|
|
1378
1378
|
|
|
1379
|
-
`)}applyUser(o){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#u(a,o.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#i(a.text));return}this.#s({id:++this.#e,role:"user",text:o.text,thinking:"",tools:[]})}applyAssistant(o){let a=this.#s({id:++this.#e,role:"assistant",text:o.text,thinking:o.thinking,tools:o.tools.map(g=>({...g,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#i(""),a.turnCopy.setText(this.#c()),this.#f(a,o.text),this.#h(),o.live)for(let g of o.tools)ovn.has(g.name)&&this.openTools.add(g.id)}#f(o,a){let d=uSt(a);d.some(g=>g.kind==="bulb")&&(o.body=d.map(g=>{if(g.kind==="md")return g.text;let m=new Qre(g.source,`embed-${this.#t++}`);return this.bulbEmbeds.push(m),m}))}#h(){let o=this.bulbEmbeds.map(g=>g.name),a=aSt(o),d=lSt(o);this.#r=new Set(this.bulbEmbeds.filter((g,m)=>a[m])),this.bulbEmbeds.forEach((g,m)=>{g.setChainPosition(d[m]),g.setMounted(!a[m]||this.expandedEmbeds.has(g.key))})}#a(o){Rxe(this.expandedEmbeds,o),this.#h(),this.update()}applyToolResult(o){let a=this.#o(o.id);a&&(a.result=o.content,a.isError=o.isError,a.digest=o.digest)}applyFork(o){let a=this.#l(o.events);this.#s({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:o.count,sub:a}})}#l(o){let a=[];for(let d of o)if(d.type==="user"){let g=a[a.length-1];g&&g.role==="user"?this.#u(g,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(g=>({...g,isError:!1}))});else if(d.type==="tool_result"){let g=a.flatMap(m=>m.tools).find(m=>m.id===d.id);g&&(g.result=d.content,g.isError=d.isError,g.digest=d.digest)}return a}#o(o){return this.messages.flatMap(a=>a.tools).find(a=>a.id===o)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#n&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let o=this.scrollEl;o&&(this.#n=o.scrollHeight-(o.scrollTop+o.clientHeight)<50)}view(){return hn({class:"messages",onScroll:()=>this.onScroll(),onMounted:o=>{this.scrollEl=o}},this.parent.ready?this.renderMessages():hn({class:"note"},"Connecting\u2026"))}renderMessages(){let o=[],a=-1;for(let d of this.messages)d.role==="user"||o.length===0?(d.role==="user"&&a++,o.push({idx:Math.max(0,a),msgs:[d]})):o[o.length-1].msgs.push(d);return o.flatMap((d,g)=>this.renderTurn(d.msgs,d.idx,g===o.length-1))}renderTurn(o,a,d){if(this.parent.prose){let T=o.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),L=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let P=D.role==="user"?D.copy:D===L?D.turnCopy:void 0;return this.bubble(D,a,P??null)})}let g=o.filter(T=>T.role==="assistant");if(d||g.length<2)return o.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let m=this.expandedTurns.has(a),v=g.slice(0,-1),w=g[g.length-1],I=o.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of o)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),m)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(w,a)),I}#p(o,a,d,g,m,v){let w=d.has(g);return hn({class:["bubble","assistant",Dye(a)],key:o},hn({class:"turn-summary",onClick:()=>{Rxe(d,g),this.update()}},Oxe(w),ys({class:"turn-summary-text"},m)),v??null)}forkStub(o,a){let d=o.fork,g=this.expandedForks.has(o.id)?hn({class:"fork-body"},d.sub.map(v=>this.bubble(v,a,void 0,!1))):void 0,m=[ivn(),` ${d.count} message${d.count===1?"":"s"} on an abandoned branch`];return this.#p(`fork-${o.id}`,a,this.expandedForks,o.id,m,g)}turnSummary(o,a){return this.#p(`summary-${a}`,a,this.expandedTurns,a,lvn(o))}bubble(o,a,d=o.copy,g=!0){let m=this.parent.prose,v=o.role==="assistant"&&!o.text&&!o.thinking&&o.tools.length>0;return hn({class:["bubble",o.role,v?"tools-only":"",g?Dye(a):""],key:o.id},!m&&o.thinking?K5t({class:"thinking"},W5t("thinking"),I7(o.thinking)):null,this.#g(o),m?null:o.tools.map(w=>this.tool(w)),d?d.view():null)}#g(o){return o.body?o.body.flatMap((a,d)=>typeof a=="string"?a.trim()?[this.#d(`md-${o.id}-${d}`,$re(a))]:[]:this.#b(a)):o.text?this.#d(`md-${o.id}`,o.role==="user"?cSt(o):$re(o.text)):null}#b(o){if(!this.#r.has(o))return[o.view()];let a=this.expandedEmbeds.has(o.key),d=hn({class:"md",key:`fold-${o.key}`},hn({class:"bulb-fold",onClick:()=>this.#a(o.key)},Oxe(a),ys({class:"bulb-fold-text"},`${o.name??"bulb"} \u2014 Version ${o.chainPosition}`),ys({class:"bulb-fold-app"},"\u{1F4A1}")));return a?[d,o.view()]:[d]}#d(o,a){return hn({class:"md",key:o,onMounted:a})}tool(o){let a=this.openTools.has(o.id),d=cvn(o.name,o.input),g=dSt(o),m=d?o6t(d,this.parent.cwd):rvn(nvn(o.input));return hn({class:["tool",o.isError?"err":""]},hn({class:"tool-head"},hn({class:"tool-label"},ys({class:"tool-name",...o.name.startsWith("mcp__")?{title:o.name}:{}},Pxe(o.name)),m?d?uM({class:"tool-sum link",title:d,onClick:v=>{v.preventDefault(),tb.server.openFile(d)}},m):ys({class:"tool-sum"},m):null),o.result===void 0?ys({class:"tool-run"},"\u2026"):null),o.result!==void 0?hn({class:"tool-digest",onClick:()=>{Rxe(this.openTools,o.id),this.update()}},Oxe(a),(g?avn(g):o.digest)||"(no output)"):null,a?hn({class:"tool-card"},this.toolBody(o),o.result!==void 0&&(!(g||hSt(o).length)||o.isError)?I7({class:"tool-out"},o.result.slice(0,4e3)):null):null)}toolBody(o){let a=dSt(o);if(a)return hn({class:"todo-list"},a.map(m=>hn({class:["todo",m.status??""]},ys({class:"todo-icon"},m.status==="completed"?"\u2714":m.status==="in_progress"?"\u25FC":"\u25FB"),ys({class:"todo-text"},m.content??""))));let d=hSt(o);if(d.length)return hn({class:"tool-in"},d.map(fSt));let g=uvn(o);if(!g){let m=Object.entries(o.input??{});return m.length?hn({class:"tool-in"},m.map(([v,w])=>hn({class:"tool-field"},ys({class:"tool-key"},`${v}:`),typeof w=="string"&&pSt(w)?fSt(w):I7({class:"tool-val"},typeof w=="string"?w:JSON.stringify(w,null,2))))):null}return hn({class:"diff"},...g.flatMap((m,v)=>[g.length>1?hn({class:"diff-step"},`edit ${v+1}/${g.length}`):null,m.old?I7({class:"diff-old"},String(m.old)):null,m.new?I7({class:"diff-new"},String(m.new)):null]))}};var qre=class extends Ia{ready=!1;cwd="";sessionId="";sessionPicker=new Gee;tokenPill=new qee;bulbsPill=new Jee;prosePill=new Uee;messageList=new Gre;tokens={in:0,out:0,cached:0,cacheCreate:0};working=!1;latestModel=null;prose=!1;ownPid=0;pills;#e=0;#t=!1;#r=!1;#n;#i;#s;constructor(o){super(),this.pills=o.pills,this.#n=o.title,this.#i=o.overlays??[],this.#s=o.onPollTick}onAttached(){this.#r||(this.#r=!0,this.init())}closePopups(o){this.sessionPicker!==o&&this.sessionPicker.close(),this.bulbsPill!==o&&this.bulbsPill.close();for(let a of this.pills)a!==o&&a.close?.()}async init(){let o=await tb.server.info();this.cwd=o.cwd,this.ownPid=o.pid??0,this.ready=!0,this.updateTitle(),this.update(),this.pump()}updateTitle(){let o=Bye(this.cwd),a=this.sessionPicker.currentPreview(),d=Hee(a,40);document.title=d&&o?`${d} \u2014 ${o}`:d||(o?`${this.#n} \u2014 ${o}`:this.#n)}pump(){if(this.#t)return;this.#t=!0;let o=async()=>{try{let{events:a,cursor:d,working:g,latestModel:m}=await tb.server.poll(this.#e);this.#e=d;for(let I of a)this.apply(I);let v=g!==this.working;this.working=g;let w=m!==this.latestModel;this.latestModel=m??null,(a.length||v||w)&&this.update(),a.length&&this.messageList.scrollSoon(),this.#s?.()}catch(a){console.error("[mirror] poll failed",a)}setTimeout(o,600)};o()}apply(o){switch(o.type){case"cleared":this.messageList.clear(),this.tokens={in:0,out:0,cached:0,cacheCreate:0};break;case"session":this.sessionId=o.sessionId,this.updateTitle();break;case"user":this.messageList.applyUser(o);break;case"assistant":this.messageList.applyAssistant(o);break;case"tool_result":this.messageList.applyToolResult(o);break;case"fork":this.messageList.applyFork(o);break;case"usage":this.tokens={in:o.in,out:o.out,cached:o.cached,cacheCreate:o.cacheCreate};break}}view(){return hn({class:"app"},this.messageList.view(),...this.#i.map(o=>o()),this.statusbar())}statusbar(){return hn({class:"statusbar"},hn({class:"statusbar-actions"},this.prosePill.view(),...this.pills.map(o=>o.view()),this.tokenPill.view(),this.sessionPicker.view(),this.bulbsPill.view()))}};var fvn=[[0,"Low"],[1,"Med"],[2,"High"]],hvn=1,Jre=s=>s?.message??String(s),dvn=()=>pC({viewBox:"0 0 24 24",width:"15",height:"15",class:"anthropic-glyph",fill:"currentColor"},dC({d:"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z"})),gSt=s=>s===!0?"ok":s===!1?"bad":"warn",Ure=class extends S7{models=[];hasKey=!0;loaded=!1;current={active:!1,managed:!1,model:null,port:null,provider:null,routedModel:null,caching:"unknown",reasoning:null,externalBaseUrl:null};busy=!1;err=null;enteringKey=!1;keyDraft="";probes={};probing=null;openDiag=null;infoOpen=!1;ticking=!1;keepOpenSelector=".model-wrap";filterId="model-filter";listSelector="#model-list";filterNoun="model";search(){return Promise.resolve([])}modeToggle(){return null}onActivate(o){let a=this.rows()[o];a&&(a.kind==="default"?this.clear():a.kind==="unlock"?this.beginKeyEntry():this.set(a.id))}onAttached(){this.refresh()}async refresh(){try{let[o,a]=await Promise.all([tb.server.listSwitchModels(),tb.server.switchState()]);this.models=o.models,this.hasKey=o.hasKey,this.current=a,this.err=null}catch(o){this.err=Jre(o),console.error("[mirror] switcher refresh failed",o)}finally{this.loaded=!0,this.update()}}async tickState(){if(!(this.ticking||this.enteringKey)){this.ticking=!0;try{let o=await tb.server.switchState(),a=o.active!==this.current.active||o.model!==this.current.model||o.caching!==this.current.caching||o.routedModel!==this.current.routedModel||o.externalBaseUrl!==this.current.externalBaseUrl||o.provider!==this.current.provider||o.reasoning!==this.current.reasoning;this.current=o,a&&this.update()}catch{}finally{this.ticking=!1}}}rows(){let o=[];if(!this.hasKey)o.push({kind:"unlock"});else{let a=this.filter.trim().toLowerCase(),d=null;for(let g of this.models){if(a&&!g.label.toLowerCase().includes(a)&&!g.id.toLowerCase().includes(a))continue;let m={kind:"model",id:g.id,label:g.label,active:this.current.active&&this.current.model===g.id};m.active?d=m:o.push(m)}d&&o.push(d)}return o.push({kind:"default",active:!this.current.active&&!this.current.externalBaseUrl}),o}activeIndex(){let o=this.rows(),a=o.findIndex(d=>d.active);return a<0?Math.max(0,o.length-1):a}async show(){this.beginOpen(),this.refreshList(this.activeIndex()),this.focusFilter(),await this.refresh(),this.refreshList(this.activeIndex()),this.armClose()}onClosed(){super.onClosed(),this.enteringKey=!1,this.keyDraft="",this.openDiag=null,this.infoOpen=!1,this.err=null}async probe(o){if(!this.probing){this.probing=o,this.update();try{this.probes[o]=await tb.server.probeModel({model:o})}catch(a){this.probes[o]={ok:!1,error:Jre(a)}}this.probing=null,this.update()}}showDiag(o){this.openDiag=o,this.update()}closeDiag(){this.openDiag=null,this.update(),this.focusFilter()}label(o){return o?this.models.find(a=>a.id===o)?.label||o.split("/").pop()||o:"Anthropic"}async runBusy(o,a){this.busy=!0,this.err=null,this.update();try{let d=await o();if(d?.ok===!1)return this.err=d.error||a,this.busy=!1,this.update(),!1}catch(d){return this.err=Jre(d),this.busy=!1,this.update(),!1}return this.busy=!1,!0}async set(o){await this.runBusy(()=>tb.server.setSwitchModel({model:o}),"failed to set model")&&(await this.refresh(),this.close())}async clear(){await this.runBusy(()=>tb.server.clearSwitchModel(),"failed to revert")&&(await this.refresh(),this.close())}get reasoningSel(){return this.current.reasoning??hvn}set reasoningSel(o){this.setReasoning(o)}async setReasoning(o){if(this.current.reasoning!==o){this.current={...this.current,reasoning:o},this.update();try{let a=await tb.server.setReasoning({level:o});a?.ok===!1&&(this.err=a.error||"failed to set reasoning",this.update())}catch(a){this.err=Jre(a),this.update()}}}beginKeyEntry(){this.enteringKey=!0,this.keyDraft="",this.update(),setTimeout(()=>document.getElementById("or-key-input")?.focus())}cancelKeyEntry(){this.enteringKey=!1,this.keyDraft="",this.update(),this.focusFilter()}async saveKey(){let o=this.keyDraft.trim();o&&await this.runBusy(()=>tb.server.saveOpenRouterKey({key:o}),"failed to save key")&&(this.enteringKey=!1,this.keyDraft="",await this.refresh(),this.refreshList(this.activeIndex()))}get overridden(){return this.current.active}get uncached(){return this.current.active&&this.current.caching==="uncached"}watchdogView(){return this.uncached?hn({class:["model-banner","warn"]},ys({class:"model-banner-text"},`${this.label(this.current.model)} was served uncached via OpenRouter \u2014 the full context is re-billed each turn. Probe the model to pin a provider that caches, or pick a different one.`)):null}head(){return this.current.active?hn({class:"model-head"},qc({class:"model-info-toggle",onClick:o=>{o.stopPropagation(),this.infoOpen=!0,this.update()}},"Routing info"),hn({class:"reasoning-ctl",title:"Reasoning sets the route\u2019s base thinking effort."},ys({class:"reasoning-label"},"reasoning"),this.reasoningSelect())):this.current.externalBaseUrl?hn({class:"model-head"},ys({class:["model-route","warn"]},"Custom base URL set elsewhere \u2014 \u201CAnthropic (native)\u201D clears it")):null}reasoningSelect(){return t6t({target:this,prop:()=>this.reasoningSel,attrs:{class:"reasoning-select",onClick:o=>o.stopPropagation()},options:fvn.map(([o,a])=>({value:o,label:a}))})}infoView(){let o=this.label(this.current.model),a=()=>{this.infoOpen=!1,this.update()};return hn({class:"picker model-picker diag-mode",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&(d.stopPropagation(),a())}},hn({class:"diag-head"},ys({class:"diag-name"},"Routing"),hn({class:"diag-head-acts"},qc({class:"diag-back",title:"Back to the model list",onClick:d=>{d.stopPropagation(),a()}},"\u2190 models"))),hn({class:"diag-body"},hn({class:"diag-line"},`You are currently routing to ${o} via OpenRouter, not your Anthropic subscription.`),hn({class:"diag-line"},"The route is shared by every Claude Code conversation scoped to this project."),hn({class:"diag-line"},"Switching to or from Anthropic toggles the proxy, so for existing conversations you\u2019ll need to:"),hn({class:["diag-line","diag-bullet"]},"\u2022 Relaunch the Claude CLI"),hn({class:["diag-line","diag-bullet"]},"\u2022 or, in the VS Code extension, run \u201CDeveloper: Restart Extension Host\u201D (Cmd/Ctrl+Shift+P).")))}view(){let o=this.current.active,a=this.uncached,d=!o&&!!this.current.externalBaseUrl,g=this.label(this.current.model),m=a?`${g} via OpenRouter, uncached \u2014 full context re-billed each turn. Click to switch or revert.`:o?`claude \u2192 ${this.current.model}: its Anthropic API is proxied to OpenRouter. Click to switch or revert.`:d?`Custom ANTHROPIC_BASE_URL set outside this tool (${this.current.externalBaseUrl}). Click to manage.`:"Anthropic (native) \u2014 click to proxy claude\u2019s Anthropic API to an OpenRouter model.",v=o?g:d?"custom route":dvn();return hn({class:"model-wrap"},qc({class:["pill",o||d?"on":"glyph",a?"warn":""],title:m,ariaLabel:o||d?void 0:"Anthropic (native)",onClick:w=>{w.stopPropagation(),this.open?this.close():this.show()}},v),this.open?this.popup():null)}popup(){if(this.enteringKey)return this.keyView();if(this.infoOpen)return this.infoView();if(this.openDiag)return this.diagView(this.openDiag);let o=this.rows(),a=this.loaded&&this.hasKey&&!this.models.length;return hn({class:"picker model-picker"},this.head(),hn({id:"model-list",class:"picker-list",onScroll:()=>this.onListScroll()},o.map((d,g)=>this.row(d,g)),a?hn({class:"picker-empty"},this.err?`Couldn't load models: ${this.err}`:"No OpenRouter models in the catalog right now."):null),this.err&&!a?hn({class:"model-err"},`\u2717 ${this.err}`):null,this.hasKey&&this.models.length?this.filterBox(this.models.length,"model"):null)}row(o,a){let d=()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},g=["picker-row",a===this.highlighted?"active":""];if(o.kind==="unlock")return hn({class:g,onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},"+ Add OpenRouter key to unlock models\u2026")));if(o.kind==="default")return hn({class:[...g,o.active?"current":"","model-row","model-default-row"],title:"Your normal claude (subscription / API) \u2014 clears the override",onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},"Anthropic (native)"),hn({class:"model-trailing"},o.active?this.activeBadge():null)));let m=o.id;return hn({class:[...g,o.active?"current":"","model-row"],title:m,onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},o.label),hn({class:"model-trailing"},o.active?this.activeBadge():null,this.statusButton(m))))}activeBadge(){return ys({class:["model-btn","model-active"],title:"Active \u2014 bare claude routes here now"},"active")}statusButton(o){if(this.probing===o)return ys({class:["model-btn","model-status","probing"]},"probing\u2026");let a=this.probes[o];if(!a)return qc({class:["model-btn","model-status","probe"],title:"Check if it\u2019s callable and which provider caches \u2014 pins the caching one.",onClick:g=>{g.stopPropagation(),this.probe(o)}},"probe");let d=this.badge(a);return qc({class:["model-btn","model-status",d.cls],title:`${d.title} \xB7 click for details`,onClick:g=>{g.stopPropagation(),this.showDiag(o)}},d.text)}badge(o){if(!o.ok||o.callable===!1)return{text:"error",cls:"bad",title:o.error||"unavailable"};let a=o.provider?` on ${o.provider}`:"",d=o.cache?.works,g=gSt(d);return d===!0?{text:"cached",cls:g,title:`caching confirmed${a} \u2014 ${o.cache?.note||""}`}:d===!1?{text:"uncached",cls:g,title:`no caching${a} \u2014 ${o.cache?.note||""}`}:{text:"cache?",cls:g,title:`${o.cache?.note||"caching unconfirmed"}${a}`}}diagView(o){return hn({class:"picker model-picker diag-mode",tabIndex:0,onMounted:a=>a.focus(),onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeDiag())}},hn({class:"diag-head"},ys({class:"diag-name"},this.label(o)),hn({class:"diag-head-acts"},qc({class:"diag-back",title:"Re-run the probe",disabled:this.probing===o,onClick:a=>{a.stopPropagation(),this.probe(o)}},this.probing===o?"probing\u2026":"probe again"),qc({class:"diag-back",title:"Back to the model list",onClick:a=>{a.stopPropagation(),this.closeDiag()}},"\u2190 models"))),hn({class:"diag-body"},...this.diagLines(o)))}diagLines(o){let a=this.probes[o];if(!a)return[hn({class:["diag-line","muted"]},"Not probed yet \u2014 close this and hit probe.")];let d=[];if(!a.ok||a.callable===!1)d.push(hn({class:["diag-line","bad"]},`\u2717 Not callable${a.status?` (HTTP ${a.status})`:""}: ${a.error||"unknown error"}`)),a.restriction&&d.push(hn({class:["diag-line","warn"]},`\u26A0 ${a.restriction}`));else{d.push(hn({class:["diag-line","ok"]},`\u2713 Callable${a.ms?` \xB7 ${a.ms}ms`:""}${a.sample?` \xB7 replied \u201C${a.sample}\u201D`:""}`)),a.provider&&d.push(hn({class:["diag-line","muted"]},`Served by ${a.provider} this probe \u2014 the proxy will pin CC's turns to it when caching is confirmed.`));let g=a.cache;if(g){let m=gSt(g.works),v=g.works===!0?"\u2713 Caching works":g.works===!1?"\u2717 No caching":"~ Caching unconfirmed",w=a.provider?` on ${a.provider}`:"";d.push(hn({class:["diag-line",m]},`${v}${w} \u2014 ${g.note}`)),g.works!==!0&&d.push(hn({class:["diag-line","muted"]},"Without prompt caching every turn re-bills the whole context \u2014 costly over a long session."))}}return d.push(hn({class:["diag-line","muted","small"]},"Caching on OpenRouter is per-provider; the proxy pins the provider a probe confirms so CC keeps caching. A probe makes two small test calls.")),d}keyView(){return hn({class:"picker model-picker key-entry",tabIndex:0,onKeyDown:o=>{o.key==="Escape"&&(o.stopPropagation(),this.cancelKeyEntry())}},hn({class:"key-entry-head"},"Paste your OpenRouter API key"),Pee({target:this,prop:()=>this.keyDraft,id:"or-key-input",attrs:{class:"bulb-filter",placeholder:"sk-or-\u2026",ariaLabel:"OpenRouter API key",onKeyDown:o=>{o.key==="Enter"?(o.preventDefault(),this.saveKey()):o.key==="Escape"&&(o.stopPropagation(),this.cancelKeyEntry())}}}),this.err?hn({class:"model-err"},`\u2717 ${this.err}`):null,hn({class:"key-entry-acts"},qc({class:"pill",onClick:o=>{o.stopPropagation(),this.cancelKeyEntry()}},"Cancel"),qc({class:["pill","on"],onClick:o=>{o.stopPropagation(),this.saveKey()}},this.busy?"Saving\u2026":"Save")),hn({class:"key-entry-note"},"Written to .env in this project (gitignored). Used only via OpenRouter."))}};document.title="Claude Mirror";var jxe=new Ure;new X5t({root:new qre({title:"Claude Mirror",pills:[jxe],overlays:[()=>jxe.watchdogView()],onPollTick:()=>{jxe.tickState()}}),id:"app"});
|
|
1379
|
+
`)}applyUser(o){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#u(a,o.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#i(a.text));return}this.#s({id:++this.#e,role:"user",text:o.text,thinking:"",tools:[]})}applyAssistant(o){let a=this.#s({id:++this.#e,role:"assistant",text:o.text,thinking:o.thinking,tools:o.tools.map(g=>({...g,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#i(""),a.turnCopy.setText(this.#c()),this.#f(a,o.text),this.#h(),o.live)for(let g of o.tools)ovn.has(g.name)&&this.openTools.add(g.id)}#f(o,a){let d=uSt(a);d.some(g=>g.kind==="bulb")&&(o.body=d.map(g=>{if(g.kind==="md")return g.text;let m=new Qre(g.source,`embed-${this.#t++}`);return this.bulbEmbeds.push(m),m}))}#h(){let o=this.bulbEmbeds.map(g=>g.name),a=aSt(o),d=lSt(o);this.#r=new Set(this.bulbEmbeds.filter((g,m)=>a[m])),this.bulbEmbeds.forEach((g,m)=>{g.setChainPosition(d[m]),g.setMounted(!a[m]||this.expandedEmbeds.has(g.key))})}#a(o){Rxe(this.expandedEmbeds,o),this.#h(),this.update()}applyToolResult(o){let a=this.#o(o.id);a&&(a.result=o.content,a.isError=o.isError,a.digest=o.digest)}applyFork(o){let a=this.#l(o.events);this.#s({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:o.count,sub:a}})}#l(o){let a=[];for(let d of o)if(d.type==="user"){let g=a[a.length-1];g&&g.role==="user"?this.#u(g,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(g=>({...g,isError:!1}))});else if(d.type==="tool_result"){let g=a.flatMap(m=>m.tools).find(m=>m.id===d.id);g&&(g.result=d.content,g.isError=d.isError,g.digest=d.digest)}return a}#o(o){return this.messages.flatMap(a=>a.tools).find(a=>a.id===o)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#n&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let o=this.scrollEl;o&&(this.#n=o.scrollHeight-(o.scrollTop+o.clientHeight)<50)}view(){return hn({class:"messages",onScroll:()=>this.onScroll(),onMounted:o=>{this.scrollEl=o}},this.parent.ready?this.renderMessages():hn({class:"note"},"Connecting\u2026"))}renderMessages(){let o=[],a=-1;for(let d of this.messages)d.role==="user"||o.length===0?(d.role==="user"&&a++,o.push({idx:Math.max(0,a),msgs:[d]})):o[o.length-1].msgs.push(d);return o.flatMap((d,g)=>this.renderTurn(d.msgs,d.idx,g===o.length-1))}renderTurn(o,a,d){if(this.parent.prose){let T=o.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),L=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let P=D.role==="user"?D.copy:D===L?D.turnCopy:void 0;return this.bubble(D,a,P??null)})}let g=o.filter(T=>T.role==="assistant");if(d||g.length<2)return o.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let m=this.expandedTurns.has(a),v=g.slice(0,-1),w=g[g.length-1],I=o.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of o)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),m)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(w,a)),I}#p(o,a,d,g,m,v){let w=d.has(g);return hn({class:["bubble","assistant",Dye(a)],key:o},hn({class:"turn-summary",onClick:()=>{Rxe(d,g),this.update()}},Oxe(w),ys({class:"turn-summary-text"},m)),v??null)}forkStub(o,a){let d=o.fork,g=this.expandedForks.has(o.id)?hn({class:"fork-body"},d.sub.map(v=>this.bubble(v,a,void 0,!1))):void 0,m=[ivn(),` ${d.count} message${d.count===1?"":"s"} on an abandoned branch`];return this.#p(`fork-${o.id}`,a,this.expandedForks,o.id,m,g)}turnSummary(o,a){return this.#p(`summary-${a}`,a,this.expandedTurns,a,lvn(o))}bubble(o,a,d=o.copy,g=!0){let m=this.parent.prose,v=o.role==="assistant"&&!o.text&&!o.thinking&&o.tools.length>0;return hn({class:["bubble",o.role,v?"tools-only":"",g?Dye(a):""],key:o.id},!m&&o.thinking?K5t({class:"thinking"},W5t("thinking"),I7(o.thinking)):null,this.#g(o),m?null:o.tools.map(w=>this.tool(w)),d?d.view():null)}#g(o){return o.body?o.body.flatMap((a,d)=>typeof a=="string"?a.trim()?[this.#d(`md-${o.id}-${d}`,$re(a))]:[]:this.#b(a)):o.text?this.#d(`md-${o.id}`,o.role==="user"?cSt(o):$re(o.text)):null}#b(o){if(!this.#r.has(o))return[o.view()];let a=this.expandedEmbeds.has(o.key),d=hn({class:"md",key:`fold-${o.key}`},hn({class:"bulb-fold",onClick:()=>this.#a(o.key)},Oxe(a),ys({class:"bulb-fold-text"},`${o.name??"bulb"} \u2014 Version ${o.chainPosition}`),ys({class:"bulb-fold-app"},"\u{1F4A1}")));return a?[d,o.view()]:[d]}#d(o,a){return hn({class:"md",key:o,onMounted:a})}tool(o){let a=this.openTools.has(o.id),d=cvn(o.name,o.input),g=dSt(o),m=d?o6t(d,this.parent.cwd):rvn(nvn(o.input));return hn({class:["tool",o.isError?"err":""]},hn({class:"tool-head"},hn({class:"tool-label"},ys({class:"tool-name",...o.name.startsWith("mcp__")?{title:o.name}:{}},Pxe(o.name)),m?d?uM({class:"tool-sum link",title:d,onClick:v=>{v.preventDefault(),tb.server.openFile(d)}},m):ys({class:"tool-sum"},m):null),o.result===void 0?ys({class:"tool-run"},"\u2026"):null),o.result!==void 0?hn({class:["tool-digest",a?"open":""],onClick:()=>{Rxe(this.openTools,o.id),this.update()}},Oxe(a),(g?avn(g):o.digest)||"(no output)"):null,a?hn({class:"tool-card"},this.toolBody(o),o.result!==void 0&&(o.isError||!(g||hSt(o).length&&o.result.trim()===o.digest))?I7({class:"tool-out"},o.result.slice(0,4e3)):null):null)}toolBody(o){let a=dSt(o);if(a)return hn({class:"todo-list"},a.map(m=>hn({class:["todo",m.status??""]},ys({class:"todo-icon"},m.status==="completed"?"\u2714":m.status==="in_progress"?"\u25FC":"\u25FB"),ys({class:"todo-text"},m.content??""))));let d=hSt(o);if(d.length)return hn({class:"tool-in"},d.map(fSt));let g=uvn(o);if(!g){let m=Object.entries(o.input??{});return m.length?hn({class:"tool-in"},m.map(([v,w])=>hn({class:"tool-field"},ys({class:"tool-key"},`${v}:`),typeof w=="string"&&pSt(w)?fSt(w):I7({class:"tool-val"},typeof w=="string"?w:JSON.stringify(w,null,2))))):null}return hn({class:"diff"},...g.flatMap((m,v)=>[g.length>1?hn({class:"diff-step"},`edit ${v+1}/${g.length}`):null,m.old?I7({class:"diff-old"},String(m.old)):null,m.new?I7({class:"diff-new"},String(m.new)):null]))}};var qre=class extends Ia{ready=!1;cwd="";sessionId="";sessionPicker=new Gee;tokenPill=new qee;bulbsPill=new Jee;prosePill=new Uee;messageList=new Gre;tokens={in:0,out:0,cached:0,cacheCreate:0};working=!1;latestModel=null;prose=!1;ownPid=0;pills;#e=0;#t=!1;#r=!1;#n;#i;#s;constructor(o){super(),this.pills=o.pills,this.#n=o.title,this.#i=o.overlays??[],this.#s=o.onPollTick}onAttached(){this.#r||(this.#r=!0,this.init())}closePopups(o){this.sessionPicker!==o&&this.sessionPicker.close(),this.bulbsPill!==o&&this.bulbsPill.close();for(let a of this.pills)a!==o&&a.close?.()}async init(){let o=await tb.server.info();this.cwd=o.cwd,this.ownPid=o.pid??0,this.ready=!0,this.updateTitle(),this.update(),this.pump()}updateTitle(){let o=Bye(this.cwd),a=this.sessionPicker.currentPreview(),d=Hee(a,40);document.title=d&&o?`${d} \u2014 ${o}`:d||(o?`${this.#n} \u2014 ${o}`:this.#n)}pump(){if(this.#t)return;this.#t=!0;let o=async()=>{try{let{events:a,cursor:d,working:g,latestModel:m}=await tb.server.poll(this.#e);this.#e=d;for(let I of a)this.apply(I);let v=g!==this.working;this.working=g;let w=m!==this.latestModel;this.latestModel=m??null,(a.length||v||w)&&this.update(),a.length&&this.messageList.scrollSoon(),this.#s?.()}catch(a){console.error("[mirror] poll failed",a)}setTimeout(o,600)};o()}apply(o){switch(o.type){case"cleared":this.messageList.clear(),this.tokens={in:0,out:0,cached:0,cacheCreate:0};break;case"session":this.sessionId=o.sessionId,this.updateTitle();break;case"user":this.messageList.applyUser(o);break;case"assistant":this.messageList.applyAssistant(o);break;case"tool_result":this.messageList.applyToolResult(o);break;case"fork":this.messageList.applyFork(o);break;case"usage":this.tokens={in:o.in,out:o.out,cached:o.cached,cacheCreate:o.cacheCreate};break}}view(){return hn({class:"app"},this.messageList.view(),...this.#i.map(o=>o()),this.statusbar())}statusbar(){return hn({class:"statusbar"},hn({class:"statusbar-actions"},this.prosePill.view(),...this.pills.map(o=>o.view()),this.tokenPill.view(),this.sessionPicker.view(),this.bulbsPill.view()))}};var fvn=[[0,"Low"],[1,"Med"],[2,"High"]],hvn=1,Jre=s=>s?.message??String(s),dvn=()=>pC({viewBox:"0 0 24 24",width:"15",height:"15",class:"anthropic-glyph",fill:"currentColor"},dC({d:"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z"})),gSt=s=>s===!0?"ok":s===!1?"bad":"warn",Ure=class extends S7{models=[];hasKey=!0;loaded=!1;current={active:!1,managed:!1,model:null,port:null,provider:null,routedModel:null,caching:"unknown",reasoning:null,externalBaseUrl:null};busy=!1;err=null;enteringKey=!1;keyDraft="";probes={};probing=null;openDiag=null;infoOpen=!1;ticking=!1;keepOpenSelector=".model-wrap";filterId="model-filter";listSelector="#model-list";filterNoun="model";search(){return Promise.resolve([])}modeToggle(){return null}onActivate(o){let a=this.rows()[o];a&&(a.kind==="default"?this.clear():a.kind==="unlock"?this.beginKeyEntry():this.set(a.id))}onAttached(){this.refresh()}async refresh(){try{let[o,a]=await Promise.all([tb.server.listSwitchModels(),tb.server.switchState()]);this.models=o.models,this.hasKey=o.hasKey,this.current=a,this.err=null}catch(o){this.err=Jre(o),console.error("[mirror] switcher refresh failed",o)}finally{this.loaded=!0,this.update()}}async tickState(){if(!(this.ticking||this.enteringKey)){this.ticking=!0;try{let o=await tb.server.switchState(),a=o.active!==this.current.active||o.model!==this.current.model||o.caching!==this.current.caching||o.routedModel!==this.current.routedModel||o.externalBaseUrl!==this.current.externalBaseUrl||o.provider!==this.current.provider||o.reasoning!==this.current.reasoning;this.current=o,a&&this.update()}catch{}finally{this.ticking=!1}}}rows(){let o=[];if(!this.hasKey)o.push({kind:"unlock"});else{let a=this.filter.trim().toLowerCase(),d=null;for(let g of this.models){if(a&&!g.label.toLowerCase().includes(a)&&!g.id.toLowerCase().includes(a))continue;let m={kind:"model",id:g.id,label:g.label,active:this.current.active&&this.current.model===g.id};m.active?d=m:o.push(m)}d&&o.push(d)}return o.push({kind:"default",active:!this.current.active&&!this.current.externalBaseUrl}),o}activeIndex(){let o=this.rows(),a=o.findIndex(d=>d.active);return a<0?Math.max(0,o.length-1):a}async show(){this.beginOpen(),this.refreshList(this.activeIndex()),this.focusFilter(),await this.refresh(),this.refreshList(this.activeIndex()),this.armClose()}onClosed(){super.onClosed(),this.enteringKey=!1,this.keyDraft="",this.openDiag=null,this.infoOpen=!1,this.err=null}async probe(o){if(!this.probing){this.probing=o,this.update();try{this.probes[o]=await tb.server.probeModel({model:o})}catch(a){this.probes[o]={ok:!1,error:Jre(a)}}this.probing=null,this.update()}}showDiag(o){this.openDiag=o,this.update()}closeDiag(){this.openDiag=null,this.update(),this.focusFilter()}label(o){return o?this.models.find(a=>a.id===o)?.label||o.split("/").pop()||o:"Anthropic"}async runBusy(o,a){this.busy=!0,this.err=null,this.update();try{let d=await o();if(d?.ok===!1)return this.err=d.error||a,this.busy=!1,this.update(),!1}catch(d){return this.err=Jre(d),this.busy=!1,this.update(),!1}return this.busy=!1,!0}async set(o){await this.runBusy(()=>tb.server.setSwitchModel({model:o}),"failed to set model")&&(await this.refresh(),this.close())}async clear(){await this.runBusy(()=>tb.server.clearSwitchModel(),"failed to revert")&&(await this.refresh(),this.close())}get reasoningSel(){return this.current.reasoning??hvn}set reasoningSel(o){this.setReasoning(o)}async setReasoning(o){if(this.current.reasoning!==o){this.current={...this.current,reasoning:o},this.update();try{let a=await tb.server.setReasoning({level:o});a?.ok===!1&&(this.err=a.error||"failed to set reasoning",this.update())}catch(a){this.err=Jre(a),this.update()}}}beginKeyEntry(){this.enteringKey=!0,this.keyDraft="",this.update(),setTimeout(()=>document.getElementById("or-key-input")?.focus())}cancelKeyEntry(){this.enteringKey=!1,this.keyDraft="",this.update(),this.focusFilter()}async saveKey(){let o=this.keyDraft.trim();o&&await this.runBusy(()=>tb.server.saveOpenRouterKey({key:o}),"failed to save key")&&(this.enteringKey=!1,this.keyDraft="",await this.refresh(),this.refreshList(this.activeIndex()))}get overridden(){return this.current.active}get uncached(){return this.current.active&&this.current.caching==="uncached"}watchdogView(){return this.uncached?hn({class:["model-banner","warn"]},ys({class:"model-banner-text"},`${this.label(this.current.model)} was served uncached via OpenRouter \u2014 the full context is re-billed each turn. Probe the model to pin a provider that caches, or pick a different one.`)):null}head(){return this.current.active?hn({class:"model-head"},qc({class:"model-info-toggle",onClick:o=>{o.stopPropagation(),this.infoOpen=!0,this.update()}},"Routing info"),hn({class:"reasoning-ctl",title:"Reasoning sets the route\u2019s base thinking effort."},ys({class:"reasoning-label"},"reasoning"),this.reasoningSelect())):this.current.externalBaseUrl?hn({class:"model-head"},ys({class:["model-route","warn"]},"Custom base URL set elsewhere \u2014 \u201CAnthropic (native)\u201D clears it")):null}reasoningSelect(){return t6t({target:this,prop:()=>this.reasoningSel,attrs:{class:"reasoning-select",onClick:o=>o.stopPropagation()},options:fvn.map(([o,a])=>({value:o,label:a}))})}infoView(){let o=this.label(this.current.model),a=()=>{this.infoOpen=!1,this.update()};return hn({class:"picker model-picker diag-mode",tabIndex:0,onMounted:d=>d.focus(),onKeyDown:d=>{d.key==="Escape"&&(d.stopPropagation(),a())}},hn({class:"diag-head"},ys({class:"diag-name"},"Routing"),hn({class:"diag-head-acts"},qc({class:"diag-back",title:"Back to the model list",onClick:d=>{d.stopPropagation(),a()}},"\u2190 models"))),hn({class:"diag-body"},hn({class:"diag-line"},`You are currently routing to ${o} via OpenRouter, not your Anthropic subscription.`),hn({class:"diag-line"},"The route is shared by every Claude Code conversation scoped to this project."),hn({class:"diag-line"},"Switching to or from Anthropic toggles the proxy, so for existing conversations you\u2019ll need to:"),hn({class:["diag-line","diag-bullet"]},"\u2022 Relaunch the Claude CLI"),hn({class:["diag-line","diag-bullet"]},"\u2022 or, in the VS Code extension, run \u201CDeveloper: Restart Extension Host\u201D (Cmd/Ctrl+Shift+P).")))}view(){let o=this.current.active,a=this.uncached,d=!o&&!!this.current.externalBaseUrl,g=this.label(this.current.model),m=a?`${g} via OpenRouter, uncached \u2014 full context re-billed each turn. Click to switch or revert.`:o?`claude \u2192 ${this.current.model}: its Anthropic API is proxied to OpenRouter. Click to switch or revert.`:d?`Custom ANTHROPIC_BASE_URL set outside this tool (${this.current.externalBaseUrl}). Click to manage.`:"Anthropic (native) \u2014 click to proxy claude\u2019s Anthropic API to an OpenRouter model.",v=o?g:d?"custom route":dvn();return hn({class:"model-wrap"},qc({class:["pill",o||d?"on":"glyph",a?"warn":""],title:m,ariaLabel:o||d?void 0:"Anthropic (native)",onClick:w=>{w.stopPropagation(),this.open?this.close():this.show()}},v),this.open?this.popup():null)}popup(){if(this.enteringKey)return this.keyView();if(this.infoOpen)return this.infoView();if(this.openDiag)return this.diagView(this.openDiag);let o=this.rows(),a=this.loaded&&this.hasKey&&!this.models.length;return hn({class:"picker model-picker"},this.head(),hn({id:"model-list",class:"picker-list",onScroll:()=>this.onListScroll()},o.map((d,g)=>this.row(d,g)),a?hn({class:"picker-empty"},this.err?`Couldn't load models: ${this.err}`:"No OpenRouter models in the catalog right now."):null),this.err&&!a?hn({class:"model-err"},`\u2717 ${this.err}`):null,this.hasKey&&this.models.length?this.filterBox(this.models.length,"model"):null)}row(o,a){let d=()=>{this.highlighted!==a&&(this.highlighted=a,this.update())},g=["picker-row",a===this.highlighted?"active":""];if(o.kind==="unlock")return hn({class:g,onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},"+ Add OpenRouter key to unlock models\u2026")));if(o.kind==="default")return hn({class:[...g,o.active?"current":"","model-row","model-default-row"],title:"Your normal claude (subscription / API) \u2014 clears the override",onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},"Anthropic (native)"),hn({class:"model-trailing"},o.active?this.activeBadge():null)));let m=o.id;return hn({class:[...g,o.active?"current":"","model-row"],title:m,onMouseEnter:d,onClick:()=>this.onActivate(a)},hn({class:"picker-row-main"},ys({class:"picker-dot"}),ys({class:"picker-preview"},o.label),hn({class:"model-trailing"},o.active?this.activeBadge():null,this.statusButton(m))))}activeBadge(){return ys({class:["model-btn","model-active"],title:"Active \u2014 bare claude routes here now"},"active")}statusButton(o){if(this.probing===o)return ys({class:["model-btn","model-status","probing"]},"probing\u2026");let a=this.probes[o];if(!a)return qc({class:["model-btn","model-status","probe"],title:"Check if it\u2019s callable and which provider caches \u2014 pins the caching one.",onClick:g=>{g.stopPropagation(),this.probe(o)}},"probe");let d=this.badge(a);return qc({class:["model-btn","model-status",d.cls],title:`${d.title} \xB7 click for details`,onClick:g=>{g.stopPropagation(),this.showDiag(o)}},d.text)}badge(o){if(!o.ok||o.callable===!1)return{text:"error",cls:"bad",title:o.error||"unavailable"};let a=o.provider?` on ${o.provider}`:"",d=o.cache?.works,g=gSt(d);return d===!0?{text:"cached",cls:g,title:`caching confirmed${a} \u2014 ${o.cache?.note||""}`}:d===!1?{text:"uncached",cls:g,title:`no caching${a} \u2014 ${o.cache?.note||""}`}:{text:"cache?",cls:g,title:`${o.cache?.note||"caching unconfirmed"}${a}`}}diagView(o){return hn({class:"picker model-picker diag-mode",tabIndex:0,onMounted:a=>a.focus(),onKeyDown:a=>{a.key==="Escape"&&(a.stopPropagation(),this.closeDiag())}},hn({class:"diag-head"},ys({class:"diag-name"},this.label(o)),hn({class:"diag-head-acts"},qc({class:"diag-back",title:"Re-run the probe",disabled:this.probing===o,onClick:a=>{a.stopPropagation(),this.probe(o)}},this.probing===o?"probing\u2026":"probe again"),qc({class:"diag-back",title:"Back to the model list",onClick:a=>{a.stopPropagation(),this.closeDiag()}},"\u2190 models"))),hn({class:"diag-body"},...this.diagLines(o)))}diagLines(o){let a=this.probes[o];if(!a)return[hn({class:["diag-line","muted"]},"Not probed yet \u2014 close this and hit probe.")];let d=[];if(!a.ok||a.callable===!1)d.push(hn({class:["diag-line","bad"]},`\u2717 Not callable${a.status?` (HTTP ${a.status})`:""}: ${a.error||"unknown error"}`)),a.restriction&&d.push(hn({class:["diag-line","warn"]},`\u26A0 ${a.restriction}`));else{d.push(hn({class:["diag-line","ok"]},`\u2713 Callable${a.ms?` \xB7 ${a.ms}ms`:""}${a.sample?` \xB7 replied \u201C${a.sample}\u201D`:""}`)),a.provider&&d.push(hn({class:["diag-line","muted"]},`Served by ${a.provider} this probe \u2014 the proxy will pin CC's turns to it when caching is confirmed.`));let g=a.cache;if(g){let m=gSt(g.works),v=g.works===!0?"\u2713 Caching works":g.works===!1?"\u2717 No caching":"~ Caching unconfirmed",w=a.provider?` on ${a.provider}`:"";d.push(hn({class:["diag-line",m]},`${v}${w} \u2014 ${g.note}`)),g.works!==!0&&d.push(hn({class:["diag-line","muted"]},"Without prompt caching every turn re-bills the whole context \u2014 costly over a long session."))}}return d.push(hn({class:["diag-line","muted","small"]},"Caching on OpenRouter is per-provider; the proxy pins the provider a probe confirms so CC keeps caching. A probe makes two small test calls.")),d}keyView(){return hn({class:"picker model-picker key-entry",tabIndex:0,onKeyDown:o=>{o.key==="Escape"&&(o.stopPropagation(),this.cancelKeyEntry())}},hn({class:"key-entry-head"},"Paste your OpenRouter API key"),Pee({target:this,prop:()=>this.keyDraft,id:"or-key-input",attrs:{class:"bulb-filter",placeholder:"sk-or-\u2026",ariaLabel:"OpenRouter API key",onKeyDown:o=>{o.key==="Enter"?(o.preventDefault(),this.saveKey()):o.key==="Escape"&&(o.stopPropagation(),this.cancelKeyEntry())}}}),this.err?hn({class:"model-err"},`\u2717 ${this.err}`):null,hn({class:"key-entry-acts"},qc({class:"pill",onClick:o=>{o.stopPropagation(),this.cancelKeyEntry()}},"Cancel"),qc({class:["pill","on"],onClick:o=>{o.stopPropagation(),this.saveKey()}},this.busy?"Saving\u2026":"Save")),hn({class:"key-entry-note"},"Written to .env in this project (gitignored). Used only via OpenRouter."))}};document.title="Claude Mirror";var jxe=new Ure;new X5t({root:new qre({title:"Claude Mirror",pills:[jxe],overlays:[()=>jxe.watchdogView()],onPollTick:()=>{jxe.tickState()}}),id:"app"});
|
|
1380
1380
|
/*! Bundled license information:
|
|
1381
1381
|
|
|
1382
1382
|
dompurify/dist/purify.es.mjs:
|
|
@@ -1258,6 +1258,9 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1258
1258
|
user-select: text;
|
|
1259
1259
|
}
|
|
1260
1260
|
.tool-digest:hover { color: var(--fg); }
|
|
1261
|
+
/* Expanded = nothing readable hidden: the digest wraps to its full text (it may be the whole
|
|
1262
|
+
result — suppressed patch one-liners rely on this). */
|
|
1263
|
+
.tool-digest.open { white-space: normal; }
|
|
1261
1264
|
|
|
1262
1265
|
/* Expanded input/output card beneath the digest row — additive only, the rows above keep their
|
|
1263
1266
|
collapsed look. First child sheds its top divider (nothing above it inside the card). */
|
package/dist/agents/pi/client.js
CHANGED
|
@@ -1376,7 +1376,7 @@ ${JEt(m)}`};let v=qEt(d.code,{jsxImportSource:g.ts?.jsxImportSource});if(v.error
|
|
|
1376
1376
|
|
|
1377
1377
|
`)}stickToBottomNextRender(){this.#n=!0}#u(o,a){o.segments=[...o.segments??[o.text],a],o.text=o.segments.join(`
|
|
1378
1378
|
|
|
1379
|
-
`)}applyUser(o){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#u(a,o.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#i(a.text));return}this.#s({id:++this.#e,role:"user",text:o.text,thinking:"",tools:[]})}applyAssistant(o){let a=this.#s({id:++this.#e,role:"assistant",text:o.text,thinking:o.thinking,tools:o.tools.map(g=>({...g,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#i(""),a.turnCopy.setText(this.#c()),this.#f(a,o.text),this.#h(),o.live)for(let g of o.tools)W4n.has(g.name)&&this.openTools.add(g.id)}#f(o,a){let d=eSt(a);d.some(g=>g.kind==="bulb")&&(o.body=d.map(g=>{if(g.kind==="md")return g.text;let m=new $re(g.source,`embed-${this.#t++}`);return this.bulbEmbeds.push(m),m}))}#h(){let o=this.bulbEmbeds.map(g=>g.name),a=tSt(o),d=nSt(o);this.#r=new Set(this.bulbEmbeds.filter((g,m)=>a[m])),this.bulbEmbeds.forEach((g,m)=>{g.setChainPosition(d[m]),g.setMounted(!a[m]||this.expandedEmbeds.has(g.key))})}#a(o){Dxe(this.expandedEmbeds,o),this.#h(),this.update()}applyToolResult(o){let a=this.#o(o.id);a&&(a.result=o.content,a.isError=o.isError,a.digest=o.digest)}applyFork(o){let a=this.#l(o.events);this.#s({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:o.count,sub:a}})}#l(o){let a=[];for(let d of o)if(d.type==="user"){let g=a[a.length-1];g&&g.role==="user"?this.#u(g,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(g=>({...g,isError:!1}))});else if(d.type==="tool_result"){let g=a.flatMap(m=>m.tools).find(m=>m.id===d.id);g&&(g.result=d.content,g.isError=d.isError,g.digest=d.digest)}return a}#o(o){return this.messages.flatMap(a=>a.tools).find(a=>a.id===o)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#n&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let o=this.scrollEl;o&&(this.#n=o.scrollHeight-(o.scrollTop+o.clientHeight)<50)}view(){return Kr({class:"messages",onScroll:()=>this.onScroll(),onMounted:o=>{this.scrollEl=o}},this.parent.ready?this.renderMessages():Kr({class:"note"},"Connecting\u2026"))}renderMessages(){let o=[],a=-1;for(let d of this.messages)d.role==="user"||o.length===0?(d.role==="user"&&a++,o.push({idx:Math.max(0,a),msgs:[d]})):o[o.length-1].msgs.push(d);return o.flatMap((d,g)=>this.renderTurn(d.msgs,d.idx,g===o.length-1))}renderTurn(o,a,d){if(this.parent.prose){let T=o.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),L=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let j=D.role==="user"?D.copy:D===L?D.turnCopy:void 0;return this.bubble(D,a,j??null)})}let g=o.filter(T=>T.role==="assistant");if(d||g.length<2)return o.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let m=this.expandedTurns.has(a),v=g.slice(0,-1),A=g[g.length-1],I=o.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of o)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),m)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(A,a)),I}#p(o,a,d,g,m,v){let A=d.has(g);return Kr({class:["bubble","assistant",Iye(a)],key:o},Kr({class:"turn-summary",onClick:()=>{Dxe(d,g),this.update()}},Mxe(A),Ro({class:"turn-summary-text"},m)),v??null)}forkStub(o,a){let d=o.fork,g=this.expandedForks.has(o.id)?Kr({class:"fork-body"},d.sub.map(v=>this.bubble(v,a,void 0,!1))):void 0,m=[z4n(),` ${d.count} message${d.count===1?"":"s"} on an abandoned branch`];return this.#p(`fork-${o.id}`,a,this.expandedForks,o.id,m,g)}turnSummary(o,a){return this.#p(`summary-${a}`,a,this.expandedTurns,a,Z4n(o))}bubble(o,a,d=o.copy,g=!0){let m=this.parent.prose,v=o.role==="assistant"&&!o.text&&!o.thinking&&o.tools.length>0;return Kr({class:["bubble",o.role,v?"tools-only":"",g?Iye(a):""],key:o.id},!m&&o.thinking?$5t({class:"thinking"},H5t("thinking"),I7(o.thinking)):null,this.#g(o),m?null:o.tools.map(A=>this.tool(A)),d?d.view():null)}#g(o){return o.body?o.body.flatMap((a,d)=>typeof a=="string"?a.trim()?[this.#d(`md-${o.id}-${d}`,Pre(a))]:[]:this.#b(a)):o.text?this.#d(`md-${o.id}`,o.role==="user"?ZIt(o):Pre(o.text)):null}#b(o){if(!this.#r.has(o))return[o.view()];let a=this.expandedEmbeds.has(o.key),d=Kr({class:"md",key:`fold-${o.key}`},Kr({class:"bulb-fold",onClick:()=>this.#a(o.key)},Mxe(a),Ro({class:"bulb-fold-text"},`${o.name??"bulb"} \u2014 Version ${o.chainPosition}`),Ro({class:"bulb-fold-app"},"\u{1F4A1}")));return a?[d,o.view()]:[d]}#d(o,a){return Kr({class:"md",key:o,onMounted:a})}tool(o){let a=this.openTools.has(o.id),d=Y4n(o.name,o.input),g=sSt(o),m=d?V5t(d,this.parent.cwd):U4n(J4n(o.input));return Kr({class:["tool",o.isError?"err":""]},Kr({class:"tool-head"},Kr({class:"tool-label"},Ro({class:"tool-name",...o.name.startsWith("mcp__")?{title:o.name}:{}},Bxe(o.name)),m?d?sM({class:"tool-sum link",title:d,onClick:v=>{v.preventDefault(),tb.server.openFile(d)}},m):Ro({class:"tool-sum"},m):null),o.result===void 0?Ro({class:"tool-run"},"\u2026"):null),o.result!==void 0?Kr({class:"tool-digest",onClick:()=>{Dxe(this.openTools,o.id),this.update()}},Mxe(a),(g?V4n(g):o.digest)||"(no output)"):null,a?Kr({class:"tool-card"},this.toolBody(o),o.result!==void 0&&(
|
|
1379
|
+
`)}applyUser(o){let a=this.messages[this.messages.length-1];if(a&&a.role==="user"){this.#u(a,o.text),a.copy?a.copy.setText(a.text):a.text&&(a.copy=this.#i(a.text));return}this.#s({id:++this.#e,role:"user",text:o.text,thinking:"",tools:[]})}applyAssistant(o){let a=this.#s({id:++this.#e,role:"assistant",text:o.text,thinking:o.thinking,tools:o.tools.map(g=>({...g,isError:!1}))}),d=this.messages[this.messages.length-2];if(a.turnCopy=d?.role==="assistant"&&d.turnCopy||this.#i(""),a.turnCopy.setText(this.#c()),this.#f(a,o.text),this.#h(),o.live)for(let g of o.tools)W4n.has(g.name)&&this.openTools.add(g.id)}#f(o,a){let d=eSt(a);d.some(g=>g.kind==="bulb")&&(o.body=d.map(g=>{if(g.kind==="md")return g.text;let m=new $re(g.source,`embed-${this.#t++}`);return this.bulbEmbeds.push(m),m}))}#h(){let o=this.bulbEmbeds.map(g=>g.name),a=tSt(o),d=nSt(o);this.#r=new Set(this.bulbEmbeds.filter((g,m)=>a[m])),this.bulbEmbeds.forEach((g,m)=>{g.setChainPosition(d[m]),g.setMounted(!a[m]||this.expandedEmbeds.has(g.key))})}#a(o){Dxe(this.expandedEmbeds,o),this.#h(),this.update()}applyToolResult(o){let a=this.#o(o.id);a&&(a.result=o.content,a.isError=o.isError,a.digest=o.digest)}applyFork(o){let a=this.#l(o.events);this.#s({id:++this.#e,role:"fork",text:"",thinking:"",tools:[],fork:{count:o.count,sub:a}})}#l(o){let a=[];for(let d of o)if(d.type==="user"){let g=a[a.length-1];g&&g.role==="user"?this.#u(g,d.text):a.push({id:++this.#e,role:"user",text:d.text,thinking:"",tools:[]})}else if(d.type==="assistant")a.push({id:++this.#e,role:"assistant",text:d.text,thinking:d.thinking,tools:d.tools.map(g=>({...g,isError:!1}))});else if(d.type==="tool_result"){let g=a.flatMap(m=>m.tools).find(m=>m.id===d.id);g&&(g.result=d.content,g.isError=d.isError,g.digest=d.digest)}return a}#o(o){return this.messages.flatMap(a=>a.tools).find(a=>a.id===o)}scrollSoon(){requestAnimationFrame(()=>{this.scrollEl&&this.#n&&(this.scrollEl.scrollTop=this.scrollEl.scrollHeight)})}onScroll(){let o=this.scrollEl;o&&(this.#n=o.scrollHeight-(o.scrollTop+o.clientHeight)<50)}view(){return Kr({class:"messages",onScroll:()=>this.onScroll(),onMounted:o=>{this.scrollEl=o}},this.parent.ready?this.renderMessages():Kr({class:"note"},"Connecting\u2026"))}renderMessages(){let o=[],a=-1;for(let d of this.messages)d.role==="user"||o.length===0?(d.role==="user"&&a++,o.push({idx:Math.max(0,a),msgs:[d]})):o[o.length-1].msgs.push(d);return o.flatMap((d,g)=>this.renderTurn(d.msgs,d.idx,g===o.length-1))}renderTurn(o,a,d){if(this.parent.prose){let T=o.filter(D=>D.role==="user"||D.role==="fork"||D.text||D.body),L=[...T].reverse().find(D=>D.role==="assistant"&&!!D.text);return T.map(D=>{if(D.role==="fork")return this.forkStub(D,a);let j=D.role==="user"?D.copy:D===L?D.turnCopy:void 0;return this.bubble(D,a,j??null)})}let g=o.filter(T=>T.role==="assistant");if(d||g.length<2)return o.map(T=>T.role==="fork"?this.forkStub(T,a):this.bubble(T,a));let m=this.expandedTurns.has(a),v=g.slice(0,-1),A=g[g.length-1],I=o.filter(T=>T.role==="user").map(T=>this.bubble(T,a));for(let T of o)T.role==="fork"&&I.push(this.forkStub(T,a));if(I.push(this.turnSummary(v,a)),m)for(let T of v)I.push(this.bubble(T,a));return I.push(this.bubble(A,a)),I}#p(o,a,d,g,m,v){let A=d.has(g);return Kr({class:["bubble","assistant",Iye(a)],key:o},Kr({class:"turn-summary",onClick:()=>{Dxe(d,g),this.update()}},Mxe(A),Ro({class:"turn-summary-text"},m)),v??null)}forkStub(o,a){let d=o.fork,g=this.expandedForks.has(o.id)?Kr({class:"fork-body"},d.sub.map(v=>this.bubble(v,a,void 0,!1))):void 0,m=[z4n(),` ${d.count} message${d.count===1?"":"s"} on an abandoned branch`];return this.#p(`fork-${o.id}`,a,this.expandedForks,o.id,m,g)}turnSummary(o,a){return this.#p(`summary-${a}`,a,this.expandedTurns,a,Z4n(o))}bubble(o,a,d=o.copy,g=!0){let m=this.parent.prose,v=o.role==="assistant"&&!o.text&&!o.thinking&&o.tools.length>0;return Kr({class:["bubble",o.role,v?"tools-only":"",g?Iye(a):""],key:o.id},!m&&o.thinking?$5t({class:"thinking"},H5t("thinking"),I7(o.thinking)):null,this.#g(o),m?null:o.tools.map(A=>this.tool(A)),d?d.view():null)}#g(o){return o.body?o.body.flatMap((a,d)=>typeof a=="string"?a.trim()?[this.#d(`md-${o.id}-${d}`,Pre(a))]:[]:this.#b(a)):o.text?this.#d(`md-${o.id}`,o.role==="user"?ZIt(o):Pre(o.text)):null}#b(o){if(!this.#r.has(o))return[o.view()];let a=this.expandedEmbeds.has(o.key),d=Kr({class:"md",key:`fold-${o.key}`},Kr({class:"bulb-fold",onClick:()=>this.#a(o.key)},Mxe(a),Ro({class:"bulb-fold-text"},`${o.name??"bulb"} \u2014 Version ${o.chainPosition}`),Ro({class:"bulb-fold-app"},"\u{1F4A1}")));return a?[d,o.view()]:[d]}#d(o,a){return Kr({class:"md",key:o,onMounted:a})}tool(o){let a=this.openTools.has(o.id),d=Y4n(o.name,o.input),g=sSt(o),m=d?V5t(d,this.parent.cwd):U4n(J4n(o.input));return Kr({class:["tool",o.isError?"err":""]},Kr({class:"tool-head"},Kr({class:"tool-label"},Ro({class:"tool-name",...o.name.startsWith("mcp__")?{title:o.name}:{}},Bxe(o.name)),m?d?sM({class:"tool-sum link",title:d,onClick:v=>{v.preventDefault(),tb.server.openFile(d)}},m):Ro({class:"tool-sum"},m):null),o.result===void 0?Ro({class:"tool-run"},"\u2026"):null),o.result!==void 0?Kr({class:["tool-digest",a?"open":""],onClick:()=>{Dxe(this.openTools,o.id),this.update()}},Mxe(a),(g?V4n(g):o.digest)||"(no output)"):null,a?Kr({class:"tool-card"},this.toolBody(o),o.result!==void 0&&(o.isError||!(g||iSt(o).length&&o.result.trim()===o.digest))?I7({class:"tool-out"},o.result.slice(0,4e3)):null):null)}toolBody(o){let a=sSt(o);if(a)return Kr({class:"todo-list"},a.map(m=>Kr({class:["todo",m.status??""]},Ro({class:"todo-icon"},m.status==="completed"?"\u2714":m.status==="in_progress"?"\u25FC":"\u25FB"),Ro({class:"todo-text"},m.content??""))));let d=iSt(o);if(d.length)return Kr({class:"tool-in"},d.map(rSt));let g=X4n(o);if(!g){let m=Object.entries(o.input??{});return m.length?Kr({class:"tool-in"},m.map(([v,A])=>Kr({class:"tool-field"},Ro({class:"tool-key"},`${v}:`),typeof A=="string"&&oSt(A)?rSt(A):I7({class:"tool-val"},typeof A=="string"?A:JSON.stringify(A,null,2))))):null}return Kr({class:"diff"},...g.flatMap((m,v)=>[g.length>1?Kr({class:"diff-step"},`edit ${v+1}/${g.length}`):null,m.old?I7({class:"diff-old"},String(m.old)):null,m.new?I7({class:"diff-new"},String(m.new)):null]))}};var Qre=class extends Ku{ready=!1;cwd="";sessionId="";sessionPicker=new Hee;tokenPill=new Qee;bulbsPill=new Gee;prosePill=new qee;messageList=new Hre;tokens={in:0,out:0,cached:0,cacheCreate:0};working=!1;latestModel=null;prose=!1;ownPid=0;pills;#e=0;#t=!1;#r=!1;#n;#i;#s;constructor(o){super(),this.pills=o.pills,this.#n=o.title,this.#i=o.overlays??[],this.#s=o.onPollTick}onAttached(){this.#r||(this.#r=!0,this.init())}closePopups(o){this.sessionPicker!==o&&this.sessionPicker.close(),this.bulbsPill!==o&&this.bulbsPill.close();for(let a of this.pills)a!==o&&a.close?.()}async init(){let o=await tb.server.info();this.cwd=o.cwd,this.ownPid=o.pid??0,this.ready=!0,this.updateTitle(),this.update(),this.pump()}updateTitle(){let o=Sye(this.cwd),a=this.sessionPicker.currentPreview(),d=Fee(a,40);document.title=d&&o?`${d} \u2014 ${o}`:d||(o?`${this.#n} \u2014 ${o}`:this.#n)}pump(){if(this.#t)return;this.#t=!0;let o=async()=>{try{let{events:a,cursor:d,working:g,latestModel:m}=await tb.server.poll(this.#e);this.#e=d;for(let I of a)this.apply(I);let v=g!==this.working;this.working=g;let A=m!==this.latestModel;this.latestModel=m??null,(a.length||v||A)&&this.update(),a.length&&this.messageList.scrollSoon(),this.#s?.()}catch(a){console.error("[mirror] poll failed",a)}setTimeout(o,600)};o()}apply(o){switch(o.type){case"cleared":this.messageList.clear(),this.tokens={in:0,out:0,cached:0,cacheCreate:0};break;case"session":this.sessionId=o.sessionId,this.updateTitle();break;case"user":this.messageList.applyUser(o);break;case"assistant":this.messageList.applyAssistant(o);break;case"tool_result":this.messageList.applyToolResult(o);break;case"fork":this.messageList.applyFork(o);break;case"usage":this.tokens={in:o.in,out:o.out,cached:o.cached,cacheCreate:o.cacheCreate};break}}view(){return Kr({class:"app"},this.messageList.view(),...this.#i.map(o=>o()),this.statusbar())}statusbar(){return Kr({class:"statusbar"},Kr({class:"statusbar-actions"},this.prosePill.view(),...this.pills.map(o=>o.view()),this.tokenPill.view(),this.sessionPicker.view(),this.bulbsPill.view()))}};var Gre=class extends Ku{get parent(){return this.ctx.parent}view(){let o=this.parent.latestModel;if(!o)return Ro({class:"model-wrap"});let a=o.includes("/")?o.slice(o.lastIndexOf("/")+1):o;return Ro({class:"model-wrap"},Ro({class:"token",title:`Model: ${o}`},a))}};document.title="Pi Mirror";new G5t({root:new Qre({title:"Pi Mirror",pills:[new Gre]}),id:"app"});
|
|
1380
1380
|
/*! Bundled license information:
|
|
1381
1381
|
|
|
1382
1382
|
dompurify/dist/purify.es.mjs:
|
|
@@ -1258,6 +1258,9 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1258
1258
|
user-select: text;
|
|
1259
1259
|
}
|
|
1260
1260
|
.tool-digest:hover { color: var(--fg); }
|
|
1261
|
+
/* Expanded = nothing readable hidden: the digest wraps to its full text (it may be the whole
|
|
1262
|
+
result — suppressed patch one-liners rely on this). */
|
|
1263
|
+
.tool-digest.open { white-space: normal; }
|
|
1261
1264
|
|
|
1262
1265
|
/* Expanded input/output card beneath the digest row — additive only, the rows above keep their
|
|
1263
1266
|
collapsed look. First child sheds its top divider (nothing above it inside the card). */
|
package/dist/index.js
CHANGED
|
@@ -326,7 +326,7 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
326
326
|
`),n=[];if(t.forEach((o,a)=>{let c=AS(o);c!==void 0&&n.push({id:c,idx:a})}),!n.length)return e==="latest"?r:"";let s=e==="latest"?n[n.length-1]:n.find(o=>o.id===e);if(!s)return"";let i=n.find(o=>o.idx>s.idx);return t.slice(s.idx,i?i.idx:t.length).join(`
|
|
327
327
|
`)}function Qa(r){try{return process.kill(r,0),!0}catch(e){return e.code==="EPERM"}}async function oi(r){await SS(_e(),{recursive:!0}),await ap(Va(r.pid),JSON.stringify(r))}async function dp(r,e){let t=Va(r);try{let n=JSON.parse(await op(t,"utf8"));if(n.denied===e)return;n.denied=e,await ap(t,JSON.stringify(n))}catch{}}async function yt(r){await Wa(Va(r)).catch(()=>{}),await Wa(rn(r)).catch(()=>{}),await Wa(Ya(r)).catch(()=>{})}async function J(r){let e;try{e=await vS(_e())}catch{return[]}let t=[];return await Promise.all(e.map(async s=>{if(!/^\d+\.json$/.test(s))return;let i=parseInt(s,10),o;try{o=JSON.parse(await op(ae.join(_e(),s),"utf8"))}catch{await yt(i);return}o&&typeof o.pid=="number"&&Qa(o.pid)?t.push(o):await yt(i)})),(r?t.filter(s=>s.agent==null&&Wd(s.file,r)):t).sort((s,i)=>s.startedAt-i.startedAt)}async function Xa(r,e){let t=z(r);return(await J()).find(n=>n.cwd!=null&&z(n.cwd)===t&&(e?n.agent===e:n.agent!=null))}async function bt(r){try{process.kill(r,"SIGTERM")}catch{}await yt(r)}function Xt(r,e){let t=z(e);return r.filter(n=>n.agent==null&&n.pid!==process.pid&&z(n.file)===t)}async function Za(r){let e=Xt(await J(),r);for(let t of e)await bt(t.pid);return e}function OS(r,e,t){(process.platform==="win32"?sp("cmd",["/c","start","","/b",r,...e],{cwd:t,stdio:"ignore",windowsHide:!0}):sp(r,e,{cwd:t,detached:!0,stdio:"ignore"})).unref()}function pp(){return ae.join(ae.dirname(xS(import.meta.url)),"index.js")}function PS(r,e={}){return{command:process.execPath,args:[pp(),...e.trust?["--trust"]:[],r,...e.open===!1?["--no-open"]:[]]}}function RS(r){return{command:process.execPath,args:[pp(),`agent:${r}`,"--no-open"]}}async function mp(r,e,t,n){let s=await r();if(s)return s;OS(e.command,e.args,t);let i=Date.now()+2e4;for(;Date.now()<i;){await TS(150);let o=await r();if(o)return o}throw new Error(`Launched ${n} but it did not register within 20s.`)}async function ai(r,e={}){let t=e.cwd??process.cwd(),n=ae.resolve(t,r);return await Za(n),mp(async()=>Xt(await J(),n)[0],PS(r,e),t,ae.basename(r))}async function hp(r,e=process.cwd()){return mp(()=>Xa(e,r),RS(r),e,`the ${r} mirror`)}var ip,ES,TS,Le=R(()=>{Xs();ip=1e6;ES=/^── run (\d+) ── /;TS=r=>new Promise(e=>setTimeout(e,r))});function Se(r){return r.length>5e4?r.slice(0,5e4)+`
|
|
328
328
|
\u2026[${r.length-5e4} more characters truncated]`:r}function $e(r){let e=r.replace(CS,"").split(`
|
|
329
|
-
`).map(i=>i.trim()).filter(Boolean),t=e[0];if(!t)return"";let n=t.length>
|
|
329
|
+
`).map(i=>i.trim()).filter(Boolean),t=e[0];if(!t)return"";let n=t.length>300?t.slice(0,300).trimEnd()+"\u2026":t,s=e.length-1;return s>0?`${n} (+${s} line${s===1?"":"s"})`:n}var CS,ci,li=R(()=>{CS=/\u001b\[[0-9;]*m/g;ci=(r,e)=>``});import{existsSync as NS,readdirSync as _S,statSync as IS}from"fs";import{join as LS}from"path";function ui(r){if(!NS(r))return[];let e;try{e=_S(r)}catch{return[]}let t=[];for(let n of e){if(!n.endsWith(".jsonl"))continue;let s=LS(r,n);try{let i=IS(s);i.isFile()&&t.push({sessionId:n.slice(0,-6),file:s,mtime:i.mtimeMs})}catch{}}return t}var ec=R(()=>{});var Zt,tc=R(()=>{Zt=class{ensureWaitSupport(){}}});import{openSync as $S,readSync as MS,closeSync as DS,statSync as BS,readdirSync as jS,readFileSync as FS}from"fs";import{join as fi}from"path";import{homedir as wp}from"os";function qS(r){return r.replace(/[^a-zA-Z0-9]/g,"-")}function di(r){let e=r?.type==="image"?r.source:void 0;return e?.type==="base64"&&e.data?ci(e.data,e.media_type):""}function rc(r){return typeof r=="string"?Se(r):Array.isArray(r)?r.map(e=>{if(typeof e=="string")return Se(e);let t=e;return typeof t?.text=="string"?Se(t.text):t?.content!=null?rc(t.content):di(t)}).join(`
|
|
330
330
|
`):r==null?"":Se(JSON.stringify(r))}function JS(r){let e=r.trim();return!!e&&vp.some(t=>t.test(e))}function KS(r){let e=r.trim();return e?vp.some(t=>{let n=t.exec(e);return!!n&&n.index===0}):!0}function HS(r){return r.replace(WS,"")}function VS(r){let e=/^<command-name>([\s\S]*?)<\/command-name>/.exec(r)?.[1]?.trim();if(!e)return;let t=/<command-args>([\s\S]*?)<\/command-args>/.exec(r)?.[1]?.trim();return t?`${e} ${t}`:e}function tt(r){let e=HS(r).trim();return e?VS(e)??(JS(e)?"":e):""}function nc(r){return r?.type==="text"&&typeof r.text=="string"?tt(r.text):""}function sc(r,e){if(typeof r=="string")return $e(r);if(!r||typeof r!="object")return $e(e);let t=r;if(t.type==="text"&&typeof t.file?.numLines=="number")return he(t.file.numLines,"line");if(t.type==="image"){let n=yp(t.file?.originalSize);return n?`image (${n})`:"image"}if(t.type==="pdf"){let n=yp(t.file?.originalSize);return n?`PDF (${n})`:"PDF"}if(t.type==="parts"&&typeof t.file?.count=="number")return he(t.file.count,"page");if(t.type==="file_unchanged")return"unchanged since last read";if(t.type==="create"&&typeof t.content=="string")return`created, ${he(t.content.split(`
|
|
331
331
|
`).length,"line")}`;if(t.mode==="content")return he(t.numLines??0,"line");if(t.mode==="count")return`${he(t.numMatches??0,"match","matches")} (${he(t.numFiles??0,"file")})`;if(t.mode==="files_with_matches")return he(t.numFiles??0,"file");if(Array.isArray(t.structuredPatch)){let n=0,s=0;for(let i of t.structuredPatch)for(let o of i?.lines??[])typeof o=="string"&&(o.startsWith("+")?n++:o.startsWith("-")&&s++);return`+${n} \u2212${s}`}return typeof t.stdout=="string"||typeof t.stderr=="string"?t.backgroundTaskId?"running in background":t.interrupted?"interrupted":$e(t.stdout||t.stderr||"")||"no output":typeof t.code=="number"&&typeof t.codeText=="string"?`${t.code} ${t.codeText}`:typeof t.searchCount=="number"?he(t.searchCount,"search","searches"):Array.isArray(t.matches)&&typeof t.total_deferred_tools=="number"?he(t.matches.length,"tool"):Array.isArray(t.newTodos)?he(t.newTodos.length,"todo"):typeof t.numFiles=="number"?he(t.numFiles,"file"):$e(e)}function nn(r){return!!r.isSidechain||!!r.isMeta}function YS(r){let e;try{e=$S(r,"r")}catch{return""}try{let n=65536;try{n=BS(r).size}catch{}let s=(l,f)=>{if(f<=0)return"";let u=Buffer.alloc(f),d=MS(e,u,0,f,l);return u.subarray(0,d).toString("utf8")},i=(l,f,u)=>{let d="";for(let p of l.split(`
|
|
332
332
|
`)){if(!p.includes(`"${f}"`))continue;let y;try{y=JSON.parse(p)}catch{continue}let m=y?.[u];y?.type===f&&typeof m=="string"&&m&&(d=m)}return d},o=s(0,Math.min(65536,n)),a=n>65536?s(n-65536,65536):"",c=i(a,"custom-title","customTitle")||i(o,"custom-title","customTitle")||i(a,"ai-title","aiTitle")||i(o,"ai-title","aiTitle");if(c)return c.replace(/\s+/g," ").trim().slice(0,200);for(let l of o.split(`
|
|
@@ -1284,7 +1284,7 @@ version: ${r}
|
|
|
1284
1284
|
${mk(e)}
|
|
1285
1285
|
|
|
1286
1286
|
${r.trim()}
|
|
1287
|
-
`}function ac(r){let e=t=>n=>r?`\x1B[${t}m${n}\x1B[0m`:n;return{lit:e("38;5;70"),litLink:e("4;38;5;70"),brand:e("38;5;135")}}async function _p(r){let e=Op(process.cwd());if("ambiguous"in e){let t=await hk(r,e.ambiguous);return t?Cp(r,t):gk(r,e.ambiguous)}return Cp(r,e.name)}async function Cp(r,e){let t,n;try{t=await hp(e)}catch(f){n=f instanceof Error?f.message:String(f)}let{lit:s,litLink:i,brand:o}=ac(process.stdout.isTTY===!0),a=[" Read the authoring skill before writing a bulb:",` \u2022 ${s("npx typebulb skill")} \u2014 assembles one SKILL.md`," \u2022 or, open its parts:",` ${s(oc())}`,` ${s(mi())}`],c=t?[" Agent mirror is live\u{1F4A1}",` ${s("\u25CF")} ${i(t.url)}`," Embedded bulbs render live here."," Agents:",` Reusable app/tool \u2192 write a ${s(".bulb.md")}`," Show something inline \u2192 embed a bulb"," background a wait for its render verdict
|
|
1287
|
+
`}function ac(r){let e=t=>n=>r?`\x1B[${t}m${n}\x1B[0m`:n;return{lit:e("38;5;70"),litLink:e("4;38;5;70"),brand:e("38;5;135")}}async function _p(r){let e=Op(process.cwd());if("ambiguous"in e){let t=await hk(r,e.ambiguous);return t?Cp(r,t):gk(r,e.ambiguous)}return Cp(r,e.name)}async function Cp(r,e){let t,n;try{t=await hp(e)}catch(f){n=f instanceof Error?f.message:String(f)}let{lit:s,litLink:i,brand:o}=ac(process.stdout.isTTY===!0),a=[" Read the authoring skill before writing a bulb:",` \u2022 ${s("npx typebulb skill")} \u2014 assembles one SKILL.md`," \u2022 or, open its parts:",` ${s(oc())}`,` ${s(mi())}`],c=t?[" Agent mirror is live\u{1F4A1}",` ${s("\u25CF")} ${i(t.url)}`," Embedded bulbs render live here."," Agents:",` Reusable app/tool \u2192 write a ${s(".bulb.md")}`," Show something inline \u2192 embed a bulb"," background a wait for its render verdict:",` \u2022 ${s('typebulb wait agent --match "[embed <name>"')}`,...a," End your reply with the mirror link above"," \u2022 easy to miss the link mid-message"]:[` The mirror did not start (${n})`,` \u25CF Start it manually: ${s(`npx typebulb agent:${e}`)}`," Agents:",...a],l=[` ${o(`typebulb v${r}`)}`,...c,""];process.stdout.write(l.join(`
|
|
1288
1288
|
`)+`
|
|
1289
1289
|
`)}async function hk(r,e){if(!process.stdin.isTTY||!process.stdout.isTTY)return;let{lit:t,brand:n}=ac(!0),s=[` ${n(`typebulb v${r}`)}`," This project has sessions for more than one agent harness.",...e.map((o,a)=>` ${t(String(a+1))}) ${o}`),""];process.stdout.write(s.join(`
|
|
1290
1290
|
`)+`
|
|
@@ -1372,8 +1372,8 @@ ${Fk}
|
|
|
1372
1372
|
`);let x=X.join(u,t,"client"),h=X.join(u,"core","client");async function b(){await(await import("esbuild")).build({entryPoints:[X.join(x,"index.ts")],bundle:!0,platform:"browser",format:"esm",outfile:X.join(f,"client.js")});for(let E of["styles.css","index.html"])t0(X.join(h,E),X.join(f,E))}let v;if(r.watch&&i){let O=r0(x),E=!1;v=St({target:O?u:f,events:"all",onChange:async()=>{if(!E){E=!0;try{O&&await b(),console.log(`Mirror rebuilt. Browser reloading...
|
|
1373
1373
|
`),i.emit("reload")}catch(_){console.error("Mirror rebuild failed:",_ instanceof Error?_.message:_)}finally{E=!1}}}})}r.open&&await jm(k);let S=async()=>{console.log(`
|
|
1374
1374
|
Shutting down...`);try{a.shutdownSwitcher?.()}catch{}g.close(),v?.(),s(),await yt(process.pid),process.exit(0)};process.on("SIGINT",S),process.on("SIGTERM",S)}import*as qm from"path";async function Um(r,e,t,n,s){let i=ke(t),o=!1,a=async()=>{let{bulb:c,config:l}=await me(r);o||(rt(i,r,c.server),o=!0),await Zr(c.server,s,n,l.dependencies)};console.log(`Running ${qm.basename(r)}...`),await a(),e&&(console.log(`Watching for changes...
|
|
1375
|
-
`),St({target:r,onChange:async()=>{try{console.log("Re-running..."),await a()}catch(c){console.error("Error:",c)}}}))}import{Console as o0}from"node:console";Le();async function Wm(r,e,t,n,s){f0();try{let p=Xt(await J(),r)[0];p&&si(p.pid,ve(p.pid).offset)}catch{}let i=ke(t),{bulb:o,config:a}=await me(r);o.server||hn("This bulb has no **server.ts** block; nothing to call."),rt(i,r,o.server);let c;try{c=await Zr(o.server,s,n,a.dependencies)}catch(p){hn(p instanceof Error?p.message:String(p))}let l=gi(c,e.fn);if(!l){let p=[...Object.keys(c).filter(y=>typeof c[y]=="function"),...Object.keys(pc)];hn(`Function '${e.fn}' not found. Available: ${p.length?p.join(", "):"(none)"}.`)}let f=await a0(e);if(yi(l)){try{for await(let p of l(...f))await Km(JSON.stringify(p,Hm)+`
|
|
1375
|
+
`),St({target:r,onChange:async()=>{try{console.log("Re-running..."),await a()}catch(c){console.error("Error:",c)}}}))}import{Console as o0}from"node:console";Le();async function Wm(r,e,t,n,s){f0();try{let p=Xt(await J(),r)[0];p&&(si(p.pid,ve(p.pid).offset),console.error(`note: 'call' boots a fresh server.ts instance; the running server (${p.url}) is not contacted \u2014 state shared with it must live on disk.`))}catch{}let i=ke(t),{bulb:o,config:a}=await me(r);o.server||hn("This bulb has no **server.ts** block; nothing to call."),rt(i,r,o.server);let c;try{c=await Zr(o.server,s,n,a.dependencies)}catch(p){hn(p instanceof Error?p.message:String(p))}let l=gi(c,e.fn);if(!l){let p=[...Object.keys(c).filter(y=>typeof c[y]=="function"),...Object.keys(pc)];hn(`Function '${e.fn}' not found. Available: ${p.length?p.join(", "):"(none)"}.`)}let f=await a0(e);if(yi(l)){try{for await(let p of l(...f))await Km(JSON.stringify(p,Hm)+`
|
|
1376
1376
|
`)}catch(p){hn(p instanceof Error?p.stack??p.message:String(p))}return Jm(0)}let u;try{u=await l(...f)}catch(p){hn(p instanceof Error?p.stack??p.message:String(p))}let d=u0(u);d!==void 0&&await Km(d+`
|
|
1377
1377
|
`),Jm(0)}function Jm(r){process.exitCode=r,setTimeout(()=>process.exit(r),2e3).unref?.()}async function a0(r){if(r.hasArgsFlag){let e=r.argsJson??"";return e==="-"&&(e=await d0()),l0(e)}return c0(r.positional)}function c0(r){return r.map(e=>{try{return JSON.parse(e)}catch{return e}})}function l0(r){let e;try{e=JSON.parse(r)}catch(t){throw new Error(`--args must be a JSON array: ${t instanceof Error?t.message:String(t)}`)}if(!Array.isArray(e))throw new Error(`--args must be a JSON array, got ${e===null?"null":typeof e}`);return e}function u0(r){if(r!==void 0)return JSON.stringify(r,Hm,2)}function Hm(r,e){return typeof e=="bigint"?e.toString():e}function hn(r){process.stderr.write(r+`
|
|
1378
|
-
`),process.exit(1)}function f0(){let r=new o0(process.stderr,process.stderr);console.log=r.log.bind(r),console.info=r.info.bind(r),console.debug=r.debug.bind(r),console.dir=r.dir.bind(r)}function Km(r){return new Promise((e,t)=>{process.stdout.write(r,n=>n?t(n):e())})}async function d0(){let r=[];for await(let e of process.stdin)r.push(e);return Buffer.concat(r).toString("utf-8")}var $c="0.20.
|
|
1378
|
+
`),process.exit(1)}function f0(){let r=new o0(process.stderr,process.stderr);console.log=r.log.bind(r),console.info=r.info.bind(r),console.debug=r.debug.bind(r),console.dir=r.dir.bind(r)}function Km(r){return new Promise((e,t)=>{process.stdout.write(r,n=>n?t(n):e())})}async function d0(){let r=[];for await(let e of process.stdin)r.push(e);return Buffer.concat(r).toString("utf-8")}var $c="0.20.4";function Vm(r,e){r||(console.error(`This bulb runs server-side Node code (server.ts), which --trust must authorize:
|
|
1379
1379
|
${e}`),process.exit(1))}async function p0(){let r=Hc(process.argv.slice(2));if(r.version&&(console.log(`typebulb ${$c}`),process.exit(0)),r.help&&(Vc(),process.exit(0)),Tp(),r.subcommand==="logs"){await Bp(r.file||void 0,{follow:r.follow,clear:r.clear,run:r.run,lines:r.lines});return}if(r.subcommand==="wait"){await jp(r.file||void 0,{match:r.match,timeoutSec:r.timeoutSec});return}if(r.subcommand==="stop"){r.stopScope?await qp(r.stopScope):await Fp(r.file||void 0);return}if(r.subcommand==="send"){await Jp(r.file,r.sendMessage,r.sendWaitMs??0);return}if(r.subcommand==="skill"){await $p($c);return}if(r.subcommand==="models"){await Ip(r.mode);return}if(r.subcommand==="agent"){await(r.agentTarget?Fm(r):_p($c));return}if(r.subcommand==="trust"||r.subcommand==="untrust"){await np(r.file||void 0,r.subcommand==="trust");return}let e;if(!r.file||r.file==="."){let l=await Kd(process.cwd());l||(console.error("No .bulb.md file found in current directory"),process.exit(1)),e=l}else e=De.resolve(r.file);await Ym.access(e).then(()=>!0,()=>!1)||(Gt().includes(r.file)&&(console.error(`To open the ${r.file} agent mirror, run: npx typebulb agent:${r.file}`),process.exit(1)),console.error(`File not found: ${e}`),process.exit(1)),e.endsWith(".bulb.md")||(console.error("File must have .bulb.md extension"),process.exit(1));let n=r.file&&r.file!=="."?r.file:De.relative(process.cwd(),e)||De.basename(e),s=`npx typebulb --trust ${n.includes(" ")?`"${n}"`:n}`;if(r.subcommand==="predict"){await rp(e,s);return}let i=!r.noTrust&>(e);i&&!r.trust&&console.log("trust: granted from memory (run `typebulb untrust` to revoke)"),r.trust=r.noTrust?!1:r.trust||i;let o;try{o=await me(e)}catch{}let a;if(r.local){o&&!(r.local.name in(o.config.dependencies??{}))&&(console.error(`--replace: '${r.local.name}' is not a dependency in this bulb's config.json; nothing to replace.`),process.exit(1)),o&&r.subcommand!=="call"&&(!o.bulb.code||r.server)&&console.warn("warning: --replace has no effect in server mode (the override is client-only).");try{a=await Kc(r.local)}catch(l){console.error(l instanceof Error?l.message:String(l)),process.exit(1)}console.log(`replace: ${a.name} \u2192 ${De.relative(process.cwd(),a.dir)||"."}`)}if(r.subcommand==="check"){await tp(e,a);return}let c=De.dirname(e);if(r.subcommand==="call"){Vm(r.trust,s),await Wm(e,{fn:r.fn,positional:r.callArgs,argsJson:r.argsJson,hasArgsFlag:r.hasArgsFlag},r.mode,a,c);return}if(o&&o.bulb.server&&(gs(o.bulb)||r.server)){Vm(r.trust,s),await Um(e,r.watch,r.mode,a,c);return}await Gp(e,r,s,a,c)}p0().catch(r=>{console.error("Error:",r.message),process.exit(1)});
|
package/package.json
CHANGED