npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@intentic/host

v1.232.0

Published

Local agent that lets an intentic sandbox work on this computer, one outbound WebSocket, an MCP tool surface, and scopes enforced here

Readme

@intentic/host

The small program on your own computer that lets your sandbox work there.

It is the machine half of the host capability; the sandbox half is _sandbox/sandbox/src/hosts/.

your laptop                                    your sandbox
┌────────────────────────────┐                 ┌─────────────────────────────┐
│ intentic-host              │  one outbound   │ /system/hosts/connect  (hub)│
│  ├─ oRPC server            │ ───── wss ────▶ │   holds the oRPC CLIENT     │
│  │   describe/setScopes/   │  hostContract   │ /mcp/hosts/<id>  (bridge)   │
│  │   ping/mcp              │                 │        ▲                    │
│  ├─ MCP tools              │                 │   the agent's tools         │
│  ├─ policy (the grant)     │                 └─────────────────────────────┘
│  └─ audit log              │
└────────────────────────────┘

Why it is shaped like this

The machine dials out. A laptop sits behind NAT, a corporate proxy and a closed lid: nothing can dial it. So it holds one ordinary outbound WebSocket and everything multiplexes over that: no open ports, no router configuration, no VPN.

The machine is the server, though it placed the call. After one plain-JSON hello carrying the enrollment token, the socket becomes oRPC: this package serves hostContract (_sandbox/sandbox-contract/src/contracts/host.contract.ts) and the daemon holds the client. oRPC's websocket adapter attaches to either peer, so who dialled and who serves are independent: and request/response correlation, argument validation and error shape all belong to the link rather than to hand-rolled framing.

The tools live here, not in the sandbox. hostContract has exactly one deliberately untyped procedure: mcp, carrying MCP JSON-RPC verbatim in both directions. That hole is the point: if the contract described each tool, the daemon would have to know every schema and translate, and this computer could no longer learn a tool without a matching daemon release. What the machine can do is decided by src/mcp.ts, here.

The grant is enforced here. src/policy.ts is the only place a scope is checked. The sandbox pushes the owner's switches down on every connect and whenever they are edited; this agent refuses everything outside them and says which switch to flip. That placement is the security argument for the whole feature: the sandbox asks, the machine answers.

The surface

| Tool | Notes | | --- | --- | | describe | OS, shell, home, allowed folders. The agent's first call: it is the difference between writing for this machine and guessing. | | run_command | PowerShell on Windows, the login shell elsewhere. Every command has a deadline; stdin is closed so nothing can hang waiting for input. | | read_file / list_dir | Bounded by the allowed folders. | | write_file / trash_file | Also need the write switch, which is off unless the user turned it on. There is no delete tool: trash_file moves the file somewhere recoverable. | | screenshot | Windows via .NET; Linux via grim/spectacle/import/scrot, picked by session type. | | list_sandboxes / manage_sandbox | The Intentic sandboxes running on this machine: list them, start/stop/restart one (tunnel sidecar included). Managing takes its own switch, off by default, narrower than run_command, so the machine's fleet can be delegated to a sandbox without handing it a shell. Listing is subsumed by either switch. | | swap_sandbox / sandbox_logs | Update one onto a newer image, roll it back, rebuild its approved overlay, prepare its next update, and read its container log. The swap runs ic, takes minutes, and keeps /work and /history; it rides the same sandboxes switch, and logs ride the same rule as listing. prepare is the one that changes nothing: it downloads and builds the next image without touching the container, so the sandbox keeps serving and the update that follows is a restart of seconds. | | remove_sandbox | Delete one, with its files and its history. Its own switch, off by default: everything under sandboxes is undone by doing it again, and this is undone by nothing. |

The sandbox flows are not reimplemented here

swap_sandbox and remove_sandbox spawn the ic CLI that the setup one-liner already put on this machine: the same binary the pasted command, the desktop app's buttons and a hand-typed ic run. This package is the fourth caller of one implementation, not a second copy of it, which is the argument the desktop app makes for spawning the scripts instead of porting them into Rust.

ic is looked for where the installers put it (~/.intentic/ic/bin, then /usr/local/bin) before PATH, and the path is built for the TARGET platform rather than by node:path: this agent's Windows spelling is asserted from a Linux runner, so a function that answers in the running host's dialect could not be checked.

Watching a flow, rather than being told about it afterwards

