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

@fivexer/agent

v0.1.2

Published

Host-side daemon that registers any local AI agent CLI as a Fivexer worker: it picks tasks off its queue, runs them, and reports results back

Downloads

394

Readme

@fivexer/agent

Run AI agents on your own machines as Fivexer workers.

Fivexer routes work to whoever fits — by tags, skills, priority, SLA and escalation. This daemon makes a local agent CLI one of those candidates. Someone asks for an SEO audit through the Fivexer MCP server, the matcher picks the worker that fits, and if that worker is an agent on your laptop, this is what picks the task up, runs it, and reports back.

Vendor-neutral by construction. The daemon spawns a command and reads a JSON file. That works with Claude Code, Codex, OpenCode, Cline, Goose, Aider — or a #!/bin/sh script, which is what the test suite runs against. Nothing here depends on any vendor's protocol.

Capability stays on your host: your MCP servers, your API keys, your browser, your filesystem. Routing, SLA, escalation and learning stay in Fivexer.

Quick start

On a machine you are sitting at:

npx @fivexer/agent init          # opens your browser to sign in
npx @fivexer/agent doctor        # check everything before a task depends on it
npx @fivexer/agent run

init finds which agent CLIs you already have, asks which should do the work, signs you in through the browser, registers the worker, puts it on shift, and scaffolds a home directory for that agent's own configuration. Nothing to copy, nothing to paste.

If the sign-in cannot get through — no browser, a proxy, a self-hosted deployment at another address — it asks for a join link instead rather than making you start over. --base-url points it at a deployment other than https://api.5xer.com.

On a machine you are not sitting at

A server, a container, anything without a browser. Create a join link in the console — picking the tags and skills this agent should have — and hand it over:

npm install -g @fivexer/agent
fivexer-agent init --yes --executor claude \
  --join 'https://api.5xer.com/v1/worker-auth/join?token=jl_…'
fivexer-agent service install    # writes a service unit; does not enable it

Install it rather than npx it on a host meant to keep running — see Running in production.

The link is scoped to creating one worker, so it is the right thing to put in a provisioning script. The browser flow is skipped automatically when a link is supplied; --no-web forces it off.

For a fleet you would rather drive from a workspace key: init --api-key sk_… --workspace-id ws_… --tags agent,research.

No long-lived workspace credential ever lands on the host. Whichever path you take, what stays on disk is a wt_ worker session that reaches only its own queue, plus a PIN to renew it. The browser token and the API key are both used once and dropped.

Commands

| | | | ----------------- | --------------------------------------------------------------------- | | init | set this machine up: pick an agent, register, scaffold its home | | join <link> | register a profile by redeeming a join link | | doctor | check config, credentials, executors and shift state; claims nothing | | run | work every profile's queue until stopped | | once | one pass over every queue, then exit — for cron, or a smoke test | | next | print the next task and exit | | mcp | speak MCP over stdio, so an interactive agent pulls its own work | | service install | write a user-level systemd / launchd / Scheduled Task unit, unenabled |

--config <path> picks a config file; --profile <name> narrows any command to one profile.

Bringing your own agent

init creates ~/.fivexer/agents/<profile>/ and runs the agent there. That directory is yours:

~/.fivexer/agents/research/
  AGENTS.md        # standing instructions for every task this agent handles
  .mcp.json        # this agent's own MCP servers — its capabilities
  .claude/skills/  # Claude Code skills, when that is the executor
  README.md        # generated: where your chosen CLI reads each of these from

Fivexer never reads it. Put your MCP servers, your instructions and your skills there and they apply to every task this profile is routed.

Two different things are called "skills" and they are not the same. Agent skills live in that directory and are what the agent can do. Fivexer skills live in the workspace catalog, are attached to the worker, and decide which tasks it is offered. Keep them honest with each other — an agent whose tags promise more than its tools deliver gets handed work it then has to reject.

Profiles

A profile is a Fivexer worker: its own id, tags, skills and backlog. Routing between profiles is therefore done by the Fivexer matcher, on the same rules that route work to people — there is no second dispatch layer on your host.

~/.fivexer/config.json, or ./fivexer.agent.json, or --config. Strict JSON — no comments, no trailing commas. cwd is where that agent's own MCP config and project state live:

{
  "workspaceId": "ws_...",
  "profiles": [
    {
      "name": "research",
      "tags": ["agent", "research", "seo"],
      "skills": [{ "skillId": "seo-audit", "level": 4 }],
      "executor": "claude",
      "cwd": "~/agents/research",
      "workspace": "cwd",
      "passEnv": ["BRAVE_API_KEY"],
      "timeoutMs": 900000
    },
    {
      "name": "dev",
      "tags": ["agent", "dev"],
      "executor": "opencode",
      "workspace": "worktree",
      "repo": "~/src/my-service",
      "concurrency": 2
    }
  ]
}

