typebulb 0.39.3 → 0.41.0

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
@@ -16,7 +16,7 @@ This document is dedicated to the typebulb CLI. At its core, it compiles and ser
16
16
  ## Features
17
17
 
18
18
  - **Server-side code** — Add a `**server.ts**` section; exported functions become callable from the browser via `tb.server.<name>()` (e.g., `export async function query(...)` → `await tb.server.query(...)`). An `export async function*` **streams**: consume it with `for await (const chunk of tb.server.gen())`. Requires `--trust`.
19
- - **CLI logging** — `tb.server.log(...)` prints to the CLI's stdout
19
+ - **CLI logging** — `tb.log(...)` prints to the CLI's stdout, from `code.tsx` and `server.ts` alike (no trust needed)
20
20
  - **Wake-on-event** — `typebulb wait <file|agent>` blocks until the target server logs a new line, prints it, and exits. Run in the background, that exit *is* an agent's wake-up: a user action a bulb logs, or an embed's render outcome — no polling.
21
21
  - **Env files** — `.env` / `.env.local` load from cwd, `.env.local` overriding `.env` (an exported shell var wins over both). `--mode <name>` adds `.env.<name>` to switch environments (local/staging/prod); a startup line reports which keys loaded from where.
22
22
  - **Server mode** — `--server` runs only the `**server.ts**` section in Node, skipping the web server. Bulbs with only `**server.ts**` (no `**code.tsx**`) use this mode automatically.
@@ -61,7 +61,7 @@ typebulb stop --global Stop every running bulb and mirror, all projects
61
61
  typebulb trust [file] Remember a bulb as trusted (no arg: list trusted bulbs)
62
62
  typebulb untrust <file> Forget a bulb's trust (back to Restricted)
63
63
  typebulb --no-watch <file> Disable hot reload
64
- typebulb --port 3333 <file> Custom port
64
+ typebulb --port 3333 <file> Bind this exact port (fails if taken)
65
65
  typebulb --no-open <file> Don't auto-open browser
66
66
  typebulb --mode <name> <file> Also load .env.<name> on top of .env / .env.local
67
67
  typebulb --batch <name> <file> Scope tb.dir + relative tb.fs paths to <bulb-folder>/batches/<name> (batch runs)
@@ -190,6 +190,7 @@ everywhere.
190
190
  | `tb.url()` | Get the bulb URL (the served localhost URL, locally) | |
191
191
  | `tb.models()` | List available AI models (for dynamic model selectors); the `.env` default is flagged (`default: true`); returns `[]` when embedded (no host AI) | |
192
192
  | `tb.hasOwnKeys()` | Whether the user's own AI keys back `tb.ai` — `false` means courtesy model only; always `false` embedded | |
193
+ | `tb.log(...)` | Print to the CLI's stdout (read back with `typebulb logs`); falls back to the browser console when no CLI serves the page | |
193
194
  | `tb.onMessage(cb)` | Receive a value pushed in from the terminal by `typebulb send`; a non-`undefined` return becomes the reply `send --wait` prints — inert when embedded (no sender) | |
194
195
  | `tb.fs.read/readBytes/write` | Read and write local files | yes |
195
196
  | `tb.dir` | The bulb's folder (absolute path), where relative `tb.fs` paths land | |
@@ -242,13 +243,13 @@ The agent mirror turns that block into a live, sandboxed app, with a *breakout
242
243
 
243
244
  ### Emitting a local bulb
244
245
 
