Updating & Upgrading
How you upgrade depends on how you installed in the first place. The three forms have completely different upgrade paths, and taking the wrong one doesn't fail loudly — it leaves two competing installations on the machine.
| What you installed | Who upgrades it | How |
|---|---|---|
| Standalone CLI (npm) | You | npm update -g @yoooclaw/cli |
| Standalone CLI (native binary) | You | Re-run install.sh, --force is required |
| Hermes plugin | The plugin itself (can be triggered from chat) | Ask it to self-update in conversation, or re-run the plugin installer |
| OpenClaw plugin | The host | openclaw plugins update @yoooclaw/phone-notifications |
Plugin users: don't upgrade the CLI separately
The Hermes plugin bundles the platform's yoooclaw executable, with the version pinned by the manifest.json inside the plugin package. Running the CLI's own install.sh puts a second CLI in ~/.local/bin at a different version from the one under the plugin-managed path — the plugin keeps using its own copy while your terminal uses the newly installed one, and the two may disagree about the same storage. Upgrading the plugin is upgrading the CLI; it neither needs nor should be done separately.
Standalone CLI
Step one: check your version and channel
yoooclaw update selfIt only checks, never installs — it reads the npm registry's dist-tags, compares versions, and tells you which command to run. Returned fields:
| Field | Meaning |
|---|---|
dist | Where this binary came from, npm or native. Determined by whether the executable path sits under node_modules |
current / latest | Local version / latest version on the channel |
channel | latest; pass --beta to query the beta dist-tag instead |
updateAvailable | Compares only the x.y.z triple, ignoring prerelease suffixes |
command | The upgrade command when one is available, already matched to dist; null when you're up to date |
yoooclaw update self --beta --format jsonThe npm channel
npm update -g @yoooclaw/cli # stable
npm i -g @yoooclaw/cli@beta # prereleaseThe npm package is a thin Node launcher; the actual Go binary is pulled per-platform through optionalDependencies, so upgrading the npm package replaces the platform binary along with it.
The native binary channel
# upgrade to the latest stable
curl -fsSL https://artifact.yoooclaw.com/cli/install.sh | sh -s -- --force
# upgrade to a specific version
curl -fsSL https://artifact.yoooclaw.com/cli/install.sh \
| sh -s -- --version 0.9.0 --force
# upgrade to the latest prerelease
curl -fsSL https://artifact.yoooclaw.com/cli/install.sh | sh -s -- --beta --forceUpgrades require --force
When a yoooclaw already exists at the target path the installer errors out (… 已存在;用 --force 覆盖,或先卸载旧版本 — "already exists; use --force to overwrite, or uninstall the old version first"). That guard exists to prevent accidental overwrites. A first install doesn't need --force; an upgrade always does.
Note that the command returned by yoooclaw update self currently omits --force, so copying it verbatim runs straight into this guard — just add --force yourself.
Two more upgrade-relevant installer behaviors:
- The install directory is re-derived, not remembered (prefer
~/.local/bin, else a writable/usr/local/bin). If you originally installed elsewhere with--dir, pass the same--diragain when upgrading, or you'll end up with a second copy in the default location. - PATH is left untouched by default: the installer only writes the install directory into your shell config when you pass
--modify-path, and that write is idempotent (if the line is already there, it just reports "PATH already configured"). Without--modify-pathit only installs the binary and leaves your shell config alone — true for both first installs and upgrades.--no-modify-pathis kept only for backward compatibility with older invocations and is equivalent to the default.
The native upgrade command from update self points at the GitHub-hosted installer script. If GitHub is slow for you, swap in artifact.yoooclaw.com (Alibaba Cloud OSS) as shown above — the two scripts take identical flags and differ only in where artifacts are downloaded from.
Don't mix the two channels
Install once via npm and once via install.sh and you have two yoooclaw binaries; whichever comes first on PATH wins, and update self only ever sees the one currently running. To genuinely switch channels, remove the old one first:
yoooclaw uninstall # stop the daemon + remove binaries and config, keep data
npm uninstall -g @yoooclaw/cli # required for the npm form, which node_modules ownsuninstall keeps notification / recording / image data by default, so your data survives a channel switch and reinstall.
Ownership handling during an upgrade
If both the CLI and the Hermes plugin are installed on the same machine, install.sh preserves whatever owned ownership before the upgrade by default — whoever held the writer lock / Relay connection before still does after, an upgrade never silently hands it off. The installer stops any running standalone CLI daemon first (if there is one), swaps the binary, then restarts it under its original profile, rolling back and restarting automatically on failure. Only passing --activate explicitly (or setting YOOOCLAW_ACTIVATE_OWNER=cli) hands ownership to the standalone CLI after install; you can also do the same handoff manually at any time with yoooclaw owner activate cli — see Command Reference · owner.
Restart the daemon after upgrading
Replacing the binary does not affect an already-running daemon — that's an old-version process, still happily receiving data. To make the new version take effect:
yoooclaw daemon restart
yoooclaw daemon status --format json # confirm `version` changedKnow the difference: rotating an api-key calls for daemon reload (incrementally refreshes tunnels without dropping live connections); replacing the binary or rotating the gateway token requires daemon restart — reload isn't enough.
Hermes plugin
The plugin package embeds the CLI binary, so "upgrading the plugin" swaps the plugin and the CLI in one step. Plugin 0.8.2 bundles CLI 0.8.2.
Option A: self-update from a conversation (recommended)
The plugin exposes two tools so you can upgrade while chatting with Hermes:
| Tool | What it does |
|---|---|
plugin_update_check | Reads the OSS latest channel marker and compares versions. Results are cached for 5 minutes; force=true bypasses the cache |
plugin_update_apply | Actually performs the upgrade. Requires confirm=true, otherwise it returns YOOOCLAW_UPDATE_NOT_CONFIRMED |
The confirm=true gate is deliberate: the model can't decide to upgrade on its own — it only passes that argument after you explicitly agree in the conversation. An optional version argument pins the target.
What plugin_update_check returns:
{ "ok": true, "currentVersion": "0.7.0", "latestVersion": "0.7.1",
"channel": "latest", "updateAvailable": true,
"updating": false, "editableInstall": false }The full flow once you confirm:
1. Write pending.json (from/to versions, originating chat, start time)
2. Download the rendered install.sh from OSS
3. Spawn the installer with setsid, detached — it must outlive the gateway restart it triggers
4. The tool returns "started" immediately, so that reply reaches you before the restart
5. A watcher thread in the old process streams update.log into the chat
6. The installer finishes → hermes gateway restart
7. The reloaded plugin calls resume_after_reload() and pushes the final ✅/❌Neither the setsid in step 3 nor the watcher in step 5 is redundant: if the installer fails during pip, the gateway never restarts, so there is no "new process" to report the outcome — only the old process's watcher can surface that failure. And the pending.json → result.json handoff is an atomic rename, guaranteeing the old process's watcher and the new process's resume path can never both notify.
If no push channel is available, the result waits for your next message and is delivered by the pre_llm_call hook rather than being silently dropped.
Option B: re-run the installer
Same command as the first install; running it again upgrades:
curl -fsSL https://artifact.yoooclaw.com/hermes-plugin/install.sh | bash -s -- --api-key "ock_..."Windows (PowerShell):
& ([scriptblock]::Create((irm https://artifact.yoooclaw.com/hermes-plugin/install.ps1))) --api-key ock_...Unlike the CLI's installer, the plugin installer needs no --force — it runs pip install --upgrade, where overwriting is the point. --api-key is also optional; omit it and the existing key in ~/.yoooclaw/credentials.json is reused.
Upgrade-relevant options:
| Option | Description |
|---|---|
--version <VERSION> | Install a specific version (resolved to the matching wheel URL on the OSS channel) |
--oss-channel <NAME> | Which version marker to read when --version is omitted; defaults to latest |
--oss-base-url <URL> | Point at a different OSS artifact source |
--no-oss | Skip OSS and fall back to the pip package (pip install yoooclaw-hermes-plugin) |
--no-restart | Don't run hermes gateway restart afterwards (you must restart yourself for it to take effect) |
--python <PATH> | Explicitly name the Python of the venv Hermes uses |
--hermes-profile <name> | If your Hermes runs a named profile (hermes --profile <name>), pass this so the installer patches that profile's config.yaml instead of the unused global config; the env var HERMES_PROFILE has the same effect |
After installing, the installer runs an activation verification pass (confirming the package is installed → the current Hermes profile's plugins.enabled is checked → the runtime actually holds the writer lock and Relay connection). If verification fails, the script now fails loudly with a non-zero exit code (previously it printed "installation complete" regardless of whether verification passed); the messaging distinguishes "installed but not activated" from "hard failure" — don't treat a non-zero exit as success, and see Troubleshooting below.
Four cases that get refused
| Code | Trigger | What to do |
|---|---|---|
YOOOCLAW_UPDATE_EDITABLE_INSTALL | The plugin is a pip install -e editable install or a source tree | Self-update would clobber the working tree you're editing, so it refuses outright; upgrade manually in the repo |
YOOOCLAW_UPDATE_IN_PROGRESS | An update is already running | Wait for it. If pending.json outlives the timeout (default 1800s, tunable via YOOOCLAW_HERMES_UPDATE_TIMEOUT_SECONDS) it's treated as stale and a new update is allowed through |
YOOOCLAW_UPDATE_NOT_NEWER | The target isn't newer than what's installed | Nothing to do. Self-update never downgrades; use the installer's --version to install an older release |
YOOOCLAW_UPDATE_BAD_VERSION | Malformed version number, or the channel marker returned something invalid | Check the --version spelling; if the channel misbehaves, check whether --oss-base-url points somewhere wrong |
Verify after upgrading
hermes yoooclaw lifecycle statusCheck three things: that components.pluginVersion is the new version, that daemon.storage.claimed is true (the new process reclaimed the storage writer lock), and that generation changed along with the plugin version. The plugin version is one of the generation's inputs, so an upgrade necessarily changes the generation and triggers one tunnel rebuild — that's expected.
Don't run yoooclaw update self in plugin mode
The CLI under the plugin-managed path isn't inside node_modules, so update self classifies it as native and suggests running the CLI's install.sh — doing so installs a second CLI that floats free of the plugin. The embedded CLI's version is governed by the plugin package's manifest.json and can only be changed by upgrading the plugin.
Troubleshooting
The installer says "already exists; use --force"
Normal behavior from the CLI's native installer on an upgrade — just add --force. See the native binary channel above.
Upgraded, but nothing changed
Check in order:
- The daemon is still the old process — by far the most common. Run
yoooclaw daemon restart, then checkversionindaemon status. - Two CLIs on PATH — run
which -a yoooclawto see whether you're hitting a copy installed from the other channel. - The Hermes gateway didn't restart — you passed
--no-restart, or the restart failed. Runhermes gateway restart. - Skills didn't update — if
~/.hermes/skills/yoooclaw/lacks the.yoooclaw-managedmarker (i.e. it was modified by hand), the plugin logs a warning and skips overwriting rather than clobbering your own content.
Self-update appears stuck
All state lives under ~/.yoooclaw/hermes-plugin/update/:
| File | Contents |
|---|---|
pending.json | An update in flight (from/to versions, originating chat, start time) |
update.log | The installer's stdout + stderr |
exit_code | The installer's exit code, written by the detached shell wrapper |
result.json | A terminal record awaiting notification to the user |
The tail of update.log plus exit_code tells you exactly where it stopped. A timeout is reported as exit code 124.
Next steps
- Standalone CLI: Overview & Installation — full install instructions for both distribution channels.
- Hermes Plugin: Overview & Installation — what the installer actually does.
- Standalone CLI: Debugging — the three-step check when things won't connect after an upgrade.