@xiaobuyu/nodesign 0.0.1 → 0.0.2

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 (282) hide show
  1. package/README.md +156 -106
  2. package/bin/nodesign.js +0 -0
  3. package/package.json +1 -1
  4. package/server/api/README.md +45 -45
  5. package/server/api/_guard.js +75 -75
  6. package/server/api/admin.js +187 -187
  7. package/server/api/assets/docx-page.js +103 -103
  8. package/server/api/assets/helpers.js +22 -22
  9. package/server/api/assets/notes.js +144 -144
  10. package/server/api/assets.js +896 -896
  11. package/server/api/board.js +57 -57
  12. package/server/api/browse.js +138 -138
  13. package/server/api/canvas.js +444 -444
  14. package/server/api/chatai.js +324 -324
  15. package/server/api/exports/build-standalone.js +980 -980
  16. package/server/api/exports/cards.js +118 -118
  17. package/server/api/exports/docx-pdf.js +46 -46
  18. package/server/api/exports/export-page.js +142 -142
  19. package/server/api/exports/handoff.js +147 -147
  20. package/server/api/exports.js +690 -690
  21. package/server/api/instruction.js +75 -75
  22. package/server/api/me.js +116 -116
  23. package/server/api/memory.js +185 -185
  24. package/server/api/pending-changes.js +366 -366
  25. package/server/api/plugins.js +164 -164
  26. package/server/api/projects.js +220 -220
  27. package/server/api/publish.js +64 -64
  28. package/server/api/recent.js +73 -73
  29. package/server/api/sessions.js +589 -589
  30. package/server/api/skills.js +77 -77
  31. package/server/api/standalone-fit.js +168 -168
  32. package/server/api/turn-compose.js +180 -180
  33. package/server/api/turn-inflight.js +44 -44
  34. package/server/auth/README.md +17 -17
  35. package/server/auth/middleware.js +138 -138
  36. package/server/auth/origin-guard.js +71 -71
  37. package/server/auth/session.js +108 -108
  38. package/server/auth/tier.js +113 -113
  39. package/server/auth/users-store.js +348 -348
  40. package/server/edit/README.md +29 -29
  41. package/server/engine/README.md +113 -113
  42. package/server/engine/agent/agent-shared.js +550 -550
  43. package/server/engine/agent/auto-mode-default-hard-deny.txt +4 -4
  44. package/server/engine/agent/auto-mode-rules.js +78 -78
  45. package/server/engine/agent/context.js +418 -418
  46. package/server/engine/agent/events.js +464 -464
  47. package/server/engine/agent/hooks/canvas-validate.js +199 -199
  48. package/server/engine/agent/hooks/failure.js +120 -120
  49. package/server/engine/agent/hooks/file-events.js +75 -75
  50. package/server/engine/agent/hooks/lifecycle.js +233 -233
  51. package/server/engine/agent/hooks/post-canvas-focus.js +80 -80
  52. package/server/engine/agent/hooks/post-guidance.js +206 -206
  53. package/server/engine/agent/hooks/post-subagent-report.js +104 -104
  54. package/server/engine/agent/hooks/post-trim.js +58 -58
  55. package/server/engine/agent/hooks/pre-board-neighborhood.js +34 -34
  56. package/server/engine/agent/hooks/pre-defaults.js +84 -84
  57. package/server/engine/agent/hooks/pre-injectors.js +277 -277
  58. package/server/engine/agent/hooks/pre-performance-log-guard.js +78 -78
  59. package/server/engine/agent/hooks/pre-starter-files.js +61 -61
  60. package/server/engine/agent/hooks/pre-workspace-scope-guard.js +78 -78
  61. package/server/engine/agent/hooks/site-validate.js +94 -94
  62. package/server/engine/agent/hooks/tool-prompts.js +31 -31
  63. package/server/engine/agent/hooks/turn-state-memory.js +56 -56
  64. package/server/engine/agent/hooks/user-prompt-submit.js +215 -215
  65. package/server/engine/agent/hooks.js +354 -354
  66. package/server/engine/agent/init-contract.js +69 -69
  67. package/server/engine/agent/isolation.js +159 -159
  68. package/server/engine/agent/model-table.js +292 -292
  69. package/server/engine/agent/plugin-loader.js +266 -266
  70. package/server/engine/agent/prompts/nodesign-prelude.md +336 -336
  71. package/server/engine/agent/prompts/tools/ask-user-question-protocol.md +71 -71
  72. package/server/engine/agent/prompts/tools/direct-edit-protocol.md +98 -98
  73. package/server/engine/agent/prompts/tools/generate-image-cookbook.gemini-gateway.md +583 -583
  74. package/server/engine/agent/prompts/tools/generate-image-cookbook.md +483 -483
  75. package/server/engine/agent/prompts/tools/hybrid-reference.md +100 -100
  76. package/server/engine/agent/prompts/tools/paint-still-cookbook.md +270 -270
  77. package/server/engine/agent/prompts/tools/roll-film-cookbook.md +70 -70
  78. package/server/engine/agent/prompts/tools/site-reference.md +106 -106
  79. package/server/engine/agent/prompts/tools/tweaks-syntax.md +125 -125
  80. package/server/engine/agent/prompts/tools/vision-checker-dispatch.md +48 -48
  81. package/server/engine/agent/session-loop.js +1024 -1024
  82. package/server/engine/agent/session-model.js +164 -164
  83. package/server/engine/agent/skill.js +204 -204
  84. package/server/engine/agent/task-events.js +78 -78
  85. package/server/engine/agents/ds-extractor.md +132 -132
  86. package/server/engine/agents/explorer.md +190 -190
  87. package/server/engine/agents/index.js +230 -230
  88. package/server/engine/agents/schemas/design-system.json +156 -156
  89. package/server/engine/agents/schemas/tweak-schema.json +113 -113
  90. package/server/engine/agents/tweak-proposer.md +127 -127
  91. package/server/engine/agents/vision-checker.md +254 -254
  92. package/server/engine/browse/capture.js +422 -422
  93. package/server/engine/browse/card.js +137 -137
  94. package/server/engine/browse/handover.js +59 -59
  95. package/server/engine/browse/page-digest.js +119 -119
  96. package/server/engine/browse/refs.js +196 -196
  97. package/server/engine/browse/registry.js +307 -307
  98. package/server/engine/browse/screencast.js +170 -170
  99. package/server/engine/browse/state.js +121 -121
  100. package/server/engine/chatai/chat-log.js +79 -79
  101. package/server/engine/chatai/index.js +193 -193
  102. package/server/engine/chatai/openai-compat.js +152 -152
  103. package/server/engine/chatai/orchestrate.js +309 -309
  104. package/server/engine/chatai/perform.js +40 -40
  105. package/server/engine/chatai/summarize.js +79 -79
  106. package/server/engine/mcp/capability-gate.js +52 -52
  107. package/server/engine/mcp/index.js +339 -339
  108. package/server/engine/mcp/param-sanitizer.js +142 -142
  109. package/server/engine/mcp/tools/arrange-on-board.js +124 -124
  110. package/server/engine/mcp/tools/artifact-session.js +278 -278
  111. package/server/engine/mcp/tools/browse-computer.js +421 -421
  112. package/server/engine/mcp/tools/browse-find-batch.js +183 -183
  113. package/server/engine/mcp/tools/browse-screenshot.js +157 -157
  114. package/server/engine/mcp/tools/browse.js +480 -480
  115. package/server/engine/mcp/tools/build-docx.js +101 -101
  116. package/server/engine/mcp/tools/clear-pending-changes.js +99 -99
  117. package/server/engine/mcp/tools/create-on-board.js +155 -155
  118. package/server/engine/mcp/tools/crystallize-skill.js +168 -168
  119. package/server/engine/mcp/tools/explain-style.js +237 -237
  120. package/server/engine/mcp/tools/export-handoff.js +133 -133
  121. package/server/engine/mcp/tools/expose-tweaks.js +149 -149
  122. package/server/engine/mcp/tools/generate-image.js +842 -842
  123. package/server/engine/mcp/tools/get-computed-styles.js +164 -164
  124. package/server/engine/mcp/tools/get-pending-changes.js +205 -205
  125. package/server/engine/mcp/tools/h3box-ssh.js +84 -84
  126. package/server/engine/mcp/tools/helpers/acquire-page.js +82 -82
  127. package/server/engine/mcp/tools/helpers/motion-lab.js +554 -554
  128. package/server/engine/mcp/tools/helpers/motion-scroll.js +112 -112
  129. package/server/engine/mcp/tools/helpers/perception-page.js +215 -215
  130. package/server/engine/mcp/tools/helpers/reference-download.js +103 -103
  131. package/server/engine/mcp/tools/helpers/rembg-bridge.py +80 -80
  132. package/server/engine/mcp/tools/helpers/shot-pipeline.js +280 -280
  133. package/server/engine/mcp/tools/highlight.js +68 -68
  134. package/server/engine/mcp/tools/list-pages.js +222 -222
  135. package/server/engine/mcp/tools/lookup-tags.js +59 -59
  136. package/server/engine/mcp/tools/navigate-to-page.js +60 -60
  137. package/server/engine/mcp/tools/organize-board.js +64 -64
  138. package/server/engine/mcp/tools/paint-still.js +434 -434
  139. package/server/engine/mcp/tools/pin-to-board.js +130 -130
  140. package/server/engine/mcp/tools/preview-deck.js +130 -130
  141. package/server/engine/mcp/tools/profile-scroll.js +305 -305
  142. package/server/engine/mcp/tools/publish-site.js +102 -102
  143. package/server/engine/mcp/tools/query-elements.js +195 -195
  144. package/server/engine/mcp/tools/read-board.js +103 -103
  145. package/server/engine/mcp/tools/read-document.js +93 -93
  146. package/server/engine/mcp/tools/read-page.js +264 -264
  147. package/server/engine/mcp/tools/read-tavern-json.js +143 -143
  148. package/server/engine/mcp/tools/record-decision.js +140 -140
  149. package/server/engine/mcp/tools/relate-on-board.js +110 -110
  150. package/server/engine/mcp/tools/report-issue.js +115 -115
  151. package/server/engine/mcp/tools/roll-film.js +266 -266
  152. package/server/engine/mcp/tools/screenshot-docx.js +138 -138
  153. package/server/engine/mcp/tools/screenshot-url.js +195 -195
  154. package/server/engine/mcp/tools/screenshot.js +583 -583
  155. package/server/engine/mcp/tools/tier-gate.js +96 -96
  156. package/server/engine/mcp/tools/trace-motion.js +215 -215
  157. package/server/engine/mcp/tools/web-search.js +548 -548
  158. package/server/engine/motion/inventory.js +349 -349
  159. package/server/engine/perception/session.js +235 -235
  160. package/server/engine/plugins/nodesign/.claude-plugin/plugin.json +5 -5
  161. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/SKILL.md +192 -192
  162. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/canvas.template.html +667 -667
  163. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/hybrid-grid.md +22 -22
  164. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/image-led-cover.md +25 -25
  165. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/portrait.md +23 -23
  166. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/quote-backdrop.md +21 -21
  167. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/section-divider.md +23 -23
  168. package/server/engine/plugins/nodesign/skills/deskskill-engine-mini/patterns/text-led.md +21 -21
  169. package/server/engine/plugins/nodesign/skills/docx-craft/SKILL.md +162 -162
  170. package/server/engine/plugins/nodesign/skills/docx-craft/references/token-schema.md +407 -407
  171. package/server/engine/plugins/nodesign/skills/docx-craft//346/226/207/346/241/243.template.json +80 -80
  172. package/server/engine/plugins/nodesign/skills/rp-craft/SKILL.md +228 -228
  173. package/server/engine/plugins/nodesign/skills/rp-craft/patterns//346/274/224/345/207/272/351/241/265-/346/234/200/345/260/217/345/256/236/347/216/260.html +78 -78
  174. package/server/engine/plugins/nodesign/skills/rp-craft//346/274/224/345/207/272.template.js +268 -268
  175. package/server/engine/plugins/nodesign/skills/site-craft/SKILL.md +323 -323
  176. package/server/engine/plugins/nodesign/skills/site-craft/patterns/build-lane.md +134 -134
  177. package/server/engine/plugins/nodesign/skills/site-craft/patterns/cutout-collage.md +61 -61
  178. package/server/engine/plugins/nodesign/skills/site-craft/patterns/mock-app.md +163 -163
  179. package/server/engine/plugins/nodesign/skills/site-craft/patterns/page-transitions.md +86 -86
  180. package/server/engine/runs/active-runs.js +698 -698
  181. package/server/engine/runs/live-turn.js +252 -252
  182. package/server/engine/runs/store.js +364 -364
  183. package/server/engine/runs/turn-relay.js +217 -217
  184. package/server/engine/runtime/workspace.js +164 -164
  185. package/server/lib/artifact-file-path.js +80 -80
  186. package/server/lib/artifact-target.js +284 -284
  187. package/server/lib/asset-refs.js +239 -239
  188. package/server/lib/async-queue.js +104 -104
  189. package/server/lib/auto-relations.js +130 -130
  190. package/server/lib/binding-types.js +60 -60
  191. package/server/lib/board-kind-sizes.js +62 -62
  192. package/server/lib/board-relations.js +133 -133
  193. package/server/lib/browse-proxy.js +180 -180
  194. package/server/lib/canvas-id.js +36 -36
  195. package/server/lib/cover.js +227 -227
  196. package/server/lib/danbooru-tags.js +291 -291
  197. package/server/lib/doc-extract.js +156 -156
  198. package/server/lib/docx/build-from-source.js +260 -260
  199. package/server/lib/docx/build.js +490 -490
  200. package/server/lib/docx/dump-styles.js +287 -287
  201. package/server/lib/docx/fonts/nodesign-cjk.conf +164 -164
  202. package/server/lib/docx/merge-runs.js +129 -129
  203. package/server/lib/docx/numbering.js +218 -218
  204. package/server/lib/docx/order.js +152 -152
  205. package/server/lib/docx/rawzip.js +172 -172
  206. package/server/lib/docx/render.js +92 -92
  207. package/server/lib/docx/text-lint.js +225 -225
  208. package/server/lib/docx/tokens.js +335 -335
  209. package/server/lib/docx/units.js +29 -29
  210. package/server/lib/docx/xml.js +291 -291
  211. package/server/lib/docx-pages.js +187 -187
  212. package/server/lib/export-collect.js +289 -289
  213. package/server/lib/export-package.js +210 -210
  214. package/server/lib/html-srcset.js +145 -145
  215. package/server/lib/image-variant.js +460 -460
  216. package/server/lib/ingress/forward-openai-chat.js +305 -305
  217. package/server/lib/ingress/openai-chat.js +491 -491
  218. package/server/lib/ingress/session-notice.js +66 -66
  219. package/server/lib/ingress/session-routes.js +68 -68
  220. package/server/lib/ingress/slot-probe.js +130 -130
  221. package/server/lib/ingress/upstream-billing.js +52 -52
  222. package/server/lib/ingress/upstream-fail-streak.js +72 -72
  223. package/server/lib/ingress/upstream-truncation.js +48 -48
  224. package/server/lib/issues-store.js +182 -182
  225. package/server/lib/kinds/deck.js +72 -72
  226. package/server/lib/kinds/docx.js +283 -283
  227. package/server/lib/kinds/file-kinds.js +113 -113
  228. package/server/lib/kinds/index.js +202 -202
  229. package/server/lib/kinds/site.js +161 -161
  230. package/server/lib/model-ingress.js +543 -543
  231. package/server/lib/moderation.js +255 -255
  232. package/server/lib/notice-store.js +103 -103
  233. package/server/lib/plugin-install.js +104 -104
  234. package/server/lib/plugin-validator.js +721 -721
  235. package/server/lib/publish-store.js +115 -115
  236. package/server/lib/quick-summary.js +52 -52
  237. package/server/lib/quota.js +270 -270
  238. package/server/lib/rate-window.js +29 -29
  239. package/server/lib/reference-assets.js +92 -92
  240. package/server/lib/region-shot.js +135 -135
  241. package/server/lib/safe-path.js +73 -73
  242. package/server/lib/sdk-session.js +33 -33
  243. package/server/lib/showcase-store.js +92 -92
  244. package/server/lib/site-publish.js +465 -465
  245. package/server/lib/ssrf-guard.js +432 -432
  246. package/server/lib/task-scan.js +158 -158
  247. package/server/lib/tavern-json.js +154 -154
  248. package/server/lib/video-variant.js +237 -237
  249. package/server/lib/workspace-path.js +33 -33
  250. package/server/ops/fix-sdk-musl.mjs +40 -40
  251. package/server/ops/install-macos-fonts.sh +114 -114
  252. package/server/ops/macos-fonts.conf +336 -336
  253. package/server/ops/sandbox-shim/bwrap +57 -57
  254. package/server/projects/assets-summary.js +119 -119
  255. package/server/projects/auto-name.js +53 -53
  256. package/server/projects/board-store.js +640 -640
  257. package/server/projects/move-entry.js +103 -103
  258. package/server/projects/store.js +270 -270
  259. package/server/projects/ui-config.js +54 -54
  260. package/server/projects/workspace-templates.js +53 -53
  261. package/server/projects/workspace.js +1025 -1025
  262. package/server/runtime/local-config.js +177 -177
  263. package/server/runtime/profile.js +72 -72
  264. package/server/services/rembg-service.py +334 -334
  265. package/server/shared/README.md +25 -25
  266. package/server/shared/deck.js +54 -54
  267. package/server/shared/time.js +49 -49
  268. package/server/style-pipeline/README.md +53 -53
  269. package/server/ws/broker.js +36 -36
  270. package/server/ws/browse-channel.js +177 -177
  271. package/server/ws/index.js +386 -386
  272. package/web/README.md +89 -89
  273. package/web/dist/assets/BrowserWindow-BYCgizHw.js +11 -0
  274. package/web/dist/assets/{DeckWindow-4G0xX7P3.js → DeckWindow-pNydEM4L.js} +1 -1
  275. package/web/dist/assets/{DocxWindow-BXQsoPkh.js → DocxWindow-PPgzh3Gq.js} +1 -1
  276. package/web/dist/assets/{SiteWindow-D6eS-2gM.js → SiteWindow-DM0LQPCO.js} +1 -1
  277. package/web/dist/assets/{index-Cbl3ATvp.js → index-BHTBJlpY.js} +32 -32
  278. package/web/dist/assets/{pending-edit-apply-DTjo2geW.js → pending-edit-apply-C0I13CbD.js} +1 -1
  279. package/web/dist/assets/{rotate-cw-LMT3x1Zj.js → rotate-cw-B1UQ8Hld.js} +1 -1
  280. package/web/dist/assets/{square-dashed-mouse-pointer-CBoAkfhi.js → square-dashed-mouse-pointer-8lrgqmfv.js} +1 -1
  281. package/web/dist/index.html +17 -17
  282. package/web/dist/assets/BrowserWindow-Dc7J7CYc.js +0 -11