An update pulls an image and recreates a container: minutes in which an MCP tool result can say nothing at all. So hostContract carries one typed, streaming procedure beside the opaque mcp one: runSandboxFlow: which the browser's Computers view reads live. Both doors call the same functions in src/tools/sandboxes.ts, so what a person watches and what the agent is told can never describe one run differently.

The log tail rides that same procedure (op: "logs"), which is one of the two ops there that change nothing. It is not a route of its own for the reason the other eight share one: it is a button in the same row, on a container that may be too broken to answer any other way, and the stream's shape is already "many lines, then an outcome": which is what a log tail is. Same reading as the sandbox_logs tool, same gate.

op: "prepare" is the other, and it is the reason the update card can now quote a real number. Downloading the image and rebuilding the environment recipe are the minutes; the restart at the end is the seconds, and the sandbox is up and serving through everything before it. Doing the first part on its own: from a button, while the fleet is busy, with nothing interrupted: is what leaves the update itself a restart of about half a minute instead of an unbounded wait.

Typed, unlike mcp, because the reader is different: a model gains nothing from a line as it arrives and everything from this machine learning tools without a daemon release, while a person watching a progress log needs exactly the opposite.

Commands

Most machines never run that one-liner by hand any more: ic sandbox connect installs this agent as part of setting a sandbox up, redeeming a pairing the platform minted with the setup code. A computer connected that way arrives with sandboxes on and every other switch off (no shell, no files, no screen) because installing a sandbox is consent to running a sandbox, not to handing the agent inside it a laptop. Widening it is a click on the card. See _sandbox/sandbox/src/hosts/host-seed.ts.

intentic-host setup --url https://sandbox-… --pair <token>   # what the card's one-liner runs
intentic-host status                                          # what it is connected to, and what it may do
intentic-host run --foreground                                # the connection loop, watchable
intentic-host uninstall [--sandbox <url>]                     # disconnect one sandbox, or all of them

One computer, several sandboxes

A computer answers to a list of sandboxes, each with its own enrollment token and its own grant, and setup adds to that list rather than replacing it. One process holds one ordinary outbound websocket per link (run), which is why running several is just running several: nothing is multiplexed and nothing is shared but the log.

That was not always true, and the way it was wrong is worth keeping written down. setup wrote config.json wholesale, so connecting a computer to a second sandbox silently disconnected it from the first — and the caller that does this most is not a person typing a command, it is the last step of onboarding (connect.ps1 → computer.ps1 → intentic-host setup). Setting up a new sandbox on a computer that already had one therefore took the computer away from the sandbox that owned it: no prompt, nothing about it on the install's progress screen, and the replacement link arriving with every scope off, so for a while the machine answered to nobody. It was found by doing it — a laptop dropped off its owner's sandbox at 99% of an install and had to be re-paired by hand. src/config.test.ts pins the shape of the fix.

uninstall follows from the same fact: naming a sandbox disconnects that one and leaves the agent running for the rest; naming none disconnects everything and takes the autostart entry and the credential file with it.

State lives in ~/.intentic/host: config.json (0600, one entry per linked sandbox: its URL, this machine's id there, the enrollment token and the cached scopes), audit.jsonl (every call, kept even across an uninstall: it is the user's record, not the agent's), host.log, and trash/.

Gotchas worth knowing before editing

  • Every spawn in this package passes windowsHide. The connection loop runs detached (without it, Windows tears it down with the command that started it), which leaves it with no console: and Windows gives a console child of a console-less process one of its own, window included. The flag is per-spawn for that reason: it applies whether or not the parent has a console.
  • The enrollment token rides the hello FRAME, never the URL. A durable credential in a query string ends up in edge logs, connector logs and every proxy between here and the sandbox.
  • A refusal is a value, not an exception. Scope errors come back as ordinary tool results so the model tells the user which switch to flip instead of reporting a broken sandbox and retrying.
  • The binary is compiled with bun build --compile, so process.argv[1] is a path inside the executable. cliLauncher() handles that; passing the entry explicitly to a compiled binary breaks the autostart entry.
  • The install and lifecycle plumbing is not in this package. The ~/.intentic/host home and its 0600 floor, self-relaunch, login autostart and the detached connection loop all come from @intentic/local-agent, shared with @intentic/sync and @intentic/acp-bridge. src/autostart.ts here is only this agent's spec: and it declares no macOS launchAgent, because the connection loop has never been run on a Mac. Opting in is three lines once someone has.

Key files