typebulb 0.21.4 → 0.22.1

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.
@@ -2217,7 +2217,7 @@ var Patcher = class _Patcher {
2217
2217
 
2218
2218
  // cli/agents/pi/server/piPatcherExtension.ts
2219
2219
  var DESCRIPTION = true ? "Apply unified diffs to files \u2014 tolerant of form, strict about intent. Repairs sloppy AI-generated diffs (mangled headers, whitespace drift, missing prefixes; line numbers can be wrong or absent \u2014 hunks anchor by fuzzy-matched context lines) and applies all hunks atomically when the intent is unambiguous; fails with a precise, typed error when it isn't. Use for multi-hunk edits in one step, or when exact string-replacement editing fails on whitespace or invisible characters." : "Apply unified diffs to files.";
2220
- var BUILD_TAG = true ? "matchu-patchu-pi 0.2.0, built 2026-07-08 16:28:04" : "matchu-patchu-pi dev";
2220
+ var BUILD_TAG = true ? "matchu-patchu-pi 0.2.0, built 2026-07-09 10:48:35" : "matchu-patchu-pi dev";
2221
2221
  var errorBlocks = (errors) => {
2222
2222
  const parts = [`Patch failed with ${errors.length} error(s):`];
2223
2223
  for (const e of errors) parts.push("", e.toString());
@@ -856,6 +856,34 @@ a.server-port:hover { text-decoration: underline; }
856
856
  width: 100%;
857
857
  margin: 0 auto;
858
858
  }
859
+ /* Top-of-transcript wordmark + tagline (messageList.ts #masthead). Centred on the prose column,
860
+ scrolls with the conversation. Both logos render; theme picks which shows. */
861
+ .masthead {
862
+ max-width: var(--content-max);
863
+ width: 100%;
864
+ margin: 0 auto 1rem auto;
865
+ padding: .35rem 0 .35rem;
866
+ display: flex;
867
+ flex-direction: column;
868
+ align-items: flex-start;
869
+ gap: .5rem;
870
+ text-align: left;
871
+ user-select: none;
872
+ }
873
+ .masthead-logo {
874
+ width: 170px;
875
+ max-width: 55%;
876
+ height: auto;
877
+ opacity: .92;
878
+ }
879
+ .masthead-tagline {
880
+ color: var(--muted);
881
+ font-size: .85rem;
882
+ }
883
+ /* Black wordmark on light, outline on dark. */
884
+ .masthead-logo.dark { display: none; }
885
+ html[data-theme="dark"] .masthead-logo.light { display: none; }
886
+ html[data-theme="dark"] .masthead-logo.dark { display: block; }
859
887
  .bubble {
860
888
  position: relative;
861
889
  max-width: var(--content-max);
Binary file
Binary file
package/dist/index.js CHANGED
@@ -431,11 +431,22 @@ export default function (pi) {
431
431
  // the user already sees the bulb and the skill orders silence on ok, so a wake would spend
432
432
  // a whole turn saying nothing and render a noise message. Silence IS the ok. Anything else
433
433
  // (an embed error, a turn-based loop event) still wakes; an unrecognized shape wakes too.
434
- const embedOk = code === 0 && /^\[embed [^\n]*\] ok$/.test(text);
434
+ // Checked PER LINE, not over the whole blob: wait lingers 10s and bursts every matching line
435
+ // into one payload, and a version-agnostic --match ("[embed <name>") replays v1+v2+... from
436
+ // log start, so an all-ok payload is routinely multi-line. Suppress iff EVERY line is an ok;
437
+ // one error line among oks still wakes.
438
+ var waitLines = text.split("\n").map(function (l) { return l.trim(); }).filter(Boolean);
439
+ const embedOk = code === 0 && waitLines.length > 0 && waitLines.every(function (l) { return /^\[embed .*\] ok$/.test(l); });
435
440
  if (code === 0 && text && !embedOk) {
436
441
  pi.sendUserMessage("typebulb wait result:\n" + text, { deliverAs: "followUp" });
437
442
  } else if (ctx.ui && typeof ctx.ui.notify === "function") {
438
- ctx.ui.notify("typebulb wait: " + (embedOk ? text : code === 2 ? "timed out, nothing to report" : "ended (code " + code + ")"), "info");
443
+ // INVARIANT: this notify must never quote the "[embed <name>" tag verbatim. In a
444
+ // composer-driven session the mirror's driver echoes extension notifies into the
445
+ // mirror's own log (driver.ts) — the very log wait watches by substring — so a
446
+ // verbatim tag re-fires the next same-match wait with a non-ok line: a self-
447
+ // sustaining wake loop that defeats this suppression (TB-Wait.md). Stripping the
448
+ // brackets kills the matchable substring while keeping the verdict readable.
449
+ ctx.ui.notify("typebulb wait: " + (embedOk ? text.replace(/[\[\]]/g, "") : code === 2 ? "timed out, nothing to report" : "ended (code " + code + ")"), "info");
439
450
  }
440
451
  } catch (e) { logErr("exit", e && e.message); }
441
452
  });
