deepseek-coordination 0.1.9 → 0.10.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.
@@ -1,206 +0,0 @@
1
- # dsh-os 授权模型(permission model)
2
-
3
- > STATUS: layer B, the write delegate, and layer A (Windows ACL) are all implemented. See section 7.
4
- > 关联:`command.txt`(权威动词契约)、`spec/verbs.tsv`(动词表)、`docs/os-spec.md`(总规范)、根目录 `permission.json`(授权数据,当前 0 字节)。
5
- >
6
- > **已落地**:读侧规则匹配(`permission query/list/audit`,动词层只读)、写入委托插件
7
- > `src/permission/index.js`(`dsh-os-permission`),以及 §6 中除 ACL 强度上报外的全部用例,
8
- > 见 `tests/permission.test.mjs`(18 条)。
9
-
10
- ---
11
-
12
- ## 1. 四种决定的语义
13
-
14
- 用户明确要求的四种弹窗决定,映射为 `permission.json` 中的记录:
15
-
16
- | 用户选择 | `decision` 值 | 生效范围 | 写入 `permission.json` |
17
- |---|---|---|---|
18
- | 同意 | `allow-once` | 仅本次调用 | **否**(一次性,不落盘) |
19
- | 拒绝 | `deny-once` | 仅本次调用 | **否** |
20
- | 永久同意 | `allow-always` | 该 `(verb, target-scope)` 组合,直到撤销 | 是 |
21
- | 永久拒绝 | `deny-always` | 同上 | 是 |
22
-
23
- **映射到 DSH 审批 seam 的硬约束**(已实测):`ApprovalService.request()` 的四种结局是 `allowed-once | rejected | cancelled | unavailable`,且源码注释写明 *`'allowed-once'` is the only grant*。因此:
24
-
25
- * `allow-once` / `deny-once` **可以**走 DSH 审批通道(用户在自己的 GUI 里点一次)。
26
- * `allow-always` / `deny-always` **不能**由审批通道表达——该 seam 刻意不提供持久授权。持久决定必须由**同意写入委托(consent delegate)**写入 `permission.json`。
27
- * 当 session 策略为 `never` 时(本会话即如此),`request()` 直接返回 `rejected`,**不会向用户询问**。此时任何需要授权的操作都以 `E_PERMISSION` 失败,且提示用户切换审批策略——**绝不静默降级为允许**。
28
-
29
- ---
30
-
31
- ## 2. `permission.json` 模式
32
-
33
- ```jsonc
34
- {
35
- "version": 1,
36
- "updatedAt": "2026-10-12T17:10:10.000Z",
37
- "updatedBy": "consent-delegate", // 只有委托进程可以写这个值
38
- "defaults": {
39
- "readOnly": "allow", // R 类动词永不询问,也不落盘
40
- "mutating": "ask" // W 类的默认动作
41
- },
42
- "rules": [
43
- {
44
- "id": "r-0001",
45
- "verb": "file.delete", // 支持末尾通配:file.* / window.*
46
- "target": { "kind": "path", "value": "R:\\project\\**" },
47
- "scope": { "kind": "user" }, // user | machine | process | session | any
48
- "decision": "allow-always", // allow-always | deny-always
49
- "grantedBy": "consent-delegate",
50
- "grantedAt": "2026-10-12T17:10:10.000Z",
51
- "expiresAt": null,
52
- "note": "optional human note"
53
- }
54
- ],
55
- "audit": [
56
- {
57
- "at": "2026-10-12T17:10:10.000Z",
58
- "verb": "file.delete",
59
- "target": { "kind": "path", "value": "R:\\project\\x.txt" },
60
- "decision": "allow-once",
61
- "decidedBy": "dsh-approval", // dsh-approval | consent-delegate
62
- "actor": { "sessionId": "...", "agentId": "..." }
63
- }
64
- ]
65
- }
66
- ```
67
-
68
- **规则设计的三个要点**
69
-
70
- 1. **先匹配先赢,无规则即 `defaults`。** 规则按数组顺序求值,第一条命中即生效——不引入优先级字段,避免"为什么这条没生效"的隐式排序问题。
71
- 2. **`deny-*` 优先于 `allow-*` 在语义上不可跨越**:若同一 `(verb, target)` 同时存在 allow 与 deny 规则,`deny` 命中即拒绝(安全默认),并在返回中说明命中了哪条规则(`ruleId`)。
72
- 3. **审计是追加式、有界**:`audit` 只保留最近 N 条(默认 200),超出时由委托进程裁剪;**审计条目不可由动词修改**。
73
- 4. **`target-scope` 必须显式**:路径类目标用绝对路径前缀或通配;进程类用 pid 或可执行名;环境变量用变量名。**不允许"所有目标"的永久同意**——`target.value` 为 `"*"` 时,委托必须要求用户二次确认,并在返回里标注 `broad: true`。
74
-
75
- ---
76
-
77
- ## 3. 强制模型(已确认:A + B 组合)
78
-
79
- ### 3.1 B —— 结构强制(跨平台,始终生效)
80
-
81
- * **`permission.json` 的写入不是任何 dsh-os 动词。** `spec/verbs.tsv` 里不存在 `permission write/set/delete` 这类动词;动词层只能 `permission query`(R 类,只读)。
82
- * **保护路径硬拒绝。** 所有 `file.*` / `execute.*` / `environment.*` 命令在路径解析后检查保护清单,命中即以 **`E_PERMISSION_PROTECTED_PATH`(退出码 3)** 拒绝,且**不产生任何副作用**。保护清单至少包含:
83
- * `permission.json` 本身及其 `.bak` / 临时文件
84
- * `bin/dsh-os.ps1`、`bin/dsh-os.sh`、`bin/dsh-os.bat`(防止脚本自我改写绕过检查)
85
- * `spec/*.tsv`、`command.txt`(防止篡改动词表)
86
- * 该检查是**路径规范化之后**的字符串比较(大小写按平台折叠、解析 `..` 与符号链接),不是正则匹配——正则会被 `..\` 或短路径 8.3 形式绕过。
87
- * **`execute` 的例外必须显式**:`execute` 能启动任意程序,因此 `execute` 本身**不是**绕过路径检查的通道;动词层无法静态判定子进程会写什么,所以这属于**已知残余风险**(见 §5),只能靠 ACL(A 层)兜底。
88
-
89
- ### 3.2 A —— ACL 内核强制(可行平台上,推荐开启)
90
-
91
- | 平台 | 做法 | 说明 |
92
- |---|---|---|
93
- | Windows | 用 `SetNamedSecurityInfoW` 在 `permission.json` 上**显式拒绝**自动化上下文的 WRITE(含 `DELETE` / `WRITE_DAC`),只授予交互用户与委托进程 | 由内核强制,无法由用户态绕开。**前提:两侧身份必须不同**(例如 harness 以受限账户/低完整性运行,委托以交互用户运行),否则 A 退化为 B |
94
- | Linux | 文件属主为交互用户,目录对自动化上下文不可写;或 `chattr +a`(只可追加) | 等价物较弱;容器内可能不可用 |
95
- | macOS | ACL + `chflags uchg` | 同上 |
96
-
97
- **初始化**:`permission acl init` 是**幂等的显式动作**(在动词层属于 W 类),它只设置保护属性、不写授权内容;失败不阻断 B 层。`selfcheck` 必须报告 A 层是否生效(`aclEnforced: true|false`),让调用方知道当前处于哪一档强度。
98
-
99
- ### 3.3 写入委托:`permission-consent`(唯一写入方)
100
-
101
- ```
102
- 用户选择 ──► dsh-os 动词 (R/W)
103
- │ 需要授权?
104
- ▼
105
- ┌───────────────────────┐
106
- │ 1. 查 permission.json │ 命中 allow-always → 放行
107
- │ │ 命中 deny-always → 拒绝(E_PERMISSION_DENIED_BY_RULE)
108
- └──────────┬────────────┘
109
- │ 无规则 → defaults
110
- ▼
111
- ┌───────────────────────┐
112
- │ 2. 走 DSH 审批通道 │ approval/request waterfall
113
- │ allow-once/deny-once│ → 不落盘
114
- └──────────┬────────────┘
115
- │ 用户选择"永久"
116
- ▼
117
- ┌───────────────────────┐
118
- │ 3. permission-consent │ 唯一持有 permission.json 写权的进程/插件
119
- │ 追加规则 + 审计 │ 以交互用户身份运行(配合 A 层 ACL)
120
- └───────────────────────┘
121
- ```
122
-
123
- * **委托是插件,不是脚本子命令。** 理由:DSH 的审批 UI 在客户端(`dsh-client-ui-approval`),只有插件能注册 `approval/request` answerer 并触达用户 GUI。脚本侧(`.ps1`/`.sh`)**只读取** `permission.json`,永不写入。
124
- * **插件形态**(照 DSH 惯例):Service Definition `dsh-os-permission`(只读契约 + 查询/匹配逻辑)+ Consumer `dsh-tool-os-permission`(暴露 `permission query` / `permission grant` / `permission revoke` 工具)+ answerer 注册在 `approval/request`。
125
- * **写入必须原子**:同目录临时文件 → `MoveFileExW(REPLACE_EXISTING)`(与 `file.*` 同机制),确保崩溃不留半写。
126
- * **每次写入都追加审计条目**,`updatedBy` 固定为 `consent-delegate`;读侧若发现 `updatedBy` 不是该值,视为**被篡改**,按 fail-closed 处理(全部按 `defaults` 求值,并报告 `E_PERMISSION_FILE_UNTRUSTED`)。
127
-
128
- ---
129
-
130
- ## 4. 新增动词(本文件覆盖的授权相关部分)
131
-
132
- | 动词 | 权限 | 说明 |
133
- |---|---|---|
134
- | `permission query [verb] [target]` | R | 返回某动作的当前判定、命中规则、生效强度(`aclEnforced`) |
135
- | `permission list` | R | 列出全部规则 |
136
- | `permission audit [--limit n]` | R | 读取审计尾部 |
137
- | `permission grant verb target --decision allow-always\|deny-always` | W | **必须**经委托 + 用户弹窗;脚本直接调用返回 `E_PERMISSION_DELEGATE_ONLY` |
138
- | `permission revoke rule-id` | W | 同上,撤销也必须经用户确认 |
139
- | `permission acl init` | W | 幂等设置 A 层保护;不涉及授权内容 |
140
-
141
- **新增错误码**
142
-
143
- | code | 退出码 | 触发条件 |
144
- |---|---|---|
145
- | `E_PERMISSION_DENIED_BY_RULE` | 3 | 命中 `deny-always` 规则 |
146
- | `E_PERMISSION_PROTECTED_PATH` | 3 | 试图经 `file.*`/`environment.*` 改动受保护路径 |
147
- | `E_PERMISSION_DELEGATE_ONLY` | 3 | 非委托方试图写入/撤销授权 |
148
- | `E_PERMISSION_FILE_UNTRUSTED` | 3 | `permission.json` 的 `updatedBy` 或完整性戳校验失败 |
149
- | `E_PERMISSION_NO_ANSWERER` | 3 | 需要用户交互但无可用 answerer(或 session 策略为 `never`) |
150
-
151
- ---
152
-
153
- ## 5. 已知残余风险(必须记录,不可声称已解决)
154
-
155
- 1. **`execute` 是路径保护之外的通道。** 动词层无法静态判定子进程行为,因此"经 `execute` 启动的程序去改 `permission.json`"只能靠 A 层 ACL 阻止。若 A 层未启用(`aclEnforced: false`),**B 层无法阻止这种改写**——这是组合方案的真实边界。
156
- 2. **A 层要求两侧身份不同。** 若 harness 与委托以同一用户身份运行且都未被 ACL 拒绝,A 层退化,整体强度等于 B。`selfcheck` 必须如实报告,不能假称已强制。
157
- 3. **DSH 审批通道不提供持久授权。** 因此"永久同意"的可信度取决于委托进程的完整性,而非审批服务本身。
158
- 4. **`--allow` 是调用方的自我声明**,与 `permission.json` 是两套东西:`--allow` 表示"harness 已从用户处取得本次授权",`permission.json` 表示"用户曾作过的持久决定"。两者**都必须满足**:`allow-always` 规则命中时仍需调用方传 `--allow`(防御纵深),而 `--allow` 单独存在不能抵消 `deny-always`。
159
-
160
- ---
161
-
162
- ## 6. 测试要求(随实现落地)
163
-
164
- | 用例 | 断言 | 状态 |
165
- |---|---|---|
166
- | 受保护路径 | `file delete permission.json --allow` → `E_PERMISSION_PROTECTED_PATH`,且文件**字节不变** | 已实现(`tests/golden.ps1`) |
167
- | 脚本自我改写 | `file create bin/dsh-os.ps1 ... --allow` → 同上 | 已实现(`tests/golden.ps1`) |
168
- | 规则命中 | 存在 `deny-always` 规则时,即使传 `--allow` 也拒绝,返回 `ruleId` | 读侧匹配已实现;**动词层尚未接入**(动词层目前不读规则做判定) |
169
- | 审计追加 | 每次授权决定后审计条数 +1,且旧条目不被改写 | 已实现(`tests/permission.test.mjs`) |
170
- | 委托专属 | 非委托上下文调用 `permission grant` → `E_PERMISSION_DELEGATE_ONLY`,**文件未被修改** | 已实现(`tests/golden.ps1`):脚本侧**根本没有实现**,故结构上无法写入 |
171
- | 篡改检出 | 手工把 `updatedBy` 改成别的值 → 读取时 `E_PERMISSION_FILE_UNTRUSTED`,判定退回 `defaults` | 已实现(`tests/permission.test.mjs`) |
172
- | fail-closed | 本会话审批策略为 `never` 时,需授权的操作返回 `E_PERMISSION_NO_ANSWERER`,**不产生副作用** | 部分:动词层以 `E_PERMISSION` 拒绝;`E_PERMISSION_NO_ANSWERER` 待接入 |
173
- | A 层强度上报 | `selfcheck` 的 `aclEnforced` 与真实 ACL 状态一致(不能恒为 true) | 已实现:**从 `permission.json` 的安全描述符实测**,并附 `aclReason` 说明原因 |
174
-
175
- ## 6.1 A 层实现现状(Windows)
176
-
177
- `permission acl init` 已实现:对 `permission.json` 的 DACL 追加一条**针对 Everyone(`S-1-1-0`)的显式 DENY**,权限位含 `FILE_GENERIC_WRITE | DELETE | WRITE_DAC | WRITE_OWNER`,DACL 原有内容保留。
178
-
179
- * **幂等**:已在保护状态时返回 `changed: false`,不重复追加 ACE。实测二次调用后 DACL 逐字节不变。
180
- * **需要提权**:`SetNamedSecurityInfo` 是特权操作,未提权时返回 **`E_ELEVATION_REQUIRED`** 且不做任何修改。
181
- * **实测生效**:施加后对 `permission.json` 的写入被内核拒绝。
182
- * **强度是实测的**:`Test-AclEnforced` 直接读取并遍历文件的 DACL 查找显式 DENY。**早期版本读取 `.temporary/acl-state.json` 里的一个标志位**——那意味着上报强度取决于一个可被编辑的文件,会在没有强制时声称已强制。该实现已删除。
183
-
184
-
185
-
186
- ---
187
-
188
- ## 7. 已确认的设计决策(本轮由用户裁决)
189
-
190
- | # | 决策点 | 结论 |
191
- |---|---|---|
192
- | 1 | 强制手段 | **A + B 组合**:ACL 内核强制 + 动词表硬拒绝 |
193
- | 2 | 弹窗实现 | **复用 DSH 自身审批通道**(`dsh-user-approval` + `dsh-client-ui-approval`);持久决定由 `permission-consent` 委托写入 |
194
- | 3 | 语义 | `file verify charset` 断言声明值;`file verify encoding` 校验字节序列合法性;`file verify completion` 校验完整性(预期由调用方给出);`task show pid` 置前该进程窗口;`task launch` 为**启动**(原文 lunch 为笔误);`service` 指**系统服务**(SCM/systemd/launchd) |
195
- | 4 | environment 作用域 | 默认**用户级**,支持 `--scope process\|user\|machine`;`machine` 级需提权,报 `E_ELEVATION_REQUIRED` 而非静默失败 |
196
-
197
- ---
198
-
199
- ## 8. 待落地顺序
200
-
201
- 1. `spec/verbs.tsv` 扩展:`file.verify.*`、`system.*`、`task.*`、`service.*`、`environment.*`、`window.minimize/maximize`、`permission.*`
202
- 2. `bin/dsh-os.ps1`:保护路径检查(B 层)→ 新增查询类动词(`system.*`、`task.list/query`、`environment.list`、`file.verify.*`)——这些是 **R 类,可立即实现且无授权风险**
203
- 3. `permission.json` 读写逻辑 + 规则匹配 + 审计(读侧先做,写侧留到委托插件)
204
- 4. 委托插件(Service Definition + Consumer + `approval/request` answerer)
205
- 5. A 层 ACL 初始化与 `selfcheck` 强度上报
206
- 6. 上述 §6 测试用例接入 `tests/golden.ps1`
@@ -1,28 +0,0 @@
1
- # dsh-os charset table - single source of truth for encoding names.
2
- # Tab-separated. Columns:
3
- # iana canonical IANA name accepted on the command line (lower case; aliases resolved to these)
4
- # aliases '|'-separated accepted aliases
5
- # dotnet [System.Text.Encoding] name or code page, as resolved by GetEncoding() on Windows PowerShell 5.1
6
- # iconv iconv(1) name for Linux/Unix/macOS
7
- # bom BOM bytes in hex, or '-' when the encoding has no BOM
8
- # fixed yes when the encoding has exactly one byte order and no auto-detection is possible
9
- #
10
- # Verified on Windows PowerShell 5.1.19041.3031 / .NET Framework: every dotnet value below resolves.
11
- # NOTE: System.Text.Encoding.CodePages does NOT exist on .NET Framework; do not use CodePagesEncodingProvider.
12
- # Code pages 936/54936/950/932/1200/1201/28591/1252/65001 are all built in.
13
- iana aliases dotnet iconv bom fixed
14
- utf-8 utf8|utf-8-nobom|cp65001 65001 UTF-8 - no
15
- utf-8-bom utf8-bom|utf-8-sig 65001 UTF-8 EFBBBF no
16
- utf-16le utf16|utf-16|ucs-2|unicode 1200 UTF-16LE FFFE yes
17
- utf-16be utf-16-be|unicodefffe 1201 UTF-16BE FEFF yes
18
- utf-32le utf-32|utf32 12000 UTF-32LE FFFE0000 yes
19
- utf-32be utf-32-be 12001 UTF-32BE 0000FEFF yes
20
- gbk cp936|ms936|chinese 936 GBK - yes
21
- gb18030 cp54936 54936 GB18030 - yes
22
- big5 cp950|big-5 950 BIG5 - yes
23
- shift_jis sjis|cp932|shift-jis 932 SHIFT_JIS - yes
24
- euc-jp cp51932 51932 EUC-JP - yes
25
- euc-kr cp949|ks_c_5601-1987 949 EUC-KR - yes
26
- latin1 iso-8859-1|iso8859-1|cp28591 28591 ISO-8859-1 - yes
27
- windows-1252 cp1252 1252 CP1252 - yes
28
- ascii us-ascii|cp20127 20127 ASCII - yes
@@ -1,120 +0,0 @@
1
- # dsh-os verb table - single source of truth for the atomic command surface.
2
- # Tab-separated. Columns:
3
- # verb dotted verb, exactly as dispatched (group + '.' + subverb, or a bare top-level verb)
4
- # positionals '|'-separated positional parameter names in order; '-' when the verb takes none
5
- # flags '|'-separated accepted flags (long form, no leading dashes); '-' when none
6
- # perm R = read-only/query, never prompts; W = mutating, requires --allow
7
- # codes '|'-separated error codes this verb can return, beyond the generic set
8
- # Generic codes available to every verb: E_USAGE E_TYPE E_PERMISSION E_IO E_ABORTED E_INTERNAL
9
- # Maintained alongside command.txt; tests/conformance.ps1 asserts the two agree.
10
- verb positionals flags perm codes
11
- file.create path|content|charset encoding|eol|allow W E_EXISTS E_NO_PARENT E_CHARSET_UNKNOWN E_ENCODING_INVALID E_PARTIAL_WRITE
12
- file.charset path - R E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT
13
- file.encode path|charset backup|bom|allow W E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_CHARSET_UNKNOWN E_CHARSET_MISMATCH E_PARTIAL_WRITE
14
- file.length path no-recursive R E_NOT_FOUND
15
- file.information path - R E_NOT_FOUND E_BINARY_CONTENT
16
- file.list path - R E_NOT_FOUND E_NOT_A_DIR
17
- file.delete path permanent|allow W E_NOT_FOUND E_IS_A_DIR E_PERMISSION E_IO
18
- file.move source|destination allow W E_NOT_FOUND E_EXISTS E_NO_PARENT E_PARTIAL_WRITE
19
- file.copy source|destination allow W E_NOT_FOUND E_EXISTS E_NO_PARENT E_PARTIAL_WRITE
20
- file.mkdir path parents|allow W E_EXISTS E_NO_PARENT
21
- file.append path|content charset|encoding|allow W E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_CHARSET_MISMATCH E_PARTIAL_WRITE
22
- file.insert path|content|offset charset|encoding|allow W E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_BOUNDARY E_RANGE E_PARTIAL_WRITE
23
- file.view path|charset|start|end max-bytes|no-numbers R E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_CHARSET_MISMATCH E_RANGE E_BOUNDARY E_TRUNCATED
24
- # file.search returns a JSON ARRAY of matches. It never walks outside the directory it is given, and
25
- # match is a literal substring by default (--regex switches to a regular expression).
26
- file.search directory|name regex|recursive|max-depth|limit|max-bytes|type R E_NOT_FOUND E_NOT_A_DIR E_RANGE E_TRUNCATED
27
- execute.command arguments... workdir|env|stdin|timeout|encoding|shell|background|allow W E_NOT_FOUND E_TIMEOUT E_NOT_SUPPORTED
28
- # execute.synchronous runs the command array one after another and stops at the first failure.
29
- # execute.asynchronous runs them concurrently and waits for all of them.
30
- # Both return one aggregate envelope per command, in input order, so a caller can map results back
31
- # to the commands it sent without tracking ids.
32
- execute.synchronous commands workdir|timeout|encoding|shell|allow W E_NOT_FOUND E_TIMEOUT E_NOT_SUPPORTED
33
- execute.asynchronous commands workdir|timeout|encoding|shell|allow W E_NOT_FOUND E_TIMEOUT E_NOT_SUPPORTED
34
- execute.query pid - R E_NOT_FOUND E_NOT_SUPPORTED
35
- execute.kill pid force|allow W E_NOT_FOUND E_PERMISSION E_NOT_SUPPORTED
36
- mouse.click x|y button|count|clearmodifiers|space|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_PERMISSION
37
- mouse.context x|y clearmodifiers|space|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_PERMISSION
38
- mouse.over x|y hold|space|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
39
- mouse.wheel x|y space|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
40
- mouse.down x|y button|space|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
41
- mouse.up - button|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
42
- mouse.cursor - - R E_NO_DISPLAY E_NOT_SUPPORTED
43
- screenshot x|y|width|height out|screen|format R E_NO_DISPLAY E_NOT_SUPPORTED E_RANGE E_IO
44
- keyboard.down code clearmodifiers|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_TYPE E_PERMISSION
45
- keyboard.up code clearmodifiers|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_TYPE E_PERMISSION
46
- keyboard.press code clearmodifiers|interval|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_TYPE E_PERMISSION
47
- keyboard.combination phase|keys clearmodifiers|interval|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_TYPE E_PERMISSION
48
- keyboard.type text method|interval|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_NOT_TEXT E_IO E_PERMISSION
49
- keyboard.codes - - R -
50
- clipboard.read - - R E_NO_DISPLAY E_NOT_SUPPORTED E_NOT_TEXT
51
- clipboard.write text allow W E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
52
- clipboard.paste - interval|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_PERMISSION
53
- window.find name regex|pid|exact R E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND
54
- window.list - z-order R E_NO_DISPLAY E_NOT_SUPPORTED
55
- window.active - - R E_NO_DISPLAY E_NOT_SUPPORTED
56
- window.close name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
57
- window.top name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
58
- window.bottom name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
59
- window.move name|x|y exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
60
- window.resize name|width|height exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
61
- wait.window name timeout|interval|absent|exact R E_NO_DISPLAY E_NOT_SUPPORTED E_TIMEOUT E_WINDOW_NOT_FOUND
62
- wait.ms ms - R E_TYPE E_RANGE
63
- text x|y source|max-chars R E_NO_DISPLAY E_NOT_SUPPORTED E_ACCESSIBILITY_DENIED E_ACCESSIBILITY_UNAVAILABLE E_ELEVATION_REQUIRED
64
- structure x|y depth|source R E_NO_DISPLAY E_NOT_SUPPORTED E_ACCESSIBILITY_DENIED E_ACCESSIBILITY_UNAVAILABLE E_ELEVATION_REQUIRED
65
- selfcheck - json R -
66
- # --- round 2 additions (see docs/os-spec.md and docs/permission-model.md) ---
67
- # file.verify.* are assertions: they compare against a caller-supplied expectation and report the
68
- # observed value alongside the match result. They never modify the target.
69
- file.verify.charset path|charset - R E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_CHARSET_UNKNOWN
70
- file.verify.encoding path|charset - R E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_CHARSET_UNKNOWN
71
- file.verify.completion path length|sha256|charset|bom|eol R E_NOT_FOUND E_NOT_A_FILE E_BINARY_CONTENT E_TRUNCATED
72
- # system.* are pure queries, never privileged, and must degrade field-by-field rather than fail whole.
73
- system.brand - json R E_NOT_SUPPORTED
74
- system.cpu - json R E_NOT_SUPPORTED
75
- system.gpu - json R E_NOT_SUPPORTED
76
- system.version - json R E_NOT_SUPPORTED
77
- system.info - json R E_NOT_SUPPORTED
78
- # task.* manage processes. task.launch is the "open this program/document" verb (command.txt wrote
79
- # "lunch", which is a typo); it never launches through a shell string.
80
- task.list - filter|limit|json R E_NOT_SUPPORTED
81
- task.query pid - R E_NOT_FOUND E_NOT_SUPPORTED
82
- task.show pid - R E_NOT_FOUND E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND
83
- task.kill pid force|allow W E_NOT_FOUND E_PERMISSION E_NOT_SUPPORTED
84
- task.launch path args|workdir|allow W E_NOT_FOUND E_NO_DISPLAY E_NOT_SUPPORTED E_PERMISSION
85
- window.minimize name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
86
- window.maximize name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
87
- window.restore name exact|allow W E_NO_DISPLAY E_NOT_SUPPORTED E_WINDOW_NOT_FOUND E_WINDOW_AMBIGUOUS E_PERMISSION
88
- # service.* address OS service managers: Windows SCM, systemd, launchd. Start/stop/restart need
89
- # elevation and must report E_ELEVATION_REQUIRED instead of failing opaquely.
90
- service.list - filter|json R E_NOT_SUPPORTED
91
- service.query name - R E_NOT_FOUND E_NOT_SUPPORTED
92
- service.start name allow W E_NOT_FOUND E_ELEVATION_REQUIRED E_NOT_SUPPORTED E_PERMISSION
93
- service.stop name allow W E_NOT_FOUND E_ELEVATION_REQUIRED E_NOT_SUPPORTED E_PERMISSION
94
- service.restart name allow W E_NOT_FOUND E_ELEVATION_REQUIRED E_NOT_SUPPORTED E_PERMISSION
95
- # environment.* default to the user scope. A change to user or machine scope does not affect
96
- # already-running processes; every mutating call reports the scope it actually wrote.
97
- #
98
- # An environment variable has attributes: PATH is one, as are PATHEXT, LIB and INCLUDE. A plain
99
- # variable holds one string; an attribute holds an ordered list joined by the platform separator
100
- # (';' on Windows, ':' on POSIX). Both levels expose the same five operations, so
101
- # `environment get PATH` returns the raw string while `environment attribute get PATH` returns the split
102
- # entries. --attr selects the attribute name and defaults to PATH.
103
- environment.list - scope|filter|json R E_NOT_SUPPORTED
104
- environment.get name scope R E_NOT_FOUND E_NOT_SUPPORTED
105
- environment.add name|value scope|allow W E_EXISTS E_ELEVATION_REQUIRED E_PERMISSION
106
- environment.set name|value scope|allow W E_NOT_FOUND E_ELEVATION_REQUIRED E_PERMISSION
107
- environment.delete name scope|allow W E_NOT_FOUND E_ELEVATION_REQUIRED E_PERMISSION
108
- environment.attribute.list - scope|attr R E_NOT_FOUND E_NOT_SUPPORTED
109
- environment.attribute.get name scope|attr R E_NOT_FOUND E_NOT_SUPPORTED
110
- environment.attribute.add name|value scope|attr|allow W E_EXISTS E_ELEVATION_REQUIRED E_PERMISSION
111
- environment.attribute.set name|index|value scope|attr|allow W E_NOT_FOUND E_RANGE E_ELEVATION_REQUIRED E_PERMISSION
112
- environment.attribute.delete name scope|attr|index|value|allow W E_NOT_FOUND E_RANGE E_ELEVATION_REQUIRED E_PERMISSION
113
- # permission.* read the authorization file; the two mutating verbs are delegate-only and always
114
- # require a user-accepted dialog (see docs/permission-model.md).
115
- permission.query verb|target scope|json R E_NOT_FOUND
116
- permission.list - json R -
117
- permission.audit - limit|json R -
118
- permission.grant verb|target|decision delegate|allow W E_PERMISSION_DELEGATE_ONLY E_PERMISSION_NO_ANSWERER
119
- permission.revoke rule-id delegate|allow W E_NOT_FOUND E_PERMISSION_DELEGATE_ONLY E_PERMISSION_NO_ANSWERER
120
- permission.acl.init - allow W E_NOT_SUPPORTED E_ELEVATION_REQUIRED E_PERMISSION