diff --git a/plugins/herdr-layerfs/README.md b/plugins/herdr-layerfs/README.md new file mode 100644 index 000000000..e67db7815 --- /dev/null +++ b/plugins/herdr-layerfs/README.md @@ -0,0 +1,34 @@ +Parallel agent workspaces in [Herdr](https://herdr.dev) without copying your repo. Each workspace the plugin opens is a LayerFS Branch: forking one copies no file bytes, closing it saves it, and after Herdr or the machine restarts the plugin mounts the branches again and reopens their workspaces. + +## What it adds + +Herdr actions, also runnable from a shell or by an agent as `node /lfs.mjs `: + +- `init` (**LayerFS: import this repo**): imports the focused repo's `HEAD` as a LayerFS project. +- `fork` (**LayerFS: new branch workspace**, or **new named branch**): forks the project, or the focused branch after saving it, and opens it as a workspace `lfs:` whose first pane is inside the branch. +- `commit`: saves the workspace on its branch. +- `drop`: discards the workspace without saving. +- `reopen`: mounts a saved branch again. +- `list` (**LayerFS: status**): projects and branches. + +Two hooks: closing a LayerFS workspace saves and unmounts it; on Herdr startup, branches whose mounts are gone are mounted and reopened. + +## How it works + +The plugin calls `@layerfs/agent` from short Node processes that Herdr starts for each action and hook, and keeps a small state file mapping Herdr workspaces to branches. In docker mode (LayerFS `main`, Linux) the branch lives in the LayerFS Sandbox container and the workspace's pane enters it with `docker exec`. + +On Debian 13 (4 vCPU, Docker 29, Herdr 0.9.5) with a 603 MB repo: import 3.2 s, a new branch workspace 0.07–0.19 s, save 0.06 s, discard 0.4 s. A server restart and a reboot-style restart (containers stopped) both brought back all 3 workspaces with their saved files. + +`herdr plugin action invoke` acts on the workspace focused in Herdr's UI, so agents should run `lfs.mjs` directly: it then uses their own pane's workspace. + +## You need + +- Linux with Docker, Node 22+, Herdr 0.9+, and Rust 1.85.1 to build LayerFS. +- A LayerFS checkout at the pinned commit with the daemon and native binding built, a runtime image with git and a shell, and a Docker volume (the install prompt covers all three). + +## Limits + +- Docker mode reads the Store inside the Docker volume, so Herdr runs as a user that can read `/var/lib/docker/volumes` (root on a default install). +- Edits a branch has not saved are lost if the machine reboots. +- Uncommitted changes in the source repo are not imported; `HEAD` is. +- The repository also has a native mode for macOS (each branch a host folder and a linked git worktree, shown under the repo in Herdr's sidebar). It needs LayerFS PR #427, which is not on `main`, so it is not part of this listing. diff --git a/plugins/herdr-layerfs/install.md b/plugins/herdr-layerfs/install.md new file mode 100644 index 000000000..eb6640cb5 --- /dev/null +++ b/plugins/herdr-layerfs/install.md @@ -0,0 +1,11 @@ +Install LayerFS for Herdr {{version}} for me, in docker mode, on this Linux machine. + +1. Check the tools: Linux, `node --version` (22 or newer), `docker info`, `herdr --version` (0.9 or newer), `git`, `jq`, and `cargo +1.85.1 --version` (if Rust 1.85.1 is missing, ask me before installing it with rustup). If anything is missing or this is not Linux, stop and tell me. +2. Clone https://github.com/Ephemeral-AI-Lab/layerfs and check out commit {{layerfs.commit}} ({{layerfs.version}} or later on main). Use exactly this commit: it is the one this version was tested with. +3. In that checkout build the daemon and the native binding: `cargo +1.85.1 build --manifest-path core/Cargo.toml --release --locked -p layerfs-daemon`, then `npm --prefix api install` and `npm --prefix api run build:native`. Expect `core/target/release/layerfs-daemon` and `api/binding/layerfs.node`. +4. Build the runtime image and the volume: `printf 'FROM node:22-trixie\nRUN apt-get update && apt-get install -y --no-install-recommends git procps\n' | docker build -t layerfs-rt:herdr -`, then `docker volume create layerfs-herdr`. +5. Install the plugin at the pinned commit: `herdr plugin install jiangkoumo/herdr-plugin-layerfs --ref {{commit}} --yes`. +6. Write the config: copy the plugin's `config.env.example` to `"$(herdr plugin config-dir layerfs)/config.env"`, keep the docker block, and set `LAYERFS_API` and `LAYERFS_DAEMON` to the checkout from step 2, `LAYERFS_IMAGE` to the output of `docker image inspect layerfs-rt:herdr | jq -r '.[0].Id'`, `LAYERFS_VOLUME=layerfs-herdr`, and `LAYERFS_STORE_PATH=/var/lib/docker/volumes/layerfs-herdr/_data/global/store.sqlite`. Herdr must run as a user that can read that path (root on a default Docker install); if it does not, stop and tell me. +7. Check it on a scratch repo: `git clone --depth 20 {{repository}} /tmp/lfs-check`, open it as a Herdr workspace (`herdr workspace create --cwd /tmp/lfs-check --label lfs-check`), then from a shell in that workspace run `LFS=$(herdr plugin list --plugin layerfs --json | jq -r '.result.plugins[0].plugin_root')`, `node "$LFS"/lfs.mjs init`, `node "$LFS"/lfs.mjs fork check` and `node "$LFS"/lfs.mjs list`. Expected: `imported`, `check ready`, and one open branch `check`. Then discard it from the new workspace with `node "$LFS"/lfs.mjs drop` and delete `/tmp/lfs-check`. + +Report what you ran and what each step returned. If anything fails, stop and give me the exact error, and don't retry it on your own. diff --git a/plugins/herdr-layerfs/logo.svg b/plugins/herdr-layerfs/logo.svg new file mode 100644 index 000000000..570004b75 --- /dev/null +++ b/plugins/herdr-layerfs/logo.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/plugins/herdr-layerfs/plugin.json b/plugins/herdr-layerfs/plugin.json new file mode 100644 index 000000000..7afc16778 --- /dev/null +++ b/plugins/herdr-layerfs/plugin.json @@ -0,0 +1,51 @@ +{ + "schema": 1, + "id": "herdr-layerfs", + "name": "LayerFS for Herdr", + "summary": "Each Herdr workspace is a LayerFS Branch: a new one in ~0.2 s, restored after restart.", + "authors": [{ "name": "jiangkoumo", "github": "jiangkoumo" }], + "license": "MIT", + "repository": "https://github.com/jiangkoumo/herdr-plugin-layerfs", + "tags": ["herdr", "agents", "workspaces", "multi-agent", "terminal"], + "platforms": ["linux"], + "uses": ["agent"], + "host": { "name": "Herdr", "url": "https://herdr.dev" }, + "status": "preview", + "versions": [ + { + "version": "0.3.3", + "commit": "d2bf1a854bded0a696381d04721a0c79d5af8cb0", + "date": "2026-10-26", + "layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" }, + "notes": "A failed fork or reopen now stops the Sandbox daemon that attempt created, through SDK sandbox.close, once its mounts are cleaned up; reused or shared daemons keep running." + }, + { + "version": "0.3.2", + "commit": "f2e7ba6b99fd14956de2a312ae7cef1ec4e908eb", + "date": "2026-10-26", + "layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" }, + "notes": "A failed fork is kept as a closed Branch with its error (reopen or drop to recover); reopen can adopt an existing Store Branch without a plugin record; fork from an agent pane outside the repo opens the Branch under the source repo." + }, + { + "version": "0.3.1", + "commit": "34dd08cc29dc587ab041e087511d0ce6520efa0c", + "date": "2026-10-26", + "layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" }, + "notes": "drop [name] now cleans up closed Branches directly (no remount); optional --delete-git-branch deletes the Git branch only when merged. Store data is kept, so drop does not reclaim space." + }, + { + "version": "0.3.0", + "commit": "2bbd70880fcee75d832ebe9a093d231782b93475", + "date": "2026-10-26", + "layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" }, + "notes": "Adds Branch merge (reports real conflicts, ignores each Branch's root .git link), keeps state until drop cleanup succeeds, and resolves the current Branch from cwd outside Herdr." + }, + { + "version": "0.2.1", + "commit": "dba17fb30d46acb7daba5ba61e4e0048a01e3d43", + "date": "2026-10-25", + "layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" }, + "notes": "First listing: docker mode on LayerFS main verified end to end on Debian 13; agents fork from their own pane." + } + ] +}