dsh-permission-rules 0.7.2 → 0.7.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 CHANGED
@@ -1,3 +1,22 @@
1
+ ## v0.7.3 - 2026-09-18
2
+
3
+ ### Security
4
+
5
+ - A plain-HTTP allow whose adjudication resolved no address now **fails closed**: the forward answers 502 (`no adjudicated address for <host>`) instead of letting `http(s).request` dial the hostname itself — a second DNS resolution whose answer the rules never saw, the plain-HTTP twin of the issue #21 CONNECT hardening (issue #23). Targets whose adjudication found addresses keep the pinned `lookup`, and the chained branch is untouched.
6
+
7
+ ### Fixed
8
+
9
+ - The `/rules` command now registers before the `attachNetworkProxy()` await in `apply()`, so an uninstall landing while the proxy is starting can no longer drop the command (the async-apply unload window).
10
+ - The Typert wire codecs now carry both published and alpha.2 faces: the `schema` field the `0.1.5-rc.2` line reads, plus the `create()` factory every codec on the `0.1.6-alpha.2`+ line must carry. Without it the alpha.2 typert-loader refused the `dsh-permission-rules#permissionRules/networkStatus` result codec and the plugin's host manifest failed to register, crashing boot.
11
+
12
+ ### Changed
13
+
14
+ - The package now declares the DSH host contract: `engines.dsh` (`>=0.1.2-rc.1 <0.2.0 || >=0.1.5-alpha.1 <0.2.0 || >=0.1.6-0 <0.2.0`) and `manifestVersion: 1`.
15
+
16
+ ### Tests
17
+
18
+ - Regression: a plain-HTTP target with no adjudicated address answers 502 and never reaches the origin; the adjudicated named-target path still forwards through the pinned lookup.
19
+
1
20
  ## v0.7.2 - 2026-09-12
2
21
 
3
22
  ### Fixed
package/README-es.md CHANGED
@@ -8,6 +8,7 @@
8
8
  *Las reglas deciden lo conocido. Un modelo revisor decide lo que no lo es.*
9
9
 
10
10
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-permission-rules)
11
12
  [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin)
