pennyrouter 0.3.32 → 0.3.33

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +37 -14
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.3.32",
3
+ "version": "0.3.33",
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
@@ -1595,9 +1595,7 @@ async function selectCodexFundingProviders(flags, selected) {
1595
1595
 
1596
1596
  console.log("PennyRouter can use your ChatGPT subscription, an OpenAI API key, an OpenAI-compatible proxy, or an OpenRouter API key to run Codex.");
1597
1597
  console.log("Choose one or more providers. PennyRouter will try them in order when available.");
1598
- const options = CODEX_PROVIDER_OPTIONS.filter(
1599
- ({ id }) => id !== "subscription" || hasChatgptLogin,
1600
- );
1598
+ const options = CODEX_PROVIDER_OPTIONS;
1601
1599
  return promptFundingProviderCheckboxes(
1602
1600
  options,
1603
1601
  "Which funding source should PennyRouter use for Codex?",
@@ -1703,7 +1701,7 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
1703
1701
  || process.env.PENNYROUTER_ANTHROPIC_PROXY_API_KEY
1704
1702
  || process.env.PENNYROUTER_PROXY_API_KEY || "",
1705
1703
  ).trim();
1706
- const codexSubscriptionAuth = wantsCodex && codexProviders.has("subscription")
1704
+ let codexSubscriptionAuth = wantsCodex && codexProviders.has("subscription")
1707
1705
  ? await detectCodexChatgptAuth()
1708
1706
  : false;
1709
1707
 
@@ -1729,9 +1727,15 @@ async function configureUserFunding(flags, selected, claudeAnthropicAuth,
1729
1727
  }
1730
1728
  }
1731
1729
  if (wantsCodex && codexProviders.has("subscription") && !codexSubscriptionAuth) {
1732
- throw new Error(
1733
- "ChatGPT subscription was selected, but Codex is not logged in. Run `codex login`, or select an API-key provider.",
1734
- );
1730
+ console.log("Codex needs to sign in to your ChatGPT account. Starting `codex login`...");
1731
+ const login = spawnSync("codex", ["login"], {
1732
+ stdio: "inherit",
1733
+ shell: process.platform === "win32",
1734
+ });
1735
+ if (login.error || login.status !== 0 || !(await detectCodexChatgptAuth())) {
1736
+ throw new Error("Codex login did not complete. Run `codex login`, then rerun `npx pennyrouter install`.");
1737
+ }
1738
+ codexSubscriptionAuth = true;
1735
1739
  }
1736
1740
  if (wantsCodex && codexProviders.has("openai-api") && !openaiApiKey) {
1737
1741
  if (!interactive) throw new Error("OpenAI API was selected, but no key was provided.");
@@ -2129,14 +2133,17 @@ async function selectHarnesses(flags) {
2129
2133
  }
2130
2134
 
2131
2135
  const detected = [];
2136
+ const available = [];
2132
2137
  for (const id of DEFAULT_INSTALL_HARNESS_IDS) {
2133
2138
  const harness = HARNESS_BY_ID[id];
2134
- if (await safeDetect(harness)) detected.push(harness);
2139
+ const installed = await safeDetect(harness);
2140
+ if (installed) detected.push(harness);
2141
+ available.push({ ...harness, installed });
2135
2142
  }
2136
2143
 
2137
2144
  if (flags.yes) return detected;
2138
2145
 
2139
- return promptHarnessCheckboxes(detected);
2146
+ return promptHarnessCheckboxes(available, detected);
2140
2147
  }
2141
2148
 
2142
2149
  async function printAdditionalHarnessesHint(selected) {
@@ -2153,10 +2160,14 @@ async function printAdditionalHarnessesHint(selected) {
2153
2160
  console.log(`To use PennyRouter with ${names}, install ${available.length === 1 ? "it" : "them"} and re-run \`npx pennyrouter install\`.`);
2154
2161
  }
2155
2162
 
2156
- async function promptHarnessCheckboxes(detected) {
2163
+ async function promptHarnessCheckboxes(available, detected) {
2157
2164
  const detectedIds = new Set(detected.map((harness) => harness.id));
2158
2165
  const selectedIds = new Set(detectedIds);
2159
- const harnesses = orderedHarnessesForPrompt(detectedIds);
2166
+ const harnesses = orderedHarnessesForPrompt(new Set(available.map((harness) => harness.id)))
2167
+ .map((harness) => ({
2168
+ ...harness,
2169
+ installed: available.find((item) => item.id === harness.id)?.installed ?? false,
2170
+ }));
2160
2171
  if (!input.isTTY || !output.isTTY) return promptHarnessCheckboxesByText(harnesses, selectedIds);
2161
2172
 
2162
2173
  return promptHarnessCheckboxesByKeys(harnesses, selectedIds);
@@ -2178,7 +2189,8 @@ async function promptHarnessCheckboxesByKeys(harnesses, selectedIds) {
2178
2189
  harnesses.forEach((harness, index) => {
2179
2190
  const pointer = index === cursor ? color(">", "cyan") : " ";
2180
2191
  const checked = selectedIds.has(harness.id) ? color("x", "green") : " ";
2181
- const name = index === cursor ? color(harness.name, "cyan") : harness.name;
2192
+ const label = harness.installed ? harness.name : `${harness.name} (install ${harness.name} first)`;
2193
+ const name = harness.installed && index === cursor ? color(label, "cyan") : label;
2182
2194
  output.write(` ${pointer} [${checked}] ${name}\n`);
2183
2195
  });
2184
2196
  output.write("\nEnter to continue.\n");
@@ -2215,7 +2227,12 @@ async function promptHarnessCheckboxesByKeys(harnesses, selectedIds) {
2215
2227
  }
2216
2228
 
2217
2229
  if (key.name === "space") {
2218
- const id = harnesses[cursor].id;
2230
+ const harness = harnesses[cursor];
2231
+ if (!harness.installed) {
2232
+ render();
2233
+ return;
2234
+ }
2235
+ const id = harness.id;
2219
2236
  if (selectedIds.has(id)) selectedIds.delete(id);
2220
2237
  else selectedIds.add(id);
2221
2238
  render();
@@ -2242,7 +2259,8 @@ async function promptHarnessCheckboxesByText(harnesses, selectedIds) {
2242
2259
  console.log(color("Where should PennyRouter be installed?", "bold"));
2243
2260
  for (const harness of harnesses) {
2244
2261
  const checked = selectedIds.has(harness.id) ? color("x", "green") : " ";
2245
- console.log(` [${checked}] ${harness.name}`);
2262
+ const label = harness.installed ? harness.name : `${harness.name} (install ${harness.name} first)`;
2263
+ console.log(` [${checked}] ${label}`);
2246
2264
  }
2247
2265
  console.log("");
2248
2266
  const answer = await rl.question(
@@ -2261,6 +2279,11 @@ async function promptHarnessCheckboxesByText(harnesses, selectedIds) {
2261
2279
  console.log(`Unknown tool "${token}".`);
2262
2280
  continue;
2263
2281
  }
2282
+ if (!harnesses.find((harness) => harness.id === id)?.installed) {
2283
+ const unavailable = HARNESS_BY_ID[id];
2284
+ console.log(`Install ${unavailable.name} first to select it.`);
2285
+ continue;
2286
+ }
2264
2287
  if (selectedIds.has(id)) selectedIds.delete(id);
2265
2288
  else selectedIds.add(id);
2266
2289
  }