@sema-agent/settings-schema 17.1.0 → 18.0.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.
Files changed (154) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/README.md +73 -5
  3. package/conformance/VERSION +1 -1
  4. package/conformance/baseline.json +3 -0
  5. package/conformance/cases/c01-entry-salvage-then-typo/expected.json +4 -1
  6. package/conformance/cases/c02-hosts-clamp-stacked/expected.json +4 -1
  7. package/conformance/cases/c03-literal-dotted-top-key/expected.json +4 -1
  8. package/conformance/cases/c04-all-bad-collection-not-salvaged/expected.json +4 -1
  9. package/conformance/cases/c07-hooks-is-not-a-config-domain/expected.json +4 -1
  10. package/conformance/cases/c08-limits-unknown-carried-64/expected.json +4 -1
  11. package/conformance/cases/c09-limits-unknown-carried-65/expected.json +4 -1
  12. package/conformance/cases/c10-record-key-secret-redacted-twice/expected.json +4 -1
  13. package/conformance/cases/c11-unicode-unknown-keys/expected.json +4 -1
  14. package/conformance/cases/c12-prototype-name-top-keys/expected.json +4 -1
  15. package/conformance/cases/c13-models-field-null/expected.json +4 -1
  16. package/conformance/cases/c14-models-field-empty-array/expected.json +4 -1
  17. package/conformance/cases/c15-models-field-number/expected.json +4 -1
  18. package/conformance/cases/c16-deep-nesting-205/expected.json +3 -0
  19. package/conformance/cases/c17-empty-domain-file/expected.json +3 -0
  20. package/conformance/cases/c18-wire-envelope-not-an-object/expected.json +4 -1
  21. package/conformance/cases/c19-wire-envelope-no-version/expected.json +4 -1
  22. package/conformance/cases/c21-governance-empty-object-wins/expected.json +4 -1
  23. package/conformance/cases/c23-mixed-validity-models/expected.json +4 -1
  24. package/conformance/cases/c24-valid-entries-with-unknown-keys/expected.json +4 -1
  25. package/conformance/cases/c25-published-roundtrip-zero-warnings/expected.json +4 -1
  26. package/conformance/cases/c26-hosts-array-1024/expected.json +4 -1
  27. package/conformance/cases/c27-hosts-array-1025/expected.json +4 -1
  28. package/conformance/cases/c28-large-document-over-1mb/expected.json +4 -1
  29. package/conformance/cases/c29-salvage-then-domain-refine-fails/expected.json +4 -1
  30. package/conformance/cases/c30-version-triple-a-first/expected.json +4 -1
  31. package/conformance/cases/c31-version-triple-b-changed/expected.json +4 -1
  32. package/conformance/cases/c32-version-triple-a-restored/expected.json +4 -1
  33. package/conformance/cases/c33-limits-nested-carried-key/expected.json +4 -1
  34. package/conformance/cases/c34-skills-content-vs-manifest/expected.json +7 -1
  35. package/conformance/cases/c35-portable-domain-file-is-null/expected.json +3 -0
  36. package/conformance/cases/c36-skills-entry-without-content/expected.json +7 -1
  37. package/conformance/cases/c37-skills-field-null/expected.json +6 -0
  38. package/conformance/cases/c38-remote-exec-is-a-non-domain-file/expected.json +4 -1
  39. package/conformance/cases/c39-hidden-file-in-config-d/expected.json +4 -1
  40. package/conformance/cases/c40-s712-identity-only-what-reads-out/expected.json +4 -1
  41. package/conformance/cases/c41-s792-encoded-pem-identity/expected.json +4 -1
  42. package/conformance/cases/c42-s792-unknown-key-secret-redacted/expected.json +4 -1
  43. package/conformance/cases/c43-s792-form-and-deep-encoded-pem/expected.json +4 -1
  44. package/conformance/cases/c44-s792-utf8-escape-glued-token/expected.json +4 -1
  45. package/conformance/cases/c45-s792-identity-bounds-and-decode-depth/expected.json +4 -1
  46. package/conformance/cases/c46-s811-host-name-url-userinfo-withheld/expected.json +4 -1
  47. package/conformance/cases/c47-s811-roles-key-url-userinfo-withheld/expected.json +4 -1
  48. package/conformance/cases/c48-s935-model-headers-transported/expected.json +4 -1
  49. package/conformance/cases/c49-s935-model-headers-secret-entry-dropped/expected.json +4 -1
  50. package/conformance/cases/c50-s784-lane-host-legal/expected.json +4 -1
  51. package/conformance/cases/c51-s784-lane-e2b-legal/expected.json +4 -1
  52. package/conformance/cases/c52-s784-lane-k8s-legal/expected.json +4 -1
  53. package/conformance/cases/c53-s784-lane-ssh-legal/expected.json +4 -1
  54. package/conformance/cases/c54-s784-lane-adb-legal/expected.json +4 -1
  55. package/conformance/cases/c55-s784-lane-local-docker-legal/expected.json +4 -1
  56. package/conformance/cases/c56-s784-lane-device-legal/expected.json +4 -1
  57. package/conformance/cases/c57-s784-g1-enabled-no-lane/expected.json +4 -1
  58. package/conformance/cases/c58-s784-g2-multitenant-host/expected.json +4 -1
  59. package/conformance/cases/c59-s784-g3-k8s-missing-image/expected.json +4 -1
  60. package/conformance/cases/c60-s784-g3-e2b-missing-key/expected.json +4 -1
  61. package/conformance/cases/c61-s784-g3-other-lane-key/expected.json +4 -1
  62. package/conformance/cases/c62-s784-g7a-session-tidb/expected.json +4 -1
  63. package/conformance/cases/c63-s784-host-two-replicas/expected.json +4 -1
  64. package/conformance/cases/c64-s784-multitenant-local-store/expected.json +4 -1
  65. package/conformance/cases/c65-s784-pg-without-db/expected.json +4 -1
  66. package/conformance/cases/c66-s784-memory-backend-mismatch/expected.json +4 -1
  67. package/conformance/cases/c67-s784-retired-field-loud/expected.json +4 -1
  68. package/conformance/cases/c68-s784-device-local-store/expected.json +4 -1
  69. package/conformance/cases/c69-deep-encoded-token-past-every-reading/expected.json +4 -1
  70. package/conformance/cases/c70-rewritten-token-in-front-of-encoded-pem-header/expected.json +4 -1
  71. package/conformance/cases/c71-one-scanner-write-reads-display-forms/expected.json +4 -1
  72. package/conformance/cases/c72-dangling-reference-write-seat-refuses/expected.json +4 -1
  73. package/conformance/cases/c73-projects-extended-registration-reads/expected.json +4 -1
  74. package/conformance/cases/c74-projects-over-bound-refused/expected.json +4 -1
  75. package/conformance/cases/c75-projects-patch-remote-uniqueness/expected.json +4 -1
  76. package/conformance/cases/c76-projects-entry-salvage-nested/expected.json +4 -1
  77. package/conformance/cases/c77-prompts-center-artifact-carried/expected.json +4 -1
  78. package/conformance/cases/c78-prompts-empty-bindings-refused/expected.json +4 -1
  79. package/conformance/cases/c79-prompts-bad-worker-artifact-not-salvaged/expected.json +4 -1
  80. package/conformance/cases/c80-prompts-wire-v2-verbatim/expected.json +7 -1
  81. package/conformance/cases/c81-prompts-declaration-bindings-refused/expected.json +4 -1
  82. package/conformance/cases/c82-prompts-v1-catalog-refused/expected.json +4 -1
  83. package/conformance/cases/c83-prompts-bad-overlay-not-salvaged/expected.json +4 -1
  84. package/conformance/cases/c84-sandbox-git-block-carried-verbatim/expected.json +7 -1
  85. package/conformance/cases/c85-sandbox-git-refused-block-still-carried/expected.json +7 -1
  86. package/conformance/cases/c86-a2a-peers-legal/expected.json +135 -0
  87. package/conformance/cases/c86-a2a-peers-legal/input.json +32 -0
  88. package/conformance/cases/c86-a2a-peers-legal/meta.json +5 -0
  89. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/expected.json +123 -0
  90. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/input.json +39 -0
  91. package/conformance/cases/c87-a2a-credential-refs-not-partially-salvaged/meta.json +5 -0
  92. package/conformance/cases/cc01-mcp-toolfaces-non-object/expected.json +4 -1
  93. package/conformance/cases/cc02-mcp-source-key-is-transported/expected.json +4 -1
  94. package/conformance/cases/cc03-model-compat-unknown-subkey/expected.json +4 -1
  95. package/conformance/cases/cli01-model-name-nfc-vs-nfd/expected.json +4 -1
  96. package/conformance/cases/cli02-control-and-zero-width-chars/expected.json +4 -1
  97. package/conformance/cases/cli03-credential-pasted-into-unknown-key/expected.json +4 -1
  98. package/conformance/cases/cli04-env-value-with-newline/expected.json +4 -1
  99. package/conformance/cases/cli05-long-and-prefix-colliding-names/expected.json +4 -1
  100. package/conformance/cases/t01-absent-domain-key-not-null/expected.json +4 -1
  101. package/conformance/cases/t03-prototype-name-key-not-swallowed/expected.json +4 -1
  102. package/conformance/cases/t04-secret-literal-never-in-warning-value/expected.json +4 -1
  103. package/conformance/cases/z01-every-domain-empty-object/expected.json +4 -1
  104. package/conformance/cases/z02-every-domain-unknown-key/expected.json +4 -1
  105. package/dist/atomic-write.d.ts +4 -3
  106. package/dist/atomic-write.js +25 -11
  107. package/dist/catalog-manages.d.ts +8 -3
  108. package/dist/catalog-manages.js +16 -7
  109. package/dist/center-refs.d.ts +4 -3
  110. package/dist/center-refs.js +8 -6
  111. package/dist/cli.d.ts +1 -1
  112. package/dist/config-d-file.d.ts +4 -2
  113. package/dist/config-d-file.js +27 -6
  114. package/dist/config-fns.d.ts +6 -1
  115. package/dist/config-fns.js +16 -2
  116. package/dist/config-root-documents.d.ts +2 -2
  117. package/dist/config-root-documents.js +1 -1
  118. package/dist/config-root-node.d.ts +8 -1
  119. package/dist/config-root-node.js +105 -23
  120. package/dist/env-model-table.js +3 -3
  121. package/dist/execution-lanes.d.ts +158 -11
  122. package/dist/execution-lanes.js +76 -11
  123. package/dist/file-edit.d.ts +1 -1
  124. package/dist/file-edit.js +2 -1
  125. package/dist/index.d.ts +1 -1
  126. package/dist/index.js +1 -1
  127. package/dist/mcp-request-entry.d.ts +11 -21
  128. package/dist/mcp-request-entry.js +21 -38
  129. package/dist/node.d.ts +4 -1
  130. package/dist/node.js +6 -2
  131. package/dist/one-time-migrations.js +1 -1
  132. package/dist/portable-domains.d.ts +1 -1
  133. package/dist/portable-domains.js +1 -1
  134. package/dist/remote-exec.d.ts +5 -0
  135. package/dist/remote-exec.js +5 -0
  136. package/dist/reserved-member-name.d.ts +13 -0
  137. package/dist/reserved-member-name.js +14 -0
  138. package/dist/retired-envs.js +2 -0
  139. package/dist/secret-refs.d.ts +1 -1
  140. package/dist/secret-refs.js +5 -1
  141. package/dist/settings-units.d.ts +3 -0
  142. package/dist/settings-units.js +18 -0
  143. package/dist/types.d.ts +70 -41
  144. package/dist/types.js +179 -32
  145. package/dist/url-credential.d.ts +12 -2
  146. package/dist/url-credential.js +164 -9
  147. package/dist/wire-name.d.ts +19 -0
  148. package/dist/wire-name.js +21 -0
  149. package/docs/API-SURFACE.md +31 -6
  150. package/docs/CC-DIVERGENCE.md +6 -0
  151. package/docs/CONSUMER-INTEGRATION.md +127 -7
  152. package/llms-full.txt +40 -17
  153. package/llms.txt +9 -9
  154. package/package.json +1 -1
