Skip to content

About

Open Source Alternative Harness to Grok Bot and Muse

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

Grokked Bot: Your Open Source Bot Harness

Grokked Bot

Your open-source bot harness: an AI teammate that runs on your own computer, Mac or Linux. Powered by OpenRouter, so you pick the model. Inspired by Grok Bot and Claude Cowork — the difference is that the "computer" your agent works on is a container on hardware you already own, not a cloud VM you rent.

The app: bot list, the conversation, and the bot's live screen

You message a bot like a colleague. It has its own computer — a real desktop with a real browser it stays logged into, plus a shell — and it uses whichever fits the job. You watch it work on the right, and you can take the mouse away from it at any moment.

Above: asked for the top 10 spenders of 2024 on fec.gov, the bot opened the page, read the table and answered in three steps for about five cents — and said which view it had read, so you know what to ask for next. On the right is its live screen, with a human holding control.

Get started

You need three things, and the app walks you through all of them:

  • Docker running (Docker Desktop or OrbStack on a Mac, Docker Engine on Linux). Each bot's computer is a container.
  • An OpenRouter key with a few dollars of credit. A typical task costs cents.
  • About 5 GB of disk for the bot's computer, downloaded once.

Then pick how you want to run it:

You have Do this
A Mac (Apple Silicon or Intel) Download the app, drag it to Applications, open it
Ubuntu / Debian 24.04+ or another Linux Install the .deb or AppImage
A Linux box you want bots on 24/7, or you want to hack on it Run from source as a background service

Once it's open, your first task takes about a minute: click +, say what the bot is for, and send it a message.

Contents: What it can do · Mac · Linux app · From source · First task · Troubleshooting · Webhooks and schedules · Configuration · How it works · API and CLI · Safety · Contributing

What it can do

Everything below works today. Details are further down.

Bots, each with its own computer

  • Any number of bots side by side, each in its own container with its own Chrome profile, logins, files and shell.
  • A real desktop the bot drives: headful Chrome, a terminal, a file manager.
  • Watch its screen live; take over the mouse and keyboard at any moment and hand it back.
  • Paste from my PC / Copy to my PC between your clipboard and the bot's.

Per bot (the tabs on the right)

  • Details: name, what it's for, standing instructions, the sites it may visit (enforced in its computer), its own model, its own limits per message (dollars, steps, minutes), and its look.
  • Env: paste a .env file; every shell command gets those variables, values hidden from the model. A GITHUB_TOKEN lets it clone private repos and use gh.
  • Slack: put a bot in Slack as its own teammate (its name, its face). DM it or @mention it; it reacts 👀, shows what it's doing and answers in the thread. Guided setup with a ready manifest; no public URL needed.
  • SSH: the bot's own SSH key. Add its public key to a server's authorized_keys, list the servers it may use, click Test, and it can ssh, scp and rsync there.
  • Triggers: webhooks (bearer token, optional wait-for-answer) and cron schedules, each with its own conversation. Or just ask the bot ("every weekday at 8…", "remind me Friday") and it makes the schedule itself.
  • Media: everything it saved or downloaded, previewable, openable on your desktop.
  • Computer: the live screen, take over, restart.

Working on code

  • Hand a bot a repo: it clones it, installs the language the project asks for, reads and edits code, runs the tests, commits, and (with a GITHUB_TOKEN) pushes a branch, opens a pull request, waits on CI and fixes what fails.
  • Node, Python, Go, Java, Bun and Deno install on demand with mise and are kept across updates; PHP and Ruby come from apt.
  • How much it reads and writes per step follows the model: a 1M-token model sees whole files and test logs, a smaller one a trimmed version.

Conversations

  • Real follow-ups ("now do 2022"), automatic titles you can rename, kept across restarts.
  • Copy any answer to your clipboard, or Pin it to keep it in Pinned results (sidebar), linked back to its conversation.
  • Per-message overrides of the allowed sites and spending cap (the + in the composer).
  • The bot stops and asks when it needs you (login wall, CAPTCHA, a question); reply or take over.

Signing in

  • Saved logins (Settings → Passwords): encrypted, typed by the daemon only on the login's own site, never shown to the model, limitable to particular bots.
  • Sign in with Google / Microsoft / Apple / GitHub through a saved provider account.
  • HTTP (basic auth) sign-ins answered from a saved login.

