vs Official CLI

The official Tabstack CLI is Mozilla-Ocho/tabstack-cli, written in Go. This version is a TypeScript/Bun reimplementation with full feature parity — plus three things that didn’t make it into the original.

Feature matrix

FeatureOfficial (Go)This (Bun/TS)
extract markdown
extract json with --schema
generate json
research (streaming SSE)
automate (streaming SSE)
agent input / tabstack input
login / logout / status
--geo, --effort, --nocache
--max-iterations
--schema from @file, -, inline
NDJSON in piped mode
Safe-by-default automate guardrail
login opens browser + verifies key
--interactive actually works
recipes cookbook command
skill install for coding agents
usage credit tracking

The three extras

1. Safe-by-default automate

automate is read-only by default. The CLI injects a guardrail unless you explicitly pass --allow-actions. The reasoning: most automation tasks are read operations (find, extract, summarize). Making destructive actions opt-in means the default path is safe for agents running unsupervised.

# Safe by default — read-only guardrail active
tabstack automate "find the top 3 trending repos" --url https://github.com/trending

# Explicit opt-in for actions
tabstack automate "fill and submit the contact form" 
  --url https://example.com/contact 
  --allow-actions

2. Login that works

The official CLI stores keys but doesn’t verify them before saving. This version:

  1. Opens the Tabstack console in your browser (API Keys → Create New Key)
  2. Prompts you to paste the key
  3. Makes one cheap verification call before storing it
  4. Stores at ~/.config/tabstack/config.json with mode 0600

A stored key that doesn’t work is worse than no stored key. The verification catches typos and expired keys at the moment you set them up, not three steps into a pipeline.

tabstack login                        # opens browser, prompts, verifies, stores
tabstack login --with-key ts_xxx      # non-interactive (CI)
echo "$KEY" | tabstack login --no-verify  # skip verification (e.g. you just created it)

3. Interactive automation that actually fires

The official CLI has an agent input command for answering paused automation tasks, but the --interactive flag that enables the API’s input-request events was never wired through to the request body. So agent input was unreachable in practice.

We found this while dogfooding, filed Mozilla-Ocho/tabstack-cli#15, and fixed it in this version:

# Start an automation that can pause and ask for input
tabstack automate "book the flight but confirm before payment" 
  --url https://example.com/flights 
  --interactive

# When it pauses — the CLI prints the request-id and instructions
tabstack input <request-id> --data '{"fields":[{"ref":"confirm","value":"yes"}]}'
tabstack input <request-id> --data '{"cancelled":true}'    # cancel the pause

The API supports this. The --interactive flag sends "interactive": true in the request body, which enables interactive:form_data:request events in the SSE stream. Without that flag, no events fire and tabstack input has nothing to respond to.

Why Bun/TypeScript vs Go?

Go produces a smaller, faster binary. Bun is already in most JS/TS developer toolchains. The Tabstack API is already the slow part — a few milliseconds of runtime overhead doesn’t matter when each request takes 2–10 seconds.

TypeScript gave one concrete advantage during the stream: the API response types could be expressed inline as the schema, and the arg parser could be written incrementally without a build step. That matters when you’re writing code live on camera.

Zero runtime dependencies. The binary is a single bun build artifact.