typebulb 0.60.1 → 0.60.2

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/SKILL.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: typebulb
3
3
  description: "Author and run Typebulb bulbs — single-file markdown apps (TypeScript/TSX) that run locally via `npx typebulb` (full power: filesystem, database, `server.ts`, `tb.ai`) or render live inline in your coding agent's session through Typebulb's agent mirror (sandboxed, client-only). A bulb can be a visual widget (chart, simulation, diagram, calculator, UI), a full-stack tool with a Node backend, or an AI app that calls models at runtime. Covers the bulb format, the `tb.*` API, trust, and the local run/inline workflow. Use when the user wants a bulb, a quick local tool (visual, backend-backed, or AI-powered), or something visual rendered inline in the conversation."
4
- version: 0.60.1
4
+ version: 0.60.2
5
5
  ---
6
6
 
7
- > Generated from typebulb v0.60.1. `npx typebulb agent` prints the running version alongside the path to its packaged SKILL.md: if that version is newer than this one, replace this file with that one.
7
+ > Generated from typebulb v0.60.2. `npx typebulb agent` prints the running version alongside the path to its packaged SKILL.md: if that version is newer than this one, replace this file with that one.
8
8
 
9
9
  # typebulb
10
10
 
@@ -1508,8 +1508,26 @@ a.agent-name:hover { text-decoration: underline; }
1508
1508
  inline bulb and a spread bulb). `100%` in --lane-ml resolves against each user's own containing block
1509
1509
  (prose width) at point of use, so both centre on the column axis — which is the viewport centre,
1510
1510
  since the gutters are symmetric. One home so the geometry can't drift between the two (the bug
1511
- that let an off-centre nudge survive in one rule after being removed from the other). */
1512
- .md { --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2); }
1511
+ that let an off-centre nudge survive in one rule after being removed from the other).
1512
+ --chop-inset (the stripe-chop's reach into the gutter) and --chop-bg (the fill that covers the
1513
+ stripe) live here too: all four describe the transcript column, so all four are the container's
1514
+ to declare rather than the artifact's. */
1515
+ .md {
1516
+ --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2);
1517
+ --chop-inset: 1rem; --chop-bg: var(--bg);
1518
+ }
1519
+
1520
+ /* A padded panel that holds rendered markdown redeclares all four as its own box: a sub-agent
1521
+ hand-back frame (TB-Agent-Children.md), a disclosure, a blockquote. Inside one there is no turn
1522
+ stripe to reach for or to cover, so the reach and the fill go to zero and an embed keeps only its
1523
+ vertical band, while the lane collapses to the panel: a wide table then shares the panel's width
1524
+ through its two ceilings and scrolls in .table-scroll, instead of a band that bleeds over the
1525
+ card's border and paints page colour across its tint. The reset must land on the `.md` element
1526
+ itself, since `.md` declares the defaults on itself and would beat a rule on an ancestor. The
1527
+ next panel is one more selector here. */
1528
+ .bubble.user > .md, .md details, .md blockquote {
1529
+ --lane-w: 100%; --lane-ml: 0px; --chop-inset: 0px; --chop-bg: transparent;
1530
+ }
1513
1531
 
1514
1532
  /* Full-lane breakout — the mermaid embed (whole element) and a SPREAD bulb's iframe step out of
1515
1533
  the prose column to the transcript width so wide content gets room. Both use --lane-w/--lane-ml
@@ -1541,7 +1559,7 @@ a.agent-name:hover { text-decoration: underline; }
1541
1559
  overflow-x: auto;
1542
1560
  display: flex;
1543
1561
  justify-content: safe center;
1544
- box-shadow: 0 0 0 1rem var(--bg);
1562
+ box-shadow: 0 0 0 1rem var(--chop-bg);
1545
1563
  }
1546
1564
  /* flex-shrink:0: otherwise the SVG shrinks to the flex container and its viewBox
1547
1565
  scales it down — no overflow, no scrollbar. Natural width is what scrolls. */
@@ -1593,8 +1611,8 @@ a.agent-name:hover { text-decoration: underline; }
1593
1611
  space that already reads as margin, while on the right the lane's own 1rem inset is all there is
1594
1612
  between the table and the bar. Inset both sides equally and the left gap measures a gutter wider
1595
1613
  than the right; inset neither and the table sits flush against the bar, which is the bug. So the
1596
- band adds back one inset on the right, and the table's cap comes down by the same 1rem or it would
1597
- overflow the band's content box by exactly that at every width — the permanent scrollbar the
1614
+ band adds back one inset on the right, and the table's cap comes down by that same inset or it
1615
+ would overflow the band's content box by exactly that at every width — the permanent scrollbar the
1598
1616
  zeroed inset was avoiding. */
1599
1617
  .md .table-embed.spread {
1600
1618
  justify-content: safe center;
@@ -1602,9 +1620,9 @@ a.agent-name:hover { text-decoration: underline; }
1602
1620
  margin-left: var(--lane-ml);
1603
1621
  margin-right: auto;
1604
1622
  padding-left: 0;
1605
- padding-right: 1rem;
1623
+ padding-right: var(--chop-inset);
1606
1624
  }
1607
- .md .table-embed.spread table { max-width: calc(var(--lane-w) - 1rem); }
1625
+ .md .table-embed.spread table { max-width: calc(var(--lane-w) - var(--chop-inset)); }
1608
1626
 
1609
1627
  /* ````bulb```` inline bulbs: a sandboxed nested app. createBulbFrame owns the iframe (auto-height,
1610
1628
  borderless). No frame of our own — like the mermaid/svg embeds, it sits in the flow rather
@@ -1616,9 +1634,10 @@ a.agent-name:hover { text-decoration: underline; }
1616
1634
  under a live inline bulb (both monospace, muted red). */
1617
1635
  /* The stripe-chop, shared by every opaque in-column visual artifact (the fit bulb and a raw
1618
1636
  `svg` — each opts in by carrying `.embed`). An opaque bg over position:relative + z-index:0
1619
- paints above .bubble::before; a -1rem left extension reaches the stripe in the gutter without a
1620
- full breakout (padding-left:1rem keeps the content in the prose column, and the box's right edge
1621
- stays at prose-right, so a bulb's controls anchored there don't move). The vertical gap is opaque
1637
+ paints above .bubble::before; a --chop-inset left extension reaches the stripe in the gutter
1638
+ without a full breakout (the matching padding-left keeps the content in the prose column, and the
1639
+ box's right edge stays at prose-right, so a bulb's controls anchored there don't move); inside a
1640
+ panel both zero out (the nesting reset above). The vertical gap is opaque
1622
1641
  padding, not margin: it occludes the stripe top and bottom so the cut is flush with the prose (no
1623
1642
  stub in a transparent gap), symmetric above/below to separate the artifact from surrounding text.
1624
1643
  margin top/bottom are zeroed — and the abutting prose margins too (below) — so the gap butts the
@@ -1628,9 +1647,9 @@ a.agent-name:hover { text-decoration: underline; }
1628
1647
  display: flex;
1629
1648
  position: relative;
1630
1649
  z-index: 0;
1631
- background: var(--bg);
1632
- margin: 0 0 0 -1rem;
1633
- padding: 1.1rem 0 1.1rem 1rem;
1650
+ background: var(--chop-bg);
1651
+ margin: 0 0 0 calc(-1 * var(--chop-inset));
1652
+ padding: 1.1rem 0 1.1rem var(--chop-inset);
1634
1653
  }
1635
1654
  /* A bulb stacks its controls/frame/code vertically; in spread mode the inner iframe's own
1636
1655
  breakout covers the stripe too (the wrapper still chops in column). */
