typebulb 0.30.0 → 0.31.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 +13 -3
- package/dist/agents/claude/client.js +154 -73
- package/dist/agents/pi/client.js +87 -6
- package/dist/agents/pi/matchu-patchu.ts +1 -1
- package/dist/dts/tbTypings.d.ts +1 -1
- package/dist/dts/tbTypings.d.ts.map +1 -1
- package/dist/dts/tbTypings.js +5 -3
- package/dist/dts/tbTypings.js.map +1 -1
- package/dist/index.js +282 -213
- package/dist/render.js +100 -19
- package/package.json +1 -1
package/dist/agents/pi/client.js
CHANGED
|
@@ -1224,7 +1224,8 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
1224
1224
|
|
|
1225
1225
|
// Receive a value pushed from the terminal via \`typebulb send\` (data-in, the dual of the
|
|
1226
1226
|
// ungated tb.server.log). Returns an unsubscribe fn. Inert when embedded \u2014 no server, so no
|
|
1227
|
-
// sender; the handler is registered but never fires (cf. tb.models returning []).
|
|
1227
|
+
// sender; the handler is registered but never fires (cf. tb.models returning []). A handler's
|
|
1228
|
+
// non-undefined return (awaited) becomes the reply \`send --wait\` prints (TB-Interrogation.md).
|
|
1228
1229
|
onMessage: (handler) => {
|
|
1229
1230
|
if (typeof handler === 'function') messageHandlers.add(handler);
|
|
1230
1231
|
return () => messageHandlers.delete(handler);
|
|
@@ -1240,6 +1241,60 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
1240
1241
|
set theme(v) { if (window.__tbTheme) window.__tbTheme.set(v); }
|
|
1241
1242
|
});
|
|
1242
1243
|
|
|
1244
|
+
// tb:snapshot \u2014 the page serializes its own accessibility outline (roles, names, visible text;
|
|
1245
|
+
// the YAML-not-pixels shape) as the reply to \`typebulb send <file> tb:snapshot\`
|
|
1246
|
+
// (TB-Interrogation.md). Heuristic by design: explicit role attr, a small
|
|
1247
|
+
// implicit-role map, aria-label/alt/value then visible text for names \u2014 enough to catch
|
|
1248
|
+
// text/structure divergence, which is what an agent can judge without pixels.
|
|
1249
|
+
const IMPLICIT_ROLES = { BUTTON: 'button', SELECT: 'combobox', TEXTAREA: 'textbox', OPTION: 'option', IMG: 'img', NAV: 'navigation', MAIN: 'main', HEADER: 'banner', FOOTER: 'contentinfo', ASIDE: 'complementary', FORM: 'form', DIALOG: 'dialog', TABLE: 'table', TR: 'row', TH: 'columnheader', TD: 'cell', UL: 'list', OL: 'list', LI: 'listitem', LABEL: 'label', P: 'paragraph', PROGRESS: 'progressbar', SUMMARY: 'button', FIGURE: 'figure', BLOCKQUOTE: 'blockquote', HR: 'separator' };
|
|
1250
|
+
const INPUT_ROLES = { checkbox: 'checkbox', radio: 'radio', range: 'slider', number: 'spinbutton', search: 'searchbox', button: 'button', submit: 'button', reset: 'button', hidden: '' };
|
|
1251
|
+
const snapshot = () => {
|
|
1252
|
+
const MAX_LINES = 400;
|
|
1253
|
+
const squash = (s) => (s || '').replace(/\\s+/g, ' ').trim();
|
|
1254
|
+
const clip = (s) => (s.length > 200 ? s.slice(0, 200) + '\u2026' : s);
|
|
1255
|
+
const roleOf = (el) => {
|
|
1256
|
+
const explicit = el.getAttribute('role');
|
|
1257
|
+
if (explicit) return explicit;
|
|
1258
|
+
const t = el.tagName;
|
|
1259
|
+
if (t === 'A') return el.hasAttribute('href') ? 'link' : '';
|
|
1260
|
+
if (t === 'INPUT') { const r = INPUT_ROLES[(el.getAttribute('type') || 'text').toLowerCase()]; return r === undefined ? 'textbox' : r; }
|
|
1261
|
+
if (/^H[1-6]$/.test(t)) return 'heading';
|
|
1262
|
+
return IMPLICIT_ROLES[t] || '';
|
|
1263
|
+
};
|
|
1264
|
+
const hiddenEl = (el) => {
|
|
1265
|
+
if (el.hidden || el.getAttribute('aria-hidden') === 'true') return true;
|
|
1266
|
+
const cs = getComputedStyle(el);
|
|
1267
|
+
return cs.display === 'none' || cs.visibility === 'hidden';
|
|
1268
|
+
};
|
|
1269
|
+
const lines = [];
|
|
1270
|
+
const walk = (node, depth) => {
|
|
1271
|
+
for (let ch = node.firstChild; ch && lines.length <= MAX_LINES; ch = ch.nextSibling) {
|
|
1272
|
+
if (ch.nodeType === 3) {
|
|
1273
|
+
const t = squash(ch.nodeValue);
|
|
1274
|
+
if (t) lines.push(' '.repeat(depth) + '- text: ' + clip(t));
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
if (ch.nodeType !== 1) continue;
|
|
1278
|
+
const tag = ch.tagName;
|
|
1279
|
+
if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'TEMPLATE' || tag === 'NOSCRIPT' || tag === 'LINK' || tag === 'META') continue;
|
|
1280
|
+
if (hiddenEl(ch)) continue;
|
|
1281
|
+
const role = roleOf(ch);
|
|
1282
|
+
if (!role) { walk(ch, depth); continue; } // structural (div/span/\u2026): descend transparently
|
|
1283
|
+
const attr = (n) => squash(ch.getAttribute(n) || '');
|
|
1284
|
+
const text = squash(ch.textContent);
|
|
1285
|
+
const name = attr('aria-label') || (tag === 'IMG' ? attr('alt') : '') || (typeof ch.value === 'string' ? squash(ch.value) || attr('placeholder') : '') || text;
|
|
1286
|
+
let line = role + (name ? ' "' + clip(name) + '"' : '');
|
|
1287
|
+
const lv = role === 'heading' ? (attr('aria-level') || (/^H[1-6]$/.test(tag) ? tag.charAt(1) : '')) : '';
|
|
1288
|
+
if (lv) line += ' [level=' + lv + ']';
|
|
1289
|
+
lines.push(' '.repeat(depth) + '- ' + line);
|
|
1290
|
+
if (!(name && name === text)) walk(ch, depth + 1); // subtree \u2261 name \u21D2 the one line says it all
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
walk(document.body, 0);
|
|
1294
|
+
if (lines.length > MAX_LINES) { lines.length = MAX_LINES; lines.push('- \u2026 (truncated)'); }
|
|
1295
|
+
return lines.length ? lines.join('\\n') : '(empty page)';
|
|
1296
|
+
};
|
|
1297
|
+
|
|
1243
1298
|
// Events channel (dev server only): 'reload' drives hot reload (only emitted when watching),
|
|
1244
1299
|
// 'message' delivers \`typebulb send\` pushes to tb.onMessage. Connect for a CLI-served page (http
|
|
1245
1300
|
// origin, not an embed); a srcdoc embed or a file:// static export has no server, so onMessage just
|
|
@@ -1250,11 +1305,37 @@ const something = require('module-name') // NOT SUPPORTED!
|
|
|
1250
1305
|
console.log('[typebulb] Reloading...');
|
|
1251
1306
|
window.location.reload();
|
|
1252
1307
|
});
|
|
1253
|
-
es.addEventListener('message', (e) => {
|
|
1254
|
-
// Wire payload
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1308
|
+
es.addEventListener('message', async (e) => {
|
|
1309
|
+
// Wire envelope { id?, payload } (JSON \u2014 SSE-line-safe). tb:* payloads are the shim's reserved
|
|
1310
|
+
// namespace (TB-Interrogation.md): answered by a built-in handler, never delivered to
|
|
1311
|
+
// tb.onMessage. Each settled non-undefined return is a reply candidate \u2014 the CLI enforces the
|
|
1312
|
+
// one-reply rule, the shim just reports what it collected.
|
|
1313
|
+
let env;
|
|
1314
|
+
try { env = JSON.parse(e.data) || {}; } catch { return; }
|
|
1315
|
+
const errText = (err) => String((err && err.message) || err);
|
|
1316
|
+
const results = []; const errors = [];
|
|
1317
|
+
const keep = (v) => {
|
|
1318
|
+
try {
|
|
1319
|
+
const enc = JSON.stringify(v);
|
|
1320
|
+
if (enc === undefined) throw new Error('unsupported value');
|
|
1321
|
+
results.push(enc);
|
|
1322
|
+
} catch (err) { errors.push('reply is not JSON-serializable: ' + errText(err)); }
|
|
1323
|
+
};
|
|
1324
|
+
const reservedCall = async () => {
|
|
1325
|
+
if (env.payload === 'tb:snapshot') return snapshot();
|
|
1326
|
+
throw new Error('unknown reserved message: ' + env.payload);
|
|
1327
|
+
};
|
|
1328
|
+
const value = parseMsg(env.payload);
|
|
1329
|
+
const calls = typeof env.payload === 'string' && env.payload.indexOf('tb:') === 0
|
|
1330
|
+
? [reservedCall]
|
|
1331
|
+
: Array.from(messageHandlers, (h) => async () => h(value));
|
|
1332
|
+
for (const s of await Promise.allSettled(calls.map((f) => f()))) {
|
|
1333
|
+
if (s.status === 'rejected') { console.error(s.reason); errors.push(errText(s.reason)); }
|
|
1334
|
+
else if (s.value !== undefined) keep(s.value);
|
|
1335
|
+
}
|
|
1336
|
+
if (env.id !== undefined) {
|
|
1337
|
+
try { fetch('/__send-reply', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ id: env.id, results, errors }) }); } catch {}
|
|
1338
|
+
}
|
|
1258
1339
|
});
|
|
1259
1340
|
es.onerror = () => {
|
|
1260
1341
|
// Server closed, stop trying
|
|
@@ -2280,7 +2280,7 @@ var Patcher = class {
|
|
|
2280
2280
|
|
|
2281
2281
|
// cli/agents/pi/server/piPatcherExtension.ts
|
|
2282
2282
|
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.";
|
|
2283
|
-
var BUILD_TAG = true ? "matchu-patchu-pi 0.3.1, built 2026-07-17
|
|
2283
|
+
var BUILD_TAG = true ? "matchu-patchu-pi 0.3.1, built 2026-07-17 22:27:42" : "matchu-patchu-pi dev";
|
|
2284
2284
|
var errorBlocks = (errors) => {
|
|
2285
2285
|
const parts = [`Patch failed with ${errors.length} error(s):`];
|
|
2286
2286
|
for (const e of errors) parts.push("", e.toString());
|
package/dist/dts/tbTypings.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* TypeChecker, and the CLI's emitted typecheck dirs.
|
|
10
10
|
*/
|
|
11
11
|
/** Typebulb globals available in browser-side code (code.tsx). */
|
|
12
|
-
export declare const clientTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n *\n * A normal export is awaited for its result (`await tb.server.fn()`). An `async function*`\n * export streams: `for await (const chunk of tb.server.gen())`. The call object supports both;\n * break the `for await` to cancel and tear down the server generator.\n */\n server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => void): () => void;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Relative paths resolve against the bulb's folder (`tb.dir` \u2014\n * `<bulb-dir>/<filename-stem>/`, created on demand), so\n * `tb.fs.write('results.json')` lands beside the bulb. `../` reaches sibling\n * bulbs' folders; everything stays confined to the project (the launch cwd).\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * The bulb's folder \u2014 absolute path to `<bulb-dir>/<filename-stem>/`\n * (or its subfolder when the run is scoped with `--dir`).\n *\n * For interop only (handing a path to `server.ts` or a spawned tool):\n * `tb.fs` already resolves relative paths against it, so bulb code writing\n * its own files never needs it. CLI only \u2014 throws in editor/published/embedded mode.\n */\n readonly dir: string;\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n /** True on the .env-configured default model (TB_AI_PROVIDER + TB_AI_MODEL); absent otherwise */\n default?: boolean;\n }>>;\n /**\n * Whether the user's own AI keys back `tb.ai`. False means only the\n * quota-limited courtesy model is available (or no AI at all) \u2014 a bulb\n * making many AI calls should check this and show a \"use your own keys\"\n * notice instead of running.\n */\n hasOwnKeys(): Promise<boolean>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
|
|
12
|
+
export declare const clientTbTypings = "\n/** A single streamed delta from `tb.ai.stream()`. Discriminated by `kind`. */\ntype AiChunk =\n | { kind: \"text\"; text: string }\n | { kind: \"reasoning\"; text: string };\n\n/**\n * Typebulb utilities namespace.\n * Type `tb.` to discover available helpers.\n */\ndeclare const tb: {\n /**\n * Get raw data chunk from the Data tab.\n * @param index - Chunk index (0-based). Separate chunks with 2 blank lines.\n */\n data(index: number): string;\n /**\n * Get data chunk parsed as JSON (handles JSON-ish with unquoted keys).\n * @param index - Chunk index (0-based)\n * @throws If chunk is not valid JSON/JSON-ish\n */\n json<T = unknown>(index: number): T;\n /**\n * Async value inspector for tensor-like objects.\n *\n * Materializes lazy values (like GPU tensors) and logs them with metadata.\n * Handles objects with `.js()`, `.data()`, `.array()`, `.arraySync()`, etc.\n *\n * @remarks\n * - Always use `await` - materialization may be async (GPU\u2192CPU readback)\n * - Large values are truncated (max 1000 elements)\n * - Promises are logged as `[Promise]` (not awaited - could hang)\n */\n dump(...args: any[]): Promise<void>;\n /**\n * Trigger inference to generate new insight data.\n *\n * Opens a confirmation modal showing the data to be analyzed, then streams\n * the inference result. On success, updates the insight so subsequent\n * `tb.insight()` calls return the new value.\n *\n * @param opts - Options for inference\n * @param opts.data - Data to pre-populate in the modal (string or array of strings). If omitted, modal opens with empty textarea for user to paste.\n * @returns Promise that resolves with the parsed insight JSON\n * @throws If inference is already in progress, or on network/parse/rate limit errors\n */\n infer<T = unknown>(opts?: { data?: string | string[] }): Promise<T>;\n /**\n * Get the current inference state.\n *\n * @returns 'idle' | 'running' | 'complete' | 'error'\n */\n inferenceState(): 'idle' | 'running' | 'complete' | 'error';\n /**\n * Set a data chunk for the next inference call.\n *\n * Use this to programmatically set data that will be sent when `tb.infer()` is called\n * without the `data` option.\n *\n * @param index - The chunk index (0-based)\n * @param content - The content for this chunk\n */\n setData(index: number, content: string): void;\n /**\n * Proxy a CDN URL through the sandbox origin for Web Worker/WASM same-origin loading.\n *\n * In the sandbox, prepends `/proxy/` so the URL is served from the same origin.\n * Outside the sandbox (exported HTML, CLI), returns the URL unchanged.\n *\n * @param url - Full HTTPS URL to an allowlisted CDN (esm.sh, unpkg.com, cdn.jsdelivr.net, cdnjs.cloudflare.com)\n * @returns The proxied URL (sandbox) or the original URL (standalone/CLI)\n */\n proxy(url: string): string;\n /**\n * Copy text to clipboard.\n * Must be called synchronously within a user gesture (click/keydown).\n * @returns true if successful, false otherwise\n */\n copy(text: string): Promise<boolean>;\n /**\n * Get the canonical URL of this bulb.\n *\n * Returns the parent typebulb.com URL (including path, query, and `#tb=` fragment),\n * resolving correctly from inside the cross-origin sandbox iframe.\n * Use this instead of `location.href` or `document.referrer`.\n *\n * @returns The full canonical URL\n */\n url(): Promise<string>;\n /**\n * Get the insight data produced by the inference layer.\n *\n * Returns the parsed JSON from insight.json, populated by the inference LLM.\n * Use a type parameter to get typed access to the insight data.\n *\n * @returns The parsed insight JSON, or undefined if no insight is available\n */\n insight<T = unknown>(): T | undefined;\n /**\n * Server-side function proxy.\n *\n * In the CLI, calls exported functions from the `**server.ts**` section.\n * `tb.server.log(...)` is a built-in that prints to CLI stdout (falls back to console.log on web).\n * User exports override built-ins of the same name.\n *\n * A normal export is awaited for its result (`await tb.server.fn()`). An `async function*`\n * export streams: `for await (const chunk of tb.server.gen())`. The call object supports both;\n * break the `for await` to cancel and tear down the server generator.\n */\n server: Record<string, (...args: any[]) => Promise<any> & AsyncIterable<any>>;\n /**\n * Subscribe to a value pushed from the terminal via `typebulb send <file> [message]`.\n *\n * The dual of `tb.server.log` (data out): a value sent *in* from the CLI, no `--trust` required.\n * Use it to start expensive work on demand instead of on load \u2014 e.g. `tb.onMessage(() => start())`\n * \u2014 so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.\n *\n * The message is the JSON-parsed value of what `send` was given (or the raw string if it isn't\n * JSON; `undefined` for a bare `typebulb send <file>`). A non-`undefined` return value (awaited)\n * becomes the reply `send --wait` prints on stdout \u2014 JSON-serializable, at most one handler\n * replying \u2014 the structured read-back for self-tests. Returns an unsubscribe function. Inert in\n * an embedded bulb (no sender) \u2014 the handler is registered but never fires.\n *\n * @param handler - Called with each pushed message; may return a JSON-serializable reply.\n * @returns An unsubscribe function.\n */\n onMessage(handler: (message: any) => unknown): () => void;\n /**\n * General-purpose AI call. `tb.ai(opts)` resolves with the full text; `tb.ai.stream(opts)`\n * returns an async iterable of {@link AiChunk} deltas you consume with `for await`.\n *\n * @returns Promise resolving to { text: string }\n * @throws On rate limit, network error, or provider error\n */\n ai: {\n (options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): Promise<{ text: string }>;\n /**\n * Streaming counterpart of `tb.ai()`. Yields `{ kind: \"text\" | \"reasoning\", text }` deltas\n * as they arrive; break the loop (or abort `signal`) to cancel and stop the upstream.\n *\n * `kind: \"reasoning\"` deltas only arrive when you pass `effort: 1-3` AND use a\n * thinking-capable model; otherwise the stream is `text`-only.\n *\n * @example\n * let answer = \"\";\n * for await (const c of tb.ai.stream({ messages })) {\n * if (c.kind === \"text\") answer += c.text;\n * }\n */\n stream(options: {\n messages: Array<{ role: \"user\" | \"assistant\"; content: string }>;\n system?: string;\n /** Reasoning effort hint: 0=minimal, 1=low, 2=med, 3=high. Mapped to each provider's native mechanism (OpenAI/OpenRouter reasoning effort, Gemini thinking budget, Anthropic adaptive thinking). 0 minimizes reasoning \u2014 a floor, not a guaranteed \"off\": some models still think a little at 0, and adaptive models already self-skip at 1 (low). Level 1 (low) is the sensible default for most work; reach for 0 only when you truly want minimal deliberation. Omit for the model's own default. */\n effort?: 0 | 1 | 2 | 3;\n provider?: string;\n model?: string;\n /** Enable/disable web search. Default: on for BYOK, always off for free model. */\n webSearch?: boolean;\n /** Abort the request. On abort the promise rejects / the stream ends. */\n signal?: AbortSignal;\n }): AsyncIterable<AiChunk>;\n };\n /**\n * Local filesystem access (CLI only).\n *\n * Relative paths resolve against the bulb's folder (`tb.dir` \u2014\n * `<bulb-dir>/<filename-stem>/`, created on demand), so\n * `tb.fs.write('results.json')` lands beside the bulb. `../` reaches sibling\n * bulbs' folders; everything stays confined to the project (the launch cwd).\n * Throws in editor/published mode.\n */\n fs: {\n /** Read a file as UTF-8 text. Throws if the file is not valid UTF-8 \u2014 use readBytes for binary. */\n read(path: string): Promise<string>;\n /** Read a file as raw bytes. */\n readBytes(path: string): Promise<Uint8Array>;\n /** Write text or raw bytes to a file. Creates parent directories if needed. */\n write(path: string, content: string | Uint8Array): Promise<boolean>;\n };\n /**\n * The bulb's folder \u2014 absolute path to `<bulb-dir>/<filename-stem>/`\n * (or its subfolder when the run is scoped with `--dir`).\n *\n * For interop only (handing a path to `server.ts` or a spawned tool):\n * `tb.fs` already resolves relative paths against it, so bulb code writing\n * its own files never needs it. CLI only \u2014 throws in editor/published/embedded mode.\n */\n readonly dir: string;\n /**\n * Returns AI models available to the current user.\n * Models are filtered by the user's configured API keys.\n * If no keys are configured, returns only the courtesy model.\n */\n models(): Promise<Array<{\n /** Provider protocol: \"anthropic\", \"openai\", \"gemini\", \"openrouter\" */\n provider: string;\n /** Model identifier, e.g. \"claude-sonnet-4-6\" */\n name: string;\n /** Human-readable display name, e.g. \"Sonnet 4.6\" */\n friendlyName: string;\n /** Provider display name, e.g. \"Anthropic\" */\n providerName: string;\n /** True on the .env-configured default model (TB_AI_PROVIDER + TB_AI_MODEL); absent otherwise */\n default?: boolean;\n }>>;\n /**\n * Whether the user's own AI keys back `tb.ai`. False means only the\n * quota-limited courtesy model is available (or no AI at all) \u2014 a bulb\n * making many AI calls should check this and show a \"use your own keys\"\n * notice instead of running.\n */\n hasOwnKeys(): Promise<boolean>;\n /**\n * The bulb's theme override (`<html data-theme>`).\n *\n * - Get: the current override \u2014 `'dark'` | `'light'`, or `undefined` when\n * following the OS preference.\n * - Set `'dark'`/`'light'` to force and persist it (per-bulb); set\n * `undefined` to clear the override and follow the OS again.\n *\n * Drives `<html data-theme>`, so render off `html[data-theme=\"\u2026\"]` selectors\n * (or observe the attribute) rather than reading `tb.theme`.\n */\n theme: 'light' | 'dark' | undefined;\n /**\n * The mode this bulb is running in.\n *\n * - `'local'` \u2014 Running via the typebulb CLI\n * - `'editor'` \u2014 Running in the typebulb.com editor\n * - `'published'` \u2014 Running as a published/standalone bulb on typebulb.com\n * - `'embedded'` \u2014 Running as a bulb embedded inside another bulb (sandboxed,\n * client-only: AI, filesystem, and server RPC are unavailable)\n */\n mode: 'local' | 'editor' | 'published' | 'embedded';\n};\n";
|
|
13
13
|
/** Typebulb globals available in Node-side code (server.ts).
|
|
14
14
|
* Only what carries a bulb-specific rule Node can't know — tb.ai, tb.fs, tb.dir (TB-FS.md);
|
|
15
15
|
* the browser-only helpers are intentionally absent. Must match serverTb.ts's runtime surface. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;
|
|
1
|
+
{"version":3,"file":"tbTypings.d.ts","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAkQH,kEAAkE;AAClE,eAAO,MAAM,eAAe,+4WAO3B,CAAA;AAED;;mGAEmG;AACnG,eAAO,MAAM,eAAe,ksLAO3B,CAAA"}
|
package/dist/dts/tbTypings.js
CHANGED
|
@@ -229,13 +229,15 @@ const onMessage = `
|
|
|
229
229
|
* — so hot reloads don't re-trigger it while you edit, and an agent kicks off one run when ready.
|
|
230
230
|
*
|
|
231
231
|
* The message is the JSON-parsed value of what \`send\` was given (or the raw string if it isn't
|
|
232
|
-
* JSON; \`undefined\` for a bare \`typebulb send <file>\`).
|
|
232
|
+
* JSON; \`undefined\` for a bare \`typebulb send <file>\`). A non-\`undefined\` return value (awaited)
|
|
233
|
+
* becomes the reply \`send --wait\` prints on stdout — JSON-serializable, at most one handler
|
|
234
|
+
* replying — the structured read-back for self-tests. Returns an unsubscribe function. Inert in
|
|
233
235
|
* an embedded bulb (no sender) — the handler is registered but never fires.
|
|
234
236
|
*
|
|
235
|
-
* @param handler - Called with each pushed message.
|
|
237
|
+
* @param handler - Called with each pushed message; may return a JSON-serializable reply.
|
|
236
238
|
* @returns An unsubscribe function.
|
|
237
239
|
*/
|
|
238
|
-
onMessage(handler: (message: any) =>
|
|
240
|
+
onMessage(handler: (message: any) => unknown): () => void;`;
|
|
239
241
|
const clientServerProxy = `
|
|
240
242
|
/**
|
|
241
243
|
* Server-side function proxy.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,2DAA2D;AAC3D,MAAM,SAAS,GAAG;;;;;;;;;;;IAWd,CAAA;AAEJ,oGAAoG;AACpG,MAAM,WAAW,GAAG;;;;;CAKnB,CAAA;AAED,MAAM,EAAE,GAAG;;;;;;;;;OASJ,SAAS;;;;;;;;;;;;;;aAcH,SAAS;KACjB,CAAA;AAEL,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;kCAwBmB,CAAA;AAElC,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;KAiBN,CAAA;AAEL,MAAM,GAAG,GAAG;;;;;;;;;wBASY,CAAA;AAExB,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAmEA,CAAA;AAE1B,MAAM,SAAS,GAAG
|
|
1
|
+
{"version":3,"file":"tbTypings.js","sourceRoot":"","sources":["../../dts/src/tbTypings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,GAAG;;;;;;;;;;;uCAWmB,CAAA;AAEvC,MAAM,OAAO,GAAG;;;;;;;;;yCASyB,CAAA;AAEzC,2DAA2D;AAC3D,MAAM,SAAS,GAAG;;;;;;;;;;;IAWd,CAAA;AAEJ,oGAAoG;AACpG,MAAM,WAAW,GAAG;;;;;CAKnB,CAAA;AAED,MAAM,EAAE,GAAG;;;;;;;;;OASJ,SAAS;;;;;;;;;;;;;;aAcH,SAAS;KACjB,CAAA;AAEL,MAAM,MAAM,GAAG;;;;;;;;;;;;;;;;;;;;;;;;kCAwBmB,CAAA;AAElC,MAAM,KAAK,GAAG;;;;;;;;;;;;uCAYyB,CAAA;AAEvC,MAAM,IAAI,GAAG;;;;;;;;;;uDAU0C,CAAA;AAEvD,MAAM,EAAE,GAAG;;;;;;;;;;;;;;;;;KAiBN,CAAA;AAEL,MAAM,GAAG,GAAG;;;;;;;;;wBASY,CAAA;AAExB,MAAM,iBAAiB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;0BAmEA,CAAA;AAE1B,MAAM,SAAS,GAAG;;;;;;;;;;;;;;;;;6DAiB2C,CAAA;AAE7D,MAAM,iBAAiB,GAAG;;;;;;;;;;;;iFAYuD,CAAA;AAEjF,kEAAkE;AAClE,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,WAAW,GAAG,iBAAiB,GAAG,OAAO,GAAG,iBAAiB,GAAG,SAAS,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI;;CAErI,CAAA;AAED;;mGAEmG;AACnG,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,WAAW;;;;;qBAKxB,EAAE,GAAG,EAAE,GAAG,GAAG,GAAG,MAAM,GAAG,IAAI;;CAEjD,CAAA"}
|