pennyrouter 0.1.18 → 0.1.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
@@ -61,6 +61,7 @@ model you chose. "Penny Custom" is the dynamic option: PennyRouter cost-routes e
61
61
  - Penny Opus: pins `anthropic/claude-opus-4-8`
62
62
  - Penny Sonnet: pins `anthropic/claude-sonnet-4-6`
63
63
  - Penny Haiku: pins `anthropic/claude-haiku-4-5-20251001`
64
+ - Penny Fable: pins `anthropic/claude-fable-5`
64
65
  - Penny Custom: `pennyrouter/auto` — dynamic routing on the bundle/profile saved in the user's account
65
66
 
66
67
  ## Claude Pro/Max Subscription Auth
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pennyrouter",
3
- "version": "0.1.18",
3
+ "version": "0.1.19",
4
4
  "description": "Install and manage PennyRouter local coding-agent integrations.",
5
5
  "homepage": "https://pennyrouter.com",
6
6
  "bugs": {
@@ -7,15 +7,17 @@ const CONNECTOR_SETTING = "disableClaudeAiConnectors";
7
7
  // Values written into Claude Code's four model slots. CC displays a slot's VALUE verbatim in
8
8
  // its /model menu AND sends it as the request `model`, so each value is both the menu label
9
9
  // and the gateway's routing key. The three concrete Claude slots carry friendly PINNED labels
10
- // ("Penny Opus/Sonnet/Haiku") that the gateway maps to that exact model — the user's pick
10
+ // ("Penny Opus/Sonnet/Haiku/Fable") that the gateway maps to that exact model — the user's pick
11
11
  // runs on the model they chose, no dynamic tier switching on the trunk (background/utility
12
- // calls still route cheap, which is where the savings come from). The Fable slot is the
13
- // DYNAMIC "Penny Custom" option: let PennyRouter cost-route. Legacy "Penny Speed/Core/Power"
12
+ // calls still route cheap, which is where the savings come from). The separate Fable slot is
13
+ // pinned to Fable; the DYNAMIC "Penny Custom" option lets PennyRouter cost-route. Legacy
14
+ // "Penny Speed/Core/Power"
14
15
  // installs (dynamic bundles) are still recognized for uninstall (see isPennyRouterModelValue).
15
16
  const CLAUDE_CODE_MODELS = {
16
17
  haiku: "Penny Haiku", // -> pinned anthropic/claude-haiku-4-5-20251001
17
18
  sonnet: "Penny Sonnet", // -> pinned anthropic/claude-sonnet-4-6
18
19
  opus: "Penny Opus", // -> pinned anthropic/claude-opus-4-8
20
+ fable: "Penny Fable", // -> pinned anthropic/claude-fable-5
19
21
  custom: "Penny Custom", // -> dynamic (PennyRouter cost-routing)
20
22
  };
21
23
  // Retired dynamic-bundle labels (pre-pinned-trunk). Not written by a fresh install; matched
@@ -57,7 +59,7 @@ export const claudeCodeHarness = {
57
59
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
58
60
  current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
59
61
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
60
- current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.custom;
62
+ current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
61
63
 
62
64
  const statusline = await configureClaudeCodeStatusline(current);
63
65
 
@@ -102,7 +104,7 @@ export const claudeCodeHarness = {
102
104
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
103
105
  current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
104
106
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
105
- current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.custom;
107
+ current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
106
108
 
107
109
  const statusline = await configureClaudeCodeStatusline(current);
108
110
 
@@ -143,7 +145,7 @@ export const claudeCodeHarness = {
143
145
  current.env.ANTHROPIC_DEFAULT_OPUS_MODEL = CLAUDE_CODE_MODELS.opus;
144
146
  current.env.ANTHROPIC_DEFAULT_SONNET_MODEL = CLAUDE_CODE_MODELS.sonnet;
145
147
  current.env.ANTHROPIC_DEFAULT_HAIKU_MODEL = CLAUDE_CODE_MODELS.haiku;
146
- current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.custom;
148
+ current.env.ANTHROPIC_DEFAULT_FABLE_MODEL = CLAUDE_CODE_MODELS.fable;
147
149
 
148
150
  const statusline = await configureClaudeCodeStatusline(current);
149
151
 
@@ -266,6 +268,7 @@ function isPennyRouterModelValue(value) {
266
268
  value === CLAUDE_CODE_MODELS.haiku ||
267
269
  value === CLAUDE_CODE_MODELS.sonnet ||
268
270
  value === CLAUDE_CODE_MODELS.opus ||
271
+ value === CLAUDE_CODE_MODELS.fable ||
269
272
  value === CLAUDE_CODE_MODELS.custom ||
270
273
  LEGACY_PENNY_LABELS.includes(value)
271
274
  );
package/src/statusline.js CHANGED
@@ -140,10 +140,7 @@ let cacheAge = Infinity;
140
140
  try { cacheAge = Date.now() - statSync(cachePath).mtimeMs; } catch { /* no cache yet */ }
141
141
 
142
142
  const { base, key } = claudeEnv();
143
- // Installed = a base URL plus a pr- key (claudeEnv only returns pr- keys, and the installer
144
- // writes both together). Do NOT sniff the base URL for "pennyrouter": that substring check
145
- // false-negatived every dev/localhost gateway (e.g. localhost:8400), rendering a hard
146
- // "penny off" while the gateway was serving the session fine (observed 2026-07-18).
143
+
147
144
  const pennyInstalled = Boolean(base) && Boolean(key);
148
145
  if (sessionId && pennyInstalled && cacheAge > refreshMs) {
149
146
  spawn(process.execPath, [process.argv[1], "--refresh", sessionId], {
@@ -165,13 +162,10 @@ if (!pennyInstalled) {
165
162
  penny = "🪙 penny" + authSuffix(cache && cache.auth_mode);
166
163
  }
167
164
 
168
- // Wrap the underlying statusline (e.g. ccstatusline) and append the penny segment to
169
- // its FIRST line only, preserving every subsequent line verbatim (the Context bar and
170
- // any other rows). Taking only line [0] here is what dropped multi-line statuslines.
171
165
  let lines;
172
166
  if (config.wrap_command) {
173
167
  const run = spawnSync(config.wrap_command, {
174
- shell: true, input: raw, encoding: "utf8", timeout: 1500,
168
+ shell: true, input: raw, encoding: "utf8", timeout: 5000,
175
169
  });
176
170
  lines = (run.stdout || "").replace(/\\n$/, "").split("\\n");
177
171
  if (lines.length === 1 && lines[0] === "") lines = [];