@@ -1508,8 +1508,26 @@ a.agent-name:hover { text-decoration: underline; }
1508
1508
  inline bulb and a spread bulb). `100%` in --lane-ml resolves against each user's own containing block
1509
1509
  (prose width) at point of use, so both centre on the column axis — which is the viewport centre,
1510
1510
  since the gutters are symmetric. One home so the geometry can't drift between the two (the bug
1511
- that let an off-centre nudge survive in one rule after being removed from the other). */
1512
- .md { --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2); }
1511
+ that let an off-centre nudge survive in one rule after being removed from the other).
1512
+ --chop-inset (the stripe-chop's reach into the gutter) and --chop-bg (the fill that covers the
1513
+ stripe) live here too: all four describe the transcript column, so all four are the container's
1514
+ to declare rather than the artifact's. */
1515
+ .md {
1516
+ --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2);
1517
+ --chop-inset: 1rem; --chop-bg: var(--bg);
1518
+ }
1519
+
1520
+ /* A padded panel that holds rendered markdown redeclares all four as its own box: a sub-agent
1521
+ hand-back frame (TB-Agent-Children.md), a disclosure, a blockquote. Inside one there is no turn
1522
+ stripe to reach for or to cover, so the reach and the fill go to zero and an embed keeps only its
1523
+ vertical band, while the lane collapses to the panel: a wide table then shares the panel's width
1524
+ through its two ceilings and scrolls in .table-scroll, instead of a band that bleeds over the
1525
+ card's border and paints page colour across its tint. The reset must land on the `.md` element
1526
+ itself, since `.md` declares the defaults on itself and would beat a rule on an ancestor. The
1527
+ next panel is one more selector here. */
1528
+ .bubble.user > .md, .md details, .md blockquote {
1529
+ --lane-w: 100%; --lane-ml: 0px; --chop-inset: 0px; --chop-bg: transparent;
1530
+ }
1513
1531
 
1514
1532
  /* Full-lane breakout — the mermaid embed (whole element) and a SPREAD bulb's iframe step out of
1515
1533
  the prose column to the transcript width so wide content gets room. Both use --lane-w/--lane-ml
@@ -1541,7 +1559,7 @@ a.agent-name:hover { text-decoration: underline; }
1541
1559
  overflow-x: auto;
1542
1560
  display: flex;
1543
1561
  justify-content: safe center;
1544
- box-shadow: 0 0 0 1rem var(--bg);
1562
+ box-shadow: 0 0 0 1rem var(--chop-bg);
1545
1563
  }
1546
1564
  /* flex-shrink:0: otherwise the SVG shrinks to the flex container and its viewBox
1547
1565
  scales it down — no overflow, no scrollbar. Natural width is what scrolls. */
@@ -1593,8 +1611,8 @@ a.agent-name:hover { text-decoration: underline; }
1593
1611
  space that already reads as margin, while on the right the lane's own 1rem inset is all there is
1594
1612
  between the table and the bar. Inset both sides equally and the left gap measures a gutter wider
1595
1613
  than the right; inset neither and the table sits flush against the bar, which is the bug. So the
1596
- band adds back one inset on the right, and the table's cap comes down by the same 1rem or it would
1597
- overflow the band's content box by exactly that at every width — the permanent scrollbar the
1614
+ band adds back one inset on the right, and the table's cap comes down by that same inset or it
1615
+ would overflow the band's content box by exactly that at every width — the permanent scrollbar the
1598
1616
  zeroed inset was avoiding. */
1599
1617
  .md .table-embed.spread {
1600
1618
  justify-content: safe center;
@@ -1602,9 +1620,9 @@ a.agent-name:hover { text-decoration: underline; }
1602
1620
  margin-left: var(--lane-ml);
1603
1621
  margin-right: auto;
1604
1622
  padding-left: 0;
1605
- padding-right: 1rem;
1623
+ padding-right: var(--chop-inset);
1606
1624
  }
1607
- .md .table-embed.spread table { max-width: calc(var(--lane-w) - 1rem); }
1625
+ .md .table-embed.spread table { max-width: calc(var(--lane-w) - var(--chop-inset)); }
1608
1626
 
1609
1627
  /* ````bulb```` inline bulbs: a sandboxed nested app. createBulbFrame owns the iframe (auto-height,
1610
1628
  borderless). No frame of our own — like the mermaid/svg embeds, it sits in the flow rather
@@ -1616,9 +1634,10 @@ a.agent-name:hover { text-decoration: underline; }
1616
1634
  under a live inline bulb (both monospace, muted red). */
1617
1635
  /* The stripe-chop, shared by every opaque in-column visual artifact (the fit bulb and a raw
1618
1636
  `svg` — each opts in by carrying `.embed`). An opaque bg over position:relative + z-index:0
1619
- paints above .bubble::before; a -1rem left extension reaches the stripe in the gutter without a
1620
- full breakout (padding-left:1rem keeps the content in the prose column, and the box's right edge
1621
- stays at prose-right, so a bulb's controls anchored there don't move). The vertical gap is opaque
1637
+ paints above .bubble::before; a --chop-inset left extension reaches the stripe in the gutter
1638
+ without a full breakout (the matching padding-left keeps the content in the prose column, and the
1639
+ box's right edge stays at prose-right, so a bulb's controls anchored there don't move); inside a
1640
+ panel both zero out (the nesting reset above). The vertical gap is opaque
1622
1641
  padding, not margin: it occludes the stripe top and bottom so the cut is flush with the prose (no
1623
1642
  stub in a transparent gap), symmetric above/below to separate the artifact from surrounding text.
1624
1643
  margin top/bottom are zeroed — and the abutting prose margins too (below) — so the gap butts the
@@ -1628,9 +1647,9 @@ a.agent-name:hover { text-decoration: underline; }
1628
1647
  display: flex;
1629
1648
  position: relative;
1630
1649
  z-index: 0;
1631
- background: var(--bg);
1632
- margin: 0 0 0 -1rem;
1633
- padding: 1.1rem 0 1.1rem 1rem;
1650
+ background: var(--chop-bg);
1651
+ margin: 0 0 0 calc(-1 * var(--chop-inset));
1652
+ padding: 1.1rem 0 1.1rem var(--chop-inset);
1634
1653
  }
1635
1654
  /* A bulb stacks its controls/frame/code vertically; in spread mode the inner iframe's own
1636
1655
  breakout covers the stripe too (the wrapper still chops in column). */