@@ -0,0 +1,135 @@
1
+ {
2
+ "domains": {
3
+ "outcome": "effective",
4
+ "effective": {
5
+ "a2a": {
6
+ "peers": [
7
+ {
8
+ "allowSkills": [
9
+ "search"
10
+ ],
11
+ "enabled": true,
12
+ "headerRefs": {
13
+ "Authorization": "KB_TOKEN"
14
+ },
15
+ "name": "kb",
16
+ "principalHeader": "X-Principal",
17
+ "scenarios": [],
18
+ "url": "https://kb.example/a2a"
19
+ },
20
+ {
21
+ "cardUrl": "https://ops.example/agent-card.json",
22
+ "enabled": false,
23
+ "name": "ops",
24
+ "scenarios": [
25
+ "oncall"
26
+ ],
27
+ "url": "https://ops.example/"
28
+ }
29
+ ]
30
+ },
31
+ "collab": {
32
+ "templates": []
33
+ },
34
+ "entitlement": {
35
+ "budgetGroups": [],
36
+ "groupBindings": [],
37
+ "subjects": [],
38
+ "tiers": []
39
+ },
40
+ "execution": {
41
+ "allowedLanes": [
42
+ "host"
43
+ ],
44
+ "required": false
45
+ },
46
+ "governance": {},
47
+ "hosts": {
48
+ "hosts": []
49
+ },
50
+ "limits": {},
51
+ "mcp": {
52
+ "servers": []
53
+ },
54
+ "models": {
55
+ "atMention": {
56
+ "mode": "all"
57
+ },
58
+ "models": [],
59
+ "roles": {},
60
+ "tierGroups": []
61
+ },
62
+ "plugins": {
63
+ "plugins": []
64
+ },
65
+ "projects": {
66
+ "projects": {}
67
+ },
68
+ "prompts": {},
69
+ "rosters": {
70
+ "rosters": []
71
+ },
72
+ "scenarios": {
73
+ "scenarios": []
74
+ },
75
+ "skills": {
76
+ "skills": []
77
+ },
78
+ "systems": {
79
+ "systems": []
80
+ },
81
+ "workers": {
82
+ "workers": []
83
+ }
84
+ },
85
+ "warnings": [],
86
+ "metadata": {
87
+ "version": 7,
88
+ "updatedAt": "2026-11-03T00:00:00.000Z"
89
+ }
90
+ },
91
+ "strictWrite": {
92
+ "outcome": "effective",
93
+ "effective": {
94
+ "a2a": {
95
+ "peers": [
96
+ {
97
+ "allowSkills": [
98
+ "search"
99
+ ],
100
+ "enabled": true,
101
+ "headerRefs": {
102
+ "Authorization": "KB_TOKEN"
103
+ },
104
+ "name": "kb",
105
+ "principalHeader": "X-Principal",
106
+ "scenarios": [],
107
+ "url": "https://kb.example/a2a"
108
+ },
109
+ {
110
+ "cardUrl": "https://ops.example/agent-card.json",
111
+ "enabled": false,
112
+ "name": "ops",
113
+ "scenarios": [
114
+ "oncall"
115
+ ],
116
+ "url": "https://ops.example/"
117
+ }
118
+ ]
119
+ }
120
+ },
121
+ "warnings": [],
122
+ "metadata": {
123
+ "version": 7,
124
+ "updatedAt": "2026-11-03T00:00:00.000Z"
125
+ },
126
+ "why": "受理:两个对端都合 `A2aPeerSpec`(URL 不带凭据与 fragment、头名合 token 文法、引用是 env-NAME、对端名可当线上名且不重名);严写收下的就是读腿 a2a 那一格。"
127
+ },
128
+ "wire": {
129
+ "derive": true
130
+ },
131
+ "localRoot": {
132
+ "derive": true,
133
+ "localVersion": 206605001400842
134
+ }
135
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "meta": {
3
+ "version": 7,
4
+ "updatedAt": "2026-11-03T00:00:00.000Z"
5
+ },
6
+ "domains": {
7
+ "a2a": {
8
+ "peers": [
9
+ {
10
+ "name": "kb",
11
+ "url": "https://kb.example/a2a",
12
+ "headerRefs": {
13
+ "Authorization": "KB_TOKEN"
14
+ },
15
+ "principalHeader": "X-Principal",
16
+ "allowSkills": [
17
+ "search"
18
+ ]
19
+ },
20
+ {
21
+ "name": "ops",
22
+ "url": "https://ops.example/",
23
+ "cardUrl": "https://ops.example/agent-card.json",
24
+ "scenarios": [
25
+ "oncall"
26
+ ],
27
+ "enabled": false
28
+ }
29
+ ]
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "contributor": "contract",
3
+ "claim": "`a2a`(常驻出站 A2A 对端名册)是便携域:两个合法对端在四条腿上同读 —— 缺省物化(缺 `scenarios` ⇒ `[]`、缺 `enabled` ⇒ `true`),停用的对端照样在表里(`enabled: false` 由读方过滤,不是读腿丢),header 值只以 env-NAME 引用出现(`headerRefs`),零告警。本地腿读 `config.d/a2a.json`:在没有这个域的包上,同一份文件报 `unread-config-file`、远端 `/effective.a2a` 原样透传 —— 一个仍按旧读法的读腿会把这条读成「对端名册缺席」或「未校验的原文」。",
4
+ "firstRedOn": []
5
+ }
@@ -0,0 +1,123 @@
1
+ {
2
+ "domains": {
3
+ "outcome": "effective",
4
+ "effective": {
5
+ "a2a": {
6
+ "peers": [
7
+ {
8
+ "enabled": true,
9
+ "headerRefs": {
10
+ "Authorization": "KB_TOKEN"
11
+ },
12
+ "name": "kb",
13
+ "scenarios": [],
14
+ "url": "https://kb.example/a2a"
15
+ },
16
+ {
17
+ "allowSkills": [
18
+ "search"
19
+ ],
20
+ "enabled": true,
21
+ "name": "narrow",
22
+ "scenarios": [],
23
+ "url": "https://narrow.example/a2a"
24
+ }
25
+ ]
26
+ },
27
+ "collab": {
28
+ "templates": []
29
+ },
30
+ "entitlement": {
31
+ "budgetGroups": [],
32
+ "groupBindings": [],
33
+ "subjects": [],
34
+ "tiers": []
35
+ },
36
+ "execution": {
37
+ "allowedLanes": [
38
+ "host"
39
+ ],
40
+ "required": false
41
+ },
42
+ "governance": {},
43
+ "hosts": {
44
+ "hosts": []
45
+ },
46
+ "limits": {},
47
+ "mcp": {
48
+ "servers": []
49
+ },
50
+ "models": {
51
+ "atMention": {
52
+ "mode": "all"
53
+ },
54
+ "models": [],
55
+ "roles": {},
56
+ "tierGroups": []
57
+ },
58
+ "plugins": {
59
+ "plugins": []
60
+ },
61
+ "projects": {
62
+ "projects": {}
63
+ },
64
+ "prompts": {},
65
+ "rosters": {
66
+ "rosters": []
67
+ },
68
+ "scenarios": {
69
+ "scenarios": []
70
+ },
71
+ "skills": {
72
+ "skills": []
73
+ },
74
+ "systems": {
75
+ "systems": []
76
+ },
77
+ "workers": {
78
+ "workers": []
79
+ }
80
+ },
81
+ "warnings": [
82
+ {
83
+ "domain": "a2a",
84
+ "identity": {
85
+ "name": "half"
86
+ },
87
+ "kind": "entry-dropped",
88
+ "path": [
89
+ "peers",
90
+ 1
91
+ ],
92
+ "reasonCode": "entry-schema-invalid"
93
+ },
94
+ {
95
+ "domain": "a2a",
96
+ "kind": "entry-dropped",
97
+ "path": [
98
+ "peers",
99
+ 2,
100
+ "allowSkills",
101
+ 1
102
+ ],
103
+ "reasonCode": "entry-schema-invalid"
104
+ }
105
+ ],
106
+ "metadata": {
107
+ "version": 7,
108
+ "updatedAt": "2026-11-03T00:00:00.000Z"
109
+ }
110
+ },
111
+ "strictWrite": {
112
+ "outcome": "rejected",
113
+ "stage": "validate",
114
+ "why": "拒:`half` 的一条 header 引用不是 env-NAME、`half` 与 `narrow` 的 `allowSkills` 各有一个空串 —— 写是一个动作,一处坏整份不受理。"
115
+ },
116
+ "wire": {
117
+ "derive": true
118
+ },
119
+ "localRoot": {
120
+ "derive": true,
121
+ "localVersion": 3675868689326288
122
+ }
123
+ }
@@ -0,0 +1,39 @@
1
+ {
2
+ "meta": {
3
+ "version": 7,
4
+ "updatedAt": "2026-11-03T00:00:00.000Z"
5
+ },
6
+ "domains": {
7
+ "a2a": {
8
+ "peers": [
9
+ {
10
+ "name": "kb",
11
+ "url": "https://kb.example/a2a",
12
+ "headerRefs": {
13
+ "Authorization": "KB_TOKEN"
14
+ }
15
+ },
16
+ {
17
+ "name": "half",
18
+ "url": "https://half.example/a2a",
19
+ "headerRefs": {
20
+ "Authorization": "HALF_TOKEN",
21
+ "X-Tenant": "not an env name"
22
+ },
23
+ "allowSkills": [
24
+ "search",
25
+ ""
26
+ ]
27
+ },
28
+ {
29
+ "name": "narrow",
30
+ "url": "https://narrow.example/a2a",
31
+ "allowSkills": [
32
+ "search",
33
+ ""
34
+ ]
35
+ }
36
+ ]
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "contributor": "contract",
3
+ "claim": "凭据引用集合不可部分抢救:对端 `half` 的 `headerRefs` 里一条引用坏形(值不是 env-NAME)⇒ **整个对端** `entry-dropped`(域 `a2a`、坐标 `[\"peers\",1]`、身份 `half`),不是只丢那一条引用、留下一个带着一半凭据去拨远端的对端;兄弟 `kb` 原样保留。对照组 `narrow`:非凭据集合(`allowSkills`)里的坏元素照旧逐条丢(坐标 `[\"peers\",2,\"allowSkills\",1]`;丢了只会更窄),对端本身活下来。严写腿整份拒。一个对每个 Record 都逐条抢救的读腿会把 `half` 读成「只带 Authorization 的对端」。",
4
+ "firstRedOn": []
5
+ }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -161,6 +164,6 @@
161
164
  },
162
165
  "localRoot": {
163
166
  "derive": true,
164
- "localVersion": 197807763204275
167
+ "localVersion": 3253988119103129
165
168
  }
166
169
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -107,6 +110,6 @@
107
110
  },
108
111
  "localRoot": {
109
112
  "derive": true,
110
- "localVersion": 2482576595667034
113
+ "localVersion": 3591318400132154
111
114
  }
112
115
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -116,6 +119,6 @@
116
119
  },
117
120
  "localRoot": {
118
121
  "derive": true,
119
- "localVersion": 4345724001787046
122
+ "localVersion": 1663867526020649
120
123
  }
121
124
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -112,6 +115,6 @@
112
115
  },
