Files
pxpipe/scripts/install.js
T
teamchong 9fddbeb28a Initial commit: working Python proxy
Token-saving HTTP proxy for Claude Code that renders system prompt + tools
as 5pt Menlo monospace images. Achieves 65-73% token reduction with 100%
reasoning quality preserved on Opus 4.7.

Production state at this commit:
- src/proxy.py: 972 lines, battle-tested with 46+ real Claude Code sessions
  observed (~551k tokens saved, ~$8.27 in stats.json as of commit time)
- bin/cli.js: Node wrapper that spawns Python proxy on port 47821
- scripts/gen_atlas.py: generates the bundled Menlo 5pt glyph atlas
- scripts/install.js: postinstall — verifies Python + Pillow + httpx
- src/zig/: earlier Zig 0.16 renderer (kept for reference, not built)
- CLAUDE.md, HANDOFF.md: contributor notes
- package.json: npm entry point, version 0.1.0

Known issue (deferred for separate fix):
  src/proxy.py:125 — `tallest = max(lines_per_col, last_col_lines) if
  c_needed == 1 else lines_per_col` pads every single-column image to
  full MAX_EDGE=1568 height regardless of content, wasting image-token
  budget for small inputs. Tight bounding would improve savings further.

This is the rollback point. Any subsequent changes (Rust port, Python
fixes, etc.) build on this proven baseline.
2026-05-18 11:52:02 -04:00

51 lines
1.8 KiB
JavaScript
Executable File

#!/usr/bin/env node
/**
* Postinstall: verify Python 3.8+ and install Pillow + httpx (the only deps).
* Idempotent — safe to run on reinstall.
*/
"use strict";
const { spawnSync } = require("child_process");
function findPython() {
const candidates = ["python3", "python", "python3.12", "python3.11", "python3.10", "python3.9", "python3.8"];
for (const c of candidates) {
const r = spawnSync(c, ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
if (r.status === 0) {
const ver = (r.stdout || r.stderr || Buffer.from("")).toString();
const m = ver.match(/Python (\d+)\.(\d+)/);
if (m && parseInt(m[1], 10) >= 3 && parseInt(m[2], 10) >= 8) return c;
}
}
return null;
}
function hasModule(py, mod) {
const r = spawnSync(py, ["-c", `import ${mod}`], { stdio: ["ignore", "pipe", "pipe"] });
return r.status === 0;
}
const py = findPython();
if (!py) {
console.warn("\n[claude-image-proxy] WARNING: Python 3.8+ not found.");
console.warn(" Install Python 3 (https://www.python.org/downloads/) then run:");
console.warn(" python3 -m pip install Pillow httpx");
console.warn(" Otherwise the proxy will fail to start.\n");
process.exit(0); // don't break npm install
}
const need = [];
if (!hasModule(py, "PIL")) need.push("Pillow");
if (!hasModule(py, "httpx")) need.push("httpx");
if (need.length === 0) {
console.log("[claude-image-proxy] Python deps already installed.");
process.exit(0);
}
console.log(`[claude-image-proxy] Installing Python deps: ${need.join(", ")}`);
const args = ["-m", "pip", "install", "--quiet", "--user", ...need];
const r = spawnSync(py, args, { stdio: "inherit" });
if (r.status !== 0) {
console.warn("\n[claude-image-proxy] WARNING: failed to auto-install Python deps.");
console.warn(` Please run manually: ${py} -m pip install ${need.join(" ")}`);
}