solrouter 0.1.0 → 0.2.1

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/README.md CHANGED
@@ -44,11 +44,15 @@ private leg → SolRouter uncensored model (auth: "solrouter", needs logi
44
44
  ## Install & run
45
45
 
46
46
  ```bash
47
- npx solrouter # or: npm i -g solrouter
48
- solrouter start # runs on http://127.0.0.1:8787 — uses your existing key
49
- solrouter login # opens the browser — sign in with your subscription
47
+ npx solrouter login # opens the browser — sign in with your subscription
48
+ npx solrouter code # starts the proxy AND launches Claude Code through it, one tab
50
49
  ```
51
50
 
51
+ `solrouter code` runs the proxy in-process and launches Claude Code already pointed
52
+ at it (same tab); when you quit Claude Code the proxy stops. Pass Claude Code args
53
+ straight through: `npx solrouter code --resume`. To run the proxy on its own instead
54
+ (point another tool at `http://127.0.0.1:8787`), use `npx solrouter start`.
55
+
52
56
  `login` opens `solrouter.com/cli/auth`, you sign in with your SolRouter
53
57
  subscription, and a key is minted and handed back to the CLI automatically — no
54
58
  paste. (`login --token sk_solrouter_...` still works for CI / headless.) The private
package/bin/proxy.js CHANGED
@@ -72,6 +72,36 @@ async function main() {
72
72
  return;
73
73
  }
74
74
 
75
+ // `solrouter code [claude args...]` — the one-command path: start the proxy
76
+ // in THIS process, then launch Claude Code already pointed at it, in the same
77
+ // tab. When Claude Code exits, so does the proxy. (`claude` is the alias.)
78
+ case "code":
79
+ case "claude": {
80
+ const cfg = loadConfig({ port: flag("--port") ? Number(flag("--port")) : undefined,
81
+ mode: flag("--mode") || undefined });
82
+ const { spawn } = await import("node:child_process");
83
+ startServer(cfg); // non-blocking listener in this process
84
+ // Pass everything after `code`/`claude` (minus our own --port/--mode) to Claude Code.
85
+ const passThru = rest.filter((a, i) =>
86
+ !["--port", "--mode"].includes(a) && !["--port", "--mode"].includes(rest[i - 1]));
87
+ const tool = process.env.SOLROUTER_TOOL || "claude";
88
+ console.log(`\nLaunching ${tool} through the proxy (ANTHROPIC_BASE_URL=http://127.0.0.1:${cfg.port})…\n`);
89
+ const child = spawn(tool, passThru, {
90
+ stdio: "inherit",
91
+ env: {
92
+ ...process.env,
93
+ ANTHROPIC_BASE_URL: `http://127.0.0.1:${cfg.port}`,
94
+ OPENAI_BASE_URL: `http://127.0.0.1:${cfg.port}/v1`,
95
+ },
96
+ });
97
+ child.on("error", e => {
98
+ console.error(e.code === "ENOENT" ? `Couldn't find \`${tool}\` on your PATH.` : e.message);
99
+ process.exit(1);
100
+ });
101
+ child.on("exit", code => process.exit(code ?? 0));
102
+ return;
103
+ }
104
+
75
105
  case "start":
76
106
  default: {
77
107
  const cfg = loadConfig({ port: flag("--port") ? Number(flag("--port")) : undefined,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "solrouter",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Local privacy router for coding tools: pseudonymises every request before it leaves your machine, and sends work a frontier model would refuse to an uncensored open-weight model you control.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/config.js CHANGED
@@ -53,6 +53,7 @@ function readJson(path) {
53
53
  function deepMerge(a, b) {
54
54
  const out = { ...a };
55
55
  for (const k of Object.keys(b || {})) {
56
+ if (b[k] === undefined) continue; // never let an unset override clobber a default
56
57
  out[k] = b[k] && typeof b[k] === "object" && !Array.isArray(b[k])
57
58
  ? deepMerge(a[k] || {}, b[k]) : b[k];
58
59
  }
package/src/server.js CHANGED
@@ -68,6 +68,15 @@ export function startServer(cfg) {
68
68
  }
69
69
  });
70
70
 
71
+ server.on("error", (e) => {
72
+ if (e.code === "EADDRINUSE") {
73
+ // A proxy is already listening here — reuse it rather than crash.
74
+ console.log(`SolRouter proxy already running on http://127.0.0.1:${cfg.port} — reusing it.`);
75
+ } else {
76
+ console.error("proxy server error:", e.message);
77
+ process.exit(1);
78
+ }
79
+ });
71
80
  server.listen(cfg.port, "127.0.0.1", () => {
72
81
  console.log(`SolRouter proxy on http://127.0.0.1:${cfg.port}`);
73
82
  console.log(` mode=${cfg.mode} frontier=${cfg.frontier.model || "(client's model)"} [${cfg.frontier.auth || "passthrough"}] private=${cfg.private.model}`);