113
116
  "localRoot": {
114
117
  "derive": true,
115
- "localVersion": 694685728514477
118
+ "localVersion": 2654544430240950
116
119
  }
117
120
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -125,6 +128,6 @@
125
128
  },
126
129
  "localRoot": {
127
130
  "derive": true,
128
- "localVersion": 4213710307592750
131
+ "localVersion": 3769852229872529
129
132
  }
130
133
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -121,6 +124,6 @@
121
124
  },
122
125
  "localRoot": {
123
126
  "derive": true,
124
- "localVersion": 2617555914511015
127
+ "localVersion": 1531100058823047
125
128
  }
126
129
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -149,6 +152,6 @@
149
152
  },
150
153
  "localRoot": {
151
154
  "derive": true,
152
- "localVersion": 2617555914511015
155
+ "localVersion": 1531100058823047
153
156
  }
154
157
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -148,6 +151,6 @@
148
151
  },
149
152
  "localRoot": {
150
153
  "derive": true,
151
- "localVersion": 1318036626730287
154
+ "localVersion": 3555569200054848
152
155
  }
153
156
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -91,6 +94,6 @@
91
94
  },
92
95
  "localRoot": {
93
96
  "derive": true,
94
- "localVersion": 2247688933988744
97
+ "localVersion": 1409565868890479
95
98
  }
