clauderipple 0.11.0 → 0.12.0
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 +42 -0
- package/README.es.md +48 -0
- package/README.ja.md +42 -0
- package/README.ko.md +39 -0
- package/README.md +44 -0
- package/README.pt-BR.md +46 -0
- package/README.zh-CN.md +40 -0
- package/dist/app/dist/claude-desktop.js +31 -0
- package/dist/app/dist/main.js +127 -8
- package/dist/app/dist/status-model.js +12 -0
- package/dist/app/dist/update-model.js +32 -0
- package/dist/app/dist/window.js +10 -3
- package/dist/app/package.json +1 -1
- package/dist/cli/src/index.js +5 -1
- package/dist/mod/hooks/i18n.ts +6 -0
- package/dist/mod/hooks/modes.ts +15 -8
- package/dist/mod/hooks/register.tsx +67 -19
- package/dist/mod/hooks/settings.js +37 -2
- package/dist/mod/hooks/settings.ts +31 -3
- package/dist/mod/hooks/ui/agents.tsx +1 -1
- package/dist/mod/hooks/ui/art.ts +8 -3
- package/dist/mod/hooks/ui/band.tsx +6 -2
- package/dist/mod/hooks/ui/flat.ts +11 -8
- package/dist/mod/hooks/ui/look.ts +3 -1
- package/dist/mod/hooks/ui/view.ts +2 -0
- package/dist/mod/hooks/workers.ts +8 -1
- package/dist/mod/tests/delegation.test.tsx +50 -0
- package/dist/mod/tests/register.test.tsx +138 -1
- package/dist/mod/tests/ui.test.tsx +6 -3
- package/dist/mod/types/index.d.ts +14 -0
- package/dist/router/src/admin.js +41 -76
- package/dist/router/src/autoupdate.js +299 -0
- package/dist/router/src/bootstrap.js +3 -2
- package/dist/router/src/cli-control.js +65 -0
- package/dist/router/src/config.js +70 -11
- package/dist/router/src/index.js +11 -1
- package/dist/router/src/picker.js +25 -1
- package/dist/router/src/presets.js +21 -13
- package/dist/router/src/providers/chatgpt/index.js +11 -0
- package/dist/router/src/providers/google/index.js +7 -2
- package/dist/router/src/providers/google/transport.js +16 -1
- package/dist/router/src/proxy.js +46 -6
- package/dist/router/src/requestlog.js +3 -2
- package/dist/router/src/version.js +1 -1
- package/dist/router/src/websearch.js +2 -2
- package/dist/ui/app.js +247 -42
- package/dist/ui/i18n.js +154 -18
- package/dist/ui/style.css +21 -0
- package/docs/ARCHITECTURE.md +61 -11
- package/package.json +1 -1
package/dist/ui/style.css
CHANGED
|
@@ -465,6 +465,23 @@ label.check input { margin-top: 2px; }
|
|
|
465
465
|
#phone-content code { display: block; margin-top: 6px; overflow-wrap: anywhere; }
|
|
466
466
|
#phone-content > .section:first-child { margin-top: 0; }
|
|
467
467
|
|
|
468
|
+
/* Ripple Remote, until a phone is paired: the connection card's shape in the accent, like the
|
|
469
|
+
laptop setup card. The QR below pairs from inside the app, so the store has its own button here. */
|
|
470
|
+
.conn-card.remote-promo { --tone: var(--accent-fill); --tint: var(--accent-soft); margin-bottom: 12px; padding-bottom: 20px; }
|
|
471
|
+
.remote-promo:lang(ko) { word-break: keep-all; overflow-wrap: anywhere; }
|
|
472
|
+
.remote-promo .conn-icon svg { stroke-width: 1.9; }
|
|
473
|
+
.remote-eyebrow { margin: 0 0 3px; color: var(--accent); font-size: var(--fs-foot); font-weight: 600; }
|
|
474
|
+
.remote-points { display: grid; gap: 8px; margin: 18px 0 0; padding: 0 0 0 60px; list-style: none; }
|
|
475
|
+
.remote-points li { position: relative; padding-left: 22px; color: var(--label-2); font-size: var(--fs-sub); line-height: 1.45; }
|
|
476
|
+
.remote-points li::before { content: ""; position: absolute; left: 2px; top: 0.42em; width: 9px; height: 5px; border-left: 2px solid var(--accent); border-bottom: 2px solid var(--accent); transform: rotate(-45deg); }
|
|
477
|
+
.remote-points strong { color: var(--label); font-weight: 600; }
|
|
478
|
+
.remote-get { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 20px 0 0 60px; }
|
|
479
|
+
.btn.large.remote-store { width: auto; gap: 8px; }
|
|
480
|
+
.remote-store-icon { width: 18px; height: 18px; stroke-width: 2; }
|
|
481
|
+
.remote-get .hint { flex: 1 1 200px; }
|
|
482
|
+
.remote-android .login-link { white-space: nowrap; }
|
|
483
|
+
.remote-next { margin: 16px 0 0 60px; padding-top: 14px; border-top: 1px solid var(--separator); color: var(--label-2); font-size: var(--fs-foot); line-height: 1.45; }
|
|
484
|
+
|
|
468
485
|
/* ---- Providers ----------------------------------------------------------------------- */
|
|
469
486
|
|
|
470
487
|
.provider-workspace { display: grid; grid-template-columns: 264px minmax(0, 1fr); gap: 28px; align-items: start; }
|
|
@@ -989,6 +1006,10 @@ a.cell { color: inherit; text-decoration: none; }
|
|
|
989
1006
|
.mac-side { flex: 1 1 100%; padding-left: 24px; text-align: left; }
|
|
990
1007
|
.mac-allow, .mac-setup-note, .mac-setup .form-field, .mac-setup-actions, .mac-setup .conn-note { margin-left: 0; padding-left: 0; }
|
|
991
1008
|
.mac-setup .form-field, .mac-setup-actions { max-width: none; }
|
|
1009
|
+
.remote-points, .remote-get, .remote-next { margin-left: 0; padding-left: 0; }
|
|
1010
|
+
.remote-get { flex-direction: column; align-items: stretch; }
|
|
1011
|
+
.btn.large.remote-store { width: 100%; }
|
|
1012
|
+
.remote-get .hint { flex: 0 0 auto; text-align: center; }
|
|
992
1013
|
|
|
993
1014
|
/* Requests read as a list: model and status on top, the numbers on one quiet line below. */
|
|
994
1015
|
.logs-table-card { overflow: visible; }
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -686,15 +686,15 @@ must preserve **Data: Artificial Analysis (artificialanalysis.ai)** attribution.
|
|
|
686
686
|
its hits arrive as Responses events (`web_search_call`, `url_citation`), a backend the router
|
|
687
687
|
does not have. DeepSeek's own endpoint and the ChatGPT provider run Anthropic's server tool;
|
|
688
688
|
OpenCode Go's chat endpoint, and every `openai-compatible` provider, do not.
|
|
689
|
-
- **OpenCode Go's
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
689
|
+
- **OpenCode Go's DeepSeek does not search, and never did** (corrected 2026-10-21).
|
|
690
|
+
DeepSeek's search has only ever worked on DeepSeek's own API. An entry written 2026-10-07
|
|
691
|
+
(commit 53a8db3) claimed Claude Code's side request to `/zen/go/v1/messages` came back with
|
|
692
|
+
ten real hits for OpenCode Go's DeepSeek models; that claim was wrong — the user reports it
|
|
693
|
+
never searched, and on 2026-10-21 the relay answered HTTP 200 with no
|
|
694
|
+
`web_search_tool_result` hits (`router.log`: "no results returned", twice, then a refusal).
|
|
695
|
+
The preset's `anthropicSearch` list is therefore empty, so `searchProviderFor` answers
|
|
696
|
+
nothing for those models, `/api/status` drops them from `webSearchModels`, and a search
|
|
697
|
+
routed there is refused instead of answered with invented prose.
|
|
698
698
|
- **A model that cannot search does not merely decline — it invents.** Measured 2026-09-21
|
|
699
699
|
against OpenCode Go's chat endpoint: `plugins: [{id: "web"}]` is accepted and ignored (HTTP
|
|
700
700
|
200, no `annotations`, cache reporting no lookup), and the model answers a search-shaped
|
|
@@ -1111,10 +1111,14 @@ what we do not have yet — so that adding a provider does not start with readin
|
|
|
1111
1111
|
lists them together (measured 2026-09-18). `wire` and `url` sat only on the provider,
|
|
1112
1112
|
so that one account had to be configured as three providers — a split the user saw in
|
|
1113
1113
|
the GUI and had to repeat in every `direct` rule. A model entry now overrides `wire`
|
|
1114
|
-
(`chat`, `responses` or `
|
|
1114
|
+
(`chat`, `responses`, `anthropic` or `google`), `url` and `authHeader`, and `providerFor` in
|
|
1115
1115
|
`config.ts` folds those into an ordinary provider at the moment a request's model is
|
|
1116
1116
|
known, so every `provider.type` branch downstream works against one unchanged
|
|
1117
|
-
contract. A
|
|
1117
|
+
contract. A `google` entry folds into a `google`/`api-key` provider (2026-10-21): the
|
|
1118
|
+
provider's key moves from its headers to `apiKey`, the model's `url` becomes the base,
|
|
1119
|
+
and the provider's `sessionHeader` and every non-auth header travel with it. The key is
|
|
1120
|
+
never restated in the model entry. A provider with no override for that model is returned
|
|
1121
|
+
by identity.
|
|
1118
1122
|
`authHeader` names a convention rather than restating the key: the provider's own
|
|
1119
1123
|
credential is re-sent under the header that model's wire expects, so there is one copy
|
|
1120
1124
|
of the key to rotate. Each endpoint follows the API it imitates — an Anthropic-wire
|
|
@@ -1221,6 +1225,17 @@ including the 2026-10-16 Antigravity measurement in §4d-ii, are identified sepa
|
|
|
1221
1225
|
- Stream endpoint `POST {base}/v1beta/models/{model}:streamGenerateContent?alt=sse`,
|
|
1222
1226
|
the key on header `x-goog-api-key`, base default `https://generativelanguage.googleapis.com`
|
|
1223
1227
|
(https://ai.google.dev/api/generate-content#method:-models.streamgeneratecontent).
|
|
1228
|
+
- **A gemini model on a relay (OpenCode Go, 2026-10-21).** `opencode-go` serves
|
|
1229
|
+
`gemini-4-argon-contributor` at `https://opencode.ai/inference/go/google/v1beta/models/<id>`
|
|
1230
|
+
(https://opencode.ai/docs/go/, read 2026-10-21), so the preset's entry carries
|
|
1231
|
+
`wire: "google"` and that base. The transport still sends the key on `x-goog-api-key`, and
|
|
1232
|
+
adds two things a relay asks for: the provider's `sessionHeader` with
|
|
1233
|
+
`conversationKey(json)` — stable for one conversation, different between two, the same
|
|
1234
|
+
derivation the OpenAI path uses (`providers/openai/index.ts`) — and, only when a session
|
|
1235
|
+
header is set, `user-agent: clauderipple/<version>` instead of an SDK/library name. Without
|
|
1236
|
+
the session header OpenCode answers `400 MissingSessionID`. Plain AI Studio providers set
|
|
1237
|
+
neither and send no custom User-Agent. The provider's other headers are applied as given.
|
|
1238
|
+
Not live-measured as of this date.
|
|
1224
1239
|
- `systemInstruction` takes `{role:"user"|"model", parts:[…]}`; `contents` is the
|
|
1225
1240
|
`user`/`model` turn list; a part is `{text}`, `{inlineData:{mimeType,data}}`,
|
|
1226
1241
|
`{functionCall:{name,args}}` or `{functionResponse:{name,response}}`. The `role` of a
|
|
@@ -1584,6 +1599,41 @@ plan unless marked otherwise.
|
|
|
1584
1599
|
| Android hotspot never recognised (2026-10-20, found by reading the code for an owner with Windows laptops and Android phones in mind): "on the hotspot" was the iPhone range 172.20.10.0/28 alone, on macOS and Windows. An Android hotspot hands out 192.168.43.x (≤ Android 10) or a random /24 (Android 11+; 192.168/16, 172.16/12, 10/8 by Android 14), so after our own failover `join()` saw no hotspot address and reported `join-failed`/`hotspot-not-visible`, while on the phone the controller read Wi-Fi and never planned the way back, and a by-hand join was never recognised | The product does not assume an iPhone. After our own join of the configured SSID is accepted by the OS, a new, non-link-local address within the settle time is the join, and its subnet (address + netmask from `os.networkInterfaces()`: no Location, no elevation) is kept in `<home>/tether.json` for that hotspot name; "on the hotspot" = iPhone range OR that network (§8). Only our own join replaces it, and a subnet equal to the Wi-Fi watched before is not remembered (logged as ambiguous) so home Wi-Fi is never read as the hotspot. Hints name both phones where the step differs. Not yet run with a real Android phone. |
|
|
1585
1600
|
| An expired Claude login on the Mac wrote `[desktop state] …로그인이 만료되었습니다…` every few seconds per Claude app session and read the keychain (a synchronous child process) for every request, while phones learned of it only by a failed send (2026-10-15) | A login problem is one host state: logged when it starts and ends, sent to phones as `HostInfo.desktopProblem` (a banner), and the keychain is reread at most every 30 s while expired or after a 401. Per-session Remote Control errors log once per change. The host never refreshes the login itself. |
|
|
1586
1601
|
|
|
1602
|
+
### Router-owned automatic updates (2026-10-21)
|
|
1603
|
+
|
|
1604
|
+
The always-running router owns the scheduler, not the optional tray. `autoupdate.ts` starts its
|
|
1605
|
+
first `update --check --json` after ten minutes and checks again every twelve hours with ±30 minutes
|
|
1606
|
+
of jitter. Only script and global npm installs are eligible; packaged apps, source checkouts and
|
|
1607
|
+
unknown layouts are checked but never automatically installed. The dashboard's **Automatic updates**
|
|
1608
|
+
switch saves `Config.autoUpdate`; omission means on, and changes are hot-reloaded without restarting.
|
|
1609
|
+
|
|
1610
|
+
Installation waits for zero model calls in flight across both the proxy and OpenAI ingress, and ten
|
|
1611
|
+
minutes since the latest request start/end (or router startup). Busy routers are checked every five
|
|
1612
|
+
minutes; after twenty-four hours the wait is abandoned until the next cycle. A request arriving after
|
|
1613
|
+
the idle decision still follows the existing graceful drain rules above. The scheduler stops when
|
|
1614
|
+
that drain begins. Manual dashboard updates and automatic updates share one coordinator and the
|
|
1615
|
+
same detached CLI process-group path in `cli-control.ts`; the installer survives the router's exit.
|
|
1616
|
+
|
|
1617
|
+
Before launch, `<home>/auto-update.json` is atomically written with from/to versions and attempt time.
|
|
1618
|
+
The CLI is pinned to that target through `CLAUDERIPPLE_PACKAGE`. The successor only records completion
|
|
1619
|
+
when its running version exactly matches the target; a CLI exit alone is not proof of completion.
|
|
1620
|
+
Failures retain the failed version, timestamp and error, suppressing automatic retries of that
|
|
1621
|
+
version for twenty-four hours, including across router restarts. A surviving installer PID blocks a
|
|
1622
|
+
second installation. Unreadable state disables installs rather than losing the retry record. Every
|
|
1623
|
+
scheduler decision is logged at info level. GET `/api/status` exposes `autoUpdate`, including enabled,
|
|
1624
|
+
install kind, available version, running state, lastUpdatedFrom/To/At and lastError.
|
|
1625
|
+
|
|
1626
|
+
The tray consumes that status, deduplicates completed/available notifications in memory and
|
|
1627
|
+
`<home>/update-notifications.json`, and relaunches an outdated script/npm tray after confirmed
|
|
1628
|
+
completion, waiting for the installer PID to exit so its tray-runtime setup can finish first.
|
|
1629
|
+
Disabled automatic updates and unsupported install kinds get a click-to-update notice;
|
|
1630
|
+
packaged apps retain the releases-page flow. Existing versions **without this scheduler cannot be
|
|
1631
|
+
remotely updated into it**: their owner must first update manually through an existing supported path.
|
|
1632
|
+
No remotely pushed command or live installation test is part of this feature's validation.
|
|
1633
|
+
|
|
1634
|
+
Sources: `packages/router/src/autoupdate.ts`, `cli-control.ts`, `index.ts`, `admin.ts`, `config.ts`;
|
|
1635
|
+
`packages/app/src/update-model.ts`, `main.ts`; scheduler and tray decision tests.
|
|
1636
|
+
|
|
1587
1637
|
## 6. Blocked paths (measured, do not retry)
|
|
1588
1638
|
|
|
1589
1639
|
- The app puts `ANTHROPIC_BASE_URL` in the CLI process env directly;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clauderipple",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Use GPT (any ChatGPT plan with Codex), DeepSeek and 400+ other models in the Claude Desktop Code tab and Claude Code while staying signed in to your Claude subscription — no third-party gateway mode. Several ChatGPT accounts that take over when one runs out, in Codex too.",
|
|
5
5
|
"license": "GPL-3.0-only",
|
|
6
6
|
"workspaces": [
|