@@ -1415,11 +1426,11 @@ ${w0}
1415
1426
  </html>`}var GE={claude:()=>Promise.resolve().then(()=>(kh(),Sh)),pi:()=>Promise.resolve().then(()=>(Ah(),Eh))};async function Oh(r){let e=process.cwd(),t=r.agentTarget;vp(t)||(console.error(`Unknown agent '${t}'. Known: ${ir().join(", ")}.`),process.exit(1));let n=await vc(e,t);if(n){console.log(`Mirror '${t}' is already running for this project:
1416
1427
  ${n.url}`),r.open&&await Ph(n.url);return}let s=gi(process.pid),i=r.watch?new YE:void 0,o=xe(r.mode),a=await GE[t](),c=a.displayName||`${t} mirror`;st(o,Z.join(e,"mirror"));let l=Z.dirname(VE(import.meta.url)),d=Z.join(l,"agents",t),u=Z.join(l,"..","cli","agents"),f=Z.join(d,"styles.css"),p=Z.join(d,"index.html"),g=()=>Fm({name:c,agent:t,styles:Th(f,"utf8"),mountHtml:Th(p,"utf8"),watch:r.watch}),m=await Sn(r.port),y=await Ii({getHtml:g,basePath:e,port:m,reloadEmitter:i,getServerExports:()=>a,trusted:!0,staticAssets:{mount:`${Z.posix.dirname(Wc(t))}/`,dir:d}}),x=`http://localhost:${m}`;await yi({pid:process.pid,port:m,url:x,file:`agent:${t}`,cwd:e,startedAt:Date.now(),trust:!0,agent:t}),console.log(`
1417
1428
  ${c}`),console.log(` ${x}`),m!==r.port&&console.log(` (port ${r.port} was busy)`),r.watch&&console.log(` Watching for changes...
1418
- `);let A=Z.join(u,t,"client"),h=Z.join(u,"core","client");async function w(){await(await import("esbuild")).build({entryPoints:[Z.join(A,"index.ts")],bundle:!0,platform:"browser",format:"esm",outfile:Z.join(d,"client.js")});for(let T of["styles.css","index.html"])WE(Z.join(h,T),Z.join(d,T))}let v;if(r.watch&&i){let O=HE(A),T=!1;v=Ot({target:O?u:d,events:"all",onChange:async()=>{if(!T){T=!0;try{O&&await w(),console.log(`Mirror rebuilt. Browser reloading...
1429
+ `);let A=Z.join(u,t,"client"),h=Z.join(u,"core","client");async function w(){await(await import("esbuild")).build({entryPoints:[Z.join(A,"index.ts")],bundle:!0,platform:"browser",format:"esm",outfile:Z.join(d,"client.js")});for(let T of["styles.css","index.html","typebulb.png","typebulb-inv.png"])WE(Z.join(h,T),Z.join(d,T))}let v;if(r.watch&&i){let O=HE(A),T=!1;v=Ot({target:O?u:d,events:"all",onChange:async()=>{if(!T){T=!0;try{O&&await w(),console.log(`Mirror rebuilt. Browser reloading...
1419
1430
  `),i.emit("reload")}catch(I){console.error("Mirror rebuild failed:",I instanceof Error?I.message:I)}finally{T=!1}}}})}r.open&&await Ph(x);let k=async()=>{console.log(`
1420
1431
  Shutting down...`);try{a.shutdownSwitcher?.()}catch{}try{a.shutdownComposer?.()}catch{}y.close(),v?.(),s(),await kt(process.pid),process.exit(0)};process.on("SIGINT",k),process.on("SIGTERM",k)}import*as Rh from"path";async function _h(r,e,t,n,s){let i=xe(t),o=!1,a=async()=>{let{bulb:c,config:l}=await he(r);o||(st(i,r,c.server),o=!0),await un(c.server,s,n,l.dependencies)};console.log(`Running ${Rh.basename(r)}...`),await a(),e&&(console.log(`Watching for changes...
1421
1432
  `),Ot({target:r,onChange:async()=>{try{console.log("Re-running..."),await a()}catch(c){console.error("Error:",c)}}}))}import{Console as zE}from"node:console";Me();async function Ih(r,e,t,n,s){tA();try{let p=cr(await K(),r)[0];p&&(hi(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=xe(t),{bulb:o,config:a}=await he(r);o.server||Tn("This bulb has no **server.ts** block; nothing to call."),st(i,r,o.server);let c;try{c=await un(o.server,s,n,a.dependencies)}catch(p){Tn(p instanceof Error?p.message:String(p))}let l=Ci(c,e.fn);if(!l){let p=[...Object.keys(c).filter(g=>typeof c[g]=="function"),...Object.keys(qc)];Tn(`Function '${e.fn}' not found. Available: ${p.length?p.join(", "):"(none)"}.`)}let d=await QE(e);if(Ni(l)){try{for await(let p of l(...d))await Nh(JSON.stringify(p,Lh)+`