96
99
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -152,6 +155,6 @@
152
155
  },
153
156
  "localRoot": {
154
157
  "derive": true,
155
- "localVersion": 3232605402901914
158
+ "localVersion": 3557208461559525
156
159
  }
157
160
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -108,6 +111,6 @@
108
111
  },
109
112
  "localRoot": {
110
113
  "derive": true,
111
- "localVersion": 2614200563056761
114
+ "localVersion": 3944259292559843
112
115
  }
113
116
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -131,6 +134,6 @@
131
134
  },
132
135
  "localRoot": {
133
136
  "derive": true,
134
- "localVersion": 499959296543014
137
+ "localVersion": 3006809479574243
135
138
  }
136
139
  }
@@ -2,6 +2,9 @@
2
2
  "domains": {
3
3
  "outcome": "effective",
4
4
  "effective": {
5
+ "a2a": {
6
+ "peers": []
7
+ },
5
8
  "collab": {
6
9
  "templates": []
7
10
  },
@@ -425,6 +428,6 @@
425
428
  },
426
429
  "localRoot": {
427
430
  "derive": true,
428
- "localVersion": 567413778934550
431
+ "localVersion": 1287865876181806
429
432
  }
430
433
  }
@@ -16,10 +16,11 @@ export declare function danglingSymlinkSentence(link: string, pointsAt: string):
16
16
  /** 读写的真目标 —— **一只判法**(写器与目录迁移的读口共用):`target` 是符号链接 ⇒ 解析到它指向的真文件(悬空 ⇒ 抛 {@link danglingSymlinkSentence});
17
17
  * 不是链接或不存在 ⇒ 原样(不存在的由调用方的读 / 写照常报)。 */
