Files
mihomo-patchbay/shared/outbound-chain.ts
T
im0o e9e2eb555d 每路入站配一组专属策略组,换线不再改配置文件
原本换一条线要改 config.yaml、跑校验、等内核重载,几秒起步;而换线恰恰是这个工具
最高频的动作,把它放在最重的路径上方向就错了。现在每一路配一个 select 组加 AUTO、
FALLBACK 两个,入站的 proxy 永久指向 select,换线只是调一次内核接口。这样做的前提是
选中不会丢:内核把它写进 cache 文件,重启后由 patchSelectGroup 恢复,而
profile.store-selected 默认为真。

链式代理靠给来源订阅复制一份声明、挂上 override.dialer-proxy 实现,因为这个字段写在
策略组上内核只会打条日志然后忽略。副本是 type: file 读原订阅已经落盘的缓存,而不是
自己再拉一次:自己拉的话订阅地址一不可达副本就是空的,那一路入站会一条线都不剩,
表现是端口还在监听却没有任何出口。

顺带把 shared/outbound-chain.ts 里「只有 Selector 接受手动选择」那条断言删了,它是
错的(内核检查的是 SelectAble 接口,URLTest 和 Fallback 也实现了)。界面仍然只让
Selector 可点,但理由换成了另外两种的手动指定会被它们自己撤销。

完整取舍见工作区决策记录 0007。
2026-08-11 03:10:38 +08:00

73 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 「这个端口现在到底走哪条线」——这个工具要回答的核心问题。
*
* 答案往往不是一跳能到的:一路入站落在 `PROXY` 组上,`PROXY` 选中了 `AUTO`,
* `AUTO` 又按延迟挑了某条线路。只显示第一层等于没回答。
*
* 这里只做纯计算,输入是内核报上来的运行时状态,输出是名字数组。放在 shared 里
* 是为了能被 `bun test` 覆盖——它带防环处理,而防环这种事写在组件的 computed 里
* 就再也没人测得到。
*/
/** 运行时状态里跟解析链路有关的部分。完整定义在 web/api.ts。 */
export interface RuntimeNode {
/** 出站类型。策略组是 Selector / URLTest / Fallback / LoadBalance 之一 */
type?: string
/** 策略组当前选中的成员。不是策略组就没有这个字段 */
now?: string
/** 策略组的成员列表 */
all?: string[]
}
/**
* 从落脚的出站一路跟着「当前选中」往下走。
*
* 返回的数组第一项是起点,最后一项是走到头的那个名字。起点为 null 时返回空数组。
* 策略组理论上可以互相引用成环,遇到重复的名字就停下——内核自己会拒绝这种配置,
* 但界面不能因为一份坏配置就转到卡死。
*/
export function resolveChain(
runtime: Record<string, RuntimeNode>,
start: string | null,
): string[] {
const hops: string[] = []
let cursor = start
while (cursor && !hops.includes(cursor)) {
hops.push(cursor)
cursor = runtime[cursor]?.now ?? null
}
return hops
}
export interface SwitchableGroup {
group: string
members: string[]
now: string
}
/**
* 链条上最后一个 `Selector` 组。这一路入站没被 Patchbay 接管时,界面上能点的就是它。
*
* 取最后一个而不是第一个,是因为越靠近线路的那一层越接近「换线路」这个意图;
* 前面的层换的是「走哪套策略」,那是另一回事。
*
* 只认 `Selector`,尽管内核对 `URLTest` 和 `Fallback` 也接受手动指定
* (hub/route/proxies.go:85-90 检查的是 `SelectAble` 接口,不是类型)。原因是那两种的
* 手动指定会自己失效:`URLTest` 选中的线路测不通时临时让位给最快的
* (adapter/outboundgroup/urltest.go:103-116),`Fallback` 更狠,测不通一次就把手动指定
* 永久清掉(fallback.go:117)。把它们和「选了就一直是它」摆成同一个控件会骗人。
*/
export function lastSwitchableGroup(
runtime: Record<string, RuntimeNode>,
chain: string[],
): SwitchableGroup | null {
for (let i = chain.length - 1; i >= 0; i -= 1) {
const name = chain[i]!
const node = runtime[name]
if (!node?.all?.length || node.type !== 'Selector') continue
return { group: name, members: node.all, now: node.now ?? '' }
}
return null
}