pennyrouter 0.2.34 → 0.2.35
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/package.json +1 -1
- package/src/cli.js +31 -8
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -78,6 +78,7 @@ const CLAUDE_PROVIDER_OPTIONS = [
|
|
|
78
78
|
const CODEX_PROVIDER_OPTIONS = [
|
|
79
79
|
{ id: "subscription", name: "ChatGPT subscription" },
|
|
80
80
|
{ id: "openai-api", name: "OpenAI API key" },
|
|
81
|
+
{ id: "openai-proxy", name: "Other OpenAI-compatible proxy" },
|
|
81
82
|
{ id: "openrouter", name: "OpenRouter API key" },
|
|
82
83
|
];
|
|
83
84
|
|
|
@@ -171,6 +172,10 @@ function parseArgs(argv) {
|
|
|
171
172
|
else if (arg === "--openai-api-key") flags.openaiApiKey = rest[++i] || "";
|
|
172
173
|
else if (arg.startsWith("--openai-api-key=")) flags.openaiApiKey = arg.slice("--openai-api-key=".length);
|
|
173
174
|
else if (arg === "--openrouter-api-key") flags.openrouterApiKey = rest[++i] || "";
|
|
175
|
+
else if (arg === "--openai-proxy-base-url") flags.openaiProxyBaseUrl = rest[++i] || "";
|
|
176
|
+
else if (arg.startsWith("--openai-proxy-base-url=")) flags.openaiProxyBaseUrl = arg.slice("--openai-proxy-base-url=".length);
|
|
177
|
+
else if (arg === "--openai-proxy-api-key") flags.openaiProxyApiKey = rest[++i] || "";
|
|
178
|
+
else if (arg.startsWith("--openai-proxy-api-key=")) flags.openaiProxyApiKey = arg.slice("--openai-proxy-api-key=".length);
|
|
174
179
|
else if (arg === "--proxy-base-url") flags.proxyBaseUrl = rest[++i] || "";
|
|
175
180
|
else if (arg.startsWith("--proxy-base-url=")) flags.proxyBaseUrl = arg.slice("--proxy-base-url=".length);
|
|
176
181
|
else if (arg === "--proxy-api-key") flags.proxyApiKey = rest[++i] || "";
|
|
@@ -598,7 +603,7 @@ export async function install(flags = {}) {
|
|
|
598
603
|
// keeps the hosted default, while a supplied URL is used for credential storage,
|
|
599
604
|
// validation, probing, and the installed harness configuration. `--local` remains the
|
|
600
605
|
// explicit localhost:8400 developer shortcut and skips this prompt.
|
|
601
|
-
if (claudeProviders.has("anthropic-proxy") && !flags.gatewayBaseUrl
|
|
606
|
+
if ((claudeProviders.has("anthropic-proxy") || codexProviders.has("openai-proxy")) && !flags.gatewayBaseUrl
|
|
602
607
|
&& !flags.yes && input.isTTY && output.isTTY) {
|
|
603
608
|
const privateGateway = (await promptText(
|
|
604
609
|
"PennyRouter gateway URL for this proxy (blank for hosted gateway): ",
|
|
@@ -1120,14 +1125,16 @@ async function selectCodexFundingProviders(flags, selected) {
|
|
|
1120
1125
|
|
|
1121
1126
|
const supplied = new Set();
|
|
1122
1127
|
if (flags.openaiApiKey || process.env.OPENAI_API_KEY) supplied.add("openai-api");
|
|
1128
|
+
if ((flags.openaiProxyBaseUrl || process.env.PENNYROUTER_OPENAI_PROXY_BASE_URL)
|
|
1129
|
+
&& (flags.openaiProxyApiKey || process.env.PENNYROUTER_OPENAI_PROXY_API_KEY)) supplied.add("openai-proxy");
|
|
1123
1130
|
if (flags.openrouterApiKey || process.env.OPENROUTER_API_KEY) supplied.add("openrouter");
|
|
1124
1131
|
const hasChatgptLogin = await detectCodexChatgptAuth();
|
|
1125
1132
|
if (hasChatgptLogin) supplied.add("subscription");
|
|
1126
1133
|
|
|
1127
|
-
const explicit = flags.openaiApiKey || flags.openrouterApiKey;
|
|
1134
|
+
const explicit = flags.openaiApiKey || flags.openaiProxyBaseUrl || flags.openaiProxyApiKey || flags.openrouterApiKey;
|
|
1128
1135
|
if (explicit || flags.yes || !input.isTTY || !output.isTTY) return supplied;
|
|
1129
1136
|
|
|
1130
|
-
console.log("PennyRouter can use your ChatGPT subscription, an OpenAI API key, or an OpenRouter API key to run Codex.");
|
|
1137
|
+
console.log("PennyRouter can use your ChatGPT subscription, an OpenAI API key, an OpenAI-compatible proxy, or an OpenRouter API key to run Codex.");
|
|
1131
1138
|
console.log("Choose one or more providers. PennyRouter will try them in order when available.");
|
|
1132
1139
|
return promptFundingProviderCheckboxes(
|
|
1133
1140
|
CODEX_PROVIDER_OPTIONS,
|
|
@@ -1209,6 +1216,12 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
|
|
|
1209
1216
|
let openrouterApiKey = String(
|
|
1210
1217
|
flags.openrouterApiKey || process.env.OPENROUTER_API_KEY || "",
|
|
1211
1218
|
).trim();
|
|
1219
|
+
let openaiProxyBaseUrl = String(
|
|
1220
|
+
flags.openaiProxyBaseUrl || process.env.PENNYROUTER_OPENAI_PROXY_BASE_URL || "",
|
|
1221
|
+
).trim();
|
|
1222
|
+
let openaiProxyApiKey = String(
|
|
1223
|
+
flags.openaiProxyApiKey || process.env.PENNYROUTER_OPENAI_PROXY_API_KEY || "",
|
|
1224
|
+
).trim();
|
|
1212
1225
|
let proxyBaseUrl = String(
|
|
1213
1226
|
flags.proxyBaseUrl || process.env.PENNYROUTER_PROXY_BASE_URL || "",
|
|
1214
1227
|
).trim();
|
|
@@ -1249,6 +1262,12 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
|
|
|
1249
1262
|
if (!interactive) throw new Error("OpenAI API was selected, but no key was provided.");
|
|
1250
1263
|
openaiApiKey = (await promptSecretish("OpenAI API key: ")).trim();
|
|
1251
1264
|
}
|
|
1265
|
+
if (wantsCodex && codexProviders.has("openai-proxy") && !(openaiProxyBaseUrl && openaiProxyApiKey)) {
|
|
1266
|
+
if (!interactive) throw new Error("OpenAI-compatible proxy was selected, but --openai-proxy-base-url and --openai-proxy-api-key were not both provided.");
|
|
1267
|
+
if (!openaiProxyBaseUrl) openaiProxyBaseUrl = await promptText("OpenAI proxy base URL (e.g. https://proxy.example.com/v1): ");
|
|
1268
|
+
if (!openaiProxyApiKey) openaiProxyApiKey = (await promptSecretish("OpenAI proxy API key: ")).trim();
|
|
1269
|
+
if (!openaiProxyBaseUrl || !openaiProxyApiKey) throw new Error("OpenAI-compatible proxy requires both a base URL and an API key.");
|
|
1270
|
+
}
|
|
1252
1271
|
if (wantsCodex && codexProviders.has("openrouter") && !openrouterApiKey) {
|
|
1253
1272
|
if (!interactive) throw new Error("OpenRouter was selected, but no key was provided.");
|
|
1254
1273
|
openrouterApiKey = (await promptSecretish("OpenRouter API key: ")).trim();
|
|
@@ -1276,11 +1295,11 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
|
|
|
1276
1295
|
console.log("");
|
|
1277
1296
|
}
|
|
1278
1297
|
|
|
1279
|
-
if (wantsCodex && !codexSubscriptionAuth && !openaiApiKey && !openrouterApiKey) {
|
|
1298
|
+
if (wantsCodex && !codexSubscriptionAuth && !openaiApiKey && !openaiProxyApiKey && !openrouterApiKey) {
|
|
1280
1299
|
if (!interactive) {
|
|
1281
1300
|
throw new Error(
|
|
1282
1301
|
"Codex requires ChatGPT login, --openai-api-key (or OPENAI_API_KEY), "
|
|
1283
|
-
+ "or --openrouter-api-key (or OPENROUTER_API_KEY). Nothing was installed.",
|
|
1302
|
+
+ "--openai-proxy-api-key (with --openai-proxy-base-url), or --openrouter-api-key (or OPENROUTER_API_KEY). Nothing was installed.",
|
|
1284
1303
|
);
|
|
1285
1304
|
}
|
|
1286
1305
|
console.log("Codex is not logged into ChatGPT and needs a user-funded provider.");
|
|
@@ -1306,6 +1325,10 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
|
|
|
1306
1325
|
if (wantsCodex && openaiApiKey) {
|
|
1307
1326
|
credentials.push({ provider: "openai_api", token: openaiApiKey });
|
|
1308
1327
|
}
|
|
1328
|
+
if (wantsCodex && openaiProxyBaseUrl && openaiProxyApiKey) {
|
|
1329
|
+
credentials.push({ provider: "openai_proxy", token: openaiProxyApiKey,
|
|
1330
|
+
metadata: { base_url: openaiProxyBaseUrl } });
|
|
1331
|
+
}
|
|
1309
1332
|
if ((wantsClaude || wantsCodex) && openrouterApiKey) {
|
|
1310
1333
|
credentials.push({ provider: "openrouter", token: openrouterApiKey });
|
|
1311
1334
|
}
|
|
@@ -1328,7 +1351,7 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
|
|
|
1328
1351
|
claudeProxyBaseUrl: claudeProxyConfigured ? proxyBaseUrl : null,
|
|
1329
1352
|
codexCredentialProvider: codexSubscriptionAuth
|
|
1330
1353
|
? "chatgpt-subscription"
|
|
1331
|
-
: openaiApiKey ? "openai-api" : openrouterApiKey ? "openrouter" : null,
|
|
1354
|
+
: openaiApiKey ? "openai-api" : openaiProxyBaseUrl ? "openai-proxy" : openrouterApiKey ? "openrouter" : null,
|
|
1332
1355
|
};
|
|
1333
1356
|
}
|
|
1334
1357
|
|
|
@@ -2044,7 +2067,7 @@ function printHelp() {
|
|
|
2044
2067
|
console.log(`PennyRouter CLI
|
|
2045
2068
|
|
|
2046
2069
|
Usage:
|
|
2047
|
-
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] [--openrouter-api-key KEY] [--proxy-base-url URL --proxy-api-key KEY] [--local[=URL]|--prod] [--config PATH]
|
|
2070
|
+
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] [--local[=URL]|--prod] [--config PATH]
|
|
2048
2071
|
pennyrouter update [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--dry-run] [--penny-key pr-...] [--gateway-base-url URL|--local[=URL]|--prod] [--no-mcp]
|
|
2049
2072
|
pennyrouter auth anthropic [--penny-key pr-...] [--token oauth-token] [--token-command CMD] [--gateway-base-url URL] [--forget]
|
|
2050
2073
|
pennyrouter disable [--harness ${SUPPORTED_HARNESS_IDS}] [--all] [--yes]
|
|
@@ -2053,7 +2076,7 @@ Usage:
|
|
|
2053
2076
|
pennyrouter status
|
|
2054
2077
|
pennyrouter mcp install|uninstall|status [--harness claude-code,codex]
|
|
2055
2078
|
|
|
2056
|
-
When an interactive install selects an Anthropic proxy, it asks for the PennyRouter gateway
|
|
2079
|
+
When an interactive install selects an Anthropic or OpenAI-compatible proxy, it asks for the PennyRouter gateway
|
|
2057
2080
|
that can reach that proxy. Leave it blank for the hosted gateway, or enter a private/self-hosted
|
|
2058
2081
|
gateway URL. Use --gateway-base-url (or --local for localhost:8400 development) for non-interactive installs.
|
|
2059
2082
|
|