18
18
  export declare function resolveSymlinkTarget(target: string): string;
19
- /** 读一份**可以缺席**的 UTF-8 文本 —— `config.d` 文档与 `.env` 的缺席律在这里:读得到 ⇒ 文本;`ENOENT` ⇒ 先在缺席臂里对 `file` 自己与 `dirSeats`(这份文件所在、包把它当布局座的目录,如 `config.d`)各问一次
20
- * {@link resolveSymlinkTarget}(与目录迁移的读口同一只判官):任一座是悬空链接 ⇒ 抛 {@link danglingSymlinkSentence} 那一句(在场却读不出,不是缺席);都不是 ⇒ `undefined`(路径上真的没有条目)。
21
- * 别的读不出(`EACCES` / `EISDIR` / `ELOOP` …)原样抛。配置根自己是悬空链接(祖先座)不在问的范围内:`dirSeats` 只列文件所在的那一级布局座。 */
19
+ /** 读一份**可以缺席**的 UTF-8 文本 —— `config.d` 文档与 `.env` 的缺席律在 {@link absentUnlessDangling}:读得到 ⇒ 文本;读不出 ⇒ 缺席尾(`ENOENT` 且无座位是悬空链接 ⇒ `undefined`;悬空 ⇒ 抛那一句;别的读不出原样抛)。
20
+ * 配置根自己是悬空链接(祖先座)不在问的范围内:`dirSeats` 只列文件所在的那一级布局座。 */
22
21
  export declare function readTextIfPresent(file: string, dirSeats?: readonly string[]): Promise<string | undefined>;
