{"_id":"@bunnyosai/worker","_rev":"5-6c4660aaad443a7c475e1633449b1bb9","name":"@bunnyosai/worker","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@bunnyosai/worker","version":"0.1.0","license":"MIT","_id":"@bunnyosai/worker@0.1.0","maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"bin":{"bunnyos-worker":"./dist/index.js"},"dist":{"shasum":"1833bc3e77b0b35d2f7128a0e632ec742dc88e4b","tarball":"https://registry.npmjs.org/@bunnyosai/worker/-/worker-0.1.0.tgz","fileCount":20,"integrity":"sha512-pKI7GbHQ8mxhJIe4EfuOYTmAKhIzgofrH7JNAqixh71zf5GDn1YC55rK/m4npRufnRybETGOHgfalUAqkF+jWQ==","signatures":[{"sig":"MEUCIDo8UDs1a7Ww8Dh8zJ/iyjpoSgkKM/NmvpwJgFdfJA/JAiEAnOCVDf76okT/Rk7zJZGbU+vBrFgRqG18k2/4hJ+HW8I=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":87033},"type":"module","engines":{"node":">=18.17"},"scripts":{"dev":"tsx src/index.ts","test":"node test/mock-broker.mjs && node test/resilience.mjs","build":"tsc -p tsconfig.json","start":"node dist/index.js","test:mock":"node test/mock-broker.mjs","typecheck":"tsc --noEmit","test:resilience":"node test/resilience.mjs"},"_npmUser":{"name":"joesanf","email":"joe@sanfoundation.com"},"description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","directories":{},"_nodeVersion":"24.2.0","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","@types/ws":"^8.5.13","typescript":"^5.7.2","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/worker_0.1.0_1783330861414_0.01060120024226996","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bunnyosai/worker","version":"0.1.1","license":"MIT","_id":"@bunnyosai/worker@0.1.1","maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"bin":{"bunnyos-worker":"./dist/index.js"},"dist":{"shasum":"b9700bad93e4dc85565e76fee280c605eadf8946","tarball":"https://registry.npmjs.org/@bunnyosai/worker/-/worker-0.1.1.tgz","fileCount":20,"integrity":"sha512-GsH7SWOoCAK+UBGaFczNOy4VmxqWrlebAJZQUD1voQumaAZBxU3TC09VcCvfJy7hNhROg/4Bu475tQ1BU2iXCA==","signatures":[{"sig":"MEUCIAaTsVaIky+Jl/gfbLfYwFo7EbMKcWNJ41SGb0B+d9HSAiEAh5hUtJvjNcH6sHGqnnhSZAtT7ME6NXa+0xCe/+PiQfc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":85855},"type":"module","engines":{"node":">=18.17"},"scripts":{"dev":"tsx src/index.ts","test":"node test/mock-broker.mjs && node test/resilience.mjs","build":"tsc -p tsconfig.json","start":"node dist/index.js","test:mock":"node test/mock-broker.mjs","typecheck":"tsc --noEmit","test:resilience":"node test/resilience.mjs"},"_npmUser":{"name":"joesanf","email":"joe@sanfoundation.com"},"description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","directories":{},"_nodeVersion":"24.2.0","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","@types/ws":"^8.5.13","typescript":"^5.7.2","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/worker_0.1.1_1783333083079_0.47979825791298536","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@bunnyosai/worker","version":"0.1.2","license":"MIT","_id":"@bunnyosai/worker@0.1.2","maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"bin":{"bunnyos-worker":"./dist/index.js"},"dist":{"shasum":"3fb46ad4fdc751960ed554b0768e108ad044bbbf","tarball":"https://registry.npmjs.org/@bunnyosai/worker/-/worker-0.1.2.tgz","fileCount":20,"integrity":"sha512-ry2V+Bjl5xj4VC2cXK4/d/DrZXeptuGJQBefw7dI0BT891JE4uPUqag+1WpFl6emQSgyCZAYGQ7fNm/TNEfQRQ==","signatures":[{"sig":"MEYCIQC9VgNbVz7+RT4gFc+uyMsg+o6X6MWgSsYJ2q+NHPaa5QIhAKAglLwr81yzCY7c7b3Mv88c22NRJSUX6a0eMVRYkMiE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":104011},"type":"module","engines":{"node":">=18.17"},"scripts":{"dev":"tsx src/index.ts","test":"node test/mock-broker.mjs && node test/resilience.mjs && node test/silent-eviction.mjs","build":"tsc -p tsconfig.json","start":"node dist/index.js","test:mock":"node test/mock-broker.mjs","typecheck":"tsc --noEmit","test:resilience":"node test/resilience.mjs","test:silent-eviction":"node test/silent-eviction.mjs"},"_npmUser":{"name":"joesanf","email":"joe@sanfoundation.com"},"description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","directories":{},"_nodeVersion":"24.2.0","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","@types/ws":"^8.5.13","typescript":"^5.7.2","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/worker_0.1.2_1783355775131_0.12465663740621635","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@bunnyosai/worker","version":"0.1.3","license":"MIT","_id":"@bunnyosai/worker@0.1.3","maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"bin":{"bunnyos-worker":"./dist/index.js"},"dist":{"shasum":"d45a72fd8a4417a4a731c3f1cef29723c83c0611","tarball":"https://registry.npmjs.org/@bunnyosai/worker/-/worker-0.1.3.tgz","fileCount":20,"integrity":"sha512-PQe52cygc91mLy5cqqBxB2iRpPzdHZxIrANKxV8gN9FnYestPVPSGwRH46HZ6FzOXmBKfiCrgcdX232PG90ThQ==","signatures":[{"sig":"MEUCIH0uZFcvKv1zTljdcuNl3wjpY+PMpck/RbV1JeRrVHOGAiEApBAtdeZao20A77OZ+kwZzo4GhTEywq5nkIFjtsdfvTg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":108334},"type":"module","engines":{"node":">=18.17"},"scripts":{"dev":"tsx src/index.ts","test":"node test/mock-broker.mjs && node test/resilience.mjs && node test/silent-eviction.mjs && node test/proxy-heartbeat.mjs","build":"tsc -p tsconfig.json","start":"node dist/index.js","test:mock":"node test/mock-broker.mjs","typecheck":"tsc --noEmit","test:resilience":"node test/resilience.mjs","test:proxy-heartbeat":"node test/proxy-heartbeat.mjs","test:silent-eviction":"node test/silent-eviction.mjs"},"_npmUser":{"name":"joesanf","email":"joe@sanfoundation.com"},"description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","directories":{},"_nodeVersion":"24.2.0","dependencies":{"ws":"^8.18.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4.19.2","@types/ws":"^8.5.13","typescript":"^5.7.2","@types/node":"^22.10.0"},"_npmOperationalInternal":{"tmp":"tmp/worker_0.1.3_1783450049507_0.8174706384044355","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@bunnyosai/worker","version":"0.2.0","description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","license":"MIT","type":"module","bin":{"bunnyos-worker":"./dist/index.js"},"publishConfig":{"access":"public"},"engines":{"node":">=18.17"},"dependencies":{"ws":"^8.18.0"},"devDependencies":{"@types/node":"^22.10.0","@types/ws":"^8.5.13","tsx":"^4.19.2","typescript":"^5.7.2"},"scripts":{"build":"tsc -p tsconfig.json","dev":"tsx src/index.ts","start":"node dist/index.js","typecheck":"tsc --noEmit","test":"node test/mock-broker.mjs && node test/resilience.mjs && node test/silent-eviction.mjs && node test/proxy-heartbeat.mjs","test:mock":"node test/mock-broker.mjs","test:resilience":"node test/resilience.mjs","test:silent-eviction":"node test/silent-eviction.mjs","test:proxy-heartbeat":"node test/proxy-heartbeat.mjs"},"_nodeVersion":"24.2.0","_id":"@bunnyosai/worker@0.2.0","dist":{"integrity":"sha512-m0nYCGs/Nahpi3dYmUmyopsEacCYXJrrF1OJSKoBi2n6Lf3wxwIetfOKQQlPfSY8mUbxGDSShVqCIqWtDyVUTQ==","shasum":"e6f17c48252582dd2646d04c89bc82de5b66a434","tarball":"https://registry.npmjs.org/@bunnyosai/worker/-/worker-0.2.0.tgz","fileCount":20,"unpackedSize":122770,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEvt3paNMeJYanN9rBuKQLnk085uxt2HgNBvJJ6ad/+hAiBoBkb1lWamrSM6B+/azLRsmSVVfh06pKmXU9SkGLlZhQ=="}]},"_npmUser":{"name":"joesanf","email":"joe@sanfoundation.com"},"directories":{},"maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/worker_0.2.0_1785231976029_0.03284888254468665"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-06T09:41:01.247Z","modified":"2026-07-28T09:46:16.412Z","0.1.0":"2026-07-06T09:41:01.543Z","0.1.1":"2026-07-06T10:18:03.268Z","0.1.2":"2026-07-06T16:36:15.300Z","0.1.3":"2026-07-07T18:47:29.637Z","0.2.0":"2026-07-28T09:46:16.181Z"},"license":"MIT","description":"Native inference worker for bunnyOS — runs Gemma 4 E4B via Ollama and serves streaming jobs for the carrot-broker.","maintainers":[{"name":"joesanf","email":"joe@sanfoundation.com"}],"readme":"# @bunnyosai/worker\n\nNative inference worker for **bunnyOS**. Runs LLMs locally via [Ollama](https://ollama.com) \nand serves streaming inference jobs for the bunnyOS network.\n\n## Requirements\n\n- **Node.js 18.17+** (22+ recommended)\n- **[Ollama](https://ollama.com/download)** installed — the worker starts it and\n  pulls the model automatically on first run\n- ~6 GB disk for the model (`gemma4:e4b-it-qat`)\n- A GPU / Apple Silicon\n\n## Get a key\n\nThe worker authenticates with a **key**. Get one from the bunnyOS dashboard:\n\n1. Go to **[platform.bunnyos.ai](https://platform.bunnyos.ai/)**\n2. Open **Settings → Workers**\n3. Create a worker and copy its **key** — it's shown once, so save it.\n\nThat key is all the worker needs to run.\n\n## Usage\n\n```bash\nnpx @bunnyosai/worker@latest --key <key>\n```\n\nOr via the environment / a saved creds file:\n\n```bash\nexport BUNNYOS_WORKER_KEY=…\nnpx @bunnyosai/worker@latest                           # picks the key up from the env\nnpx @bunnyosai/worker@latest --save-creds --key <key>  # persist to ~/.bunnyos/worker.json\n```\n\nThat's it — it starts Ollama if needed, pulls the model, then **loads and warms\nit up**: before connecting it runs a short generation to warm the GPU and measure\ndecode speed, retrying while the machine is still cold. This keeps the broker's\nadmission probe from being your first (cold, possibly-too-slow) generation. Then\nit connects and begins serving. Press **Ctrl-C** to stop.\n\n### Options\n\n| flag | env | default | meaning |\n|------|-----|---------|---------|\n| `--key` | `BUNNYOS_WORKER_KEY` | — | worker key (**required**) |\n| `--model` | `BUNNYOS_MODEL` | `gemma-4-e4b` | model to serve |\n| `--api` | `BUNNYOS_API` | `https://api.bunnyos.ai` | broker base URL |\n| `--ollama-url` | `OLLAMA_HOST` | `http://localhost:11434` | Ollama base URL |\n| `--no-pull` | — | off | don't auto-pull the model if missing |\n| `--save-creds` | — | off | save the key to `~/.bunnyos/worker.json` |\n| `--health-port` | `BUNNYOS_HEALTH_PORT` | off | serve `/healthz` + `/status` on this port |\n| `--health-host` | `BUNNYOS_HEALTH_HOST` | `127.0.0.1` | health bind host (`0.0.0.0` for k8s) |\n| `--worker-id` | `BUNNYOS_WORKER_ID` | — | optional; display only |\n| `--label` | — | — | informational label |\n| — | `BUNNYOS_MIN_TPS` | `8` | warmup decode floor (tok/s) required before connecting |\n\nSet `BUNNYOS_LOG_LEVEL=debug` for verbose logging.\n\n## Images\n\nWhen the model build reports the `vision` capability, the worker loads the\nmultimodal portion at startup and serves image content parts on `user`/`system`\nmessages. Images must be inline — a `data:image/...;base64,...` URL or a bare\nbase64 string. Remote `http(s)` image URLs are **dropped, never fetched**: the\nworker would be dereferencing consumer-supplied URLs from inside your network.\n\n## Health checks\n\nPass `--health-port <n>` to expose two HTTP endpoints — useful when running the\nworker under systemd, Docker, or Kubernetes. Bound to `127.0.0.1` by default;\nuse `--health-host 0.0.0.0` for k8s probes.\n\n- **`GET /healthz`** — *liveness*. Returns `200` as long as the process is\n  responsive; it stays `200` even during a reconnect. Point a **restart policy**\n  at this — a failure means the process is wedged.\n- **`GET /status`** — *readiness + observability*. Returns `200` when the worker\n  is `connected`, `admitted`, Ollama is reachable, and the broker is still\n  actively talking to us (`brokerFresh`); `503` otherwise. `brokerFresh` catches\n  a *silent eviction* — where the broker drops the worker from its pool without\n  closing the socket — so a dropped worker reports `503` instead of a false\n  `200`. Wire this to **monitoring/dashboards** — do *not* restart on `503`; the\n  worker detects the drop and reconnects on its own. Body:\n\n  ```json\n  {\n    \"state\": \"admitted\",\n    \"connected\": true,\n    \"admitted\": true,\n    \"brokerFresh\": true,\n    \"ollamaOk\": true,\n    \"model\": \"gemma-4-e4b\",\n    \"uptimeSec\": 1234,\n    \"jobsServed\": 42,\n    \"totalOutputTokens\": 9001,\n    \"lastJobAt\": \"2026-07-06T12:00:00.000Z\"\n  }\n  ```\n\n## Troubleshooting\n\n- **\"Ollama is not installed\"** — install it from <https://ollama.com/download>\n  and re-run.\n- **Evicted `too_slow`** — your machine is decoding below the network's speed\n  floor. The worker warms up and measures decode speed before connecting and\n  logs it (`warmup N/5: … tok/s, 100% GPU`); if it also warns the model is on\n  CPU or a partial GPU split, that's the cause. Close other GPU-heavy apps, or\n  use a machine with more VRAM / a faster GPU.\n- **Slow / stuck on a shared cloud GPU** — if warmup logs a low tok/s or a\n  `CPU`/partial placement, the model didn't fully fit on the GPU. Watch the\n  `ollama: …` lines at startup for `no compatible GPUs`/CUDA errors.\n- **Frequent reconnects** — usually a flaky network between you and the broker;\n  the worker reconnects automatically and keeps serving.\n","readmeFilename":""}