@omniralph/agent
v2.3.4
Published
Omniralph agent daemon
Readme
@omniralph/agent
omniralph-agent is the Omniralph agent: the daemon that runs jobs on a machine. It pulls jobs
(reviews, queries, coding stories) from an Omniralph control panel and runs them with the Claude
Agent SDK. It needs Node 22 and git ≥ 2.40.
This README covers installing and releasing the package. To set up a host (the agent's user, the Claude login, pm2, tmux or systemd), see docs/deploy-agent.md.
Install
The package is public on the npm registry: installing it needs no npm token. To install globally without root, point npm's prefix at your home:
npm config set prefix ~/.local # binaries go to ~/.local/bin; put it on PATH
npm install -g @omniralph/agent
omniralph-agent --versionEnroll
As an admin, open Fleet › Enroll in the control panel and run the command it shows on the host, as the agent's user:
omniralph-agent enroll --server https://omniralph.example.com --token <enrollment-token>This writes ~/.config/omniralph/agent.json (--config or OMNIRALPH_AGENT_CONFIG choose
another path), which holds the agent's identity. Its machine settings (slots, accepted task types,
host tool paths, the installables allow-list, the version pin) are in settings.json in its data
directory (~/.omniralph/settings.json), which the agent applies as soon as it changes. Run
omniralph-agent --help to see all the options, and
docs/deploy-agent.md
for every key of both files.
Start
omniralph-agent start --healthz-port 9310To run it as a service, see the systemd, tmux and pm2 sections in docs/deploy-agent.md.
The agent logs to stderr and to <dataDir>/logs/agent.log (~/.omniralph/logs/agent.log by
default), rotated at 10 MB with five older files kept (agent.log.1 … agent.log.5). See
Logs.
While it runs, the agent serves its page on 127.0.0.1 (port 7410 by default, ui.port in
agent.json). omniralph-agent ui prints its link, with a token that changes at every start;
--open also opens it in the browser. From another computer, forward the port (ssh -L
7410:127.0.0.1:7410, coder port-forward <workspace> --tcp 7410:7410). See
The agent page.
Upgrade
Stop the agent, then:
npm install -g @omniralph/agent@latest
omniralph-agent --versionStart it again. Upgrading keeps the config and the repo cache. The version shows on the agent's detail on Fleet after its next heartbeat.
From @omniralph/harness
Up to 0.13 the agent was published as @omniralph/harness with the omniralph-harness binary and
the config file ~/.config/omniralph/harness.json. Self-update can't cross the rename, so install
@omniralph/agent once by hand, as the agent's user:
- Stop the old daemon (
pm2 delete omniralph-harness, or however you run it). npm install -g @omniralph/agent.omniralph-agent import: it reads the old install's config and writes~/.config/omniralph/agent.json(and itsacceptedTaskTypesandinstallables.allowinto~/.omniralph/settings.json), so the machine comes back as the same agent without enrolling again. It makes no network call, leaves the old install untouched and prints one line per setting.--dry-runprints the same summary without writing anything;--forcebacks up and replaces an existingagent.json. When pm2 runs the old daemon, it prints the commands that replace it.- Start
omniralph-agent startunder pm2, tmux or systemd as before. npm uninstall -g @omniralph/harnessonce the agent is back.
Self-update works again from then on. The full walkthrough, with what each import outcome means,
is in
docs/deploy-agent.md › Moving from @omniralph/harness.
Releasing
Releases are automated with changesets and
.github/workflows/publish-agent.yml. Nobody edits version or pushes agent-v* tags by hand.
Merging the "Version Packages" pull request is the only release action.
The @omniralph/agent line starts at 2.0.0, to match Omniralph 2.0. apps/agent/package.json
holds 1.0.0, which is never published, and a major changeset takes the first Version Packages
pull request to 2.0.0. The name's older 0.3.1–0.8.0 releases on npm are deprecated, so
scripts/agent-release.ts status refuses to publish anything below AGENT_MIN_VERSION (2.0.0,
in packages/shared).
Add a changeset to your pull request
Every pull request that changes this package must add a changeset: a small Markdown file in
.changeset/ that names the semver bump and holds the release note. To add one, run this from the
repository root, pick @omniralph/agent and the bump, write the note, and commit the file:
pnpm changesetThe agent bundles the workspace packages @omniralph/engine, @omniralph/ralph,
@omniralph/shared, @omniralph/tasks and @omniralph/ui (its workspace:* devDependencies):
packages/engine, packages/ralph, packages/shared, packages/tasks and packages/ui.
Changesets doesn't release those private packages, and it doesn't bump the agent when only they
change. A pull request that changes one of them must add a changeset for @omniralph/agent
itself. That includes a change made only under packages/ui: the agent's page is built from it.
CI (ci.yml, scripts/check-agent-changeset.ts) fails a pull request that changes
apps/agent/, or any workspace package the agent depends on, and adds no
@omniralph/agent changeset. The package list comes from the workspace graph, so a new
dependency is covered automatically. If the change needs no release note (tests, an internal
refactor), add an empty changeset instead. CI accepts it as a deliberate opt-out:
pnpm changeset --emptyPull requests that touch neither the agent nor its workspace packages need no changeset.
The Version Packages pull request
When changesets reach main, the workflow opens a pull request titled "Version Packages" (branch
changeset-release/main), and updates it on every later push to main. The pull request runs
pnpm changeset version, which:
- bumps
versioninapps/agent/package.jsonby the largest bump the changesets declare; - writes their notes to
apps/agent/CHANGELOG.md, linking each entry's pull request and author; - deletes the changeset files it consumed.
Empty changesets alone don't open the pull request. CI checks the pull request like any other, but it skips the changeset check there.
Release by merging it
Merging the Version Packages pull request releases the new version. On that push to main, the
workflow finds no pending changesets and a version that isn't on npm yet, and then:
- installs only this package and the workspace packages it bundles (the web app and db are left to CI, so no npm token is needed);
- typechecks, lints and tests them;
- reads the version's section of
CHANGELOG.mdwithscripts/agent-release.ts notes(it fails here if there is none); - runs
scripts/smoke-agent-package.sh; - publishes the tarball the smoke script tested, through npm trusted publishing (OIDC). No npm publish token is stored anywhere;
- pushes the tag
agent-v<version>on the released commit and creates a GitHub Release named after it, with that version's changelog section as its notes.
Any other push to main finds the version already on npm, logs that there is nothing to release,
and succeeds.
Re-run a failed publish
If a release fails before publishing (a check, the smoke test, npm), fix the cause on main if
needed. Then run the workflow again from Actions › Publish agent › Run workflow on main
(workflow_dispatch), or re-run the failed job. It publishes the version on main if that
version isn't on npm yet.
Once the version is on npm, the workflow treats it as released. If the tag or the GitHub Release step failed after publishing, create them by hand for that version:
git tag agent-v<version> <released commit> && git push origin agent-v<version>
gh release create agent-v<version> --verify-tag --title agent-v<version> --notes "<changelog section>"The CHANGESETS_GITHUB_TOKEN secret
GitHub runs no workflows for pull requests or pushes made with the workflow's default
GITHUB_TOKEN. So the Version Packages pull request is opened and updated with the repository
secret CHANGESETS_GITHUB_TOKEN, and ci.yml runs on it. The secret holds a fine-grained
personal access token, or a GitHub App installation token, scoped to mcm/omniralph, with:
- Contents: Read and write, to commit the version bump and push the
changeset-release/mainbranch. These commits must come from this token, so that CI also runs on later updates to the pull request; - Pull requests: Read and write, to open the pull request and update its title and body.
@changesets/changelog-githubalso uses it to look up each entry's pull request and author; - Metadata: Read-only, which GitHub adds to every fine-grained token.
A GitHub App needs the same Contents and Pull requests permissions on its installation.
If the secret is unset, the workflow falls back to the default GITHUB_TOKEN. Everything still
works, except that CI doesn't run on the Version Packages pull request. To get the checks, close
and reopen the pull request, or push a commit to its branch yourself. Tags and GitHub Releases
always use the default token.
Bundling
The build runs tsup, then vite build --config ui/vite.config.ts.
tsup inlines the workspace packages (@omniralph/engine, @omniralph/shared …) into
dist/cli.js. Third-party packages stay external. A third-party package that the bundle imports
must be listed in this package's dependencies, or the installed binary won't start. The smoke
script catches this.
Vite builds the agent's page (ui/) into dist/ui/: static files with hashed names, served by
the daemon. React, TanStack Query and @omniralph/ui are bundled into them, so they are
devDependencies and the published package gains no runtime dependency. index.html loads one
module script and has no inline script or style, so the page's Content Security Policy holds.
The smoke script checks that the tarball has dist/ui/index.html and every asset it loads, that
dist/ui stays under 3 MB and that no source map of it ships.
The page's code is in ui/src: main.tsx (providers), router.tsx (a small history router),
shell.tsx (the top bar, the section nav and the open page), pages/ and live/. live/ holds
the query keys, the change event map (invalidations.ts: each resource the daemon publishes
either appends to the cache, for logs and transcript, or invalidates queries; an unmapped
resource fails typecheck), the event stream with its polling fallback, and useLocalMutation
for changes (optimistic and applyResult, as apps/web's useApiMutation). typecheck runs
tsc -p ui too, and the page's tests (ui/src/**/*.test.tsx, under jsdom) run with the
package's Vitest run.
