@deepwhale/coding-agent 1.0.12 → 1.0.13

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 (162) hide show
  1. package/dist/agent/agent-compaction.d.ts +74 -0
  2. package/dist/agent/agent-compaction.d.ts.map +1 -0
  3. package/dist/agent/agent-compaction.js +145 -0
  4. package/dist/agent/agent-compaction.js.map +1 -0
  5. package/dist/agent/index.d.ts +16 -0
  6. package/dist/agent/index.d.ts.map +1 -0
  7. package/dist/agent/index.js +17 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/agent/session-adapter.d.ts +177 -0
  10. package/dist/agent/session-adapter.d.ts.map +1 -0
  11. package/dist/agent/session-adapter.js +365 -0
  12. package/dist/agent/session-adapter.js.map +1 -0
  13. package/dist/agent/tool-loop.d.ts +123 -0
  14. package/dist/agent/tool-loop.d.ts.map +1 -0
  15. package/dist/agent/tool-loop.js +436 -0
  16. package/dist/agent/tool-loop.js.map +1 -0
  17. package/dist/env/load-project-env.d.ts +40 -0
  18. package/dist/env/load-project-env.d.ts.map +1 -0
  19. package/dist/env/load-project-env.js +80 -0
  20. package/dist/env/load-project-env.js.map +1 -0
  21. package/dist/index.d.ts +31 -0
  22. package/dist/index.d.ts.map +1 -0
  23. package/dist/index.js +30 -0
  24. package/dist/index.js.map +1 -0
  25. package/dist/llm-factory.d.ts +50 -0
  26. package/dist/llm-factory.d.ts.map +1 -0
  27. package/dist/llm-factory.js +110 -0
  28. package/dist/llm-factory.js.map +1 -0
  29. package/dist/modes/index.d.ts +14 -0
  30. package/dist/modes/index.d.ts.map +1 -0
  31. package/dist/modes/index.js +14 -0
  32. package/dist/modes/index.js.map +1 -0
  33. package/dist/modes/print.d.ts +50 -0
  34. package/dist/modes/print.d.ts.map +1 -0
  35. package/dist/modes/print.js +236 -0
  36. package/dist/modes/print.js.map +1 -0
  37. package/dist/modes/rpc.d.ts +52 -0
  38. package/dist/modes/rpc.d.ts.map +1 -0
  39. package/dist/modes/rpc.js +316 -0
  40. package/dist/modes/rpc.js.map +1 -0
  41. package/dist/modes/tui.d.ts +107 -0
  42. package/dist/modes/tui.d.ts.map +1 -0
  43. package/dist/modes/tui.js +680 -0
  44. package/dist/modes/tui.js.map +1 -0
  45. package/dist/policy/args-digest.d.ts +13 -0
  46. package/dist/policy/args-digest.d.ts.map +1 -0
  47. package/dist/policy/args-digest.js +29 -0
  48. package/dist/policy/args-digest.js.map +1 -0
  49. package/dist/policy/chain.d.ts +19 -0
  50. package/dist/policy/chain.d.ts.map +1 -0
  51. package/dist/policy/chain.js +24 -0
  52. package/dist/policy/chain.js.map +1 -0
  53. package/dist/policy/index.d.ts +17 -0
  54. package/dist/policy/index.d.ts.map +1 -0
  55. package/dist/policy/index.js +16 -0
  56. package/dist/policy/index.js.map +1 -0
  57. package/dist/policy/sanitize-reason.d.ts +11 -0
  58. package/dist/policy/sanitize-reason.d.ts.map +1 -0
  59. package/dist/policy/sanitize-reason.js +24 -0
  60. package/dist/policy/sanitize-reason.js.map +1 -0
  61. package/dist/policy/static-rules.d.ts +32 -0
  62. package/dist/policy/static-rules.d.ts.map +1 -0
  63. package/dist/policy/static-rules.js +106 -0
  64. package/dist/policy/static-rules.js.map +1 -0
  65. package/dist/policy/types.d.ts +56 -0
  66. package/dist/policy/types.d.ts.map +1 -0
  67. package/dist/policy/types.js +13 -0
  68. package/dist/policy/types.js.map +1 -0
  69. package/dist/repl/repl-command-router.d.ts +78 -0
  70. package/dist/repl/repl-command-router.d.ts.map +1 -0
  71. package/dist/repl/repl-command-router.js +112 -0
  72. package/dist/repl/repl-command-router.js.map +1 -0
  73. package/dist/repl/repl-confirm.d.ts +49 -0
  74. package/dist/repl/repl-confirm.d.ts.map +1 -0
  75. package/dist/repl/repl-confirm.js +88 -0
  76. package/dist/repl/repl-confirm.js.map +1 -0
  77. package/dist/repl/repl-session.d.ts +79 -0
  78. package/dist/repl/repl-session.d.ts.map +1 -0
  79. package/dist/repl/repl-session.js +129 -0
  80. package/dist/repl/repl-session.js.map +1 -0
  81. package/dist/repl/repl-signal-coordinator.d.ts +74 -0
  82. package/dist/repl/repl-signal-coordinator.d.ts.map +1 -0
  83. package/dist/repl/repl-signal-coordinator.js +73 -0
  84. package/dist/repl/repl-signal-coordinator.js.map +1 -0
  85. package/dist/repl.d.ts +117 -0
  86. package/dist/repl.d.ts.map +1 -0
  87. package/dist/repl.js +626 -0
  88. package/dist/repl.js.map +1 -0
  89. package/dist/sandbox/docker-runner.d.ts +147 -0
  90. package/dist/sandbox/docker-runner.d.ts.map +1 -0
  91. package/dist/sandbox/docker-runner.js +426 -0
  92. package/dist/sandbox/docker-runner.js.map +1 -0
  93. package/dist/sandbox/env-gate.d.ts +28 -0
  94. package/dist/sandbox/env-gate.d.ts.map +1 -0
  95. package/dist/sandbox/env-gate.js +65 -0
  96. package/dist/sandbox/env-gate.js.map +1 -0
  97. package/dist/sandbox/local-runner.d.ts +29 -0
  98. package/dist/sandbox/local-runner.d.ts.map +1 -0
  99. package/dist/sandbox/local-runner.js +79 -0
  100. package/dist/sandbox/local-runner.js.map +1 -0
  101. package/dist/sandbox/types.d.ts +80 -0
  102. package/dist/sandbox/types.d.ts.map +1 -0
  103. package/dist/sandbox/types.js +25 -0
  104. package/dist/sandbox/types.js.map +1 -0
  105. package/dist/tools/bash.d.ts +35 -0
  106. package/dist/tools/bash.d.ts.map +1 -0
  107. package/dist/tools/bash.js +233 -0
  108. package/dist/tools/bash.js.map +1 -0
  109. package/dist/tools/edit-file.d.ts +22 -0
  110. package/dist/tools/edit-file.d.ts.map +1 -0
  111. package/dist/tools/edit-file.js +79 -0
  112. package/dist/tools/edit-file.js.map +1 -0
  113. package/dist/tools/find.d.ts +21 -0
  114. package/dist/tools/find.d.ts.map +1 -0
  115. package/dist/tools/find.js +168 -0
  116. package/dist/tools/find.js.map +1 -0
  117. package/dist/tools/grep.d.ts +19 -0
  118. package/dist/tools/grep.d.ts.map +1 -0
  119. package/dist/tools/grep.js +170 -0
  120. package/dist/tools/grep.js.map +1 -0
  121. package/dist/tools/index.d.ts +10 -0
  122. package/dist/tools/index.d.ts.map +1 -0
  123. package/dist/tools/index.js +10 -0
  124. package/dist/tools/index.js.map +1 -0
  125. package/dist/tools/read-file.d.ts +18 -0
  126. package/dist/tools/read-file.d.ts.map +1 -0
  127. package/dist/tools/read-file.js +52 -0
  128. package/dist/tools/read-file.js.map +1 -0
  129. package/dist/tools/registry.d.ts +39 -0
  130. package/dist/tools/registry.d.ts.map +1 -0
  131. package/dist/tools/registry.js +67 -0
  132. package/dist/tools/registry.js.map +1 -0
  133. package/dist/tools/write-file.d.ts +18 -0
  134. package/dist/tools/write-file.d.ts.map +1 -0
  135. package/dist/tools/write-file.js +47 -0
  136. package/dist/tools/write-file.js.map +1 -0
  137. package/dist/tui-ink-bundle.js +38587 -0
  138. package/dist/types.d.ts +89 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +5 -0
  141. package/dist/types.js.map +1 -0
  142. package/dist/util/index.d.ts +16 -0
  143. package/dist/util/index.d.ts.map +1 -0
  144. package/dist/util/index.js +16 -0
  145. package/dist/util/index.js.map +1 -0
  146. package/dist/util/tui-history.d.ts +37 -0
  147. package/dist/util/tui-history.d.ts.map +1 -0
  148. package/dist/util/tui-history.js +93 -0
  149. package/dist/util/tui-history.js.map +1 -0
  150. package/dist/verify/format-report.d.ts +57 -0
  151. package/dist/verify/format-report.d.ts.map +1 -0
  152. package/dist/verify/format-report.js +128 -0
  153. package/dist/verify/format-report.js.map +1 -0
  154. package/dist/verify/index.d.ts +8 -0
  155. package/dist/verify/index.d.ts.map +1 -0
  156. package/dist/verify/index.js +8 -0
  157. package/dist/verify/index.js.map +1 -0
  158. package/dist/verify/verify-runner.d.ts +186 -0
  159. package/dist/verify/verify-runner.d.ts.map +1 -0
  160. package/dist/verify/verify-runner.js +707 -0
  161. package/dist/verify/verify-runner.js.map +1 -0
  162. package/package.json +1 -1
@@ -0,0 +1,147 @@
1
+ /**
2
+ * DockerSandboxRunner — 通过 `docker run --rm` 隔离执行 BashTool 命令
3
+ *
4
+ * Sprint 1c-revive-3-D-12 (2026-06-05): MVP 隔离, **不**等于完整 sandbox.
5
+ * .hermes/plans/d12/D12-PLAN.md 写清威胁模型 + 已知风险.
6
+ *
7
+ * 安全红线 (实现 + 自查都覆盖):
8
+ * - args 用数组传给 execFile, **不** 拼 shell 字符串
9
+ * - **不** 加 --privileged (grep 自查)
10
+ * - **不** 挂宿主根目录 (--volume /:/host 之类)
11
+ * - **不** 传 --env-file, **不** 传 DEEPSEEK_API_KEY / ANTHROPIC_AUTH_TOKEN
12
+ * - 容器名加 random suffix (8 字符) 避免冲突
13
+ * - workspace mount 用 --volume ${resolvedCwd}:/workspace:rw
14
+ * - 默认 --network=none (DEEPWHALE_DOCKER_NETWORK=bridge 显式允许)
15
+ * - timeout 走 docker stop 5s grace, 然后 docker kill 兜底
16
+ * - cleanup 失败进 result.warning, 不静默假成功
17
+ *
18
+ * MVP 边界: 这是执行环境抽象, **不是**:
19
+ * - 完整 policy language (Sprint D-15)
20
+ * - 完整 seccomp / apparmor profile (用 Docker default)
21
+ * - 远程容器 (本地 docker socket)
22
+ * - 跨平台验证 (Linux 本机; Docker Desktop on Mac/Win 不在 D-12 范围)
23
+ */
24
+ import type { SandboxRunRequest, SandboxRunResult, SandboxRunner } from './types.js';
25
+ /** 上限 10 分钟, BashTool 60s 但 docker 模式可放宽. */
26
+ export declare const DOCKER_DEFAULT_TIMEOUT_MS = 60000;
27
+ /**
28
+ * Sprint 1c-revive-3-D-12 review 修复 (2026-06-05, 基于 9348650 review).
29
+ *
30
+ * 黑名单: 这些 key **必须** 不传给 docker CLI 子进程. D-7 `.env` loader 会把
31
+ * API key 放进 `process.env`; 默认 `env: process.env` 透传给 docker CLI 时,
32
+ * docker CLI 自身能 `env | grep KEY` dump 出来, 也可能透传到 `docker run --env`
33
+ * 启动的容器 (MVP 没用 --env, 但 docker CLI 内部行为 / 错误日志 / 未来扩展都有
34
+ * 风险).
35
+ *
36
+ * 范围拍板: D-12 review 只列了 deepseek + anthropic + "等". 保守走 deepseek/
37
+ * anthropic + session key (README L159 拍板的 at-rest encryption key), 其他
38
+ * 第三方 API key 风险不在 D-12 范围. 后续 sprint 可扩.
39
+ *
40
+ * 模式: 黑名单 (比白名单稳 — 加新 deny 项立即生效, 不需要遍历允许列表).
41
+ */
42
+ export declare const DOCKER_CLI_DENY_KEYS: ReadonlySet<string>;
43
+ /**
44
+ * Sprint 1c-revive-3-D-12 review 修复 (2026-06-05, 基于 9348650 review).
45
+ *
46
+ * 给 docker CLI 子进程构造最小 env. docker CLI 自身需要的:
47
+ * - PATH (Linux/macOS 找其他 binary; 这里是子进程启动新 docker 子调用时用)
48
+ * - HOME (读 ~/.docker/config.json, docker config 凭据加载)
49
+ * - USERPROFILE (Windows 走这个替代 HOME)
50
+ * - DOCKER_HOST (连远程 docker daemon, e.g. ssh:// / tcp://)
51
+ * - DOCKER_CONFIG (config 路径, 跟 HOME/config 区分时)
52
+ * - DOCKER_TLS_VERIFY (TLS 开关)
53
+ * - DOCKER_TLS_CERTPATH (TLS 凭据路径)
54
+ *
55
+ * 其他 host 进程 env (TZ / LANG / 等) 通过 DOCKER_CLI_ALLOW_KEYS 白名单兜底,
56
+ * 默认**不**透传. 跟 process.env 的差别显式记录, 避免 review 时再撞"env 注入
57
+ * 未知 key" 的问题.
58
+ */
59
+ export declare const DOCKER_CLI_ALLOW_KEYS: ReadonlySet<string>;
60
+ /**
61
+ * 构造给 docker CLI 子进程的 env: DOCKER_CLI_ALLOW_KEYS 交集 process.env
62
+ * + 跳过 DOCKER_CLI_DENY_KEYS (黑名单优先, 即便用户在 allow set 里也跳过).
63
+ * 返回**新对象**, 不影响 process.env.
64
+ */
65
+ export declare function makeDockerCliEnv(env?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
66
+ export interface DockerSandboxOptions {
67
+ /** 容器镜像. 默认 'node:22-alpine'. */
68
+ readonly image?: string;
69
+ /** 沙箱根目录. BashTool ctor 拿 process.cwd() 注入. */
70
+ readonly sandboxRoot: string;
71
+ /** 'none' (默认, 禁网) / 'bridge' (允许 docker 默认 bridge). */
72
+ readonly network?: 'none' | 'bridge';
73
+ /** 默认 60_000. clamp 上限 10 分钟. */
74
+ readonly defaultTimeoutMs?: number;
75
+ /**
76
+ * Sprint 1c-revive-4-D-20.1 P0-F (2026-06-05): 容器内存硬上限.
77
+ * 默认 '512m' (拍板: 防 OOM 把宿主打挂). docker CLI 接受字符串 ('512m'/'1g'/'2048m' 等).
78
+ * env override: DEEPWHALE_DOCKER_MEMORY. 解析失败 docker 自身会 stderr, 走 result.warning.
79
+ */
80
+ readonly memory?: string;
81
+ /**
82
+ * Sprint 1c-revive-4-D-20.1 P0-F: 容器 CPU 配额.
83
+ * 默认 '1.0' (1 核). docker 接受小数 ('0.5' / '2.0' / 等).
84
+ * env override: DEEPWHALE_DOCKER_CPUS.
85
+ */
86
+ readonly cpus?: string;
87
+ /**
88
+ * Sprint 1c-revive-4-D-20.1 P0-F: 容器进程数硬上限.
89
+ * 默认 '256' (防 fork 炸弹). docker 接受整数字符串.
90
+ * env override: DEEPWHALE_DOCKER_PIDS_LIMIT.
91
+ */
92
+ readonly pidsLimit?: string;
93
+ }
94
+ /**
95
+ * DockerSandboxRunner — opt-in sandbox, 通过 `docker run` 隔离命令.
96
+ *
97
+ * 关键: 所有 docker 子命令都用 execFile 传数组, **不** 拼字符串.
98
+ */
99
+ export declare class DockerSandboxRunner implements SandboxRunner {
100
+ readonly kind: "docker";
101
+ private readonly image;
102
+ private readonly sandboxRoot;
103
+ private readonly network;
104
+ private readonly defaultTimeoutMs;
105
+ /** Sprint 1c-revive-4-D-20.1 P0-F: 资源限制字段. env override 走 env-gate. */
106
+ private readonly memory;
107
+ private readonly cpus;
108
+ private readonly pidsLimit;
109
+ /**
110
+ * Sprint 1c-revive-3-D-12 review P2 修复 (2026-06-05): 每个 runner 实例
111
+ * 唯一 runId. 容器打两个 label: `deepwhale.sandbox=true` (粗筛) +
112
+ * `deepwhale.sandbox.run_id=<runId>` (精筛). cleanup() 只删**自己** runId
113
+ * 的容器, 避免并发 runner 时一个 cleanup 误删另一个的容器.
114
+ */
115
+ readonly runId: string;
116
+ constructor(opts: DockerSandboxOptions);
117
+ /**
118
+ * 跑命令. 不抛异常 — 失败 / timeout / docker 不存在都在 result 里.
119
+ *
120
+ * docker run → 在容器里跑命令. timeout 通过 docker stop 触发.
121
+ * 实现拆 3 步: buildArgs → spawn → 收集 stdout/stderr/exit.
122
+ * 不在沙箱里, 行为是 Node 进程, timeout 走 SIGKILL child of docker CLI.
123
+ */
124
+ run(req: SandboxRunRequest): Promise<SandboxRunResult>;
125
+ /**
126
+ * 构建 docker run args. **核心安全边界** — 全部用数组, 任何 caller 都无法通过
127
+ * 输入污染 args (args 里的空格/--xxx 都被当作字面量).
128
+ */
129
+ buildDockerArgs(containerName: string, workspaceAbs: string, req: SandboxRunRequest): string[];
130
+ /** cwd 是否在 sandboxRoot 内. */
131
+ private isInsideSandbox;
132
+ /** 停容器 (SIGTERM), grace ms 内不退就当失败. */
133
+ private stopContainer;
134
+ /** 强杀容器 (SIGKILL). 失败不抛. */
135
+ private killContainer;
136
+ /**
137
+ * 主动清理 — 给 BashTool 退出时调. docker run --rm 模式理论上不需要, 但如果
138
+ * 容器因 timeout 残留 (kill 失败) 就在这里兜底.
139
+ * **不抛异常**, 错误进 stderr warning.
140
+ *
141
+ * Sprint 1c-revive-3-D-12 review P2 修复 (2026-06-05): filter 同时**精筛**
142
+ * runId label, 只删本 runner 的容器. 之前只用 `deepwhale.sandbox=true` 粗筛,
143
+ * 并发两个 runner 时一个 cleanup 可能误删另一个 runner 的容器 — 跨实例污染.
144
+ */
145
+ cleanup(): Promise<void>;
146
+ }
147
+ //# sourceMappingURL=docker-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-runner.d.ts","sourceRoot":"","sources":["../../src/sandbox/docker-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAOH,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKrF,6CAA6C;AAC7C,eAAO,MAAM,yBAAyB,QAAS,CAAC;AAgBhD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAInD,CAAC;AAEH;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,qBAAqB,EAAE,WAAW,CAAC,MAAM,CAcpD,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAAC,UAAU,CASxF;AAED,MAAM,WAAW,oBAAoB;IACnC,iCAAiC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACrC,iCAAiC;IACjC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAWD;;;;GAIG;AACH,qBAAa,mBAAoB,YAAW,aAAa;IACvD,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,uEAAuE;IACvE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC;;;;;OAKG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,IAAI,EAAE,oBAAoB;IAkBtC;;;;;;OAMG;IACG,GAAG,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAuI5D;;;OAGG;IACH,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,iBAAiB,GAAG,MAAM,EAAE;IA6C9F,6BAA6B;IAC7B,OAAO,CAAC,eAAe;IAQvB,uCAAuC;YACzB,aAAa;IAmB3B,4BAA4B;YACd,aAAa;IAY3B;;;;;;;;OAQG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAuC/B"}
@@ -0,0 +1,426 @@
1
+ /**
2
+ * DockerSandboxRunner — 通过 `docker run --rm` 隔离执行 BashTool 命令
3
+ *
4
+ * Sprint 1c-revive-3-D-12 (2026-06-05): MVP 隔离, **不**等于完整 sandbox.
5
+ * .hermes/plans/d12/D12-PLAN.md 写清威胁模型 + 已知风险.
6
+ *
7
+ * 安全红线 (实现 + 自查都覆盖):
8
+ * - args 用数组传给 execFile, **不** 拼 shell 字符串
9
+ * - **不** 加 --privileged (grep 自查)
10
+ * - **不** 挂宿主根目录 (--volume /:/host 之类)
11
+ * - **不** 传 --env-file, **不** 传 DEEPSEEK_API_KEY / ANTHROPIC_AUTH_TOKEN
12
+ * - 容器名加 random suffix (8 字符) 避免冲突
13
+ * - workspace mount 用 --volume ${resolvedCwd}:/workspace:rw
14
+ * - 默认 --network=none (DEEPWHALE_DOCKER_NETWORK=bridge 显式允许)
15
+ * - timeout 走 docker stop 5s grace, 然后 docker kill 兜底
16
+ * - cleanup 失败进 result.warning, 不静默假成功
17
+ *
18
+ * MVP 边界: 这是执行环境抽象, **不是**:
19
+ * - 完整 policy language (Sprint D-15)
20
+ * - 完整 seccomp / apparmor profile (用 Docker default)
21
+ * - 远程容器 (本地 docker socket)
22
+ * - 跨平台验证 (Linux 本机; Docker Desktop on Mac/Win 不在 D-12 范围)
23
+ */
24
+ import { execFile, spawn } from 'node:child_process';
25
+ import { randomUUID } from 'node:crypto';
26
+ import { resolve as pathResolve, sep as pathSep } from 'node:path';
27
+ import { promisify } from 'node:util';
28
+ import process from 'node:process';
29
+ const execFileP = promisify(execFile);
30
+ const DEFAULT_IMAGE = 'node:22-alpine';
31
+ /** 上限 10 分钟, BashTool 60s 但 docker 模式可放宽. */
32
+ export const DOCKER_DEFAULT_TIMEOUT_MS = 60_000;
33
+ /** Docker 容器 stop grace 5s, 之后 SIGKILL. */
34
+ const _STOP_GRACE_MS_UNUSED = 5_000;
35
+ /** sandboxRoot 内的相对路径映射到容器 /workspace. */
36
+ const CONTAINER_WORKDIR = '/workspace';
37
+ /** stdout/stderr 末尾 cap 跟 LocalSandboxRunner 一致. */
38
+ const DEFAULT_STDOUT_CAP = 4 * 1024;
39
+ /** 10MB hard ceiling, 跟 LocalSandboxRunner 一致. */
40
+ const _MAX_BUFFER_UNUSED = 10 * 1024 * 1024;
41
+ // Sprint 1c-revive-4-D-20.1 P0-F (2026-06-05): 容器资源限制默认值 (拍板红线).
42
+ // 防止 fork 炸弹 / OOM / CPU 100% 把宿主打挂. env override 走 env-gate.
43
+ const DEFAULT_MEMORY = '512m';
44
+ const DEFAULT_CPUS = '1.0';
45
+ const DEFAULT_PIDS_LIMIT = '256';
46
+ /**
47
+ * Sprint 1c-revive-3-D-12 review 修复 (2026-06-05, 基于 9348650 review).
48
+ *
49
+ * 黑名单: 这些 key **必须** 不传给 docker CLI 子进程. D-7 `.env` loader 会把
50
+ * API key 放进 `process.env`; 默认 `env: process.env` 透传给 docker CLI 时,
51
+ * docker CLI 自身能 `env | grep KEY` dump 出来, 也可能透传到 `docker run --env`
52
+ * 启动的容器 (MVP 没用 --env, 但 docker CLI 内部行为 / 错误日志 / 未来扩展都有
53
+ * 风险).
54
+ *
55
+ * 范围拍板: D-12 review 只列了 deepseek + anthropic + "等". 保守走 deepseek/
56
+ * anthropic + session key (README L159 拍板的 at-rest encryption key), 其他
57
+ * 第三方 API key 风险不在 D-12 范围. 后续 sprint 可扩.
58
+ *
59
+ * 模式: 黑名单 (比白名单稳 — 加新 deny 项立即生效, 不需要遍历允许列表).
60
+ */
61
+ export const DOCKER_CLI_DENY_KEYS = new Set([
62
+ 'DEEPSEEK_API_KEY',
63
+ 'ANTHROPIC_AUTH_TOKEN',
64
+ 'DEEPWHALE_SESSION_KEY',
65
+ ]);
66
+ /**
67
+ * Sprint 1c-revive-3-D-12 review 修复 (2026-06-05, 基于 9348650 review).
68
+ *
69
+ * 给 docker CLI 子进程构造最小 env. docker CLI 自身需要的:
70
+ * - PATH (Linux/macOS 找其他 binary; 这里是子进程启动新 docker 子调用时用)
71
+ * - HOME (读 ~/.docker/config.json, docker config 凭据加载)
72
+ * - USERPROFILE (Windows 走这个替代 HOME)
73
+ * - DOCKER_HOST (连远程 docker daemon, e.g. ssh:// / tcp://)
74
+ * - DOCKER_CONFIG (config 路径, 跟 HOME/config 区分时)
75
+ * - DOCKER_TLS_VERIFY (TLS 开关)
76
+ * - DOCKER_TLS_CERTPATH (TLS 凭据路径)
77
+ *
78
+ * 其他 host 进程 env (TZ / LANG / 等) 通过 DOCKER_CLI_ALLOW_KEYS 白名单兜底,
79
+ * 默认**不**透传. 跟 process.env 的差别显式记录, 避免 review 时再撞"env 注入
80
+ * 未知 key" 的问题.
81
+ */
82
+ export const DOCKER_CLI_ALLOW_KEYS = new Set([
83
+ 'PATH',
84
+ // Sprint 1c-revive-3-D-12 review chain 关单 (2026-06-05, 基于 dfe9d9a review):
85
+ // Windows / PowerShell 用户的 process.env 习惯用 'Path' (跟 cmd.exe / PowerShell
86
+ // 一致), Unix 用 'PATH'. Node 透传时**不**归一化大小写, 只 allow 单一大小写会
87
+ // 漏 Windows. 两个都列, 跟其他 DOCKER_* key 风格一致; case-insensitive 归一化
88
+ // 跨平台有"同 key 不同大小写同时存在被覆盖" 的不可见副作用, 不引入.
89
+ 'Path',
90
+ 'HOME',
91
+ 'USERPROFILE',
92
+ 'DOCKER_HOST',
93
+ 'DOCKER_CONFIG',
94
+ 'DOCKER_TLS_VERIFY',
95
+ 'DOCKER_TLS_CERTPATH',
96
+ ]);
97
+ /**
98
+ * 构造给 docker CLI 子进程的 env: DOCKER_CLI_ALLOW_KEYS 交集 process.env
99
+ * + 跳过 DOCKER_CLI_DENY_KEYS (黑名单优先, 即便用户在 allow set 里也跳过).
100
+ * 返回**新对象**, 不影响 process.env.
101
+ */
102
+ export function makeDockerCliEnv(env = process.env) {
103
+ const result = {};
104
+ for (const key of DOCKER_CLI_ALLOW_KEYS) {
105
+ const value = env[key];
106
+ if (value === undefined)
107
+ continue;
108
+ if (DOCKER_CLI_DENY_KEYS.has(key))
109
+ continue; // 防御: 黑名单优先
110
+ result[key] = value;
111
+ }
112
+ return result;
113
+ }
114
+ /**
115
+ * 容器的 stdout/stderr cap. 用 Buffer.from 包一层, ts 5.x 严格模式抓得到
116
+ * Buffer<ArrayBufferLike> 跟 Buffer<ArrayBuffer> 的差别.
117
+ */
118
+ function capTail(buf, cap) {
119
+ if (buf.length <= cap)
120
+ return buf;
121
+ return Buffer.from(buf.subarray(buf.length - cap));
122
+ }
123
+ /**
124
+ * DockerSandboxRunner — opt-in sandbox, 通过 `docker run` 隔离命令.
125
+ *
126
+ * 关键: 所有 docker 子命令都用 execFile 传数组, **不** 拼字符串.
127
+ */
128
+ export class DockerSandboxRunner {
129
+ kind = 'docker';
130
+ image;
131
+ sandboxRoot;
132
+ network;
133
+ defaultTimeoutMs;
134
+ /** Sprint 1c-revive-4-D-20.1 P0-F: 资源限制字段. env override 走 env-gate. */
135
+ memory;
136
+ cpus;
137
+ pidsLimit;
138
+ /**
139
+ * Sprint 1c-revive-3-D-12 review P2 修复 (2026-06-05): 每个 runner 实例
140
+ * 唯一 runId. 容器打两个 label: `deepwhale.sandbox=true` (粗筛) +
141
+ * `deepwhale.sandbox.run_id=<runId>` (精筛). cleanup() 只删**自己** runId
142
+ * 的容器, 避免并发 runner 时一个 cleanup 误删另一个的容器.
143
+ */
144
+ runId;
145
+ constructor(opts) {
146
+ this.image = opts.image ?? DEFAULT_IMAGE;
147
+ // Sprint 1c-revive-5-D-20.6.1 review-fix (2026-06-06): 规范化 sandboxRoot
148
+ // 到绝对路径, 避免 Windows 端 caller 传 '/tmp/sbx-test' (POSIX 风格)
149
+ // 跟 pathResolve(req.cwd) 落到 Windows 盘符 (e.g. C:\Users\xxx) 不在同空间
150
+ // → isInsideSandbox early return → mock child 不创建, 测试 fail.
151
+ // 修法: 构造时 pathResolve(opts.sandboxRoot), 绝对路径原样, 相对路径
152
+ // 也变成绝对 (跟 process.cwd() 解). 兼容 caller 写 '/tmp/sbx-test' /
153
+ // 'C:\xxx' / 'C:/xxx' / './sbx' 多种形态.
154
+ this.sandboxRoot = pathResolve(opts.sandboxRoot);
155
+ this.network = opts.network ?? 'none';
156
+ this.defaultTimeoutMs = opts.defaultTimeoutMs ?? DOCKER_DEFAULT_TIMEOUT_MS;
157
+ this.memory = opts.memory ?? DEFAULT_MEMORY;
158
+ this.cpus = opts.cpus ?? DEFAULT_CPUS;
159
+ this.pidsLimit = opts.pidsLimit ?? DEFAULT_PIDS_LIMIT;
160
+ this.runId = randomUUID().slice(0, 8);
161
+ }
162
+ /**
163
+ * 跑命令. 不抛异常 — 失败 / timeout / docker 不存在都在 result 里.
164
+ *
165
+ * docker run → 在容器里跑命令. timeout 通过 docker stop 触发.
166
+ * 实现拆 3 步: buildArgs → spawn → 收集 stdout/stderr/exit.
167
+ * 不在沙箱里, 行为是 Node 进程, timeout 走 SIGKILL child of docker CLI.
168
+ */
169
+ async run(req) {
170
+ const start = Date.now();
171
+ const cap = req.stdoutCapBytes || DEFAULT_STDOUT_CAP;
172
+ const timeoutMs = req.timeoutMs || this.defaultTimeoutMs;
173
+ // clamp timeout 上限 10 分钟
174
+ const clampedTimeout = Math.min(Math.max(timeoutMs, 1_000), 10 * 60_000);
175
+ const containerName = `deepwhale-sbx-${randomUUID().slice(0, 8)}`;
176
+ const workspaceAbs = pathResolve(req.cwd);
177
+ // 校验 cwd 在 sandboxRoot 内 (防止 mount escape: bash 工具说在 /workspace
178
+ // 但实际 pathResolve 跳出 sandboxRoot 之后 docker -v 挂载到宿主别处)
179
+ if (!this.isInsideSandbox(workspaceAbs)) {
180
+ return {
181
+ ok: false,
182
+ exitCode: null,
183
+ stdoutTail: '',
184
+ stderrTail: '',
185
+ durationMs: Date.now() - start,
186
+ warning: `cwd '${workspaceAbs}' is outside sandbox root '${this.sandboxRoot}' — refusing to mount`,
187
+ };
188
+ }
189
+ const dockerArgs = this.buildDockerArgs(containerName, workspaceAbs, req);
190
+ return new Promise((resolve) => {
191
+ let stdoutBuf = Buffer.alloc(0);
192
+ let stderrBuf = Buffer.alloc(0);
193
+ let killTimer = null;
194
+ let sigkillTimer = null;
195
+ let resolved = false;
196
+ const finalize = (r) => {
197
+ if (resolved)
198
+ return;
199
+ resolved = true;
200
+ if (killTimer)
201
+ clearTimeout(killTimer);
202
+ if (sigkillTimer)
203
+ clearTimeout(sigkillTimer);
204
+ resolve({ ...r, durationMs: Date.now() - start });
205
+ };
206
+ const child = spawn('docker', dockerArgs, {
207
+ cwd: this.sandboxRoot,
208
+ stdio: ['ignore', 'pipe', 'pipe'],
209
+ // Sprint 1c-revive-3-D-12 review 修复 (2026-06-05, 基于 9348650 review):
210
+ // 之前 `env: process.env` 透传, D-7 `.env` loader 注入的 API key 会
211
+ // 进 docker CLI 子进程 (可能被 docker 内部 dump 到诊断日志 / 未来
212
+ // 扩展透传到容器). 修法: makeDockerCliEnv() 黑名单 + 白名单, 默认
213
+ // 只透传 docker CLI 必需的 7 个 key, 显式剔除 API key.
214
+ env: makeDockerCliEnv(),
215
+ // 注: 这里不传 shell: true (默认 false), 数组 args 安全
216
+ });
217
+ child.stdout?.on('data', (chunk) => {
218
+ stdoutBuf = capTail(Buffer.concat([stdoutBuf, chunk]), cap);
219
+ });
220
+ child.stderr?.on('data', (chunk) => {
221
+ stderrBuf = capTail(Buffer.concat([stderrBuf, chunk]), cap);
222
+ });
223
+ // timeout 触发: 走 docker stop (SIGTERM + 5s grace) → docker kill (SIGKILL)
224
+ killTimer = setTimeout(() => {
225
+ // docker stop 给容器 5s grace, 容器跑不到就 docker kill
226
+ void this.stopContainer(containerName, 5_000)
227
+ .then((stopOk) => {
228
+ if (stopOk) {
229
+ finalize({
230
+ ok: false,
231
+ exitCode: null,
232
+ stdoutTail: stdoutBuf.toString('utf8'),
233
+ stderrTail: stderrBuf.toString('utf8'),
234
+ signal: 'SIGTERM',
235
+ });
236
+ }
237
+ else {
238
+ // stop 失败, 兜底 kill
239
+ void this.killContainer(containerName).finally(() => {
240
+ finalize({
241
+ ok: false,
242
+ exitCode: null,
243
+ stdoutTail: stdoutBuf.toString('utf8'),
244
+ stderrTail: stderrBuf.toString('utf8'),
245
+ signal: 'SIGKILL',
246
+ warning: 'docker stop failed, force-killed via docker kill',
247
+ });
248
+ });
249
+ }
250
+ })
251
+ .catch((err) => {
252
+ finalize({
253
+ ok: false,
254
+ exitCode: null,
255
+ stdoutTail: stdoutBuf.toString('utf8'),
256
+ stderrTail: stderrBuf.toString('utf8'),
257
+ signal: 'SIGKILL',
258
+ warning: `docker stop error: ${err instanceof Error ? err.message : String(err)}`,
259
+ });
260
+ });
261
+ }, clampedTimeout);
262
+ // 上面的 finalize 内部已清 killTimer, 但 sigkill 兜底场景下我们没注册 sigkillTimer
263
+ // 实际 SIGTERM 失败转 SIGKILL 已经在上面, sigkillTimer 字段是预留, 这里清空
264
+ sigkillTimer = null;
265
+ child.on('error', (err) => {
266
+ // spawn 失败 (docker 不在 PATH / docker daemon 死)
267
+ finalize({
268
+ ok: false,
269
+ exitCode: null,
270
+ stdoutTail: stdoutBuf.toString('utf8'),
271
+ stderrTail: stderrBuf.toString('utf8'),
272
+ warning: `docker spawn failed: ${err.message}. Is docker installed and running?`,
273
+ });
274
+ });
275
+ child.on('close', (code, signal) => {
276
+ // docker CLI 自己的 exit code: 0 = 容器跑完 + 退出 0
277
+ // 非 0 = 容器命令失败 / docker 出错
278
+ // signal = docker CLI 自己被 kill (这里只有 child.kill 时, 实际不走这里)
279
+ if (signal === 'SIGTERM' || signal === 'SIGKILL') {
280
+ finalize({
281
+ ok: false,
282
+ exitCode: null,
283
+ stdoutTail: stdoutBuf.toString('utf8'),
284
+ stderrTail: stderrBuf.toString('utf8'),
285
+ signal: signal === 'SIGTERM' ? 'SIGTERM' : 'SIGKILL',
286
+ });
287
+ return;
288
+ }
289
+ finalize({
290
+ ok: code === 0,
291
+ exitCode: code,
292
+ stdoutTail: stdoutBuf.toString('utf8'),
293
+ stderrTail: stderrBuf.toString('utf8'),
294
+ });
295
+ });
296
+ });
297
+ }
298
+ /**
299
+ * 构建 docker run args. **核心安全边界** — 全部用数组, 任何 caller 都无法通过
300
+ * 输入污染 args (args 里的空格/--xxx 都被当作字面量).
301
+ */
302
+ buildDockerArgs(containerName, workspaceAbs, req) {
303
+ const args = [
304
+ 'run',
305
+ '--rm', // 跑完自动删容器
306
+ '--label',
307
+ 'deepwhale.sandbox=true',
308
+ // Sprint 1c-revive-3-D-12 review P2 修复: 跟 runId 配对的精筛 label.
309
+ // cleanup() 用它只删本 runner 的容器, 并发安全.
310
+ '--label',
311
+ `deepwhale.sandbox.run_id=${this.runId}`,
312
+ '--name',
313
+ containerName,
314
+ '--user',
315
+ '1000:1000', // 非 root
316
+ '--read-only', // 容器 fs 只读, 写只走 /workspace + /tmp
317
+ '--cap-drop=ALL', // 丢弃所有 capabilities
318
+ '--security-opt',
319
+ 'no-new-privileges', // 防 setuid 提权
320
+ '--network',
321
+ this.network, // 默认 none 禁网
322
+ // Sprint 1c-revive-4-D-20.1 P0-F: 资源限制, 防 fork 炸弹 / OOM / CPU 100%
323
+ // 拍板默认值: memory=512m / cpus=1.0 / pids-limit=256, env override 走 env-gate.
324
+ '--memory',
325
+ this.memory,
326
+ '--cpus',
327
+ this.cpus,
328
+ '--pids-limit',
329
+ this.pidsLimit,
330
+ '-v',
331
+ `${workspaceAbs}:${CONTAINER_WORKDIR}:rw`, // 显式 workspace bind mount
332
+ '-w',
333
+ CONTAINER_WORKDIR, // 容器内工作目录
334
+ '--tmpfs',
335
+ '/tmp:size=64m,noexec,nosuid', // 临时目录禁执行
336
+ // **不** 加 --privileged (grep 自查红线)
337
+ // **不** 加 --env-file, **不** 传 .env / API key
338
+ // **不** 加 --volume /:/host (禁止宿主根 mount)
339
+ this.image,
340
+ // 容器里要执行的命令
341
+ req.command,
342
+ ...req.args,
343
+ ];
344
+ return args;
345
+ }
346
+ /** cwd 是否在 sandboxRoot 内. */
347
+ isInsideSandbox(absoluteCwd) {
348
+ if (absoluteCwd === this.sandboxRoot)
349
+ return true;
350
+ const rootWithSep = this.sandboxRoot.endsWith(pathSep)
351
+ ? this.sandboxRoot
352
+ : this.sandboxRoot + pathSep;
353
+ return absoluteCwd.startsWith(rootWithSep);
354
+ }
355
+ /** 停容器 (SIGTERM), grace ms 内不退就当失败. */
356
+ async stopContainer(name, graceMs) {
357
+ try {
358
+ const { stdout } = await execFileP('docker', ['stop', '--time', String(Math.ceil(graceMs / 1000)), name], {
359
+ timeout: graceMs + 2_000,
360
+ encoding: 'buffer',
361
+ maxBuffer: 1024 * 1024,
362
+ });
363
+ const out = stdout.toString('utf8').trim();
364
+ // docker stop 退 0 + stdout 含容器名 = 成功
365
+ return out.includes(name);
366
+ }
367
+ catch {
368
+ return false;
369
+ }
370
+ }
371
+ /** 强杀容器 (SIGKILL). 失败不抛. */
372
+ async killContainer(name) {
373
+ try {
374
+ await execFileP('docker', ['kill', name], {
375
+ timeout: 5_000,
376
+ encoding: 'buffer',
377
+ maxBuffer: 1024 * 1024,
378
+ });
379
+ }
380
+ catch {
381
+ // best-effort, 不抛
382
+ }
383
+ }
384
+ /**
385
+ * 主动清理 — 给 BashTool 退出时调. docker run --rm 模式理论上不需要, 但如果
386
+ * 容器因 timeout 残留 (kill 失败) 就在这里兜底.
387
+ * **不抛异常**, 错误进 stderr warning.
388
+ *
389
+ * Sprint 1c-revive-3-D-12 review P2 修复 (2026-06-05): filter 同时**精筛**
390
+ * runId label, 只删本 runner 的容器. 之前只用 `deepwhale.sandbox=true` 粗筛,
391
+ * 并发两个 runner 时一个 cleanup 可能误删另一个 runner 的容器 — 跨实例污染.
392
+ */
393
+ async cleanup() {
394
+ try {
395
+ // 找本 runner runId 的残留容器并删
396
+ const { stdout } = await execFileP('docker', [
397
+ 'ps',
398
+ '-aq',
399
+ '--filter',
400
+ 'label=deepwhale.sandbox=true',
401
+ '--filter',
402
+ `label=deepwhale.sandbox.run_id=${this.runId}`,
403
+ ], { encoding: 'buffer', maxBuffer: 1024 * 1024 });
404
+ const ids = stdout.toString('utf8').trim().split('\n').filter(Boolean);
405
+ if (ids.length === 0)
406
+ return;
407
+ // 逐个删, 失败不阻塞其他
408
+ const results = await Promise.allSettled(ids.map((id) => execFileP('docker', ['rm', '-f', id], { encoding: 'buffer', maxBuffer: 1024 * 1024 })));
409
+ const failures = results.filter((r) => r.status === 'rejected');
410
+ if (failures.length > 0) {
411
+ // warning 通过 console 报告, 不抛 (caller 拿不到 stderr)
412
+ // 注: 跟 result.warning 是不同通道 — cleanup() 是 no-return, 没法填 result.
413
+ // D-12 MVP 接受这个限制; 后续 sprint 加 sandbox-level logger.
414
+ const msgs = failures
415
+ .map((f) => f.reason)
416
+ .map((r) => (r instanceof Error ? r.message : String(r)))
417
+ .join('; ');
418
+ console.warn(`[sandbox] cleanup partial failure: ${msgs}`);
419
+ }
420
+ }
421
+ catch (err) {
422
+ console.warn(`[sandbox] cleanup error: ${err instanceof Error ? err.message : String(err)}`);
423
+ }
424
+ }
425
+ }
426
+ //# sourceMappingURL=docker-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker-runner.js","sourceRoot":"","sources":["../../src/sandbox/docker-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * env gate helper — 从环境变量解析 sandbox 配置
3
+ *
4
+ * Sprint 1c-revive-3-D-12 (2026-06-05). MVP 入口, 跟 BashTool 解耦, 后续可换 config.toml.
5
+ *
6
+ * 支持的 env:
7
+ * - DEEPWHALE_SANDBOX=local|docker — 选 runner. 缺省 = local (BashTool 现状行为)
8
+ * - DEEPWHALE_DOCKER_IMAGE — 容器镜像. 缺省 = 'node:22-alpine'
9
+ * - DEEPWHALE_DOCKER_NETWORK=none|bridge — 容器网络. 缺省 = 'none' 禁网
10
+ *
11
+ * 安全: 不传 DEEPSEEK_API_KEY / ANTHROPIC_AUTH_TOKEN 等敏感 env 到 docker 容器
12
+ * (DockerSandboxRunner 默认只透 process.env, **不** 注入 .env / API key).
13
+ */
14
+ import type { SandboxRunner } from './types.js';
15
+ export interface SandboxEnvConfig {
16
+ readonly sandboxRoot: string;
17
+ }
18
+ /**
19
+ * 解析 env → 选 sandbox runner. 缺省 = LocalSandboxRunner (BashTool 现状).
20
+ * env DEEPWHALE_SANDBOX=docker 时返 DockerSandboxRunner, 镜像/网络用对应 env.
21
+ *
22
+ * Sprint 1c-revive-3-D-12 review P1 修复 (2026-06-05): 严格 enum, 未知值 throw.
23
+ * 之前 `mode !== 'docker'` 全部 fallback local, 拼错 `dokcer` 之类会静默本地执行
24
+ * — fail-open, 安全红线. 修法: 只接受 unset / `local` / `docker`, 其他值抛错.
25
+ * 入口 (CLI / REPL 启动) 应当把 throw 转到 stderr + exit 1, 不静默.
26
+ */
27
+ export declare function resolveSandboxRunnerFromEnv(config: SandboxEnvConfig, env?: NodeJS.ProcessEnv): SandboxRunner;
28
+ //# sourceMappingURL=env-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-gate.d.ts","sourceRoot":"","sources":["../../src/sandbox/env-gate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAIhD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,gBAAgB,EACxB,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,aAAa,CAyCf"}