245
- - **Launch once, with `--no-open`.** `npx typebulb foo.bulb.md --no-open` starts the server; share the printed link for the user to open.
246
+ - **Launch once, and share the printed link.** `npx typebulb foo.bulb.md` starts the server and opens a tab (in VS Code's terminal it prints the link for the user to click instead). The link stays good — a bulb keeps its port across runs.
246
247
 
247
248
  ### Iterating on a local bulb
248
249
 
249
250
  That one launch *is* the loop: the server watches the file, so every save recompiles and reloads the page (`server.ts` included) — editing the file is the iteration.
250
251
 
251
- - **Don't relaunch, and don't wrap it in `timeout`.** A relaunch only replaces the running server (one per bulb file); `timeout` kills it, and the racing relaunch is what spawns a second window on a fresh port.
252
+ - **Don't relaunch, and don't wrap it in `timeout`.** A relaunch replaces the running server (one per bulb file), reclaiming the same port so the open tab reloads itself — it costs the page's in-memory state and nothing else. `timeout` kills it outright, and the racing relaunch is what spawns extra windows.
252
253
  - **What needs a restart:** a `.env` change (read once at boot) and in-memory `server.ts` state (reset on each reload).
253
254
  - **Each reload re-runs the bulb.** A save re-executes `code.tsx` from scratch, so work you start on mount repeats every edit — re-spending GPU/network, re-firing side effects, flooding the log. Put expensive or side-effecting work behind a trigger: `tb.onMessage(() => start())`, then `typebulb send <file>` when ready (also a general terminal→page channel — pass params, drive a loop).
254
255
  - **Reading the log:** it appends across every reload, so `typebulb logs --run latest <file>` shows just the current run (no need to clear).
@@ -261,13 +262,13 @@ That one launch *is* the loop: the server watches the file, so every save recomp
261
262
  - **Structured selftest** — a handler that returns `{ count, verdict }` beats one that logs prose: `typebulb send <file> selftest --wait` prints the object as JSON, and you assert on fields instead of parsing `logs`. At most one handler, in one page, may return a value; a slow check needs `--wait=<ms>` above the 5s default.
262
263
  - **Rendered truth** — `typebulb send <file> tb:snapshot` prints the page's accessibility outline (roles, names, visible text) without disturbing its state. Use it when logs say ok but the screen might not, and as the first probe on a live page in a state you can't reproduce — a save would hot-reload and destroy it. (`tb:` messages are answered by the runtime, never your handlers, and imply `--wait`.)
263
264
  - **Poking state** — to tweak a value in a live page (a save hot-reloads and destroys its state), author a set-handler up front: a `tb.onMessage` branch that takes a data payload (JSON arrives parsed), applies it to your state — committing the change if your framework needs an explicit step — and returns the new state: `typebulb send <file> '{"set":"speed","value":2}' --wait` prints it. In React, register it in an effect so it closes over the setters (the returned unsubscribe is the cleanup). Adding the handler later is itself the edit that destroys the state.
264
- - **A page must be open** — `send` reports "no page connected" until someone opens the printed link; the CLI runs no browser of its own.
265
+ - **A page must be open** — the CLI runs no browser of its own, so every client-side check waits on a real window (and `--no-open` means there isn't one). `send` says which case it is: nobody has ever connected (share the link), or a page dropped and hasn't returned (it's stale — reload it).
265
266
 
266
267
  ### Emitting a server-only bulb
267
268
 
268
269
  A `**server.ts**` block with no `**code.tsx**` is a headless bulb — no UI, no port, absent from the launcher. Under `--trust` its code can use `tb.fs`, and call `tb.ai`, `tb.ai.stream`, and `tb.models` against your `.env` keys.
269
270
 
270
- - **Invoke one export with `typebulb call <file> <fn> [args…] --trust`.** It boots, runs that export, prints the `return` as JSON to stdout, and exits — a fresh boot per call. Log with `console.log` (no `tb.server.log` here); under `call` logs go to stderr, so the JSON result owns stdout.
271
+ - **Invoke one export with `typebulb call <file> <fn> [args…] --trust`.** It boots, runs that export, prints the `return` as JSON to stdout, and exits — a fresh boot per call. Under `call`, logs (`console.log` / `tb.log`) go to stderr, so the JSON result owns stdout.
271
272
 
272
273
  ## Sizing
273
274
 
@@ -290,7 +291,8 @@ The host owns a bulb's **width**; you own its **height**.
290
291
  - **A bulb's working files land beside it automatically** — relative `tb.fs` paths resolve to the bulb's folder, in `code.tsx` and `server.ts` alike: `tb.fs.write('run.json')`, no path prefix, no mkdir.
291
292
  - **Images & media: an `assets/` subfolder of the bulb's folder** (`birds.bulb.md` → `birds/assets/robin.png`) — `<img src="assets/robin.png">` just works (always that relative form, never `/assets/…`), every tier except embedded.
292
293
  - **Batch runs: scope with `--batch`, don't hand-roll plumbing** — `--batch pilot` on a run or `call` lands `tb.dir` and relative `tb.fs` paths in `<bulb-folder>/batches/pilot/`; the bulb's code stays batch-unaware, an unscoped run sees `batches/` as an ordinary subfolder, and the agent mirror lists a bulb's batches on its launcher row (play opens the newest).
293
- - **Self-testing a local bulb** — To confirm a bulb works, run it, instrument with `tb.server.log(...)` (prints to the server's stdout, captured in the log — and works **even on a Restricted bulb**), and read it back with `typebulb logs`. That's the loop to verify behaviour without asking the user to copy-paste console output. `tb.fs.write(...)` is handy for dumping large outputs.
294
+ - **See what's already running** — `typebulb logs` with no argument lists every running bulb and mirror; check it before launching anything.
295
+ - **Self-testing a local bulb** — To confirm a bulb works, run it, instrument with `tb.log(...)`, and read it back with `typebulb logs`. That's the loop to verify behaviour without asking the user to copy-paste console output. `tb.fs.write(...)` is handy for dumping large outputs.
294
296
  - **Self-testing client code** — gate checks behind `tb.onMessage(m => { if (m === 'selftest') return run() })`, trigger with `typebulb send <file> selftest --wait`, and assert on the JSON reply — see [Interrogating the live page](#interrogating-the-live-page).
295
297
  - **Testing a `server.ts` export directly** — `typebulb call <file> <fn> [arg…]` boots `server.ts`, invokes one export, and prints its return as JSON to stdout (logs/errors to stderr, so `… | jq` works). Args after `<fn>` are JSON-or-string; `--args '<json-array>'` (or `--args -` for stdin) escapes tricky quoting. Needs `--trust`.
296
298
  - **Mount to the container your `index.html` declares.** The corpus convention is `<div id="root"></div>` with `createRoot(document.getElementById("root")!)`.