@@ -1508,8 +1508,26 @@ a.agent-name:hover { text-decoration: underline; }
1508
1508
  inline bulb and a spread bulb). `100%` in --lane-ml resolves against each user's own containing block
1509
1509
  (prose width) at point of use, so both centre on the column axis — which is the viewport centre,
1510
1510
  since the gutters are symmetric. One home so the geometry can't drift between the two (the bug
1511
- that let an off-centre nudge survive in one rule after being removed from the other). */
1512
- .md { --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2); }
1511
+ that let an off-centre nudge survive in one rule after being removed from the other).
1512
+ --chop-inset (the stripe-chop's reach into the gutter) and --chop-bg (the fill that covers the
1513
+ stripe) live here too: all four describe the transcript column, so all four are the container's
1514
+ to declare rather than the artifact's. */
1515
+ .md {
1516
+ --lane-w: calc(100vw - 2rem); --lane-ml: calc((100% - var(--lane-w)) / 2);
1517
+ --chop-inset: 1rem; --chop-bg: var(--bg);
1518
+ }
1519
+
1520
+ /* A padded panel that holds rendered markdown redeclares all four as its own box: a sub-agent
1521
+ hand-back frame (TB-Agent-Children.md), a disclosure, a blockquote. Inside one there is no turn
1522
+ stripe to reach for or to cover, so the reach and the fill go to zero and an embed keeps only its
1523
+ vertical band, while the lane collapses to the panel: a wide table then shares the panel's width
1524
+ through its two ceilings and scrolls in .table-scroll, instead of a band that bleeds over the
1525
+ card's border and paints page colour across its tint. The reset must land on the `.md` element
1526
+ itself, since `.md` declares the defaults on itself and would beat a rule on an ancestor. The
1527
+ next panel is one more selector here. */
1528
+ .bubble.user > .md, .md details, .md blockquote {
1529
+ --lane-w: 100%; --lane-ml: 0px; --chop-inset: 0px; --chop-bg: transparent;
1530
+ }
1513
1531
 
1514
1532
  /* Full-lane breakout — the mermaid embed (whole element) and a SPREAD bulb's iframe step out of
1515
1533
  the prose column to the transcript width so wide content gets room. Both use --lane-w/--lane-ml
@@ -1541,7 +1559,7 @@ a.agent-name:hover { text-decoration: underline; }
1541
1559
  overflow-x: auto;
1542
1560
  display: flex;
1543
1561
  justify-content: safe center;
1544
- box-shadow: 0 0 0 1rem var(--bg);
1562
+ box-shadow: 0 0 0 1rem var(--chop-bg);
1545
1563
  }
1546
1564
  /* flex-shrink:0: otherwise the SVG shrinks to the flex container and its viewBox
1547
1565
  scales it down — no overflow, no scrollbar. Natural width is what scrolls. */
@@ -1593,8 +1611,8 @@ a.agent-name:hover { text-decoration: underline; }
1593
1611
  space that already reads as margin, while on the right the lane's own 1rem inset is all there is
1594
1612
  between the table and the bar. Inset both sides equally and the left gap measures a gutter wider
1595
1613
  than the right; inset neither and the table sits flush against the bar, which is the bug. So the
1596
- band adds back one inset on the right, and the table's cap comes down by the same 1rem or it would
1597
- overflow the band's content box by exactly that at every width — the permanent scrollbar the
1614
+ band adds back one inset on the right, and the table's cap comes down by that same inset or it
1615
+ would overflow the band's content box by exactly that at every width — the permanent scrollbar the
1598
1616
  zeroed inset was avoiding. */
1599
1617
  .md .table-embed.spread {
1600
1618
  justify-content: safe center;
@@ -1602,9 +1620,9 @@ a.agent-name:hover { text-decoration: underline; }
1602
1620
  margin-left: var(--lane-ml);
1603
1621
  margin-right: auto;
1604
1622
  padding-left: 0;
1605
- padding-right: 1rem;
1623
+ padding-right: var(--chop-inset);
1606
1624
  }
1607
- .md .table-embed.spread table { max-width: calc(var(--lane-w) - 1rem); }
1625
+ .md .table-embed.spread table { max-width: calc(var(--lane-w) - var(--chop-inset)); }
1608
1626
 
1609
1627
  /* ````bulb```` inline bulbs: a sandboxed nested app. createBulbFrame owns the iframe (auto-height,
1610
1628
  borderless). No frame of our own — like the mermaid/svg embeds, it sits in the flow rather
@@ -1616,9 +1634,10 @@ a.agent-name:hover { text-decoration: underline; }
1616
1634
  under a live inline bulb (both monospace, muted red). */
1617
1635
  /* The stripe-chop, shared by every opaque in-column visual artifact (the fit bulb and a raw
1618
1636
  `svg` — each opts in by carrying `.embed`). An opaque bg over position:relative + z-index:0
1619
- paints above .bubble::before; a -1rem left extension reaches the stripe in the gutter without a
1620
- full breakout (padding-left:1rem keeps the content in the prose column, and the box's right edge
1621
- stays at prose-right, so a bulb's controls anchored there don't move). The vertical gap is opaque
1637
+ paints above .bubble::before; a --chop-inset left extension reaches the stripe in the gutter
1638
+ without a full breakout (the matching padding-left keeps the content in the prose column, and the
1639
+ box's right edge stays at prose-right, so a bulb's controls anchored there don't move); inside a
1640
+ panel both zero out (the nesting reset above). The vertical gap is opaque
1622
1641
  padding, not margin: it occludes the stripe top and bottom so the cut is flush with the prose (no
1623
1642
  stub in a transparent gap), symmetric above/below to separate the artifact from surrounding text.
1624
1643
  margin top/bottom are zeroed — and the abutting prose margins too (below) — so the gap butts the
@@ -1628,9 +1647,9 @@ a.agent-name:hover { text-decoration: underline; }
1628
1647
  display: flex;
1629
1648
  position: relative;
1630
1649
  z-index: 0;
1631
- background: var(--bg);
1632
- margin: 0 0 0 -1rem;
1633
- padding: 1.1rem 0 1.1rem 1rem;
1650
+ background: var(--chop-bg);
1651
+ margin: 0 0 0 calc(-1 * var(--chop-inset));
1652
+ padding: 1.1rem 0 1.1rem var(--chop-inset);
1634
1653
  }
1635
1654
  /* A bulb stacks its controls/frame/code vertically; in spread mode the inner iframe's own
1636
1655
  breakout covers the stripe too (the wrapper still chops in column). */