22
+ /** {@link readTextIfPresent} 的同步孪生(壳在同步具名锁里读 `config.d` 文档要它):同一个缺席尾,只有读文本那一行 I/O 不同。 */
23
+ export declare function readTextIfPresentSync(file: string, dirSeats?: readonly string[]): string | undefined;
23
24
  /** 原子写 `text`(UTF-8)到 `target`(符号链接则写到它指向的真文件,见文件头);模式按 `kind` 在 {@link ATOMIC_FILE_CLASSES} 里的行。父目录须已存在。 */
24
25
  export declare function writeFileAtomic(target: string, text: string, kind: AtomicFileClass): void;
25
26
  /**
@@ -20,9 +20,9 @@
20
20
  * 「链接 ⇒ 真文件、悬空 ⇒ 响亮抛」这一只判法是 {@link resolveSymlinkTarget}:目录迁移的读口(`./config-dir-io`)读目录 / 读文件前也问它,
21
21
  * 悬空链接在读阶段与写阶段是**同一句**({@link danglingSymlinkSentence})。
22
22
  *
23
- * ## `config.d` 文档与 `.env` 的缺席只有一种读法({@link readTextIfPresent})
23
+ * ## `config.d` 文档与 `.env` 的缺席只有一种读法({@link absentUnlessDangling})
24
24
  * **缺席 = 路径上没有任何条目。** `readFile` 跟着符号链接走,链接在而目标不在时内核同样答 `ENOENT`:那是在场却读不出,不是缺席。读一份可以缺席的文本(`config.d` 的文档、配置根下的 `.env`)
25
- * 因此都过同一只读者 —— 读不出且是 `ENOENT` ⇒ 在**缺席臂里**问一次链接判法再答「缺席」,悬空 ⇒ 抛那一句(同一句,不带 errno 码)。判在缺席臂而不在读前:读得到的路径零变化(不多一次系统调用);读之后目标恰好出现仍答缺席 —— 读的那一刻它确实不在,与旧读法同一边界,不是放宽。
25
+ * 因此都过同一只缺席尾({@link absentUnlessDangling};异步读者 {@link readTextIfPresent} 与同步读者 {@link readTextIfPresentSync} 各留一行读文本的 I/O) —— 读不出且是 `ENOENT` ⇒ 在**缺席臂里**问一次链接判法再答「缺席」,悬空 ⇒ 抛那一句(同一句,不带 errno 码)。判在缺席臂而不在读前:读得到的路径零变化(不多一次系统调用);读之后目标恰好出现仍答缺席 —— 读的那一刻它确实不在,与旧读法同一边界,不是放宽。
26
26
  *
27
27
  * ## 不存在才建({@link createFileExclusive})
28
28
  * 另一种写法只有一处不同:最后一步不是 `rename` 盖过目标,而是把(已 fsync 的)临时文件 `link` 成目标名 —— 目标名已在(任何东西:文件、目录、哪怕是悬空的符号链接)⇒
@@ -43,7 +43,7 @@
43
43
  */
