pennyrouter 0.3.20 → 0.3.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.3.20",
3
+ "version": "0.3.21",
4
4
  "description": "Install and manage PennyRouter local coding-agent integrations.",
5
5
  "homepage": "https://pennyrouter.com",
6
6
  "bugs": {
package/src/launch.js CHANGED
@@ -806,9 +806,26 @@ export function startPennyTerminalTitle({ gatewayBaseUrl, apiKey, titleSessionId
806
806
  };
807
807
  }
808
808
 
809
- async function writeClaudeLaunchSettings() {
809
+ async function writeClaudeLaunchSettings({ gatewayBaseUrl, apiKey } = {}) {
810
810
  const native = (await readJsonFile(join(homeDir(), ".claude", "settings.json"), null)) || {};
811
- const overlay = { disableClaudeAiConnectors: true };
811
+ // Claude Code gives values from --settings precedence over the parent process
812
+ // environment. Keep this overlay authoritative so a separately-managed
813
+ // ~/.claude/settings.json (for example scripts/proxy/claude-proxy.sh) cannot
814
+ // redirect a `penny claude` session away from the selected Penny profile.
815
+ const overlay = {
816
+ disableClaudeAiConnectors: true,
817
+ ...(gatewayBaseUrl && apiKey
818
+ ? {
819
+ env: {
820
+ ANTHROPIC_BASE_URL: gatewayBaseUrl,
821
+ ANTHROPIC_AUTH_TOKEN: apiKey,
822
+ // An empty value is treated as unset by Claude Code, and prevents a
823
+ // stale API key in the shared settings from creating dual auth.
824
+ ANTHROPIC_API_KEY: "",
825
+ },
826
+ }
827
+ : {}),
828
+ };
812
829
  if (native.statusLine) overlay.statusLine = native.statusLine;
813
830
  await configureClaudeCodeStatusline(overlay);
814
831
  await privateWrite(claudeLaunchSettingsPath(), `${JSON.stringify(overlay, null, 2)}\n`);
@@ -874,7 +891,7 @@ export async function configurePennyHarness(harness, {
874
891
  config.harnesses ||= {};
875
892
  if (harness === "claude-code") {
876
893
  await writeClaudeUserSkill();
877
- await writeClaudeLaunchSettings();
894
+ await writeClaudeLaunchSettings({ gatewayBaseUrl, apiKey });
878
895
  config.harnesses.claude = {
879
896
  enabled: true,
880
897
  subscription_auth: Boolean(subscriptionAuth),
@@ -1268,6 +1285,14 @@ export async function runPenny(argv = process.argv.slice(2)) {
1268
1285
  profile: config.profile,
1269
1286
  });
1270
1287
  }
1288
+ if (harness === "claude") {
1289
+ // Refresh on every launch so profile selection is honored immediately,
1290
+ // including --private, --cloud, and named profiles.
1291
+ await writeClaudeLaunchSettings({
1292
+ gatewayBaseUrl: config.gateway_base_url,
1293
+ apiKey: config.api_key,
1294
+ });
1295
+ }
1271
1296
  const models = harness === "opencode" ? await resolveOpenCodeLaunchModels(config) : null;
1272
1297
  const invocation = buildPennyInvocation(harness, argv.slice(1), config, models);
1273
1298
  const child = spawn(invocation.command, invocation.args, {
@@ -123,6 +123,14 @@ async function main() {
123
123
  });
124
124
 
125
125
  assert.equal(fs.readFileSync(path.join(home, ".claude", "settings.json"), "utf8"), nativeClaude);
126
+ const claudeLaunchSettings = JSON.parse(
127
+ fs.readFileSync(claudeLaunchSettingsPath(), "utf8"),
128
+ );
129
+ assert.deepEqual(claudeLaunchSettings.env, {
130
+ ANTHROPIC_BASE_URL: "https://gateway.example/",
131
+ ANTHROPIC_AUTH_TOKEN: "pr-super-secret",
132
+ ANTHROPIC_API_KEY: "",
133
+ });
126
134
  assert.equal(fs.readFileSync(path.join(home, ".codex", "config.toml"), "utf8"), nativeCodex);
127
135
  assert.equal(await fileMode(pennyConfigPath()), 0o600);
128
136
  assert.equal(await fileMode(claudeLaunchSettingsPath()), 0o600);