pennyrouter 0.3.18 → 0.3.19

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
@@ -29,7 +29,7 @@ Installing against a self-hosted gateway is its own flow, with no browser login:
29
29
  keeps its own accounts, so the key is minted there.
30
30
 
31
31
  ```bash
32
- npx pennyrouter install --private-gateway https://gateway.example.internal
32
+ npx pennyrouter install --private-gateway https://penny-router.apps-dev.gtri.org
33
33
  ```
34
34
 
35
35
  It asks for the gateway URL (unless given) and your email, and mints a key against that
@@ -44,11 +44,11 @@ ones:
44
44
  ```json
45
45
  {
46
46
  "harnesses": ["claude-code", "codex"],
47
- "gatewayBaseUrl": "https://gateway.example.internal",
47
+ "gatewayBaseUrl": "https://penny-router.apps-dev.gtri.org",
48
48
  "claudeFunding": ["anthropic-proxy"],
49
- "proxyBaseUrl": "https://litellm.example.internal",
49
+ "anthropicProxyBaseUrl": "https://litellm.core.gtri.org/v1",
50
50
  "codexFunding": ["openai-proxy"],
51
- "openaiProxyBaseUrl": "https://litellm.example.internal"
51
+ "openaiProxyBaseUrl": "https://litellm.core.gtri.org/v1"
52
52
  }
53
53
  ```
54
54
 
@@ -57,6 +57,13 @@ npx pennyrouter install --config team-install.json
57
57
  ```
58
58
 
59
59
  Each person is prompted for their email and their proxy key; everything else is prefilled.
60
+
61
+ For clarity, `proxyBaseUrl`/`proxyApiKey` are the legacy names for the Claude/Anthropic
62
+ wire lane. New files should use `anthropicProxyBaseUrl`/`anthropicProxyApiKey`; Codex uses
63
+ `openaiProxyBaseUrl`/`openaiProxyApiKey`. The `--private-server` spelling is a compatibility
64
+ alias for `--private-gateway`.
65
+
66
+
60
67
  Any field left out of the file is prompted for, and an unattended run that cannot prompt
61
68
  refuses by name rather than installing a half-configured harness. Add `email`, `proxyApiKey`
62
69
  and `openaiProxyApiKey` to the file to skip the prompts entirely.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.3.18",
3
+ "version": "0.3.19",
4
4
  "description": "Install and manage PennyRouter local coding-agent integrations.",
5
5
  "homepage": "https://pennyrouter.com",
6
6
  "bugs": {
package/src/cli.js CHANGED
@@ -234,6 +234,10 @@ function parseArgs(argv) {
234
234
  else if (arg.startsWith("--openai-proxy-base-url=")) flags.openaiProxyBaseUrl = arg.slice("--openai-proxy-base-url=".length);
235
235
  else if (arg === "--openai-proxy-api-key") flags.openaiProxyApiKey = rest[++i] || "";
236
236
  else if (arg.startsWith("--openai-proxy-api-key=")) flags.openaiProxyApiKey = arg.slice("--openai-proxy-api-key=".length);
237
+ else if (arg === "--anthropic-proxy-base-url") flags.proxyBaseUrl = rest[++i] || "";
238
+ else if (arg.startsWith("--anthropic-proxy-base-url=")) flags.proxyBaseUrl = arg.slice("--anthropic-proxy-base-url=".length);
239
+ else if (arg === "--anthropic-proxy-api-key") flags.proxyApiKey = rest[++i] || "";
240
+ else if (arg.startsWith("--anthropic-proxy-api-key=")) flags.proxyApiKey = arg.slice("--anthropic-proxy-api-key=".length);
237
241
  else if (arg === "--proxy-base-url") flags.proxyBaseUrl = rest[++i] || "";
238
242
  else if (arg.startsWith("--proxy-base-url=")) flags.proxyBaseUrl = arg.slice("--proxy-base-url=".length);
239
243
  else if (arg === "--proxy-api-key") flags.proxyApiKey = rest[++i] || "";
@@ -250,17 +254,17 @@ function parseArgs(argv) {
250
254
  // --gateway-base-url, which expects the URL and an admin-issued key up front, this asks
251
255
  // for what it needs (gateway URL, then email) and mints the key from the gateway itself,
252
256
  // so a user on a VPN can install without an administrator handing them a key first.
253
- else if (arg === "--private-gateway") {
257
+ else if (arg === "--private-gateway" || arg === "--private-server") {
254
258
  flags.privateGatewayFlow = true;
255
259
  flags.privateGateway = true;
256
260
  // Optional inline URL: `--private-gateway https://...` rather than a prompt. Only
