magpie-page-token-widget 0.2.0 → 0.3.3
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 +45 -0
- package/NOTICE +1 -1
- package/README.en.md +235 -0
- package/README.md +97 -163
- package/index.mjs +51 -0
- package/lib/collector.mjs +92 -13
- package/lib/forward.mjs +23 -1
- package/lib/network.mjs +179 -0
- package/lib/server.mjs +325 -28
- package/lib/state.mjs +33 -34
- package/lib/tailnet.mjs +64 -1
- package/mobile/app.js +2 -2
- package/mobile/index.html +1 -1
- package/mobile/style.css +3 -3
- package/package.json +6 -2
- package/scriptable/SetupUsageWidget.js +63 -9
- package/scriptable/UsageWidget.js +85 -22
- package/scripts/check-syntax.mjs +43 -0
- package/scripts/run-tests.mjs +28 -0
- package/ui/app.js +200 -10
- package/ui/index.html +15 -6
- package/ui/style.css +23 -1
- package/ui/timing.mjs +16 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.3
|
|
4
|
+
|
|
5
|
+
一次针对网络边界、连接模式和文档的专项修正。行为的变化都在下面这一节里;插件 ID、数据目录、令牌格式和已配对设备都不变,升级之后不需要重新配对。
|
|
6
|
+
|
|
7
|
+
### 网络安全
|
|
8
|
+
|
|
9
|
+
- 服务端现在自己校验「外部地址」,规则和手机上的安装器一致:
|
|
10
|
+
- HTTPS 地址可以写任何域名;
|
|
11
|
+
- 明文 HTTP 只允许明确可信的地址:RFC1918 私网、Tailscale 的 100.64.0.0/10、IPv6 ULA、本机回环、以及保留给内网的域名(`.local`、`.ts.net` 这类和本机解析器认得的单段名字);
|
|
12
|
+
- 公网上的明文 HTTP 一律拒绝,并且会写明原因;
|
|
13
|
+
- 带用户名密码、带查询参数、带片段、非法端口和不带路径前缀以外的东西的地址一律拒绝;
|
|
14
|
+
- **带路径前缀的地址这一版不支持**,反向代理请用一个独立域名,把它的根路径转到插件服务。
|
|
15
|
+
- 转发过来的请求按你配置的地址判断协议:走 HTTPS 的代理不再被当成 HTTP;`X-Forwarded-Proto`、`X-Forwarded-Host` 这些谁都能写的请求头一律不信;伪造的 `Host` 和对不上的 `Origin` 会被拒绝;OPTIONS 预检请求会得到 204 和正确的 CORS 头。
|
|
16
|
+
- Magpie 网关(`gatewayURL`)必须指着本机(`127.0.0.1`、`::1`、`localhost`)。填成别的地址会被明确拒绝,服务不会再把你的请求发到那台机器上去。
|
|
17
|
+
- 升级到 0.3.3 时,配置里存着的外部地址会按新规则重新过一遍:以前填过的公网明文 HTTP 地址会被去掉,控制台写明原因,然后回到 Tailscale 的转发。内网地址、Tailscale 地址和 HTTPS 地址不受影响,已配对的手机不用重新配对。
|
|
18
|
+
- 手机侧和控制台侧的权限隔离没有变:手机口上没有控制台接口,控制台不在尾网上。
|
|
19
|
+
|
|
20
|
+
### 连接模式
|
|
21
|
+
|
|
22
|
+
- 网络设置里多了「**恢复 Tailscale 默认连接**」:清掉自己填的地址和记下的网络设置,**把局域网那个口真的关掉**,再把 Tailscale 的转发建回来,控制台会写明现在生效的是哪一种。
|
|
23
|
+
- 换地址时如果局域网那个口开不起来,控制台会写明失败,而不是显示成功。
|
|
24
|
+
- 手机端口和控制台端口的隔离照旧:改了端口,手机那边要重新配对一次。
|
|
25
|
+
|
|
26
|
+
### 数据采集
|
|
27
|
+
|
|
28
|
+
- 今天的 Token 只在网关**明确回答「这个接口没有」**(404、405、501)时才退回老办法。网关临时出错(500、超时、断网)不再触发逐账号的请求,而是保留上一次读到的数字并标成「旧数据」。
|
|
29
|
+
- 「这个接口没有」这件事会被记住一阵子,不会每次刷新都去问一遍没有的接口。
|
|
30
|
+
|
|
31
|
+
### 生命周期
|
|
32
|
+
|
|
33
|
+
- 插件停止或卸载时会关掉自己开的监听、撤掉自己建的转发;进程被强杀时做不了这一步,Tailscale 里的转发会留着,需要自己 `tailscale serve --tcp=8790 off` 收一次。
|
|
34
|
+
- 别人的转发只凭端口相同不算「是我的」,插件既不碰也不删。
|
|
35
|
+
|
|
36
|
+
### 文档
|
|
37
|
+
|
|
38
|
+
- 中英文 README 都重写了「网络连接」:默认 Tailscale、自定义外部地址、局域网直连三种方式分开讲清楚,反向代理的要求逐条写明。
|
|
39
|
+
- 安装器(Scriptable)不再要求先连 Tailscale,只要求「iPhone 能访问你填的地址」。
|
|
40
|
+
- 手机页的安装说明同样不再无条件要求 Tailscale。
|
|
41
|
+
- 插件设置里的 `gatewayURL` 必须是本机地址这件事,写进了两份 README。
|
|
42
|
+
|
|
43
|
+
### 自动化
|
|
44
|
+
|
|
45
|
+
- 新增 GitHub Actions:Ubuntu 与 Windows × Node.js 20 / 22 的测试矩阵,每次 push 和 pull request 都会跑 `npm test`、`npm pack --dry-run`、语法检查和 `git diff --check`。
|
package/NOTICE
CHANGED
|
@@ -19,4 +19,4 @@
|
|
|
19
19
|
|
|
20
20
|
## Runtime applications
|
|
21
21
|
|
|
22
|
-
Scriptable, Tailscale and Magpie are separately installed applications with their own licences and terms. OpenAI, MiniMax and GLM names identify the subscriptions observed through Magpie. No proprietary logo assets or model lists are bundled.
|
|
22
|
+
Scriptable, Tailscale and Magpie are separately installed applications with their own licences and terms. Tailscale is only needed for the default way of connecting; with an address of your own the plugin does not use it at all. OpenAI, MiniMax and GLM names identify the subscriptions observed through Magpie. No proprietary logo assets or model lists are bundled.
|
package/README.en.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# magpie-page-token-widget
|
|
2
|
+
|
|
3
|
+
> English · [中文](README.md) · version 0.3.3 (see [CHANGELOG.md](CHANGELOG.md))
|
|
4
|
+
|
|
5
|
+
An iPhone home screen widget for Magpie, brought in as a plugin: install the plugin, scan a code, and the widget shows the remaining quota and the real token usage of Codex, MiniMax and GLM.
|
|
6
|
+
|
|
7
|
+
Installing it needs no install script, no Node.js and no scheduled task, and you do not have to set up port forwarding yourself: in the default Tailscale mode the plugin runs the service itself and builds the forward in the tailnet itself, and the phone pairs by scanning a code once.
|
|
8
|
+
|
|
9
|
+
> The numbers come from your own Magpie, and the script holds no key of its own. What the widget shows is still Magpie's own wording.
|
|
10
|
+
|
|
11
|
+
The plugin page looks like this: the scan-to-pair block on top, and below it the state, the data update, the network, the device management, which subscriptions to show, and the advanced settings.
|
|
12
|
+
|
|
13
|
+
## What it does
|
|
14
|
+
|
|
15
|
+
- The plugin page shows a QR code. Scan it with the camera of the iPhone, let Safari open the pairing page, download the widget script, open it with Scriptable and add the widget to the home screen.
|
|
16
|
+
- The widget shows the remaining quota of every subscription, the five-hour and the weekly window in percent, the reset countdown, today's tokens, the last seven days and the rankings by vendor. The parameters pick the subscription (`codex`, `minimax`, `glm`, or `codex@<account>`), and can swap the percent for what is used (`\|used`).
|
|
17
|
+
- The plugin page is a config centre with five blocks: **which subscriptions** (the cards the phone shows), **data update** (how often it reads), **network** (the state of the tailnet, the way the connection is made, the network cards of this computer), **device management** (the phones that are paired, each one revoked on its own) and **advanced settings** (the port, the gateway, the other things that are rarely changed and only looked at).
|
|
18
|
+
- Tap the widget on the phone and Safari opens the usage page of the phone: the remaining quota of every subscription, today's tokens, the last seven days, the rankings by model, vendor and client, the same numbers the computer shows.
|
|
19
|
+
- The service only reads the local API of Magpie. In the default mode (listening on this computer plus a Tailscale forward) the service opens on this computer only, the phone comes in through the forward of the tailnet, and the tokens stay in the directory of Magpie.
|
|
20
|
+
|
|
21
|
+
## Requirements
|
|
22
|
+
|
|
23
|
+
- Magpie on the desktop (the plugin host is Bun).
|
|
24
|
+
- [Scriptable](https://scriptable.app/) on the iPhone.
|
|
25
|
+
- Tailscale: **only the default mode needs it.** The computer and the iPhone are in the same tailnet, and the plugin finds the address and builds the forward by itself. With a server of your own or a trusted address inside your own network, Tailscale is no longer needed.
|
|
26
|
+
|
|
27
|
+
The three ways to connect (the default Tailscale mode, an address of your own, a direct way over the LAN) are in "Three ways to connect" below.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
magpie plugin add magpie-page-token-widget
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
From a checkout: `magpie plugin add ./magpie-page-token-widget`.
|
|
36
|
+
|
|
37
|
+
## Pair
|
|
38
|
+
|
|
39
|
+
1. Open the plugin page in Magpie.
|
|
40
|
+
2. The page shows the state of Tailscale. When the computer and the iPhone share a tailnet it shows the tailnet address and a code to scan.
|
|
41
|
+
3. Scan the code with the camera of the iPhone, open the page in Safari and download the widget script.
|
|
42
|
+
4. Open the downloaded `UsageWidget.js` in the Files app and choose "Share → Open with Scriptable".
|
|
43
|
+
5. Long press the home screen, add the Scriptable widget and pick `UsageWidget`.
|
|
44
|
+
|
|
45
|
+
The pairing code works once and lives ten minutes at most: after that it is dead, and the plugin page makes a new one by itself (there is nothing to click), so the first pairing after the install needs no extra click. **Every phone that pairs gets a read-only token of its own.** The service keeps only the hash of that token; the plain text is handed over exactly once, at the moment of pairing, and never again. Revoking one phone stops that phone alone.
|
|
46
|
+
|
|
47
|
+
There is no shared token any more. The one token that 0.1 gave to every phone is gone the moment the service is updated, so a phone that still holds it has to pair once more and get a token of its own.
|
|
48
|
+
|
|
49
|
+
Guessing a code is not cheap: ten wrong tries at one code lock that code for five minutes. The lock follows the code, not the machine — behind the Tailscale forward every phone arrives as the loopback of this computer, so a lock per address would let anybody on the tailnet shut the pairing of everybody else. An `X-Forwarded-For` header can be written by anybody, so it is not believed.
|
|
50
|
+
|
|
51
|
+
The service keeps one budget for the whole of it: a hundred wrong tries in five minutes, whichever code they were aimed at, and the pairing page, the widget script and the pairing door all close until the five minutes are up. Asking for a new pairing code at the computer empties that budget at once, so a person can open the door again without waiting. What really makes a code hard to guess is neither of those two doors: it has ninety-six bits of randomness, it lives ten minutes and it opens the door once.
|
|
52
|
+
|
|
53
|
+
The script keeps the token in the keychain of Scriptable, and the three widgets of one iPhone share it.
|
|
54
|
+
|
|
55
|
+
Without the code you can also use "Device management": give a phone a token of its own and put it, with the service address, into `scriptable/SetupUsageWidget.js` (make a new script in Scriptable and paste it in). The service address may be a Tailscale address (`http://100.x.x.x:8791`), an address inside your own network (`http://192.168.1.50:8791`), or an HTTPS domain of your own (`https://my-server`). **A plain HTTP address out in the public internet is refused**; the rules are in "Three ways to connect" below.
|
|
56
|
+
|
|
57
|
+
## Device management
|
|
58
|
+
|
|
59
|
+
The list holds every device that was paired: its name, when it was paired, when it was last seen and when it was revoked. Every entry can be renamed and revoked on its own. To add a phone, press "give this phone a token" and put the token that appears once into the phone.
|
|
60
|
+
|
|
61
|
+
## Data update
|
|
62
|
+
|
|
63
|
+
The service does not read Magpie for every request of the phone. It keeps a snapshot that is good for a minute: a fresh snapshot is given right away, an old one triggers a reading, and all the requests that come at that moment share that one reading.
|
|
64
|
+
|
|
65
|
+
A reader waits at most a few seconds: it gets the new numbers when they arrive, and the old ones marked as old when they do not, while the reading continues in the background.
|
|
66
|
+
|
|
67
|
+
How often the service reads on its own:
|
|
68
|
+
|
|
69
|
+
| Strategy | What it does |
|
|
70
|
+
|---|---|
|
|
71
|
+
| Smart (the default) | every minute while somebody is looking, every five minutes after ten quiet minutes, and almost never after half an hour of quiet |
|
|
72
|
+
| Every minute | a fixed rhythm |
|
|
73
|
+
| Every two minutes | a fixed rhythm |
|
|
74
|
+
| Every five minutes | a fixed rhythm |
|
|
75
|
+
|
|
76
|
+
Changing it in the plugin page takes effect at once; Magpie does not have to be restarted.
|
|
77
|
+
|
|
78
|
+
## Network
|
|
79
|
+
|
|
80
|
+
The page shows whether Tailscale is installed and connected, the address of this computer in the tailnet, the MagicDNS name, the iPhones of the tailnet and the way the connection is made. Three ways are told apart:
|
|
81
|
+
|
|
82
|
+
- **Direct**: Tailscale talks to the phone itself, encrypted end to end; on one LAN it usually finds the other side on its own, and that is the fastest way.
|
|
83
|
+
- **Peer relay**: there is no direct path, so another device of the tailnet carries the traffic.
|
|
84
|
+
- **DERP**: Tailscale's own relay server carries the traffic; the slowest way, and the last way out. None of the three is a promise that the connection works — DERP is a fallback, not a guarantee.
|
|
85
|
+
|
|
86
|
+
When the way cannot be read, the page says so instead of guessing. The button "diagnose" pings the phone once and writes down the answer; the service never pings on its own.
|
|
87
|
+
|
|
88
|
+
The network cards of this computer are listed as well: name, IPv4, IPv6, the length of the network part, and whether the card is Tailscale, Wi-Fi, wired or virtual. **These addresses are for the computer only; the phone API never gets them.** They are there to help: when the phone cannot be reached, look at the address of this computer in the LAN and at the address of the tailnet.
|
|
89
|
+
|
|
90
|
+
**Tailscale stays the default way in.** The phone does not go around the tailnet just because this computer has a 192.168.x.x: a direct way over the LAN would have to deal with plain HTTP (or with HTTPS of its own), with addresses that move when the router hands out new ones, and with the local network permission of iOS. That is a new hole to look after, and the tailnet address is stable on both sides.
|
|
91
|
+
|
|
92
|
+
If you want a road of your own, it is in the next part: you choose it, you write it down, and it is yours to look after. It never happens on its own.
|
|
93
|
+
|
|
94
|
+
## Three ways to connect
|
|
95
|
+
|
|
96
|
+
These are three different things; do not read them as one. The "Network" part of the console always says which one is in use.
|
|
97
|
+
|
|
98
|
+
### One, the default: Tailscale
|
|
99
|
+
|
|
100
|
+
- This is what it looks like right after the install, and nothing has to be configured. The plugin finds the tailnet address of this computer, builds a forward in the tailnet itself (`tailscale serve --tcp`), and the phone comes in on the 100.x address.
|
|
101
|
+
- The console stays on 127.0.0.1; only the phone side is in the tailnet. A request that arrives over the tailnet is never taken for a request of this computer, so the phone cannot read the console and cannot get the console token.
|
|
102
|
+
- When another forwarding already owns that port, the plugin does not touch it and the console says that the forward was not built.
|
|
103
|
+
- When the phone cannot be reached, look at three things: is the iPhone in this tailnet, is Tailscale logged in, and is there a direct path between the two.
|
|
104
|
+
- The way the traffic goes is written down as the path of this computer: **direct** is the two machines talking to each other encrypted, on one LAN usually found on their own, the fastest way; **peer relay** borrows a third device of the tailnet; **DERP** is Tailscale's own relay server and the last way out, the slowest but the most robust. None of them is a promise that the connection works.
|
|
105
|
+
|
|
106
|
+
### Two, an address of your own: your server, a reverse proxy or a Headscale of your own
|
|
107
|
+
|
|
108
|
+
Write an address into the field of the "Network" part and the phone is told that address when it pairs; the widget and the page on the phone use it as well. After that the plugin builds no Tailscale forward, and nothing of Tailscale is needed for this any more.
|
|
109
|
+
|
|
110
|
+
What may be written:
|
|
111
|
+
|
|
112
|
+
| Address | Allowed | Why |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `https://my-server` / `https://my-server.example.com` | yes | An HTTPS address may name any domain. The token and the numbers travel encrypted. |
|
|
115
|
+
| `http://192.168.1.50:8791`, `http://10.0.0.7:8791`, `http://172.16.3.9:8791` | yes | The private ranges of RFC1918. |
|
|
116
|
+
| `http://100.64.0.5:8791` | yes | The range of Tailscale (and of a Headscale of your own). |
|
|
117
|
+
| `http://[fd00::1]:8791`, `http://[::1]:8791` | yes | IPv6 ULA and the loopback of this computer. |
|
|
118
|
+
| `http://box.local:8791`, `http://myserver:8791` | yes | Names kept for a network of your own, or a single label that the resolver of this computer knows. |
|
|
119
|
+
| `http://example.com`, `http://93.184.216.34:8791` | **no** | Plain HTTP out in the public internet: the token goes past everybody on the way. |
|
|
120
|
+
|
|
121
|
+
The check on the server and the check in the installer of the phone are the same one, so the two never contradict each other.
|
|
122
|
+
|
|
123
|
+
After the update to 0.3.3 the address that is written down is looked at again with these rules: a **plain HTTP address out in the public internet is not accepted any more**. The plugin takes it out of the settings, writes down why in the console, and goes back to the forward of the tailnet. Addresses of a private network, addresses of the tailnet and HTTPS addresses are not touched, and the phone that is already paired does not have to pair again.
|
|
124
|
+
|
|
125
|
+
What a reverse proxy has to do:
|
|
126
|
+
|
|
127
|
+
- **This version does not support an address with a path.** Use a domain of its own, map its **root** to the service of the plugin, and write `https://my-server`. Writing `https://my-server/widget` is refused outright, not half working.
|
|
128
|
+
- The proxy has to point at the **phone-side port** (8791 by default), not at the port of the console (8790 by default): the console stays on this computer for good, and the proxy should only put the phone side out.
|
|
129
|
+
- The proxy has to pass the original `Host` on (`proxy_set_header Host $host;`). The plugin recognises "this request is meant for me" by it, and a forged `Host` is refused.
|
|
130
|
+
- The plugin does **not** believe `X-Forwarded-Proto`, `X-Forwarded-Host` and the like: anybody can write them. Whether the road is HTTPS or plain HTTP is decided by the address you wrote in the console, and by nothing else.
|
|
131
|
+
- A proxy that ends the HTTPS itself and talks plain HTTP to the plugin is fine: the plugin judges by the address you wrote, not by the last leg.
|
|
132
|
+
|
|
133
|
+
What to keep in mind:
|
|
134
|
+
|
|
135
|
+
- On plain HTTP the token travels inside it. Unless the address is one of the trusted ones in the table above, use it only on a network you trust yourself.
|
|
136
|
+
- When the phone goes to an address of the LAN, the iPhone asks for the "local network" permission — allow it.
|
|
137
|
+
- Addresses move: when the router hands out a new one the phone cannot find the service any more, and you write the new address in the console.
|
|
138
|
+
- The firewall: the phone comes in over the LAN, so the firewall of Windows may shut that door; the first time, let it through (the "private networks" of Windows are enough).
|
|
139
|
+
|
|
140
|
+
### Three, a direct way over the LAN: the phone stays out of the tailnet
|
|
141
|
+
|
|
142
|
+
Without Tailscale, on one LAN only: the phone side opens a door on an address of this computer and the phone comes in over the LAN.
|
|
143
|
+
|
|
144
|
+
- Only the **phone side** opens a door; the console stays on this computer (127.0.0.1) for good, and the phone API carries no console.
|
|
145
|
+
- Press "the phone comes in here" behind a network card in the "Network" part, or write the address yourself (`http://192.168.1.50:8791`) and pick one of the addresses of this computer; the plugin then opens the phone side on that address.
|
|
146
|
+
- The iPhone asks for the "local network" permission — allow it; the firewall of Windows has to let the door through once.
|
|
147
|
+
- The address of the LAN moves when the router hands out a new one, and then the phone cannot find the service; go back to the console and pick it again.
|
|
148
|
+
- That road is plain HTTP, and the token travels inside it.
|
|
149
|
+
|
|
150
|
+
**How to shut it down for good**: press **"restore the Tailscale default"** in the "Network" part. That action empties the address you wrote, empties the network settings that were written down, **really closes the door of the LAN** (it is not only a change of the settings), and builds the Tailscale forward again. The console then writes down which way is in use now.
|
|
151
|
+
|
|
152
|
+
## The page on the phone
|
|
153
|
+
|
|
154
|
+
Tapping the widget opens the usage page on the phone: the remaining quota of every subscription, today's tokens, the last seven days and the rankings by model, vendor and client, the same numbers the computer shows.
|
|
155
|
+
|
|
156
|
+
The page opens with the read-only token of the widget. The token travels in the `#token=` part of the address and is wiped from there as soon as the page is open; Scriptable keeps it from then on. The page is served on the tailnet side only (on your own network, when you have written an address of your own), the console of the computer stays on this computer.
|
|
157
|
+
|
|
158
|
+
## Widget parameter
|
|
159
|
+
|
|
160
|
+
| Size | Shows | Widget Parameter |
|
|
161
|
+
|---|---|---|
|
|
162
|
+
| Small | one quota, the reset countdown, today's tokens | `codex` (default), `minimax`, `glm`; one account is written `codex@<account>`; `\|used` shows what is used |
|
|
163
|
+
| Medium | up to 6 per page; 1–4 side by side, 5–6 in two rows of three; today's tokens; the weekly limit, when there is one, on the same line as the reset time (with 5–6 subscriptions in two rows only the 5-hour quota fits) | empty; `page=2` for the second page |
|
|
164
|
+
| Large | up to 6 per page; two columns with the quota, the reset and the second window; today's and the last seven days | empty; `page=2` for the second page |
|
|
165
|
+
|
|
166
|
+
The account in a parameter is the account as it is called now. The console writes the parameters the widget understands next to every subscription, so one can be copied from there. When an account is renamed the parameter finds nothing, and the small widget is no longer empty: it shows the first card and writes "not found" next to it.
|
|
167
|
+
|
|
168
|
+
Every number says which way it is read: "remaining" or "used". The bar is the same ink in the light and in the dark (only the depth differs), so it is not read as a warning at night; only the number turns orange or red when the quota is nearly gone.
|
|
169
|
+
|
|
170
|
+
## Lifetime
|
|
171
|
+
|
|
172
|
+
The service runs as long as Magpie runs. With Magpie closed, the phone keeps the last result it received. That is the trade of being a plugin, and it is not the same as the old service that stayed alive as a scheduled task.
|
|
173
|
+
|
|
174
|
+
When Magpie stops or reloads the plugin, the plugin runs through its own cleanup: it closes the doors it opened and takes away the forward it built. When the process is killed there is no time for that, see "Uninstall" below.
|
|
175
|
+
|
|
176
|
+
## Port
|
|
177
|
+
|
|
178
|
+
The service listens on 8790 by default, the phone side on 8791. Pick another one when the port is taken:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
magpie plugin options magpie-page-token-widget '{"port":8890}'
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Restart Magpie afterwards. In the default (Tailscale) mode the plugin publishes the port in the tailnet with a forwarding of Tailscale (`tailscale serve --tcp`), so the phone reaches it on the 100.x address. A forwarding of another service on that port is never touched.
|
|
185
|
+
|
|
186
|
+
The forwarding points at the phone side of the service (by default one port above the service port, so 8791), while the console stays on 127.0.0.1:8790. A request that comes in through the tailnet lands on that phone port and is never taken for a request of this computer, so the phone cannot read the console or the token. Once you write an address of your own, that forward is not built any more.
|
|
187
|
+
|
|
188
|
+
The plugin writes down the forwarding it made (the port and where it points) in its own file. After a restart it recognizes it and does not make it twice; a forwarding of an older version is moved to the phone side; the forwarding of somebody else is neither touched nor removed — the same port alone does not make it ours. It is taken away only when the plugin is stopped or uninstalled properly and the forwarding still points where it was written down.
|
|
189
|
+
|
|
190
|
+
`tailscale serve --bg --tcp=8790` is a standing setting of Tailscale: when the process is killed (a crash, a power cut) it stays there and does not go away by itself. The plugin finds it again on the next start, sees that it still points at itself, and goes on using it; to take it away by hand, run `tailscale serve --tcp=8790 off`, and `tailscale serve status` shows what is left.
|
|
191
|
+
|
|
192
|
+
The port and the gateway are both given to the plugin when Magpie starts it: the port is the address the plugin listens on, the gateway is the Magpie the plugin reads from. The plugin cannot move itself, so they stay startup parameters of Magpie. To change either of them, change the plugin settings of Magpie:
|
|
193
|
+
|
|
194
|
+
`magpie plugin options magpie-page-token-widget '{"port":8890,"gatewayURL":"http://127.0.0.1:3500"}'`
|
|
195
|
+
|
|
196
|
+
Then restart Magpie. The address the phone uses carries the port, so a new port means the phone has to be paired again.
|
|
197
|
+
|
|
198
|
+
The plugin reads Magpie's own local API, `http://127.0.0.1:3425` by default. **That gateway has to point at this computer** (`127.0.0.1`, `::1` or `localhost`): the plugin reads the Magpie of this computer only, and another address is refused outright (the console writes down why) instead of sending your requests to that other machine. The **Advanced settings** of the console show where it reads from (that is all; they do not change it). A different gateway belongs in the plugin settings of Magpie: it only counts after Magpie has been restarted, and half-changing it in the console only leads to confusion.
|
|
199
|
+
|
|
200
|
+
## Compatibility
|
|
201
|
+
|
|
202
|
+
- Magpie `0.1.3681` or later is needed (`package.json` says so).
|
|
203
|
+
- The quota is read from the report Magpie has already cached (`/v1/magpie/quotas?kept=1`); the vendors are not asked again.
|
|
204
|
+
- Today's tokens come from one request to `/v1/magpie/usage?period=today`, which holds the totals, the hours, the vendors and the accounts. **When an older Magpie does not know that endpoint, the old way is used instead** (`/v1/magpie/usage/requests?period=today` plus one request per account) and the widget keeps its numbers. The old way is only used when the gateway says clearly that the endpoint is not there (404, 405, 501); a moment of trouble (500, a timeout, a broken connection) does not start a request per account, it keeps the numbers of the last good reading and marks them as old. "This endpoint is not there" is remembered for a while, so it is not asked again at every refresh.
|
|
205
|
+
- The trend of the last seven days and the rankings by model and vendor come from `/v1/magpie/usage/requests?period=7d&limit=1`: this is the only endpoint that carries the groups `by.model` and `by.agent`, and another endpoint would lose the rankings and the trend, so it stays.
|
|
206
|
+
- Magpie does not know the period `week` (it is taken for 30 days); seven days is `7d`.
|
|
207
|
+
|
|
208
|
+
## Uninstall
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
magpie plugin rm magpie-page-token-widget
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
When the plugin is stopped or removed, it closes the doors it opened and takes away the forward it built (only the one it wrote down). One thing it cannot do: when the process is killed (a crash, a power cut), there is no time for that, and the forward stays in Tailscale. It has to be taken away by hand:
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
tailscale serve --tcp=8790 off
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Privacy
|
|
221
|
+
|
|
222
|
+
- The service only reads the local API of Magpie (`127.0.0.1:3425` by default, this computer only). It does not write the configuration of Magpie and does not touch the logins.
|
|
223
|
+
- The tokens are random, read only and stay in the directory of Magpie; the service keeps only their hashes. There is no shared token: one token per phone, and revoking one stops that phone alone. Only the plugin page of this computer and the phone that carries a pairing code can get one.
|
|
224
|
+
- The addresses of the LAN are only shown in the console of the computer; the phone API does not have them.
|
|
225
|
+
- In the default (Tailscale) mode the service listens on this computer only (127.0.0.1); the phone comes in through the forwarding of Tailscale. There is no public entrance and no firewall rule is needed. Once you write an address of your own, the phone side opens a door on the address you chose (**the phone side only** — the console stays on this computer), the firewall has to let it through once, and that road is plain HTTP.
|
|
226
|
+
|
|
227
|
+
## Development
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
npm test
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
The tests never touch the real Tailscale: everything that speaks with the command of Tailscale talks to a fake one instead (`mockTailscale` in `test/helpers.mjs`), and the real `tailscale serve` settings are never changed. The gateway of Magpie is one the tests start themselves.
|
|
234
|
+
|
|
235
|
+
`lib/collector.mjs` and `lib/normalize.mjs` come from [ScriptableTokenWidgets](https://github.com/liuzq1103/ScriptableTokenWidgets), `lib/vendor/qrcode-generator.cjs` is [qrcode-generator](https://github.com/kazuhikoarase/qrcode-generator). See [NOTICE](NOTICE).
|