Repository navigation
Conversation
The usage pages that are listed read what magpie already knows and show it in the window. This one puts the same numbers on another device: the iPhone shows the remaining quota of Codex, MiniMax and GLM, the reset countdown and the real token usage on its home screen. The page in the window is a launcher. It carries no data and reads nothing: the plugin's host part starts a local service, and the page navigates its own frame to it. The service reads magpie's local quota API, holds the phone's read-only token, and publishes its port in the Tailscale tailnet, so the phone reaches it over the reader's own network and never over the public internet. The pairing code is worth one try and then it is spent. The service answers only to a Host it knows and to a token, and the console page that holds the token may only be opened by hand, never fetched from another page. How it differs from the pages that are listed: usage, page-usage-tree, page-usage-board and page-model-quota all answer on the machine that runs magpie, in the window. This one answers on the phone, over the reader's tailnet, and its numbers are the quota windows of the subscriptions themselves rather than a model-by-model split of them. Reviewed at 753a61f. Run (on this machine, Windows, temp HOME): bun scripts/check.mjs page-token-widget ok; bun scripts/check.mjs all packages ok; packages/page-token-widget bun test 16 pass 0 fail. Not tried: an iPhone, and a tailnet other than the reader's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the summary and in Chinese The summary in registry.json and the Chinese part of the README now say what the new entry does that the listed usage pages can't: the numbers land on another device, and the quota stays the quota window of the subscription.
|
Harm scan at 1f6c0af: nothing hostile. The vendored qrcode-generator is npm's 1.4.4 with one change (row/col in One security gap to fix before merge: and get Decide "local" by the socket: the 127.0.0.1 listener's requests are local, the tailnet listener's never are, whatever their Host says. Two listeners already exist, so a flag per listener is enough. A test: a request on the tailnet listener with Also worth saying in the README: |
The console page and its API decide "is this request from this computer" by the Host header, so a device in the tailnet could send "Host: 127.0.0.1:3498" and read the console, which carries the token that lets it read every subscription's quota, and could change the settings of the plugin. The service now listens on two sockets: the console on 127.0.0.1:3498, the phone side on 127.0.0.1:3499. The tailnet reaches the phone side through "tailscale serve --bg --tcp=3498 tcp://127.0.0.1:3499", so the port in the tailnet stays 3498 and the widget, the pairing code and the qr code do not change. Every console route answers 403 on the phone side, whatever Host says.
Tapping the widget on the home screen asked the service for the root of the phone, and the root of the phone was the console of the computer. The console is only opened on the loopback address, so the phone was told that this page is only opened on the computer of the service. The service now has two rooms. The console keeps the console and its API; the phone side answers with the page of the phone, which shows the same numbers. That page carries no token: the phone brings its own in the address and the page drops it as soon as it has read it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What this changes
Adds
packages/page-token-widget: a page in Magpie's window that puts a quota widget on the home screen of an iPhone.The window shows a QR code. The camera of the phone opens the pairing page in Safari, the widget script is downloaded, Scriptable opens it, and the widget goes on the home screen. The widget shows the remaining quota of every subscription, the percentage and the reset countdown of the 5-hour and the weekly window, today's tokens, the last seven days and the ranking per provider. The plugin page picks which subscriptions the medium and the large widget show, and shows the state of Tailscale and the iPhones in the same tailnet.
Tapping the widget opens the page of the phone in Safari: the same numbers, written for a narrow screen. The page carries no token of its own — the phone brings its own in the address and the page drops it as soon as it has read it.
The plugin runs the service itself: no install script, no Node.js, no scheduled task, no manual port forwarding. It finds the tailnet address through Tailscale and publishes the port with a
tailscale serve --tcpforwarding of its own.How it is put together:
page/holds a launcher only. The page is in a sandbox frame whose CSP isconnect-src 'none', so it cannot fetch anything; it navigates its frame to the local service and the service answers with the console.index.mjs) starts the service on127.0.0.1. It listens first and asks Tailscale afterwards, because the host is only given a few seconds to come up.127.0.0.1:3425) and writes nothing in the configuration of Magpie.127.0.0.1:<port>and the phone side on127.0.0.1:<phonePort>; the forwarding of Tailscale points at the phone side, so a request that comes in through the tailnet is never taken for a request of this computer. The Host header decides nothing: a device in the tailnet cannot read the console or the token whatever it puts in it. Every console route answers 403 on the phone side; the page of the phone and the API of the phone (/api/v1/overview,/api/v1/session,/scriptable/*) answer there instead. The page that carries the token may only be opened by hand (sec-fetch-mode: navigate), so another page cannot fetch it either.Registry text (verbatim)
{ "package": "@magpie-community/page-token-widget", "name": "iPhone Quota Widget", "kind": "page", "community": true, "summary": { "en": "A quota widget on the home screen of your iPhone: scan one code and the widget shows the remaining quota, the reset countdown and the real token usage of Codex, MiniMax and GLM. Unlike the usage pages listed above, the numbers land on another device, and the quota stays the quota window of the subscription rather than a per-model split of tokens; the phone reaches the local service over your Tailscale tailnet.", "zh": "把额度组件放到 iPhone 桌面:扫一次码,组件显示 Codex、MiniMax、GLM 各家套餐的剩余额度、重置倒计时和真实 Token 用量。和上面那些用量页面不同,数字落在另一台设备上;额度保持订阅本身的额度窗口,而不是按模型摊开的 token;手机通过 Tailscale 尾网连进本机服务。" } },Difference declaration: the registry entry,
magpie.summaryinpackage.jsonand this description carry the same text.The listed usage pages answer on the machine that runs Magpie. This one answers on another device: the home screen of an iPhone, over the tailnet of the user, with the quota window of the subscription itself rather than a per-model split of tokens.
Testing
bun scripts/check.mjs— every package of the repository passes, the new one included. (This machine has no bun, so the same script ran under node withimport.meta.dirreplaced byimport.meta.dirname: exit 0, every package ✓, including the new one.)node --test test/*.test.mjsinpackages/page-token-widget— 18 pass, 0 fail.Hostheader of127.0.0.1:<port>and without one. The page of the phone carries no token of the service.Standards kept: the page reads nothing of Magpie and reaches no network (the page files carry no
fetch, nomagpie.read); the package has no runtime dependency; the port is declared inpackage.jsonand the launcher falls back to it; the summary is in English and Chinese; the README has a Chinese part.Not tried: the phone side was not run through the whole pair by someone else than the author of the package; the widget on a device without a tailnet was not checked.
🤖 Generated with Claude Code