@trim21/personal-pi-extensions 0.0.433 → 0.0.436

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trim21/personal-pi-extensions",
3
- "version": "0.0.433",
3
+ "version": "0.0.436",
4
4
  "type": "module",
5
5
  "description": "Custom pi coding-agent extensions: bwrap sandbox, workspace guard, opencode edit, and more",
6
6
  "keywords": [
@@ -49,7 +49,7 @@
49
49
  "eslint-plugin-import-x": "^4.17.1",
50
50
  "eslint-plugin-promise": "7.3.0",
51
51
  "eslint-plugin-simple-import-sort": "14.0.0",
52
- "eslint-plugin-unicorn": "73.0.0",
52
+ "eslint-plugin-unicorn": "74.0.0",
53
53
  "eslint-plugin-unused-imports": "4.4.1",
54
54
  "husky": "^9.1.7",
55
55
  "lint-staged": "^17.3.0",
@@ -27,6 +27,8 @@ export interface MihomoConfig {
27
27
  nameserver: string[];
28
28
  "default-nameserver": string[];
29
29
  "fake-ip-filter"?: string[];
30
+ /** 域名级 DNS 规则(mihomo >= 1.18):allowlist 域名 DIRECT,其余 REJECT。 */
31
+ rules: string[];
30
32
  };
31
33
  tun: {
32
34
  enable: true;
@@ -111,6 +113,8 @@ function ipRule(host: string): string {
111
113
  interface BuiltRules {
112
114
  rules: string[];
113
115
  fakeIpFilter: string[];
116
+ /** DNS 层规则:allowlist 域名正常解析,未允许域名直接拒绝(解析失败而非 fake-ip 后连接失败)。 */
117
+ dnsRules: string[];
114
118
  }
115
119
 
116
120
  /**
@@ -119,14 +123,20 @@ interface BuiltRules {
119
123
  * 拿到 fakeip 再进 TUN 形成环(loopback detector 会拒绝);
120
124
  * - 无端口条目直接匹配;带端口条目用 AND 组合(域名/IP + DST-PORT)精确放行;
121
125
  * - 最后以 MATCH,REJECT 兜底实现 deny-by-default。
126
+ * dns.rules 同步构建:allowlist 域名 DIRECT,其余 MATCH,REJECT——未允许域名
127
+ * 在 DNS 层即被拒绝(curl 报 Could not resolve host),而不是先拿 fake-ip、
128
+ * 到连接层才断(报 TLS decode error,容易误判为网络故障)。
122
129
  */
123
130
  function buildRules(allowlist: readonly string[]): BuiltRules {
124
131
  const rules: string[] = [];
125
132
  const fakeIpFilter: string[] = [];
133
+ const dnsRules: string[] = [];
126
134
  for (const entry of allowlist) {
127
135
  const { host, port } = parseAllowlistEntry(entry);
128
136
  if (!isIp(host)) {
129
137
  fakeIpFilter.push(`+.${host}`);
138
+ // DNS 层按域名放行(端口无关);连接层规则保留端口语义
139
+ dnsRules.push(`DOMAIN-SUFFIX,${host},DIRECT`);
130
140
  if (port === undefined) {
131
141
  rules.push(`DOMAIN-SUFFIX,${host},DIRECT`);
132
142
  } else {
@@ -139,7 +149,8 @@ function buildRules(allowlist: readonly string[]): BuiltRules {
139
149
  }
140
150
  }
141
151
  rules.push("MATCH,REJECT");
142
- return { rules, fakeIpFilter };
152
+ dnsRules.push("MATCH,REJECT");
153
+ return { rules, fakeIpFilter, dnsRules };
143
154
  }
144
155
 
145
156
  /**
@@ -154,7 +165,7 @@ export function generateMihomoConfig(options: MihomoConfigOptions): MihomoConfig
154
165
  if (dnsServers.length === 0) {
155
166
  throw new Error("At least one DNS server is required");
156
167
  }
157
- const { rules, fakeIpFilter } = buildRules(allowlist);
168
+ const { rules, fakeIpFilter, dnsRules } = buildRules(allowlist);
158
169
  return {
159
170
  "mixed-port": 0,
160
171
  mode: "rule",
@@ -168,6 +179,7 @@ export function generateMihomoConfig(options: MihomoConfigOptions): MihomoConfig
168
179
  nameserver: [...dnsServers],
169
180
  "default-nameserver": [...dnsServers],
170
181
  ...(fakeIpFilter.length > 0 && { "fake-ip-filter": fakeIpFilter }),
182
+ rules: dnsRules,
171
183
  },
172
184
  tun: {
173
185
  enable: true,