{"_id":"@archastro/archbrowse","_rev":"2-60ad065584e8ccca96fb70a11fd3eb21","name":"@archastro/archbrowse","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@archastro/archbrowse","version":"0.1.0","keywords":["terminal","browser","react","kitty","chromium","agents","herdr"],"author":{"name":"ArchAstro Inc."},"license":"MIT","_id":"@archastro/archbrowse@0.1.0","maintainers":[{"name":"calvin.grunewald","email":"calvin@archastro.ai"},{"name":"vks-archastro","email":"vks@archastro.ai"},{"name":"tore-archastro","email":"tore@archastro.ai"}],"homepage":"https://github.com/ArchAstro/archbrowse#readme","bugs":{"url":"https://github.com/ArchAstro/archbrowse/issues"},"bin":{"starpane":"dist/cli.js","archbrowse":"dist/cli.js","react-kitty":"dist/cli.js"},"dist":{"shasum":"dfa89f01166108967bfb8fb751cb299cd36ca824","tarball":"https://registry.npmjs.org/@archastro/archbrowse/-/archbrowse-0.1.0.tgz","fileCount":73,"integrity":"sha512-HwA9fCR4iRMSjctpQJOoG+Te8r6opy85YVg/EowWyGHKNxhTrUxoQ7JSccaawvcLSliLm8y3biNVkZicNcFTLA==","signatures":[{"sig":"MEUCICam8MFcLS+SZSBUI+o5NzWZMu1T+UUPAE7HtEeTD7ZqAiEAuO8Rf36saJ0stHkITGCQb5RtX0lQxGycQD4QsIoVuvk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":295118},"type":"module","engines":{"node":">=22"},"gitHead":"5a3849c7721da0fc5c30755f3926d549b2d434a0","scripts":{"dev":"tsx src/cli.ts","test":"tsx --test test/*.test.ts","build":"tsc","check":"npm run typecheck && npm test && npm run test:e2e && npm run test:sessions && npm run test:agent","prepack":"npm run build","test:e2e":"tsx test/harness/run.ts","typecheck":"tsc --noEmit","test:agent":"npm run build && node scripts/prepare-pty.mjs && tsx test/harness/agent.ts","test:herdr":"tsx test/harness/herdr-run.ts","test:skill":"node scripts/prepare-pty.mjs && tsx test/harness/skill.ts","pretest:e2e":"npm run build && node scripts/prepare-pty.mjs","test:native":"npm run build && tsx test/harness/native.ts","test:package":"node scripts/check-package.mjs","pretest:herdr":"npm run build && node scripts/prepare-pty.mjs","test:sessions":"npm run build && node scripts/prepare-pty.mjs && tsx test/harness/persistence.ts"},"_npmUser":{"name":"calvin.grunewald","email":"calvin@archastro.ai"},"repository":{"url":"git+https://github.com/ArchAstro/archbrowse.git","type":"git"},"_npmVersion":"11.19.0","description":"A terminal browser you and your agents can control","directories":{},"_nodeVersion":"26.8.1","dependencies":{"yaml":"^2.9.0","react":"^19.0.0","esbuild":"^0.25.0","react-dom":"^19.0.0","playwright-core":"^1.58.0","proper-lockfile":"^4.1.2","toml-eslint-parser":"^0.10.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.0","pngjs":"^7.0.0","node-pty":"^1.1.0","typescript":"^5.9.0","@types/node":"^22.0.0","@types/pngjs":"^6.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/proper-lockfile":"^4.1.4"},"_npmOperationalInternal":{"tmp":"tmp/archbrowse_0.1.0_1788727508882_0.09559042312743138","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"_id":"@archastro/archbrowse@0.2.0","bin":{"starpane":"dist/cli.js","archbrowse":"dist/cli.js","react-kitty":"dist/cli.js"},"bugs":{"url":"https://github.com/ArchAstro/archbrowse/issues"},"dist":{"shasum":"11ed451fb55d175813dc40c2075497eb35a1ad12","tarball":"https://registry.npmjs.org/@archastro/archbrowse/-/archbrowse-0.2.0.tgz","fileCount":86,"integrity":"sha512-143lrljLnweb0/KCDay/QAPMo/IMASXwPn780VwV33+GY8yTI5o+mPYk8Nk7/MaYDI/w+fyaPC972VvJ0XiFew==","signatures":[{"sig":"MEQCICbC/Gn8zDRZ2mcWVfYPZ80TR/ZCFlISS8nXEgxaAx7LAiAn15dDVc1tM9feu/4ihJ8++UuZG7GGGM967U4AGGRTpA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIFTQRQgkv+YJf3J4ybqWwBtI1ZmkpYGClsH5uuFIqbVPAiEAt7TySozduyQKHEDsDtlZY00tmem9EX6mm9W+OviMBKs="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@archastro%2farchbrowse@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":353957},"name":"@archastro/archbrowse","type":"module","_from":"file:archastro-archbrowse-0.2.0.tgz","author":{"name":"ArchAstro Inc."},"engines":{"node":">=22"},"license":"MIT","scripts":{"dev":"tsx src/cli.ts","test":"tsx --test test/*.test.ts","build":"tsc","check":"npm run typecheck && npm test && npm run test:e2e && npm run test:sessions && npm run test:agent && npm run test:headless","prepack":"npm run build","test:e2e":"tsx test/harness/run.ts","typecheck":"tsc --noEmit","test:agent":"npm run build && node scripts/prepare-pty.mjs && tsx test/harness/agent.ts","test:herdr":"tsx test/harness/herdr-run.ts","test:skill":"node scripts/prepare-pty.mjs && tsx test/harness/skill.ts","pretest:e2e":"npm run build && node scripts/prepare-pty.mjs","test:native":"npm run build && tsx test/harness/native.ts","test:package":"node scripts/check-package.mjs","pretest:herdr":"npm run build && node scripts/prepare-pty.mjs","test:headless":"npm run build && node scripts/prepare-pty.mjs && tsx test/harness/headless.ts","test:sessions":"npm run build && node scripts/prepare-pty.mjs && tsx test/harness/persistence.ts"},"version":"0.2.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:a22e6d84-b399-4fa5-bbe5-3deb51b4ce53"}},"homepage":"https://github.com/ArchAstro/archbrowse#readme","keywords":["terminal","browser","react","kitty","chromium","agents","herdr"],"_resolved":"/home/runner/work/archbrowse/archbrowse/archastro-archbrowse-0.2.0.tgz","_integrity":"sha512-143lrljLnweb0/KCDay/QAPMo/IMASXwPn780VwV33+GY8yTI5o+mPYk8Nk7/MaYDI/w+fyaPC972VvJ0XiFew==","repository":{"url":"git+https://github.com/ArchAstro/archbrowse.git","type":"git"},"_npmVersion":"11.19.0","description":"A terminal browser you and your agents can control","directories":{},"maintainers":[{"name":"calvin.grunewald","email":"calvin@archastro.ai"},{"name":"vks-archastro","email":"vks@archastro.ai"},{"name":"tore-archastro","email":"tore@archastro.ai"}],"_nodeVersion":"24.20.0","dependencies":{"yaml":"^2.9.0","react":"^19.0.0","esbuild":"^0.25.0","react-dom":"^19.0.0","playwright-core":"^1.58.0","proper-lockfile":"^4.1.2","toml-eslint-parser":"^0.10.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.20.0","pngjs":"^7.0.0","node-pty":"^1.1.0","typescript":"^5.9.0","@types/node":"^22.0.0","@types/pngjs":"^6.0.0","@types/react":"^19.0.0","@types/react-dom":"^19.0.0","@types/proper-lockfile":"^4.1.4"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/archbrowse_0.2.0_1789428044679_0.9175012403766829"}}},"time":{"created":"2026-09-06T20:45:08.708Z","modified":"2026-09-14T23:20:45.172Z","0.1.0":"2026-09-06T20:45:09.042Z","0.2.0":"2026-09-14T23:20:44.779Z"},"bugs":{"url":"https://github.com/ArchAstro/archbrowse/issues"},"author":{"name":"ArchAstro Inc."},"license":"MIT","homepage":"https://github.com/ArchAstro/archbrowse#readme","keywords":["terminal","browser","react","kitty","chromium","agents","herdr"],"repository":{"url":"git+https://github.com/ArchAstro/archbrowse.git","type":"git"},"description":"A terminal browser you and your agents can control","maintainers":[{"name":"calvin.grunewald","email":"calvin@archastro.ai"},{"name":"vks-archastro","email":"vks@archastro.ai"},{"name":"tore-archastro","email":"tore@archastro.ai"}],"readme":"# ArchBrowse\n\n[![CI](https://github.com/ArchAstro/archbrowse/actions/workflows/ci.yml/badge.svg)](https://github.com/ArchAstro/archbrowse/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n*A terminal browser you and your agents can control.*\n\nRun React apps, arbitrary HTTP(S) websites, and local HTML files inside a Kitty graphics terminal. Chromium renders the DOM, CSS, SVG, canvas and browser events; the CLI transports pixels and terminal input. Source edits rebuild automatically.\n\n```fish\ngit clone https://github.com/ArchAstro/archbrowse.git\ncd archbrowse\nnpm ci\nnpm run build\nnode dist/cli.js examples/App.tsx\n```\n\nUse **Node 22+** and a terminal implementing Kitty graphics, such as Kitty or Ghostty. Quit with **Ctrl+Q**. The CLI reuses installed Chromium when available, otherwise downloads Chromium; `--no-install` disables it. Start from source as shown above; an npm release is not assumed.\n\n## Agent skill and first-use setup\n\nInstall the agent skill directly from this repository:\n\n```fish\nnpx skills add ArchAstro/archbrowse --skill archbrowse\n```\n\nAdd `--global` for user-wide installation, or `--agent codex` to select Codex explicitly.\n\nThe bundled [ArchBrowse skill](skills/archbrowse/SKILL.md) is discoverable at `skills/archbrowse/`. It includes its bootstrap installer, saved-preference helper and usage references. It can install the CLI, collect terminal/layout/session preferences, and teach an agent to launch and control a viewer. The skill is also included in the npm package; `archbrowse --skill` prints its entrypoint.\n\nBootstrap from a checkout:\n\n```fish\nnode skills/archbrowse/scripts/bootstrap.mjs install\n```\n\nThe helper reuses a working CLI, otherwise installs a user-local build and links `~/.local/bin/archbrowse`. With a standalone skill copy, it fetches the canonical GitHub repo using Git over HTTPS (private repositories require Git credentials). It does not assume an npm release exists and does not install Chromium unconditionally.\n\nPreferences are saved in `~/.config/archbrowse/preferences.json` (or `$XDG_CONFIG_HOME/archbrowse/preferences.json`), independently of browser profiles and the source repository. `ARCHBROWSE_PREFERENCES_FILE` overrides that path. Global defaults can be overridden per canonical worktree.\n\nExample preference choices:\n\n```fish\nnode skills/archbrowse/scripts/preferences.mjs set --host herdr --placement split --sessions workspace --focus keep\nnode skills/archbrowse/scripts/preferences.mjs set --workspace /path/to/worktree --placement tab\nnode skills/archbrowse/scripts/preferences.mjs plan --workspace /path/to/worktree\n```\n\nSupported choices cover HerdR splits/tabs, direct terminal placement, workspace/fresh/named/ask session policies, focus, split direction, mobile mode and browser downloads. A tmux preference is retained but reported as unsupported for rendering; agent commands can still run in tmux against a live viewer elsewhere.\n\n`npm run test:skill` exercises saved split and tab preferences against an isolated real HerdR session using the installed CLI. Run the bootstrap installer first. Unit tests cover preference persistence, overrides and session isolation.\n\n## Background sessions: start now, attach later\n\n```fish\narchbrowse example.com --session work --headless\narchbrowse --session work snapshot -i --json\narchbrowse attach work --view\n```\n\nThe first command starts Chromium in a detached background process and returns\nwhen it is ready. It works without a TTY, including from an agent or tmux. HTML\nand React entry files work too. The attached viewer shows the same live page;\n**Ctrl+Q detaches the viewer without closing the browser**. A killed/disconnected\nviewer also leaves the session alive. One visual viewer may attach at a time,\nalongside agent commands.\n\n```fish\narchbrowse sessions stop work          # Save and close the background browser\narchbrowse --session work --headless   # Reopen its saved profile\n```\n\nDesktop starts at 1280×720 (`--width`/`--height` override it) and resizes when a\nviewer attaches. Mobile uses `--mobile` with a fixed 390×844 viewport. Detaching\nkeeps the current viewport and live JavaScript state; stopping or rebooting does\nnot. `attach work --json` reports the owner PID, mode, and viewer attachment state.\nLogs are private to the session directory at `background.log`.\n\n`attach work` without `--view` remains the agent inspection command. A direct\nviewer started without `--headless` still closes its browser on Ctrl+Q. Visual\nattachment requires a background owner and a Kitty-compatible terminal; tmux\nrendering remains unsupported.\n\nTo make headless the skill's default:\n\n```fish\nnode skills/archbrowse/scripts/preferences.mjs set --mode headless --sessions workspace\n```\n\nThe skill collects a terminal layout only when a visual viewer is wanted. Its\n[background-session guide](skills/archbrowse/references/background.md) teaches the\ncomplete start, reuse, attach, detach, stop and recovery flow.\n\n## Upgrading from Starpane or React Kitty\n\nThe package is now `@archastro/archbrowse`, the primary command is `archbrowse`, and the skill is `$archbrowse`. Installed `starpane` and `react-kitty` commands remain aliases to the same CLI.\n\n1. New settings use `ARCHBROWSE_*`. The corresponding `STARPANE_*` settings remain accepted, along with the original `REACT_KITTY_SESSIONS_DIR` and `REACT_KITTY_CHROMIUM`; new names take precedence.\n2. New profiles use `~/.local/share/archbrowse/sessions`. Existing `starpane/sessions` or `react-kitty/sessions` directories are reused when no newer directory exists, preserving profiles, locks and live agent sockets.\n3. Preferences default to `~/.config/archbrowse/preferences.json`. Existing `~/.config/starpane/preferences.json` is reused if the new file does not exist. The saved layout and session policy are preserved.\n4. Optional test settings `STARPANE_WEB_SMOKE`, `STARPANE_HERDR_TEST`, `REACT_KITTY_WEB_SMOKE` and `REACT_KITTY_HERDR_TEST` remain aliases for `ARCHBROWSE_*`.\n\n## 1. Run your component\n\n```fish\nnode dist/cli.js /path/to/App.tsx\nnode dist/cli.js /path/to/App.jsx\nnode dist/cli.js /path/to/App.js\nnode dist/cli.js https://example.com\nnode dist/cli.js example.com\nnode dist/cli.js localhost:3000\nnode dist/cli.js ./index.html\nnode dist/cli.js examples/App.tsx --mobile\n```\n\nExport a React component as `default` or `App`:\n\n```tsx\nimport { useState } from 'react';\nimport './styles.css';\n\nexport default function App() {\n  const [count, setCount] = useState(0);\n  return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;\n}\n```\n\n1. `.tsx`, `.jsx`, `.js` (including JSX), and `.ts` entries work. Entries that mount themselves can use the supplied `#root` element.\n2. Local imports, installed project dependencies, CSS imports, imported images/fonts and `public/` assets work. React/React DOM fall back to the CLI's dependencies when the project doesn't supply them.\n3. Edits anywhere in the bundled dependency graph trigger a **full reload**. Component state resets. Compile errors retain the last build behind an error overlay; runtime errors also appear in the page. Fixing the file reloads the app.\n4. This is a client-side esbuild environment. It does not run a project's Vite/Next configuration, SSR, server components, or backend. For those projects, start their dev server and pass its URL.\n5. Local code executes with normal browser capabilities. Only the generated build and `public/` assets are served, bound to an ephemeral loopback port. No files are written into your app.\n\n### Websites and local HTML\n\n```fish\nnode dist/cli.js example.com\nnode dist/cli.js 'https://example.com/path?query=value'\nnode dist/cli.js localhost:3000\nnode dist/cli.js ./site/index.html\nnode dist/cli.js ./site/pages/about.htm --root ./site\nnode dist/cli.js 'file:///absolute/path/site/index.html'\n```\n\n1. Full HTTP/HTTPS URLs open directly in Chromium. Bare domains use HTTPS; localhost and loopback addresses use HTTP. Redirects, page scripts, links and forms use normal browser behavior. Sites can still require login or reject automated browsers.\n2. `.html` and `.htm` documents are served **unchanged** from an ephemeral loopback HTTP server. Relative CSS, JavaScript modules, images, fonts, fetch requests, links and media range requests work. No React runtime is added. A static HTML site has no server-side POST handler; point at your running backend for that.\n3. The entry file's directory is the default document root. `--root` selects a parent directory for nested pages and shared assets. Paths outside the root, escaping symlinks, and hidden paths are not served. `file:` inputs use the same HTTP server, preserving query/fragment and avoiding browser file-origin module restrictions.\n4. Changes to the entry or loaded local assets reload local pages, including background tabs. This is a full reload; state resets. HTTP cache is disabled for local assets.\n5. New-window links and `window.open()` become active tabs. **Ctrl+Tab / Ctrl+Shift+Tab** switch tabs, **Ctrl+W** closes the active tab, and closing the final tab exits. **Alt+Left / Alt+Right** go back/forward; **Ctrl+R**, **Command+R**, or **F5** reload. Terminal-reserved shortcuts may require terminal configuration.\n\nTry the complete plain-HTML example:\n\n```fish\nnode dist/cli.js examples/html/index.html\nnode dist/cli.js examples/html/index.html --mobile\n```\n\n### Running inside HerdR\n\nHerdR requires its pane-graphics API; raw Kitty escapes from a pane are not forwarded to the outer terminal. ArchBrowse detects `HERDR_ENV`, targets the calling `HERDR_PANE_ID`, and streams frames through `HERDR_SOCKET_PATH`. The stream's owned image layer is removed on exit. Direct Ghostty/Kitty uses the regular Kitty transport.\n\nWhen graphics are disabled, launching ArchBrowse offers to update your HerdR config and reload it:\n\n```text\nEnable experimental.kitty_graphics in /path/to/config.toml and reload HerdR? [y/N]\n```\n\nAnswer `y` to apply it. The edit preserves comments, unrelated keys and symlinks, and saves a backup. Answering `n` leaves the file untouched. The CLI honors `HERDR_CONFIG_PATH` and refuses to overwrite edits made while the prompt was open. If graphics are already enabled, it explains the running-client limitation without offering a redundant write.\n\nThe equivalent manual setting is:\n\n```toml\n[experimental]\nkitty_graphics = true\n```\n\nRun `herdr server reload-config`, then detach and reattach the HerdR client so it discovers the host terminal's graphics and cell-size capabilities. Run ArchBrowse normally inside a pane:\n\n```fish\nnode dist/cli.js example.com\n```\n\nThe CLI automatically retries dimension discovery for up to five seconds before launching Chromium. HerdR 0.8.2 latches the client graphics setting at startup: `reload-config` updates other client settings but does not enable graphics in a client started with it disabled. That existing client requires one reattachment; the public API has no operation to enable its graphics flag in place. `--force` cannot enable HerdR rendering. HerdR's virtual terminal can answer a Kitty support query with `OK` even while image rendering is disabled, so that reply alone is not used as proof of support. Mouse coordinates use pane-relative pixel reporting when HerdR advertises pixel mouse support. This keeps small links clickable even with large/high-DPI terminal cells. Older clients fall back to cell reporting.\n\n## Named sessions\n\n```fish\n# Create a session or use its existing browser profile\nnode dist/cli.js example.com --session work\nnode dist/cli.js ./App.tsx --session preview\n\n# Reopen saved tabs without repeating the target\nnode dist/cli.js --session work\nnode dist/cli.js --session preview\n\n# Manage profiles\nnode dist/cli.js sessions list\nnode dist/cli.js sessions list --json\nnode dist/cli.js sessions delete work\n```\n\n1. **Persistence:** each name owns a private Chromium profile. Cookies (including session cookies), localStorage and IndexedDB survive clean exits. Open HTTP(S) tab URLs and the active tab are saved and reopened. Site login expiration rules still apply.\n2. **Lifecycle:** Ctrl+Q saves state and closes Chromium. Reopening starts a new browser process and reloads the pages. JavaScript/React memory, unsaved form edits, navigation history and `sessionStorage` are not restored. There is no background daemon or live detach/reattach.\n3. **Targets:** omitting the target reopens saved tabs. Supplying a target uses the same profile but starts at that target. Local paths and `--root` are saved as absolute paths; the mobile viewport setting is remembered.\n4. **Local apps:** a named session reuses its HTTP port so the origin—and therefore localStorage/IndexedDB—stays stable. If another process occupies that port, startup fails instead of silently changing the origin. Local files must still exist when reopening.\n5. **Isolation:** names use separate profiles and allow only one active CLI per name. Another launch or deletion is refused while the session is active. A crashed CLI's heartbeat lock becomes reclaimable after 10 seconds. Clean exit is required for the latest tab/cookie snapshot.\n6. **Storage:** defaults to `$XDG_DATA_HOME/archbrowse/sessions`, or `~/.local/share/archbrowse/sessions`. `ARCHBROWSE_SESSIONS_DIR` overrides the directory. Directories use mode `0700` and metadata/cookie files use `0600` on Unix. These files contain browsing state; deleting a session removes its profile and saved state.\n7. **Names:** 1–64 lowercase letters, digits, hyphens or underscores; the first character must be a letter or digit. `--session` cannot be combined with `--cdp`, since named sessions must own their profile. Without `--session`, launches remain temporary.\n\n## Let an agent drive a live session\n\nStart the viewer in one terminal:\n\n```fish\nnode dist/cli.js ./App.tsx --session work\n```\n\nFrom another shell or agent process, use the same session name:\n\n```fish\nnode dist/cli.js attach work\nnode dist/cli.js --session work snapshot -i\n# Use the exact ref printed by the snapshot:\nnode dist/cli.js --session work fill @eabcd_2 \"Ada\"\nnode dist/cli.js --session work click @eabcd_3\nnode dist/cli.js --session work wait --text \"Welcome\"\nnode dist/cli.js --session work snapshot -i\nnode dist/cli.js --session work screenshot ./page.png\n```\n\nThe agent drives the **same Chromium page displayed in the terminal**, including the same login, React state and storage. This also works when the viewer is inside HerdR. The viewer must remain running; an inactive saved session returns `session_not_running`. `attach NAME` reports the live session's URL/title/tab. Pass `--session NAME` on each action so the target stays explicit.\n\nThe interface follows agent-browser's documented snapshot/ref/action loop; its implementation is original and uses the viewer's existing Playwright context.\n\n| Command | Purpose |\n| --- | --- |\n| `snapshot [-i]` | Accessibility tree and compact interactive refs |\n| `read` | Visible body text |\n| `click`, `dblclick`, `hover`, `focus` `REF_OR_SELECTOR` | Interact with an element |\n| `fill`, `type` `REF_OR_SELECTOR TEXT` | Replace text or type at the element |\n| `press KEY` | Key/chord at current focus, e.g. `Enter` or `Control+a` |\n| `check`, `uncheck` `REF_OR_SELECTOR` | Checkbox state |\n| `select REF_OR_SELECTOR VALUE...` | Select option values |\n| `scroll up\\|down\\|left\\|right [PIXELS]` | Scroll, default 500 pixels |\n| `get url\\|title` | Current page metadata |\n| `get text\\|html\\|value REF_OR_SELECTOR` | Element contents |\n| `get attr REF_OR_SELECTOR NAME` | Element attribute |\n| `wait SELECTOR`, `wait --text TEXT`, `wait --url GLOB` | Wait for a product condition |\n| `screenshot PATH [--full]` | Save a PNG at the caller's resolved path |\n| `open URL`, `back`, `forward`, `reload` | Navigate the active tab |\n| `tab list`, `tab new [URL]`, `tab t1`, `tab close [t1]` | Inspect, open, switch and close tabs |\n| `eval JAVASCRIPT` | Evaluate JavaScript in the active page |\n\n1. **Refs:** generated by `snapshot`, pinned to actual elements, and invalidated by a new snapshot, navigation or a tab change. Detached/replaced elements return `stale_ref`; duplicate labels do not silently retarget a ref after DOM reordering. Take a fresh snapshot after page changes. Refs are intentionally scoped to one live browser session and cannot be guessed/reused across restarts.\n2. **Selectors:** commands accept Playwright selectors as an alternative to refs, e.g. `'#submit'` or `'input[name=email]'`. Commands target the active tab's main frame.\n3. **Results:** add `--json` for `{ \"ok\": true, \"result\": ... }` or `{ \"ok\": false, \"error\": { \"code\": ..., \"message\": ... } }`. Failures exit with status 1. Snapshots include a structured `refs` array alongside the readable tree.\n4. **Timeouts:** `--timeout MS` defaults to 10000 and accepts 1–30000. Agent waits do not block terminal input. Other commands share the viewer's action queue. A failed command leaves the viewer running.\n5. **Lifecycle:** control uses a private local socket under the session directory (a short owner-only runtime directory is used when the path is too long). It is removed when the viewer exits. Agent attachment does not take the profile lock or open a second browser.\n\nTest the complete two-process workflow:\n\n```fish\nnpm run test:agent\nnpm run test:herdr\n```\n\nThe tests drive a named viewer from independent CLI processes, verify terminal/browser pixel parity, exercise stale/duplicate refs and tab switching, mix agent waits with human terminal input, and check endpoint cleanup. The HerdR suite also verifies agent-driven changes in the real multiplexer’s outer image stream.\n\n## 2. Reuse Chromium\n\nDiscovery checks the matching Playwright installation, common system Chrome/Chromium/Edge locations, PATH and cached Playwright versions. Unnamed launches use an isolated temporary browser context. Named launches use their own persistent profile; existing personal Chrome profiles are not opened.\n\n```fish\nnode dist/cli.js App.tsx --no-install\nnode dist/cli.js App.tsx --chromium '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'\nnode dist/cli.js App.tsx --cdp http://127.0.0.1:9222\n```\n\n`ARCHBROWSE_CHROMIUM` also selects a binary. `--cdp` attaches to an explicitly supplied Chromium debugging endpoint, creates its own context, and closes only that context on exit. It leaves the external browser running. It does not scan for or take over arbitrary running browsers.\n\nIf no executable exists, the default behavior installs Playwright Chromium once. `--no-install` prevents that. To install explicitly:\n\n```fish\nnode dist/cli.js --install-browser\n```\n\nLinux hosts still need Chromium's OS libraries. This CLI does not install OS packages.\n\n## 3. Input and display\n\n| Input | Behavior |\n| --- | --- |\n| Click, move, drag, double/triple click | Chromium mouse events |\n| Wheel / horizontal wheel | Scroll the element under the pointer |\n| Text, Enter, Tab, Shift+Tab, arrows, editing keys | Chromium keyboard events |\n| Enhanced Kitty keys | Modifiers, repeat and release events |\n| Bracketed paste | Inserts the complete UTF-8 text, including newlines |\n| Terminal resize | Resizes the desktop CSS viewport |\n| Alt+Left / Alt+Right | Browser back / forward |\n| Ctrl+R / Command+R / F5 | Reload active page |\n| Ctrl+Tab / Ctrl+Shift+Tab | Next / previous tab |\n| Ctrl+W / Command+W | Close active tab |\n| Ctrl+Q | Exit and restore terminal modes |\n\nOn macOS, Chromium uses macOS editing conventions (for example, Command+A selects all). Terminal-reserved shortcuts may never reach the application. Legacy keyboard protocols cannot express every key combination or key release.\n\n`--mobile` selects a 390 × 844 CSS-pixel viewport, mobile user agent and touch support. Mouse down/move/up become one-finger touch start/move/end. Mobile output fits the available terminal cells without stretching across the full window; unused space is on the right/bottom. `--width` and `--height` customize that viewport.\n\nTerminal cell/pixel dimensions and pixel mouse support are queried. SGR cell mouse coordinates are the fallback. Use `--cell-width 8 --cell-height 16` to override fallback dimensions when your terminal omits geometry replies. A positive Kitty graphics query is required unless `--force` is given. Run directly in the terminal; tmux passthrough is not implemented.\n\n`--fps 15` caps outgoing frame rate (range 1–60). Chromium streams compositor PNGs; the CLI keeps the newest frame, skips identical images, chunks base64 into 4096-byte Kitty packets, and respects stdout backpressure. It alternates two image IDs, deletes the old image after replacement, and uses synchronized terminal output.\n\n### Current boundaries\n\n1. This is a page viewport, not complete browser chrome: downloads, file pickers, browser permission prompts and OS-native popup surfaces aren't integrated. JavaScript dialogs are dismissed so they cannot freeze the terminal. In-page React dialogs work normally; native select controls can be operated with keyboard input.\n2. Pasting from the terminal works. Automatic synchronization with the OS clipboard, IME composition sessions, accessibility text and screen-reader semantics are not transported in the raster image.\n3. Mobile mode emulates one touch point. Pinch/multitouch and physical mobile terminal clients are not verified. Support depends on the events the terminal sends.\n4. CI verifies browser input, lossless PNG transport, and real HerdR placement. Native Kitty/Ghostty compositing remains a separate manual visual check; the automated suite does not verify every terminal emulator.\n\n## 4. Test and inspect\n\n[GitHub Actions CI](https://github.com/ArchAstro/archbrowse/actions/workflows/ci.yml) runs on pull requests, pushes to `main` and `feat/**`, and manual dispatch. It checks Node 22 and 24 on Linux and Node 22 on macOS: typechecking, unit tests, real PTY/browser pixel parity, persistent sessions, agent control, and installation of the packed production CLI with its aliases and bundled skill. Chromium is provisioned explicitly from the locked Playwright version. Screenshot reports and terminal transcripts are retained as workflow artifacts for seven days.\n\n`npm run test:headless` verifies detached-process startup, agent/human control, viewer crashes, mobile mode and stop/restart persistence. `test:herdr` also exercises background sessions through real HerdR. Run `npm run test:package` locally to verify the distributable. Node 22 CI also runs the actual HerdR integration (`test:herdr` and `test:skill`) on Linux and macOS using checksum-verified upstream HerdR 0.8.2 binaries. These exercise graphics, high-DPI clicks, configuration prompts, agent control, and bootstrapping with saved split/tab preferences inside isolated PTYs. Locally they require HerdR on PATH. Native Ghostty capture remains a separate manual check.\n\n```fish\nnpm run check\n```\n\n1. Typecheck and protocol tests cover byte-fragmented UTF-8/escape sequences, paste, key releases, mouse coordinates, graphics chunking and browser discovery.\n2. A real `node-pty` process launches the **built CLI**. An independent receiver answers terminal queries and reconstructs Kitty PNGs. A separate Playwright connection asserts UI outcomes and compares decoded pixels against browser screenshots.\n3. Journeys cover all entry formats, URL mode, mouse/keyboard/Unicode input, checkbox and slider dragging, wheel, resize, compile/runtime errors and recovery, mobile touch and text, cell mouse fallback, external-browser ownership, unchanged HTML asset loading, forms, links, history, reload, popup/tab switching, HTML edits, mobile HTML, and HTTP redirects.\n4. Each run produces `artifacts/<timestamp>/index.html`, `report.md`, reference/terminal PNG pairs and a terminal transcript. Failures retain screenshot/HTML evidence. The suite uses an existing browser and never downloads one.\n5. `npm run test:sessions` runs separate named CLI/browser processes to prove restart persistence, stable local origins, profile isolation, tab restoration, concurrent-use rejection, listing and deletion. It uses a temporary session store.\n6. `node-pty` is **test-only**. The pretest script restores execute permission on its macOS prebuilt spawn helper if the npm artifact arrives without it.\n\nAn optional public-network smoke checks an external HTTPS page (use a stable page with an `h1`):\n\n```fish\nenv ARCHBROWSE_WEB_SMOKE=https://example.com npm run test:e2e\n```\n\nTo test the **actual HerdR process inside a PTY**, including emitted Kitty pixels, image placement, outer-terminal clicks, resize, and the Example Domain link at 21×48-pixel cell size:\n\n```fish\nnpm run test:herdr\n```\n\nSet `ARCHBROWSE_WEB_SMOKE=https://example.com` when running this command to also click the live Learn more link through HerdR and follow its IANA redirect.\n\nThis requires `herdr` on PATH (and `python3` for the high-DPI PTY ioctl fixture), creates and removes an isolated named HerdR session with graphics enabled, and opens no native terminal windows. It decodes HerdR's outer-terminal image packets and compares them with Chromium without pre-seeding host dimensions. It also reproduces starting a client with graphics disabled and then reloading the enabled config, proving the old-client limitation. The PTY suite also accepts and declines the configuration prompt and verifies reload/backup behavior. Unit tests cover delayed capability replies, format-preserving TOML edits and concurrent edit protection. The ordinary PTY tests explicitly clear inherited HerdR pane variables so they cannot accidentally target an existing user pane.\n\nFor a real Ghostty window screenshot on macOS:\n\n```fish\nnpm run test:native\n```\n\nThis checks Screen Recording access before opening a window. With access enabled, it launches the app and saves `artifacts/native/ghostty.png` for visual review. It leaves the window open for manual input testing. This is explicitly separate from the automated pixel-parity gate.\n\n## 5. Design and provenance\n\n```text\nAgent edits App.tsx ── esbuild watch ── loopback HTTP + reload events\n                                              │\n                                              ▼\nTerminal input ── streaming parser ── Chromium page / CDP\n      ▲                                       │\n      └──── Kitty PNG transport ◀── compositor screencast\n```\n\nThe implementation is original. Research used public descriptions of [terminal-browser](https://github.com/zenbu-labs/terminal-browser), the official [Kitty graphics](https://sw.kovidgoyal.net/kitty/graphics-protocol/) and [keyboard](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) specifications, and browser/build APIs. No implementation source from terminal-browser was copied or used as a template. The input boundary also follows the renderer-owned event model used by [dino-dna/react-tui](https://github.com/dino-dna/react-tui/blob/main/src/components/util/eventHandlers.ts): terminal transports preserve coordinates, while Chromium owns DOM hit testing and default link behavior. Its Blessed widget renderer is a different rendering backend; no code from it was copied.\n\n| File | Responsibility |\n| --- | --- |\n| `src/cli.ts` | Arguments and help |\n| `src/server.ts` | In-memory React bundling, assets, reload/error overlays |\n| `src/target.ts` | File/URL detection and source selection |\n| `src/html.ts` | Unmodified HTML/static assets, document root and file watching |\n| `src/page-view.ts` | Active tab, popup handling, navigation and compositor stream |\n| `src/browser.ts` | Installed browser discovery, optional download, CDP attach |\n| `src/input.ts` | Incremental terminal input decoding |\n| `src/interaction.ts` | Browser keyboard, mouse and touch dispatch |\n| `src/herdr.ts` | HerdR capability checks and owned pane-graphics stream |\n| `src/herdr-setup.ts` | Interactive config update, backup and reload |\n| `src/kitty.ts` | Graphics transport and viewport geometry |\n| `src/agent/` | Agent CLI, private IPC, live commands and snapshot refs |\n| `src/session.ts` | Terminal/browser lifecycle, persistence orchestration and frame loop |\n| `src/sessions.ts` | Named profile store, metadata, cookie snapshots and exclusive locks |\n\n## Contributing and license\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development and verification, [SECURITY.md](SECURITY.md) for private vulnerability reports and the local data model, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community expectations. ArchBrowse is licensed under [MIT](LICENSE).\n","readmeFilename":"README.md"}