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 +11 -4
- package/package.json +1 -1
- package/src/cli.js +36 -8
- package/src/install-manifest.json +3 -3
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://
|
|
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://
|
|
47
|
+
"gatewayBaseUrl": "https://penny-router.apps-dev.gtri.org",
|
|
48
48
|
"claudeFunding": ["anthropic-proxy"],
|
|
49
|
-
"
|
|
49
|
+
"anthropicProxyBaseUrl": "https://litellm.core.gtri.org/v1",
|
|
50
50
|
"codexFunding": ["openai-proxy"],
|
|
51
|
-
"openaiProxyBaseUrl": "https://litellm.
|
|
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
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("
|
|
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.
|
|
1427
|
-
|
|
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 ||
|
|
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 ||
|
|
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": "
|
|
60
|
-
"label": "
|
|
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": "
|
|
64
|
+
{ "config_key": "anthropicProxyApiKey", "label": "Anthropic proxy API key", "secret": true }
|
|
65
65
|
]
|
|
66
66
|
}
|
|
67
67
|
]
|