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/dist/anon-pi.js CHANGED
@@ -17,46 +17,66 @@
17
17
  // trust.json that trusts /work). anon-pi does not synthesize pi's trust.json.
18
18
  // - Session identity = the ABSOLUTE workdir path (hashed). Same folder resumes
19
19
  // the same session config+state; reseed is manual (delete the session dir).
20
- import { createHash } from 'node:crypto';
20
+ import { existsSync } from 'node:fs';
21
21
  import { homedir } from 'node:os';
22
- import { isAbsolute, join, resolve } from 'node:path';
22
+ import { dirname, isAbsolute, join, resolve } from 'node:path';
23
+ import { fileURLToPath } from 'node:url';
23
24
  /** The container path the workdir is mounted at (pi's cwd). */
24
25
  export const CONTAINER_WORKDIR = '/work';
25
26
  /**
26
- * The DEFAULT container path the seeded pi config is mounted at (the pi global).
27
- * Absolute and image-independent: both podman (the -v target) and pi (the
28
- * PI_CODING_AGENT_DIR env) agree on it with no home-resolution guessing. A user
29
- * who knows their image's home can override it (ANON_PI_AGENT_MOUNT) to the
30
- * standard `~/.pi/agent` location, e.g. /root/.pi/agent for a root image.
27
+ * The container path pi uses as its config+state home. anon-pi mounts a
28
+ * PERSISTENT host dir here (Model B), so everything pi writes, sessions,
29
+ * history, settings (your model choice), `pi install`ed extensions, downloaded
30
+ * bin/fd, survives across launches. Statefulness is the default; --ephemeral
31
+ * mounts a throwaway dir here instead.
31
32
  */
32
- export const DEFAULT_CONTAINER_AGENT_DIR = '/opt/pi-agent';
33
- /** The pi env var that overrides its config dir (see pi config.ts getAgentDir). */
34
- export const PI_AGENT_DIR_ENV = 'PI_CODING_AGENT_DIR';
35
- const DEFAULT_PROXY = 'socks5h://127.0.0.1:9050';
36
- /** A user-facing error whose message is meant to be printed verbatim (no stack). */
37
- export class AnonPiError extends Error {
38
- }
33
+ export const CONTAINER_AGENT_DIR = '/root/.pi/agent';
39
34
  /**
40
- * Resolve the container agent-mount path: ANON_PI_AGENT_MOUNT or the default.
41
- * It MUST be an absolute container path, because it is BOTH the podman `-v`
42
- * target AND pi's PI_CODING_AGENT_DIR, and podman (unlike pi) does not expand
43
- * `~`. A `~`-relative or relative value is rejected loudly rather than silently
44
- * mounted at a literal `~` directory or a cwd-relative path.
35
+ * Where the image STAGES its first-launch defaults (extensions + trust.json).
36
+ * NOT the agent dir, so it never conflicts with the persistent mount. The
37
+ * entrypoint promotes these into the mounted agent dir only when the home is
38
+ * FRESH (Model C seed-if-fresh).
45
39
  */
