@unchen/dsh-tokenworks-iwan
v0.0.15
Published
在词元工坊(TokenWorks)中单独完成 USTC iWAN 登录,并管理本地校园网代理线路
Readme
dsh-tokenworks-iwan
在词元工坊(TokenWorks)里单独完成 USTC iWAN 登录的插件:不经过主登录,自己走一遍学校统一身份认证,取回校园网线路,并可按需启动本地代理。也适用于通用 DSH:设置页照常可用,没有 TokenWorks 的账户菜单时自动省略该入口。
它做什么
- 登录:默认驱动
iwan-client-oidc的--fetch;配置了clientId/appSecret时插件自己走 PKCE; - 线路:复用客户端写的
servers.json,或插件自己从控制器取;控制器下发的salt/mtu(Panabit Android 2.4.3 起)原样透传,新版客户端派生密码不受影响;线路下拉里有「自动选择最快线路」,由客户端(v26.11.1+)ping 后连延迟最低的一条; - 本地代理:启动
iwan-client-oidc,在回环地址上开 HTTP 或 SOCKS5 代理; - 按域名接管:HTTP 模式下连接后,把设置的域名接到本地代理;默认是
api.llm.ustc.edu.cn、llm.ustc.edu.cn、id.ustc.edu.cn。接管列表可在设置页直接编辑,也支持父域(如ustc.edu.cn覆盖所有子域)和端口;清空表示只提供代理地址、不自动接管。TokenWorks 当前模型网关会从tokenworksBootstrap自动加入; - 校园网位置与自动切换:检测本机直连出口在校园网内还是校外;打开开关后,离开校园网自动连接、回到校园网自动断开(默认关,可在设置页开关);
- 两个入口:设置页的独立标签页「校园网隧道」(排在微信下面)负责完整设置;TokenWorks 账户菜单里有快捷入口,直接看状态、连接或断开。
它不做什么
- 不修改系统路由、不创建虚拟网卡;
- 不实现隧道协议:登录、线路与按域名接管之外,隧道交给
iwan-client-oidc; - 不随包分发任何客户端二进制;
- 不把线路密码解密后写盘,也不把密码或 token 发给界面;
- 不接管配置列表之外的任何请求;非接管请求继续走宿主原来的 dispatcher(包括宿主已有的系统代理);
- SOCKS5 模式不自动接管模型流量;
- 不修改 TokenWorks 自己的校园网检测,也不改它的登录或 Key 获取代码。
安装
用 tgz 安装(完全退出应用后执行),再启动应用:
dsh plugin --profile <profile> add <tgz 绝对路径>或发布到 npm 后:
dsh plugin --profile <profile> add @unchen/dsh-tokenworks-iwan@<版本>升级方式:卸载后装新版。插件不自带隧道客户端:先安装 iwan-client-oidc,放到 PATH 或配置目录的 bin/ 里,插件会自动找到;也可以在设置页「客户端」里一键下载。
平台
iwan-client-oidc 的官方 release 覆盖 Linux(x86_64 / aarch64 / armv7 / riscv64,musl)、macOS(x86_64 / aarch64)与 Windows(x86_64 / aarch64)。插件按 process.platform 与 process.arch 选对应资产,Linux/macOS 写入后补上可执行位,Windows 使用 .exe 文件名。非 Windows 平台的配置目录与客户端共用 ~/.config/iwan。
- macOS 首次运行若被系统拦下,可手动下载后执行
xattr -dr com.apple.quarantine <客户端路径>,或在「系统设置 → 隐私与安全性」里允许; - Windows 若被 SmartScreen/Defender 拦截,需要手动允许;插件不会修改系统安全设置;
- 没有 Windows/macOS 真机时只做了本地静态检查(资产映射、路径展开、可执行位分支),未做跨平台 CI。
配置
在 profile 的插件条目里配置。必填项没有写进源码或安装包,需要自己提供:
| 字段 | 必填 | 默认 | 含义 |
| --- | --- | --- | --- |
| enabled | 否 | true | false 时只关闭后台自动行为(定时续期、周期检测、启动自动连接);设置页里的手动登录/连接仍可用 |
| clientId | 否 | 空 | 只在插件自己登录时使用;通常不用填,登录会交给客户端 |
| appSecret | 否 | 空 | 同上;也可用环境变量 TOKENWORKS_IWAN_APP_SECRET |
| sidecarPath | 否 | 空 | 留空时自动查找 iwan-client-oidc(配置目录 bin/ → PATH);显式填写则优先 |
| sidecarDownloadBase | 否 | ustc-iwan 的 latest/download | 一键下载的来源;可指向镜像,或直接给一个 .zip 地址 |
| sidecarSha256 | 否 | 空 | 可选的下载校验值(zip 的 SHA-256) |
| sidecarMode | 否 | http | http 或 socks |
| listenHost | 否 | 127.0.0.1 | 代理监听地址;不要改成 0.0.0.0 |
| listenPort | 否 | 17890 | 代理端口 |
| configDir | 否 | <DSH_HOME>/tokenworks-iwan | auth.json、servers.json 与设置页写入的 settings.json 的目录;没有 servers.json 时会读 ~/.config/iwan/ |
| autoConnect | 否 | false | 登录后自动连接第一条线路 |
| routeHosts | 否 | api.llm.ustc.edu.cn、llm.ustc.edu.cn、id.ustc.edu.cn | 连接后自动走本地代理的主机;子域自动匹配,空数组关闭自动接管。设置页保存过之后以设置页为准 |
| campusCheckUrl | 否 | https://id.ustc.edu.cn/doc/claude/netcheck | 校园网位置检测地址;按来源 IP 返回 {"campus":1} 或 {"campus":0} |
| autoSwitch | 否 | false | 默认是否按校园网环境自动连接/断开;设置页里的开关优先 |
| issuer | 否 | https://auth.ivpn.ustc.edu.cn | 统一身份认证 issuer |
| redirectUri | 否 | com.panabit.mobile://oauth2redirect | 回调地址;见「已知限制」 |
| scope | 否 | openid profile email offline_access | 授权范围 |
| controller | 否 | https://crtl.ivpn.ustc.edu.cn | iWAN 控制器 |
| appId | 否 | controller-ustc | 控制器要求的 App ID |
| domain | 否 | iwan.ustc | 线路配置里的 domain |
| requestTimeoutMs | 否 | 15000 | 单次请求超时 |
推荐用环境变量提供 appSecret,避免写进任何会被提交或同步的文件:
export TOKENWORKS_IWAN_APP_SECRET='...'设置页写入的 sidecarPath、autoSwitch、routeHosts、selected 保存在 <configDir>/settings.json(权限 0600),优先于 profile 里的默认值;上次选择的线路重启后继续使用。
使用
打开「设置 → 校园网隧道」(在微信下面),页面先显示状态摘要,再按常用操作排列:
- 线路与连接:线路出现后选一条(或选「自动选择最快线路」,需要 iwan-client-oidc v26.11.1 及以上),点「连接」;连接后显示本地代理地址和接管域名数量。连接操作集中在这里,状态摘要不重复放按钮。选择会写入本地设置,重启应用后继续使用;「自动选择」同样会被记住。
- 网络:显示「本机直连位置」(校园网内 / 校园网外 / 未知)、「请求转发」规则,以及「按校园网环境自动切换」开关。检测失败算「未知」,不据此连接或断开;旁边可以「重新检测」。系统代理已提供校园网出口是普通状态;手动断开的暂停提示只在校外且自动切换开启时显示。
- 账号:点「登录 iWAN」。插件启动客户端的
--fetch,显示授权链接并打开浏览器。完成统一身份认证后,在登录成功的网页上右键 「打开应用」,选择 「复制链接地址」,将复制的完整链接粘贴回插件,再点「完成登录」。链接以com.panabit.mobile://开头。浏览器如果提示打开iWAN.app,选择取消。等待回调期间可以点「取消」;授权步骤会临时移到页面前部。配置了clientId与appSecret时改用插件自己的 PKCE 登录,并显示访问令牌的预计到期时间。 - 高级配置:默认折叠,包含接管域名、客户端路径与运行诊断。「接管域名」一行一个,**「恢复默认」**填回内置的三个学校域名;保存后立即生效,不需要重连。客户端路径支持
~;「网络诊断」可查看系统代理端点和完整的有效接管列表。
未安装 iwan-client-oidc 时,页面会直接显示下载入口;指定路径失效时显示「自动查找」,无需先展开高级配置。装好后可在高级配置查看实际路径和来源(手动指定 / 配置目录 / PATH)。
TokenWorks 的账户菜单里还有一个「校园网隧道」入口:显示简短状态,可直接连接或断开;展开后可选线路和修改自动切换开关,主操作移到面板里,始终只显示一处。
本机位置、请求转发与 TokenWorks 侧栏
TokenWorks 侧栏的校园网圆点查的是本机直连位置;本插件的「本机直连位置」用直连请求(绕过隧道)检测同一件事。连接隧道后:
- 被接管域名下的模型请求与登录/Key 获取走隧道(「请求转发」显示「指定域名经 iWAN 隧道」);
- 本机直连出口不一定变化,所以 TokenWorks 侧栏仍可能显示「校园网外」——这是正常的,不代表隧道没生效。
插件不修改 TokenWorks 的检测逻辑,也不造第三个「校园网」圆点;两个入口展示的都是真实但不同的路径。
与系统代理共存
插件会从 HTTPS_PROXY、HTTP_PROXY、ALL_PROXY(含小写)识别宿主已有的系统代理,并通过它探测校园网出口:
- 自动切换发现「本机在校外」但系统代理出口已在校园网(或无法确认)时,不会另起隧道,避免双层隧道;界面会说明原因,仍可手动连接;
- 未接管的请求继续走安装插件前的 dispatcher,不会把宿主的系统代理悄悄关掉;
- 已在隧道里时,校园网检测仍走
node:http(s)直连学校检测地址,读到的是本机真实位置。
DSH 自定义服务商
DSH 里手动配置的 USTC 服务商只要域名在接管列表里,连接隧道后就会自动走代理。默认列表已覆盖 api.llm.ustc.edu.cn、llm.ustc.edu.cn 和 id.ustc.edu.cn;换了域名(或想覆盖所有子域)时,在设置页「接管域名」里加上 ustc.edu.cn 或具体主机即可。该机制依赖 Node 内置 fetch 与 undici 共享的全局 dispatcher;插件只替换 dispatcher,不劫持 DNS,也不改系统设置。
工作原理
设置标签页 / 账户菜单(client 半)
→ Remote `tokenworksIwan`
→ host 半:
1. 点登录 → 解析客户端(显式路径 → 配置目录 bin/ → PATH;都没有时提示到「客户端」栏下载或指定)
2. 启动 iwan-client-oidc --fetch,读输出里的授权链接并打开浏览器
3. 回调写回客户端 stdin;客户端完成 token 交换并写 servers.json
4. 点连接 → 启动 iwan-client-oidc --config-dir <线路目录> --connect --server <线路|auto> --http --http-listen 127.0.0.1:17890
5. 替换全局 undici dispatcher:routeHosts(含动态发现的 TokenWorks 网关)走本地代理,
其余请求交给替换前的 dispatcher(断开时恢复)
6. 自动切换开启时每分钟检测一次校园网位置(settings.json 的 autoSwitch),
只在「进入校外」或「回到校内」时连接/断开;检测走 node:http(s),不经过上面的 dispatcher其它插件(例如 tokenworks-bootstrap)可以从运行时服务 tokenworksIwan 读取:
proxy():已连接时返回{ protocol, host, port },否则null;subscribe(listener):状态变化通知。
tokenworks-bootstrap 是否调用 proxy() 由 TokenWorks 自己的装配代码决定;本仓库只负责暴露这个运行时服务和按域名接管,不修改 TokenWorks 的登录/Key 获取链路。
已知限制
- 驱动客户端登录时回调要手动粘贴。
--fetch是交互式 CLI,插件读它的授权链接、把回调写回 stdin;Panabit 移动端 client 只注册了com.panabit.mobile://oauth2redirect,浏览器不会自动交给插件。要变成无感登录,需要一个允许回环或自定义协议回调的正式 client;代码里redirectUri可配置,拿到后不用重写流程。 - 只有 HTTP 模式能自动接管模型流量。undici 的
ProxyAgent只支持 HTTP CONNECT;SOCKS5 模式只提供地址,需要手动配置或等后续接入 SOCKS 客户端。 - 隧道客户端需要单独获取。插件不随包分发客户端;没安装时可以在设置页「客户端」里点「下载」(从 ustc-iwan 的 release 取对应平台的 zip),也可以手动放到 PATH 或配置目录的
bin/。下载只写文件,不会自动执行;重定向逐跳校验(只允许 HTTPS 或本机回环),解压只展开客户端二进制并限制解压后大小。 - refresh token 存在文件里(权限 0600),还没有接系统钥匙串;共用电脑上请注意。退出登录会删除文件,删不掉时至少覆盖为空文件,避免下次启动恢复登录。
- 非官方实现。控制器签名与隧道协议来自对官方客户端的观察,可能随学校升级失效;使用前请自行确认符合学校网络使用规定。
- 平台兼容性靠本地静态检查:没有跨平台 CI,也没有在 Windows/macOS 真机上跑完整登录与隧道;macOS 的隔离属性、Windows 的 SmartScreen 与执行权限差异可能需要在系统层面处理。
- 没有「测试隧道」按钮:校园网检测刻意绕过隧道,只能验证本机位置;要确认隧道出口是否可达,请直接访问学校服务或在接管域名下发起一次请求。宿主侧的错误与诊断文案目前只有中文。
开发
npm install
npm test # 构建 + 类型检查 + 单测
npm run build # 只构建产物 lib/ 不入库;lib/client.js 是 window.__ModuleLoader__ 形态,由 DSH 客户端加载。
单元测试覆盖 PKCE、回调解析、令牌响应、控制器签名、密码解密、线路文件读写(含 salt/mtu 透传与旧格式兼容)、sidecar 参数/端口占用/生命周期、dispatcher 分流与 fallback、接管域名解析、系统代理识别与校园网检测、下载重定向与解压上限;test/service.test.mjs 用本地假服务器跑完整登录链路(令牌交换 + 控制器签名校验 + 线路落盘 + 设置持久化 + 刷新退避/登出竞态),test/auto-switch.test.mjs 用假 sidecar 跑「校外自动连、回校自动断、手动断开不抢连、检测失败不误判、选择持久化(含 auto)、连接单飞」,test/client-login.test.mjs 覆盖客户端提前退出与未处理拒绝。真实登录与代理连接需要在能访问学校服务的环境里验证。
跨平台兼容只做本地静态检查:资产映射、~ 展开、可执行位与文件名(.exe)、配置目录解析;不提供跨平台 CI。
依赖的客户端
隧道和本地代理由 ustc-iwan 的 iwan-client-oidc 提供,本插件不重复实现。插件负责登录、取线路、按域名接管和客户端的启停,两者通过 servers.json 交换线路配置(字段与 v26.11.1 起对齐 Panabit Android 2.4.3 的格式一致:salt、mtu 原样透传,旧版客户端会忽略它们)。「自动选择最快线路」需要 v26.11.1 及以上的客户端;旧版本收到 --server auto 会报「no server name contains "auto"」,在界面里重选一条具体线路即可。
许可
MIT。发布包中的 lib/index.js 包含 fflate 代码,lib/client.js 与 lib/typert.remote-client.js 包含 zod 代码;完整版权声明与许可文本见 THIRD_PARTY_NOTICES.md。
