tensorbuzz-api
v0.0.19
Published
JavaScript client for TensorBuzz error reporting with browser, Node, and Expo/React Native support.
Readme
tensorbuzz-api
JavaScript client for TensorBuzz error reporting with browser, Node, and Expo/React Native support.
Install
npm install tensorbuzz-apiDevelopment and local file-dependency builds
This package is written in TypeScript and ships from its compiled build/
output (main/types point at build/). Building requires the dev
dependencies (TypeScript) and is wired into the prepare lifecycle, so a normal
npm ci/npm install in this directory installs the toolchain and builds
automatically.
Sibling packages such as tensorbuzz-instance-control consume this package
through a local file:../tensorbuzz-api reference. When npm prepares that local
dependency it runs this package's prepare step without installing its
devDependencies, so a plain tsc build would fail with tsc: not found. To keep
the package installable from a clean checkout in that case, prepare
(scripts/prepare.mjs) detects the missing toolchain and self-installs it with
npm ci --ignore-scripts before building. Use Node 24.x (the CI base image
version).
Usage
import {BugReporting, debuggerInstance} from "tensorbuzz-api"
const bugReporting = new BugReporting({
authToken: "your-token",
projectId: "your-project-id",
runtimeEnvironment: "browser"
})
bugReporting.connect()You can add deployment/user context to every report:
bugReporting.collectEnvironment(() => ({
release: "2026.05.19",
runtime: "browser"
}))
bugReporting.collectParams(() => ({
accountId: currentAccount?.id,
userId: currentUser?.id
}))handleError() resolves with the parsed TensorBuzz response when the report is accepted. Parameters and environment values from collectParams(), collectEnvironment(), error.tensorbuzzParameters, and error.tensorbuzzEnvironment are sanitized before posting: sensitive keys are redacted, long strings and large arrays are bounded, and binary/upload values are summarized without including bytes.
Applications that collect request bodies or other raw diagnostic metadata can reuse the same boundary helper:
import {sanitizeBugReportPayload} from "tensorbuzz-api"
const parameters = sanitizeBugReportPayload({
body: requestBody,
headers: requestHeaders
})Project environment control
EnvironmentControlClient composes the public project-environment lifecycle,
durable automation-supervisor command, and resumable command-event APIs. A
service token must be scoped to the target account/project/environment and hold
the corresponding project-environment and agent_runs read/write scopes.
import {EnvironmentControlClient} from "tensorbuzz-api"
const control = new EnvironmentControlClient({
host: "https://server.tensorbuzz.com",
serviceToken: process.env.TENSORBUZZ_API_TOKEN
})
const ensured = await control.ensureEnvironmentInstance({
idempotencyKey: "deploy-task-42",
projectEnvironmentId: "environment-id",
reuse: true,
reuseKey: "preview-main"
})
const dispatched = await control.dispatchCommand({
idempotencyKey: "task-42-attempt-1",
projectEnvironmentId: "environment-id",
text: "Implement task 42"
})
const commandId = dispatched.projectEnvironmentAgentCommand.id
const stream = await control.streamCommandEvents({
afterSequence: loadCheckpoint(commandId),
commandId,
onEvent: (event, afterSequence) => {
consumeRedactedEvent(event)
saveCheckpoint(commandId, afterSequence)
}
})
const terminalStatus = await control.waitForCommand({commandId})
await stream.close()Persist the numeric afterSequence supplied to onEvent and provide it on the
next subscription. Reconnect delivery is at-least-once, and the client suppresses
events at or below its current checkpoint. Event callbacks may be asynchronous;
the client processes them in order and advances only after each callback succeeds.
If a callback fails, the subscription closes and stream.done rejects so the
caller can reconnect from its last durable checkpoint. Pending callback delivery
is capped at 1,000 events; overflow also fails closed for cursor-safe reconnect.
The CLI honors stdout backpressure before advancing each event. The service token is sent as an HTTP
header or transient WebSocket session metadata; it is never placed in command
payloads, URLs, cursor parameters, or output. Event and status content retains
the server's public redaction and size bounds. The client deliberately exposes
no provider selection, worker/terminal session, or raw shell controls.
Controlling error message and backtrace inclusion
By default every report includes the original error message and backtrace. To include those raw values only for trusted requests, pass shouldIncludeErrorDetails — either a boolean or a callback returning boolean | Promise<boolean>:
const bugReporting = new BugReporting({
authToken: "your-token",
projectId: "your-project-id",
shouldIncludeErrorDetails: (context) => context.environment?.isAdmin === true
})The callback receives the report/policy context: {error, errorClass, message, backtrace, url, httpMethod, requestBody, environment, parameters, hostname, runtimeEnvironment}. Surface trusted request/user metadata for decisions such as "current user is an admin" or "request IP is allowlisted" through the environment/parameters you already provide via collectEnvironment(), collectParams(), error.tensorbuzzEnvironment, or error.tensorbuzzParameters.
- Default is
true(include message and backtrace), preserving previous behavior. - When the policy denies (or is
false), the report omits bothmessageandbacktracewhile keeping the safe metadata needed for grouping and diagnostics (error_class,url,http_method,user_agent,parameters,environment,requestBody). - The callback may be synchronous or asynchronous; it is awaited before the report is serialized and sent.
- If the callback throws or rejects, evaluation fails closed (details are omitted) and never propagates out of the reporting path, so a broken policy cannot break the host application.
Request transport
By default, BugReporting uses XMLHttpRequest when available, otherwise it falls back to fetch. If neither is available, you must provide a request class.
import {BugReporting, NodeRequest} from "tensorbuzz-api"
const bugReporting = new BugReporting({
authToken: "your-token",
projectId: "your-project-id",
hostname: "api.example.com",
postUrl: "https://server.tensorbuzz.com/errors/reports",
runtimeEnvironment: "node",
RequestClass: NodeRequest
})Browser error listeners
import {BugReporting} from "tensorbuzz-api"
const bugReporting = new BugReporting({authToken: "your-token", projectId: "your-project-id"})
// Optional: enable source map parsing for script tags in web apps.
bugReporting.enableSourceMapsLoader()
bugReporting.connectOnError()
bugReporting.connectUnhandledRejection()Node error listeners
import {BugReporting, NodeRequest} from "tensorbuzz-api"
const bugReporting = new BugReporting({
authToken: process.env.TENSORBUZZ_BUG_REPORT_AUTH_TOKEN,
hostname: process.env.TENSORBUZZ_BUG_REPORT_HOSTNAME,
projectId: process.env.TENSORBUZZ_BUG_REPORT_PROJECT_ID,
runtimeEnvironment: "node",
RequestClass: NodeRequest
})
bugReporting.connectNodeFatalHandlers()The Node fatal handlers are coordinated process-wide, including when multiple
reporter instances are connected. The first uncaught exception or unhandled
rejection gets one bounded reporting attempt, then the process terminates with
exit status 1 whether reporting succeeds, fails, or hangs. Nested fatal events
do not start more reports or extend the exit deadline. The existing
connectNodeUncaughtException() and connectNodeUnhandledRejection() methods
remain available for compatibility.
Instance control audits
import {InstanceControlAudits} from "tensorbuzz-api"
const instanceControlAudits = new InstanceControlAudits()
await instanceControlAudits.report({
emittedAt: new Date().toISOString(),
eventType: "action_cable.subscription.rejected",
instanceControlToken: "instance-control-token",
level: "error",
message: "ProjectEnvironmentTerminalSessionsChannel subscription rejected",
metadata: {attempt: 3},
postUrl: "https://server.tensorbuzz.com/errors/instance_control_audits",
projectEnvironmentInstanceId: "instance-id",
projectEnvironmentTerminalSessionId: "terminal-session-id",
projectId: "project-id"
})Expo / React Native error listeners
import {BugReporting} from "tensorbuzz-api"
const bugReporting = new BugReporting({authToken: "your-token", projectId: "your-project-id"})
bugReporting.connectExpoErrorHandlers()Direct reporting endpoint
Non-JavaScript runtimes can post JSON directly to /errors/reports. The error.parameters, error.environment, and top-level metadata fields can be JSON objects or JSON strings; TensorBuzz parses, sanitizes, and bounds them before storing the bug-report instance.
await fetch("https://server.tensorbuzz.com/errors/reports", {
body: JSON.stringify({
auth_token: "your-token",
projectId: "your-project-id",
hostname: "app.example.test",
runtimeEnvironment: "browser",
error: {
backtrace: ["Error: boom", "at checkout.js:10:4"],
environment: {release: "2026.05.19"},
error_class: "CheckoutError",
message: "Checkout failed",
parameters: {orderId: "ord_123"},
url: "https://app.example.test/checkout",
user_agent: navigator.userAgent
}
}),
headers: {"Content-Type": "application/json"},
method: "POST"
})CLI
npx tensorbuzz-api login
npx tensorbuzz-api build-logs --latest-build-group --failing
npx tensorbuzz-api environment-instance readiness --project-environment ENVIRONMENT_ID
npx tensorbuzz-api environment-instance ensure --project-environment ENVIRONMENT_ID --idempotency-key TASK_KEY
npx tensorbuzz-api environment-instance inspect --instance INSTANCE_ID
npx tensorbuzz-api environment-command dispatch --project-environment ENVIRONMENT_ID --idempotency-key TASK_KEY --text "Implement task 42"
npx tensorbuzz-api environment-command status --command-id COMMAND_ID
npx tensorbuzz-api environment-command wait --command-id COMMAND_ID --timeout-ms 300000
npx tensorbuzz-api environment-command events --command-id COMMAND_ID --after-sequence 41
npx tensorbuzz-api environment-command cancel --command-id COMMAND_ID
npx tensorbuzz-api webhook-watcher list
npx tensorbuzz-api webhook-watcher register-ci --thread-id THREAD_ID --chat-id CHAT_ID --project-id PROJECT_UUID --pr-number PR_NUMBER --build-group-id BUILD_GROUP_ID --prompt "Resume the task, inspect this completed build group, and continue from its verified result."For a durable CI + PR-review continuation, use the transactional paired flow. It creates opaque Hermes callback paths and preserves the persistent PR watcher when the exact head/build-group gate is replaced:
npx tensorbuzz-api webhook-watcher register-continuation --thread-id THREAD_ID --chat-id CHAT_ID --repository OWNER/NAME --project-id PROJECT_UUID --pr-number PR_NUMBER --build-group-id BUILD_GROUP_ID --head-sha HEAD_SHAThe chat ID must be an exact decimal Telegram ID (negative group IDs are
supported) and the thread/topic ID must be a positive decimal integer. Composite
values such as CHAT_ID:THREAD_ID, whitespace-corrupted values, and zero are
rejected before the API or route store is accessed. Before activation, the
command reads Project, BuildGroup, and Build association and calls the
BuildGroup.currentPullRequestGeneration read contract. That contract resolves
the live GitHub PR head through the linked GitHub App and requires exactly one
project/PR build group containing it, so a retained historical generation,
missing generation, or ambiguous mapping fails before provider or route-store
mutation.
The continuation's persistent review callback uses the deployed TensorBuzz
provider event set: github.pr_review.submitted,
github.pr_review_comment.created, github.pr_review_thread.changed,
github.pr_review.readiness_changed, and
github.pr_review.activity_after_merge. Exact provider readback must return that
same event set before registration becomes active.
Both new and idempotent paired registrations print one stable, secret-free JSON
receipt. It contains the exact binding, both opaque route names and provider
subscription IDs, their event and lifecycle contracts, and proof claims showing
an accepted nonduplicate first delivery followed by an accepted same-ID
duplicate. Any paired-flow failure is nonzero and includes
UNMONITORED — registration incomplete plus a stable safe reason code. Receipts
and errors never include the signing secret, token, signature, request headers,
payload body, or credential path.
The webhook-watcher command also supports list, remove, cleanup-orphans,
and test. list includes both legacy named watchers and opaque continuation
routes, including each continuation role, registration state, expected head,
owning project, provider subscription ID, and a conservative route
classification. Versioned continuation routes are schema-validated before any
mutation. Malformed legacy records remain read-only inventory: they are neither
coerced into the managed schema nor silently deleted. An exact active
pre-schema continuation pair is adopted in place only after both provider IDs,
scopes, lifecycles, and signed ingress proofs succeed; partial, conflicting, or
ambiguous legacy state fails UNMONITORED without creating replacements. Route
readback also requires deliver_only: false; notification-only records cannot
claim an agent-capable continuation. Matching active strict routes must have
unambiguous build/review cardinality. Partial current-generation, duplicate, or
mixed-role state fails closed without provider mutation; one exact persistent
review route from an earlier generation remains eligible for exact readback and
reuse during the next build-generation registration. Route
updates reload and compare the store immediately before atomic replacement, and
compensation removes or restores only routes still exactly owned by the failed registration,
so unrelated concurrent additions survive. Run cleanup with an explicit
cleanup-orphans --project-id PROJECT_UUID; it applies only to routes persisted
for that project. Before cleanup, legacy routes without stored project ownership
are read back from the provider: a route is backfilled only when that readback
proves exact ownership by the requested project. A backfilled completed one-shot
route can then be removed. Routes owned by other projects, plus legacy routes
with missing, ambiguous, malformed, or inaccessible provider readbacks, are
retained conservatively because a project-scoped token cannot prove they are
missing. Unrelated Hermes routes are also untouched. Set
TENSORBUZZ_API_TOKEN when using commands that access the
TensorBuzz API. The token used for paired registration needs projects:read
and webhooks:manage; no broader scope is required. It
should be scoped to the single target project. Configure
HERMES_WEBHOOK_SIGNING_SECRET through the deployment secret store so
registration can safely bootstrap an empty HERMES_WEBHOOK_SUBS_PATH; the
secret is never recovered by restoring old route records. Existing installations
without that variable retain their route-level secret fallback for compatibility.
After createCallback, registration reads the returned ID back through the same
authenticated frontend-model API and requires exactly one active record matching
the intended project, PR, build group or build, events, callback URL, one-shot
behavior, and terminal filter before writing the local Hermes route. Missing,
ambiguous, or unrelated records are rejected and compensated through the
authenticated destroy command.
If an otherwise reusable persistent review route returns 404 during public
ingress proof, paired registration first reads back the exact old provider ID,
deactivates that exact callback, marks the local route stale, and only then
creates a replacement. It never bulk-deletes ambiguous, provider-only,
local-only, no-topic, no-provider-ID, or cross-project records.
register-ci and register-review create agent-capable Hermes routes by default. Incoming TensorBuzz
events therefore start an agent continuation as well as targeting the configured
Telegram chat/thread. For meaningful autonomous workflows, pass a self-contained
--prompt that identifies the task and tells the agent what evidence to inspect
and what continuation to perform. Without --prompt, the watcher preserves its
concise payload-aware default (TensorBuzz: {buildGroup.name} —
{buildGroup.status} or the equivalent build text). Empty or whitespace-only
prompts are rejected before creating a subscription. Use --deliver-only
explicitly when the route should send only a Telegram notification and must not
start an agent continuation.
register-ci has a deliberately fixed contract: it requires the exact project,
PR, and build-group identifiers, subscribes only to build_group.completed, and
creates a one-time callback that auto-deletes after successful delivery. It does
not accept per-build events or lifecycle overrides.
register-review is separate and persistent. It requires the exact project and
PR plus an explicit comma-separated --events selection from
github.pr_review.submitted, github.pr_review_comment.created, and
github.pr_review_thread.changed. Review callbacks are never one-time or
auto-deleting. Both commands read the exact returned provider subscription ID
back and verify all non-secret scope and lifecycle fields before replacing the
local route.
webhook-watcher test --route-name NAME copies the selected route to a temporary,
non-one-shot test route, sends a correctly signed semantic delivery, replays the
same delivery ID to verify duplicate handling, and removes the test route. It
never sends synthetic traffic through the live CI route.
login uses browser approval by default: it prints a TensorBuzz URL, you approve the request while signed in (including GitHub-authenticated users), and the CLI stores credentials automatically.
The CLI stores credentials in .tensorbuzz-api/credentials.json at the git root and uses the current branch by default.
Agent runs
agent-run uses the documented raw frontend-model API and a scoped service
token. Set the server and token in the environment so the secret does not enter
shell history:
export TENSORBUZZ_API_URL=https://server.tensorbuzz.com
export TENSORBUZZ_API_TOKEN=your-scoped-service-tokenThe token needs the scopes appropriate to each operation (commonly
projects:read, agent_runs:read, agent_runs:write, environments:write,
builds:read, and builds:write). The CLI also accepts --host and
--service-token; it never
prints the token. A serviceToken stored alongside host in
.tensorbuzz-api/credentials.json is supported for unattended installations.
Check readiness and start a planning-first run:
npx tensorbuzz-api agent-run readiness --project-environment ENVIRONMENT_ID --json
npx tensorbuzz-api agent-run start \
--project-environment ENVIRONMENT_ID \
--goal "Implement task TB-42" \
--source-system jira \
--source-project-id PROJECT_KEY \
--source-task-id TB-42 \
--source-url https://jira.example/browse/TB-42 \
--idempotency-key jira-TB-42start requires plan approval by default. Only pass
--plan-approval-policy auto when the automation is explicitly authorized to
execute without owner approval. Source metadata is accepted as a JSON object
with --source-metadata '{"priority":"high"}'.
Supervise the resulting run:
npx tensorbuzz-api agent-run status --run-id RUN_ID
npx tensorbuzz-api agent-run logs --run-id RUN_ID --limit 200 --json
npx tensorbuzz-api agent-run send-message --run-id RUN_ID --message "Approved" --decision approve_plan
npx tensorbuzz-api agent-run resume-from-source --run-id RUN_ID --source-event-id COMMENT_ID --source-task-id TB-42 --planning-revision 1 --message "Include CSV export" --decision request_plan_changes
npx tensorbuzz-api agent-run verify --run-id RUN_ID --branch-name agent/run-RUN_ID
npx tensorbuzz-api agent-run failure-context --run-id RUN_ID --json
npx tensorbuzz-api agent-run retry --run-id RUN_ID --reason "Verification failed" --failed-command "npm run typecheck" --prompt "Fix and verify again"
npx tensorbuzz-api agent-run cancel --run-id RUN_ID --reason "Superseded"Use the returned nextCursor as --cursor for the next log page. --limit is
bounded to 1–1000. --json writes only the command response as one compact JSON
line for reliable scripting; human output stays concise.
For manual token login, pass --auth-token (or TENSORBUZZ_API_AUTH_TOKEN):
npx tensorbuzz-api login --host https://tensorbuzz.example --repo owner/name --auth-token your-project-auth-tokenFor legacy password login, pass --password (and optionally --email, or TENSORBUZZ_API_EMAIL / TENSORBUZZ_API_PASSWORD):
npx tensorbuzz-api login --host https://tensorbuzz.example --repo owner/name --email [email protected] --password your-passwordPersistent review watcher
Register a persistent observer with an explicit supported review-event set:
npx tensorbuzz-api webhook-watcher register-review \
--thread-id THREAD_ID --chat-id CHAT_ID --project-id PROJECT_UUID \
--pr-number 42 \
--events github.pr_review.submitted,github.pr_review_comment.created