FIVEXER_WORKSPACE_ID, FIVEXER_BASE_URL, FIVEXER_API_KEY, FIVEXER_STATE_DIR and FIVEXER_POLL_INTERVAL_MS override the file, so one config can be pointed at staging.

A profile registered by a join link carries "registration": "join" and an empty tags list — its routing lives in the console, and a second copy here would only diverge. One caveat worth knowing: join links do not set a backlog size, so such a worker gets the workspace default rather than the profile's concurrency. The daemon still runs no more than concurrency tasks, but the matcher may hand it more, and the surplus waits where nobody else can take it. Match the two, or use the API-key path when the backlog matters.

Profile options

| Field | Default | | | --------------------- | --------------------- | --------------------------------------------------------------------------------- | | name | — | unique; also the suffix of the default worker id | | registration | api-key | join means the console owns tags and skills; written by init/join | | workerId | agent-<host>-<name> | the host is in it because two machines sharing an id would fight over one backlog | | tags | — | required on the api-key path; a worker with no tags matches nothing, silently | | skills | [] | { skillId, level: 1–5, weightOverride? }; needs the skill catalog | | executor | — | a preset name, or { command, args, input } | | workspace | scratch | scratch | cwd | worktree | | concurrency | 1 | also the worker's maxBacklogSize | | passEnv | [] | environment variables the agent is allowed to see | | timeoutMs | 900000 | after this the process group is killed and the task requeued | | requireApprovalTags | [] | tasks with any of these wait for an operator | | progressIntervalMs | 0 | post a progress comment at most this often; 0 is off |

Executors

Presets: claude, codex, opencode, goose, aider, cline. Or name a command yourself:

{ "executor": { "command": "./scripts/handle-task.sh", "args": ["{{briefPath}}"], "input": "file" } }

input is how the brief reaches the process — stdin, file (path at {{briefPath}}), or inline (substituted at {{brief}}). {{resultPath}}, {{runDir}} and {{taskId}} also substitute. No shell is involved, so quoting inside a task can never become argument structure.

Presets are a convenience, not a contract. If a preset's flags have drifted, it fails as "command not found" or falls back to the exit code — never to a confident wrong answer.

The result contract

Each task gets a directory under ~/.fivexer/runs/<taskId>/ holding task.json, BRIEF.md, output.log and state.json. The brief tells the agent to write result.json, and the path is also in $FIVEXER_RESULT_FILE:

{
    "success": true,
    "summary": "One short paragraph — stored on the task, read first by a person.",
    "output": "The full deliverable. Posted to the task thread.",
    "data": { "anythingStructured": "for downstream workflow steps" }
}

If no file appears, the daemon falls back to the preset's structured stdout, and then to the exit code with the tail of the log. That last tier works for any command at all, which is what makes the whole thing vendor-neutral.

summary and output are kept apart deliberately: the summary goes into the task's completion payload, which lives on the assignment and is read on every queue operation, while the deliverable goes to the thread in 4 KB comments. The untruncated copy always stays on your host.

Three kinds of failure

The daemon reports them differently, because Fivexer treats them differently.

  • The agent ran and could not do the work → the task is completed with success: false. A real outcome: it can be routed onward, and the learning layer sees it.
  • Trouble on this host — missing binary, timeout, shutdown → the task is rejected, with the reason on the thread first, while the thread is still writable. It requeues for someone else.
  • The agent runtime itself died — a provider outage, dead credentials, an exhausted quota → also rejected. These exit non-zero like a real failure, so without telling them apart a provider having a bad afternoon would permanently close every task it touched. Only the tail of the output counts as evidence: a stack trace in the middle is a tool result, and often exactly what the agent was asked to investigate.

Kill the daemon mid-task and nothing strands: in-flight tasks are requeued, and anything it misses is caught by the task's own escalation and SLA clocks. On restart it recovers accepted work, and reports a result a previous run finished but never sent rather than doing it twice.

Safety

