typebulb 0.20.1 → 0.20.3
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 +1 -1
- package/dist/agents/claude/styles.css +4 -0
- package/dist/agents/pi/styles.css +4 -0
- package/dist/index.js +3 -3
- package/package.json +1 -1
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
|
|
|
@@ -1191,6 +1191,10 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1191
1191
|
padding-left: .6rem;
|
|
1192
1192
|
border-left: 2px solid color-mix(in srgb, var(--fg) 18%, transparent);
|
|
1193
1193
|
opacity: .82;
|
|
1194
|
+
/* Same column geometry as .messages — the tools-only negative-margin rule assumes a 1rem gap. */
|
|
1195
|
+
display: flex;
|
|
1196
|
+
flex-direction: column;
|
|
1197
|
+
gap: 1rem;
|
|
1194
1198
|
/* --turn-color is a custom property and INHERITS — the stub's own .bubble.turn-N sets it, so without
|
|
1195
1199
|
this reset every nested orphan bubble inherits the parent turn's color and redraws the stripe
|
|
1196
1200
|
(the recursive-line bug). Reset to transparent so .bubble::before falls back to no stripe here. */
|
|
@@ -1191,6 +1191,10 @@ a.server-port:hover { text-decoration: underline; }
|
|
|
1191
1191
|
padding-left: .6rem;
|
|
1192
1192
|
border-left: 2px solid color-mix(in srgb, var(--fg) 18%, transparent);
|
|
1193
1193
|
opacity: .82;
|
|
1194
|
+
/* Same column geometry as .messages — the tools-only negative-margin rule assumes a 1rem gap. */
|
|
1195
|
+
display: flex;
|
|
1196
|
+
flex-direction: column;
|
|
1197
|
+
gap: 1rem;
|
|
1194
1198
|
/* --turn-color is a custom property and INHERITS — the stub's own .bubble.turn-N sets it, so without
|
|
1195
1199
|
this reset every nested orphan bubble inherits the parent turn's color and redraws the stripe
|
|
1196
1200
|
(the recursive-line bug). Reset to transparent so .bubble::before falls back to no stripe here. */
|
package/dist/index.js
CHANGED
|
@@ -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.3";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