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 +1 -0
- package/package.json +1 -1
- package/src/harnesses/claude-code.js +9 -6
- package/src/statusline.js +2 -8
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
|
@@ -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
|
|
13
|
-
// DYNAMIC "Penny Custom" option
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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 = [];
|