@@ -1,69 +1,69 @@
1
- /**
2
- * server/engine/agent/init-contract.js — 开局契约自检(2026-08-20 从 session-loop.js
3
- * 拆出,行数棘轮;逻辑原样)。session-loop 在每条 system:init 到达时调一次。
4
- */
5
-
6
- import { recordIssue, signatureOf } from '../../lib/issues-store.js';
7
-
8
- // ── 开局契约自检(2026-08-14 空壳钩子灭门案第 3 层,真正治本)──
9
- //
10
- // 背景:一个 `{ matcher: 'Bash' }` 空壳钩子条目让 SDK initialize 的大 try 吞掉
11
- // TypeError → 全部程序化钩子 + 全部 in-process MCP server 无声蒸发,mcp_servers
12
- // 里连 failed 都不留,会话照常跑 —— 潜伏六天。能静默这么久的结构性原因是这里
13
- // 从来不消费 system:init:SDK 开局就把「会话里实际有哪些 server / 工具」告诉了
14
- // 我们,但没人看。
15
- //
16
- // 现在对账:nodesign 必须 connected,且 server 实例声明的每个工具(探针实测
17
- // deferred 工具也在 init.tools 里,27/27)都必须出现。不满足 → recordIssue 进
18
- // 自动层 + throw 杀会话(外层 catch 走真错路径:markRunFailed + run.error 前端
19
- // 显式可见)。已知代价:SDK 改 init 形状会误杀会话 —— 但误杀 5 分钟定位,
20
- // 静默降级是 6 天暗账;工具残废的会话产出是负价值还烧钱,杀掉比放行仁慈。
21
- export function assertInitContract(init, { sessionId, projectId, isResume, initialPermissionMode, platform, sdkModel, nodesignServer }) {
22
- const problems = [];
23
- const nd = (init.mcp_servers || []).find((s) => s.name === 'nodesign');
24
- if (!nd) {
25
- problems.push('mcp_servers 里没有 nodesign(in-process MCP server 蒸发,连 failed 状态都不留)');
26
- } else if (nd.status !== 'connected') {
27
- problems.push(`nodesign server status=${nd.status}(预期 connected)`);
28
- }
29
- const registered = new Set(init.tools || []);
30
- const expected = (nodesignServer.toolNames || []).map((n) => `mcp__nodesign__${n}`);
31
- const missing = expected.filter((n) => !registered.has(n));
32
- if (missing.length) {
33
- problems.push(`nodesign 工具缺 ${missing.length}/${expected.length}:${missing.join(', ')}`);
34
- }
35
- // 权限模式对账(2026-08-15):**要的和拿到的可能不一样,而且是静默的**。
36
- // 实测:会话模型是 haiku 时 `permissionMode:'auto'` 会被无声降级成 'default',
37
- // init 里照报 default,没有任何报错 —— 分类器一次都不跑,我们却以为它在把关。
38
- // 只警告不杀:降级后的会话还能干活,杀掉代价大于收益;但必须在日志里喊出来。
39
- const wantMode = isResume ? null : (initialPermissionMode === 'plan' ? 'plan' : platform.permissionModeDefault);
40
- if (wantMode && init.permissionMode && init.permissionMode !== wantMode) {
41
- console.warn(
42
- `[session-loop] ⚠️ sid=${sessionId.slice(0, 8)} 权限模式被降级:要 ${wantMode},`
43
- + `实际 ${init.permissionMode}(模型 ${sdkModel} 可能不支持该模式)`,
44
- );
45
- }
46
- if (!problems.length) {
47
- console.info(
48
- `[session-loop] sid=${sessionId.slice(0, 8)} init 契约自检 ✓ `
49
- + `(nodesign connected, ${expected.length}/${expected.length} tools, `
50
- + `mode=${init.permissionMode ?? '未报'})`,
51
- );
52
- return;
53
- }
54
- const detail = problems.join(';');
55
- // 自动层留案底(fail-soft:记录本身不能变成新故障源)。signature 只含缺失
56
- // 集合不含 sessionId —— 同一种蒸发聚成一行计数。
57
- try {
58
- recordIssue({
59
- source: 'auto',
60
- toolName: 'session_init_contract',
61
- summary: `开局契约自检失败:${detail.slice(0, 120)}`,
62
- detail,
63
- projectId,
64
- sessionId,
65
- signature: signatureOf(`session_init_contract|${missing.join(',')}|${nd ? nd.status : 'absent'}`),
66
- });
67
- } catch { /* ignore */ }
68
- throw new Error(`开局契约自检失败(杀会话):${detail}`);
69
- }
1
+ /**
2
+ * server/engine/agent/init-contract.js — 开局契约自检(2026-08-20 从 session-loop.js
3
+ * 拆出,行数棘轮;逻辑原样)。session-loop 在每条 system:init 到达时调一次。
4
+ */
5
+
6
+ import { recordIssue, signatureOf } from '../../lib/issues-store.js';
7
+
8
+ // ── 开局契约自检(2026-08-14 空壳钩子灭门案第 3 层,真正治本)──
9
+ //
10
+ // 背景:一个 `{ matcher: 'Bash' }` 空壳钩子条目让 SDK initialize 的大 try 吞掉
11
+ // TypeError → 全部程序化钩子 + 全部 in-process MCP server 无声蒸发,mcp_servers
12
+ // 里连 failed 都不留,会话照常跑 —— 潜伏六天。能静默这么久的结构性原因是这里
13
+ // 从来不消费 system:init:SDK 开局就把「会话里实际有哪些 server / 工具」告诉了
14
+ // 我们,但没人看。
15
+ //
16
+ // 现在对账:nodesign 必须 connected,且 server 实例声明的每个工具(探针实测
17
+ // deferred 工具也在 init.tools 里,27/27)都必须出现。不满足 → recordIssue 进
18
+ // 自动层 + throw 杀会话(外层 catch 走真错路径:markRunFailed + run.error 前端
19
+ // 显式可见)。已知代价:SDK 改 init 形状会误杀会话 —— 但误杀 5 分钟定位,
20
+ // 静默降级是 6 天暗账;工具残废的会话产出是负价值还烧钱,杀掉比放行仁慈。
21
+ export function assertInitContract(init, { sessionId, projectId, isResume, initialPermissionMode, platform, sdkModel, nodesignServer }) {
22
+ const problems = [];
23
+ const nd = (init.mcp_servers || []).find((s) => s.name === 'nodesign');
24
+ if (!nd) {
25
+ problems.push('mcp_servers 里没有 nodesign(in-process MCP server 蒸发,连 failed 状态都不留)');
26
+ } else if (nd.status !== 'connected') {
27
+ problems.push(`nodesign server status=${nd.status}(预期 connected)`);
28
+ }
29
+ const registered = new Set(init.tools || []);
30
+ const expected = (nodesignServer.toolNames || []).map((n) => `mcp__nodesign__${n}`);
31
+ const missing = expected.filter((n) => !registered.has(n));
32
+ if (missing.length) {
33
+ problems.push(`nodesign 工具缺 ${missing.length}/${expected.length}:${missing.join(', ')}`);
34
+ }
35
+ // 权限模式对账(2026-08-15):**要的和拿到的可能不一样,而且是静默的**。
36
+ // 实测:会话模型是 haiku 时 `permissionMode:'auto'` 会被无声降级成 'default',
37
+ // init 里照报 default,没有任何报错 —— 分类器一次都不跑,我们却以为它在把关。
38
+ // 只警告不杀:降级后的会话还能干活,杀掉代价大于收益;但必须在日志里喊出来。
39
+ const wantMode = isResume ? null : (initialPermissionMode === 'plan' ? 'plan' : platform.permissionModeDefault);
40
+ if (wantMode && init.permissionMode && init.permissionMode !== wantMode) {
41
+ console.warn(
42
+ `[session-loop] ⚠️ sid=${sessionId.slice(0, 8)} 权限模式被降级:要 ${wantMode},`
43
+ + `实际 ${init.permissionMode}(模型 ${sdkModel} 可能不支持该模式)`,
44
+ );
45
+ }
46
+ if (!problems.length) {
47
+ console.info(
48
+ `[session-loop] sid=${sessionId.slice(0, 8)} init 契约自检 ✓ `
49
+ + `(nodesign connected, ${expected.length}/${expected.length} tools, `
50
+ + `mode=${init.permissionMode ?? '未报'})`,
51
+ );
52
+ return;
53
+ }
54
+ const detail = problems.join(';');
55
+ // 自动层留案底(fail-soft:记录本身不能变成新故障源)。signature 只含缺失
56
+ // 集合不含 sessionId —— 同一种蒸发聚成一行计数。
57
+ try {
58
+ recordIssue({
59
+ source: 'auto',
60
+ toolName: 'session_init_contract',
61
+ summary: `开局契约自检失败:${detail.slice(0, 120)}`,
62
+ detail,
63
+ projectId,
64
+ sessionId,
65
+ signature: signatureOf(`session_init_contract|${missing.join(',')}|${nd ? nd.status : 'absent'}`),
66
+ });
67
+ } catch { /* ignore */ }
68
+ throw new Error(`开局契约自检失败(杀会话):${detail}`);
69
+ }
@@ -1,159 +1,159 @@
1
- /**
2
- * agent/isolation.js —— 会话隔离配置的单一来源(2026-08-15)
3
- *
4
- * 一件事被拆成两道闸,因为 SDK 的工具分两拨跑:
5
- *
6
- * ① sandbox → **只管 Bash**。bwrap 起独立 mount/net namespace:写只落工作区、
7
- * 凭据读不到、env 里的 key 被 unset、AF_UNIX 与回环被切断、
8
- * 外网走代理。开关在 runtime/platform.js(NODESIGN_SANDBOX=on)。
9
- * ② settings.permissions.deny → 管 **Read / Grep / Glob / Write / Edit**。
10
- * 这些是 SDK 进程内工具,根本不进 bwrap,沙盒对它们零作用。
11
- *
12
- * 缺一半等于没关。第三块(跨项目边界)在 hooks/pre-workspace-scope-guard.js ——
13
- * deny 规则写不出"除了自己这个项目"(deny 压过 allow,项目又是动态新建的)。
14
- *
15
- * 08-15 开这套时真跑出来的四件事,改之前先读:
16
- * 1. `dangerouslyDisableSandbox` 默认允许,agent 撞到偶发失败会自己拿它关沙盒
17
- * —— 必须 allowUnsandboxedCommands:false 焊死。
18
- * 2. deny 规则的路径必须是**双斜杠**绝对形式,单斜杠静默失效。
19
- * 3. npm 默认缓存在 ~/.npm,沙盒里 HOME 不可写 → `npm i` EROFS,构建道整条断。
20
- * 4. 目录级 denyRead 拦得住读文件,拦不住 `ls` 看文件名(接受:名字不是秘密)。
21
- */
22
-
23
- import path from 'node:path';
24
- import os from 'node:os';
25
- import fs from 'node:fs/promises';
26
- import { platform } from '../../runtime/platform.js';
27
- import { autoModeSettings } from './auto-mode-rules.js';
28
-
29
- /**
30
- * 会话要用的缓存/临时目录一次备齐(2026-08-19 从 session-loop 下沉)。
31
- *
32
- * - npmCacheDir:共用 npm 缓存(数据根下;内容寻址 + 完整性校验,跨会话共用
33
- * 不构成投毒面)。沙盒里 HOME 不可写,不给它 `npm i` 直接 EROFS。
34
- * - agentTmpDir:沙盒里 Bash 的 $TMPDIR。SDK 按 CLAUDE_CODE_TMPDIR 派生沙盒
35
- * tmp(没设就 os.tmpdir()),而派生出来的 /tmp/claude 在 bwrap 里只读 ——
36
- * agent 装个 py7zr 连撞四堵墙、venv 静默失败(iss_msz25m5p_v5so)。
37
- * ⚠️ 必须短:SDK 二进制里有条 warn —— 这个路径超 ~30 字节时 AF_UNIX socket
38
- * 放不下,子进程 $TMPDIR **静默回退**默认值(= 白配)。所以挂 /tmp/nd 下、
39
- * 按项目分目录;沙盒里遮兄弟目录见 buildIsolationOptions。
40
- * - pip 缓存指进 agentTmpDir/pip,不然每条 pip 命令先吐一段
41
- * ~/.cache/pip 不可写的 WARNING 噪音。
42
- *
43
- * 目录必须先存在 —— bwrap 绑一个不存在的路径会起不来;建不出来也不拦会话。
44
- * envPatch 直接摊进 sdkEnv。
45
- */
46
- export async function prepareAgentDirs({ dataRoot, projectId, sessionId }) {
47
- const npmCacheDir = path.join(dataRoot, '.npm-cache');
48
- const agentTmpRoot = path.join(os.tmpdir(), 'nd');
49
- const agentTmpDir = path.join(agentTmpRoot, projectId || String(sessionId || 'anon').slice(0, 12));
50
- for (const d of [npmCacheDir, path.join(agentTmpDir, 'pip')]) {
51
- try { await fs.mkdir(d, { recursive: true }); } catch { /* 起不来也不该拦会话 */ }
52
- }
53
- return {
54
- npmCacheDir,
55
- agentTmpRoot,
56
- agentTmpDir,
57
- envPatch: {
58
- npm_config_cache: npmCacheDir,
59
- CLAUDE_CODE_TMPDIR: agentTmpDir,
60
- PIP_CACHE_DIR: path.join(agentTmpDir, 'pip'),
61
- },
62
- };
63
- }
64
-
65
- /**
66
- * bwrap 垫片的 env(PATH 前插一个目录,里面的 `bwrap` 是我们的包装脚本)
67
- *
68
- * 治的是 `apply-seccomp: unshare(CLONE_NEWUSER): Invalid argument` 这个偶发
69
- * —— 内核级竞态,机器越闲越容易撞,实测每十几次 Bash 调用炸一次,而且会把
70
- * agent 带进错误推断(有一次它干脆自己拿 `dangerouslyDisableSandbox` 绕过去)。
71
- * 原理与安全不变量写在 server/ops/sandbox-shim/bwrap 里。
72
- *
73
- * 📮 上游 issue:https://github.com/anthropics/claude-code/issues/86928
74
- * ⏳ **临时设施**:垫片认的是 SDK 内部的命令前缀,**SDK 一升级前缀变了就静默
75
- * 失效**(原样透传 = 悄悄退回那个偶发,不报错)。升级后查
76
- * `<数据根>/.sandbox-shim.log` 里 `rewrote=1` 的计数还在不在涨;
77
- * 上游修好之后连这个函数带 ops/sandbox-shim/ 一起删掉。
78
- * 关掉:`NODESIGN_SANDBOX_SHIM=off`。
79
- */
80
- export function sandboxShimEnv({ baseEnv = process.env, dataRoot } = {}) {
81
- if (!platform.sandboxEnabled) return {};
82
- if (process.env.NODESIGN_SANDBOX_SHIM === 'off') return {};
83
- const dir = path.join(platform.repoRoot, 'server', 'ops', 'sandbox-shim');
84
- return {
85
- PATH: `${dir}${path.delimiter}${baseEnv.PATH || ''}`,
86
- // 改写次数记一笔:SDK 升级后 pattern 变了会静默退回现状,靠这个计数发现
87
- ...(dataRoot ? { NODESIGN_SHIM_LOG: path.join(dataRoot, '.sandbox-shim.log') } : {}),
88
- };
89
- }
90
-
91
- /**
92
- * @param {object} o
93
- * @param {string} o.cwdRoot 会话工作区(= sharedRoot,扁平化之后同一个目录)
94
- * @param {string} o.sharedRoot 项目共享根
95
- * @param {string} o.npmCacheDir 共用 npm 缓存(在数据根下,必须可写可读)
96
- * @param {string} [o.agentTmpRoot] 所有项目沙盒 tmp 的根(/tmp/nd)—— 整体遮读
97
- * @param {string} [o.agentTmpDir] 本项目的沙盒 tmp(CLAUDE_CODE_TMPDIR 指它)——
98
- * 开可写可读天窗。两个一起传或都不传。
99
- * @param {string} o.dataRoot PROJECTS_DATA_ROOT
100
- * @param {object} o.env 传给 SDK 的 env(凭据抹除按它的键名算)
101
- * @returns {{ sandbox: object, settings: object }} 直接摊进 query options
102
- */
103
- export function buildIsolationOptions({ cwdRoot, sharedRoot, npmCacheDir, agentTmpRoot, agentTmpDir, dataRoot, env }) {
104
- return {
105
- sandbox: {
106
- enabled: platform.sandboxEnabled,
107
- failIfUnavailable: false,
108
- // ⭐ 逃生门焊死:SDK 给 Bash 留了 `dangerouslyDisableSandbox` 参数,**默认允许**。
109
- // 08-15 开沙盒当天就被 agent 自发用上了 —— 它撞到 apply-seccomp 偶发失败,
110
- // 第三次自己带上这个参数关掉沙盒,然后读到了隔壁项目的文件(它老实汇报了,
111
- // 所以我们看见了)。一个能被工具参数关掉的沙盒等于没有沙盒。
112
- allowUnsandboxedCommands: false,
113
- network: {
114
- allowLocalBinding: false,
115
- // 全域允许:这层留着不是为了管出口,是为了 Linux 上顺带切断 unix socket
116
- // —— pm2 的 rpc.sock 就在家目录里,通了的话 `pm2 start` 能起一个沙盒外的
117
- // 进程(完整逃逸)。只读挂载拦不住 socket 连接,实测过。
118
- allowedDomains: ['*'],
119
- },
120
- filesystem: {
121
- allowWrite: [
122
- cwdRoot,
123
- ...(sharedRoot ? [
124
- path.join(sharedRoot, '.claude', 'agent-memory'),
125
- path.join(sharedRoot, 'assets'),
126
- ] : []),
127
- npmCacheDir,
128
- // 本项目的沙盒 tmp(CLAUDE_CODE_TMPDIR / $TMPDIR / pip 缓存都指这里)。
129
- // 没有它 Bash 的 tmp 只读:venv 静默失败、pip --user EROFS、npm 装不上
130
- ...(agentTmpDir ? [agentTmpDir] : []),
131
- ],
132
- denyWrite: ['/etc', '/usr', '/bin', '/sbin', '/private/etc'],
133
- // 数据根整个盖住、再用 allowRead 给自己的工作区开天窗。
134
- // ⚠️ 口径要准(08-18 上生产时实测纠正):`ls 数据根` **看得见别的项目的
135
- // 目录名**(沙盒里实测列出了另一个 pid),拦住的是**进去读**——
136
- // `cat <别人的>/board.json` 与 `ls <别人的>/` 都空。
137
- // 原注释写的"连 ls 数据根都只看得见自己那一个条目"过强,是错的;
138
- // platform.js 那边的说法才对:「目录级 denyRead 拦得住 cat,拦不住 ls
139
- // 看文件名 —— 文件名不是秘密,接受」。
140
- // agentTmpRoot(/tmp/nd)同 dataRoot 一个待遇:根整个遮住、自己的子目录
141
- // 开天窗 —— 不遮的话 /tmp/nd/<别的项目>/ 就是跨项目读通道
142
- denyRead: [...platform.credentialBlacklist(), dataRoot, ...(agentTmpRoot ? [agentTmpRoot] : [])],
143
- allowRead: [cwdRoot, npmCacheDir, ...(agentTmpDir ? [agentTmpDir] : [])],
144
- },
145
- credentials: {
146
- // filesystem 那层拦得住 `cat .env`,拦不住 `env` —— 服务端 process.env
147
- // 原样继承给了 Bash 子进程,key 就明晃晃躺着。按名字在沙盒内 unset。
148
- envVars: platform.secretEnvVarNames(env).map(name => ({ name, mode: 'deny' })),
149
- },
150
- },
151
- settings: {
152
- permissions: { deny: platform.protectedPathRules({ dataRoot }) },
153
- // auto 模式的分类器规则(只在开了 auto 时注入,省得白占 settings)。
154
- // ⚠️ 按节替换不是追加 —— 为什么只覆盖 environment / hard_deny 两节,
155
- // 见 auto-mode-rules.js 文件头。
156
- ...(platform.autoModeEnabled ? { autoMode: autoModeSettings() } : {}),
157
- },
158
- };
159
- }
1
+ /**
2
+ * agent/isolation.js —— 会话隔离配置的单一来源(2026-08-15)
3
+ *
4
+ * 一件事被拆成两道闸,因为 SDK 的工具分两拨跑:
5
+ *
6
+ * ① sandbox → **只管 Bash**。bwrap 起独立 mount/net namespace:写只落工作区、
7
+ * 凭据读不到、env 里的 key 被 unset、AF_UNIX 与回环被切断、
8
+ * 外网走代理。开关在 runtime/platform.js(NODESIGN_SANDBOX=on)。
9
+ * ② settings.permissions.deny → 管 **Read / Grep / Glob / Write / Edit**。
10
+ * 这些是 SDK 进程内工具,根本不进 bwrap,沙盒对它们零作用。
11
+ *
12
+ * 缺一半等于没关。第三块(跨项目边界)在 hooks/pre-workspace-scope-guard.js ——
13
+ * deny 规则写不出"除了自己这个项目"(deny 压过 allow,项目又是动态新建的)。
14
+ *
15
+ * 08-15 开这套时真跑出来的四件事,改之前先读:
16
+ * 1. `dangerouslyDisableSandbox` 默认允许,agent 撞到偶发失败会自己拿它关沙盒
17
+ * —— 必须 allowUnsandboxedCommands:false 焊死。
18
+ * 2. deny 规则的路径必须是**双斜杠**绝对形式,单斜杠静默失效。
19
+ * 3. npm 默认缓存在 ~/.npm,沙盒里 HOME 不可写 → `npm i` EROFS,构建道整条断。
20
+ * 4. 目录级 denyRead 拦得住读文件,拦不住 `ls` 看文件名(接受:名字不是秘密)。
21
+ */
22
+
23
+ import path from 'node:path';
24
+ import os from 'node:os';
25
+ import fs from 'node:fs/promises';
26
+ import { platform } from '../../runtime/platform.js';
27
+ import { autoModeSettings } from './auto-mode-rules.js';
28
+
29
+ /**
30
+ * 会话要用的缓存/临时目录一次备齐(2026-08-19 从 session-loop 下沉)。
31
+ *
32
+ * - npmCacheDir:共用 npm 缓存(数据根下;内容寻址 + 完整性校验,跨会话共用
33
+ * 不构成投毒面)。沙盒里 HOME 不可写,不给它 `npm i` 直接 EROFS。
34
+ * - agentTmpDir:沙盒里 Bash 的 $TMPDIR。SDK 按 CLAUDE_CODE_TMPDIR 派生沙盒
35
+ * tmp(没设就 os.tmpdir()),而派生出来的 /tmp/claude 在 bwrap 里只读 ——
36
+ * agent 装个 py7zr 连撞四堵墙、venv 静默失败(iss_msz25m5p_v5so)。
37
+ * ⚠️ 必须短:SDK 二进制里有条 warn —— 这个路径超 ~30 字节时 AF_UNIX socket
38
+ * 放不下,子进程 $TMPDIR **静默回退**默认值(= 白配)。所以挂 /tmp/nd 下、
39
+ * 按项目分目录;沙盒里遮兄弟目录见 buildIsolationOptions。
40
+ * - pip 缓存指进 agentTmpDir/pip,不然每条 pip 命令先吐一段
41
+ * ~/.cache/pip 不可写的 WARNING 噪音。
42
+ *
43
+ * 目录必须先存在 —— bwrap 绑一个不存在的路径会起不来;建不出来也不拦会话。
44
+ * envPatch 直接摊进 sdkEnv。
45
+ */
46
+ export async function prepareAgentDirs({ dataRoot, projectId, sessionId }) {
47
+ const npmCacheDir = path.join(dataRoot, '.npm-cache');
48
+ const agentTmpRoot = path.join(os.tmpdir(), 'nd');
49
+ const agentTmpDir = path.join(agentTmpRoot, projectId || String(sessionId || 'anon').slice(0, 12));
50
+ for (const d of [npmCacheDir, path.join(agentTmpDir, 'pip')]) {
51
+ try { await fs.mkdir(d, { recursive: true }); } catch { /* 起不来也不该拦会话 */ }
52
+ }
53
+ return {
54
+ npmCacheDir,
55
+ agentTmpRoot,
56
+ agentTmpDir,
57
+ envPatch: {
58
+ npm_config_cache: npmCacheDir,
59
+ CLAUDE_CODE_TMPDIR: agentTmpDir,
60
+ PIP_CACHE_DIR: path.join(agentTmpDir, 'pip'),
61
+ },
62
+ };
63
+ }
64
+
65
+ /**
66
+ * bwrap 垫片的 env(PATH 前插一个目录,里面的 `bwrap` 是我们的包装脚本)
67
+ *
68
+ * 治的是 `apply-seccomp: unshare(CLONE_NEWUSER): Invalid argument` 这个偶发
69
+ * —— 内核级竞态,机器越闲越容易撞,实测每十几次 Bash 调用炸一次,而且会把
70
+ * agent 带进错误推断(有一次它干脆自己拿 `dangerouslyDisableSandbox` 绕过去)。
71
+ * 原理与安全不变量写在 server/ops/sandbox-shim/bwrap 里。
72
+ *
73
+ * 📮 上游 issue:https://github.com/anthropics/claude-code/issues/86928
74
+ * ⏳ **临时设施**:垫片认的是 SDK 内部的命令前缀,**SDK 一升级前缀变了就静默
75
+ * 失效**(原样透传 = 悄悄退回那个偶发,不报错)。升级后查
76
+ * `<数据根>/.sandbox-shim.log` 里 `rewrote=1` 的计数还在不在涨;
77
+ * 上游修好之后连这个函数带 ops/sandbox-shim/ 一起删掉。
78
+ * 关掉:`NODESIGN_SANDBOX_SHIM=off`。
79
+ */
80
+ export function sandboxShimEnv({ baseEnv = process.env, dataRoot } = {}) {
81
+ if (!platform.sandboxEnabled) return {};
82
+ if (process.env.NODESIGN_SANDBOX_SHIM === 'off') return {};
83
+ const dir = path.join(platform.repoRoot, 'server', 'ops', 'sandbox-shim');
84
+ return {
85
+ PATH: `${dir}${path.delimiter}${baseEnv.PATH || ''}`,
86
+ // 改写次数记一笔:SDK 升级后 pattern 变了会静默退回现状,靠这个计数发现
87
+ ...(dataRoot ? { NODESIGN_SHIM_LOG: path.join(dataRoot, '.sandbox-shim.log') } : {}),
88
+ };
89
+ }
90
+
91
+ /**
92
+ * @param {object} o
93
+ * @param {string} o.cwdRoot 会话工作区(= sharedRoot,扁平化之后同一个目录)
94
+ * @param {string} o.sharedRoot 项目共享根
95
+ * @param {string} o.npmCacheDir 共用 npm 缓存(在数据根下,必须可写可读)
96
+ * @param {string} [o.agentTmpRoot] 所有项目沙盒 tmp 的根(/tmp/nd)—— 整体遮读
97
+ * @param {string} [o.agentTmpDir] 本项目的沙盒 tmp(CLAUDE_CODE_TMPDIR 指它)——
98
+ * 开可写可读天窗。两个一起传或都不传。
99
+ * @param {string} o.dataRoot PROJECTS_DATA_ROOT
100
+ * @param {object} o.env 传给 SDK 的 env(凭据抹除按它的键名算)
101
+ * @returns {{ sandbox: object, settings: object }} 直接摊进 query options
102
+ */
103
+ export function buildIsolationOptions({ cwdRoot, sharedRoot, npmCacheDir, agentTmpRoot, agentTmpDir, dataRoot, env }) {
104
+ return {
105
+ sandbox: {
106
+ enabled: platform.sandboxEnabled,
107
+ failIfUnavailable: false,
108
+ // ⭐ 逃生门焊死:SDK 给 Bash 留了 `dangerouslyDisableSandbox` 参数,**默认允许**。
109
+ // 08-15 开沙盒当天就被 agent 自发用上了 —— 它撞到 apply-seccomp 偶发失败,
110
+ // 第三次自己带上这个参数关掉沙盒,然后读到了隔壁项目的文件(它老实汇报了,
111
+ // 所以我们看见了)。一个能被工具参数关掉的沙盒等于没有沙盒。
112
+ allowUnsandboxedCommands: false,
113
+ network: {
114
+ allowLocalBinding: false,
115
+ // 全域允许:这层留着不是为了管出口,是为了 Linux 上顺带切断 unix socket
116
+ // —— pm2 的 rpc.sock 就在家目录里,通了的话 `pm2 start` 能起一个沙盒外的
117
+ // 进程(完整逃逸)。只读挂载拦不住 socket 连接,实测过。
118
+ allowedDomains: ['*'],
119
+ },
120
+ filesystem: {
121
+ allowWrite: [
122
+ cwdRoot,
123
+ ...(sharedRoot ? [
124
+ path.join(sharedRoot, '.claude', 'agent-memory'),
125
+ path.join(sharedRoot, 'assets'),
126
+ ] : []),
127
+ npmCacheDir,
128
+ // 本项目的沙盒 tmp(CLAUDE_CODE_TMPDIR / $TMPDIR / pip 缓存都指这里)。
129
+ // 没有它 Bash 的 tmp 只读:venv 静默失败、pip --user EROFS、npm 装不上
130
+ ...(agentTmpDir ? [agentTmpDir] : []),
131
+ ],
132
+ denyWrite: ['/etc', '/usr', '/bin', '/sbin', '/private/etc'],
133
+ // 数据根整个盖住、再用 allowRead 给自己的工作区开天窗。
134
+ // ⚠️ 口径要准(08-18 上生产时实测纠正):`ls 数据根` **看得见别的项目的
135
+ // 目录名**(沙盒里实测列出了另一个 pid),拦住的是**进去读**——
136
+ // `cat <别人的>/board.json` 与 `ls <别人的>/` 都空。
137
+ // 原注释写的"连 ls 数据根都只看得见自己那一个条目"过强,是错的;
138
+ // platform.js 那边的说法才对:「目录级 denyRead 拦得住 cat,拦不住 ls
139
+ // 看文件名 —— 文件名不是秘密,接受」。
140
+ // agentTmpRoot(/tmp/nd)同 dataRoot 一个待遇:根整个遮住、自己的子目录
141
+ // 开天窗 —— 不遮的话 /tmp/nd/<别的项目>/ 就是跨项目读通道
142
+ denyRead: [...platform.credentialBlacklist(), dataRoot, ...(agentTmpRoot ? [agentTmpRoot] : [])],
143
+ allowRead: [cwdRoot, npmCacheDir, ...(agentTmpDir ? [agentTmpDir] : [])],
144
+ },
145
+ credentials: {
146
+ // filesystem 那层拦得住 `cat .env`,拦不住 `env` —— 服务端 process.env
147
+ // 原样继承给了 Bash 子进程,key 就明晃晃躺着。按名字在沙盒内 unset。
148
+ envVars: platform.secretEnvVarNames(env).map(name => ({ name, mode: 'deny' })),
149
+ },
150
+ },
151
+ settings: {
152
+ permissions: { deny: platform.protectedPathRules({ dataRoot }) },
153
+ // auto 模式的分类器规则(只在开了 auto 时注入,省得白占 settings)。
154
+ // ⚠️ 按节替换不是追加 —— 为什么只覆盖 environment / hard_deny 两节,
155
+ // 见 auto-mode-rules.js 文件头。
156
+ ...(platform.autoModeEnabled ? { autoMode: autoModeSettings() } : {}),
157
+ },
158
+ };
159
+ }