1422
1433
  `)}catch(p){Tn(p instanceof Error?p.stack??p.message:String(p))}return Ch(0)}let u;try{u=await l(...d)}catch(p){Tn(p instanceof Error?p.stack??p.message:String(p))}let f=eA(u);f!==void 0&&await Nh(f+`
1423
1434
  `),Ch(0)}function Ch(r){process.exitCode=r,setTimeout(()=>process.exit(r),2e3).unref?.()}async function QE(r){if(r.hasArgsFlag){let e=r.argsJson??"";return e==="-"&&(e=await rA()),ZE(e)}return XE(r.positional)}function XE(r){return r.map(e=>{try{return JSON.parse(e)}catch{return e}})}function ZE(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 eA(r){if(r!==void 0)return JSON.stringify(r,Lh,2)}function Lh(r,e){return typeof e=="bigint"?e.toString():e}function Tn(r){process.stderr.write(r+`
1424
- `),process.exit(1)}function tA(){let r=new zE(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 Nh(r){return new Promise((e,t)=>{process.stdout.write(r,n=>n?t(n):e())})}async function rA(){let r=[];for await(let e of process.stdin)r.push(e);return Buffer.concat(r).toString("utf-8")}var nl="0.21.4";function $h(r,e){r||(console.error(`This bulb runs server-side Node code (server.ts), which --trust must authorize:
1435
+ `),process.exit(1)}function tA(){let r=new zE(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 Nh(r){return new Promise((e,t)=>{process.stdout.write(r,n=>n?t(n):e())})}async function rA(){let r=[];for await(let e of process.stdin)r.push(e);return Buffer.concat(r).toString("utf-8")}var nl="0.22.1";function $h(r,e){r||(console.error(`This bulb runs server-side Node code (server.ts), which --trust must authorize:
1425
1436
  ${e}`),process.exit(1))}async function nA(){let r=gl(process.argv.slice(2));if(r.version&&(console.log(`typebulb ${nl}`),process.exit(0)),r.help&&(yl(),process.exit(0)),bm(),r.subcommand==="logs"){await Om(r.file||void 0,{follow:r.follow,clear:r.clear,run:r.run,lines:r.lines});return}if(r.subcommand==="wait"){await Rm(r.file||void 0,{match:r.match,timeoutSec:r.timeoutSec});return}if(r.subcommand==="stop"){r.stopScope?await Cm(r.stopScope):await _m(r.file||void 0);return}if(r.subcommand==="send"){await Im(r.file,r.sendMessage,r.sendWaitMs??0);return}if(r.subcommand==="skill"){await Am(nl);return}if(r.subcommand==="models"){await xm(r.mode);return}if(r.subcommand==="agent"){await(r.agentTarget?Oh(r):km(nl));return}if(r.subcommand==="trust"||r.subcommand==="untrust"){await Rp(r.file||void 0,r.subcommand==="trust");return}let e;if(!r.file||r.file==="."){let l=await gp(process.cwd());l||(console.error("No .bulb.md file found in current directory"),process.exit(1)),e=l}else e=je.resolve(r.file);await Mh.access(e).then(()=>!0,()=>!1)||(ir().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:je.relative(process.cwd(),e)||je.basename(e),s=`npx typebulb --trust ${n.includes(" ")?`"${n}"`:n}`;if(r.subcommand==="predict"){await Op(e,s);return}let i=!r.noTrust&&St(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 he(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 ml(r.local)}catch(l){console.error(l instanceof Error?l.message:String(l)),process.exit(1)}console.log(`replace: ${a.name} \u2192 ${je.relative(process.cwd(),a.dir)||"."}`)}if(r.subcommand==="check"){await Pp(e,a);return}let c=je.dirname(e);if(r.subcommand==="call"){$h(r.trust,s),await Ih(e,{fn:r.fn,positional:r.callArgs,argsJson:r.argsJson,hasArgsFlag:r.hasArgsFlag},r.mode,a,c);return}if(o&&o.bulb.server&&(Ps(o.bulb)||r.server)){$h(r.trust,s),await _h(e,r.watch,r.mode,a,c);return}await jm(e,r,s,a,c)}nA().catch(r=>{console.error("Error:",r.message),process.exit(1)});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typebulb",
3
- "version": "0.21.4",
3
+ "version": "0.22.1",
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
  "license": "MIT",
6
6
  "repository": {