The posture is sandbox and review the outcome: the agent runs unattended in a bounded environment, and a person reviews the deliverable before acting on it.

  • A task never influences how the agent is spawned. Command, args, working directory and environment come from your config alone. A task reaches the agent only as text, fenced in BRIEF.md with an explicit note that it is a request to act on rather than instructions about how the agent or the host should behave. A task is filed by whoever wanted the work.
  • The agent never sees the daemon's credentials. The child environment is built from nothing: PATH, HOME and a few locale variables, plus exactly what passEnv names. Not the workspace API key, not the worker session token.
  • Credentials the agent prints are masked before they leave the host. Summaries, deliverables, structured result data and progress comments all go through a redactor covering NAME=value, --flag value, Authorization: headers and the common token shapes. output.log is left unmasked on purpose: it is the local diagnostic copy, written 0600, and a filter that runs over a stream cannot see a secret split across two chunks — so masking there would be both weaker and destructive of the one faithful record of what happened.
  • Runs can be held for a person. requireApprovalTags posts a request to the task thread and waits for yes <id> from a console user or an API integration. Comments authored by any worker are ignored — which is what stops an agent approving itself, since the API derives comment authorship from the session rather than the request body.
  • The whole process group is killed on timeout or shutdown, so subprocesses an agent spawned do not outlive the task.

Credentials

Steady state is always the same: a wt_ worker session that reaches only its own worker's queue, rotating itself before expiry, cached 0600 under ~/.fivexer/credentials/ alongside a generated PIN. The PIN is kept because it is the only way back once a refresh chain hits its absolute ceiling — without it an unattended agent needs a human to re-provision it.

The setup paths differ only in how that session is first obtained:

  • Browser sign-in (default when you are at the machine) — the standard native-app OAuth flow: a public client, PKCE, and a redirect to a loopback port only this machine can reach. The token it returns opens the workspace, so it is held just long enough to mint a single-use join link, spend it, and set the worker's backlog and shift — then dropped. It is never written anywhere. The link it opens only works on the machine running the command; over SSH, use a join link.
  • Join link — the console owns the worker's tags and skills. If the link required approval, the machine registers but stays off shift until an operator admits it; doctor says so rather than looking like an empty queue.
  • API keyFIVEXER_API_KEY creates the worker record and sets its PIN, then is not needed again. Read from the environment; never written to the config file.

Platforms

Node 20.12 or newer. The floor is not arbitrary: on Windows the executor has to go through a shell for .cmd shims, and 20.12 is where Node's argument escaping for those was fixed.

| | | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Linux | Developed and tested here. service install writes a systemd user unit. | | macOS | Same code paths as Linux. service install writes a launchd agent. | | Windows | Handled deliberately — .cmd shims, taskkill /T for the process tree, a Scheduled Task from service install, no console windows — but not yet exercised on a Windows machine. The per-platform decisions have tests that run everywhere; a real Windows run has not happened. Treat it as supported-but-unproven and please report what breaks. |

Two behaviours differ on Windows and are worth knowing about. .cmd shims are spawned through a shell because Node requires it, so for those the "no shell is involved" property above is provided by Node's own argument escaping rather than by avoiding the shell entirely — prefer "input": "stdin" or "file" on Windows if that distinction matters to you. And file modes (0600 on credentials and run directories) are a no-op there; the state directory relies on the user profile's own permissions.

Running in production

Use service install rather than a bare run in a terminal — it sets the restart policy and gives the daemon enough time on shutdown to hand its in-flight tasks back.

Install it globally first. npx unpacks into a cache npm eventually deletes, so a unit pointing there works today and stops weeks later; service install refuses to write one and tells you this.

npm install -g @fivexer/agent
fivexer-agent service install      # prints the enable command; does not run it

Upgrades are npm install -g @fivexer/agent@latest then a restart of the service. Nothing in the run directory or credential cache changes format between patch versions.

What to watch: the worker's shift state in the Fivexer console tells you whether a host is present — the daemon goes on shift when it starts and off when it stops cleanly. A crash skips the off-shift step, which is the one case where the console can lag reality; the task's own escalation and SLA clocks are what actually guarantee nothing strands.

Shift state

Workers are created off shift, on the assumption a human turns themselves on at a portal. An agent has no portal, so the daemon goes on shift when it starts and off shift when it stops cleanly — which also means a stopped daemon reads as off shift in team presence, rather than "working" forever. A crash skips the off-shift step; the task's own escalation and SLA clocks are what actually guarantee nothing strands.

Interactive mode

fivexer-agent mcp exposes the task lifecycle as MCP tools (fivexer_next_task, fivexer_accept, fivexer_complete, fivexer_reject, fivexer_comment), so any MCP-capable client can pull its own work with no executor configured at all:

{ "mcpServers": { "fivexer": { "command": "npx", "args": ["fivexer-agent", "mcp"] } } }