package/dist/index.js CHANGED
@@ -311,7 +311,7 @@ ${t.comment}`:t.comment}this.doc.range[2]=t.offset;break}default:this.errors.pus
311
311
  `);if(r<0)return t.bytes>=65536?{cwd:"",subagent:!1}:void 0;try{let s=JSON.parse(t.text.slice(0,r)),i=s?.type==="session_meta"?s.payload:void 0;return typeof i?.cwd!="string"?{cwd:"",subagent:!1}:{cwd:i.cwd,subagent:typeof i.source=="object"&&!!i.source?.subagent}}catch{return{cwd:"",subagent:!1}}}var pT,ys,bu,yT,vT,ST,kT,xT,CT,NT,Ng,LT,zn,BT,wu=S(()=>{"use strict";ms();bo();pT=["<permissions instructions>","<apps_instructions>","<plugins_instructions>","<skills_instructions>","<recommended_plugins>","<environment_context>","<node_repl_review_evidence>","# AGENTS.md instructions for "];ys=n=>!!n&&typeof n=="object"&&!Array.isArray(n),bu=n=>n==="shell_command"||n==="exec_command",yT=new Set(["max_output_tokens","yield_time_ms","timeout_ms"]);vT=/^(Get-Content|gc|cat|type|nl|bat|sed|head|tail)$/i,ST=/^-(Path|LiteralPath)$/i,kT=/^-(Encoding|TotalCount|Head|Tail|ReadCount|Delimiter)$/i,xT=/^(sed -n|head|tail|Select-Object|select)(\s|$)/i;CT={n:`
312
312
  `,t:" ",r:"\r",b:"\b",f:"\f",v:"\v",0:"\0"};NT=n=>n?n.replace(/\r?\n$/,"").split(`
313
313
  `).length:0;Ng=n=>{let e=fT(n);return process.platform==="win32"?e.toLowerCase():e},LT=12e4,zn=class extends $t{constructor(t=yu(_g(),".codex","sessions")){super();this.root=t}displayName="Codex Mirror";#e=0;#i;#t=new Map;#n=new Map;#f=new Map;#r=new Map;detectsSelf(){return!!process.env.CODEX_THREAD_ID}detectsInstalled(){return uT(yu(_g(),".codex"))}sessionsDir(t){return this.root}listSessionFiles(t){let r=Ng(t),s=[];for(let i of this.#o()){let o=0,a=0;try{let f=Cg(i);if(!f.isFile())continue;o=f.size,a=f.mtimeMs}catch{continue}let l=this.#t.get(i);if(l===void 0){if(l=jT(i),l===void 0)continue;this.#t.set(i,l)}if(l.subagent||Ng(l.cwd)!==r)continue;let c=dT(i,".jsonl");this.#f.set(c,i),s.push({sessionId:c,file:i,mtime:this.#c(i,o,a)})}return s}#c(t,r,s){let i=this.#n.get(t);if(i&&i.size===r&&i.mtime===s)return i.ms;let o=Math.max(s,DT(t)??0);return this.#n.set(t,{size:r,mtime:s,ms:o}),o}#o(){let t=[],r=s=>{try{return cT(s).map(i=>yu(s,i))}catch{return[]}};for(let s of r(this.root))for(let i of r(s))for(let o of r(i))for(let a of r(o))a.endsWith(".jsonl")&&t.push(a);return t}parseEntry(t){let r;try{r=JSON.parse(t)}catch{return null}return!r||typeof r.type!="string"?null:r}idOf(t){return t.ord===void 0&&(t.ord=String(this.#e++),t.parent=this.#i,this.#i=t.ord),t.ord}parentOf(t){return t.parent}timestampOf(t){return t.timestamp}isSidechain(t){return!1}isLeafType(t){return!0}isRecoveryNoise(t){return!1}apply(t,r){let s=[],i=t.payload;if(!i)return{events:s};let o=()=>{let a=Date.parse(t.timestamp??"");return!isNaN(a)&&a>=r};if(t.type==="response_item"){if(i.type==="message"){if(i.role==="user"){let a=Vn(i.content,!0).trim();a&&s.push({type:"user",text:a})}else if(i.role==="assistant"){let a=Vn(i.content,!1);a&&s.push({type:"assistant",text:a,thinking:"",tools:[],live:o()})}return{events:s}}if(i.type==="reasoning"){let a=Vn(i.summary,!1);return a&&s.push({type:"assistant",text:"",thinking:a,tools:[],live:o()}),{events:s}}if(i.type==="custom_tool_call"||i.type==="function_call"){let a=i.call_id??"",{pending:l,...c}=i.type==="custom_tool_call"?gT(i.name??"",i.input):{name:i.name??"",input:$T(i.arguments)};return l&&this.#r.set(a,l),s.push({type:"assistant",text:"",thinking:"",tools:[{id:a,...c}],live:o()}),{events:s}}if(i.type==="custom_tool_call_output"||i.type==="function_call_output"){let a=i.call_id??"",l=this.#r.get(a);return this.#r.delete(a),s.push({type:"tool_result",id:a,...RT(mT(i.output),l)}),{events:s}}return{events:s}}if(t.type==="event_msg"){if(i.type==="token_count"){let a=i.info?.last_token_usage;if(!a)return{events:s};let l={in:Math.max(0,(a.input_tokens??0)-(a.cached_input_tokens??0)),out:a.output_tokens??0,cached:a.cached_input_tokens??0,cacheCreate:a.cache_write_input_tokens??0};return{events:s,usage:l}}return{events:s}}return t.type==="turn_context"?{events:s,model:i.model}:{events:s}}chainWorking(t){let r=!1;for(let s of t){if(s.type!=="event_msg")continue;let i=s.payload?.type;i==="task_started"?r=!0:(i==="task_complete"||i==="turn_aborted")&&(r=!1)}return r}sessionAlive(t){let r=this.#f.get(t);if(!r)return;let s;try{let i=Cg(r);s=this.#c(r,i.size,i.mtimeMs)}catch{return}return Date.now()-s<LT}readPreview(t){for(let r of(Fg(t,256*1024)?.text??"").split(`
314
- `))if(!(!r.includes('"user_message"')&&!r.includes('"UserMessage"')))try{let s=JSON.parse(r);if(s?.type!=="event_msg")continue;let i=s.payload,a=((i?.type==="user_message"?i.message:i?.type==="item_completed"&&i.item?.type==="UserMessage"?Vn(i.item.content,!0):void 0)??"").replace(/\s+/g," ").trim();if(a)return a.slice(0,200)}catch{}return""}searchText(t){if(t?.type!=="response_item"||t.payload?.type!=="message")return"";let r=t.payload.role;return r!=="user"&&r!=="assistant"?"":Vn(t.payload.content,r==="user").replace(/\s+/g," ").trim()}};BT=[64*1024,1024*1024]});import{realpathSync as FT}from"fs";import{createHash as UT}from"crypto";import{join as Ao,resolve as qT}from"path";import{homedir as JT}from"os";function Mt(){let n=process.env.TYPEBULB_SERVERS_DIR;return n?Ao(n,".."):Ao(JT(),".typebulb")}function ut(){Ug||(Ug=!0,console.error(`${Mt()} not writable \u2014 continuing stateless: ports won't stick, caches stay cold, and typebulb logs/wait/send/stop may not see this run`))}function dt(){return process.env.TYPEBULB_SERVERS_DIR||Ao(Mt(),"servers")}function qg(n){let e=se(n),t=e.slice(e.lastIndexOf("/")+1).replace(/\.bulb\.md$/,"");return Ao(Mt(),"canvas",`${t}-${bs(n)}.png`)}function bs(n){return UT("sha1").update(se(n)).digest("hex").slice(0,8)}function se(n){let e=qT(n),t=e;try{t=FT.native(e)}catch{}return(process.platform==="win32"?t.toLowerCase():t).replace(/\\/g,"/")}function Jg(n,e){let t=se(n),r=se(e);return t.startsWith(r.endsWith("/")?r:r+"/")?!t.includes("/node_modules/"):!1}var Ug,nt=S(()=>{"use strict";Ug=!1});import*as Hg from"net";import{mkdir as WT,readFile as KT,writeFile as Yg}from"fs/promises";import{existsSync as HT}from"fs";import{join as Vg}from"path";function Xg(){return Vg(Mt(),"blocks")}function vu(n){return Vg(Xg(),`${n}.json`)}function Po(n){return new Promise(e=>{let t=Hg.createServer();t.once("error",()=>e(!1)),t.listen(n,"127.0.0.1",()=>t.close(()=>e(!0)))})}async function GT(n){for(let e=n;e<65535;e++)if(await Po(e))return e;throw new Error(`No free port found above ${n}`)}async function Su(n){let{listBulbServers:e}=await Promise.resolve().then(()=>($e(),Qg)),t=(await e()).find(s=>s.port===n),r=t?` (pid ${t.pid}, ${t.agent?`agent:${t.agent}`:t.file})`:"";console.error(`port ${n} in use${r}`),process.exit(1)}async function Oo(n){try{let e=JSON.parse(await KT(vu(n),"utf8"));return e&&typeof e.cwd=="string"?{cwd:e.cwd,slots:e.slots??{},mirrors:e.mirrors??{}}:void 0}catch{return}}async function Co(n,e){try{return await Yg(vu(n),JSON.stringify(e,null,2)),!0}catch{return ut(),!1}}async function XT(n){let e=se(n);try{await WT(Xg(),{recursive:!0})}catch{return}let t=[];for(let o=0;o<Gg;o++)t.push(zg+o*an);let r=await Promise.all(t.map(async o=>({base:o,block:await Oo(o)})));for(let{base:o,block:a}of r)if(a?.cwd===e)return o;let s={cwd:e,slots:{},mirrors:{}};for(let{base:o,block:a}of r){if(!a)try{return await Yg(vu(o),JSON.stringify(s,null,2),{flag:"wx"}),o}catch(l){if((await Oo(o))?.cwd===e)return o;l.code!=="EEXIST"&&ut();continue}if(!HT(a.cwd))return await Co(o,s)?o:void 0}let i=r.filter(o=>!!o.block).sort((o,a)=>Wg(o.block)-Wg(a.block))[0];if(i)return await Co(i.base,s)?i.base:void 0}function Wg(n){return Object.values(n.slots).reduce((e,t)=>Math.max(e,t.usedAt??0),0)}async function QT(n,e){let t=await Oo(n)??{cwd:"",slots:{},mirrors:{}};if(e.kind==="mirror"){let i=t.mirrors[e.agent];if(i!==void 0)return i;let o=Kg(t),a=o.has(0)?VT:0;for(;a>To&&o.has(a);)a--;return t.mirrors[e.agent]=a,await Co(n,t),a}let r=se(e.file),s=t.slots[r]?.offset;if(s===void 0){let i=Kg(t);for(s=To;s<an&&i.has(s);)s++;if(s>=an){let o=Object.entries(t.slots).sort((a,l)=>(a[1].usedAt??0)-(l[1].usedAt??0))[0];s=o?o[1].offset:To,o&&delete t.slots[o[0]]}}return t.slots[r]={offset:s,usedAt:Date.now()},await Co(n,t),s}function Kg(n){return new Set([...Object.values(n.mirrors),...Object.values(n.slots).map(e=>e.offset)])}async function Gn(n,e){let t=await XT(e);return t===void 0?void 0:t+await QT(t,n)}async function ln(n){let e=se(n);for(let t=0;t<Gg;t++){let r=await Oo(zg+t*an);if(r?.cwd===e)return s=>r.slots[se(s)]?.usedAt??0}return()=>0}async function _o(n){if(n.explicit!==void 0)return await Po(n.explicit)||await Su(n.explicit),n.target.kind==="bulb"&&await Gn(n.target,n.cwd),{port:n.explicit};let e=await Gn(n.target,n.cwd);if(e!==void 0){let r=e-e%an,s=Date.now()+zT;do{if(await Po(e))return{port:e};await new Promise(i=>setTimeout(i,150))}while(Date.now()<s);for(let i=To;i<an;i++){let o=r+i;if(o!==e&&await Po(o))return{port:o,note:`(slot ${e} was busy)`}}}return{port:await GT(YT),note:e===void 0?"(no port block available)":"(this project's block was full)"}}var zg,an,Gg,YT,To,VT,zT,cn=S(()=>{"use strict";nt();zg=20100,an=100,Gg=19,YT=23e3,To=1,VT=an-1,zT=3e3});var Le,Ro=S(()=>{"use strict";Le="0.60.1"});var Qg={};ci(Qg,{agentViewerCommand:()=>ly,bulbServerCommand:()=>ay,clearServerLog:()=>Tu,findProjectViewer:()=>$o,isAlive:()=>Xn,isMirror:()=>me,isProjectBulb:()=>Ru,isProjectMirror:()=>er,launchAgentViewer:()=>$u,launchBulbServer:()=>Ss,listBulbServers:()=>Q,probePageCount:()=>Nu,readServerLog:()=>Ye,readWaitCursor:()=>No,recordDenial:()=>_u,registerServer:()=>Cu,relayOpen:()=>Iu,runMarker:()=>Pu,serverLogPath:()=>Zn,serversForBulb:()=>dn,sliceRunLog:()=>Ou,startServerLog:()=>vs,stopBulbServer:()=>Qn,stopServer:()=>Bt,stopServersForBulb:()=>Lo,typebulbBinPath:()=>tr,unregisterServer:()=>un,writeWaitCursor:()=>ws});import{spawn as Zg}from"child_process";import{readdir as ZT,readFile as ny,writeFile as ry,unlink as ku,mkdir as eP}from"fs/promises";import{mkdirSync as sy,writeFileSync as Io,appendFileSync as tP,readFileSync as xu}from"fs";import*as Te from"path";import{fileURLToPath as nP}from"url";function Eu(n){return Te.join(dt(),`${n}.json`)}function Zn(n){return Te.join(dt(),`${n}.log`)}function Au(n){return Te.join(dt(),`${n}.wait.json`)}function iy(n){try{let e=JSON.parse(xu(Au(n),"utf8"));return e.cursors&&typeof e.cursors=="object"?e.cursors:{}}catch{return{}}}function No(n,e=""){let t=iy(n)[e];return typeof t=="number"&&Number.isFinite(t)&&t>=0?t:void 0}function ws(n,e,t=""){try{sy(dt(),{recursive:!0});let r=iy(n);r[t]=e,Io(Au(n),JSON.stringify({cursors:r}))}catch{}}function vs(n){let e=Zn(n);try{sy(dt(),{recursive:!0}),Io(e,"")}catch{return()=>{}}let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr),s=0,i=!1,o=a=>{if(!i)try{let l=typeof a=="string"?Buffer.from(a,"utf8"):Buffer.from(a);if(tP(e,l),s+=l.length,s>2*ey){let c=xu(e),f=c.subarray(Math.max(0,c.length-ey));Io(e,f),s=f.length}}catch{i=!0}};return process.stdout.write=((a,...l)=>(o(a),t(a,...l))),process.stderr.write=((a,...l)=>(o(a),r(a,...l))),()=>{process.stdout.write=t,process.stderr.write=r}}function Tu(n){try{Io(Zn(n),"")}catch{}}function Ye(n,e=0){try{let t=xu(Zn(n)),r=e>=0&&e<=t.length?e:0;return{text:t.subarray(r).toString("utf8"),offset:t.length}}catch{return{text:"",offset:0}}}function Pu(n){return`\u2500\u2500 run ${n} \u2500\u2500 ${new Date().toTimeString().slice(0,8)}`}function sP(n){let e=rP.exec(n);return e?Number(e[1]):void 0}function Ou(n,e){let t=n.split(`
314
+ `))if(!(!r.includes('"user_message"')&&!r.includes('"UserMessage"')))try{let s=JSON.parse(r);if(s?.type!=="event_msg")continue;let i=s.payload,a=((i?.type==="user_message"?i.message:i?.type==="item_completed"&&i.item?.type==="UserMessage"?Vn(i.item.content,!0):void 0)??"").replace(/\s+/g," ").trim();if(a)return a.slice(0,200)}catch{}return""}searchText(t){if(t?.type!=="response_item"||t.payload?.type!=="message")return"";let r=t.payload.role;return r!=="user"&&r!=="assistant"?"":Vn(t.payload.content,r==="user").replace(/\s+/g," ").trim()}};BT=[64*1024,1024*1024]});import{realpathSync as FT}from"fs";import{createHash as UT}from"crypto";import{join as Ao,resolve as qT}from"path";import{homedir as JT}from"os";function Mt(){let n=process.env.TYPEBULB_SERVERS_DIR;return n?Ao(n,".."):Ao(JT(),".typebulb")}function ut(){Ug||(Ug=!0,console.error(`${Mt()} not writable \u2014 continuing stateless: ports won't stick, caches stay cold, and typebulb logs/wait/send/stop may not see this run`))}function dt(){return process.env.TYPEBULB_SERVERS_DIR||Ao(Mt(),"servers")}function qg(n){let e=se(n),t=e.slice(e.lastIndexOf("/")+1).replace(/\.bulb\.md$/,"");return Ao(Mt(),"canvas",`${t}-${bs(n)}.png`)}function bs(n){return UT("sha1").update(se(n)).digest("hex").slice(0,8)}function se(n){let e=qT(n),t=e;try{t=FT.native(e)}catch{}return(process.platform==="win32"?t.toLowerCase():t).replace(/\\/g,"/")}function Jg(n,e){let t=se(n),r=se(e);return t.startsWith(r.endsWith("/")?r:r+"/")?!t.includes("/node_modules/"):!1}var Ug,nt=S(()=>{"use strict";Ug=!1});import*as Hg from"net";import{mkdir as WT,readFile as KT,writeFile as Yg}from"fs/promises";import{existsSync as HT}from"fs";import{join as Vg}from"path";function Xg(){return Vg(Mt(),"blocks")}function vu(n){return Vg(Xg(),`${n}.json`)}function Po(n){return new Promise(e=>{let t=Hg.createServer();t.once("error",()=>e(!1)),t.listen(n,"127.0.0.1",()=>t.close(()=>e(!0)))})}async function GT(n){for(let e=n;e<65535;e++)if(await Po(e))return e;throw new Error(`No free port found above ${n}`)}async function Su(n){let{listBulbServers:e}=await Promise.resolve().then(()=>($e(),Qg)),t=(await e()).find(s=>s.port===n),r=t?` (pid ${t.pid}, ${t.agent?`agent:${t.agent}`:t.file})`:"";console.error(`port ${n} in use${r}`),process.exit(1)}async function Oo(n){try{let e=JSON.parse(await KT(vu(n),"utf8"));return e&&typeof e.cwd=="string"?{cwd:e.cwd,slots:e.slots??{},mirrors:e.mirrors??{}}:void 0}catch{return}}async function Co(n,e){try{return await Yg(vu(n),JSON.stringify(e,null,2)),!0}catch{return ut(),!1}}async function XT(n){let e=se(n);try{await WT(Xg(),{recursive:!0})}catch{return}let t=[];for(let o=0;o<Gg;o++)t.push(zg+o*an);let r=await Promise.all(t.map(async o=>({base:o,block:await Oo(o)})));for(let{base:o,block:a}of r)if(a?.cwd===e)return o;let s={cwd:e,slots:{},mirrors:{}};for(let{base:o,block:a}of r){if(!a)try{return await Yg(vu(o),JSON.stringify(s,null,2),{flag:"wx"}),o}catch(l){if((await Oo(o))?.cwd===e)return o;l.code!=="EEXIST"&&ut();continue}if(!HT(a.cwd))return await Co(o,s)?o:void 0}let i=r.filter(o=>!!o.block).sort((o,a)=>Wg(o.block)-Wg(a.block))[0];if(i)return await Co(i.base,s)?i.base:void 0}function Wg(n){return Object.values(n.slots).reduce((e,t)=>Math.max(e,t.usedAt??0),0)}async function QT(n,e){let t=await Oo(n)??{cwd:"",slots:{},mirrors:{}};if(e.kind==="mirror"){let i=t.mirrors[e.agent];if(i!==void 0)return i;let o=Kg(t),a=o.has(0)?VT:0;for(;a>To&&o.has(a);)a--;return t.mirrors[e.agent]=a,await Co(n,t),a}let r=se(e.file),s=t.slots[r]?.offset;if(s===void 0){let i=Kg(t);for(s=To;s<an&&i.has(s);)s++;if(s>=an){let o=Object.entries(t.slots).sort((a,l)=>(a[1].usedAt??0)-(l[1].usedAt??0))[0];s=o?o[1].offset:To,o&&delete t.slots[o[0]]}}return t.slots[r]={offset:s,usedAt:Date.now()},await Co(n,t),s}function Kg(n){return new Set([...Object.values(n.mirrors),...Object.values(n.slots).map(e=>e.offset)])}async function Gn(n,e){let t=await XT(e);return t===void 0?void 0:t+await QT(t,n)}async function ln(n){let e=se(n);for(let t=0;t<Gg;t++){let r=await Oo(zg+t*an);if(r?.cwd===e)return s=>r.slots[se(s)]?.usedAt??0}return()=>0}async function _o(n){if(n.explicit!==void 0)return await Po(n.explicit)||await Su(n.explicit),n.target.kind==="bulb"&&await Gn(n.target,n.cwd),{port:n.explicit};let e=await Gn(n.target,n.cwd);if(e!==void 0){let r=e-e%an,s=Date.now()+zT;do{if(await Po(e))return{port:e};await new Promise(i=>setTimeout(i,150))}while(Date.now()<s);for(let i=To;i<an;i++){let o=r+i;if(o!==e&&await Po(o))return{port:o,note:`(slot ${e} was busy)`}}}return{port:await GT(YT),note:e===void 0?"(no port block available)":"(this project's block was full)"}}var zg,an,Gg,YT,To,VT,zT,cn=S(()=>{"use strict";nt();zg=20100,an=100,Gg=19,YT=23e3,To=1,VT=an-1,zT=3e3});var Le,Ro=S(()=>{"use strict";Le="0.60.2"});var Qg={};ci(Qg,{agentViewerCommand:()=>ly,bulbServerCommand:()=>ay,clearServerLog:()=>Tu,findProjectViewer:()=>$o,isAlive:()=>Xn,isMirror:()=>me,isProjectBulb:()=>Ru,isProjectMirror:()=>er,launchAgentViewer:()=>$u,launchBulbServer:()=>Ss,listBulbServers:()=>Q,probePageCount:()=>Nu,readServerLog:()=>Ye,readWaitCursor:()=>No,recordDenial:()=>_u,registerServer:()=>Cu,relayOpen:()=>Iu,runMarker:()=>Pu,serverLogPath:()=>Zn,serversForBulb:()=>dn,sliceRunLog:()=>Ou,startServerLog:()=>vs,stopBulbServer:()=>Qn,stopServer:()=>Bt,stopServersForBulb:()=>Lo,typebulbBinPath:()=>tr,unregisterServer:()=>un,writeWaitCursor:()=>ws});import{spawn as Zg}from"child_process";import{readdir as ZT,readFile as ny,writeFile as ry,unlink as ku,mkdir as eP}from"fs/promises";import{mkdirSync as sy,writeFileSync as Io,appendFileSync as tP,readFileSync as xu}from"fs";import*as Te from"path";import{fileURLToPath as nP}from"url";function Eu(n){return Te.join(dt(),`${n}.json`)}function Zn(n){return Te.join(dt(),`${n}.log`)}function Au(n){return Te.join(dt(),`${n}.wait.json`)}function iy(n){try{let e=JSON.parse(xu(Au(n),"utf8"));return e.cursors&&typeof e.cursors=="object"?e.cursors:{}}catch{return{}}}function No(n,e=""){let t=iy(n)[e];return typeof t=="number"&&Number.isFinite(t)&&t>=0?t:void 0}function ws(n,e,t=""){try{sy(dt(),{recursive:!0});let r=iy(n);r[t]=e,Io(Au(n),JSON.stringify({cursors:r}))}catch{}}function vs(n){let e=Zn(n);try{sy(dt(),{recursive:!0}),Io(e,"")}catch{return()=>{}}let t=process.stdout.write.bind(process.stdout),r=process.stderr.write.bind(process.stderr),s=0,i=!1,o=a=>{if(!i)try{let l=typeof a=="string"?Buffer.from(a,"utf8"):Buffer.from(a);if(tP(e,l),s+=l.length,s>2*ey){let c=xu(e),f=c.subarray(Math.max(0,c.length-ey));Io(e,f),s=f.length}}catch{i=!0}};return process.stdout.write=((a,...l)=>(o(a),t(a,...l))),process.stderr.write=((a,...l)=>(o(a),r(a,...l))),()=>{process.stdout.write=t,process.stderr.write=r}}function Tu(n){try{Io(Zn(n),"")}catch{}}function Ye(n,e=0){try{let t=xu(Zn(n)),r=e>=0&&e<=t.length?e:0;return{text:t.subarray(r).toString("utf8"),offset:t.length}}catch{return{text:"",offset:0}}}function Pu(n){return`\u2500\u2500 run ${n} \u2500\u2500 ${new Date().toTimeString().slice(0,8)}`}function sP(n){let e=rP.exec(n);return e?Number(e[1]):void 0}function Ou(n,e){let t=n.split(`
315
315
  `),r=[];if(t.forEach((o,a)=>{let l=sP(o);l!==void 0&&r.push({id:l,idx:a})}),!r.length)return e==="latest"?n:"";let s=e==="latest"?r[r.length-1]:r.find(o=>o.id===e);if(!s)return"";let i=r.find(o=>o.idx>s.idx);return t.slice(s.idx,i?i.idx:t.length).join(`
316
316
  `)}function Xn(n){try{return process.kill(n,0),!0}catch(e){return e.code==="EPERM"}}async function Cu(n){try{await eP(dt(),{recursive:!0}),await ry(Eu(n.pid),JSON.stringify({version:Le,...n}))}catch{ut()}}async function _u(n,e){let t=Eu(n);try{let r=JSON.parse(await ny(t,"utf8"));if(r.denied===e)return;r.denied=e,await ry(t,JSON.stringify(r))}catch{}}async function un(n){await ku(Eu(n)).catch(()=>{}),await ku(Zn(n)).catch(()=>{}),await ku(Au(n)).catch(()=>{})}function me(n){return n.agent!=null}function er(n,e){return me(n)&&n.cwd!=null&&se(n.cwd)===se(e)}function Ru(n,e){return!me(n)&&Jg(n.file,e)}async function Q(n){let e;try{e=await ZT(dt())}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 ny(Te.join(dt(),s),"utf8"))}catch{await un(i);return}o&&typeof o.pid=="number"&&Xn(o.pid)?t.push(o):await un(i)})),(n?t.filter(s=>Ru(s,n)):t).sort((s,i)=>s.startedAt-i.startedAt)}async function $o(n,e){return(await Q()).find(t=>er(t,n)&&(e?t.agent===e:!0))}async function Iu(n,e){let t=new URL(n).port,r=(await Q()).filter(a=>String(a.port)!==t),s=r.filter(a=>er(a,e)),i=r.filter(a=>Ru(a,e)),o=0;for(let a of[...s,...i])try{let l=await fetch(`${a.url}/__open`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({url:n}),signal:AbortSignal.timeout(2e3)});if(!l.ok)continue;let{opened:c,pages:f}=await l.json();if(c)return{where:"editor",via:a.agent?"the agent mirror":`${Te.basename(a.file)}'s page`};me(a)&&(o+=f??0)}catch{}return o>0?{where:"external"}:void 0}async function Qn(n){try{process.kill(n,"SIGTERM")}catch{}await un(n)}async function Nu(n){try{let e=await fetch(`${n.url}/__pages`,{signal:AbortSignal.timeout(iP)});if(!e.ok)return;let{pages:t}=await e.json();return typeof t=="number"?t:void 0}catch{return}}async function Bt(n,e=me(n)?"keep":"close"){try{let t=await fetch(`${n.url}/__stop`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({pages:e}),signal:AbortSignal.timeout(ty)});if(!t.ok)throw new Error(`/__stop ${t.status}`);let{closed:r=0,stuck:s=0}=await t.json(),i=Date.now()+ty;for(;Xn(n.pid)&&Date.now()<i;)await oy(100);return Xn(n.pid)?(await Qn(n.pid),"killed"):(await un(n.pid),{closed:r,stuck:s})}catch{return await Qn(n.pid),"killed"}}function dn(n,e){let t=se(e);return n.filter(r=>!me(r)&&r.pid!==process.pid&&se(r.file)===t)}async function Lo(n,e){let t=dn(await Q(),n);for(let r of t)await Bt(r,e!==void 0&&r.port===e?"keep":"close");return t}function oP(n,e,t){(process.platform==="win32"?Zg("cmd",["/c","start","","/b",n,...e],{cwd:t,stdio:"ignore",windowsHide:!0}):Zg(n,e,{cwd:t,detached:!0,stdio:"ignore"})).unref()}function tr(){return Te.join(Te.dirname(nP(import.meta.url)),"index.js")}function ay(n,e={}){return{command:process.execPath,args:[tr(),...e.trust?["--trust"]:[],...e.mode?["--mode",e.mode]:[],n,...e.open===!1?["--no-open"]:[]]}}function ly(n){return{command:process.execPath,args:[tr(),`agent:${n}`,"--no-open"]}}async function cy(n,e,t,r){let s=await n();if(s)return s;oP(e.command,e.args,t);let i=Date.now()+2e4;for(;Date.now()<i;){await oy(150);let o=await n();if(o)return o}throw new Error(`Launched ${r} but it did not register within 20s.`)}async function Ss(n,e={}){let t=e.cwd??process.cwd(),r=Te.resolve(t,n),s=await Gn({kind:"bulb",file:r},t),[i]=await Lo(r,s),o={...e,mode:e.mode??i?.mode};return cy(async()=>dn(await Q(),r)[0],ay(n,o),t,Te.basename(n))}async function $u(n,e=process.cwd()){return cy(()=>$o(e,n),ly(n),e,`the ${n} mirror`)}var ey,rP,ty,iP,oy,$e=S(()=>{"use strict";nt();cn();Ro();ey=1e6;rP=/^── run (\d+) ── /;ty=6e3,iP=500;oy=n=>new Promise(e=>setTimeout(e,n))});function nr(n){return{...Zf(n),server:n.files.get("server.ts")??""}}function Mo(n){return!!n.server&&!n.code}var ks=S(()=>{"use strict";ee();ee()});function Bo(n){let e=/^---[^\n]*\n([\s\S]*?)\n---/.exec(n),t=e?/^\s*name:\s*(.+?)\s*$/m.exec(e[1]):null;return t?t[1].replace(/^["']|["']$/g,""):void 0}function Lu(n){return Kn(Bo(n)??"bulb")||"bulb"}var Mu=S(()=>{"use strict";ee()});import{readdirSync as aP,readFileSync as lP,statSync as cP}from"fs";import{join as uy,basename as uP}from"path";function fy(n,e,t){if(e>8)return t;let r;try{r=aP(n,{withFileTypes:!0})}catch{return t}for(let s of r)if(s.isDirectory()){if(s.name.startsWith(".")||s.name==="node_modules")continue;fy(uy(n,s.name),e+1,t)}else s.isFile()&&s.name.endsWith(".bulb.md")&&t.push(uy(n,s.name));return t}function dP(n,e){let t,r=!1;try{let s=lP(n,"utf8");t=Bo(s.slice(0,1024));let i=We(s);r=!!i&&Mo(nr(i))}catch{}return{mtime:e,name:t??uP(n).replace(/\.bulb\.md$/,""),serverOnly:r}}function Do(n){let e=new Map,t=fy(n,0,[]).map(r=>{let s=0;try{s=cP(r).mtimeMs}catch{}let i=s?dy.get(r):void 0,o=i?.mtime===s?i:dP(r,s);return s&&e.set(r,o),{path:r,...o}});return dy=e,t}var dy,py=S(()=>{"use strict";ee();ks();Mu();dy=new Map});function rr(n){if(n.server.trim())return"server-side code (server.ts)";let e=n.code;if(/\btb\s*\.\s*fs\b/.test(e)||e.includes("/__fs"))return"the filesystem";if(/\btb\s*\.\s*ai\b/.test(e)||e.includes("/__ai")||n.infer.trim()||/\btb\s*\.\s*infer\b/.test(e))return"AI (your API keys)";if(/\btb\s*\.\s*server\s*\.\s*\w/.test(e)||e.includes("/__api"))return"server-side code (server.ts)"}var jo=S(()=>{"use strict"});import{readFile as fP}from"fs/promises";async function Bu(n){try{let e=We(await fP(n,"utf-8"));return e?rr(nr(e)):void 0}catch{return}}var hy=S(()=>{"use strict";ks();jo()});function Du(n){let e=n.split(""),t=(i,o)=>{for(let a=i;a<o&&a<e.length;a++)e[a]!==`
317
317
  `&&(e[a]=" ")},r=0,s="";for(;r<n.length;){let i=n[r],o=n[r+1];if(i==="/"&&o==="/"){let a=r;for(;a<n.length&&n[a]!==`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typebulb",
3
- "version": "0.60.1",
3
+ "version": "0.60.2",
4
4
  "description": "Typebulb CLI to run single-file markdown apps called bulbs, either as standalone web apps or embedded in agent responses.",
5
5
  "keywords": [
6
6
  "bulb",