Settings

  • Default budget and step / time limits; which models do the work and name conversations.
  • Instructions: standing rules every bot follows on every task (time zone, tone, what to check with you first).
  • Remaining OpenRouter credit in the sidebar.
  • The version you're running, at the bottom of Settings.
  • Remote access: webhooks on your Tailscale tailnet, and public ones through Funnel.

Beyond the app

  • An HTTP + WebSocket API and a CLI (bin/task.ts) to start and stream runs.
  • An eval suite of twelve real tasks (eleven on the web, one coding) to compare models and catch regressions.
  • Installers for Mac (Apple Silicon, Intel) and Linux (.deb, AppImage), or run from source.
  • Run it on AWS with one Terraform apply (deploy/aws): a desktop you open through an encrypted tunnel (no open ports), and HTTPS webhooks through CloudFront.

On a Mac

Download for Apple Silicon · Intel · All releases

Drag Grokked Bot to Applications and open it. The first launch is blocked because the app isn't notarized yet: open System Settings → Privacy & Security and click Open Anyway, once per version. The app then walks you through the rest:

  1. Docker. Install Docker Desktop (or OrbStack) and open it. It supplies the Linux VM each bot's computer runs in.
  2. The bot's computer. A one-time ~5 GB download, started from the app.
  3. An OpenRouter key. Pasted into the app, checked, and saved.

The app ships its own Node and runs the daemon itself. Two differences from Linux: bots stop when you quit the app, and on Apple Silicon the bot's Chrome runs under Rosetta, so it's a bit slower.

On Linux, the app

.deb for Ubuntu/Debian 24.04+ · AppImage

Like the Mac app, it ships its own Node, runs the daemon itself and walks you through Docker, the image and your key. It runs as its own instance, so it can sit beside a source checkout: port 8797 (webhooks 8798), data in ~/.config/grokked-app and ~/.local/share/grokked-app, and always the published bot computer image. Bots stop when you quit the app; for always-on bots, use the systemd setup below.

Quick start (Linux, from source)

Tested on Ubuntu with Node 24. Nothing below needs root except installing system packages.

1. Install the prerequisites

Node 24+ Runs the TypeScript daemon directly, no build step. nvm works fine.
pnpm corepack enable (ships with Node)
Rust rustup.rs — for the Tauri desktop app
Tauri's system libs sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev
Rootless Docker docs.docker.com/engine/security/rootless
An OpenRouter key openrouter.ai/keys, with a few dollars of credit

Then set up rootless Docker and let your user services outlive your login:

dockerd-rootless-setuptool.sh install
systemctl --user enable --now docker.service
loginctl enable-linger "$USER"          # bots keep working after you log out

Prefer rootless Docker over adding yourself to the docker group: that group is effectively root (docker run -v /:/host), a poor trade for a system whose job is holding live credentials.

2. Get the code and build the bot's computer

git clone https://github.com/alnutile/grokked-bot.git
cd grokked-bot
pnpm install

export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
docker build -t grokked/computer:0.1 container/     # ~5GB, takes a few minutes

3. Add your OpenRouter key

mkdir -p ~/.config/grokked
printf 'OPENROUTER_API_KEY=sk-or-...\n' > ~/.config/grokked/env
chmod 600 ~/.config/grokked/env

4. Install the daemon

The daemon is a systemd --user service. The unit file has placeholders for where Node and this repo live; this fills them in:

mkdir -p ~/.config/systemd/user
sed -e "s|@NODE@|$(command -v node)|" -e "s|@REPO@|$PWD|g" \
  packages/daemon/deploy/grokked.service > ~/.config/systemd/user/grokked.service
systemctl --user daemon-reload
systemctl --user enable --now grokked.service

systemctl --user status grokked           # should say active (running)

If you upgrade Node later (nvm puts the version in the path), re-run the sed line.

5. Open the app

./run-app.sh

The first run compiles the Tauri app, which takes a few minutes. After that it opens in seconds.

Your first task

  1. Click + at the top of the sidebar. The new bot opens on its Details tab: give it a name, say what it's for, add standing instructions, and list the sites it may visit (for example linkedin.com). That allowlist is enforced inside its computer, not just in the prompt. Each bot gets its own computer — its own container, browser profile and logins — created the first time it's needed.
  2. Type what you want, the way you would to a colleague, and press Ctrl+Enter. The + in the message bar overrides the sites or the spending cap for one message.

The conversation shows each step as it happens, and the Computer tab shows its screen. It's a real conversation: follow-ups like "now do the same for 2022" or "did that work?" see what came before. Conversations get a title automatically (click it to rename), are listed in the sidebar, and are still there after a restart. New starts a fresh one. Anything the bot saves or downloads shows up in its Media tab.