12
13
  [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-permission-rules.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
13
14
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
@@ -76,7 +77,7 @@ Una **política de red a nivel de proceso** estilo Codex: el tráfico de subproc
76
77
  - **`allow-all`** — el preset danger-full-access: permitir todo.
77
78
  - **`auto`** (por defecto) — sigue el preset del sandbox; en hosts sin el servicio de política de sandbox se resuelve a `autoFallback` (`allow-all`).
78
79
 
79
- - **Emparejamiento** — `match.network` con `domains` / `ips` / `ports` / `schemes` (globs, comodines, CIDR, rangos de puertos; se aceptan puertos YAML numéricos). La extracción de candidatos URL en la ruta caliente `tools/pre-execute` se dispara sobre argumentos de herramientas web y URLs embebidas en texto de comandos bash/pwsh; los destinos de loopback pueden cortocircuitar reglas según la política `loopback`. Los literales IPv6 mapeados a IPv4 se normalizan a su forma IPv4 antes de comparar, y el proxy se conecta a las direcciones con las que se tomó la decisión, sin una segunda resolución DNS.
80
+ - **Emparejamiento** — `match.network` con `domains` / `ips` / `ports` / `schemes` (globs, comodines, CIDR, rangos de puertos; se aceptan puertos YAML numéricos). La extracción de candidatos URL en la ruta caliente `tools/pre-execute` se dispara sobre argumentos de herramientas web y URLs embebidas en texto de comandos bash/pwsh; los destinos de loopback pueden cortocircuitar reglas según la política `loopback`. Los literales IPv6 mapeados a IPv4 se normalizan a su forma IPv4 antes de comparar, y el proxy se conecta a las direcciones con las que se tomó la decisión, sin una segunda resolución DNS; una conexión cuya adjudicación no resolvió ninguna dirección falla cerrada (502) en lugar de marcar el nombre.
80
81
  - **Encadenado a un proxy ascendente** — `network.upstreamProxy` (por defecto `off`) envía las conexiones que este proxy **permite** a través de un proxy ascendente: CONNECT le pide un túnel (`CONNECT host:port`) y las peticiones HTTP simples se le reenvían en forma absoluta. Un destino bloqueado nunca llega al proxy ascendente: sigue recibiendo el 403 estructurado de este plugin. Dos casos no se encadenan nunca aunque haya un proxy ascendente configurado: un destino de **loopback** (un proxy fuera de este host no puede enrutar su loopback) y cualquier decisión producida por una **regla con ámbito `ips`** (encadenar entrega el nombre de host al proxy ascendente, así que «la conexión aterriza en una dirección que las reglas vieron» — issue #21 — dejaría de cumplirse justo donde a las reglas les importaba la dirección; esas decisiones siguen conectando directamente a la dirección adjudicada), más un destino cuyo esquema no tenga un proxy ascendente utilizable. El nombre de host del propio proxy ascendente es configuración del operador, no entrada del agente, y no lo adjudican estas reglas. Una URL con credenciales nunca se emite en claro: los avisos, `/rules network` y la instantánea de settings enmascaran la contraseña (`http://user:***@host:port`). Un proxy ascendente inalcanzable, un timeout (10 s) o una respuesta no 2xx producen **502**; no hay a propósito una caída silenciosa a conexión directa, para que una mala configuración siga siendo visible.
81
82
  - **Auditoría** — las conexiones denegadas anexan `permissionRules/network` a la sesión propietaria (la misma puerta adaptativa `ignorable`), con contadores de bloqueo e intercepciones recientes en `/rules network` y la página de settings.
82
83
  - **Diagnóstico** — las conexiones bloqueadas llevan un mensaje `[network: …]` que nombra el destino bloqueado, el modo o la regla que decidió y la solución.
@@ -172,6 +173,7 @@ Todos los parámetros son campos Schemastery `Config` (modificables desde cordis
172
173
  - **Sin cambios de sandbox.** La política de sandbox a nivel de SO pertenece a la costura del sandbox, no a este plugin.
173
174
  - **Rechazo ruidoso de mala configuración.** Campos YAML desconocidos, acciones desconocidas y patrones malos se rechazan al cargar.
174
175
  - **Límites de backtracking.** Los patrones glob se limitan a `maxGlobStars` expansiones de estrella no acotadas; los patrones regex rechazan cuantificadores anidados no acotados y alternancias literales solapadas cuantificadas.
176
+ - **Marcado a prueba de fallos.** Los reenvíos HTTP simples y los túneles CONNECT conectan solo a direcciones adjudicadas; un allow cuya adjudicación no resolvió ninguna dirección falla cerrado con 502 en lugar de marcar el nombre (issue #23).
175
177
 
176
178
  ## Known limitations
177
179
 
@@ -210,7 +212,7 @@ node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
210
212
  pnpm install # node ^22.19 || >=24
211
213
  pnpm run typecheck # tsc, src + tests
212
214
  pnpm run lint # eslint, src + tests + scripts
213
- pnpm test # vitest: 280 tests, 23 files
215
+ pnpm test # vitest: 24 spec files
214
216
  pnpm run test:coverage # coverage gate (90/80/90/90)
215
217
  pnpm run build # tsc declarations + tsdown bundles (lib/)
216
218
  pnpm run pack:check # build + pack (the published artifact)
@@ -235,7 +237,7 @@ Consulta [VERIFICATION.md](VERIFICATION.md) para el registro de verificación en
235
237
 
236
238
  ## PerryLink DSH Plugin Family
237
239
 
238
- Este proyecto es uno de los [37 complementos de DeepSeek Harness](https://github.com/PerryLink) mantenidos por [PerryLink](https://github.com/PerryLink). Si este te ayuda, probablemente los demás también:
240
+ Este proyecto es uno de los [40 complementos de DeepSeek Harness](https://github.com/PerryLink) mantenidos por [PerryLink](https://github.com/PerryLink). Si este te ayuda, probablemente los demás también:
239
241
 
240
242
  | Plugin | One-liner |
241
243
  |---|---|
@@ -264,6 +266,7 @@ Este proyecto es uno de los [37 complementos de DeepSeek Harness](https://github
264
266
  | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Cambio de estilo en tiempo de ejecución equivalente a outputStyles de Claude Code | |
265
267
  | **[dsh-personal-directive](https://github.com/PerryLink/dsh-personal-directive)** | Inyector de directivas personales con interruptor en la barra superior (edición framework) |
266
268
  | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | Base de conocimiento de desarrollo de plugins como habilidad de agente bajo demanda | |
269
+ | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | |
267
270
  | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | Puente multicanal de aprobación/preguntas: WeChat/Telegram/Feishu, consola de sesión |
268
271
  | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | Motor de informes de investigación verificables con evidencia direccionada por contenido | |
269
272
  | **[dsh-score](https://github.com/PerryLink/dsh-score)** | Puntuación de calidad multidimensional para plugins de DeepSeek Harness. | |
@@ -274,7 +277,16 @@ Este proyecto es uno de los [37 complementos de DeepSeek Harness](https://github
274
277
  | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | Pruebas de instalación y humo aisladas para plugins de DeepSeek Harness. | |
275
278
  | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | Puente de tareas TickTick/Dida365: panel de cabecera de sesión + 11 herramientas |
276
279
  | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | Traducción de parámetros entre proveedores y reparación determinista de JSON para DeepSeek Harness. | |
277
- | **[dsh-wechat](https://github.com/PerryLink/dsh-wechat)** | Puente WeChat ↔ DSH (bot Tencent iLink): texto/imagen/archivo/voz, aprobaciones en el chat |
280
+ | **[dsh-wechat](https://github.com/pan17/dsh-wechat)** | Puente WeChat ↔ DSH (bot Tencent iLink): texto/imagen/archivo/voz, aprobaciones en el chat |
281
+ | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | |
282
+ | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | |
283
+ | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | |
284
+ | **[dsh-kit](https://github.com/PerryLink/dsh-kit)** | One-command starter pack that installs the core family | |
285
+ | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | |
286
+ | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | |
287
+ | **[dsh-plugin-portal](https://github.com/PerryLink/dsh-plugin-portal)** | Zero-dependency static portal rendering the whole plugin family as one page | |
288
+ | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | |
289
+ | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | |
278
290
 
279
291
  ## License
280
292
 
package/README-hi.md CHANGED
@@ -8,6 +8,7 @@
8
8
  *नियम ज्ञात को तय करते हैं। एक समीक्षक मॉडल अज्ञात को तय करता है।*
9
9
 
10
10
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-permission-rules)
11
12
  [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin)
12
13
  [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-permission-rules.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
13
14
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
@@ -76,7 +77,7 @@ Codex-शैली की **प्रक्रिया-स्तरीय न
76
77
  - **`allow-all`** — danger-full-access preset: सब कुछ अनुमत।
77
78
  - **`auto`** (डिफ़ॉल्ट) — sandbox preset का अनुसरण करता है; बिना sandbox-नीति सेवा वाले hosts पर `autoFallback` (`allow-all`) में हल होता है।
78
79
 
79
- - **मिलान** — `match.network` `domains` / `ips` / `ports` / `schemes` के साथ (globs, वाइल्डकार्ड, CIDR, पोर्ट श्रेणियाँ; संख्यात्मक YAML पोर्ट स्वीकृत)। `tools/pre-execute` हॉट पथ पर URL-उम्मीदवार निष्कर्षण वेब-टूल तर्कों और bash/pwsh कमांड टेक्स्ट में एम्बेडेड URLs पर चलता है; लूपबैक लक्ष्य `loopback` नीति के अनुसार नियमों को शॉर्ट-सर्किट कर सकते हैं। IPv4-मैप्ड IPv6 लिटरल तुलना से पहले IPv4 रूप में सामान्यीकृत होते हैं, और प्रॉक्सी उन्हीं पतों पर कनेक्ट करता है जिन पर निर्णय लिया गया — दूसरा DNS रिज़ॉल्यूशन नहीं।
80
+ - **मिलान** — `match.network` `domains` / `ips` / `ports` / `schemes` के साथ (globs, वाइल्डकार्ड, CIDR, पोर्ट श्रेणियाँ; संख्यात्मक YAML पोर्ट स्वीकृत)। `tools/pre-execute` हॉट पथ पर URL-उम्मीदवार निष्कर्षण वेब-टूल तर्कों और bash/pwsh कमांड टेक्स्ट में एम्बेडेड URLs पर चलता है; लूपबैक लक्ष्य `loopback` नीति के अनुसार नियमों को शॉर्ट-सर्किट कर सकते हैं। IPv4-मैप्ड IPv6 लिटरल तुलना से पहले IPv4 रूप में सामान्यीकृत होते हैं, और प्रॉक्सी उन्हीं पतों पर कनेक्ट करता है जिन पर निर्णय लिया गया — दूसरा DNS रिज़ॉल्यूशन नहीं; जिस कनेक्शन की निर्णय-प्रक्रिया ने कोई पता हल नहीं किया वह नाम डायल करने के बजाय बंद-विफल (502) होता है।
80
81
  - **अपस्ट्रीम श्रृंखलन** — `network.upstreamProxy` (डिफ़ॉल्ट `off`) इस प्रॉक्सी द्वारा **अनुमत** कनेक्शनों को अपस्ट्रीम प्रॉक्सी से भेजता है: CONNECT उससे सुरंग माँगता है (`CONNECT host:port`), और सादे HTTP अनुरोध पूर्ण रूप में उसे अग्रेषित होते हैं। अवरुद्ध लक्ष्य कभी अपस्ट्रीम तक नहीं पहुँचता — उसे इस प्लगइन का संरचित 403 ही मिलता है। अपस्ट्रीम कॉन्फ़िगर होने पर भी दो स्थितियाँ कभी श्रृंखलित नहीं होतीं: **लूपबैक** लक्ष्य (इस होस्ट के बाहर का प्रॉक्सी उसके लूपबैक को रूट नहीं कर सकता), और **`ips`-स्कोप वाले नियम** से उत्पन्न कोई भी निर्णय (श्रृंखलन होस्टनाम अपस्ट्रीम को सौंप देता है, इसलिए "कनेक्शन उसी पते पर पहुँचता है जो नियमों ने देखा" — issue #21 — ठीक वहीं टूट जाता जहाँ नियमों को पते की परवाह थी; ऐसे निर्णय निर्णीत पते पर ही सीधे कनेक्ट करते रहते हैं), साथ ही वह लक्ष्य जिसके scheme के लिए कोई उपयोगी अपस्ट्रीम न हो। अपस्ट्रीम का अपना होस्टनाम ऑपरेटर कॉन्फ़िगरेशन है, एजेंट इनपुट नहीं, और इन नियमों द्वारा निर्णीत नहीं होता। क्रेडेंशियल वाला URL कभी कच्चा नहीं छपता: चेतावनियाँ, `/rules network` और settings स्नैपशॉट पासवर्ड मास्क करते हैं (`http://user:***@host:port`)। अपस्ट्रीम अगम्य हो, टाइमआउट (10 सेकंड) हो, या गैर-2xx उत्तर दे — तो **502** मिलता है; सीधे कनेक्शन पर मौन वापसी जानबूझकर नहीं है, ताकि गलत कॉन्फ़िगरेशन दिखता रहे।
81
82
  - **ऑडिट** — अस्वीकृत कनेक्शन स्वामी सत्र में `permissionRules/network` जोड़ते हैं (वही अनुकूली `ignorable` द्वार), `/rules network` और settings पृष्ठ में ब्लॉक काउंटर व हाल की अवरोधन के साथ।
82
83
  - **निदान** — अवरुद्ध कनेक्शन `[network: …]` संदेश लेकर आते हैं जो अवरुद्ध लक्ष्य, निर्णय लेने वाला मोड या नियम, और समाधान बताता है।
@@ -172,6 +173,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
172
173
  - **कोई sandbox परिवर्तन नहीं।** OS-स्तरीय sandbox नीति sandbox सीम की है, इस प्लगइन की नहीं।
173
174
  - **ग़लत विन्यास की ज़ोरदार अस्वीकृति।** अज्ञात YAML फ़ील्ड, अज्ञात क्रियाएँ और ख़राब पैटर्न लोड पर अस्वीकृत।
174
175
  - **बैकट्रैकिंग सीमाएँ।** glob पैटर्न `maxGlobStars` असीमित स्टार विस्तार तक सीमित; regex-मोड पैटर्न नेस्टेड असीमित क्वांटिफ़ायर और क्वांटिफ़ाइड ओवरलैपिंग शाब्दिक विकल्प अस्वीकारते हैं।
176
+ - **बंद-विफल डायलिंग।** सादे HTTP अग्रेषण और CONNECT सुरंगें केवल निर्णीत पतों से जुड़ती हैं; जिस allow की निर्णय-प्रक्रिया ने कोई पता हल नहीं किया वह नाम डायल करने के बजाय 502 के साथ बंद-विफल होता है (issue #23)।
175
177
 
176
178
  ## Known limitations
177
179
 
@@ -210,7 +212,7 @@ node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
210
212
  pnpm install # node ^22.19 || >=24
211
213
  pnpm run typecheck # tsc, src + tests
212
214
  pnpm run lint # eslint, src + tests + scripts
213
- pnpm test # vitest: 280 tests, 23 files
215
+ pnpm test # vitest: 24 spec files
214
216
  pnpm run test:coverage # coverage gate (90/80/90/90)
215
217
  pnpm run build # tsc declarations + tsdown bundles (lib/)
216
218
  pnpm run pack:check # build + pack (the published artifact)
@@ -235,7 +237,7 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
235
237
 
236
238
  ## PerryLink DSH Plugin Family
237
239
 
238
- यह प्रोजेक्ट [PerryLink](https://github.com/PerryLink) द्वारा अनुरक्षित [37 DeepSeek Harness प्लगइनों](https://github.com/PerryLink) में से एक है। अगर यह आपकी मदद करता है, तो बाकी भी करेंगे:
240
+ यह प्रोजेक्ट [PerryLink](https://github.com/PerryLink) द्वारा अनुरक्षित [40 DeepSeek Harness प्लगइनों](https://github.com/PerryLink) में से एक है। अगर यह आपकी मदद करता है, तो बाकी भी करेंगे:
239
241
 
240
242
  | Plugin | One-liner |
241
243
  |---|---|
@@ -264,6 +266,7 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
264
266
  | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-समतुल्य रनटाइम शैली बदलाव | |
265
267
  | **[dsh-personal-directive](https://github.com/PerryLink/dsh-personal-directive)** | शीर्ष-बार टॉगल के साथ व्यक्तिगत निर्देश इंजेक्टर (फ्रेमवर्क संस्करण) |
266
268
  | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | माँग पर एजेंट कौशल के रूप में प्लगइन-विकास ज्ञान आधार | |
269
+ | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | |
267
270
  | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | मल्टी-चैनल अनुमोदन/प्रश्न ब्रिज: WeChat/Telegram/Feishu, सत्र कंसोल |
268
271
  | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | सामग्री-पता साक्ष्य और सीलबंद संस्करणों वाला सत्यापन-योग्य अनुसंधान-रिपोर्ट इंजन | |
269
272
  | **[dsh-score](https://github.com/PerryLink/dsh-score)** | DeepSeek Harness प्लगिनों की बहु-आयामी गुणवत्ता स्कोरिंग। | |
@@ -274,7 +277,16 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
274
277
  | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | DeepSeek Harness प्लगिनों के लिए पृथक इंस्टॉल-एंड-स्मोक टेस्ट ड्राइव। | |
275
278
  | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | TickTick/Dida365 कार्य ब्रिज: सत्र-हेडर पैनल + 11 टूल |
276
279
  | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | DeepSeek Harness के लिए वेंडर पैरामीटर अनुवाद और नियतात्मक JSON मरम्मत। | |
277
- | **[dsh-wechat](https://github.com/PerryLink/dsh-wechat)** | WeChat ↔ DSH ब्रिज (Tencent iLink bot): टेक्स्ट/इमेज/फ़ाइल/आवाज़, चैट में अनुमोदन |
280
+ | **[dsh-wechat](https://github.com/pan17/dsh-wechat)** | WeChat ↔ DSH ब्रिज (Tencent iLink bot): टेक्स्ट/इमेज/फ़ाइल/आवाज़, चैट में अनुमोदन |
281
+ | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | |
282
+ | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | |
283
+ | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | |
284
+ | **[dsh-kit](https://github.com/PerryLink/dsh-kit)** | One-command starter pack that installs the core family | |
285
+ | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | |
286
+ | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | |
287
+ | **[dsh-plugin-portal](https://github.com/PerryLink/dsh-plugin-portal)** | Zero-dependency static portal rendering the whole plugin family as one page | |
288
+ | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | |
289
+ | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | |
278
290
 
279
291
  ## License
280
292
 
package/README-pt.md CHANGED
@@ -8,6 +8,7 @@
8
8
  *Regras decidem o conhecido. Um modelo revisor decide o que não é.*
9
9
 
10
10
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-permission-rules)
11
12
  [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin)
12
13
  [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-permission-rules.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
13
14
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
@@ -76,7 +77,7 @@ Uma **política de rede em nível de processo** estilo Codex: o tráfego de subp
76
77
  - **`allow-all`** — o preset danger-full-access: permitir tudo.
77
78
  - **`auto`** (padrão) — segue o preset do sandbox; em hosts sem o serviço de política de sandbox resolve para `autoFallback` (`allow-all`).
78
79
 
79
- - **Correspondência** — `match.network` com `domains` / `ips` / `ports` / `schemes` (globs, curingas, CIDR, faixas de porta; portas YAML numéricas são aceitas). A extração de candidatos URL na rota quente `tools/pre-execute` dispara sobre argumentos de ferramentas web e URLs embutidas em texto de comando bash/pwsh; destinos de loopback podem curto-circuitar regras conforme a política `loopback`. Literais IPv6 mapeados em IPv4 são normalizados para a forma IPv4 antes da comparação, e o proxy conecta nos endereços usados na decisão, sem uma segunda resolução DNS.
80
+ - **Correspondência** — `match.network` com `domains` / `ips` / `ports` / `schemes` (globs, curingas, CIDR, faixas de porta; portas YAML numéricas são aceitas). A extração de candidatos URL na rota quente `tools/pre-execute` dispara sobre argumentos de ferramentas web e URLs embutidas em texto de comando bash/pwsh; destinos de loopback podem curto-circuitar regras conforme a política `loopback`. Literais IPv6 mapeados em IPv4 são normalizados para a forma IPv4 antes da comparação, e o proxy conecta nos endereços usados na decisão, sem uma segunda resolução DNS; uma conexão cuja adjudicação não resolveu nenhum endereço falha fechada (502) em vez de discar o nome.
80
81
  - **Encadeamento por proxy ascendente** — `network.upstreamProxy` (padrão `off`) envia as conexões que este proxy **permite** por um proxy ascendente: o CONNECT pede um túnel a ele (`CONNECT host:port`) e as requisições HTTP simples são encaminhadas em forma absoluta. Um destino bloqueado nunca chega ao proxy ascendente — ele continua recebendo o 403 estruturado deste plugin. Dois casos nunca encadeiam, mesmo com um proxy ascendente configurado: um destino de **loopback** (um proxy fora deste host não consegue rotear o loopback dele) e qualquer decisão produzida por uma **regra com escopo `ips`** (encadear entrega o nome do host ao proxy ascendente, então «a conexão chega a um endereço que as regras viram» — issue #21 — deixaria de valer exatamente onde as regras se importavam com o endereço; essas decisões continuam conectando direto no endereço adjudicado), além de um destino cujo esquema não tenha um proxy ascendente utilizável. O nome do host do próprio proxy ascendente é configuração do operador, não entrada do agente, e não é adjudicado por estas regras. Uma URL com credenciais nunca é emitida em claro: avisos, `/rules network` e o snapshot de settings mascaram a senha (`http://user:***@host:port`). Um proxy ascendente inalcançável, um timeout (10 s) ou uma resposta não 2xx resultam em **502** — não há, de propósito, queda silenciosa para conexão direta, para que uma má configuração continue visível.
81
82
  - **Auditoria** — conexões negadas anexam `permissionRules/network` à sessão proprietária (a mesma porta adaptativa `ignorable`), com contadores de bloqueio e intercepções recentes em `/rules network` e na página de settings.
82
83
  - **Diagnóstico** — conexões bloqueadas trazem uma mensagem `[network: …]` nomeando o destino bloqueado, o modo ou a regra que decidiu e a solução.
@@ -172,6 +173,7 @@ Todos os parâmetros são campos Schemastery `Config` (alteráveis pelo cordis.y
172
173
  - **Sem mudanças de sandbox.** A política de sandbox em nível de SO pertence à costura do sandbox, não a este plugin.
173
174
  - **Rejeição ruidosa de má configuração.** Campos YAML desconhecidos, ações desconhecidas e padrões ruins são rejeitados na carga.
174
175
  - **Limites de backtracking.** Padrões glob são limitados a `maxGlobStars` expansões de estrela não limitadas; padrões regex rejeitam quantificadores aninhados não limitados e alternâncias literais sobrepostas quantificadas.
176
+ - **Discagem à prova de falhas.** Encaminhamentos HTTP simples e túneis CONNECT conectam apenas a endereços adjudicados; um allow cuja adjudicação não resolveu nenhum endereço falha fechado com 502 em vez de discar o nome (issue #23).
175
177
 
176
178
  ## Known limitations
177
179
 
@@ -210,7 +212,7 @@ node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
210
212
  pnpm install # node ^22.19 || >=24
211
213
  pnpm run typecheck # tsc, src + tests
212
214
  pnpm run lint # eslint, src + tests + scripts
213
- pnpm test # vitest: 280 tests, 23 files
215
+ pnpm test # vitest: 24 spec files
214
216
  pnpm run test:coverage # coverage gate (90/80/90/90)
215
217
  pnpm run build # tsc declarations + tsdown bundles (lib/)
216
218
  pnpm run pack:check # build + pack (the published artifact)
@@ -235,7 +237,7 @@ Consulte [VERIFICATION.md](VERIFICATION.md) para o registro de verificação end
235
237
 
236
238
  ## PerryLink DSH Plugin Family
237
239
 
238
- Este projeto é um dos [37 plugins de DeepSeek Harness](https://github.com/PerryLink) mantidos por [PerryLink](https://github.com/PerryLink). Se este ajuda você, os outros provavelmente também:
240
+ Este projeto é um dos [40 plugins de DeepSeek Harness](https://github.com/PerryLink) mantidos por [PerryLink](https://github.com/PerryLink). Se este ajuda você, os outros provavelmente também:
239
241
 
240
242
  | Plugin | One-liner |
241
243
  |---|---|
@@ -264,6 +266,7 @@ Este projeto é um dos [37 plugins de DeepSeek Harness](https://github.com/Perry
264
266
  | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Troca de estilo em tempo de execução equivalente ao outputStyles do Claude Code | |
265
267
  | **[dsh-personal-directive](https://github.com/PerryLink/dsh-personal-directive)** | Injetor de diretivas pessoais com alternância na barra superior (edição framework) |
266
268
  | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | Base de conhecimento de desenvolvimento de plugins como habilidade de agente sob demanda | |
269
+ | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | |
267
270
  | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | Ponte multicanal de aprovação/perguntas: WeChat/Telegram/Feishu, console de sessão |
268
271
  | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | Motor de relatórios de pesquisa verificáveis com evidência endereçada por conteúdo | |
269
272
  | **[dsh-score](https://github.com/PerryLink/dsh-score)** | Pontuação de qualidade multidimensional para plugins de DeepSeek Harness. | |
@@ -274,7 +277,16 @@ Este projeto é um dos [37 plugins de DeepSeek Harness](https://github.com/Perry
274
277
  | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | Test drives isolados de instalação e smoke para plugins de DeepSeek Harness. | |
275
278
  | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | Ponte de tarefas TickTick/Dida365: painel no cabeçalho da sessão + 11 ferramentas |
276
279
  | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | Tradução de parâmetros entre fornecedores e reparo determinístico de JSON para DeepSeek Harness. | |
277
- | **[dsh-wechat](https://github.com/PerryLink/dsh-wechat)** | Ponte WeChat ↔ DSH (bot Tencent iLink): texto/imagem/arquivo/voz, aprovações no chat |
280
+ | **[dsh-wechat](https://github.com/pan17/dsh-wechat)** | Ponte WeChat ↔ DSH (bot Tencent iLink): texto/imagem/arquivo/voz, aprovações no chat |
281
+ | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | |
282
+ | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | |
283
+ | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | |
284
+ | **[dsh-kit](https://github.com/PerryLink/dsh-kit)** | One-command starter pack that installs the core family | |
285
+ | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | |
286
+ | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | |
287
+ | **[dsh-plugin-portal](https://github.com/PerryLink/dsh-plugin-portal)** | Zero-dependency static portal rendering the whole plugin family as one page | |
288
+ | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | |
289
+ | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | |
278
290
 
279
291
  ## License
280
292
 
package/README-zh.md CHANGED
@@ -8,6 +8,7 @@
8
8
  *规则裁决已知的。评审模型裁决未知的。*
9
9
 
10
10
  [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](LICENSE)
11
+ [![Gitee](https://img.shields.io/badge/Gitee-mirror-c71d23?logo=gitee)](https://gitee.com/perrylink/dsh-permission-rules)
11
12
  [![DSH plugin](https://img.shields.io/badge/dsh--plugin-✅-green)](https://github.com/topics/dsh-plugin)
12
13
  [![dsh-doctor](https://raw.githubusercontent.com/PerryLink/dsh-plugin-doctor/main/badges/PerryLink__dsh-permission-rules.svg)](https://github.com/PerryLink/dsh-plugin-doctor#verified-徽章)
13
14
  [![Node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-brightgreen.svg)](#)
@@ -76,7 +77,7 @@ Codex 风格的**进程级网络策略**:shell 子进程流量经内置本地
76
77
  - **`allow-all`** —— danger-full-access 预设:放行一切。
77
78
  - **`auto`**(默认)—— 跟随沙箱预设;无沙箱策略服务的宿主上解析为 `autoFallback`(`allow-all`)。
78
79
 
79
- - **匹配** —— `match.network` 用 `domains` / `ips` / `ports` / `schemes`(glob、通配符、CIDR、端口范围;数值型 YAML 端口可接受)。`tools/pre-execute` 热路径上的 URL 候选抽取作用于 web 工具参数与嵌入 bash/pwsh 命令文本的 URL;回环目标可按 `loopback` 策略短路规则。IPv4 映射的 IPv6 字面量在匹配前归一化为 IPv4 形式;代理按裁决到的地址建连,不做二次 DNS 解析。
80
+ - **匹配** —— `match.network` 用 `domains` / `ips` / `ports` / `schemes`(glob、通配符、CIDR、端口范围;数值型 YAML 端口可接受)。`tools/pre-execute` 热路径上的 URL 候选抽取作用于 web 工具参数与嵌入 bash/pwsh 命令文本的 URL;回环目标可按 `loopback` 策略短路规则。IPv4 映射的 IPv6 字面量在匹配前归一化为 IPv4 形式;代理按裁决到的地址建连,不做二次 DNS 解析——裁决未解析出任何地址的连接以 502 关闭失败,绝不按名字拨号。
80
81
  - **上游链式** —— `network.upstreamProxy`(默认 `off`)把本代理**放行**的连接送上上游代理:CONNECT 向上游要一条隧道(`CONNECT host:port`),纯 HTTP 请求以绝对形式转发给它。被拦截的目标永远到不了上游——它照旧收到本插件的结构化 403。即使配置了上游,两类情况也绝不链式:**回环**目标(本机之外的代理无法路由它的回环),以及由 **`ips` 作用域规则**产生的任何裁决(链式会把主机名交给上游,"连接落在规则看过的地址上"(issue #21)恰恰会在规则真正在意地址的地方不再成立;这些裁决仍按裁决到的地址直连),再加上目标 scheme 没有可用上游的情况。上游自身的主机名是运维配置而非 agent 输入,不参与这些规则的裁决。带凭据的 URL 绝不原样输出:警告、`/rules network` 与设置页快照都会把口令打码(`http://user:***@host:port`)。上游不可达、超时(10 秒)或返回非 2xx 一律 **502**——刻意没有静默回退直连,配置错误因此始终可见。
81
82
  - **审计** —— 被拒连接向所属会话追加 `permissionRules/network`(同样的自适应 `ignorable` 门),块计数器与近期拦截在 `/rules network` 与设置页展示。
82
83
  - **诊断** —— 被拦截的连接带 `[network: …]` 消息,点名被拦目标、做出裁决的模式或规则,以及处置办法。
@@ -172,6 +173,7 @@ dsh --profile web --dump-config | grep -A4 'id: permission-rules'
172
173
  - **不改沙箱。** OS 级沙箱策略属于沙箱接缝,与本插件无关。
173
174
  - **响亮地拒绝错误配置。** 未知 YAML 字段、未知 action 与坏模式在加载期被拒绝。
174
175
  - **回溯界限。** glob 模式以 `maxGlobStars` 限制无界星号展开;正则模式拒绝嵌套无界量词与量化重叠字面交替。
176
+ - **关闭失败的拨号。** 明文 HTTP 转发与 CONNECT 隧道只连接裁决到的地址;裁决未解析出任何地址的 allow 以 502 关闭失败,而不是按名字拨号(issue #23)。
175
177
 
176
178
  ## Known limitations
177
179
 
@@ -210,7 +212,7 @@ node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
210
212
  pnpm install # node ^22.19 || >=24
211
213
  pnpm run typecheck # tsc, src + tests
212
214
  pnpm run lint # eslint, src + tests + scripts
213
- pnpm test # vitest: 280 tests, 23 files
215
+ pnpm test # vitest: 24 spec files
214
216
  pnpm run test:coverage # coverage gate (90/80/90/90)
215
217
  pnpm run build # tsc declarations + tsdown bundles (lib/)
216
218
  pnpm run pack:check # build + pack (the published artifact)
@@ -235,7 +237,7 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
235
237
 
236
238
  ## PerryLink DSH Plugin Family
237
239
 
238
- 这是 [PerryLink](https://github.com/PerryLink) 维护的 [37 个 DeepSeek Harness 插件](https://github.com/PerryLink) 之一。如果它能帮到你,其他的也会:
240
+ 这是 [PerryLink](https://github.com/PerryLink) 维护的 [40 个 DeepSeek Harness 插件](https://github.com/PerryLink) 之一。如果它能帮到你,其他的也会:
239
241
 
240
242
  | Plugin | One-liner |
241
243
  |---|---|
@@ -264,6 +266,7 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
264
266
  | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles 等价的运行时风格切换 | |
265
267
  | **[dsh-personal-directive](https://github.com/PerryLink/dsh-personal-directive)** | 个人指令注入器:顶栏开关(框架版) |
266
268
  | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | 作为按需代理技能的插件开发知识库 | |
269
+ | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | |
267
270
  | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | 多渠道审批/提问桥接:微信/Telegram/飞书,会话控制台 |
268
271
  | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | 可验证研究报告引擎:内容寻址证据账本与封存版本 | |
269
272
  | **[dsh-score](https://github.com/PerryLink/dsh-score)** | DeepSeek Harness 插件的多维质量评分。 | |
@@ -274,7 +277,16 @@ node scripts/check-readme-sync.mjs # five-language README sync gate (also in C
274
277
  | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | DeepSeek Harness 插件的隔离试装冒烟。 | |
275
278
  | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | TickTick/滴答清单任务桥接:会话头面板 + 11 个工具 |
276
279
  | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | DeepSeek Harness 的厂商参数翻译与确定性 JSON 修复。 | |
277
- | **[dsh-wechat](https://github.com/PerryLink/dsh-wechat)** | 微信 ↔ DSH 桥接(Tencent iLink 机器人):文本/图片/文件/语音,聊天内审批卡片 |
280
+ | **[dsh-wechat](https://github.com/pan17/dsh-wechat)** | 微信 ↔ DSH 桥接(Tencent iLink 机器人):文本/图片/文件/语音,聊天内审批卡片 |
281
+ | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | |
282
+ | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | |
283
+ | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | |
284
+ | **[dsh-kit](https://github.com/PerryLink/dsh-kit)** | One-command starter pack that installs the core family | |
285
+ | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | |
286
+ | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | |
287
+ | **[dsh-plugin-portal](https://github.com/PerryLink/dsh-plugin-portal)** | Zero-dependency static portal rendering the whole plugin family as one page | |
288
+ | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | |
289
+ | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | |
278
290
 
279
291
  ## License
280
292
 
package/README.md CHANGED
@@ -77,7 +77,7 @@ A Codex-style **process-level network policy**: shell subprocess traffic flows t
77
77
  - **`allow-all`** — the danger-full-access preset: allow everything.
78
78
  - **`auto`** (default) — follows the sandbox preset; on hosts without the sandbox-policy service it resolves to `autoFallback` (`allow-all`).
79
79
 
80
- - **Matching** — `match.network` with `domains` / `ips` / `ports` / `schemes` (globs, wildcards, CIDRs, port ranges; numeric YAML ports are accepted). URL-candidate extraction on the `tools/pre-execute` hot path fires on web-tool arguments and URLs embedded in bash/pwsh command text; loopback targets can short-circuit rules per `loopback` policy. IPv4-mapped IPv6 literals are normalized to their IPv4 form before matching, and the proxy connects on the addresses the decision was made on — never a second DNS resolution.
80
+ - **Matching** — `match.network` with `domains` / `ips` / `ports` / `schemes` (globs, wildcards, CIDRs, port ranges; numeric YAML ports are accepted). URL-candidate extraction on the `tools/pre-execute` hot path fires on web-tool arguments and URLs embedded in bash/pwsh command text; loopback targets can short-circuit rules per `loopback` policy. IPv4-mapped IPv6 literals are normalized to their IPv4 form before matching, and the proxy connects on the addresses the decision was made on — never a second DNS resolution — a connection whose adjudication resolved no address fails closed (502) instead of dialing the name.
81
81
  - **Upstream chaining** — `network.upstreamProxy` (default `off`) sends the connections this proxy **allows** through an upstream proxy: CONNECT asks the upstream for a tunnel (`CONNECT host:port`) and plain-HTTP requests are forwarded to it in absolute form. A blocked target never reaches the upstream — it still gets this plugin's structured 403. Two cases never chain even when an upstream is configured: a **loopback** target (a proxy outside this host cannot route its loopback) and any decision produced by an **`ips`-scoped rule** (chaining hands the hostname to the upstream, so "the connection lands on an address the rules saw" — issue #21 — would stop holding exactly where the rules cared about the address; those decisions keep dialing the adjudicated address directly), plus a target whose scheme has no usable upstream. The upstream's own hostname is operator configuration, not agent input, and is not adjudicated by these rules. A URL carrying credentials is never emitted raw: warnings, `/rules network` and the settings snapshot mask the password (`http://user:***@host:port`). An unreachable upstream, a timeout (10 s), or a non-2xx answer yields **502** — there is deliberately no silent fallback to a direct dial, so a misconfiguration stays visible.
82
82
  - **Audit** — denied connections append `permissionRules/network` to the owning session (same adaptive `ignorable` gate), with block counters and recent interceptions in `/rules network` and the settings page.
83
83
  - **Diagnosis** — blocked connections carry a `[network: …]` message naming the blocked target, the mode or rule that decided, and the remediation.
@@ -173,6 +173,7 @@ All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id
173
173
  - **No sandbox changes.** OS-level sandbox policy belongs to the sandbox seam, not this plugin.
174
174
  - **Loud misconfiguration.** Unknown YAML fields, unknown actions, and bad patterns are rejected at load.
175
175
  - **Backtracking bounds.** Glob patterns are capped at `maxGlobStars` unbounded star expansions; regex-mode patterns reject nested unbounded quantifiers and quantified overlapping literal alternations.
176
+ - **Fail-closed dialing.** Plain-HTTP forwards and CONNECT tunnels connect only to adjudicated addresses; an allow whose adjudication resolved no address fails closed with 502 instead of dialing the name (issue #23).
176
177
 
177
178
  ## Known limitations
178
179
 
@@ -211,7 +212,7 @@ node scripts/repair-session-logs.mjs strip [--home DIR] [--dry-run]
211
212
  pnpm install # node ^22.19 || >=24
212
213
  pnpm run typecheck # tsc, src + tests
213
214
  pnpm run lint # eslint, src + tests + scripts
214
- pnpm test # vitest: 280 tests, 23 files
215
+ pnpm test # vitest: 24 spec files
215
216
  pnpm run test:coverage # coverage gate (90/80/90/90)
216
217
  pnpm run build # tsc declarations + tsdown bundles (lib/)
217
218
  pnpm run pack:check # build + pack (the published artifact)
@@ -236,7 +237,7 @@ See [VERIFICATION.md](VERIFICATION.md) for the headless end-to-end verification
236
237
 
237
238
  ## PerryLink DSH Plugin Family
238
239
 
239
- This project is one of the [37 DeepSeek Harness plugins](https://github.com/PerryLink) maintained by [PerryLink](https://github.com/PerryLink). If this one helps you, the others likely will too:
240
+ This project is one of the [40 DeepSeek Harness plugins](https://github.com/PerryLink) maintained by [PerryLink](https://github.com/PerryLink). If this one helps you, the others likely will too:
240
241
 
241
242
  | Plugin | One-liner |
242
243
  |---|---|
@@ -265,6 +266,7 @@ This project is one of the [37 DeepSeek Harness plugins](https://github.com/Perr
265
266
  | **[dsh-output-styles](https://github.com/PerryLink/dsh-output-styles)** | Claude Code outputStyles-equivalent runtime style switching | |
266
267
  | **[dsh-personal-directive](https://github.com/PerryLink/dsh-personal-directive)** | Personal directive injector with top-bar toggle (framework edition) |
267
268
  | **[dsh-plugin-guide](https://github.com/PerryLink/dsh-plugin-guide)** | Plugin-development knowledge base as an on-demand agent skill | |
269
+ | **[dsh-plugin-doctor](https://github.com/PerryLink/dsh-plugin-doctor)** | Zero-dependency static + sandbox smoke detector for DSH plugins | |
268
270
  | **[dsh-reach](https://github.com/PerryLink/dsh-reach)** | Multi-channel approval/question bridge: WeChat/Telegram/Feishu, session console |
269
271
  | **[dsh-research-report](https://github.com/PerryLink/dsh-research-report)** | Verifiable research-report engine: content-addressed evidence ledger and sealed versions | |
270
272
  | **[dsh-score](https://github.com/PerryLink/dsh-score)** | Multi-dimensional quality scoring for DeepSeek Harness plugins. | |
@@ -275,7 +277,16 @@ This project is one of the [37 DeepSeek Harness plugins](https://github.com/Perr
275
277
  | **[dsh-test-drive](https://github.com/PerryLink/dsh-test-drive)** | Isolated install-and-smoke test drives for DeepSeek Harness plugins. | |
276
278
  | **[dsh-ticktick](https://github.com/PerryLink/dsh-ticktick)** | TickTick/Dida365 task bridge: session-header panel + 11 tools |
277
279
  | **[dsh-translate](https://github.com/PerryLink/dsh-translate)** | Vendor parameter translation and deterministic JSON repair for DeepSeek Harness. | |
278
- | **[dsh-wechat](https://github.com/PerryLink/dsh-wechat)** | WeChat ↔ DSH bridge (Tencent iLink bot): text/image/file/voice, approvals in chat |
280
+ | **[dsh-wechat](https://github.com/pan17/dsh-wechat)** | WeChat ↔ DSH bridge (Tencent iLink bot): text/image/file/voice, approvals in chat |
281
+ | **[dsh-autotier](https://github.com/PerryLink/dsh-autotier)** | Automatic strong/cheap model-tier routing with deterministic risk guards and a `/tier` command | |
282
+ | **[dsh-catalog](https://github.com/PerryLink/dsh-catalog)** | DSH Desktop Market standard catalog source for the PerryLink family | |
283
+ | **[dsh-cert-mcp](https://github.com/PerryLink/dsh-cert-mcp)** | Read-only MCP server exposing the certification registry: grades, snapshots and five-dimension evidence | |
284
+ | **[dsh-kit](https://github.com/PerryLink/dsh-kit)** | One-command starter pack that installs the core family | |
285
+ | **[dsh-plugin-certification](https://github.com/PerryLink/dsh-plugin-certification)** | Community certification registry with repro-checkable grades and badges | |
286
+ | **[dsh-plugin-kit](https://github.com/PerryLink/dsh-plugin-kit)** | Shared zero-runtime-dependency toolkit for the PerryLink DSH plugins | |
287
+ | **[dsh-plugin-portal](https://github.com/PerryLink/dsh-plugin-portal)** | Zero-dependency static portal rendering the whole plugin family as one page | |
288
+ | **[dsh-plugin-upgrade-015](https://github.com/PerryLink/dsh-plugin-upgrade-015)** | Merged `0.1.3-alpha.1` → `0.1.5-rc.1` upgrade corridor card plus a zero-dependency seam scanner | |
289
+ | **[dsh-team-rooms](https://github.com/PerryLink/dsh-team-rooms)** | Cross-session team rooms: shared message bus, task board and timeline | |
279
290
 
280
291
  ### Install from the DSH Desktop Market
281
292
 
package/lib/client.js CHANGED
@@ -4128,6 +4128,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4128
4128
  * keeps the host and client codecs from ever drifting apart.
4129
4129
  * @module dsh-permission-rules/wire
4130
4130
  */
4131
+ /**
4132
+ * Strict wire codec carrying BOTH published and checkout faces: the `schema`
4133
+ * field feeds the npm-published 0.1.5-rc.2 line, while `create` feeds the
4134
+ * 0.1.6-alpha.2+ line, whose typert-loader requires the factory on every
4135
+ * codec and materializes schemas lazily on first use. Built through a
4136
+ * variable, so neither typecheck ruler flags the other face's field as excess.
4137
+ */
4138
+ function strictWire(typeSymbol, schema) {
4139
+ return Object.freeze({
4140
+ mode: "strict",
4141
+ typeSymbol,
4142
+ schema,
4143
+ create: () => schema
4144
+ });
4145
+ }
4131
4146
  /** Strict wire schema for {@link PermissionRulesSnapshot}. */
4132
4147
  const PERMISSION_RULES_SNAPSHOT_SCHEMA = object({
4133
4148
  enabled: boolean(),
@@ -4240,11 +4255,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4240
4255
  method: "networkStatus",
4241
4256
  invocation: Object.freeze({ kind: "direct" }),
4242
4257
  parameters: Object.freeze([]),
4243
- result: Object.freeze({
4244
- mode: "strict",
4245
- typeSymbol: "dsh-permission-rules/types#PermissionRulesSnapshot",
4246
- schema: PERMISSION_RULES_SNAPSHOT_SCHEMA
4247
- }),
4258
+ result: strictWire("dsh-permission-rules/types#PermissionRulesSnapshot", PERMISSION_RULES_SNAPSHOT_SCHEMA),
4248
4259
  sourceLocation: Object.freeze({
4249
4260
  file: "src/wire.ts",
4250
4261
  line: 1,
@@ -4262,17 +4273,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4262
4273
  name: "path",
4263
4274
  wire: "path",
4264
4275
  source: "json",
4265
- codec: Object.freeze({
4266
- mode: "strict",
4267
- typeSymbol: "dsh-permission-rules/types#RulesReadRequestPath",
4268
- schema: string()
4269
- })
4276
+ codec: strictWire("dsh-permission-rules/types#RulesReadRequestPath", string())
4270
4277
  })]),
4271
- result: Object.freeze({
4272
- mode: "strict",
4273
- typeSymbol: "dsh-permission-rules/types#RulesReadResult",
4274
- schema: RULES_READ_SCHEMA
4275
- }),
4278
+ result: strictWire("dsh-permission-rules/types#RulesReadResult", RULES_READ_SCHEMA),
4276
4279
  sourceLocation: Object.freeze({
4277
4280
  file: "src/wire.ts",
4278
4281
  line: 1,
@@ -4290,26 +4293,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4290
4293
  name: "path",
4291
4294
  wire: "path",
4292
4295
  source: "json",
4293
- codec: Object.freeze({
4294
- mode: "strict",
4295
- typeSymbol: "dsh-permission-rules/types#RulesSaveRequestPath",
4296
- schema: string()
4297
- })
4296
+ codec: strictWire("dsh-permission-rules/types#RulesSaveRequestPath", string())
4298
4297
  }), Object.freeze({
4299
4298
  name: "text",
4300
4299
  wire: "text",
4301
4300
  source: "json",
4302
- codec: Object.freeze({
4303
- mode: "strict",
4304
- typeSymbol: "dsh-permission-rules/types#RulesSaveRequestText",
4305
- schema: string()
4306
- })
4301
+ codec: strictWire("dsh-permission-rules/types#RulesSaveRequestText", string())
4307
4302
  })]),
4308
- result: Object.freeze({
4309
- mode: "strict",
4310
- typeSymbol: "dsh-permission-rules/types#RulesSaveResult",
4311
- schema: RULES_SAVE_SCHEMA
4312
- }),
4303
+ result: strictWire("dsh-permission-rules/types#RulesSaveResult", RULES_SAVE_SCHEMA),
4313
4304
  sourceLocation: Object.freeze({
4314
4305
  file: "src/wire.ts",
4315
4306
  line: 1,
@@ -4324,11 +4315,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4324
4315
  method: "reload",
4325
4316
  invocation: Object.freeze({ kind: "direct" }),
4326
4317
  parameters: Object.freeze([]),
4327
- result: Object.freeze({
4328
- mode: "strict",
4329
- typeSymbol: "dsh-permission-rules/types#RulesReloadResult",
4330
- schema: RULES_RELOAD_SCHEMA
4331
- }),
4318
+ result: strictWire("dsh-permission-rules/types#RulesReloadResult", RULES_RELOAD_SCHEMA),
4332
4319
  sourceLocation: Object.freeze({
4333
4320
  file: "src/wire.ts",
4334
4321
  line: 1,
@@ -4349,17 +4336,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4349
4336
  name: "request",
4350
4337
  wire: "request",
4351
4338
  source: "json",
4352
- codec: Object.freeze({
4353
- mode: "strict",
4354
- typeSymbol: "dsh-permission-rules/types#AllowHostRequest",
4355
- schema: ALLOW_HOST_REQUEST_SCHEMA
4356
- })
4339
+ codec: strictWire("dsh-permission-rules/types#AllowHostRequest", ALLOW_HOST_REQUEST_SCHEMA)
4357
4340
  })]),
4358
- result: Object.freeze({
4359
- mode: "strict",
4360
- typeSymbol: "dsh-permission-rules/types#AllowHostResult",
4361
- schema: ALLOW_HOST_RESULT_SCHEMA
4362
- }),
4341
+ result: strictWire("dsh-permission-rules/types#AllowHostResult", ALLOW_HOST_RESULT_SCHEMA),
4363
4342
  sourceLocation: Object.freeze({
4364
4343
  file: "src/wire.ts",
4365
4344
  line: 1,