257
261
  // consume the next argument when it is not itself a flag.
258
262
  if (rest[i + 1] && !rest[i + 1].startsWith("-")) flags.gatewayBaseUrl = rest[++i];
259
263
  }
260
- else if (arg.startsWith("--private-gateway=")) {
264
+ else if (arg.startsWith("--private-gateway=") || arg.startsWith("--private-server=")) {
261
265
  flags.privateGatewayFlow = true;
262
266
  flags.privateGateway = true;
263
- flags.gatewayBaseUrl = arg.slice("--private-gateway=".length);
267
+ flags.gatewayBaseUrl = arg.slice(arg.indexOf("=") + 1);
264
268
  }
265
269
  // Email the private gateway mints the key against; prompted for when absent.
266
270
  else if (arg === "--email") flags.email = rest[++i] || "";
@@ -328,6 +332,15 @@ export function applyConfigFile(flags) {
328
332
  if (config.harnesses && flags.harness === undefined) {
329
333
  flags.harness = config.harnesses.join(",");
330
334
  }
335
+ // Migrate the ambiguous Claude proxy keys in-memory. Existing shared install files remain
336
+ // valid while new files can say explicitly which wire lane they configure.
337
+ if (config.anthropicProxyBaseUrl === undefined && config.proxyBaseUrl !== undefined) {
338
+ config.anthropicProxyBaseUrl = config.proxyBaseUrl;
339
+ }
340
+ if (config.anthropicProxyApiKey === undefined && config.proxyApiKey !== undefined) {
341
+ config.anthropicProxyApiKey = config.proxyApiKey;
342
+ }
343
+
331
344
  const claudeFunding = new Set(config.claudeFunding || []);
332
345
  const codexFunding = new Set(config.codexFunding || []);
333
346
  if (claudeFunding.has("subscription") && flags.anthropicAuth === undefined) {
@@ -358,6 +371,15 @@ export function applyConfigFile(flags) {
358
371
  const value = String(config[field.config_key] || "").trim();
359
372
  if (value && flags[field.config_key] === undefined) flags[field.config_key] = value;
360
373
  }
374
+ // ``proxy*`` was the original Claude/Anthropic naming. Keep it as an input alias, but
375
+ // normalize the explicit JSON names back to the internal flags used by the installer.
376
+ if (flags.proxyBaseUrl === undefined && flags.anthropicProxyBaseUrl !== undefined) {
377
+ flags.proxyBaseUrl = flags.anthropicProxyBaseUrl;
378
+ }
379
+ if (flags.proxyApiKey === undefined && flags.anthropicProxyApiKey !== undefined) {
380
+ flags.proxyApiKey = flags.anthropicProxyApiKey;
381
+ }
382
+
361
383
  if (flags.gatewayBaseUrl) {
362
384
  flags.privateGateway = true;
363
385
  // Take the self-hosted install path rather than the hosted browser claim, whose key is
@@ -1423,8 +1445,10 @@ async function selectClaudeFundingProviders(flags, selected) {
1423
1445
  || process.env.ANTHROPIC_AUTH_TOKEN)) supplied.add("subscription");
1424
1446
  if (flags.anthropicApiKey || process.env.ANTHROPIC_API_KEY) supplied.add("anthropic-api");
1425
1447
  if (flags.openrouterApiKey || process.env.OPENROUTER_API_KEY) supplied.add("openrouter");
1426
- if ((flags.proxyBaseUrl || process.env.PENNYROUTER_PROXY_BASE_URL)
1427
- && (flags.proxyApiKey || process.env.PENNYROUTER_PROXY_API_KEY)) {
1448
+ if ((flags.proxyBaseUrl || process.env.PENNYROUTER_ANTHROPIC_PROXY_BASE_URL
1449
+ || process.env.PENNYROUTER_PROXY_BASE_URL)
1450
+ && (flags.proxyApiKey || process.env.PENNYROUTER_ANTHROPIC_PROXY_API_KEY
1451
+ || process.env.PENNYROUTER_PROXY_API_KEY)) {
1428
1452
  supplied.add("anthropic-proxy");
1429
1453
  }
1430
1454
  // A --config file states its providers outright, and that choice stands whether or not the
@@ -1559,10 +1583,14 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
1559
1583
  flags.openaiProxyApiKey || process.env.PENNYROUTER_OPENAI_PROXY_API_KEY || "",
1560
1584
  ).trim();
1561
1585
  let proxyBaseUrl = String(
1562
- flags.proxyBaseUrl || process.env.PENNYROUTER_PROXY_BASE_URL || "",
1586
+ flags.proxyBaseUrl || flags.anthropicProxyBaseUrl
1587
+ || process.env.PENNYROUTER_ANTHROPIC_PROXY_BASE_URL
1588
+ || process.env.PENNYROUTER_PROXY_BASE_URL || "",
1563
1589
  ).trim();
1564
1590
  let proxyApiKey = String(
1565
- flags.proxyApiKey || process.env.PENNYROUTER_PROXY_API_KEY || "",
1591
+ flags.proxyApiKey || flags.anthropicProxyApiKey
1592
+ || process.env.PENNYROUTER_ANTHROPIC_PROXY_API_KEY
1593
+ || process.env.PENNYROUTER_PROXY_API_KEY || "",
1566
1594
  ).trim();
1567
1595
  const codexSubscriptionAuth = wantsCodex && codexProviders.has("subscription")
1568
1596
  ? await detectCodexChatgptAuth()
@@ -2401,7 +2429,7 @@ function printHelp() {
2401
2429
  console.log(`PennyRouter CLI
2402
2430
 
2403
2431
  Usage:
2404
- pennyrouter install [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--yes] [--dry-run] [--make-default|--penny-only] [--no-path-update] [--no-mcp] [--anthropic-auth|--no-anthropic-auth] [--anthropic-api-key KEY] [--openai-api-key KEY] [--openai-proxy-base-url URL --openai-proxy-api-key KEY] [--openrouter-api-key KEY] [--proxy-base-url URL --proxy-api-key KEY] [--private-gateway[=URL] [--email you@example.com]] [--gateway-base-url URL] [--private-key pr-...] [--local[=URL]|--prod] [--config PATH]
2432
+ pennyrouter install [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--yes] [--dry-run] [--make-default|--penny-only] [--no-path-update] [--no-mcp] [--anthropic-auth|--no-anthropic-auth] [--anthropic-api-key KEY] [--openai-api-key KEY] [--openai-proxy-base-url URL --openai-proxy-api-key KEY] [--openrouter-api-key KEY] [--anthropic-proxy-base-url URL --anthropic-proxy-api-key KEY] [--proxy-base-url URL --proxy-api-key KEY] [--private-gateway|--private-server[=URL] [--email you@example.com]] [--gateway-base-url URL] [--private-key pr-...] [--local[=URL]|--prod] [--config PATH]
2405
2433
  pennyrouter update [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--dry-run] [--penny-key pr-...] [--gateway-base-url URL|--local[=URL]|--prod] [--no-mcp]
2406
2434
  pennyrouter auth anthropic [--penny-key pr-...] [--token oauth-token] [--token-command CMD] [--gateway-base-url URL] [--forget]
2407
2435
  pennyrouter disable [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--yes]
@@ -56,12 +56,12 @@
56
56
  "name": "Proxy (e.g., Bedrock, Azure, private server)",
57
57
  "fields": [
58
58
  {
59
- "config_key": "proxyBaseUrl",
60
- "label": "Proxy base URL",
59
+ "config_key": "anthropicProxyBaseUrl",
60
+ "label": "Anthropic proxy base URL",
61
61
  "secret": false,
62
62
  "placeholder": "https://proxy.example.com"
63
63
  },
64
- { "config_key": "proxyApiKey", "label": "Proxy API key", "secret": true }
64
+ { "config_key": "anthropicProxyApiKey", "label": "Anthropic proxy API key", "secret": true }
65
65
  ]
66
66
  }
67
67
  ]