dsh-permission-rules 0.2.0
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 +50 -0
- package/LICENSE +201 -0
- package/README.es.md +182 -0
- package/README.hi.md +182 -0
- package/README.md +182 -0
- package/README.pt.md +182 -0
- package/README.zh.md +182 -0
- package/cordis.patch.yml +16 -0
- package/docs/rules-format.en.md +105 -0
- package/docs/rules-format.md +103 -0
- package/docs/rules-format.schema.json +117 -0
- package/lib/index.js +1606 -0
- package/lib/types/config.d.ts +83 -0
- package/lib/types/config.d.ts.map +1 -0
- package/lib/types/config.js +72 -0
- package/lib/types/config.js.map +1 -0
- package/lib/types/events.d.ts +68 -0
- package/lib/types/events.d.ts.map +1 -0
- package/lib/types/events.js +21 -0
- package/lib/types/events.js.map +1 -0
- package/lib/types/glob.d.ts +55 -0
- package/lib/types/glob.d.ts.map +1 -0
- package/lib/types/glob.js +307 -0
- package/lib/types/glob.js.map +1 -0
- package/lib/types/index.d.ts +25 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/index.js +25 -0
- package/lib/types/index.js.map +1 -0
- package/lib/types/prose.d.ts +66 -0
- package/lib/types/prose.d.ts.map +1 -0
- package/lib/types/prose.js +143 -0
- package/lib/types/prose.js.map +1 -0
- package/lib/types/rules.d.ts +226 -0
- package/lib/types/rules.d.ts.map +1 -0
- package/lib/types/rules.js +581 -0
- package/lib/types/rules.js.map +1 -0
- package/lib/types/runtime.d.ts +175 -0
- package/lib/types/runtime.d.ts.map +1 -0
- package/lib/types/runtime.js +497 -0
- package/lib/types/runtime.js.map +1 -0
- package/package.json +103 -0
- package/scripts/repair-session-logs.mjs +327 -0
- package/src/config.ts +141 -0
- package/src/events.ts +75 -0
- package/src/glob.ts +303 -0
- package/src/index.ts +27 -0
- package/src/prose.ts +206 -0
- package/src/rules.ts +695 -0
- package/src/runtime.ts +504 -0
package/README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🛡️ dsh-permission-rules
|
|
4
|
+
|
|
5
|
+
**Claude Code-style declarative permission rules for DeepSeek Harness.**
|
|
6
|
+
|
|
7
|
+
*Rules decide what is known. A reviewer model decides what is not.*
|
|
8
|
+
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/topics/dsh-plugin)
|
|
11
|
+
[](#)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-permission-rules/actions)
|
|
13
|
+
[](https://github.com/PerryLink/dsh-permission-rules/releases)
|
|
14
|
+
|
|
15
|
+
[English](README.md) · [简体中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## What it does
|
|
22
|
+
|
|
23
|
+
`dsh-permission-rules` puts an ordered **`allow` / `deny` / `ask`** rule list in front of every tool call on the `tools/pre-execute` waterfall — deterministic, instant, auditable, and written by you in plain YAML:
|
|
24
|
+
|
|
25
|
+
- **`deny`** blocks the call. The rule's `reason` becomes the model-visible error, so the agent learns *why* instead of retrying blindly.
|
|
26
|
+
- **`ask`** rides the official approval seam. Mount `dsh-auto-review` alongside and the question is settled by a second model; otherwise a human answers; with neither, the harness fails closed.
|
|
27
|
+
- **`allow`** (and no-match) strictly delegates via `next()` — downstream listeners are never short-circuited.
|
|
28
|
+
|
|
29
|
+
Every hit **and** every passthrough is audit-logged as a `permissionRules/decision` session event (log-only — nothing extra is injected into the model context).
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
tools/pre-execute waterfall approval/request waterfall (answerer chain)
|
|
33
|
+
│ │
|
|
34
|
+
dsh-permission-rules dsh-auto-review answerer
|
|
35
|
+
· first-match rules in file order ┌───────────────┴──────────────┐
|
|
36
|
+
· deny/ask claim the call │ AI verdict (second model) │ no ── next() ──▶ human UI
|
|
37
|
+
· allow/passthrough → next() └───────────────┬──────────────┘
|
|
38
|
+
│ deny ──▶ denied tool result │ allowed-once / rejected
|
|
39
|
+
│ ask ──▶ ctx.approval ──────────────────────────┘
|
|
40
|
+
│
|
|
41
|
+
audit: permissionRules/decision → approval/asked → autoReview/verdict → approval/decided
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Why rules *and* a reviewer?
|
|
45
|
+
|
|
46
|
+
A second model answers *"is THIS call okay?"* with judgment, but costs a round-trip and can be wrong. Declarative rules answer deterministically, instantly, and without a model — but only cover what an admin wrote down. Combined, you get the **"rules first, AI backstop"** loop: rules decide the known, the reviewer decides the unknown.
|
|
47
|
+
|
|
48
|
+
## Features
|
|
49
|
+
|
|
50
|
+
- ✅ **Three-state semantics** — `allow`, `deny`, `ask`, evaluated in file order, first match wins
|
|
51
|
+
- ✅ **Rich matching** — tool-name globs (including `mcp__*`), argument key/value globs **or** regexes (with `!pattern` negation and an `absent` key dimension), workspace-relative path globs extracted from documented argument keys at **any nesting depth**, and `when` host conditions (env vars, platform)
|
|
52
|
+
- ✅ **Hierarchical rule files** — optional `searchUp` merges every `.dsh/rules.yaml` from the session cwd to the filesystem root, nearest first, so a child project can override parent rules
|
|
53
|
+
- ✅ **Rule metadata** — `enabled: false`, `description`, `tags`; `/rules` warns about rules shadowed by an earlier catch-all
|
|
54
|
+
- ✅ **Waterfall-safe** — `allow`/passthrough always call `next()`; only `deny`/`ask` short-circuit
|
|
55
|
+
- ✅ **Official approval seam** — `ask` flows through `ctx.approval`; never re-implemented, never bypassed
|
|
56
|
+
- ✅ **Full audit** — `permissionRules/decision` events carry the rule action AND the final outcome for every call; `/rules decisions` replays the trail in-session
|
|
57
|
+
- ✅ **Dry-run testing** — `/rules test <tool> <json-args>` evaluates the active rules without executing anything
|
|
58
|
+
- ✅ **Hot reload** — Chokidar watch with debounce; a broken edit keeps the previous rules, never crashes
|
|
59
|
+
- ✅ **Fail loud** — invalid YAML, unknown actions/fields, bad globs/regexes, backtracking-prone patterns, or > `maxRules` fail the load
|
|
60
|
+
- ✅ **Bounded hot path** — precompiled matchers, O(rules × patterns), capped by `maxRules`; glob backtracking degree capped by `maxGlobStars`
|
|
61
|
+
|
|
62
|
+
## Quick start
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# 1. install the bundle into your profile
|
|
66
|
+
dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
67
|
+
|
|
68
|
+
# or from a packed tarball (built artifacts, no build permission needed)
|
|
69
|
+
pnpm pack
|
|
70
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
|
|
71
|
+
|
|
72
|
+
# 2. restart
|
|
73
|
+
dsh --profile web
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Then create the rules file for your project and start a session in it:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
# <project>/.dsh/rules.yaml
|
|
80
|
+
rules:
|
|
81
|
+
- match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
|
|
82
|
+
action: deny
|
|
83
|
+
reason: "No pushes from protected paths"
|
|
84
|
+
|
|
85
|
+
- match: { tools: [edit, write] }
|
|
86
|
+
action: ask
|
|
87
|
+
reason: "File writes need confirmation"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
dsh --profile web --dump-config | grep -A4 'id: permission-rules' # verify the row
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
A complete 5-rule security baseline and the full schema live in [docs/rules-format.en.md](docs/rules-format.en.md).
|
|
95
|
+
|
|
96
|
+
## Configuration
|
|
97
|
+
|
|
98
|
+
All tunables are Schemastery `Config` fields (changeable from cordis.yml). An id-targeted override replaces the whole row — restate every key you need.
|
|
99
|
+
|
|
100
|
+
| Key | Default | Meaning |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `rulesFile` | `.dsh/rules.yaml` | Rule file location; relative = resolved against the calling session's cwd, absolute = global and validated at mount |
|
|
103
|
+
| `fallbackPath` | *(none)* | Rule file used when per-cwd discovery finds nothing; validated at mount |
|
|
104
|
+
| `badFilePolicy` | `fail` | Bad rule file: `fail` errors the pending tool call loudly (reloads keep the previous rules); `ignore-with-warning` warns and continues empty |
|
|
105
|
+
| `maxRules` | `256` | Hard cap on rule count across the effective source chain; larger files fail the load |
|
|
106
|
+
| `maxCachedWorkspaces` | `512` | Hard cap on cached per-workspace rule loads; the least-recently-used workspace (and its watcher) is evicted beyond it |
|
|
107
|
+
| `patternMode` | `glob` | `params`/`paths`/`when.env` pattern flavor: `glob` or `regex` (tool names are always globs) |
|
|
108
|
+
| `watch` | `true` | Chokidar watch + reload on change |
|
|
109
|
+
| `watchStabilityThresholdMs` | `200` | Reload debounce window (ms) |
|
|
110
|
+
| `language` | `en` | `/rules` output language: `en`, `zh`, `es`, `pt`, `hi` (`en`/`zh` are the reference translations) |
|
|
111
|
+
| `caseInsensitivePaths` | *(win32)* | `paths` patterns and workspace-root comparison ignore ASCII case; defaults to `true` on Windows, `false` elsewhere |
|
|
112
|
+
| `audit` | `all` | Audit granularity: `all` logs every hit AND passthrough; `hits` skips passthrough events |
|
|
113
|
+
| `searchUp` | `false` | Walk parent directories from the session cwd and merge every found rule file, nearest first |
|
|
114
|
+
| `maxGlobStars` | `2` | Hard cap on unbounded `*`/`**` quantifiers per glob pattern (backtracking-degree bound) |
|
|
115
|
+
|
|
116
|
+
### Session commands
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
/rules list the active rules, their source files, and any last-reload error
|
|
120
|
+
/rules reload re-read the rule-file chain for this workspace
|
|
121
|
+
/rules decisions [n] show the last n permission decisions of this session (default 10)
|
|
122
|
+
/rules test <tool> <json> dry-evaluate the rules against a hypothetical call, e.g. /rules test bash {"command":"git push origin main"}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Command output is UI-only — the model learns the rules only through the tool results they produce. `language` picks the output language. A JSON Schema for the rule file ships at [docs/rules-format.schema.json](docs/rules-format.schema.json) (wire it up with `# yaml-language-server: $schema=...` for editor completion).
|
|
126
|
+
|
|
127
|
+
## Collaborating with dsh-auto-review
|
|
128
|
+
|
|
129
|
+
- `dsh-permission-rules` produces `ask`; `dsh-auto-review` answers on the `approval/request` waterfall with a read-only second-model verdict (or delegates to humans). Mount both for the full closed loop.
|
|
130
|
+
- Integration-tested (`test/integration.spec.ts`): `permissionRules/decision` → `approval/asked` → `autoReview/verdict` → `approval/decided`, with the reviewer replaced by a scripted mock.
|
|
131
|
+
- The `never` approval policy and every fail-closed guarantee of the [official harness](https://github.com/deepseek-ai/deepseek-harness) stay untouched.
|
|
132
|
+
|
|
133
|
+
## Security boundaries
|
|
134
|
+
|
|
135
|
+
- **Policy, not a kernel.** `paths` candidates come only from a documented set of argument keys (at any nesting depth, depth-capped), and only workspace-relative paths match.
|
|
136
|
+
- **No reviewer here.** The plugin never spawns subagents or calls models — producing an `ask` decision is the end of its work.
|
|
137
|
+
- **No sandbox changes.** OS-level sandbox policy belongs to the sandbox seam, not this plugin.
|
|
138
|
+
- **Loud misconfiguration.** Unknown YAML fields, unknown actions, and bad patterns are rejected at load, never silently ignored.
|
|
139
|
+
- **Backtracking bounds.** Glob patterns are capped at `maxGlobStars` unbounded star expansions; regex-mode patterns reject nested unbounded quantifiers and quantified overlapping literal alternations. (Regex chains like `\d+\.\d+\.\d+` stay allowed — regex mode is the escape hatch, glob mode is the guarded default.)
|
|
140
|
+
|
|
141
|
+
## Related work
|
|
142
|
+
|
|
143
|
+
- [Andy8647/dsh-auto-approval](https://github.com/Andy8647/dsh-auto-approval) — two-state allow/deny classifier with its own file-log audit; this plugin adds the full three-state semantics, declarative YAML rules, session-log audit, and `next()`-safe delegation.
|
|
144
|
+
- `Drifter-yh/dsh-tool-policy` — deny-by-default tool policy; documented here to avoid duplicate implementation.
|
|
145
|
+
- `dsh-auto-review` — the AI-backstop half of the loop this plugin fronts.
|
|
146
|
+
|
|
147
|
+
## Known limitations
|
|
148
|
+
|
|
149
|
+
- `permissionRules/decision` is appended with the envelope's `ignorable: true` marker, so any harness build loads the log — readers that do not know the out-of-repo type simply skip the audit record instead of refusing the session. (rc.6 hosts accept and ignore the marker, keeping the exact pre-marker behavior; sessions written on rc.6 hosts lack the marker and may need `scripts/repair-session-logs.mjs` before loading on hosts with required-on-read semantics.)
|
|
150
|
+
- `paths` candidates are heuristic: only the documented argument keys feed path matching, and workspace-relative matching is ASCII-case-insensitive only when `caseInsensitivePaths` is on.
|
|
151
|
+
- Globs are a conservative subset (no brace expansion) — write two patterns, or use regex mode.
|
|
152
|
+
- The regex backtracking guard is structural, not exhaustive: alternation-ambiguity cases without literal prefixes (e.g. crafted lookarounds) are the author's responsibility; prefer glob mode for untrusted files.
|
|
153
|
+
|
|
154
|
+
## Session log repair
|
|
155
|
+
|
|
156
|
+
Session logs written before the `ignorable` marker existed can be refused by newer harness builds (`SessionFormatUnsupportedError`). The shipped `scripts/repair-session-logs.mjs` rewrites only the targeted audit rows to carry `ignorable: true`, frame-preserving, with backups:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
node scripts/repair-session-logs.mjs scan [--home DIR] # report foreign rows, change nothing
|
|
160
|
+
node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`--home` defaults to `$DSH_HOME/sessions` (or `~/.dsh/sessions`). See the script header for the full contract.
|
|
164
|
+
|
|
165
|
+
## Development
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
pnpm install # node ^22.19 || >=24
|
|
169
|
+
pnpm run typecheck # tsc, src + tests
|
|
170
|
+
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
+
pnpm test # vitest: 106 tests, 8 suites
|
|
172
|
+
pnpm run test:coverage # coverage gate (90/80/90/90)
|
|
173
|
+
pnpm run build # tsc declarations + tsdown bundles (lib/)
|
|
174
|
+
pnpm run pack:check # build + pack (the published artifact)
|
|
175
|
+
node scripts/check-readme-sync.mjs # five-language README sync gate (also in CI)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
See [VERIFICATION.md](VERIFICATION.md) for the headless end-to-end verification record (deny blocking a shell tool, ask routing through the approval seam, `--dump-config`).
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
[Apache License 2.0](LICENSE) © 2026 dsh-permission-rules contributors
|
package/README.pt.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🛡️ dsh-permission-rules
|
|
4
|
+
|
|
5
|
+
**Regras de permissão declarativas estilo Claude Code para o DeepSeek Harness.**
|
|
6
|
+
|
|
7
|
+
*As regras decidem o que é conhecido. Um modelo revisor decide o que não é.*
|
|
8
|
+
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/topics/dsh-plugin)
|
|
11
|
+
[](#)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-permission-rules/actions)
|
|
13
|
+
[](https://github.com/PerryLink/dsh-permission-rules/releases)
|
|
14
|
+
|
|
15
|
+
[English](README.md) · [简体中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## O que ele faz
|
|
22
|
+
|
|
23
|
+
O `dsh-permission-rules` coloca uma lista ordenada de regras **`allow` / `deny` / `ask`** na frente de cada chamada de ferramenta no waterfall `tools/pre-execute` — determinístico, instantâneo, auditável e escrito por você em YAML puro:
|
|
24
|
+
|
|
25
|
+
- **`deny`** bloqueia a chamada. O `reason` da regra vira o erro visível para o modelo, para que o agente aprenda o *porquê* em vez de tentar de novo às cegas.
|
|
26
|
+
- **`ask`** usa o seam de aprovação oficial. Monte o `dsh-auto-review` junto e a pergunta é decidida por um segundo modelo; caso contrário, um humano responde; sem nenhum dos dois, o harness falha fechado (fail-closed).
|
|
27
|
+
- **`allow`** (e a ausência de correspondência) delega estritamente via `next()` — os listeners seguintes nunca são curto-circuitados.
|
|
28
|
+
|
|
29
|
+
Cada acerto **e** cada passagem direta é auditado como evento de sessão `permissionRules/decision` (somente registro — nada extra é injetado no contexto do modelo).
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
waterfall tools/pre-execute waterfall approval/request (cadeia de answerers)
|
|
33
|
+
│ │
|
|
34
|
+
dsh-permission-rules answerer dsh-auto-review
|
|
35
|
+
· primeira correspondência em ordem ┌───────────────────┴──────────────┐
|
|
36
|
+
· deny/ask reivindicam a chamada │ veredito de IA (segundo modelo) │ não ── next() ──▶ UI humana
|
|
37
|
+
· allow/passagem → next() └───────────────────┬──────────────┘
|
|
38
|
+
│ deny ──▶ resultado negado │ allowed-once / rejected
|
|
39
|
+
│ ask ──▶ ctx.approval ────────────────────────┘
|
|
40
|
+
│
|
|
41
|
+
auditoria: permissionRules/decision → approval/asked → autoReview/verdict → approval/decided
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Por que regras *e* um revisor?
|
|
45
|
+
|
|
46
|
+
Um segundo modelo responde *"ESTA chamada é segura?"* com julgamento, mas custa uma ida e volta e pode errar. Regras declarativas respondem de forma determinística, instantânea e sem modelo — mas só cobrem o que um administrador escreveu. Combinados, você obtém o ciclo **"regras primeiro, IA de apoio"**: as regras decidem o conhecido, o revisor decide o desconhecido.
|
|
47
|
+
|
|
48
|
+
## Funcionalidades
|
|
49
|
+
|
|
50
|
+
- ✅ **Semântica de três estados** — `allow`, `deny`, `ask`, avaliadas na ordem do arquivo; a primeira correspondência vence
|
|
51
|
+
- ✅ **Correspondência rica** — globs de nome de ferramenta (incluindo `mcp__*`), globs **ou** regex de chave/valor de argumentos (com negação `!pattern` e a dimensão de chave `absent`), globs de caminhos relativos ao workspace extraídos de chaves de argumentos documentadas em **qualquer profundidade de aninhamento**, e condições de host `when` (variáveis de ambiente, plataforma)
|
|
52
|
+
- ✅ **Arquivos de regras hierárquicos** — `searchUp` opcional combina cada `.dsh/rules.yaml` do cwd da sessão até a raiz do sistema de arquivos, o mais próximo primeiro, para que um projeto filho possa sobrescrever regras do pai
|
|
53
|
+
- ✅ **Metadados de regras** — `enabled: false`, `description`, `tags`; `/rules` avisa sobre regras encobertas por um catch-all anterior
|
|
54
|
+
- ✅ **Seguro para o waterfall** — `allow`/passagem sempre chamam `next()`; somente `deny`/`ask` curto-circuitam
|
|
55
|
+
- ✅ **Seam de aprovação oficial** — `ask` flui por `ctx.approval`; nunca reimplementado, nunca contornado
|
|
56
|
+
- ✅ **Auditoria completa** — os eventos `permissionRules/decision` carregam a ação da regra E o resultado final de cada chamada; `/rules decisions` reproduz o rastro na sessão
|
|
57
|
+
- ✅ **Teste em seco** — `/rules test <tool> <json-args>` avalia as regras ativas sem executar nada
|
|
58
|
+
- ✅ **Recarga a quente** — vigilância Chokidar com debounce; uma edição quebrada mantém as regras anteriores, nunca quebra
|
|
59
|
+
- ✅ **Falha ruidosa** — YAML inválido, ações/campos desconhecidos, globs/regex ruins, padrões propensos a backtracking ou > `maxRules` fazem a carga falhar
|
|
60
|
+
- ✅ **Caminho quente limitado** — matchers pré-compilados, O(regras × padrões), limitado por `maxRules`; o grau de backtracking de glob limitado por `maxGlobStars`
|
|
61
|
+
|
|
62
|
+
## Início rápido
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# 1. instale o bundle no seu perfil
|
|
66
|
+
dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
67
|
+
|
|
68
|
+
# ou a partir de um tarball empacotado (artefatos compilados, sem permissão de build)
|
|
69
|
+
pnpm pack
|
|
70
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
|
|
71
|
+
|
|
72
|
+
# 2. reinicie
|
|
73
|
+
dsh --profile web
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Depois crie o arquivo de regras do seu projeto e inicie uma sessão nele:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
# <project>/.dsh/rules.yaml
|
|
80
|
+
rules:
|
|
81
|
+
- match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
|
|
82
|
+
action: deny
|
|
83
|
+
reason: "Sem push a partir de caminhos protegidos"
|
|
84
|
+
|
|
85
|
+
- match: { tools: [edit, write] }
|
|
86
|
+
action: ask
|
|
87
|
+
reason: "Escrita de arquivos precisa de confirmação"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
dsh --profile web --dump-config | grep -A4 'id: permission-rules' # verifique a linha
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Uma linha de base de segurança completa com 5 regras e o esquema completo estão em [docs/rules-format.en.md](docs/rules-format.en.md).
|
|
95
|
+
|
|
96
|
+
## Configuração
|
|
97
|
+
|
|
98
|
+
Todos os ajustes são campos `Config` do Schemastery (alteráveis no cordis.yml). Uma sobrescrita direcionada por id substitui a linha inteira — reescreva todas as chaves que você precisa.
|
|
99
|
+
|
|
100
|
+
| Chave | Padrão | Significado |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `rulesFile` | `.dsh/rules.yaml` | Local do arquivo de regras; relativo = resolvido contra o cwd da sessão, absoluto = global e validado na montagem |
|
|
103
|
+
| `fallbackPath` | *(nenhum)* | Arquivo de regras usado quando a descoberta por cwd não encontra nada; validado na montagem |
|
|
104
|
+
| `badFilePolicy` | `fail` | Arquivo ruim: `fail` faz a chamada pendente falhar ruidosamente (recargas mantêm as regras anteriores); `ignore-with-warning` avisa e segue vazio |
|
|
105
|
+
| `maxRules` | `256` | Limite rígido de regras em toda a cadeia de fontes efetiva; arquivos maiores falham a carga |
|
|
106
|
+
| `maxCachedWorkspaces` | `512` | Limite rígido de cargas de regras por workspace em cache; além dele o menos usado recentemente (e seu watcher) é expulso |
|
|
107
|
+
| `patternMode` | `glob` | Sabor dos padrões de `params`/`paths`/`when.env`: `glob` ou `regex` (nomes de ferramenta sempre são globs) |
|
|
108
|
+
| `watch` | `true` | Vigilância Chokidar + recarga ao mudar |
|
|
109
|
+
| `watchStabilityThresholdMs` | `200` | Janela de debounce da recarga (ms) |
|
|
110
|
+
| `language` | `en` | Idioma da saída do `/rules`: `en`, `zh`, `es`, `pt`, `hi` (`en`/`zh` são as traduções de referência) |
|
|
111
|
+
| `caseInsensitivePaths` | *(win32)* | Padrões de `paths` e comparação com a raiz do workspace ignoram maiúsculas ASCII; por padrão `true` no Windows, `false` nos demais |
|
|
112
|
+
| `audit` | `all` | Granularidade da auditoria: `all` registra cada acerto E passagem; `hits` pula eventos de passagem |
|
|
113
|
+
| `searchUp` | `false` | Percorre os diretórios pais a partir do cwd da sessão e combina cada arquivo de regras encontrado, o mais próximo primeiro |
|
|
114
|
+
| `maxGlobStars` | `2` | Limite rígido de quantificadores `*`/`**` ilimitados por padrão glob (cota do grau de backtracking) |
|
|
115
|
+
|
|
116
|
+
### Comandos de sessão
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
/rules lista as regras ativas, seus arquivos de origem e qualquer erro da última recarga
|
|
120
|
+
/rules reload relê a cadeia de arquivos de regras deste workspace
|
|
121
|
+
/rules decisions [n] mostra as últimas n decisões de permissão desta sessão (padrão 10)
|
|
122
|
+
/rules test <tool> <json> avalia em seco as regras contra uma chamada hipotética, ex.: /rules test bash {"command":"git push origin main"}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
A saída dos comandos é somente UI — o modelo aprende as regras apenas pelos resultados de ferramenta que elas produzem. `language` escolhe o idioma da saída. Um JSON Schema do arquivo de regras é distribuído em [docs/rules-format.schema.json](docs/rules-format.schema.json) (conecte-o com `# yaml-language-server: $schema=...` para autocompletar no editor).
|
|
126
|
+
|
|
127
|
+
## Colaboração com o dsh-auto-review
|
|
128
|
+
|
|
129
|
+
- O `dsh-permission-rules` produz `ask`; o `dsh-auto-review` responde no waterfall `approval/request` com um veredito de um segundo modelo somente leitura (ou delega para humanos). Monte os dois para o ciclo completo.
|
|
130
|
+
- Testado em integração (`test/integration.spec.ts`): `permissionRules/decision` → `approval/asked` → `autoReview/verdict` → `approval/decided`, com o revisor substituído por um mock.
|
|
131
|
+
- A política de aprovação `never` e todas as garantias fail-closed do [harness oficial](https://github.com/deepseek-ai/deepseek-harness) permanecem intactas.
|
|
132
|
+
|
|
133
|
+
## Limites de segurança
|
|
134
|
+
|
|
135
|
+
- **Política, não kernel.** Os candidatos de `paths` vêm apenas de um conjunto documentado de chaves de argumentos, e somente caminhos relativos ao workspace correspondem.
|
|
136
|
+
- **Nenhum revisor aqui.** O plugin nunca lança subagentes nem chama modelos — produzir uma decisão `ask` é o fim do seu trabalho.
|
|
137
|
+
- **Sem mudanças de sandbox.** A política de sandbox no nível do SO pertence ao seam de sandbox, não a este plugin.
|
|
138
|
+
- **Configuração errada ruidosa.** Campos YAML desconhecidos, ações desconhecidas e padrões ruins são rejeitados na carga, nunca ignorados em silêncio.
|
|
139
|
+
- **Limites de backtracking.** Padrões glob são limitados a `maxGlobStars` expansões de estrela ilimitadas; padrões em modo regex rejeitam quantificadores aninhados ilimitados e alternâncias literais sobrepostas quantificadas. (Cadeias regex como `\d+\.\d+\.\d+` permanecem permitidas — o modo regex é a válvula de escape, o modo glob é o padrão protegido.)
|
|
140
|
+
|
|
141
|
+
## Trabalho relacionado
|
|
142
|
+
|
|
143
|
+
- [Andy8647/dsh-auto-approval](https://github.com/Andy8647/dsh-auto-approval) — classificador allow/deny de dois estados com auditoria em arquivo próprio; este plugin adiciona a semântica completa de três estados, regras YAML declarativas, auditoria no log de sessão e delegação segura com `next()`.
|
|
144
|
+
- `Drifter-yh/dsh-tool-policy` — política de ferramentas deny-by-default; documentada aqui para evitar implementação duplicada.
|
|
145
|
+
- `dsh-auto-review` — a metade de apoio de IA do ciclo que este plugin lidera.
|
|
146
|
+
|
|
147
|
+
## Limitações conhecidas
|
|
148
|
+
|
|
149
|
+
- `permissionRules/decision` é anexado com o marcador de envelope `ignorable: true`, então qualquer build do harness carrega o log — leitores que não conhecem o tipo fora do repositório simplesmente pulam o registro de auditoria em vez de recusar a sessão. (Hosts rc.6 aceitam e ignoram o marcador, mantendo exatamente o comportamento anterior; sessões gravadas em hosts rc.6 não têm o marcador e podem precisar de `scripts/repair-session-logs.mjs` antes de carregar em hosts com semântica required-on-read.)
|
|
150
|
+
- Os candidatos de `paths` são heurísticos: apenas as chaves de argumentos documentadas alimentam a correspondência de caminhos, e a correspondência relativa ao workspace ignora maiúsculas ASCII somente quando `caseInsensitivePaths` está ativo.
|
|
151
|
+
- Os globs são um subconjunto conservador (sem expansão de chaves) — escreva dois padrões ou use o modo regex.
|
|
152
|
+
- O guard de backtracking de regex é estrutural, não exaustivo: casos de ambiguidade por alternância sem prefixos literais (ex.: lookarounds elaborados) são responsabilidade do autor; prefira o modo glob para arquivos não confiáveis.
|
|
153
|
+
|
|
154
|
+
## Reparo de registros de sessão
|
|
155
|
+
|
|
156
|
+
Registros de sessão gravados antes do marcador `ignorable` podem ser recusados por builds mais novos do harness (`SessionFormatUnsupportedError`). O `scripts/repair-session-logs.mjs` incluído reescreve apenas as linhas de auditoria alvo para adicionar `ignorable: true`, preservando os frames, com backups:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
node scripts/repair-session-logs.mjs scan [--home DIR] # relata linhas externas, sem alterar
|
|
160
|
+
node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`--home` por padrão é `$DSH_HOME/sessions` (ou `~/.dsh/sessions`). O contrato completo está no cabeçalho do script.
|
|
164
|
+
|
|
165
|
+
## Desenvolvimento
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
pnpm install # node ^22.19 || >=24
|
|
169
|
+
pnpm run typecheck # tsc, src + tests
|
|
170
|
+
pnpm run lint # eslint, src + tests + scripts
|
|
171
|
+
pnpm test # vitest: 106 tests, 8 suites
|
|
172
|
+
pnpm run test:coverage # portão de cobertura (90/80/90/90)
|
|
173
|
+
pnpm run build # declarações tsc + bundles tsdown (lib/)
|
|
174
|
+
pnpm run pack:check # build + pack (o artefato publicado)
|
|
175
|
+
node scripts/check-readme-sync.mjs # portão de sincronização dos READMEs em cinco idiomas (também na CI)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Veja [VERIFICATION.md](VERIFICATION.md) para o registro de verificação headless de ponta a ponta (deny bloqueando uma ferramenta de shell, ask roteado pelo seam de aprovação, `--dump-config`).
|
|
179
|
+
|
|
180
|
+
## Licença
|
|
181
|
+
|
|
182
|
+
[Apache License 2.0](LICENSE) © 2026 dsh-permission-rules contributors
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# 🛡️ dsh-permission-rules
|
|
4
|
+
|
|
5
|
+
**DeepSeek Harness 的 Claude Code 式声明式权限规则。**
|
|
6
|
+
|
|
7
|
+
*规则裁决已知,reviewer 模型裁决未知。*
|
|
8
|
+
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
[](https://github.com/topics/dsh-plugin)
|
|
11
|
+
[](#)
|
|
12
|
+
[](https://github.com/PerryLink/dsh-permission-rules/actions)
|
|
13
|
+
[](https://github.com/PerryLink/dsh-permission-rules/releases)
|
|
14
|
+
|
|
15
|
+
[English](README.md) · [简体中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 它能做什么
|
|
22
|
+
|
|
23
|
+
`dsh-permission-rules` 在 `tools/pre-execute` waterfall 上为每一次工具调用放置一个有序的 **`allow` / `deny` / `ask`** 规则列表——确定性、零延迟、可审计,规则用你写的普通 YAML 声明:
|
|
24
|
+
|
|
25
|
+
- **`deny`** 阻止调用。规则的 `reason` 成为模型可见的错误,让 agent 知道*为什么*而不是盲目重试。
|
|
26
|
+
- **`ask`** 走官方审批 seam。同时挂载 `dsh-auto-review` 时由第二模型裁决;否则人类审批;两者都没有时 harness fail-closed。
|
|
27
|
+
- **`allow`**(以及无命中)严格通过 `next()` 委托——绝不短路下游监听器。
|
|
28
|
+
|
|
29
|
+
每次命中**和**每次透传都会以 `permissionRules/decision` 会话事件审计落盘(log-only,不向模型上下文注入任何额外内容)。
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
tools/pre-execute waterfall approval/request waterfall(answerer 链)
|
|
33
|
+
│ │
|
|
34
|
+
dsh-permission-rules dsh-auto-review answerer
|
|
35
|
+
· 按文件顺序首条命中 ┌───────────────┴──────────────┐
|
|
36
|
+
· deny/ask 认领调用 │ 第二模型裁决 │ 否 ── next() ──▶ 人类 UI
|
|
37
|
+
· allow/透传 → next() └───────────────┬──────────────┘
|
|
38
|
+
│ deny ──▶ 被拒工具结果 │ allowed-once / rejected
|
|
39
|
+
│ ask ──▶ ctx.approval ────────────────────────┘
|
|
40
|
+
│
|
|
41
|
+
审计:permissionRules/decision → approval/asked → autoReview/verdict → approval/decided
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 为什么既要有规则又要有 reviewer?
|
|
45
|
+
|
|
46
|
+
第二模型回答*"这次调用行不行"*有判断力,但每次都要一次模型往返,也可能判错。声明式规则确定性、即时、不跑模型——但只能覆盖管理员写下来的情形。两者组合就是 **"规则先行、AI 兜底"** 闭环:规则裁决已知,reviewer 裁决未知。
|
|
47
|
+
|
|
48
|
+
## 特性
|
|
49
|
+
|
|
50
|
+
- ✅ **三态语义** — `allow`、`deny`、`ask`,按文件顺序求值,首条命中生效
|
|
51
|
+
- ✅ **丰富匹配** — 工具名 glob(含 `mcp__*`)、参数键值 glob **或** 正则(支持 `!pattern` 否定与 `absent` 键维度)、工作区相对路径 glob(从文档化参数键在**任意嵌套深度**提取候选),以及 `when` 宿主条件(环境变量、平台)
|
|
52
|
+
- ✅ **分层规则文件** — 可选 `searchUp` 从会话 cwd 向上逐级合并每份 `.dsh/rules.yaml`(最近的优先),子项目可覆盖父级规则
|
|
53
|
+
- ✅ **规则元数据** — `enabled: false`、`description`、`tags`;`/rules` 会告警被前面通配规则遮蔽的规则
|
|
54
|
+
- ✅ **waterfall 安全** — `allow`/透传一律 `next()`;只有 `deny`/`ask` 短路
|
|
55
|
+
- ✅ **官方审批 seam** — `ask` 走 `ctx.approval`;不重复实现、绝不绕过
|
|
56
|
+
- ✅ **完整审计** — `permissionRules/decision` 事件同时记录规则动作与最终裁决;`/rules decisions` 可在会话内回放审计轨迹
|
|
57
|
+
- ✅ **干跑测试** — `/rules test <工具> <json-参数>` 不执行任何东西地评估规则命中
|
|
58
|
+
- ✅ **热更新** — Chokidar 监视 + 去抖;改坏了保留旧规则,绝不崩溃
|
|
59
|
+
- ✅ **响亮失败** — 非法 YAML、未知字段/action、坏 glob/正则、易回溯灾难的模式、超过 `maxRules` 都使加载失败
|
|
60
|
+
- ✅ **热路径有界** — 预编译匹配器,O(规则数 × 模式数),`maxRules` 封顶;glob 回溯度由 `maxGlobStars` 封顶
|
|
61
|
+
|
|
62
|
+
## 快速开始
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# 1. 把 bundle 装进 profile
|
|
66
|
+
dsh plugin --profile web add "github:PerryLink/dsh-permission-rules#main"
|
|
67
|
+
|
|
68
|
+
# 或从打包好的 tarball 安装(预构建产物,无需构建许可)
|
|
69
|
+
pnpm pack
|
|
70
|
+
dsh plugin --profile web add ./dsh-permission-rules-0.1.1.tgz
|
|
71
|
+
|
|
72
|
+
# 2. 重启
|
|
73
|
+
dsh --profile web
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
然后在你的项目里创建规则文件,并在其中启动会话:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
# <project>/.dsh/rules.yaml
|
|
80
|
+
rules:
|
|
81
|
+
- match: { tools: [bash, pwsh], params: { command: "git push*" }, paths: ["**/secrets/**"] }
|
|
82
|
+
action: deny
|
|
83
|
+
reason: "禁止从受保护路径 push"
|
|
84
|
+
|
|
85
|
+
- match: { tools: [edit, write] }
|
|
86
|
+
action: ask
|
|
87
|
+
reason: "写文件需要确认"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
```sh
|
|
91
|
+
dsh --profile web --dump-config | grep -A4 'id: permission-rules' # 验证挂载行
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
完整的 5 条安全基线示例与完整 schema 见 [docs/rules-format.md](docs/rules-format.md)。
|
|
95
|
+
|
|
96
|
+
## 配置
|
|
97
|
+
|
|
98
|
+
所有可调参数都是 Schemastery `Config` 字段(可在 cordis.yml 修改)。按 id 的覆盖会整行替换——覆盖时请重写所有需要的键。
|
|
99
|
+
|
|
100
|
+
| 键 | 默认 | 含义 |
|
|
101
|
+
|---|---|---|
|
|
102
|
+
| `rulesFile` | `.dsh/rules.yaml` | 规则文件位置;相对值 = 按调用会话 cwd 解析,绝对值 = 全局生效并在挂载时校验 |
|
|
103
|
+
| `fallbackPath` | 无 | cwd 发现不到时使用的回退规则文件;挂载时校验 |
|
|
104
|
+
| `badFilePolicy` | `fail` | 坏规则文件处理:`fail` 让待处理工具调用响亮失败(重读保留旧规则);`ignore-with-warning` 告警后以空集继续 |
|
|
105
|
+
| `maxRules` | `256` | 生效规则链的总规则数硬上界,超限加载失败 |
|
|
106
|
+
| `maxCachedWorkspaces` | `512` | 按工作区缓存的规则装载硬上界;超出时逐出最久未用工作区(连同其 watcher) |
|
|
107
|
+
| `patternMode` | `glob` | `params`/`paths`/`when.env` 模式类型:`glob` 或 `regex`(工具名始终是 glob) |
|
|
108
|
+
| `watch` | `true` | Chokidar 监视并在变更时重读 |
|
|
109
|
+
| `watchStabilityThresholdMs` | `200` | 重读去抖窗口(毫秒) |
|
|
110
|
+
| `language` | `en` | `/rules` 输出语言:`en`、`zh`、`es`、`pt`、`hi`(`en`/`zh` 为参考译文) |
|
|
111
|
+
| `caseInsensitivePaths` | *(win32)* | `paths` 模式与工作区根比较忽略 ASCII 大小写;Windows 默认 `true`,其余平台默认 `false` |
|
|
112
|
+
| `audit` | `all` | 审计粒度:`all` 记录每次命中与透传;`hits` 跳过透传事件 |
|
|
113
|
+
| `searchUp` | `false` | 从会话 cwd 向上逐级查找并合并规则文件,最近的优先 |
|
|
114
|
+
| `maxGlobStars` | `2` | 每个 glob 模式的无界 `*`/`**` 量词数硬上界(回溯度封顶) |
|
|
115
|
+
|
|
116
|
+
### 会话命令
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
/rules 列出当前生效规则、来源文件与最近一次重载错误
|
|
120
|
+
/rules reload 重读本工作区的规则文件链
|
|
121
|
+
/rules decisions [n] 显示本会话最近 n 条权限裁决(默认 10)
|
|
122
|
+
/rules test <工具> <json> 对假设调用干跑评估,如 /rules test bash {"command":"git push origin main"}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
命令输出只进 UI——模型只通过规则产生的工具结果感知规则。`language` 选择输出语言。规则文件的 JSON Schema 随包发布在 [docs/rules-format.schema.json](docs/rules-format.schema.json)(用 `# yaml-language-server: $schema=...` 启用编辑器补全)。
|
|
126
|
+
|
|
127
|
+
## 与 dsh-auto-review 协作
|
|
128
|
+
|
|
129
|
+
- `dsh-permission-rules` 产出 `ask`;`dsh-auto-review` 在 `approval/request` waterfall 上用只读第二模型裁决(或转交人类)。两者同挂载即完整闭环。
|
|
130
|
+
- 已做集成测试(`test/integration.spec.ts`):`permissionRules/decision` → `approval/asked` → `autoReview/verdict` → `approval/decided`,reviewer 以脚本化 mock 替代。
|
|
131
|
+
- [官方 harness](https://github.com/deepseek-ai/deepseek-harness) 的 `never` 审批策略与全部 fail-closed 保证不受影响。
|
|
132
|
+
|
|
133
|
+
## 安全边界
|
|
134
|
+
|
|
135
|
+
- **是策略,不是内核。**`paths` 候选只来自文档化的参数键集合(任意嵌套深度、深度封顶),且只匹配工作区相对路径。
|
|
136
|
+
- **不自己起 reviewer。**本插件不 spawn 子代理、不调模型——产出 `ask` 决定就是它工作的终点。
|
|
137
|
+
- **不改沙箱。**OS 级沙箱策略属于 sandbox seam,不属于本插件。
|
|
138
|
+
- **响亮拒绝错误配置。**未知 YAML 字段、未知 action、坏模式在加载期被拒绝,绝不静默忽略。
|
|
139
|
+
- **回溯有界。**glob 模式的无界星号量词数由 `maxGlobStars` 封顶;regex 模式拒绝嵌套无界量词与量化重叠字面量交替。(`\d+\.\d+\.\d+` 这类独立量词链仍然放行——regex 模式是逃生舱,glob 模式是受守护的默认。)
|
|
140
|
+
|
|
141
|
+
## 相关实现
|
|
142
|
+
|
|
143
|
+
- [Andy8647/dsh-auto-approval](https://github.com/Andy8647/dsh-auto-approval) — 二态 allow/deny 分类器,审计走自有文件日志;本插件补齐三态语义、声明式 YAML 规则、会话日志审计与 `next()` 安全委托。
|
|
144
|
+
- `Drifter-yh/dsh-tool-policy` — deny-by-default 工具策略;在此声明差异化,避免重复实现。
|
|
145
|
+
- `dsh-auto-review` — 本插件所前置的"AI 兜底"半环。
|
|
146
|
+
|
|
147
|
+
## 已知局限
|
|
148
|
+
|
|
149
|
+
- `permissionRules/decision` 以信封 `ignorable: true` 标记写入,任何 harness 构建都能加载日志——不认识该仓库外类型的第一方读取器会跳过这条审计记录而不是拒绝整个会话。(rc.6 宿主会接受并忽略该标记,行为与打标前完全一致;rc.6 宿主写出的日志没有该标记,在 required-on-read 语义的宿主上加载前可能需要 `scripts/repair-session-logs.mjs`。)
|
|
150
|
+
- `paths` 候选是启发式的:只有文档化的参数键进入路径匹配;工作区相对匹配仅在 `caseInsensitivePaths` 开启时忽略 ASCII 大小写。
|
|
151
|
+
- glob 是保守子集(不支持花括号展开)——写两条模式,或用 regex 模式。
|
|
152
|
+
- regex 回溯守卫是结构性的而非穷尽的:不含字面量前缀的交替歧义(如精心构造的环视)由作者自负其责;不可信文件请优先用 glob 模式。
|
|
153
|
+
|
|
154
|
+
## 会话日志修复
|
|
155
|
+
|
|
156
|
+
`ignorable` 标记出现之前的会话日志可能被较新的 harness 构建拒绝(`SessionFormatUnsupportedError`)。随包发布的 `scripts/repair-session-logs.mjs` 只改写目标审计行、补上 `ignorable: true`,帧边界保留并自动备份:
|
|
157
|
+
|
|
158
|
+
```sh
|
|
159
|
+
node scripts/repair-session-logs.mjs scan [--home DIR] # 只报告外来行,不改动
|
|
160
|
+
node scripts/repair-session-logs.mjs repair [--home DIR] [--dry-run]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
`--home` 默认为 `$DSH_HOME/sessions`(或 `~/.dsh/sessions`)。完整约定见脚本头注释。
|
|
164
|
+
|
|
165
|
+
## 开发
|
|
166
|
+
|
|
167
|
+
```sh
|
|
168
|
+
pnpm install # node ^22.19 || >=24
|
|
169
|
+
pnpm run typecheck # tsc,src + tests
|
|
170
|
+
pnpm run lint # eslint,src + tests + scripts
|
|
171
|
+
pnpm test # vitest:106 测试 / 8 套件
|
|
172
|
+
pnpm run test:coverage # 覆盖率门禁(90/80/90/90)
|
|
173
|
+
pnpm run build # tsc 声明 + tsdown 打包(lib/)
|
|
174
|
+
pnpm run pack:check # 构建 + pack(发布产物)
|
|
175
|
+
node scripts/check-readme-sync.mjs # 五语 README 同步门禁(CI 同步执行)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
headless 端到端验证记录(deny 阻止 shell 工具、ask 走审批 seam、`--dump-config`)见 [VERIFICATION.md](VERIFICATION.md)。
|
|
179
|
+
|
|
180
|
+
## License
|
|
181
|
+
|
|
182
|
+
[Apache License 2.0](LICENSE) © 2026 dsh-permission-rules contributors
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# dsh-permission-rules bundle patch: mount the declarative permission-rule
|
|
2
|
+
# listener. `name` is the package name, resolved through the profile's
|
|
3
|
+
# node_modules. An id-targeted override replaces the whole config row —
|
|
4
|
+
# restate every key when overriding. Full schema and config vocabulary:
|
|
5
|
+
# README.md and docs/rules-format.schema.json.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: permission-rules
|
|
8
|
+
name: dsh-permission-rules
|
|
9
|
+
config:
|
|
10
|
+
# Rule file discovered per session cwd (<cwd>/.dsh/rules.yaml);
|
|
11
|
+
# see docs/rules-format.md for the full rule vocabulary.
|
|
12
|
+
rulesFile: .dsh/rules.yaml
|
|
13
|
+
badFilePolicy: fail
|
|
14
|
+
maxRules: 256
|
|
15
|
+
patternMode: glob
|
|
16
|
+
watch: true
|