44
44
  import { randomBytes } from "node:crypto";
45
45
  import { readFile } from "node:fs/promises";
46
- import { chmodSync, closeSync, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readlinkSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
46
+ import { chmodSync, closeSync, existsSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, readlinkSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
47
47
  import path from "node:path";
48
48
  import { FsFailure } from "./fs-failure.js";
49
49
  /** 按文档类的模式表(闭集)。 */
@@ -80,20 +80,34 @@ export function resolveSymlinkTarget(target) {
80
80
  throw e;
81
81
  }
82
82
  }
83
- /** 读一份**可以缺席**的 UTF-8 文本 —— `config.d` 文档与 `.env` 的缺席律在这里:读得到 ⇒ 文本;`ENOENT` ⇒ 先在缺席臂里对 `file` 自己与 `dirSeats`(这份文件所在、包把它当布局座的目录,如 `config.d`)各问一次
84
- * {@link resolveSymlinkTarget}(与目录迁移的读口同一只判官):任一座是悬空链接 ⇒ 抛 {@link danglingSymlinkSentence} 那一句(在场却读不出,不是缺席);都不是 ⇒ `undefined`(路径上真的没有条目)。
85
- * 别的读不出(`EACCES` / `EISDIR` / `ELOOP` …)原样抛。配置根自己是悬空链接(祖先座)不在问的范围内:`dirSeats` 只列文件所在的那一级布局座。 */
83
+ /** 读一份**可以缺席**的文本之后,「读不出」怎么答的**缺席尾**(异步 {@link readTextIfPresent} 与同步 {@link readTextIfPresentSync} 共用,缺席律只此一处):读失败 `e` 不是 `ENOENT` ⇒ 原样抛(`EACCES` / `EISDIR` / `ELOOP` …);
84
+ * 是 `ENOENT` ⇒ 先在缺席臂里对 `file` 自己与 `dirSeats`(这份文件所在、包把它当布局座的目录,如 `config.d`)各问一次 {@link resolveSymlinkTarget}(与目录迁移的读口同一只判官):任一座是悬空链接 ⇒ 抛
85
+ * {@link danglingSymlinkSentence} 那一句(在场却读不出,不是缺席);都不是 ⇒ `undefined`(路径上真的没有条目)。 */
86
+ function absentUnlessDangling(e, file, dirSeats) {
87
+ if (e.code !== "ENOENT")
88
+ throw e;
89
+ for (const seat of [file, ...dirSeats])
90
+ resolveSymlinkTarget(seat);
91
+ return undefined;
92
+ }
93
+ /** 读一份**可以缺席**的 UTF-8 文本 —— `config.d` 文档与 `.env` 的缺席律在 {@link absentUnlessDangling}:读得到 ⇒ 文本;读不出 ⇒ 缺席尾(`ENOENT` 且无座位是悬空链接 ⇒ `undefined`;悬空 ⇒ 抛那一句;别的读不出原样抛)。
94
+ * 配置根自己是悬空链接(祖先座)不在问的范围内:`dirSeats` 只列文件所在的那一级布局座。 */
86
95
  export async function readTextIfPresent(file, dirSeats = []) {
87
96
  try {
88
97
  return await readFile(file, "utf8");
89
98
  }
90
99
  catch (e) {
91
- if (e.code !== "ENOENT")
92
- throw e;
100
+ return absentUnlessDangling(e, file, dirSeats);
101
+ }
102
+ }
103
+ /** {@link readTextIfPresent} 的同步孪生(壳在同步具名锁里读 `config.d` 文档要它):同一个缺席尾,只有读文本那一行 I/O 不同。 */
104
+ export function readTextIfPresentSync(file, dirSeats = []) {
105
+ try {
106
+ return readFileSync(file, "utf8");
107
+ }
108
+ catch (e) {
109
+ return absentUnlessDangling(e, file, dirSeats);
93
110
  }
94
- for (const seat of [file, ...dirSeats])
95
- resolveSymlinkTarget(seat);
96
- return undefined;
97
111
  }
98
112
  /** 原子写 `text`(UTF-8)到 `target`(符号链接则写到它指向的真文件,见文件头);模式按 `kind` 在 {@link ATOMIC_FILE_CLASSES} 里的行。父目录须已存在。 */
99
113
  export function writeFileAtomic(target, text, kind) {