If it hits something it can't do alone — a login wall, a CAPTCHA, a question only you can answer — it stops and asks. Reply in the chat, or click Take over the screen; either way it carries on from where it stopped.

Taking over. Click Take over to pause the bot and drive its screen yourself, full size; this is how you sign in to sites for it. Paste from my PC pastes your clipboard where the bot's cursor is (handy for passwords), and Copy to my PC brings back whatever you copied on its screen. The ⤢ button opens the screen full size just to watch. Click Give control back and it picks up on whatever page you left it, in the same browser with the same session. Logins are kept in the bot's profile, so you sign in once.

Saved logins. Open Settings → Passwords (bottom left) and add a site, username and password. When a bot hits that site's sign-in page it fills them in itself — the password is typed by the daemon, so the AI model never sees it, and it's only ever typed on that site. You can limit each login to particular bots.

For sites that use Sign in with Google (or Microsoft, Apple, GitHub), save your Google account once as its own login, then set the site's login to Sign in with Google and link that account. The bot clicks the site's Google button, follows the popup, and fills your Google account on Google's own pages; MFA codes and phone prompts come back to you.

Environment variables. Each bot has an Env tab: paste a .env file and every command it runs in its shell gets those variables. They're stored encrypted with the same key as saved passwords and passed to the shell per command, never written to the bot's disk, so an edit applies to the very next command. The bot is told the names, never the values, and anything secret-looking (a name with TOKEN, KEY, SECRET, PASS…, or a value 16+ characters long) is masked as [$NAME] in what its tools return.

Add GITHUB_TOKEN (or GH_TOKEN) and the bot can work on GitHub: git clone https://github.com/owner/repo works for private repos with no token in the URL, through a git credential helper that only answers for github.com, and so does the gh CLI. Use a fine-grained token limited to the repos and permissions that bot needs, since the bot's shell can use it for anything that token allows.

