clauderipple 0.3.0 → 0.3.1
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/CHANGELOG.md +20 -0
- package/dist/cli/src/tray.js +17 -2
- package/dist/router/src/presets.js +7 -1
- package/dist/router/src/proxy.js +21 -5
- package/dist/router/src/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 — 2026-09-24
|
|
4
|
+
|
|
5
|
+
Fixes for three reports against 0.3.0.
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- **Claude account rotation no longer breaks requests with a 400** (#15). With
|
|
10
|
+
`accountPool` on, the account's `anthropic-beta` header replaced the client's,
|
|
11
|
+
so fields Claude Code enables per request (`cache_control.scope`,
|
|
12
|
+
`context_management`, …) were refused with `Extra inputs are not permitted`.
|
|
13
|
+
A conversation pinned to an added account failed on every retry, because a
|
|
14
|
+
400 does not move it. The account now supplies identity only: its OAuth flags
|
|
15
|
+
are added to the client's, on the first attempt and on every retry.
|
|
16
|
+
- **The Windows tray appears** (#8). It was started with `ELECTRON_RUN_AS_NODE`
|
|
17
|
+
set to an empty string, which macOS Electron treats as unset and Windows
|
|
18
|
+
Electron treats as set, so on Windows the tray ran as plain Node and exited
|
|
19
|
+
while the CLI reported "tray started". The variable is now removed.
|
|
20
|
+
- **GLM-5.3 and GLM-5.3 Flash in the Z.AI preset** (#14). Z.AI serves no model
|
|
21
|
+
list, so the preset's list is the whole picker, and it stopped at GLM-5.2.
|
|
22
|
+
|
|
3
23
|
## 0.3.0 — 2026-09-23
|
|
4
24
|
|
|
5
25
|
Everything on `main` since 0.2.0. The account rotation that issue #8 asked for
|
package/dist/cli/src/tray.js
CHANGED
|
@@ -24,6 +24,20 @@ export function electronPath() {
|
|
|
24
24
|
return null;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Our environment without ELECTRON_RUN_AS_NODE, so Electron starts as the tray rather than as a
|
|
29
|
+
* plain Node process. The variable has to be removed, not emptied: macOS Electron reads an empty
|
|
30
|
+
* value as unset, but Windows Electron only asks whether it exists, so an empty one ran the tray
|
|
31
|
+
* as Node — it exited at once and no icon ever appeared (measured 2026-09-24, Electron 38,
|
|
32
|
+
* Windows 11; issue #8).
|
|
33
|
+
*/
|
|
34
|
+
export function trayEnv(env = process.env) {
|
|
35
|
+
const out = { ...env };
|
|
36
|
+
for (const name of Object.keys(out))
|
|
37
|
+
if (name.toUpperCase() === "ELECTRON_RUN_AS_NODE")
|
|
38
|
+
delete out[name];
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
27
41
|
/**
|
|
28
42
|
* Starts the tray and returns immediately. `detached` outlives this process, which is what a
|
|
29
43
|
* command typed in a terminal wants; the supervisor uses the foreground form instead.
|
|
@@ -32,7 +46,8 @@ export function startTray(options = {}) {
|
|
|
32
46
|
const current = runtime();
|
|
33
47
|
if (current.packaged) {
|
|
34
48
|
// The packaged app IS the tray: relaunching its own binary with no script is what opens it.
|
|
35
|
-
|
|
49
|
+
// Our own CLI runs under ELECTRON_RUN_AS_NODE here, and inheriting it would relaunch Node.
|
|
50
|
+
const child = spawn(current.node, [], { detached: true, stdio: "ignore", env: trayEnv() });
|
|
36
51
|
child.unref();
|
|
37
52
|
return { ok: true, message: "✓ tray started" };
|
|
38
53
|
}
|
|
@@ -51,7 +66,7 @@ export function startTray(options = {}) {
|
|
|
51
66
|
detached: options.detached ?? true,
|
|
52
67
|
stdio: "ignore",
|
|
53
68
|
// Electron would otherwise re-enter as a plain Node process, since that is how the CLI runs.
|
|
54
|
-
env:
|
|
69
|
+
env: trayEnv(),
|
|
55
70
|
});
|
|
56
71
|
if (options.detached ?? true)
|
|
57
72
|
child.unref();
|
|
@@ -55,7 +55,13 @@ export const PRESETS = [
|
|
|
55
55
|
vendorUrl: "https://z.ai/",
|
|
56
56
|
anthropicBaseUrl: "https://api.z.ai/api/anthropic",
|
|
57
57
|
authHeader: "authorization-bearer",
|
|
58
|
-
|
|
58
|
+
// No model list is served, so this list is the picker. glm-5.3-flash is named in
|
|
59
|
+
// https://docs.z.ai/guides/vlm/glm-5.3-flash; its FlashX sibling is not on the Coding Plan yet (issue #14).
|
|
60
|
+
fallbackModels: [
|
|
61
|
+
{ id: "glm-5.3", name: "GLM-5.3" },
|
|
62
|
+
{ id: "glm-5.3-flash", name: "GLM-5.3 Flash" },
|
|
63
|
+
{ id: "glm-5.2", name: "GLM-5.2" },
|
|
64
|
+
],
|
|
59
65
|
// https://docs.z.ai/devpack/tool/others documents the endpoint but not an Anthropic effort/thinking contract.
|
|
60
66
|
effortLevels: [],
|
|
61
67
|
thinking: "none",
|
package/dist/router/src/proxy.js
CHANGED
|
@@ -97,6 +97,22 @@ export function withCredential(headers, credential, previous = {}) {
|
|
|
97
97
|
out.push(k, v);
|
|
98
98
|
return out;
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* A Claude account's headers for one turn, with the client's beta flags kept. The account supplies
|
|
102
|
+
* identity; the flags belong to the request, because they switch on body fields the client sent
|
|
103
|
+
* (`cache_control.scope`, `context_management`, …) and the API refuses those fields without their
|
|
104
|
+
* flag — `Extra inputs are not permitted` (issue #15). The account's own OAuth flags are added to
|
|
105
|
+
* the client's, never used in their place. Only for native Claude accounts: a compatible vendor's
|
|
106
|
+
* beta filtering happens elsewhere and must not be undone here.
|
|
107
|
+
*/
|
|
108
|
+
export function withClientBetas(credential, clientBeta) {
|
|
109
|
+
if (clientBeta === undefined)
|
|
110
|
+
return credential;
|
|
111
|
+
const name = Object.keys(credential).find((key) => key.toLowerCase() === "anthropic-beta") ?? "anthropic-beta";
|
|
112
|
+
const flags = (raw) => raw.split(",").map((flag) => flag.trim()).filter(Boolean);
|
|
113
|
+
const merged = [...new Set([...flags([].concat(clientBeta).join(",")), ...flags(credential[name] ?? "")])];
|
|
114
|
+
return { ...credential, [name]: merged.join(",") };
|
|
115
|
+
}
|
|
100
116
|
/**
|
|
101
117
|
* A body decoded for reading, as text. Bytes that cannot be decoded are read as they arrived.
|
|
102
118
|
*
|
|
@@ -706,7 +722,7 @@ export class Proxy {
|
|
|
706
722
|
host: cfg.upstream,
|
|
707
723
|
port: this.deps.upstreamPort ?? 443,
|
|
708
724
|
agent: this.deps.upstreamAgent ?? this.agentFor(route.provider, "https:"),
|
|
709
|
-
extraHeaders: using.headers,
|
|
725
|
+
extraHeaders: withClientBetas(using.headers, req.headers["anthropic-beta"]),
|
|
710
726
|
dropClientAuth: true,
|
|
711
727
|
dropHeaders: new Set(credentials.flatMap((credential) => Object.keys(credential.headers).map((name) => name.toLowerCase()))),
|
|
712
728
|
dropHeaderPrefixes: ["anthropic-client-", "x-stainless-"],
|
|
@@ -871,13 +887,13 @@ export class Proxy {
|
|
|
871
887
|
// The model's own shape again: its credential pool is the provider's, but an auth convention
|
|
872
888
|
// this model overrides has to be the one the retry actually sends.
|
|
873
889
|
const provider = providerFor(owner, route.model);
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
: this.credentialsOf(route.provider, provider);
|
|
890
|
+
const native = provider.type === "anthropic" && provider.accountPool;
|
|
891
|
+
const available = native ? this.claudeAccounts.peekCredentials() : this.credentialsOf(route.provider, provider);
|
|
877
892
|
const rest = available.filter((c) => !tried.has(c.id));
|
|
878
893
|
if (rest.length === 0)
|
|
879
894
|
return null;
|
|
880
|
-
|
|
895
|
+
const next = this.pool.pick(route.provider, rest, conversationKey(json));
|
|
896
|
+
return next && native ? { ...next, headers: withClientBetas(next.headers, req.headers["anthropic-beta"]) } : next;
|
|
881
897
|
};
|
|
882
898
|
// Destroying the upstream makes it emit ECONNRESET (measured, Node 24.15), which is
|
|
883
899
|
// indistinguishable from the provider dropping us unless we remember that we did it. Without
|
|
@@ -5,4 +5,4 @@
|
|
|
5
5
|
// from the manifests. Until 0.1.1 the router and the CLI each carried their own literal, and both
|
|
6
6
|
// still said "0.1.0" in the 0.1.1 release: after an update nobody could tell which router was
|
|
7
7
|
// running, and the tray app had nothing to compare (2026-09-15, reported from a Windows install).
|
|
8
|
-
export const VERSION = "0.3.
|
|
8
|
+
export const VERSION = "0.3.1";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clauderipple",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Use GPT and 400+ other models inside the Claude Desktop app while staying signed in to your Claude subscription — no third-party gateway mode.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"workspaces": [
|