anon-pi 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile.pi +35 -7
- package/README.md +65 -23
- package/dist/anon-pi.d.ts +157 -44
- package/dist/anon-pi.d.ts.map +1 -1
- package/dist/anon-pi.js +311 -104
- package/dist/anon-pi.js.map +1 -1
- package/dist/cli.js +92 -18
- package/dist/cli.js.map +1 -1
- package/examples/Dockerfile.pi-webveil +100 -0
- package/package.json +3 -2
- package/src/anon-pi.ts +391 -135
- package/src/cli.ts +129 -19
package/Dockerfile.pi
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# A minimal image with `pi` (the Earendil Works coding agent) on PATH, for use as
|
|
2
|
-
# anon-pi's ANON_PI_IMAGE.
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
# package, plus the handful of tools pi's built-in bash/grep/find expect.
|
|
2
|
+
# anon-pi's ANON_PI_IMAGE. node:24-bookworm-slim + the official
|
|
3
|
+
# `@earendil-works/pi-coding-agent` npm package + the tools pi's built-in
|
|
4
|
+
# bash/grep/find expect.
|
|
6
5
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
6
|
+
# HOW anon-pi USES THIS IMAGE (stateful model):
|
|
7
|
+
# anon-pi mounts a PERSISTENT per-workdir host dir at the container's
|
|
8
|
+
# ~/.pi/agent, so pi's sessions/history/settings and any extensions you
|
|
9
|
+
# `pi install` persist across launches. On a FRESH home, anon-pi seeds it once
|
|
10
|
+
# from this image's STAGING dir (/opt/anon-pi-seed/agent) + your imported
|
|
11
|
+
# models.json, then stamps a marker and never overwrites again.
|
|
12
|
+
#
|
|
13
|
+
# So image defaults (extensions, trust.json) go in the STAGING dir, NOT in
|
|
14
|
+
# ~/.pi/agent (which is the mount and would be shadowed). Set
|
|
15
|
+
# PI_CODING_AGENT_DIR to the staging dir while running `pi install` so the
|
|
16
|
+
# extension is recorded there.
|
|
10
17
|
#
|
|
11
18
|
# Build (pin a digest for reproducibility once pushed to a registry):
|
|
12
19
|
# podman build -t localhost/anon-pi-pi:latest -f Dockerfile.pi .
|
|
@@ -20,4 +27,25 @@ RUN apt-get update \
|
|
|
20
27
|
# The official pi coding agent CLI (puts `pi` on PATH).
|
|
21
28
|
RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
|
22
29
|
|
|
30
|
+
# The staging dir anon-pi promotes into a fresh home on first launch.
|
|
31
|
+
ENV ANON_PI_STAGE=/opt/anon-pi-seed/agent
|
|
32
|
+
RUN mkdir -p "$ANON_PI_STAGE"
|
|
33
|
+
|
|
34
|
+
# Trust /work so pi does not prompt on the mounted project (staged, so it lands
|
|
35
|
+
# in the persistent home on first launch; anon-pi never writes this for you).
|
|
36
|
+
RUN printf '{"/work": true}\n' > "$ANON_PI_STAGE/trust.json"
|
|
37
|
+
|
|
38
|
+
# --- Optional: extensions + skills (the capability layer) -------------------
|
|
39
|
+
# Install into the STAGING dir (PI_CODING_AGENT_DIR), so `pi install` records the
|
|
40
|
+
# extension there and anon-pi promotes it into the persistent home on first
|
|
41
|
+
# launch. After that you can `pi install` more from inside the session and it
|
|
42
|
+
# persists. Uncomment what you want in the anon persona:
|
|
43
|
+
#
|
|
44
|
+
# RUN PI_CODING_AGENT_DIR="$ANON_PI_STAGE" pi install npm:pi-subagents
|
|
45
|
+
# RUN PI_CODING_AGENT_DIR="$ANON_PI_STAGE" pi install npm:pi-plan-mode
|
|
46
|
+
#
|
|
47
|
+
# An extension that needs a running SERVICE (e.g. pi-webveil + a local SearXNG)
|
|
48
|
+
# installs and configures that service in the image too. See the worked example
|
|
49
|
+
# examples/Dockerfile.pi-webveil.
|
|
50
|
+
|
|
23
51
|
WORKDIR /work
|
package/README.md
CHANGED
|
@@ -33,7 +33,8 @@ export ANON_PI_IMAGE=your/pi-image:tag
|
|
|
33
33
|
export ANON_PI_LLM=192.168.1.150:8080 # your local model, reached directly
|
|
34
34
|
export ANON_PI_PROXY=socks5h://127.0.0.1:9050
|
|
35
35
|
|
|
36
|
-
anon-pi
|
|
36
|
+
anon-pi import # one-time: generate the seed models.json from your model
|
|
37
|
+
anon-pi ./recon # launch
|
|
37
38
|
```
|
|
38
39
|
|
|
39
40
|
You land in pi, inside the jail, cwd `/work` = `./recon`. pi's web/tool egress is anonymized through the proxy; the local model at `ANON_PI_LLM` is reachable directly; everything else is dropped if the proxy is down (fail-closed).
|
|
@@ -42,24 +43,39 @@ You land in pi, inside the jail, cwd `/work` = `./recon`. pi's web/tool egress i
|
|
|
42
43
|
|
|
43
44
|
| Var | Required | Default | Meaning |
|
|
44
45
|
| --- | --- | --- | --- |
|
|
45
|
-
| `ANON_PI_IMAGE` |
|
|
46
|
+
| `ANON_PI_IMAGE` | for run | | container image with `pi` on `PATH` |
|
|
46
47
|
| `ANON_PI_LLM` | yes | | RFC1918/link-local `IP[:port]` of the local model (the one direct hole) |
|
|
47
|
-
| `ANON_PI_PROXY` |
|
|
48
|
+
| `ANON_PI_PROXY` | yes | | the socks5h proxy (Tor/wireproxy/ssh -D). No default: it is what anonymizes |
|
|
49
|
+
| `ANON_PI_EPHEMERAL` | no | (off) | set to `1` for a throwaway, non-persistent session |
|
|
48
50
|
| `ANON_PI_HOME` | no | `$XDG_CONFIG_HOME/anon-pi` or `~/.config/anon-pi` | anon-pi home |
|
|
49
|
-
| `ANON_PI_CONFIG` | no | `<ANON_PI_HOME>/agent` |
|
|
50
|
-
| `
|
|
51
|
+
| `ANON_PI_CONFIG` | no | `<ANON_PI_HOME>/agent` | canonical seed dir (holds the imported `models.json`) |
|
|
52
|
+
| `ANON_PI_SOURCE_MODELS` | no | `~/.pi/agent/models.json` | (`import`) the host `models.json` to read from |
|
|
51
53
|
|
|
52
54
|
## How it works
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
2. **Mount.** The session config dir is mounted as pi's global config (`PI_CODING_AGENT_DIR=<mount>`, default `/opt/pi-agent`), and the workdir is mounted at `/work`.
|
|
56
|
-
3. **Run.** anon-pi execs `netcage run --proxy <proxy> --allow-direct <ANON_PI_LLM> -it -v <workdir> -v <session>:<mount> -e PI_CODING_AGENT_DIR=<mount> <image> pi`.
|
|
56
|
+
**Stateful by default.** anon-pi mounts a persistent per-workdir host dir at the container's `~/.pi/agent`, so pi's **sessions, history, settings (your model choice), and any extensions you `pi install`** all persist across launches. Re-running in the same folder resumes it. The state dir is `<ANON_PI_HOME>/state/<workdir>/agent`, named with pi's own readable path convention (e.g. `--home-me-proj--`), not a hash, so you can see which folder it belongs to and delete it to reset.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
1. **Mount the persistent home.** `-v <ANON_PI_HOME>/state/<workdir>/agent:/root/.pi/agent`. Everything pi writes there survives.
|
|
59
|
+
2. **First-launch seed (only when the home is fresh).** anon-pi mounts the image's staged defaults and your imported `models.json`, and the container promotes them into the fresh home, then stamps a `.anon-pi-seed` marker. On later launches the marker is present, so nothing is re-copied and **your changes (added models, installed extensions) are never clobbered**.
|
|
60
|
+
3. **Run.** `netcage run --proxy <proxy> --allow-direct <ANON_PI_LLM> -it -v <workdir> -v <state>:/root/.pi/agent [-v <models.json>:...:ro] <image> sh -c '<seed-if-fresh> && exec pi'`.
|
|
59
61
|
|
|
60
|
-
|
|
62
|
+
pi auto-selects the first available model (your local one; it needs no real API key), so no default needs to be set. Services and default extensions live in the **image**; your state lives in the persistent home.
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
### Ephemeral (throwaway) sessions
|
|
65
|
+
|
|
66
|
+
For a clean, no-local-trace session, pass `--ephemeral` (or `ANON_PI_EPHEMERAL=1`): anon-pi mounts **no writable state** at all. pi writes to the container's own filesystem, which netcage runs with `--rm`, so it is destroyed when the container exits. Nothing writable ever touches a host path (only the read-only `models.json` seed is mounted), there is no cleanup step, and nothing is left behind even if anon-pi is killed.
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
anon-pi --ephemeral ./scratch
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Reset a session
|
|
73
|
+
|
|
74
|
+
Delete its state home; the next launch re-seeds:
|
|
75
|
+
|
|
76
|
+
```sh
|
|
77
|
+
rm -rf ~/.config/anon-pi/state/<workdir-slug>/agent
|
|
78
|
+
```
|
|
63
79
|
|
|
64
80
|
## Providing a pi image
|
|
65
81
|
|
|
@@ -73,33 +89,59 @@ podman build -t localhost/anon-pi-pi:latest -f Dockerfile.pi .
|
|
|
73
89
|
export ANON_PI_IMAGE=localhost/anon-pi-pi:latest
|
|
74
90
|
```
|
|
75
91
|
|
|
76
|
-
The image only needs `pi` reachable on `PATH`. anon-pi passes `pi` as the run command and mounts pi's config
|
|
92
|
+
The image only needs `pi` reachable on `PATH`. anon-pi passes `pi` as the run command (via a small copy-then-exec step) and never mounts over pi's config dir, so the image needs **no `ENTRYPOINT` and no config volume** (unlike pi's upstream `Dockerfile.pi`, which is written for running pi directly).
|
|
77
93
|
|
|
78
94
|
A community image also exists ([`gni/pi-coding-agent-container`](https://github.com/gni/pi-coding-agent-container)); it is third-party and unvetted, so review it yourself before trusting it with your (anonymized) credentials.
|
|
79
95
|
|
|
80
|
-
|
|
96
|
+
### Extensions, skills, and their services go in the image
|
|
81
97
|
|
|
82
|
-
anon-pi **
|
|
98
|
+
anon-pi deliberately imports **only your local model** (see below), never your extensions or skills. That is on purpose: your extension set is an identity fingerprint, extensions run code and can leak, and many need a runtime that a copied folder cannot carry (for example `pi-webveil` needs a running searxng). The right home for capabilities is the **image**, where they are installed once, reviewably, with clean config:
|
|
83
99
|
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
100
|
+
```dockerfile
|
|
101
|
+
FROM node:24-bookworm-slim
|
|
102
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
103
|
+
bash ca-certificates git ripgrep && rm -rf /var/lib/apt/lists/*
|
|
104
|
+
RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent
|
|
105
|
+
|
|
106
|
+
# Extensions are installed with `pi install` (which records them in settings),
|
|
107
|
+
# NOT a global npm install:
|
|
108
|
+
# RUN pi install npm:pi-webveil
|
|
109
|
+
# ...and an extension that needs a service (pi-webveil -> searxng) also installs
|
|
110
|
+
# and configures that service in the image. Its egress is forced through the
|
|
111
|
+
# socks proxy by netcage at runtime, so it must be happy with proxy-only,
|
|
112
|
+
# DNS-through-proxy networking.
|
|
113
|
+
|
|
114
|
+
WORKDIR /work
|
|
88
115
|
```
|
|
89
116
|
|
|
90
|
-
|
|
117
|
+
Install image defaults into the **staging dir** (`PI_CODING_AGENT_DIR=/opt/anon-pi-seed/agent pi install ...`), NOT `~/.pi/agent`: anon-pi mounts a persistent home over `~/.pi/agent`, and promotes the staging dir into it on a fresh launch. Anything you then `pi install` *inside* a session also persists (it is written to the mounted home). See the `Dockerfile.pi` comments for the exact `pi install` form.
|
|
91
118
|
|
|
92
|
-
|
|
119
|
+
A worked example ships in this package: [`examples/Dockerfile.pi-webveil`](examples/Dockerfile.pi-webveil) builds pi + the `pi-webveil` extension (staged) + a local SearXNG (over a Unix socket, `http-socket` so webveil's `unix:` baseUrl can speak HTTP to it, JSON API on, limiter off), started by an entrypoint that then execs anon-pi's seed-then-pi command. Note the anonymity subtlety it documents: SearXNG's own crawl is anonymized here **because netcage forces every process's egress through the proxy**, so webveil's plain `egress: direct` is correct in-jail (the usual "local SearXNG leaks your IP" caveat does not apply).
|
|
93
120
|
|
|
94
|
-
|
|
121
|
+
## Generating the seed (`anon-pi import`)
|
|
122
|
+
|
|
123
|
+
anon-pi **never** copies your real pi config. Instead, `anon-pi import` synthesizes a minimal `models.json` from your local model:
|
|
95
124
|
|
|
96
125
|
```sh
|
|
97
|
-
|
|
126
|
+
export ANON_PI_LLM=192.168.1.150:8080
|
|
127
|
+
anon-pi import
|
|
98
128
|
```
|
|
99
129
|
|
|
130
|
+
It reads your host `~/.pi/agent/models.json` (override with `ANON_PI_SOURCE_MODELS`), finds the provider whose `baseUrl` serves `ANON_PI_LLM` (matched on host:port, so `192.168.1.150:8080` matches `http://192.168.1.150:8080/v1`), and writes **just that provider** to `<ANON_PI_CONFIG>/models.json`. Everything else, your paid providers and their API keys, your sessions, your trust list, is left behind on the host.
|
|
131
|
+
|
|
132
|
+
That file **seeds a fresh session home** (it is copied in on first launch). Models you later add *inside* pi persist in the session home and are never clobbered by import. To change what a fresh home seeds, re-run `anon-pi import --force`; to apply it to an existing session, reset that session (delete its state home).
|
|
133
|
+
|
|
134
|
+
- If no provider matches `ANON_PI_LLM`, it errors and lists the providers it did find.
|
|
135
|
+
- If the matched provider carries a real-looking `apiKey` (not `none`/`ollama`/empty), it warns but proceeds (for a local model this is usually fine).
|
|
136
|
+
- It refuses to overwrite the canonical seed unless you pass `--force`.
|
|
137
|
+
|
|
138
|
+
## Trusting `/work`
|
|
139
|
+
|
|
140
|
+
pi treats a mounted project as untrusted until approved. The shipped Dockerfiles stage a `trust.json` trusting `/work` (in `/opt/anon-pi-seed/agent`), which is promoted into the session home on first launch, so you are not prompted. You can also approve once inside a session; it persists.
|
|
141
|
+
|
|
100
142
|
## Overriding the config per workdir
|
|
101
143
|
|
|
102
|
-
|
|
144
|
+
pi also supports a **project-local** config at `<cwd>/.pi/`, which layers on top of the image's global config. Since your workdir is pi's cwd (`/work`), you can drop a `/work/.pi/` (i.e. `<workdir>/.pi/`) into the folder to override settings for that folder only. anon-pi does nothing special for this; it is pi's normal project-over-global layering.
|
|
103
145
|
|
|
104
146
|
## Platform
|
|
105
147
|
|
package/dist/anon-pi.d.ts
CHANGED
|
@@ -1,20 +1,48 @@
|
|
|
1
1
|
/** The container path the workdir is mounted at (pi's cwd). */
|
|
2
2
|
export declare const CONTAINER_WORKDIR = "/work";
|
|
3
3
|
/**
|
|
4
|
-
* The
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* The container path pi uses as its config+state home. anon-pi mounts a
|
|
5
|
+
* PERSISTENT host dir here (Model B), so everything pi writes, sessions,
|
|
6
|
+
* history, settings (your model choice), `pi install`ed extensions, downloaded
|
|
7
|
+
* bin/fd, survives across launches. Statefulness is the default; --ephemeral
|
|
8
|
+
* mounts a throwaway dir here instead.
|
|
9
9
|
*/
|
|
10
|
-
export declare const
|
|
11
|
-
/**
|
|
12
|
-
|
|
10
|
+
export declare const CONTAINER_AGENT_DIR = "/root/.pi/agent";
|
|
11
|
+
/**
|
|
12
|
+
* Where the image STAGES its first-launch defaults (extensions + trust.json).
|
|
13
|
+
* NOT the agent dir, so it never conflicts with the persistent mount. The
|
|
14
|
+
* entrypoint promotes these into the mounted agent dir only when the home is
|
|
15
|
+
* FRESH (Model C seed-if-fresh).
|
|
16
|
+
*/
|
|
17
|
+
export declare const CONTAINER_STAGE_DIR = "/opt/anon-pi-seed/agent";
|
|
18
|
+
/**
|
|
19
|
+
* Where anon-pi mounts the canonical models.json (from `import`) read-only, so
|
|
20
|
+
* the first-launch seed can copy it into the fresh home alongside the image's
|
|
21
|
+
* staged defaults. Read-only: the container never writes back to the host seed.
|
|
22
|
+
*/
|
|
23
|
+
export declare const CONTAINER_MODELS_SEED = "/anon-pi-seed/models.json";
|
|
24
|
+
/** Marker file written into the agent dir after seeding; holds the seed version. */
|
|
25
|
+
export declare const SEED_MARKER = ".anon-pi-seed";
|
|
26
|
+
/** The single file the host-side seed carries: pi's model/provider registry. */
|
|
27
|
+
export declare const MODELS_FILE = "models.json";
|
|
28
|
+
/**
|
|
29
|
+
* containerRunCmd builds the container command: on a FRESH home (no seed
|
|
30
|
+
* marker), promote the image's staged defaults + the mounted models.json into
|
|
31
|
+
* the persistent agent dir and stamp the marker; then exec pi. On a seeded home
|
|
32
|
+
* it does nothing but exec pi, so pi's persisted state (incl. anything you
|
|
33
|
+
* `pi install`ed or models pi added) is used as-is and NEVER clobbered.
|
|
34
|
+
*
|
|
35
|
+
* seedVersion is written into the marker so a future image can re-seed changed
|
|
36
|
+
* defaults on a version bump; v1 only seeds when the marker is absent.
|
|
37
|
+
*/
|
|
38
|
+
export declare function containerRunCmd(seedVersion: string): string;
|
|
39
|
+
/** The seed version anon-pi stamps when it seeds a fresh home (bump to re-seed). */
|
|
40
|
+
export declare const SEED_VERSION = "1";
|
|
13
41
|
/** Inputs resolved from the environment + argv, injected so this stays pure. */
|
|
14
42
|
export interface AnonPiEnv {
|
|
15
43
|
/** $HOME (or an override) used to derive default paths. */
|
|
16
44
|
home: string;
|
|
17
|
-
/** socks5h proxy URL.
|
|
45
|
+
/** socks5h proxy URL. REQUIRED (no default: the proxy is what anonymizes). */
|
|
18
46
|
proxy?: string;
|
|
19
47
|
/** The anon-pi home dir. Default $XDG_CONFIG_HOME/anon-pi or ~/.config/anon-pi. */
|
|
20
48
|
anonPiHome?: string;
|
|
@@ -27,63 +55,148 @@ export interface AnonPiEnv {
|
|
|
27
55
|
/** XDG_CONFIG_HOME, if set (used to derive the default anon-pi home). */
|
|
28
56
|
xdgConfigHome?: string;
|
|
29
57
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
|
|
58
|
+
* Absolute path to the Dockerfile.pi that ships with anon-pi, used only to
|
|
59
|
+
* make the missing-image error's build command concrete. cli.ts resolves it
|
|
60
|
+
* from import.meta.url; when absent the message falls back to a bare
|
|
61
|
+
* `Dockerfile.pi`.
|
|
62
|
+
*/
|
|
63
|
+
dockerfilePath?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Absolute path to the shipped examples/Dockerfile.pi-webveil (pi + pi-webveil
|
|
66
|
+
* + SearXNG), used to make the missing-image error mention the fuller build.
|
|
35
67
|
*/
|
|
36
|
-
|
|
68
|
+
webveilDockerfilePath?: string;
|
|
69
|
+
/** `import` source models.json override (ANON_PI_SOURCE_MODELS). */
|
|
70
|
+
sourceModels?: string;
|
|
71
|
+
/** The host pi agent dir override (PI_CODING_AGENT_DIR), used to find models.json. */
|
|
72
|
+
piAgentDir?: string;
|
|
73
|
+
/** When true, use a throwaway state home (no persistence). Default false. */
|
|
74
|
+
ephemeral?: boolean;
|
|
75
|
+
/** The seed version anon-pi stamps into a fresh home. Default SEED_VERSION. */
|
|
76
|
+
seedVersion?: string;
|
|
37
77
|
}
|
|
38
78
|
/** The fully-resolved run plan cli.ts executes. */
|
|
39
79
|
export interface RunPlan {
|
|
40
80
|
/** Absolute workdir on the host (mounted at /work). */
|
|
41
81
|
workdir: string;
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The PERSISTENT per-workdir state dir on the host, mounted at the container's
|
|
84
|
+
* ~/.pi/agent. Everything pi writes here survives. For --ephemeral this is a
|
|
85
|
+
* throwaway path cli.ts creates + discards.
|
|
86
|
+
*/
|
|
87
|
+
stateDir: string;
|
|
88
|
+
/** The canonical host models.json (from `import`) mounted read-only for the seed, or '' if absent. */
|
|
45
89
|
configSeed: string;
|
|
46
|
-
/**
|
|
47
|
-
|
|
48
|
-
/** True iff the session dir does not exist yet and must be seeded from configSeed. */
|
|
49
|
-
needsSeed: boolean;
|
|
90
|
+
/** True when this workdir has no state yet (fresh home; the seed will run). */
|
|
91
|
+
fresh: boolean;
|
|
50
92
|
/** The argv passed to `netcage` (after the `netcage` program name). */
|
|
51
93
|
netcageArgs: string[];
|
|
52
94
|
}
|
|
53
95
|
/** A user-facing error whose message is meant to be printed verbatim (no stack). */
|
|
54
96
|
export declare class AnonPiError extends Error {
|
|
55
97
|
}
|
|
98
|
+
/** Resolve the anon-pi home dir (holds the seed). */
|
|
99
|
+
export declare function resolveAnonPiHome(env: AnonPiEnv): string;
|
|
56
100
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
* `~`. A `~`-relative or relative value is rejected loudly rather than silently
|
|
61
|
-
* mounted at a literal `~` directory or a cwd-relative path.
|
|
101
|
+
* The CANONICAL host seed dir holding models.json (written by `anon-pi import`).
|
|
102
|
+
* Mounted read-only so the first-launch seed can copy models.json into a fresh
|
|
103
|
+
* persistent home. Workdir-independent (import does not need a workdir).
|
|
62
104
|
*/
|
|
63
|
-
export declare function resolveAgentMount(env: AnonPiEnv): string;
|
|
64
|
-
/** Resolve the anon-pi home dir (holds the canonical seed + per-session state). */
|
|
65
|
-
export declare function resolveAnonPiHome(env: AnonPiEnv): string;
|
|
66
|
-
/** The canonical seed dir (copied FROM, never mounted). */
|
|
67
105
|
export declare function resolveConfigSeed(env: AnonPiEnv): string;
|
|
68
106
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
107
|
+
* Encode an absolute path into a directory name using pi's OWN convention (see
|
|
108
|
+
* pi coding-agent session-manager: `--${cwd without leading slash, / \ : -> -}--`),
|
|
109
|
+
* so an anon-pi state dir is readable and matches pi's mental model (no opaque
|
|
110
|
+
* hash). e.g. /home/u/dev/x -> --home-u-dev-x--
|
|
72
111
|
*/
|
|
73
|
-
export declare function
|
|
74
|
-
/**
|
|
75
|
-
|
|
112
|
+
export declare function pathSlug(absPath: string): string;
|
|
113
|
+
/**
|
|
114
|
+
* The persistent per-workdir state dir on the host (mounted at the container's
|
|
115
|
+
* ~/.pi/agent). Keyed by the workdir via pi's path-slug convention:
|
|
116
|
+
* <anonPiHome>/state/<slug>/agent
|
|
117
|
+
*/
|
|
118
|
+
export declare function stateAgentDir(env: AnonPiEnv, absWorkdir: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* Normalise a proxy-less host:port key from an ANON_PI_LLM value or a provider
|
|
121
|
+
* baseUrl, so `192.168.1.150:8080` matches `http://192.168.1.150:8080/v1`.
|
|
122
|
+
* Returns `host` (no port) or `host:port`, lowercased, scheme/path stripped.
|
|
123
|
+
*/
|
|
124
|
+
export declare function hostPortKey(value: string): string;
|
|
125
|
+
/**
|
|
126
|
+
* A pi provider entry (as it appears under models.json `providers[name]`). Only
|
|
127
|
+
* the fields anon-pi reads are typed; the rest is preserved verbatim.
|
|
128
|
+
*/
|
|
129
|
+
export interface PiProvider {
|
|
130
|
+
baseUrl?: string;
|
|
131
|
+
apiKey?: string;
|
|
132
|
+
api?: string;
|
|
133
|
+
models?: unknown[];
|
|
134
|
+
[k: string]: unknown;
|
|
135
|
+
}
|
|
136
|
+
/** Parsed shape of a pi models.json (only `providers` is required). */
|
|
137
|
+
export interface PiModelsFile {
|
|
138
|
+
providers?: Record<string, PiProvider>;
|
|
139
|
+
[k: string]: unknown;
|
|
140
|
+
}
|
|
141
|
+
/** The result of picking the ANON_PI_LLM provider out of a host models.json. */
|
|
142
|
+
export interface ImportResult {
|
|
143
|
+
/** The provider key (e.g. "llamacpp-router"). */
|
|
144
|
+
name: string;
|
|
145
|
+
/** The barebones models.json to write (just the matched provider). */
|
|
146
|
+
models: PiModelsFile;
|
|
147
|
+
/** True if the matched provider's apiKey looks like a REAL secret (warn). */
|
|
148
|
+
apiKeyLooksReal: boolean;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* PURE: given a parsed host models.json and the ANON_PI_LLM value, select the
|
|
152
|
+
* provider whose baseUrl points at that host:port and return a barebones
|
|
153
|
+
* models.json carrying ONLY that provider (verbatim, with its models). Throws
|
|
154
|
+
* AnonPiError if nothing matches. Carries no other provider (so etherplay /
|
|
155
|
+
* google / paid API keys never enter the seed).
|
|
156
|
+
*/
|
|
157
|
+
export declare function pickProviderForLlm(hostModels: PiModelsFile, llmDirect: string): ImportResult;
|
|
158
|
+
/**
|
|
159
|
+
* The default host models.json path `import` reads FROM. Overridable via
|
|
160
|
+
* ANON_PI_SOURCE_MODELS; defaults to the real pi config (~/.pi/agent/models.json
|
|
161
|
+
* under the container-less host HOME, or PI_CODING_AGENT_DIR if the user set it).
|
|
162
|
+
*/
|
|
163
|
+
export declare function resolveSourceModelsPath(env: AnonPiEnv): string;
|
|
76
164
|
/**
|
|
77
165
|
* Build the run plan from the environment + the (optional) workdir arg. PURE: it
|
|
78
|
-
* resolves paths and composes the netcage argv,
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
166
|
+
* resolves paths and composes the netcage argv, performing NO filesystem writes
|
|
167
|
+
* or spawns. It THROWS AnonPiError for the required inputs (image, llm, proxy).
|
|
168
|
+
*
|
|
169
|
+
* Statefulness (Model B): a persistent per-workdir host dir is mounted at the
|
|
170
|
+
* container's ~/.pi/agent, so pi's sessions/history/settings/extensions persist.
|
|
171
|
+
* First-launch seed (Model C): when that home is FRESH, the container run
|
|
172
|
+
* command promotes the image's staged defaults + the imported models.json into
|
|
173
|
+
* it and stamps a marker; thereafter pi OWNS the home and nothing is clobbered.
|
|
174
|
+
*
|
|
175
|
+
* `modelsSeedExists` reports whether the canonical import models.json exists (so
|
|
176
|
+
* it is mounted for the seed); `stateExists` reports whether this workdir's
|
|
177
|
+
* state home already exists (so `fresh` is known).
|
|
178
|
+
*
|
|
179
|
+
* --ephemeral mounts NO writable state: pi writes to the container's own
|
|
180
|
+
* filesystem, which netcage runs with `--rm`, so it is destroyed when the
|
|
181
|
+
* container exits. Nothing writable ever touches a host path; there is no
|
|
182
|
+
* cleanup and no leftover-on-crash. (The read-only models.json seed is still
|
|
183
|
+
* mounted; it is a single file anon-pi never writes to.)
|
|
184
|
+
*/
|
|
185
|
+
export declare function buildRunPlan(env: AnonPiEnv, workdirArg: string | undefined, modelsSeedExists: (modelsJsonPath: string) => boolean, stateExists: (stateDir: string) => boolean): RunPlan;
|
|
186
|
+
/**
|
|
187
|
+
* Absolute path to the Dockerfile.pi that ships with anon-pi, resolved from this
|
|
188
|
+
* module's location (package root, one level up from dist/anon-pi.js), or
|
|
189
|
+
* undefined if it cannot be found. Used only to make the missing-image error's
|
|
190
|
+
* build command concrete.
|
|
191
|
+
*/
|
|
192
|
+
export declare function shippedDockerfilePath(): string | undefined;
|
|
193
|
+
/**
|
|
194
|
+
* Absolute path to the fuller pi-webveil + SearXNG example that ships with
|
|
195
|
+
* anon-pi (examples/Dockerfile.pi-webveil), or undefined if not found.
|
|
83
196
|
*/
|
|
84
|
-
export declare function
|
|
197
|
+
export declare function shippedWebveilDockerfilePath(): string | undefined;
|
|
85
198
|
/** Read the AnonPiEnv from a process env map (kept separate so tests inject one). */
|
|
86
199
|
export declare function envFromProcess(penv: Record<string, string | undefined>): AnonPiEnv;
|
|
87
200
|
/** The --help text (kept here so it is covered by the same module). */
|
|
88
|
-
export declare const HELP = "anon-pi - launch pi inside a netcage (anonymized egress + one direct local model)\n\nUSAGE\n anon-pi [WORKDIR]\n\n WORKDIR the host folder pi works in (mounted at /work).
|
|
201
|
+
export declare const HELP = "anon-pi - launch pi inside a netcage (anonymized egress + one direct local model)\n\nUSAGE\n anon-pi [WORKDIR] launch pi jailed, working in WORKDIR (default: cwd)\n anon-pi import seed models.json from your local model\n\n WORKDIR the host folder pi works in (mounted at /work; pi's cwd). Files pi\n writes there land on the host.\n\nWHAT IT DOES\n Runs pi inside netcage with all web/DNS egress forced through the socks5h\n proxy (fail-closed) and ONE direct hole to your local model (ANON_PI_LLM).\n\n STATEFUL by default: a persistent per-workdir home\n (<ANON_PI_HOME>/state/<workdir>/agent) is mounted at the container's\n ~/.pi/agent, so your conversations, history, settings (model choice), and any\n extensions you `pi install` persist across launches. Re-running in the same\n folder resumes it. On a FRESH home, the image's staged defaults (extensions,\n trust) and your imported models.json are seeded in once; after that pi owns the\n home and nothing is overwritten. Requires `netcage`.\n\n --ephemeral (or ANON_PI_EPHEMERAL=1): mount NO writable state; pi writes to the\n container's own --rm layer, gone on exit. Nothing writable touches the host,\n no cleanup, no leftover-on-crash.\n\nimport\n Reads your host ~/.pi/agent/models.json, picks the provider whose baseUrl\n serves ANON_PI_LLM, and writes JUST that provider to the canonical seed\n (<ANON_PI_CONFIG>/models.json). No other provider's API keys, no sessions, no\n identity. It SEEDS a fresh home; models you later add inside pi persist and are\n never clobbered. Re-run with --force to overwrite the canonical seed.\n\nENVIRONMENT\n ANON_PI_IMAGE (required for run) image with `pi` on PATH. No image yet?\n Running anon-pi without it prints a ready-to-build\n Dockerfile.pi recipe; see the README (Providing a pi image).\n ANON_PI_LLM (required) RFC1918/link-local IP[:port] of the local model\n ANON_PI_PROXY (required) socks5h URL of your proxy (Tor/wireproxy/ssh -D).\n No default: the proxy is what anonymizes, so it is never guessed.\n ANON_PI_EPHEMERAL set to 1 for a throwaway (non-persistent) session\n ANON_PI_HOME anon-pi home (default $XDG_CONFIG_HOME/anon-pi or ~/.config/anon-pi)\n ANON_PI_CONFIG canonical seed dir holding models.json (default <ANON_PI_HOME>/agent)\n ANON_PI_SOURCE_MODELS (import) host models.json to read (default ~/.pi/agent/models.json)\n\nRESET A SESSION\n Delete its state home to start fresh (re-seeds next launch):\n rm -rf <ANON_PI_HOME>/state/<workdir-slug>/agent\n\nPLATFORM\n Linux only (via netcage's netns/nft jail). On macOS/Windows it works only\n inside a Linux VM, where --allow-direct to a LAN model is VM-boundary-sensitive.\n";
|
|
89
202
|
//# sourceMappingURL=anon-pi.d.ts.map
|
package/dist/anon-pi.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anon-pi.d.ts","sourceRoot":"","sources":["../src/anon-pi.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"anon-pi.d.ts","sourceRoot":"","sources":["../src/anon-pi.ts"],"names":[],"mappings":"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"}
|