原始内容
pi-packed
Package service for the Pi agent — DNF-style package management that both you and the agent can use.
The agent gets native tools (pkg_search, pkg_info, pkg_install, pkg_update, and pkg_remove); you get
the packed CLI and an interactive /packages TUI. The extension is a thin,
Node-compatible client. Registry access, SQLite, and package execution remain
inside the supervised Bun daemon.
┌─ Pi extension (Node-compatible) ──────────────┐
│ pkg_search · pkg_info · pkg_install/update/remove│
│ /packages · /packed permission settings │
│ operation-aware approval · no Bun/SQLite access │
└──────────────────┬────────────────────────────┘
│ authenticated loopback HTTP
┌─ packed.service (Bun) ────────────────────────┐
│ typed package client API · watcher · mirror │
│ npm registry · SQLite WAL · pi install/remove │
└───────────────────────────────────────────────┘
Quickstart
bun test
packed service > ~/.config/systemd/user/packed.service
systemctl --user daemon-reload
systemctl --user enable --now packed.service
packed search lsp
packed install npm:pi-lsp
packed installed --json
# In Pi after installing the package:
/packages
Packages execute arbitrary code and mutate Pi settings/install roots. One daemon-owned operation policy classifies every public package operation. Install, update, remove, and security-setting changes require explicit approval by default (mutationApproval: always); search, info, installed, catalog, and update-status reads are bounded reads, while mirror refresh is classified maintenance. Open /packed to retain the recommended approval policy or deliberately choose the unsafe Never require mutation approval opt-out. /packages uses the same policy for updates and removals.
CLI
| Command | What |
|---|---|
packed search <q> [--offline] [--limit N] [--json] |
Search npm or the local mirror, scoped to keywords:pi-package |
packed info <name> [--json] |
Show version, repository, Pi manifest, size, and license |
packed updates [--json] |
Show drift from the local mirror |
packed update <source> [--approve] [--json] |
Update one configured source through pi update --extension |
packed mirror [--json] |
Refresh the SQLite package index |
packed installed [--json] |
Read Pi's installed package declarations |
packed catalog [--json] |
Inspect the local package index |
packed install <source> [--approve] [--json] |
Authenticated daemon install for npm:, git:, or https:// sources |
packed remove <name> [--approve] [--json] |
Authenticated daemon removal by bare npm name |
packed security [always|never] [--approve] [--json] |
Read or set the package mutation approval policy |
packed serve |
Run the loopback daemon |
packed service |
Print the systemd user unit |
packed version |
Print the package/service version |
Guarded CLI mutations require --approve under the secure default. This is pi-packed mutation authorization, distinct from Pi's project-trust --approve semantics. Install/remove JSON results are stable objects:
{"ok":true,"source":"npm:pi-lsp","output":"Installed npm:pi-lsp"}
Failures use exit code 1 and { "ok": false, ... , "error": "..." } with
credential-safe diagnostics. Usage errors use exit code 2.
Native tool presentation
Native tools keep three output contracts independent:
- model-facing
contentis concise, credential-safe, and capped at 2,000 characters; - renderer-facing
detailsuses a versioned bounded package DTO and never retains raw npm metadata or manifest values; - CLI human output and
--jsonremain presenters over daemon DTOs and do not parse either native-tool channel.
Calls and results have themed collapsed and expanded renderers. Missing or legacy details fall back to model content, while daemon and execution failures are thrown through Pi's native error channel. Package approval refusal remains a normal cancelled or denied outcome.
Service API
Every route requires the bearer token stored in the private state directory. The daemon listens on loopback only.
| Method | Route |
|---|---|
GET |
/health |
GET |
/search?q=&limit=&offline=1 |
GET |
/info?name= |
GET |
/installed |
GET |
/security |
POST |
/security with `{ "mutationApproval": "always" |
GET |
/updates |
GET |
/catalog |
POST |
/install with { "source": "...", "approved": true } |
POST |
/update with { "source": "...", "approved": true } |
POST |
/remove with { "name": "...", "approved": true } |
State defaults to ~/.cache/pi-packed/ and contains token, port,
updates.json, security.json, and packed.db. Relevant environment variables:
PI_PACKED_HOMEPI_PACKED_PI_HOMEPI_PACKED_WATCH_SECSPI_PACKED_CATALOG_SECSPI_PACKED_IDLE_SECSPI_PACKED_PI_BIN/PI_BIN
Architecture and safety
- Daemon-owned SQLite: extensions never open the mirror directly.
- Runtime boundary: extensions never call
Bun.spawn; only the supervised Bun daemon owns theExecInstalleradapter. - Authenticated typed client: extension and mutation CLI paths call the same loopback API and reconnect after daemon restarts.
- Ports and adapters: registry and installer ports keep policy independent from npm, SQLite, subprocess, HTTP, and UI adapters.
- Operation-aware authorization: one policy matrix classifies reads, maintenance, code execution, settings mutation, and security mutation; guarded daemon routes reject missing approval with stable
approval_requirederrors. - Allowlisted mutation input: package sources and names reject shell metacharacters before reaching the installer.
- Bounded requests: daemon calls use timeouts and return structured errors without tokens or credentials.
Development
bun test
bunx tsc --noEmit