SSH. Each bot has an SSH tab. Create SSH key makes it an ed25519 key of its own (so one bot can be let in, or shut out, without touching anyone else's access) and shows the public half with a ready-to-paste command that adds it to a server user's ~/.ssh/authorized_keys. List the servers it may use (deploy@web.example.com, a port, a note like "read only") and Test checks each one from the bot's computer. The private key is stored encrypted and lives only in an ssh-agent inside the bot's computer, so ssh, scp and rsync work from its shell but the key is never on its disk and can't be printed. Connections are non-interactive (a server that asks for a password fails at once), new servers' host keys are remembered on first connect, and Replace or remove key makes the old key stop working everywhere.

Slack. Each bot has a Slack tab that walks you through giving it its own Slack app in about five minutes: copy the manifest (pre-filled with the bot's name, description and colour), create the app from it on api.slack.com, save the bot's face as the app icon, make an App-Level Token (connections:write), install the app, and paste the two tokens (xoxb- and xapp-, checked with Slack before they're saved, then stored encrypted). It connects over Socket Mode: your computer holds a connection out to Slack, so there's no public URL, tunnel or Funnel, and it works on any network while the app is running.

People DM the bot, or /invite it to a channel and @mention it. It reacts 👀 straight away, posts "On it…", edits that message with the step it's on, and replaces it with the answer (✅). Replies in its thread carry on the same conversation without an @, and if it asks a question, your reply in the thread is the answer. Every Slack thread also shows up in the app's conversation list. Only the people on its list can use it: the first person to message it is added (that's you, during setup), and you can add others or open it to the whole workspace. Pause, update tokens or disconnect from the same tab.

Coding bots. Ask a bot to work on a repo and it clones it into its computer, installs the runtime the project asks for (Node, Python, Go, Java, Bun and Deno through mise, kept in a volume of their own so they survive updates; PHP and Ruby through apt), reads and edits the code, runs the project's tests and commits. Asked to, it pushes a branch, opens a pull request, waits on CI with gh, fixes what fails and merges. Commits are signed with the bot's name, or yours if you set GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL in its Env tab. Coding takes more steps than browsing, so give a coding bot its own limits in Details (say 300 steps and 180 minutes); the Settings defaults are 150 steps and 3 hours.

Settings also holds the default budget and limits, and which models do the work; any bot can override the model in its Details.

The sidebar shows your remaining OpenRouter credit. The app connects to the bot's screen for you. To watch a bot in a full browser tab instead, ./vnc.sh lists every running bot with its password and a link that logs straight in; ./vnc.sh <bot-id> opens that one.

Troubleshooting

Something not working? Run this in Terminal (Mac or Linux). It writes grokked-diagnostics.txt to your Desktop: app and Docker versions, your bots and their Slack status, recent runs and the last 300 log lines, with tokens and keys replaced by [redacted]. Skim it and send it to whoever is helping.

curl -fsSL https://raw.githubusercontent.com/alnutile/grokked-bot/main/scripts/diagnostics.sh | bash
Symptom Fix
App says the daemon is offline systemctl --user status grokked, then journalctl --user -u grokked -n 50
Cannot connect to the Docker daemon export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock and systemctl --user start docker
Bot's screen stays blank or says "computer unreachable" Click Restart computer. The first start can take a minute while Chrome comes up.
A run ends blocked Something stopped it — usually a domain not on the allowlist, or a page it couldn't get past. The run's last steps say which. Take over if it needs you, then send another message.
A run pauses on budget It hit its max $. Continue it with more, or raise the default in ~/.config/grokked/config.json.
Model errors on boot A model id in ~/.config/grokked/config.json no longer exists on OpenRouter. The daemon log names it.

Each bot's computer is a container named after the bot (docker ps). Docker picks free host ports for its screen and its control API, so any number of bots can run side by side.

Webhooks and schedules

A bot can be started by more than you. In its Triggers tab:

  • Webhooks — a URL and a bearer token. Another app, a phone shortcut, n8n or a service POSTs to it and the bot gets to work on the hook's standing instruction, plus whatever the caller sends:

    curl -X POST https://<machine>.<tailnet>.ts.net/hooks/<id> \
      -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
      -d '{"prompt": "Summarise this and draft a reply", "payload": {"from": "…", "body": "…"}, "wait": 120}'

    wait holds the request open (up to 300 s) and returns {"state": "succeeded", "answer": "…"}; without it you get 202 and a status URL. Any other JSON body (GitHub, Stripe) is taken whole as the payload. To have the bot report back somewhere — your webhook, your app's API — just say so in the prompt; it has a shell and will make the call. A busy bot answers 409 with Retry-After.

  • Schedules — a cron schedule (or a preset like every weekday at 8:00), a time zone and an instruction: "check my inbox for anything from recruiters and summarise it".

    You don't have to make them by hand: ask the bot in chat or Slack ("every Monday at 9, summarise last week's PRs", "remind me Friday at 3pm to send the invoice") and it creates the schedule with its schedule_create tool, in your time zone, and tells you when it next runs. One-offs switch themselves off after they fire. Limits: at most every 10 minutes, 20 schedules per bot, and only when a person asks. A run that a schedule or webhook started can't create or change schedules, so a webhook payload can't plant a job that keeps coming back. Webhooks are still made by you, since their token is shown only once.

Each trigger gets its own conversation (marked ⚡ in the sidebar) with its full history.

Reaching them from other devices. Webhooks have their own listener (port 8788) that serves nothing but /hooks; the rest of the API never leaves the machine. In Settings → Remote access, one switch puts it on your Tailscale tailnet with HTTPS, and another publishes hooks you mark Public through Funnel (for services that can't join your tailnet). Changing Tailscale settings from the app needs, once: sudo tailscale set --operator=$USER.

Not using Tailscale? Point Headscale, WireGuard, Caddy or a Cloudflare Tunnel at http://127.0.0.1:8788 — expose /hooks privately and /public/hooks publicly — or set GROKKED_HOOKS_HOST=0.0.0.0 in ~/.config/grokked/env to listen on your network directly.

Configuration

~/.config/grokked/env          OPENROUTER_API_KEY, optional DOCKER_HOST   (0600)
~/.config/grokked/config.json  model per role, default caps, instructions for every bot
~/.config/grokked/token        API bearer token     (0600, generated on first boot)
~/.config/grokked/vault.key    encryption key for saved passwords (0600, generated on first use)
~/.local/share/grokked/        grokked.db, blobs, per-bot work dirs

On macOS all of these live in ~/Library/Application Support/Grokked/ instead, along with daemon.log, the daemon's output.

Settings in the app edits config.json: the worker model does the actual task and classifier names conversations, using any tool-capable OpenRouter model id; the defaults are the per-run caps (max_steps, max_usd, max_wall_s). Changes apply from the next step, no restart. Back up vault.key with the database — without it, saved passwords can't be decrypted. The daemon listens on 127.0.0.1:8787; set GROKKED_PORT in the env file to change it.

Why Linux is the right host for this

The expensive thing these products sell is "a computer of its own in the cloud." On macOS they have to rent you a Linux VM to deliver it. On Linux that computer is just a container with a virtual display, so several bots cost you tokens and nothing else.

It needs no root anywhere: rootless Docker, a systemd --user unit, and loginctl enable-linger so your bots keep working after you log out.

How it works

Tauri app  ──HTTP/WS──▶  daemon (systemd --user)  ──Docker API──▶  bot container
 React UI                Node 24 + SQLite                          Xvfb + Chrome + shim
 noVNC ──────────────────────────────────────────────────────────▶ the bot's screen

The agent loop lives in the daemon, never in the container: your OpenRouter key stays out of the zone where untrusted web pages are read, models swap without rebuilding a 5GB image, and transcripts stay on the host. The container is a replaceable "computer" behind a typed action API — which is also what makes moving the daemon to an always-on box later a base-URL change rather than a rewrite.

The bot's computer is not headless

Xvfb + openbox + real Google Chrome at 1280x800, with a taskbar, a terminal and a file manager. We launch Chrome ourselves and attach over CDP rather than letting Playwright launch it, which keeps navigator.webdriver === false, avoids the automation infobar, and leaves a desktop a human can take over.

Browser control is by accessibility ref, not pixel coordinates

OpenRouter exposes plain OpenAI-style function calling, with no access to a computer-use-tuned model. Asking a general model for the pixel position of a button is a regression task it was never trained for; picking s7e42 out of a labelled list is a selection task every instruction-tuned model is good at. So the agent gets an accessibility outline of the page and acts by ref. Refs are generation-scoped, so acting on a stale one is a hard error rather than a wrong click.

Watch it, then take the wheel

Watching is view_only; Take over flips that and claims a lease, so the agent stops acting — without it, both drive the same X server and fight over the cursor. The lock is one-directional: it stops the bot, never you. On release the agent resumes on whatever page you left it on. That is how sign-ins work: hit a login wall, the bot calls ask_human, you sign in by hand, it carries on.

Without the GUI

TOKEN=$(cat ~/.config/grokked/token)

# Hand the default bot a job and stream its progress in the terminal
node packages/daemon/bin/task.ts "<goal>" fec.gov

# Or over the API: create a bot, then give it a run
curl -X POST localhost:8787/v1/bots -H "Authorization: Bearer $TOKEN" \
  -d '{"id":"bot-alpha","name":"Alpha"}'
curl -X POST localhost:8787/v1/runs -H "Authorization: Bearer $TOKEN" \
  -d '{"bot_id":"bot-alpha","goal":"...","allowed_domains":["fec.gov"],"max_usd":5}'

And the container is drivable by hand with no model in the loop, which is how the browser layer was built and verified. container/run.sh <name> starts a standalone computer and prints its ports; act.sh talks to it:

container/act.sh navigate url=https://example.com
container/act.sh snapshot
container/act.sh run_bash command='python3 -c "print(1+1)"'

The agent's tools

Browser navigate snapshot click type find read_text scroll wait_for screenshot
The machine run_bash read_file edit_file write_file desktop_action
Schedules schedules_list schedule_create schedule_update schedule_delete
Control finish give_up ask_human

The system prompt tells it that it has a computer, not that it can call a browser API: use the shell when a shell is easier, read pages by ref rather than by screenshot, wait for the thing you actually want, verify before reporting, and don't route around an instruction — an answer obtained the way you were told not to is a failed task.

Safety

  • Per-task domain allowlist, enforced in the container, not the prompt. A hostile page can talk the model into anything; it cannot change what the shim checks.
  • Page text is fenced in <untrusted_page_content> and the goal is immutable for the run.
  • Spending: a per-run cap that pauses (one click to continue with more) plus step and wall-clock caps. The cap is a runaway-loop guardrail — the sidebar shows your actual remaining OpenRouter credit so the two are never confused.
  • CDP port 9222 is never published. It is unauthenticated, and Network.getAllCookies over it would drain every session in the profile.
  • Everything binds to 127.0.0.1. The screen and control ports are loopback-only, and VNC is password-protected on top of that.
  • Saved passwords never reach the model. They're AES-256-GCM encrypted at rest with a key kept outside the database. The bot lists logins without passwords and asks the daemon to type one into a field; the daemon checks the page is on the login's own domain first, so a lookalike page can't collect it, and strips the typed value from what the bot sees.
  • The container never mounts your home directory. Only a narrow work/ bind mount is shared.

Approvals — a gate before the bot acts on a site you're logged into — are not built yet. Until they are, keep the domain allowlist tight on any bot that holds real logins.

Status

Working today: everything in What it can do.

M0 Daemon, schema, HTTP + WS with durable ?since= replay, systemd unit
M2 The bot's computer: headful Chrome over CDP, shell, VNC takeover
M3 The harness: OpenRouter tool loop, durable runs, caps, loop detection
M1 Three-pane desktop app with the bot's live screen
next M4 approvals — the gate before pointing this at anything you're logged into

Not done yet: approvals, persistent memory, learned routines, bots working together, prompt caching (which is the biggest cost lever still on the table).

Layout

Path What
packages/protocol Zod schemas shared by daemon and UI — change one, the other fails typecheck
packages/daemon HTTP + WS, SQLite, agent loop, tool registry, Docker runtime
packages/daemon/src/db/migrations Schema. Everything is downstream of 001_init.sql
container The bot's computer: Xvfb + openbox + Chrome + x11vnc + the CDP shim
apps/desktop Tauri v2 + React client

Design notes worth knowing before changing things

  • The daemon holds no run state that is not in SQLite. One code path serves start, resume-after-crash, resume-after-approval and wake-from-sleep. Never add a second.
  • The WS is a cache-invalidation channel, not the source of truth. Every frame is appended to events first so ?since= replay can rebuild client state; a client whose since predates retention gets hello{dropped:true} and refetches.
  • Never assume a bot's ports. Docker assigns them per container and can change them on restart; ask the runtime (LocalDockerRuntime.ports()). Pinning them is what once let one bot's screen open with another bot's VNC password.
  • --password-store=basic is load-bearing. Without it Chrome looks for gnome-keyring over D-Bus, which a container has not got, and re-derives its cookie key each boot — so every login silently stops persisting and it looks like a broken volume.
  • BYOK keys report usage.cost: 0; real spend is in cost_details.upstream_inference_cost. Reading cost alone disables every budget cap.

Evals

Twelve everyday computer jobs, run against the real stack — daemon, bot computer, live websites — and graded automatically: FEC data, a dropdown inside an iframe, Wikipedia facts and a written briefing, Hacker News to CSV, Reddit, an Indeed search, signing in with a saved password, signing in with Google through a popup, a browser sign-in popup, a job application form with a resume upload, and fixing a failing test in a Node repo (graded by what the bot left behind: tests pass, a commit on the right branch, the test file untouched). Run them on every build, and to compare models:

pnpm eval                                  # the Settings worker model
pnpm eval --model x-ai/grok-4.6            # any OpenRouter model
pnpm eval --only wiki-fact,apply-form      # a subset

They use a dedicated Eval bot, so your bots' logins and conversations are untouched, and the test logins they create are deleted afterwards. Each run prints pass/fail, steps, cost and time per task, flags anything that regressed since the last run on that model, and saves the results to ~/.local/share/grokked/evals/. The tasks and their checks live in packages/daemon/evals/tasks.ts; the local test pages in evals/fixtures/.

Contributing

pnpm install
pnpm test                                 # unit tests
pnpm typecheck                            # daemon and protocol
(cd apps/desktop && pnpm exec tsc --noEmit)   # the desktop app

Every pull request and every push to main runs the same checks in GitHub Actions, plus a build of the bot computer image (.github/workflows/ci.yml).

Releases come from main. Merge a change that bumps the version (the files are listed in CLAUDE.md) together with a "What's new" section in .github/release-notes.md, and the release workflow builds the Mac dmgs (Apple Silicon and Intel), the Linux .deb and AppImage, and the bot computer image on ghcr.io/alnutile/grokked-computer. It then tags vX.Y.Z and opens a draft release; publish it from the Releases page. To retry a failed release, run the release workflow by hand on main. A local Mac build is scripts/build-mac.sh.

What changed in each version is in CHANGELOG.md.

CLAUDE.md has the working rules for AI agents on this repo, including keeping the What it can do list current with every change.

Name

Grokked Bot is a working title and leans on someone else's trademark. Worth renaming before this goes anywhere public — this project is not affiliated with xAI, Grok, or Anthropic.

About

Open Source Alternative Harness to Grok Bot and Muse

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages