anon-pi 0.1.0 → 0.2.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,37 @@
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
+ * Where the seed (just models.json) is mounted read-only in the container. The
28
+ * run command copies models.json FROM here INTO the container's own
29
+ * ~/.pi/agent, so it LAYERS onto the image's config (extensions/skills the image
30
+ * installed survive) instead of replacing it. This is why we mount+copy rather
31
+ * than mount-as-agent-dir: mounting over ~/.pi/agent would shadow the image's
32
+ * extensions.
31
33
  */
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';
34
+ export const CONTAINER_SEED_DIR = '/anon-pi-seed';
35
+ /**
36
+ * The container command: copy the seeded models.json into pi's own config dir
37
+ * (creating it if absent), then exec pi. `$HOME/.pi/agent` is pi's default
38
+ * config dir when PI_CODING_AGENT_DIR is unset, i.e. exactly where the image
39
+ * installed pi + any extensions, so the copy augments rather than shadows.
40
+ */
41
+ export const CONTAINER_RUN_CMD = `mkdir -p "$HOME/.pi/agent" && ` +
42
+ `cp ${CONTAINER_SEED_DIR}/models.json "$HOME/.pi/agent/models.json" && ` +
43
+ `exec pi`;
44
+ /** The single file the seed carries: pi's model/provider registry. */
45
+ export const MODELS_FILE = 'models.json';
35
46
  const DEFAULT_PROXY = 'socks5h://127.0.0.1:9050';
36
47
  /** A user-facing error whose message is meant to be printed verbatim (no stack). */
37
48
  export class AnonPiError extends Error {
38
49
  }
39
- /**
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.
45
- */
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;
58
- }
59
- /** Resolve the anon-pi home dir (holds the canonical seed + per-session state). */
50
+ /** Resolve the anon-pi home dir (holds the seed). */
60
51
  export function resolveAnonPiHome(env) {
61
52
  if (env.anonPiHome)
62
53
  return resolve(env.anonPiHome);
@@ -65,35 +56,108 @@ export function resolveAnonPiHome(env) {
65
56
  : join(env.home, '.config');
66
57
  return join(base, 'anon-pi');
67
58
  }
68
- /** The canonical seed dir (copied FROM, never mounted). */
59
+ /** The seed dir (holds models.json), mounted read-only into the container. */
69
60
  export function resolveConfigSeed(env) {
70
61
  if (env.configSeed)
71
62
  return resolve(env.configSeed);
72
63
  return join(resolveAnonPiHome(env), 'agent');
73
64
  }
74
65
  /**
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).
66
+ * Normalise a proxy-less host:port key from an ANON_PI_LLM value or a provider
67
+ * baseUrl, so `192.168.1.150:8080` matches `http://192.168.1.150:8080/v1`.
68
+ * Returns `host` (no port) or `host:port`, lowercased, scheme/path stripped.
78
69
  */
79
- export function sessionId(absWorkdir) {
80
- return createHash('sha256').update(absWorkdir).digest('hex').slice(0, 16);
70
+ export function hostPortKey(value) {
71
+ let v = value.trim();
72
+ const scheme = v.indexOf('://');
73
+ if (scheme >= 0)
74
+ v = v.slice(scheme + 3);
75
+ v = v.split('/')[0]; // drop path (/v1, ...)
76
+ v = v.replace(/^[^@]*@/, ''); // drop any user:pass@
77
+ return v.toLowerCase();
81
78
  }
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');
79
+ /** apiKey values that are NOT real secrets (safe to carry into the seed). */
80
+ const BENIGN_API_KEYS = new Set(['', 'none', 'ollama', 'no-key', 'local']);
81
+ /**
82
+ * PURE: given a parsed host models.json and the ANON_PI_LLM value, select the
83
+ * provider whose baseUrl points at that host:port and return a barebones
84
+ * models.json carrying ONLY that provider (verbatim, with its models). Throws
85
+ * AnonPiError if nothing matches. Carries no other provider (so etherplay /
86
+ * google / paid API keys never enter the seed).
87
+ */
88
+ export function pickProviderForLlm(hostModels, llmDirect) {
89
+ const providers = hostModels.providers ?? {};
90
+ const want = hostPortKey(llmDirect);
91
+ const matches = [];
92
+ for (const [name, p] of Object.entries(providers)) {
93
+ if (!p || typeof p !== 'object' || !p.baseUrl)
94
+ continue;
95
+ if (hostPortKey(p.baseUrl) === want)
96
+ matches.push(name);
97
+ }
98
+ if (matches.length === 0) {
99
+ const known = Object.entries(providers)
100
+ .filter(([, p]) => p && p.baseUrl)
101
+ .map(([n, p]) => ` ${n}: ${p.baseUrl}`)
102
+ .join('\n');
103
+ throw new AnonPiError(`anon-pi import: no provider in your host models.json points at ANON_PI_LLM (${want}).\n` +
104
+ (known
105
+ ? `Providers found:\n${known}\n`
106
+ : 'No providers with a baseUrl were found.\n') +
107
+ 'Set ANON_PI_LLM to the host:port of a provider above, or add that provider to pi first.');
108
+ }
109
+ const name = matches[0];
110
+ const provider = providers[name];
111
+ const key = (provider.apiKey ?? '').trim().toLowerCase();
112
+ const apiKeyLooksReal = !BENIGN_API_KEYS.has(key);
113
+ return {
114
+ name,
115
+ models: { providers: { [name]: provider } },
116
+ apiKeyLooksReal,
117
+ };
118
+ }
119
+ /**
120
+ * The default host models.json path `import` reads FROM. Overridable via
121
+ * ANON_PI_SOURCE_MODELS; defaults to the real pi config (~/.pi/agent/models.json
122
+ * under the container-less host HOME, or PI_CODING_AGENT_DIR if the user set it).
123
+ */
124
+ export function resolveSourceModelsPath(env) {
125
+ if (env.sourceModels && env.sourceModels.trim() !== '') {
126
+ return resolve(env.sourceModels);
127
+ }
128
+ const agentDir = env.piAgentDir && env.piAgentDir.trim() !== ''
129
+ ? env.piAgentDir
130
+ : join(env.home, '.pi', 'agent');
131
+ return join(agentDir, MODELS_FILE);
85
132
  }
86
133
  /**
87
134
  * 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`.
135
+ * resolves paths and composes the netcage argv, performing NO filesystem writes
136
+ * or spawns. It THROWS AnonPiError for the hard preconditions (missing image,
137
+ * missing llm, missing seed models.json) so the required inputs fail loud.
138
+ *
139
+ * The seed (models.json) is mounted READ-ONLY at /anon-pi-seed and copied into
140
+ * the container's own ~/.pi/agent by the run command, so it LAYERS onto the
141
+ * image's config (image-installed extensions/skills survive) rather than
142
+ * shadowing it.
93
143
  */
94
- export function buildRunPlan(env, workdirArg, seedExists, sessionExists) {
144
+ export function buildRunPlan(env, workdirArg, seedModelsExists) {
95
145
  if (!env.image || env.image.trim() === '') {
96
- throw new AnonPiError('anon-pi: set ANON_PI_IMAGE to a container image that has `pi` on its PATH (e.g. ANON_PI_IMAGE=your/pi-image:tag).');
146
+ // dockerfilePath is injected (cli.ts resolves the shipped Dockerfile.pi via
147
+ // import.meta.url; tests pass a fixed path). Every command is emitted
148
+ // flush-left so it copy-pastes cleanly: an indented heredoc would bake
149
+ // leading spaces into the Dockerfile and break the EOF terminator, so we
150
+ // point at the shipped file instead of printing a heredoc.
151
+ const df = env.dockerfilePath ?? 'Dockerfile.pi';
152
+ throw new AnonPiError('anon-pi: set ANON_PI_IMAGE to a container image that has `pi` on its PATH.\n' +
153
+ '\n' +
154
+ 'No image yet? A ready Dockerfile.pi ships with anon-pi (it installs the\n' +
155
+ 'official @earendil-works/pi-coding-agent). Build it and point at it:\n' +
156
+ '\n' +
157
+ `podman build -t localhost/anon-pi-pi:latest -f "${df}" "$(dirname "${df}")"\n` +
158
+ 'export ANON_PI_IMAGE=localhost/anon-pi-pi:latest\n' +
159
+ '\n' +
160
+ 'See the README (Providing a pi image) for details and a community-image note.');
97
161
  }
98
162
  if (!env.llmDirect || env.llmDirect.trim() === '') {
99
163
  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.');
@@ -105,17 +169,18 @@ export function buildRunPlan(env, workdirArg, seedExists, sessionExists) {
105
169
  const raw = workdirArg && workdirArg.trim() !== '' ? workdirArg : process.cwd();
106
170
  const workdir = isAbsolute(raw) ? raw : resolve(raw);
107
171
  const configSeed = resolveConfigSeed(env);
108
- if (!seedExists(configSeed)) {
109
- throw new AnonPiError(`anon-pi: canonical config not found at ${configSeed}.\n` +
110
- 'anon-pi never populates it for you. Create it yourself with the pi config you want\n' +
111
- '(anon accounts, chosen models/skills, and a trust.json that trusts /work), e.g.:\n' +
112
- ` mkdir -p ${configSeed}\n` +
113
- ` cp -a ~/.pi/agent/. ${configSeed}/ # then remove any identity you do not want anonymized\n` +
114
- 'See the README (Populating the seed) for the trust.json requirement.');
172
+ const modelsJson = join(configSeed, MODELS_FILE);
173
+ if (!seedModelsExists(modelsJson)) {
174
+ throw new AnonPiError(`anon-pi: no seed models.json at ${modelsJson}.\n` +
175
+ '\n' +
176
+ 'anon-pi never populates it for you. Generate it from your local model:\n' +
177
+ '\n' +
178
+ 'anon-pi import\n' +
179
+ '\n' +
180
+ '`import` reads your host ~/.pi/agent/models.json, picks the provider that\n' +
181
+ 'serves ANON_PI_LLM, and writes just that provider here (no auth for other\n' +
182
+ 'providers, no sessions, no identity). See the README (Populating the seed).');
115
183
  }
116
- const sessionDir = sessionAgentDir(env, workdir);
117
- const needsSeed = !sessionExists(sessionDir);
118
- const agentMount = resolveAgentMount(env);
119
184
  const proxy = env.proxy && env.proxy.trim() !== '' ? env.proxy : DEFAULT_PROXY;
120
185
  const netcageArgs = [
121
186
  'run',
@@ -127,21 +192,44 @@ export function buildRunPlan(env, workdirArg, seedExists, sessionExists) {
127
192
  '-v',
128
193
  workdir, // netcage defaults a target-less -v to /work and cwd to /work
129
194
  '-v',
130
- `${sessionDir}:${agentMount}`,
131
- '-e',
132
- `${PI_AGENT_DIR_ENV}=${agentMount}`,
195
+ // Mount the seed READ-ONLY at a neutral path; the run command copies
196
+ // models.json into the container's own ~/.pi/agent so image extensions
197
+ // survive (see CONTAINER_RUN_CMD).
198
+ `${configSeed}:${CONTAINER_SEED_DIR}:ro`,
133
199
  env.image,
134
- 'pi',
200
+ 'sh',
201
+ '-c',
202
+ CONTAINER_RUN_CMD,
135
203
  ];
136
204
  return {
137
205
  workdir,
138
- sessionAgentDir: sessionDir,
139
206
  configSeed,
140
- agentMount,
141
- needsSeed,
142
207
  netcageArgs,
143
208
  };
144
209
  }
210
+ /**
211
+ * Absolute path to the Dockerfile.pi that ships with anon-pi, resolved from this
212
+ * module's location (package root, one level up from dist/anon-pi.js), or
213
+ * undefined if it cannot be found. Used only to make the missing-image error's
214
+ * build command concrete.
215
+ */
216
+ export function shippedDockerfilePath() {
217
+ try {
218
+ const here = dirname(fileURLToPath(import.meta.url));
219
+ // dist/anon-pi.js -> ../Dockerfile.pi; also try alongside for safety.
220
+ for (const p of [
221
+ join(here, '..', 'Dockerfile.pi'),
222
+ join(here, 'Dockerfile.pi'),
223
+ ]) {
224
+ if (existsSync(p))
225
+ return p;
226
+ }
227
+ }
228
+ catch {
229
+ // import.meta.url unavailable (e.g. some test bundlers): fall through.
230
+ }
231
+ return undefined;
232
+ }
145
233
  /** Read the AnonPiEnv from a process env map (kept separate so tests inject one). */
146
234
  export function envFromProcess(penv) {
147
235
  return {
@@ -152,38 +240,46 @@ export function envFromProcess(penv) {
152
240
  image: penv.ANON_PI_IMAGE,
153
241
  llmDirect: penv.ANON_PI_LLM,
154
242
  xdgConfigHome: penv.XDG_CONFIG_HOME,
155
- agentMount: penv.ANON_PI_AGENT_MOUNT,
243
+ dockerfilePath: shippedDockerfilePath(),
244
+ sourceModels: penv.ANON_PI_SOURCE_MODELS,
245
+ piAgentDir: penv.PI_CODING_AGENT_DIR,
156
246
  };
157
247
  }
158
248
  /** The --help text (kept here so it is covered by the same module). */
159
249
  export const HELP = `anon-pi - launch pi inside a netcage (anonymized egress + one direct local model)
160
250
 
161
251
  USAGE
162
- anon-pi [WORKDIR]
252
+ anon-pi [WORKDIR] launch pi jailed, working in WORKDIR (default: cwd)
253
+ anon-pi import write the seed models.json from your local model
163
254
 
164
- WORKDIR the host folder pi works in (mounted at /work). Defaults to the
165
- current directory. The session config+state is keyed to this folder.
255
+ WORKDIR the host folder pi works in (mounted at ${CONTAINER_WORKDIR}; pi's cwd). Files pi
256
+ writes there land on the host.
166
257
 
167
258
  WHAT IT DOES
168
- Seeds a per-workdir writable copy of your canonical anon-pi config into
169
- ~/.config/anon-pi/sessions/<hash>/agent, mounts it as pi's global config
170
- (${PI_AGENT_DIR_ENV}=<mount>, default ${DEFAULT_CONTAINER_AGENT_DIR}), mounts WORKDIR at
171
- ${CONTAINER_WORKDIR}, opens ONE direct hole to your local model, and runs pi with all other
172
- egress forced through the socks5h proxy, fail-closed. Requires \`netcage\`.
259
+ Runs pi inside netcage with all web/DNS egress forced through the socks5h
260
+ proxy (fail-closed) and ONE direct hole to your local model (ANON_PI_LLM).
261
+ The seed models.json is mounted read-only and COPIED into the container's own
262
+ ~/.pi/agent at start, so it layers onto the image's config: extensions and
263
+ skills you baked into the image survive. Requires \`netcage\`.
264
+
265
+ import
266
+ Reads your host ~/.pi/agent/models.json, picks the provider whose baseUrl
267
+ serves ANON_PI_LLM, and writes JUST that provider to the seed
268
+ (<ANON_PI_CONFIG>/models.json). No other provider's API keys, no sessions, no
269
+ identity. Re-run with --force to overwrite an existing seed.
173
270
 
174
271
  ENVIRONMENT
175
- ANON_PI_IMAGE (required) image with \`pi\` on PATH
272
+ ANON_PI_IMAGE (required for run) image with \`pi\` on PATH. No image yet?
273
+ Running anon-pi without it prints a ready-to-build
274
+ Dockerfile.pi recipe; see the README (Providing a pi image).
176
275
  ANON_PI_LLM (required) RFC1918/link-local IP[:port] of the local model
177
276
  ANON_PI_PROXY socks5h URL (default ${DEFAULT_PROXY})
178
277
  ANON_PI_HOME anon-pi home (default $XDG_CONFIG_HOME/anon-pi or ~/.config/anon-pi)
179
- ANON_PI_CONFIG canonical seed dir (default <ANON_PI_HOME>/agent)
180
- ANON_PI_AGENT_MOUNT absolute container path for pi's config (default
181
- ${DEFAULT_CONTAINER_AGENT_DIR}; set to your image's ~/.pi/agent, e.g. /root/.pi/agent)
278
+ ANON_PI_CONFIG seed dir holding models.json (default <ANON_PI_HOME>/agent)
279
+ ANON_PI_SOURCE_MODELS (import) host models.json to read (default ~/.pi/agent/models.json)
182
280
 
183
281
  RESEED
184
- Reseed is manual: delete the session dir, e.g.
185
- rm -rf ~/.config/anon-pi/sessions/<hash>/agent
186
- and the next run re-seeds it from the canonical config.
282
+ anon-pi import --force regenerates the seed models.json.
187
283
 
188
284
  PLATFORM
189
285
  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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"}
package/dist/cli.js CHANGED
@@ -1,17 +1,30 @@
1
1
  #!/usr/bin/env node
2
- // anon-pi CLI: resolve the run plan (pure), do the one filesystem side-effect
3
- // (seed the session config if absent), then exec `netcage run ...` with inherited
4
- // stdio so the interactive pi session (-it) passes through the terminal cleanly.
5
- import { cpSync, existsSync, mkdirSync } from 'node:fs';
2
+ // anon-pi CLI. Two commands:
3
+ // anon-pi [WORKDIR] resolve the run plan (pure) and exec `netcage run ...`
4
+ // with inherited stdio (so -it is a real interactive TTY).
5
+ // The seed models.json is mounted read-only and copied
6
+ // into the container's ~/.pi/agent by the run command, so
7
+ // it layers onto the image's config (extensions survive).
8
+ // anon-pi import generate the seed models.json from the host models.json,
9
+ // carrying only the provider that serves ANON_PI_LLM.
10
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
6
11
  import { spawnSync } from 'node:child_process';
7
- import { dirname } from 'node:path';
8
- import { AnonPiError, buildRunPlan, envFromProcess, HELP } from './anon-pi.js';
12
+ import { join } from 'node:path';
13
+ import { AnonPiError, buildRunPlan, envFromProcess, HELP, MODELS_FILE, pickProviderForLlm, resolveConfigSeed, resolveSourceModelsPath, } from './anon-pi.js';
9
14
  function main(argv) {
10
15
  const args = argv.slice(2);
11
16
  if (args.includes('--help') || args.includes('-h')) {
12
17
  process.stdout.write(HELP);
13
18
  return 0;
14
19
  }
20
+ // Subcommand dispatch: the first bare token may be `import`.
21
+ if (args[0] === 'import') {
22
+ return runImport(args.slice(1));
23
+ }
24
+ return runLaunch(args);
25
+ }
26
+ // --- anon-pi [WORKDIR] : launch pi jailed -----------------------------------
27
+ function runLaunch(args) {
15
28
  // The only positional is the optional workdir. Reject stray flags so a typo
16
29
  // (e.g. --allow-direct) is not silently swallowed: anon-pi owns the netcage
17
30
  // argv, extra flags are not passed through.
@@ -28,7 +41,7 @@ function main(argv) {
28
41
  const env = envFromProcess(process.env);
29
42
  let plan;
30
43
  try {
31
- plan = buildRunPlan(env, positionals[0], existsSync, existsSync);
44
+ plan = buildRunPlan(env, positionals[0], existsSync);
32
45
  }
33
46
  catch (e) {
34
47
  if (e instanceof AnonPiError) {
@@ -43,13 +56,6 @@ function main(argv) {
43
56
  '(https://github.com/wighawag/netcage). Linux only.\n');
44
57
  return 1;
45
58
  }
46
- // The one side-effect: seed the per-session config from the canonical seed the
47
- // FIRST time this workdir is used. Reuse-if-present, seed-if-absent.
48
- if (plan.needsSeed) {
49
- mkdirSync(dirname(plan.sessionAgentDir), { recursive: true });
50
- cpSync(plan.configSeed, plan.sessionAgentDir, { recursive: true });
51
- process.stderr.write(`anon-pi: seeded session config -> ${plan.sessionAgentDir}\n`);
52
- }
53
59
  // Ensure the workdir exists (a fresh named folder is fine).
54
60
  mkdirSync(plan.workdir, { recursive: true });
55
61
  // Hand off to netcage with inherited stdio so -it is a real interactive TTY.
@@ -61,6 +67,61 @@ function main(argv) {
61
67
  // Propagate netcage's exit code (which itself propagates the tool's).
62
68
  return res.status ?? 1;
63
69
  }
70
+ // --- anon-pi import : write the seed models.json ----------------------------
71
+ function runImport(args) {
72
+ const force = args.includes('--force') || args.includes('-f');
73
+ const stray = args.filter((a) => a.startsWith('-') && a !== '--force' && a !== '-f');
74
+ if (stray.length > 0) {
75
+ process.stderr.write(`anon-pi import: unknown option(s): ${stray.join(' ')}\nRun \`anon-pi --help\`.\n`);
76
+ return 2;
77
+ }
78
+ const env = envFromProcess(process.env);
79
+ if (!env.llmDirect || env.llmDirect.trim() === '') {
80
+ process.stderr.write('anon-pi import: set ANON_PI_LLM to the RFC1918/link-local IP[:port] of the local\n' +
81
+ 'model whose provider should be imported (e.g. ANON_PI_LLM=192.168.1.150:8080).\n');
82
+ return 1;
83
+ }
84
+ const source = resolveSourceModelsPath(env);
85
+ if (!existsSync(source)) {
86
+ process.stderr.write(`anon-pi import: host models.json not found at ${source}.\n` +
87
+ 'Set ANON_PI_SOURCE_MODELS to your pi models.json, or run pi once to create it.\n');
88
+ return 1;
89
+ }
90
+ let hostModels;
91
+ try {
92
+ hostModels = JSON.parse(readFileSync(source, 'utf8'));
93
+ }
94
+ catch (e) {
95
+ process.stderr.write(`anon-pi import: could not parse ${source}: ${e.message}\n`);
96
+ return 1;
97
+ }
98
+ let result;
99
+ try {
100
+ result = pickProviderForLlm(hostModels, env.llmDirect);
101
+ }
102
+ catch (e) {
103
+ if (e instanceof AnonPiError) {
104
+ process.stderr.write(e.message + '\n');
105
+ return 1;
106
+ }
107
+ throw e;
108
+ }
109
+ const seedDir = resolveConfigSeed(env);
110
+ const dest = join(seedDir, MODELS_FILE);
111
+ if (existsSync(dest) && !force) {
112
+ process.stderr.write(`anon-pi import: ${dest} already exists. Re-run with --force to overwrite.\n`);
113
+ return 1;
114
+ }
115
+ if (result.apiKeyLooksReal) {
116
+ process.stderr.write(`anon-pi import: WARNING: provider "${result.name}" carries a real-looking apiKey; it\n` +
117
+ 'will be written into the seed. For a local model this is usually fine, but review\n' +
118
+ `${dest} if that key identifies you.\n`);
119
+ }
120
+ mkdirSync(seedDir, { recursive: true });
121
+ writeFileSync(dest, JSON.stringify(result.models, null, 2) + '\n');
122
+ process.stderr.write(`anon-pi import: wrote ${dest} (provider "${result.name}"). Run \`anon-pi\` to launch.\n`);
123
+ return 0;
124
+ }
64
125
  function hasNetcage() {
65
126
  const which = spawnSync(process.platform === 'win32' ? 'where' : 'command', ['-v', 'netcage'], {
66
127
  stdio: 'ignore',
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,94 @@
1
+ # A fuller ANON_PI_IMAGE example: pi + the pi-webveil extension + a local SearXNG
2
+ # (over a Unix socket) for anonymized web search, baked into ONE image (the
3
+ # capability layer). Modelled on a real working host setup (Unix socket,
4
+ # http-socket, json+limiter:false, unix: baseUrl, egress: direct).
5
+ #
6
+ # STATUS: a documented REFERENCE, not a CI-built image. The topology matches a
7
+ # working host, but pin versions and test the build for your platform before
8
+ # relying on it (SearXNG's pip install + engine set drift over time).
9
+ #
10
+ # WHY THE USUAL SearXNG ANONYMITY CAVEAT DOES NOT APPLY HERE:
11
+ # webveil warns that a LOCAL SearXNG crawls the web from its OWN process egress
12
+ # (direct), so a socks5 backend hop to 127.0.0.1 is FALSE confidence. Under
13
+ # anon-pi that risk is gone: netcage forces ALL TCP+DNS egress from EVERY
14
+ # process in the jail through the socks5h proxy, fail-closed. SearXNG's crawl is
15
+ # anonymized by the network layer regardless. So we use webveil's plain
16
+ # `egress: direct` and let netcage do the anonymizing.
17
+ #
18
+ # WHY A UNIX SOCKET (matches the host setup, avoids the loopback-TCP guard):
19
+ # webveil's `unix:` baseUrl speaks HTTP over a Unix socket, so uWSGI must use
20
+ # `http-socket = <path>` (NOT `socket = <path>`, which is the native uwsgi
21
+ # protocol webveil cannot speak). SearXNG also needs `json` in search.formats
22
+ # and `server.limiter: false` for a local instance.
23
+ #
24
+ # Build:
25
+ # podman build -t localhost/anon-pi-webveil:latest -f Dockerfile.pi-webveil .
26
+ # export ANON_PI_IMAGE=localhost/anon-pi-webveil:latest
27
+ # Then: `anon-pi import` (your local model), then `anon-pi <workdir>`.
28
+ FROM node:24-bookworm-slim
29
+
30
+ # --- system deps: pi's tools + SearXNG's Python/uwsgi runtime ----------------
31
+ RUN apt-get update \
32
+ && apt-get install -y --no-install-recommends \
33
+ bash ca-certificates git ripgrep \
34
+ python3 python3-venv python3-pip python3-dev \
35
+ build-essential libxml2-dev libxslt1-dev zlib1g-dev libffi-dev libssl-dev \
36
+ uwsgi uwsgi-plugin-python3 \
37
+ && rm -rf /var/lib/apt/lists/*
38
+
39
+ # --- pi + the pi-webveil extension (via `pi install`, which records it) -------
40
+ RUN npm install -g --ignore-scripts @earendil-works/pi-coding-agent
41
+ RUN pi install npm:pi-webveil
42
+
43
+ # --- SearXNG in a venv -------------------------------------------------------
44
+ ENV SEARXNG_HOME=/opt/searxng
45
+ RUN python3 -m venv "$SEARXNG_HOME/venv" \
46
+ && "$SEARXNG_HOME/venv/bin/pip" install --no-cache-dir -U pip setuptools wheel \
47
+ && "$SEARXNG_HOME/venv/bin/pip" install --no-cache-dir searxng
48
+
49
+ # settings.yml: JSON API on + limiter off (both required for a local instance).
50
+ RUN mkdir -p /etc/searxng \
51
+ && printf '%s\n' \
52
+ 'use_default_settings: true' \
53
+ 'server:' \
54
+ ' secret_key: "change-me-anon-pi"' \
55
+ ' limiter: false' \
56
+ ' public_instance: false' \
57
+ 'search:' \
58
+ ' formats: [html, json]' \
59
+ > /etc/searxng/settings.yml
60
+ ENV SEARXNG_SETTINGS_PATH=/etc/searxng/settings.yml
61
+
62
+ # webveil.json (global): point at the Unix socket, backend hop DIRECT (netcage
63
+ # anonymizes it). fetchEgress is left DIRECT too, since netcage forces the fetch
64
+ # hop through the proxy as well; no webveil-side proxy config is needed in-jail.
65
+ RUN mkdir -p /root/.pi/agent \
66
+ && printf '%s\n' \
67
+ '{' \
68
+ ' "backend": "searxng",' \
69
+ ' "baseUrl": "unix:/run/searxng/socket",' \
70
+ ' "egress": { "mode": "direct" }' \
71
+ '}' \
72
+ > /root/.pi/agent/webveil.json \
73
+ && printf '{"/work": true}\n' > /root/.pi/agent/trust.json
74
+
75
+ # --- entrypoint: start SearXNG (HTTP over the Unix socket), then exec CMD ------
76
+ # anon-pi passes `sh -c 'cp ... && exec pi'` as the CMD; a Dockerfile ENTRYPOINT
77
+ # is PREPENDED to it (OCI), so this wrapper starts SearXNG then execs anon-pi's
78
+ # command. `http-socket` (not `socket`) makes uWSGI speak HTTP over the socket,
79
+ # which is what webveil's `unix:` baseUrl requires.
80
+ RUN mkdir -p /run/searxng && printf '%s\n' \
81
+ '#!/bin/sh' \
82
+ 'set -e' \
83
+ 'uwsgi --master --plugin python3,http \' \
84
+ ' --http-socket /run/searxng/socket --chmod-socket=666 \' \
85
+ ' --venv "$SEARXNG_HOME/venv" --module searx.webapp \' \
86
+ ' --daemonize /tmp/searxng.log' \
87
+ '# Wait for the socket to appear (bounded).' \
88
+ 'i=0; while [ ! -S /run/searxng/socket ] && [ $i -lt 30 ]; do i=$((i+1)); sleep 0.5; done' \
89
+ 'exec "$@"' \
90
+ > /usr/local/bin/anon-pi-entrypoint \
91
+ && chmod +x /usr/local/bin/anon-pi-entrypoint
92
+ ENTRYPOINT ["/usr/local/bin/anon-pi-entrypoint"]
93
+
94
+ WORKDIR /work
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "anon-pi",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Launch pi inside a netcage: anonymized web egress through a socks5h proxy, one direct hole for a local model, seeded pi config on the host.",
5
5
  "license": "AGPL-3.0-only",
6
6
  "keywords": [
@@ -40,7 +40,8 @@
40
40
  "dist",
41
41
  "src",
42
42
  "LICENSE",
43
- "Dockerfile.pi"
43
+ "Dockerfile.pi",
44
+ "examples"
44
45
  ],
45
46
  "engines": {
46
47
  "node": ">=20"