46
- export function resolveAgentMount(env) {
47
- const raw = env.agentMount && env.agentMount.trim() !== ''
48
- ? env.agentMount.trim()
49
- : DEFAULT_CONTAINER_AGENT_DIR;
50
- if (raw.startsWith('~')) {
51
- throw new AnonPiError(`anon-pi: ANON_PI_AGENT_MOUNT must be an ABSOLUTE container path, not \`${raw}\`. ` +
52
- 'podman does not expand `~`; use the concrete home, e.g. /root/.pi/agent.');
53
- }
54
- if (!raw.startsWith('/')) {
55
- throw new AnonPiError(`anon-pi: ANON_PI_AGENT_MOUNT must be an ABSOLUTE container path, not \`${raw}\` (it is both the mount target and pi's config dir).`);
56
- }
57
- return raw;
40
+ export const CONTAINER_STAGE_DIR = '/opt/anon-pi-seed/agent';
41
+ /**
42
+ * Where anon-pi mounts the canonical models.json (from `import`) read-only, so
43
+ * the first-launch seed can copy it into the fresh home alongside the image's
44
+ * staged defaults. Read-only: the container never writes back to the host seed.
45
+ */
46
+ export const CONTAINER_MODELS_SEED = '/anon-pi-seed/models.json';
47
+ /** Marker file written into the agent dir after seeding; holds the seed version. */
48
+ export const SEED_MARKER = '.anon-pi-seed';
49
+ /** The single file the host-side seed carries: pi's model/provider registry. */
50
+ export const MODELS_FILE = 'models.json';
51
+ /**
52
+ * containerRunCmd builds the container command: on a FRESH home (no seed
53
+ * marker), promote the image's staged defaults + the mounted models.json into
54
+ * the persistent agent dir and stamp the marker; then exec pi. On a seeded home
55
+ * it does nothing but exec pi, so pi's persisted state (incl. anything you
56
+ * `pi install`ed or models pi added) is used as-is and NEVER clobbered.
57
+ *
58
+ * seedVersion is written into the marker so a future image can re-seed changed
59
+ * defaults on a version bump; v1 only seeds when the marker is absent.
60
+ */
61
+ export function containerRunCmd(seedVersion) {
62
+ const agent = CONTAINER_AGENT_DIR;
63
+ const marker = `${agent}/${SEED_MARKER}`;
64
+ return (`mkdir -p "${agent}" && ` +
65
+ `if [ ! -f "${marker}" ]; then ` +
66
+ // image-staged defaults (extensions, trust.json), if the image provides them
67
+ `{ [ -d "${CONTAINER_STAGE_DIR}" ] && cp -a "${CONTAINER_STAGE_DIR}/." "${agent}/" || true; } && ` +
68
+ // the host-imported models.json, if mounted
69
+ `{ [ -f "${CONTAINER_MODELS_SEED}" ] && cp "${CONTAINER_MODELS_SEED}" "${agent}/${MODELS_FILE}" || true; } && ` +
70
+ `printf '%s\\n' "${seedVersion}" > "${marker}"; ` +
71
+ `fi && ` +
72
+ `exec pi`);
73
+ }
74
+ /** The seed version anon-pi stamps when it seeds a fresh home (bump to re-seed). */
75
+ export const SEED_VERSION = '1';
76
+ /** A user-facing error whose message is meant to be printed verbatim (no stack). */
77
+ export class AnonPiError extends Error {
58
78
  }
59
- /** Resolve the anon-pi home dir (holds the canonical seed + per-session state). */
79
+ /** Resolve the anon-pi home dir (holds the seed). */
60
80
  export function resolveAnonPiHome(env) {
61
81
  if (env.anonPiHome)
62
82
  return resolve(env.anonPiHome);
@@ -65,99 +85,258 @@ export function resolveAnonPiHome(env) {
65
85
  : join(env.home, '.config');
66
86
  return join(base, 'anon-pi');
67
87
  }
68
- /** The canonical seed dir (copied FROM, never mounted). */
88
+ /**
89
+ * The CANONICAL host seed dir holding models.json (written by `anon-pi import`).
90
+ * Mounted read-only so the first-launch seed can copy models.json into a fresh
91
+ * persistent home. Workdir-independent (import does not need a workdir).
92
+ */
69
93
  export function resolveConfigSeed(env) {
70
94
  if (env.configSeed)
71
95
  return resolve(env.configSeed);
72
96
  return join(resolveAnonPiHome(env), 'agent');
73
97
  }
74
98
  /**
75
- * Session id = a stable short hash of the ABSOLUTE workdir path, so re-running
76
- * anon-pi on the same folder resumes the same session config+state, and moving
77
- * the folder starts a new session (documented, accepted).
99
+ * Encode an absolute path into a directory name using pi's OWN convention (see
100
+ * pi coding-agent session-manager: `--${cwd without leading slash, / \ : -> -}--`),
101
+ * so an anon-pi state dir is readable and matches pi's mental model (no opaque
102
+ * hash). e.g. /home/u/dev/x -> --home-u-dev-x--
78
103
  */
79
- export function sessionId(absWorkdir) {
80
- return createHash('sha256').update(absWorkdir).digest('hex').slice(0, 16);
104
+ export function pathSlug(absPath) {
105
+ return `--${absPath.replace(/^[/\\]/, '').replace(/[/\\:]/g, '-')}--`;
81
106
  }
82
- /** The per-session seeded config dir on the host for a given workdir. */
83
- export function sessionAgentDir(env, absWorkdir) {
84
- return join(resolveAnonPiHome(env), 'sessions', sessionId(absWorkdir), 'agent');
107
+ /**
108
+ * The persistent per-workdir state dir on the host (mounted at the container's
109
+ * ~/.pi/agent). Keyed by the workdir via pi's path-slug convention:
110
+ * <anonPiHome>/state/<slug>/agent
111
+ */
112
+ export function stateAgentDir(env, absWorkdir) {
113
+ return join(resolveAnonPiHome(env), 'state', pathSlug(absWorkdir), 'agent');
114
+ }
115
+ /**
116
+ * Normalise a proxy-less host:port key from an ANON_PI_LLM value or a provider
117
+ * baseUrl, so `192.168.1.150:8080` matches `http://192.168.1.150:8080/v1`.
118
+ * Returns `host` (no port) or `host:port`, lowercased, scheme/path stripped.
119
+ */
120
+ export function hostPortKey(value) {
121
+ let v = value.trim();
122
+ const scheme = v.indexOf('://');
123
+ if (scheme >= 0)
124
+ v = v.slice(scheme + 3);
125
+ v = v.split('/')[0]; // drop path (/v1, ...)
126
+ v = v.replace(/^[^@]*@/, ''); // drop any user:pass@
127
+ return v.toLowerCase();
128
+ }
129
+ /** apiKey values that are NOT real secrets (safe to carry into the seed). */
130
+ const BENIGN_API_KEYS = new Set(['', 'none', 'ollama', 'no-key', 'local']);
131
+ /**
132
+ * PURE: given a parsed host models.json and the ANON_PI_LLM value, select the
133
+ * provider whose baseUrl points at that host:port and return a barebones
134
+ * models.json carrying ONLY that provider (verbatim, with its models). Throws
135
+ * AnonPiError if nothing matches. Carries no other provider (so etherplay /
136
+ * google / paid API keys never enter the seed).
137
+ */
138
+ export function pickProviderForLlm(hostModels, llmDirect) {
139
+ const providers = hostModels.providers ?? {};
140
+ const want = hostPortKey(llmDirect);
141
+ const matches = [];
142
+ for (const [name, p] of Object.entries(providers)) {
143
+ if (!p || typeof p !== 'object' || !p.baseUrl)
144
+ continue;
145
+ if (hostPortKey(p.baseUrl) === want)
146
+ matches.push(name);
147
+ }
148
+ if (matches.length === 0) {
149
+ const known = Object.entries(providers)
150
+ .filter(([, p]) => p && p.baseUrl)
151
+ .map(([n, p]) => ` ${n}: ${p.baseUrl}`)
152
+ .join('\n');
153
+ throw new AnonPiError(`anon-pi import: no provider in your host models.json points at ANON_PI_LLM (${want}).\n` +
154
+ (known
155
+ ? `Providers found:\n${known}\n`
156
+ : 'No providers with a baseUrl were found.\n') +
157
+ 'Set ANON_PI_LLM to the host:port of a provider above, or add that provider to pi first.');
158
+ }
159
+ const name = matches[0];
160
+ const provider = providers[name];
161
+ const key = (provider.apiKey ?? '').trim().toLowerCase();
162
+ const apiKeyLooksReal = !BENIGN_API_KEYS.has(key);
163
+ return {
164
+ name,
165
+ models: { providers: { [name]: provider } },
166
+ apiKeyLooksReal,
167
+ };
168
+ }
169
+ /**
170
+ * The default host models.json path `import` reads FROM. Overridable via
171
+ * ANON_PI_SOURCE_MODELS; defaults to the real pi config (~/.pi/agent/models.json
172
+ * under the container-less host HOME, or PI_CODING_AGENT_DIR if the user set it).
173
+ */
174
+ export function resolveSourceModelsPath(env) {
175
+ if (env.sourceModels && env.sourceModels.trim() !== '') {
176
+ return resolve(env.sourceModels);
177
+ }
178
+ const agentDir = env.piAgentDir && env.piAgentDir.trim() !== ''
179
+ ? env.piAgentDir
180
+ : join(env.home, '.pi', 'agent');
181
+ return join(agentDir, MODELS_FILE);
85
182
  }
86
183
  /**
87
184
  * Build the run plan from the environment + the (optional) workdir arg. PURE: it
88
- * resolves paths and composes the netcage argv, and reports whether a seed copy
89
- * is needed, but performs NO filesystem writes or spawns. It THROWS AnonPiError
90
- * for the two hard preconditions (missing image, missing llm) so the required
91
- * inputs fail loud; the missing-SEED check is left to cli.ts (it needs a real
92
- * `existsSync`), but `needsSeed` is derived from the injected `seedExists`.
185
+ * resolves paths and composes the netcage argv, performing NO filesystem writes
186
+ * or spawns. It THROWS AnonPiError for the required inputs (image, llm, proxy).
187
+ *
188
+ * Statefulness (Model B): a persistent per-workdir host dir is mounted at the
189
+ * container's ~/.pi/agent, so pi's sessions/history/settings/extensions persist.
190
+ * First-launch seed (Model C): when that home is FRESH, the container run
191
+ * command promotes the image's staged defaults + the imported models.json into
192
+ * it and stamps a marker; thereafter pi OWNS the home and nothing is clobbered.
193
+ *
194
+ * `modelsSeedExists` reports whether the canonical import models.json exists (so
195
+ * it is mounted for the seed); `stateExists` reports whether this workdir's
196
+ * state home already exists (so `fresh` is known).
197
+ *
198
+ * --ephemeral mounts NO writable state: pi writes to the container's own
199
+ * filesystem, which netcage runs with `--rm`, so it is destroyed when the
200
+ * container exits. Nothing writable ever touches a host path; there is no
201
+ * cleanup and no leftover-on-crash. (The read-only models.json seed is still
202
+ * mounted; it is a single file anon-pi never writes to.)
93
203
  */
94
- export function buildRunPlan(env, workdirArg, seedExists, sessionExists) {
204
+ export function buildRunPlan(env, workdirArg, modelsSeedExists, stateExists) {
95
205
  if (!env.image || env.image.trim() === '') {
206
+ // dockerfilePath is injected (cli.ts resolves the shipped Dockerfile.pi via
207
+ // import.meta.url; tests pass a fixed path). Every command is emitted
208
+ // flush-left so it copy-pastes cleanly: an indented heredoc would bake
209
+ // leading spaces into the Dockerfile and break the EOF terminator, so we
210
+ // point at the shipped file instead of printing a heredoc.
211
+ const df = env.dockerfilePath ?? 'Dockerfile.pi';
212
+ const wv = env.webveilDockerfilePath ?? 'examples/Dockerfile.pi-webveil';
96
213
  throw new AnonPiError('anon-pi: set ANON_PI_IMAGE to a container image that has `pi` on its PATH.\n' +
97
214
  '\n' +
98
- 'No such image yet? Build a small one from the upstream-documented recipe\n' +
99
- '(it installs the official @earendil-works/pi-coding-agent npm package):\n' +
215
+ 'No image yet? A ready Dockerfile.pi ships with anon-pi (it installs the\n' +
216
+ 'official @earendil-works/pi-coding-agent). Build it and point at it:\n' +
217
+ '\n' +
218
+ `podman build -t localhost/anon-pi-pi:latest -f "${df}" "$(dirname "${df}")"\n` +
219
+ 'export ANON_PI_IMAGE=localhost/anon-pi-pi:latest\n' +
100
220
  '\n' +
101
- " cat > Dockerfile.pi <<'EOF'\n" +
102
- ' FROM node:24-bookworm-slim\n' +
103
- ' RUN apt-get update && apt-get install -y --no-install-recommends \\\n' +
104
- ' bash ca-certificates git ripgrep && rm -rf /var/lib/apt/lists/*\n' +
105
- ' RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent\n' +
106
- ' WORKDIR /work\n' +
107
- ' EOF\n' +
108
- ' podman build -t localhost/anon-pi-pi:latest -f Dockerfile.pi .\n' +
109
- ' export ANON_PI_IMAGE=localhost/anon-pi-pi:latest\n' +
221
+ 'Or the fuller example with the pi-webveil extension + a local SearXNG\n' +
222
+ '(anonymized web search):\n' +
110
223
  '\n' +
111
- 'A ready Dockerfile.pi also ships with this package. See the README\n' +
112
- '(Providing a pi image) for details and a community-image note.');
224
+ `podman build -t localhost/anon-pi-webveil:latest -f "${wv}" "$(dirname "${wv}")"\n` +
225
+ 'export ANON_PI_IMAGE=localhost/anon-pi-webveil:latest\n' +
226
+ '\n' +
227
+ 'See the README (Providing a pi image) for details and a community-image note.');
113
228
  }
114
229
  if (!env.llmDirect || env.llmDirect.trim() === '') {
115
230
  throw new AnonPiError('anon-pi: set ANON_PI_LLM to the RFC1918/link-local IP[:port] of the local model pi should reach directly (e.g. ANON_PI_LLM=192.168.1.150:8080). All other egress stays forced through the proxy.');
116
231
  }
232
+ if (!env.proxy || env.proxy.trim() === '') {
233
+ // No default: this is an anonymity tool, so the proxy is REQUIRED and never
234
+ // guessed (mirrors netcage, which fails closed without --proxy). A silent
235
+ // default would anonymize through the wrong endpoint, or fail deep in the
236
+ // jail with a confusing DNS error, if the guessed proxy is not actually up.
237
+ throw new AnonPiError('anon-pi: set ANON_PI_PROXY to your socks5h proxy. anon-pi has no default:\n' +
238
+ 'the proxy is what makes the session anonymous, so it is never guessed.\n' +
239
+ '\n' +
240
+ 'Pick the one you run (copy-paste), then re-run anon-pi:\n' +
241
+ '\n' +
242
+ '# Tor (system tor / Tor Browser bundle default port)\n' +
243
+ 'export ANON_PI_PROXY=socks5h://127.0.0.1:9050\n' +
244
+ '\n' +
245
+ '# wireproxy -> a WireGuard VPN (Mullvad, Proton, ...); use YOUR configured\n' +
246
+ '# [Socks5] BindAddress port (1080 in wireproxy examples):\n' +
247
+ 'export ANON_PI_PROXY=socks5h://127.0.0.1:1080\n' +
248
+ '\n' +
249
+ '# an SSH dynamic-forward (ssh -D 1080 host) or any other socks5h endpoint\n' +
250
+ 'export ANON_PI_PROXY=socks5h://127.0.0.1:1080\n' +
251
+ '\n' +
252
+ 'Only socks5h:// is accepted (plain socks5:// resolves DNS locally and leaks).');
253
+ }
117
254
  const home = env.home;
118
255
  if (!home || home.trim() === '') {
119
256
  throw new AnonPiError('anon-pi: could not resolve HOME.');
120
257
  }
121
258
  const raw = workdirArg && workdirArg.trim() !== '' ? workdirArg : process.cwd();
122
259
  const workdir = isAbsolute(raw) ? raw : resolve(raw);
123
- const configSeed = resolveConfigSeed(env);
124
- if (!seedExists(configSeed)) {
125
- throw new AnonPiError(`anon-pi: canonical config not found at ${configSeed}.\n` +
126
- 'anon-pi never populates it for you. Create it yourself with the pi config you want\n' +
127
- '(anon accounts, chosen models/skills, and a trust.json that trusts /work), e.g.:\n' +
128
- ` mkdir -p ${configSeed}\n` +
129
- ` cp -a ~/.pi/agent/. ${configSeed}/ # then remove any identity you do not want anonymized\n` +
130
- 'See the README (Populating the seed) for the trust.json requirement.');
131
- }
132
- const sessionDir = sessionAgentDir(env, workdir);
133
- const needsSeed = !sessionExists(sessionDir);
134
- const agentMount = resolveAgentMount(env);
135
- const proxy = env.proxy && env.proxy.trim() !== '' ? env.proxy : DEFAULT_PROXY;
260
+ // Persistent per-workdir state home, unless --ephemeral (no writable mount).
261
+ const ephemeral = env.ephemeral === true;
262
+ const stateDir = ephemeral ? '' : stateAgentDir(env, workdir);
263
+ // Ephemeral home is always fresh (the container's throwaway layer); a
264
+ // persistent home is fresh iff its dir is absent.
265
+ const fresh = ephemeral ? true : !stateExists(stateDir);
266
+ // The canonical imported models.json is mounted (read-only) for the seed only
267
+ // when it exists; pi can also start with no models and you add them in-session.
268
+ const modelsSeed = join(resolveConfigSeed(env), MODELS_FILE);
269
+ const haveModelsSeed = modelsSeedExists(modelsSeed);
270
+ const proxy = env.proxy.trim();
271
+ // netcage's --allow-direct wants a bare IP[:port]/CIDR (no scheme/path), but a
272
+ // user naturally sets ANON_PI_LLM to a URL (http://192.168.1.150:8080). Strip
273
+ // it to host:port with the same helper `import` uses to match providers, so a
274
+ // URL, an ip:port, or a bare ip all work.
275
+ const directTarget = hostPortKey(env.llmDirect);
276
+ const seedVersion = env.seedVersion ?? SEED_VERSION;
136
277
  const netcageArgs = [
137
278
  'run',
138
279
  '--proxy',
139
280
  proxy,
140
281
  '--allow-direct',
141
- env.llmDirect,
282
+ directTarget,
142
283
  '-it',
143
284
  '-v',
144
285
  workdir, // netcage defaults a target-less -v to /work and cwd to /work
145
- '-v',
146
- `${sessionDir}:${agentMount}`,
147
- '-e',
148
- `${PI_AGENT_DIR_ENV}=${agentMount}`,
149
- env.image,
150
- 'pi',
151
286
  ];
287
+ // Persistent mode ONLY: mount the per-workdir state home at ~/.pi/agent
288
+ // (Model B). --ephemeral mounts nothing writable: pi writes to the container's
289
+ // own --rm layer, gone on exit, no host state.
290
+ if (!ephemeral) {
291
+ netcageArgs.push('-v', `${stateDir}:${CONTAINER_AGENT_DIR}`);
292
+ }
293
+ // Mount the imported models.json read-only for the first-launch seed, if any.
294
+ if (haveModelsSeed) {
295
+ netcageArgs.push('-v', `${modelsSeed}:${CONTAINER_MODELS_SEED}:ro`);
296
+ }
297
+ netcageArgs.push(env.image, 'sh', '-c', containerRunCmd(seedVersion));
152
298
  return {
153
299
  workdir,
154
- sessionAgentDir: sessionDir,
155
- configSeed,
156
- agentMount,
157
- needsSeed,
300
+ stateDir,
301
+ configSeed: haveModelsSeed ? modelsSeed : '',
302
+ fresh,
158
303
  netcageArgs,
159
304
  };
160
305
  }
306
+ /**
307
+ * Absolute path to the Dockerfile.pi that ships with anon-pi, resolved from this
308
+ * module's location (package root, one level up from dist/anon-pi.js), or
309
+ * undefined if it cannot be found. Used only to make the missing-image error's
310
+ * build command concrete.
311
+ */
312
+ export function shippedDockerfilePath() {
313
+ return shippedFile('Dockerfile.pi');
314
+ }
315
+ /**
316
+ * Absolute path to the fuller pi-webveil + SearXNG example that ships with
317
+ * anon-pi (examples/Dockerfile.pi-webveil), or undefined if not found.
318
+ */
319
+ export function shippedWebveilDockerfilePath() {
320
+ return shippedFile(join('examples', 'Dockerfile.pi-webveil'));
321
+ }
322
+ /**
323
+ * Resolve a file shipped in the package root, from this module's location
324
+ * (package root is one level up from dist/anon-pi.js). Returns undefined if it
325
+ * cannot be found or import.meta.url is unavailable.
326
+ */
327
+ function shippedFile(rel) {
328
+ try {
329
+ const here = dirname(fileURLToPath(import.meta.url));
330
+ for (const p of [join(here, '..', rel), join(here, rel)]) {
331
+ if (existsSync(p))
332
+ return p;
333
+ }
334
+ }
335
+ catch {
336
+ // import.meta.url unavailable (e.g. some test bundlers): fall through.
337
+ }
338
+ return undefined;
339
+ }
161
340
  /** Read the AnonPiEnv from a process env map (kept separate so tests inject one). */
162
341
  export function envFromProcess(penv) {
163
342
  return {
@@ -168,40 +347,68 @@ export function envFromProcess(penv) {
168
347
  image: penv.ANON_PI_IMAGE,
169
348
  llmDirect: penv.ANON_PI_LLM,
170
349
  xdgConfigHome: penv.XDG_CONFIG_HOME,
171
- agentMount: penv.ANON_PI_AGENT_MOUNT,
350
+ dockerfilePath: shippedDockerfilePath(),
351
+ webveilDockerfilePath: shippedWebveilDockerfilePath(),
352
+ sourceModels: penv.ANON_PI_SOURCE_MODELS,
353
+ piAgentDir: penv.PI_CODING_AGENT_DIR,
354
+ ephemeral: isTruthy(penv.ANON_PI_EPHEMERAL),
172
355
  };
173
356
  }
357
+ /** Whether an env-var string is set to a truthy value (1/true/yes, any case). */
358
+ function isTruthy(v) {
359
+ if (!v)
360
+ return false;
361
+ const s = v.trim().toLowerCase();
362
+ return s === '1' || s === 'true' || s === 'yes' || s === 'on';
363
+ }
174
364
  /** The --help text (kept here so it is covered by the same module). */
175
365
  export const HELP = `anon-pi - launch pi inside a netcage (anonymized egress + one direct local model)
176
366
 
177
367
  USAGE
178
- anon-pi [WORKDIR]
368
+ anon-pi [WORKDIR] launch pi jailed, working in WORKDIR (default: cwd)
369
+ anon-pi import seed models.json from your local model
179
370
 
180
- WORKDIR the host folder pi works in (mounted at /work). Defaults to the
181
- current directory. The session config+state is keyed to this folder.
371
+ WORKDIR the host folder pi works in (mounted at ${CONTAINER_WORKDIR}; pi's cwd). Files pi
372
+ writes there land on the host.
182
373
 
183
374
  WHAT IT DOES
184
- Seeds a per-workdir writable copy of your canonical anon-pi config into
185
- ~/.config/anon-pi/sessions/<hash>/agent, mounts it as pi's global config
186
- (${PI_AGENT_DIR_ENV}=<mount>, default ${DEFAULT_CONTAINER_AGENT_DIR}), mounts WORKDIR at
187
- ${CONTAINER_WORKDIR}, opens ONE direct hole to your local model, and runs pi with all other
188
- egress forced through the socks5h proxy, fail-closed. Requires \`netcage\`.
375
+ Runs pi inside netcage with all web/DNS egress forced through the socks5h
376
+ proxy (fail-closed) and ONE direct hole to your local model (ANON_PI_LLM).
377
+
378
+ STATEFUL by default: a persistent per-workdir home
379
+ (<ANON_PI_HOME>/state/<workdir>/agent) is mounted at the container's
380
+ ~/.pi/agent, so your conversations, history, settings (model choice), and any
381
+ extensions you \`pi install\` persist across launches. Re-running in the same
382
+ folder resumes it. On a FRESH home, the image's staged defaults (extensions,
383
+ trust) and your imported models.json are seeded in once; after that pi owns the
384
+ home and nothing is overwritten. Requires \`netcage\`.
385
+
386
+ --ephemeral (or ANON_PI_EPHEMERAL=1): mount NO writable state; pi writes to the
387
+ container's own --rm layer, gone on exit. Nothing writable touches the host,
388
+ no cleanup, no leftover-on-crash.
389
+
390
+ import
391
+ Reads your host ~/.pi/agent/models.json, picks the provider whose baseUrl
392
+ serves ANON_PI_LLM, and writes JUST that provider to the canonical seed
393
+ (<ANON_PI_CONFIG>/models.json). No other provider's API keys, no sessions, no
394
+ identity. It SEEDS a fresh home; models you later add inside pi persist and are
395
+ never clobbered. Re-run with --force to overwrite the canonical seed.
189
396
 
190
397
  ENVIRONMENT
191
- ANON_PI_IMAGE (required) image with \`pi\` on PATH. No image yet? Running
192
- anon-pi without it prints a ready-to-build Dockerfile.pi
193
- recipe; see the README (Providing a pi image).
398
+ ANON_PI_IMAGE (required for run) image with \`pi\` on PATH. No image yet?
399
+ Running anon-pi without it prints a ready-to-build
400
+ Dockerfile.pi recipe; see the README (Providing a pi image).
194
401
  ANON_PI_LLM (required) RFC1918/link-local IP[:port] of the local model
195
- ANON_PI_PROXY socks5h URL (default ${DEFAULT_PROXY})
402
+ ANON_PI_PROXY (required) socks5h URL of your proxy (Tor/wireproxy/ssh -D).
403
+ No default: the proxy is what anonymizes, so it is never guessed.
404
+ ANON_PI_EPHEMERAL set to 1 for a throwaway (non-persistent) session
196
405
  ANON_PI_HOME anon-pi home (default $XDG_CONFIG_HOME/anon-pi or ~/.config/anon-pi)
197
- ANON_PI_CONFIG canonical seed dir (default <ANON_PI_HOME>/agent)
198
- ANON_PI_AGENT_MOUNT absolute container path for pi's config (default
199
- ${DEFAULT_CONTAINER_AGENT_DIR}; set to your image's ~/.pi/agent, e.g. /root/.pi/agent)
406
+ ANON_PI_CONFIG canonical seed dir holding models.json (default <ANON_PI_HOME>/agent)
407
+ ANON_PI_SOURCE_MODELS (import) host models.json to read (default ~/.pi/agent/models.json)
200
408
 
201
- RESEED
202
- Reseed is manual: delete the session dir, e.g.
203
- rm -rf ~/.config/anon-pi/sessions/<hash>/agent
204
- and the next run re-seeds it from the canonical config.
409
+ RESET A SESSION
410
+ Delete its state home to start fresh (re-seeds next launch):
411
+ rm -rf <ANON_PI_HOME>/state/<workdir-slug>/agent
205
412
 
206
413
  PLATFORM
207
414
  Linux only (via netcage's netns/nft jail). On macOS/Windows it works only
@@ -1 +1 @@
1
- {"version":3,"file":"anon-pi.js","sourceRoot":"","sources":["../src/anon-pi.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"anon-pi.js","sourceRoot":"","sources":["../src/anon-pi.ts"],"names":[],"mappings":"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"}