apex-code 0.0.6 → 0.1.1

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 (234) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +8 -9
  3. package/dist/cli/args.d.ts +0 -12
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +2 -32
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/cli.d.ts.map +1 -1
  8. package/dist/cli.js +4 -130
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +1 -1
  11. package/dist/config.d.ts.map +1 -1
  12. package/dist/config.js +1 -1
  13. package/dist/config.js.map +1 -1
  14. package/dist/core/agent-session-services.d.ts.map +1 -1
  15. package/dist/core/agent-session-services.js +0 -8
  16. package/dist/core/agent-session-services.js.map +1 -1
  17. package/dist/core/delegation/agents.d.ts.map +1 -1
  18. package/dist/core/delegation/agents.js +2 -1
  19. package/dist/core/delegation/agents.js.map +1 -1
  20. package/dist/core/delegation/runtime.d.ts +6 -14
  21. package/dist/core/delegation/runtime.d.ts.map +1 -1
  22. package/dist/core/delegation/runtime.js +1 -2
  23. package/dist/core/delegation/runtime.js.map +1 -1
  24. package/dist/core/extensions/loader.d.ts.map +1 -1
  25. package/dist/core/extensions/loader.js +3 -4
  26. package/dist/core/extensions/loader.js.map +1 -1
  27. package/dist/core/extensions/source-runtime.d.ts +4 -5
  28. package/dist/core/extensions/source-runtime.d.ts.map +1 -1
  29. package/dist/core/extensions/source-runtime.js +4 -5
  30. package/dist/core/extensions/source-runtime.js.map +1 -1
  31. package/dist/core/formatter-lifecycle.d.ts +2 -1
  32. package/dist/core/formatter-lifecycle.d.ts.map +1 -1
  33. package/dist/core/formatter-lifecycle.js +2 -1
  34. package/dist/core/formatter-lifecycle.js.map +1 -1
  35. package/dist/core/hooks/types.d.ts +2 -3
  36. package/dist/core/hooks/types.d.ts.map +1 -1
  37. package/dist/core/hooks/types.js +2 -3
  38. package/dist/core/hooks/types.js.map +1 -1
  39. package/dist/core/http-idle-timeout.d.ts +3 -5
  40. package/dist/core/http-idle-timeout.d.ts.map +1 -1
  41. package/dist/core/http-idle-timeout.js +3 -5
  42. package/dist/core/http-idle-timeout.js.map +1 -1
  43. package/dist/core/mcp/config.d.ts +18 -1
  44. package/dist/core/mcp/config.d.ts.map +1 -1
  45. package/dist/core/mcp/config.js +24 -7
  46. package/dist/core/mcp/config.js.map +1 -1
  47. package/dist/core/mcp/connector.d.ts +2 -3
  48. package/dist/core/mcp/connector.d.ts.map +1 -1
  49. package/dist/core/mcp/connector.js.map +1 -1
  50. package/dist/core/mcp/oauth/authorize.d.ts +2 -0
  51. package/dist/core/mcp/oauth/authorize.d.ts.map +1 -1
  52. package/dist/core/mcp/oauth/authorize.js +5 -2
  53. package/dist/core/mcp/oauth/authorize.js.map +1 -1
  54. package/dist/core/mcp/oauth/mcp-token.d.ts +4 -6
  55. package/dist/core/mcp/oauth/mcp-token.d.ts.map +1 -1
  56. package/dist/core/mcp/oauth/mcp-token.js +5 -7
  57. package/dist/core/mcp/oauth/mcp-token.js.map +1 -1
  58. package/dist/core/mcp/runtime.d.ts +1 -0
  59. package/dist/core/mcp/runtime.d.ts.map +1 -1
  60. package/dist/core/mcp/runtime.js +2 -1
  61. package/dist/core/mcp/runtime.js.map +1 -1
  62. package/dist/core/package-manager.d.ts.map +1 -1
  63. package/dist/core/package-manager.js +0 -15
  64. package/dist/core/package-manager.js.map +1 -1
  65. package/dist/core/permissions/store.d.ts +6 -11
  66. package/dist/core/permissions/store.d.ts.map +1 -1
  67. package/dist/core/permissions/store.js +23 -51
  68. package/dist/core/permissions/store.js.map +1 -1
  69. package/dist/core/project-resources.d.ts +62 -0
  70. package/dist/core/project-resources.d.ts.map +1 -0
  71. package/dist/core/project-resources.js +42 -0
  72. package/dist/core/project-resources.js.map +1 -0
  73. package/dist/core/sdk.d.ts +1 -9
  74. package/dist/core/sdk.d.ts.map +1 -1
  75. package/dist/core/sdk.js +10 -35
  76. package/dist/core/sdk.js.map +1 -1
  77. package/dist/core/session-share.d.ts +6 -8
  78. package/dist/core/session-share.d.ts.map +1 -1
  79. package/dist/core/session-share.js +9 -12
  80. package/dist/core/session-share.js.map +1 -1
  81. package/dist/core/settings-manager.d.ts +0 -21
  82. package/dist/core/settings-manager.d.ts.map +1 -1
  83. package/dist/core/settings-manager.js +4 -12
  84. package/dist/core/settings-manager.js.map +1 -1
  85. package/dist/core/tools/bash.d.ts.map +1 -1
  86. package/dist/core/tools/bash.js +0 -47
  87. package/dist/core/tools/bash.js.map +1 -1
  88. package/dist/core/tools/web-fetch.d.ts.map +1 -1
  89. package/dist/core/tools/web-fetch.js +4 -8
  90. package/dist/core/tools/web-fetch.js.map +1 -1
  91. package/dist/core/tools/web-search-exa.d.ts +3 -6
  92. package/dist/core/tools/web-search-exa.d.ts.map +1 -1
  93. package/dist/core/tools/web-search-exa.js +5 -8
  94. package/dist/core/tools/web-search-exa.js.map +1 -1
  95. package/dist/core/trust-manager.d.ts +6 -5
  96. package/dist/core/trust-manager.d.ts.map +1 -1
  97. package/dist/core/trust-manager.js +42 -19
  98. package/dist/core/trust-manager.js.map +1 -1
  99. package/dist/core/web-search-provider.d.ts +0 -10
  100. package/dist/core/web-search-provider.d.ts.map +1 -1
  101. package/dist/core/web-search-provider.js +2 -16
  102. package/dist/core/web-search-provider.js.map +1 -1
  103. package/dist/main.d.ts +0 -1
  104. package/dist/main.d.ts.map +1 -1
  105. package/dist/main.js +2 -3
  106. package/dist/main.js.map +1 -1
  107. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  108. package/dist/modes/interactive/components/settings-selector.js +1 -1
  109. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  110. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  111. package/dist/modes/interactive/interactive-mode.js +0 -29
  112. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  113. package/dist/modes/rpc/rpc-types.d.ts +1 -1
  114. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  115. package/dist/modes/rpc/rpc-types.js.map +1 -1
  116. package/dist/utils/tools-manager.d.ts +0 -25
  117. package/dist/utils/tools-manager.d.ts.map +1 -1
  118. package/dist/utils/tools-manager.js +3 -52
  119. package/dist/utils/tools-manager.js.map +1 -1
  120. package/dist/utils/version-check.d.ts +0 -2
  121. package/dist/utils/version-check.d.ts.map +1 -1
  122. package/dist/utils/version-check.js +0 -2
  123. package/dist/utils/version-check.js.map +1 -1
  124. package/docs/containerization.md +8 -4
  125. package/docs/sdk.md +17 -24
  126. package/docs/security.md +7 -5
  127. package/docs/settings.md +2 -7
  128. package/docs/skills.md +4 -7
  129. package/npm-shrinkwrap.json +5 -5
  130. package/package.json +2 -2
  131. package/dist/core/sandbox/bwrap-arguments.d.ts +0 -41
  132. package/dist/core/sandbox/bwrap-arguments.d.ts.map +0 -1
  133. package/dist/core/sandbox/bwrap-arguments.js +0 -136
  134. package/dist/core/sandbox/bwrap-arguments.js.map +0 -1
  135. package/dist/core/sandbox/child-entry.d.ts +0 -2
  136. package/dist/core/sandbox/child-entry.d.ts.map +0 -1
  137. package/dist/core/sandbox/child-entry.js +0 -24
  138. package/dist/core/sandbox/child-entry.js.map +0 -1
  139. package/dist/core/sandbox/cli-launch.d.ts +0 -162
  140. package/dist/core/sandbox/cli-launch.d.ts.map +0 -1
  141. package/dist/core/sandbox/cli-launch.js +0 -366
  142. package/dist/core/sandbox/cli-launch.js.map +0 -1
  143. package/dist/core/sandbox/cli-supervisor.d.ts +0 -31
  144. package/dist/core/sandbox/cli-supervisor.d.ts.map +0 -1
  145. package/dist/core/sandbox/cli-supervisor.js +0 -147
  146. package/dist/core/sandbox/cli-supervisor.js.map +0 -1
  147. package/dist/core/sandbox/default-hosts.d.ts +0 -28
  148. package/dist/core/sandbox/default-hosts.d.ts.map +0 -1
  149. package/dist/core/sandbox/default-hosts.js +0 -69
  150. package/dist/core/sandbox/default-hosts.js.map +0 -1
  151. package/dist/core/sandbox/full-access.d.ts +0 -28
  152. package/dist/core/sandbox/full-access.d.ts.map +0 -1
  153. package/dist/core/sandbox/full-access.js +0 -63
  154. package/dist/core/sandbox/full-access.js.map +0 -1
  155. package/dist/core/sandbox/git-identity.d.ts +0 -57
  156. package/dist/core/sandbox/git-identity.d.ts.map +0 -1
  157. package/dist/core/sandbox/git-identity.js +0 -78
  158. package/dist/core/sandbox/git-identity.js.map +0 -1
  159. package/dist/core/sandbox/host-approval.d.ts +0 -42
  160. package/dist/core/sandbox/host-approval.d.ts.map +0 -1
  161. package/dist/core/sandbox/host-approval.js +0 -99
  162. package/dist/core/sandbox/host-approval.js.map +0 -1
  163. package/dist/core/sandbox/linux-backend.d.ts +0 -48
  164. package/dist/core/sandbox/linux-backend.d.ts.map +0 -1
  165. package/dist/core/sandbox/linux-backend.js +0 -328
  166. package/dist/core/sandbox/linux-backend.js.map +0 -1
  167. package/dist/core/sandbox/macos-backend.d.ts +0 -30
  168. package/dist/core/sandbox/macos-backend.d.ts.map +0 -1
  169. package/dist/core/sandbox/macos-backend.js +0 -385
  170. package/dist/core/sandbox/macos-backend.js.map +0 -1
  171. package/dist/core/sandbox/network-proxy.d.ts +0 -38
  172. package/dist/core/sandbox/network-proxy.d.ts.map +0 -1
  173. package/dist/core/sandbox/network-proxy.js +0 -153
  174. package/dist/core/sandbox/network-proxy.js.map +0 -1
  175. package/dist/core/sandbox/network-refusal.d.ts +0 -24
  176. package/dist/core/sandbox/network-refusal.d.ts.map +0 -1
  177. package/dist/core/sandbox/network-refusal.js +0 -72
  178. package/dist/core/sandbox/network-refusal.js.map +0 -1
  179. package/dist/core/sandbox/policy.d.ts +0 -40
  180. package/dist/core/sandbox/policy.d.ts.map +0 -1
  181. package/dist/core/sandbox/policy.js +0 -55
  182. package/dist/core/sandbox/policy.js.map +0 -1
  183. package/dist/core/sandbox/profiles.d.ts +0 -25
  184. package/dist/core/sandbox/profiles.d.ts.map +0 -1
  185. package/dist/core/sandbox/profiles.js +0 -23
  186. package/dist/core/sandbox/profiles.js.map +0 -1
  187. package/dist/core/sandbox/rpc/command-client.d.ts +0 -28
  188. package/dist/core/sandbox/rpc/command-client.d.ts.map +0 -1
  189. package/dist/core/sandbox/rpc/command-client.js +0 -86
  190. package/dist/core/sandbox/rpc/command-client.js.map +0 -1
  191. package/dist/core/sandbox/rpc/command-proxy.d.ts +0 -48
  192. package/dist/core/sandbox/rpc/command-proxy.d.ts.map +0 -1
  193. package/dist/core/sandbox/rpc/command-proxy.js +0 -120
  194. package/dist/core/sandbox/rpc/command-proxy.js.map +0 -1
  195. package/dist/core/sandbox/rpc/credential-client.d.ts +0 -20
  196. package/dist/core/sandbox/rpc/credential-client.d.ts.map +0 -1
  197. package/dist/core/sandbox/rpc/credential-client.js +0 -170
  198. package/dist/core/sandbox/rpc/credential-client.js.map +0 -1
  199. package/dist/core/sandbox/rpc/credential-proxy.d.ts +0 -44
  200. package/dist/core/sandbox/rpc/credential-proxy.d.ts.map +0 -1
  201. package/dist/core/sandbox/rpc/credential-proxy.js +0 -287
  202. package/dist/core/sandbox/rpc/credential-proxy.js.map +0 -1
  203. package/dist/core/sandbox/rpc/framing.d.ts +0 -35
  204. package/dist/core/sandbox/rpc/framing.d.ts.map +0 -1
  205. package/dist/core/sandbox/rpc/framing.js +0 -114
  206. package/dist/core/sandbox/rpc/framing.js.map +0 -1
  207. package/dist/core/sandbox/rpc/git-credential-helper.d.ts +0 -47
  208. package/dist/core/sandbox/rpc/git-credential-helper.d.ts.map +0 -1
  209. package/dist/core/sandbox/rpc/git-credential-helper.js +0 -222
  210. package/dist/core/sandbox/rpc/git-credential-helper.js.map +0 -1
  211. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts +0 -66
  212. package/dist/core/sandbox/rpc/git-credential-proxy.d.ts.map +0 -1
  213. package/dist/core/sandbox/rpc/git-credential-proxy.js +0 -209
  214. package/dist/core/sandbox/rpc/git-credential-proxy.js.map +0 -1
  215. package/dist/core/sandbox/supervisor-temp.d.ts +0 -18
  216. package/dist/core/sandbox/supervisor-temp.d.ts.map +0 -1
  217. package/dist/core/sandbox/supervisor-temp.js +0 -21
  218. package/dist/core/sandbox/supervisor-temp.js.map +0 -1
  219. package/dist/core/sandbox/supervisor.d.ts +0 -62
  220. package/dist/core/sandbox/supervisor.d.ts.map +0 -1
  221. package/dist/core/sandbox/supervisor.js +0 -32
  222. package/dist/core/sandbox/supervisor.js.map +0 -1
  223. package/dist/core/sandbox/terminal-handoff.d.ts +0 -68
  224. package/dist/core/sandbox/terminal-handoff.d.ts.map +0 -1
  225. package/dist/core/sandbox/terminal-handoff.js +0 -221
  226. package/dist/core/sandbox/terminal-handoff.js.map +0 -1
  227. package/dist/core/sandbox/terminal-size.d.ts +0 -53
  228. package/dist/core/sandbox/terminal-size.d.ts.map +0 -1
  229. package/dist/core/sandbox/terminal-size.js +0 -126
  230. package/dist/core/sandbox/terminal-size.js.map +0 -1
  231. package/dist/core/sandbox/violations.d.ts +0 -23
  232. package/dist/core/sandbox/violations.d.ts.map +0 -1
  233. package/dist/core/sandbox/violations.js +0 -30
  234. package/dist/core/sandbox/violations.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport {\n\tchmodSync,\n\tcreateReadStream,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n} from \"fs\";\nimport { arch, platform } from \"os\";\nimport { basename, join, relative, resolve as resolvePath } from \"path\";\nimport { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { getBinDir } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = getApexEnvironment(\"APEX_CODE_OFFLINE\");\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n}\n\n// Per-platform/architecture asset names live in MANAGED_TOOL_PINS (ADR 0017) as the\n// single reviewed source of truth, not derived here.\nconst TOOLS: Record<string, ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t},\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\", options: { toolsDirectory?: string } = {}): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first. Existence alone is not enough: the sandbox\n\t// projects host tools by bind-mounting over a file here, and bwrap leaves that\n\t// mountpoint behind as an empty, non-executable stub once the namespace is gone.\n\tconst managedPath = join(\n\t\toptions.toolsDirectory ?? TOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (isExecutableFile(managedPath, platform())) {\n\t\treturn managedPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Reviewed, pinned release metadata for auto-downloaded executable tools (ADR 0017).\n * Never resolved from a mutable \"latest\" API call. Updating a version, asset name, or\n * digest here is itself the reviewed change that authorizes installing a new release.\n *\n * Every digest and byte size below was established by downloading the artifact,\n * computing its SHA-256 locally, and cross-checking that value against the\n * upstream-published digest (ripgrep's own `.sha256` release sidecars; GitHub's own\n * per-asset `digest` field for both projects) — not invented, and not taken from a\n * single unverified source.\n */\ninterface ManagedToolPin {\n\tversion: string;\n\tassetName: string;\n\tsha256: string;\n\texpectedBytes: number;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n}\n\nconst MANAGED_TOOL_PINS: Record<\"fd\" | \"rg\", Partial<Record<string, ManagedToolPin>>> = {\n\trg: {\n\t\t\"linux:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz\",\n\t\t\tsha256: \"4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e\",\n\t\t\texpectedBytes: 2566310,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"linux:arm64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-aarch64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f\",\n\t\t\texpectedBytes: 2047405,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"fc87e78f7cb3fea12d69072e7ef3b21509754717b746368fd40d88963630e2b3\",\n\t\t\texpectedBytes: 2082672,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:arm64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-aarch64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"24ad76777745fbff131c8fbc466742b011f925bfa4fffa2ded6def23b5b937be\",\n\t\t\texpectedBytes: 1787248,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"win32:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"d0f534024c42afd6cb4d38907c25cd2b249b79bbe6cc1dbee8e3e37c2b6e25a1\",\n\t\t\texpectedBytes: 2058893,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t\t// No upstream win32/arm64 ripgrep release asset exists at 14.1.1; resolution\n\t\t// fails closed for that combination rather than constructing a URL that 404s.\n\t},\n\tfd: {\n\t\t\"linux:x64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-x86_64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"def59805cd14b5651b68990855f426ad087f3b96881296d963910431ba3143c8\",\n\t\t\texpectedBytes: 1700779,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"linux:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"6c51f7c5446b3338b1e401ff15dc194c590bb2fa64fd43ff3278300f073adec5\",\n\t\t\texpectedBytes: 1559490,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t// Upstream stopped shipping an Intel macOS fd binary after 10.3.0 (see\n\t\t// 7afd80d78, \"pin fd for macos x64\"); preserved here rather than special-cased.\n\t\t\"darwin:x64\": {\n\t\t\tversion: \"10.3.0\",\n\t\t\tassetName: \"fd-v10.3.0-x86_64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"50d30f13fe3d5914b14c4fff5abcbd4d0cdab4b855970a6956f4f006c17117a3\",\n\t\t\texpectedBytes: 1430203,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"623dc0afc81b92e4d4606b380d7bc91916ba7b97814263e554d50923a39e480a\",\n\t\t\texpectedBytes: 1328933,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"win32:x64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-x86_64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"b2816e506390a89941c63c9187d58a3cc10e9a55f2ef0685f9ea0eccaf7c98c8\",\n\t\t\texpectedBytes: 1525898,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t\t\"win32:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"4f9110c2d5b33a7f760bfa5510f4c113d828109f7277d421b1053a9943c0fc92\",\n\t\t\texpectedBytes: 1370473,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t},\n};\n\nexport interface ManagedToolArtifact {\n\tversion: string;\n\turl: string;\n\tsha256: string;\n\texpectedBytes: number;\n\t/** Bounded download ceiling (ADR 0017): four times the reviewed expected size. */\n\tmaxBytes: number;\n\tbinaryName: string;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n}\n\n/** Resolve reviewed, pinned metadata for a managed tool. Never queries \"latest\". */\nexport function resolveManagedToolArtifact(options: {\n\ttool: \"fd\" | \"rg\";\n\tplatform: NodeJS.Platform | string;\n\tarchitecture: string;\n}): ManagedToolArtifact {\n\tconst config = TOOLS[options.tool];\n\tconst pin = MANAGED_TOOL_PINS[options.tool]?.[`${options.platform}:${options.architecture}`];\n\tif (!config || !pin) {\n\t\tthrow new Error(`No reviewed pinned artifact for ${options.tool} on ${options.platform}/${options.architecture}`);\n\t}\n\treturn {\n\t\tversion: pin.version,\n\t\turl: `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${pin.version}/${pin.assetName}`,\n\t\tsha256: pin.sha256,\n\t\texpectedBytes: pin.expectedBytes,\n\t\tmaxBytes: pin.expectedBytes * 4,\n\t\tbinaryName: config.binaryName,\n\t\tarchiveKind: pin.archiveKind,\n\t};\n}\n\n// Download a file from URL, aborting if the declared or received size exceeds the\n// bounded ceiling (ADR 0017) rather than writing an unbounded stream to disk.\nasync function downloadFile(url: string, dest: string, maxBytes: number): Promise<void> {\n\tconst response = await fetchWithRetry(url, undefined, { timeoutMs: DOWNLOAD_TIMEOUT_MS });\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tconst declaredLength = Number(response.headers.get(\"content-length\") ?? Number.NaN);\n\tif (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n\t\tthrow new Error(`Refusing download: declared size ${declaredLength} exceeds bounded limit ${maxBytes}`);\n\t}\n\n\tlet received = 0;\n\tconst source = Readable.fromWeb(response.body as any);\n\tsource.on(\"data\", (chunk: Buffer) => {\n\t\treceived += chunk.length;\n\t\tif (received > maxBytes) {\n\t\t\tsource.destroy(new Error(`Refusing download: exceeded bounded limit ${maxBytes} bytes`));\n\t\t}\n\t});\n\n\tconst fileStream = createWriteStream(dest);\n\ttry {\n\t\tawait pipeline(source, fileStream);\n\t} catch (error) {\n\t\trmSync(dest, { force: true });\n\t\tthrow error;\n\t}\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\nasync function sha256File(path: string): Promise<string> {\n\tconst hash = createHash(\"sha256\");\n\tfor await (const chunk of createReadStream(path)) {\n\t\thash.update(chunk as Buffer);\n\t}\n\treturn hash.digest(\"hex\");\n}\n\nexport interface InstallManagedToolArchiveOptions {\n\tarchivePath: string;\n\tdestination: string;\n\tbinaryName: string;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n\texpectedSha256: string;\n\tmaxBytes: number;\n}\n\n/**\n * Verify a downloaded tool archive against its pinned digest, extract it into a\n * per-install quarantine directory, confirm the located binary did not escape that\n * quarantine, and only then atomically promote it into the live tools directory\n * (ADR 0017). Rejects a digest mismatch or an out-of-bounds archive before anything\n * is written to `destination`.\n */\nexport async function installManagedToolArchive(options: InstallManagedToolArchiveOptions): Promise<string> {\n\tconst { archivePath, destination, binaryName, archiveKind, expectedSha256, maxBytes } = options;\n\n\tconst { size } = statSync(archivePath);\n\tif (size > maxBytes) {\n\t\tthrow new Error(`Refusing install: archive size ${size} exceeds bounded limit ${maxBytes}`);\n\t}\n\n\tconst actualSha256 = await sha256File(archivePath);\n\tif (actualSha256.toLowerCase() !== expectedSha256.toLowerCase()) {\n\t\tthrow new Error(`Refusing install: digest mismatch for ${basename(archivePath)}`);\n\t}\n\n\tmkdirSync(destination, { recursive: true });\n\tconst quarantineDirectory = join(\n\t\tdestination,\n\t\t`.quarantine_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(quarantineDirectory, { recursive: true });\n\n\ttry {\n\t\tconst assetName = basename(archivePath);\n\t\tif (archiveKind === \"tar.gz\") {\n\t\t\textractTarGzArchive(archivePath, quarantineDirectory, assetName);\n\t\t} else {\n\t\t\textractZipArchive(archivePath, quarantineDirectory, assetName);\n\t\t}\n\n\t\tconst binaryFileName = binaryName + (platform() === \"win32\" ? \".exe\" : \"\");\n\t\tconst extractedBinary = findBinaryRecursively(quarantineDirectory, binaryFileName);\n\t\tif (!extractedBinary) {\n\t\t\tthrow new Error(`Binary not found in verified archive: expected ${binaryFileName}`);\n\t\t}\n\n\t\tconst resolvedBinary = resolvePath(extractedBinary);\n\t\tconst containmentPath = relative(resolvePath(quarantineDirectory), resolvedBinary);\n\t\tif (containmentPath.startsWith(\"..\") || resolvePath(containmentPath) === containmentPath) {\n\t\t\tthrow new Error(`Refusing install: archive entry escaped its quarantine directory: ${extractedBinary}`);\n\t\t}\n\n\t\tconst finalPath = join(destination, binaryFileName);\n\t\trenameSync(resolvedBinary, finalPath);\n\t\tif (platform() !== \"win32\") {\n\t\t\tchmodSync(finalPath, 0o755);\n\t\t}\n\t\treturn finalPath;\n\t} finally {\n\t\trmSync(quarantineDirectory, { recursive: true, force: true });\n\t}\n}\n\n// Download and install a tool from reviewed, pinned metadata (ADR 0017): bounded\n// download, digest verification, quarantine extraction, then atomic promotion.\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst plat = platform();\n\tconst architecture = arch();\n\tconst artifact = resolveManagedToolArtifact({ tool, platform: plat, architecture });\n\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Unique download filename: fd and rg downloads can run concurrently during\n\t// startup, so sharing a fixed path causes races.\n\tconst archivePath = join(\n\t\tTOOLS_DIR,\n\t\t`download_${artifact.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}${\n\t\t\tartifact.archiveKind === \"zip\" ? \".zip\" : \".tar.gz\"\n\t\t}`,\n\t);\n\n\ttry {\n\t\tawait downloadFile(artifact.url, archivePath, artifact.maxBytes);\n\t\treturn await installManagedToolArchive({\n\t\t\tarchivePath,\n\t\t\tdestination: TOOLS_DIR,\n\t\t\tbinaryName: artifact.binaryName,\n\t\t\tarchiveKind: artifact.archiveKind,\n\t\t\texpectedSha256: artifact.sha256,\n\t\t\tmaxBytes: artifact.maxBytes,\n\t\t});\n\t} finally {\n\t\trmSync(archivePath, { force: true });\n\t}\n}\n\nexport interface HostToolBinary {\n\t/** File name the child looks for in its own tools directory (\"fd\", \"rg\", \"fd.exe\"). */\n\tname: string;\n\t/** Absolute host path to the executable backing that name. */\n\tpath: string;\n}\n\nfunction isExecutableFile(candidate: string, targetPlatform: NodeJS.Platform): boolean {\n\ttry {\n\t\tconst stats = statSync(candidate);\n\t\tif (!stats.isFile()) return false;\n\t\t// Windows has no POSIX execute bit; presence of the file is the only signal.\n\t\treturn targetPlatform === \"win32\" || (stats.mode & 0o111) !== 0;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Absolute host path for a managed tool, or undefined when it is installed nowhere.\n *\n * `getToolPath` may return a bare command name because its caller spawns through PATH.\n * A sandboxed child has no such luxury: its PATH still lists host directories that the\n * OS boundary has already replaced, so the supervisor must resolve a real path here and\n * project it in (ADR 0017, and the sandbox spec's tool-projection amendment).\n */\nexport function resolveHostToolBinary(\n\ttool: \"fd\" | \"rg\",\n\toptions: { toolsDirectory?: string; pathValue?: string; platform?: NodeJS.Platform } = {},\n): string | undefined {\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tconst targetPlatform = options.platform ?? platform();\n\tconst executableSuffix = targetPlatform === \"win32\" ? \".exe\" : \"\";\n\tconst managedPath = join(options.toolsDirectory ?? TOOLS_DIR, config.binaryName + executableSuffix);\n\tif (isExecutableFile(managedPath, targetPlatform)) {\n\t\treturn managedPath;\n\t}\n\n\tconst pathEntries = (options.pathValue ?? process.env.PATH ?? \"\")\n\t\t.split(targetPlatform === \"win32\" ? \";\" : \":\")\n\t\t.filter((entry) => entry.length > 0);\n\t// Preference runs by name first, matching getToolPath: a real `fd` anywhere on PATH\n\t// beats a `fdfind` alias, rather than whichever directory happens to come first.\n\tfor (const systemBinaryName of config.systemBinaryNames ?? [config.binaryName]) {\n\t\tfor (const entry of pathEntries) {\n\t\t\tconst candidate = join(entry, systemBinaryName + executableSuffix);\n\t\t\tif (isExecutableFile(candidate, targetPlatform)) {\n\t\t\t\treturn candidate;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Install any missing managed tool on the host, then report absolute paths for the ones\n * that exist. Downloading here rather than inside the child means one install serves every\n * workspace, and it happens on the side of the boundary that still has network access.\n */\nexport async function prepareHostToolBinaries(silent: boolean = false): Promise<HostToolBinary[]> {\n\tconst binaries: HostToolBinary[] = [];\n\tfor (const tool of [\"fd\", \"rg\"] as const) {\n\t\tawait ensureTool(tool, silent ? undefined : ({ message }) => console.error(message));\n\t\tconst hostPath = resolveHostToolBinary(tool);\n\t\tif (hostPath) {\n\t\t\tbinaries.push({ name: TOOLS[tool].binaryName + (platform() === \"win32\" ? \".exe\" : \"\"), path: hostPath });\n\t\t}\n\t}\n\treturn binaries;\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n};\n\nexport interface ToolStatus {\n\ttype: \"info\" | \"warning\";\n\tmessage: string;\n}\n\n/**\n * Ensure a tool is available, downloading if necessary.\n * Reports progress through `onStatus`; status messages are otherwise silent.\n * Returns the tool path, or undefined if unavailable.\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tonStatus?: (status: ToolStatus) => void,\n): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Offline mode enabled, skipping download.` });\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n\t// Users must install via pkg.\n\tif (platform() === \"android\") {\n\t\tconst pkgName = TERMUX_PACKAGES[tool] ?? tool;\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Install with: pkg install ${pkgName}` });\n\t\treturn undefined;\n\t}\n\n\t// Tool not found - download it\n\tonStatus?.({ type: \"info\", message: `${config.name} not found. Downloading...` });\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tonStatus?.({ type: \"info\", message: `${config.name} installed to ${path}` });\n\t\treturn path;\n\t} catch (e) {\n\t\tonStatus?.({\n\t\t\ttype: \"warning\",\n\t\t\tmessage: `Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t});\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"tools-manager.js","sourceRoot":"","sources":["../../src/utils/tools-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,SAAS,EAAE,MAAM,eAAe,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EACN,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,MAAM,EACN,QAAQ,GACR,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,MAAM,CAAC;AACxE,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAEtD,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;AAC9B,MAAM,mBAAmB,GAAG,OAAO,CAAC;AAEpC,SAAS,oBAAoB,GAAY;IACxC,MAAM,KAAK,GAAG,kBAAkB,CAAC,mBAAmB,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,KAAK,CAAC;IACzB,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,MAAM,IAAI,KAAK,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;AAAA,CACxF;AAUD,oFAAoF;AACpF,qDAAqD;AACrD,MAAM,KAAK,GAA+B;IACzC,EAAE,EAAE;QACH,IAAI,EAAE,IAAI;QACV,IAAI,EAAE,YAAY;QAClB,UAAU,EAAE,IAAI;QAChB,iBAAiB,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC;QACnC,SAAS,EAAE,GAAG;KACd;IACD,EAAE,EAAE;QACH,IAAI,EAAE,SAAS;QACf,IAAI,EAAE,oBAAoB;QAC1B,UAAU,EAAE,IAAI;QAChB,SAAS,EAAE,EAAE;KACb;CACD,CAAC;AAEF,wDAAwD;AACxD,SAAS,aAAa,CAAC,GAAW,EAAW;IAC5C,IAAI,CAAC;QACJ,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QAChE,6CAA6C;QAC7C,OAAO,MAAM,CAAC,KAAK,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,2DAA2D;AAC3D,MAAM,UAAU,WAAW,CAAC,IAAiB,EAAE,OAAO,GAAgC,EAAE,EAAiB;IACxG,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEzB,iFAAiF;IACjF,iFAAiF;IACjF,iCAAiC;IACjC,MAAM,WAAW,GAAG,IAAI,CACvB,OAAO,CAAC,cAAc,IAAI,SAAS,EACnC,MAAM,CAAC,UAAU,GAAG,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAC1D,CAAC;IACF,IAAI,gBAAgB,CAAC,WAAW,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QAC/C,OAAO,WAAW,CAAC;IACpB,CAAC;IAED,4EAA4E;IAC5E,MAAM,iBAAiB,GAAG,MAAM,CAAC,iBAAiB,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IAC1E,KAAK,MAAM,gBAAgB,IAAI,iBAAiB,EAAE,CAAC;QAClD,IAAI,aAAa,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrC,OAAO,gBAAgB,CAAC;QACzB,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC;AAAA,CACZ;AAqBD,MAAM,iBAAiB,GAAiE;IACvF,EAAE,EAAE;QACH,WAAW,EAAE;YACZ,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,iDAAiD;YAC5D,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,aAAa,EAAE;YACd,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,iDAAiD;YAC5D,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,YAAY,EAAE;YACb,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,2CAA2C;YACtD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,cAAc,EAAE;YACf,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,4CAA4C;YACvD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,WAAW,EAAE;YACZ,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,2CAA2C;YACtD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,KAAK;SAClB;QACD,6EAA6E;QAC7E,8EAA8E;KAC9E;IACD,EAAE,EAAE;QACH,WAAW,EAAE;YACZ,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,4CAA4C;YACvD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,aAAa,EAAE;YACd,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,6CAA6C;YACxD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,uEAAuE;QACvE,gFAAgF;QAChF,YAAY,EAAE;YACb,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,uCAAuC;YAClD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,cAAc,EAAE;YACf,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,wCAAwC;YACnD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,QAAQ;SACrB;QACD,WAAW,EAAE;YACZ,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,uCAAuC;YAClD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,KAAK;SAClB;QACD,aAAa,EAAE;YACd,OAAO,EAAE,QAAQ;YACjB,SAAS,EAAE,wCAAwC;YACnD,MAAM,EAAE,kEAAkE;YAC1E,aAAa,EAAE,OAAO;YACtB,WAAW,EAAE,KAAK;SAClB;KACD;CACD,CAAC;AAaF,oFAAoF;AACpF,MAAM,UAAU,0BAA0B,CAAC,OAI1C,EAAuB;IACvB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC7F,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,mCAAmC,OAAO,CAAC,IAAI,OAAO,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IACnH,CAAC;IACD,OAAO;QACN,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,GAAG,EAAE,sBAAsB,MAAM,CAAC,IAAI,sBAAsB,MAAM,CAAC,SAAS,GAAG,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,SAAS,EAAE;QAC7G,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,QAAQ,EAAE,GAAG,CAAC,aAAa,GAAG,CAAC;QAC/B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,WAAW,EAAE,GAAG,CAAC,WAAW;KAC5B,CAAC;AAAA,CACF;AAED,kFAAkF;AAClF,8EAA8E;AAC9E,KAAK,UAAU,YAAY,CAAC,GAAW,EAAE,IAAY,EAAE,QAAgB,EAAiB;IACvF,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,GAAG,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAE1F,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,uBAAuB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACrC,CAAC;IAED,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC;IACpF,IAAI,MAAM,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,cAAc,GAAG,QAAQ,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CAAC,oCAAoC,cAAc,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IACzG,CAAC;IAED,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAW,CAAC,CAAC;IACtD,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC;QACpC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QACzB,IAAI,QAAQ,GAAG,QAAQ,EAAE,CAAC;YACzB,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,6CAA6C,QAAQ,QAAQ,CAAC,CAAC,CAAC;QAC1F,CAAC;IAAA,CACD,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC;QACJ,MAAM,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAC9B,MAAM,KAAK,CAAC;IACb,CAAC;AAAA,CACD;AAED,SAAS,qBAAqB,CAAC,OAAe,EAAE,cAAsB,EAAiB;IACtF,MAAM,KAAK,GAAa,CAAC,OAAO,CAAC,CAAC;IAElC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;QAC/B,IAAI,CAAC,UAAU;YAAE,SAAS;QAE1B,MAAM,OAAO,GAAG,WAAW,CAAC,UAAU,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACjE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACrD,OAAO,QAAQ,CAAC;YACjB,CAAC;YACD,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACzB,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACtB,CAAC;QACF,CAAC;IACF,CAAC;IAED,OAAO,IAAI,CAAC;AAAA,CACZ;AAED,SAAS,kBAAkB,CAAC,MAAgC,EAAU;IACrE,IAAI,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC;QAC3B,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;IAC7B,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC;IACf,CAAC;IACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;IAChD,IAAI,MAAM,EAAE,CAAC;QACZ,OAAO,MAAM,CAAC;IACf,CAAC;IACD,OAAO,eAAe,MAAM,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;AAAA,CACnD;AAED,SAAS,oBAAoB,CAAC,OAAe,EAAE,IAAc,EAAiB;IAC7E,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1C,OAAO,IAAI,CAAC;IACb,CAAC;IACD,OAAO,GAAG,OAAO,KAAK,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AAAA,CACnD;AAED,SAAS,mBAAmB,CAAC,WAAmB,EAAE,UAAkB,EAAE,SAAiB,EAAQ;IAC9F,MAAM,OAAO,GAAG,oBAAoB,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IACpF,IAAI,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,KAAK,OAAO,EAAE,CAAC,CAAC;IAC/D,CAAC;AAAA,CACD;AAED,SAAS,oBAAoB,GAAW;IACvC,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC;IAChE,IAAI,UAAU,EAAE,CAAC;QAChB,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;QAC1D,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC3B,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED,SAAS,iBAAiB,CAAC,WAAmB,EAAE,UAAkB,EAAE,SAAiB,EAAQ;IAC5F,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;QAC5B,wEAAwE;QACxE,+EAA+E;QAC/E,MAAM,UAAU,GAAG,oBAAoB,CAAC,oBAAoB,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QACvG,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAE1B,MAAM,MAAM,GACX,gJAAgJ,CAAC;QAClJ,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,gBAAgB,EAAE;YAChE,SAAS;YACT,YAAY;YACZ,iBAAiB;YACjB,kBAAkB;YAClB,QAAQ;YACR,UAAU;YACV,MAAM;YACN,WAAW;YACX,UAAU;SACV,CAAC,CAAC;QACH,IAAI,CAAC,iBAAiB;YAAE,OAAO;QAC/B,QAAQ,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAClC,CAAC;SAAM,CAAC;QACP,MAAM,YAAY,GAAG,oBAAoB,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QAC1F,IAAI,CAAC,YAAY;YAAE,OAAO;QAC1B,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE5B,MAAM,UAAU,GAAG,oBAAoB,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;QACtF,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,qBAAqB,SAAS,KAAK,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAAA,CAC1E;AAED,KAAK,UAAU,UAAU,CAAC,IAAY,EAAmB;IACxD,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;QAClD,IAAI,CAAC,MAAM,CAAC,KAAe,CAAC,CAAC;IAC9B,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAAA,CAC1B;AAWD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAAC,OAAyC,EAAmB;IAC3G,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAEhG,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvC,IAAI,IAAI,GAAG,QAAQ,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,kCAAkC,IAAI,0BAA0B,QAAQ,EAAE,CAAC,CAAC;IAC7F,CAAC;IAED,MAAM,YAAY,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IACnD,IAAI,YAAY,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,WAAW,EAAE,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CAAC,yCAAyC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,MAAM,mBAAmB,GAAG,IAAI,CAC/B,WAAW,EACX,eAAe,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CACrF,CAAC;IACF,SAAS,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,IAAI,CAAC;QACJ,MAAM,SAAS,GAAG,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxC,IAAI,WAAW,KAAK,QAAQ,EAAE,CAAC;YAC9B,mBAAmB,CAAC,WAAW,EAAE,mBAAmB,EAAE,SAAS,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACP,iBAAiB,CAAC,WAAW,EAAE,mBAAmB,EAAE,SAAS,CAAC,CAAC;QAChE,CAAC;QAED,MAAM,cAAc,GAAG,UAAU,GAAG,CAAC,QAAQ,EAAE,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3E,MAAM,eAAe,GAAG,qBAAqB,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC;QACnF,IAAI,CAAC,eAAe,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,kDAAkD,cAAc,EAAE,CAAC,CAAC;QACrF,CAAC;QAED,MAAM,cAAc,GAAG,WAAW,CAAC,eAAe,CAAC,CAAC;QACpD,MAAM,eAAe,GAAG,QAAQ,CAAC,WAAW,CAAC,mBAAmB,CAAC,EAAE,cAAc,CAAC,CAAC;QACnF,IAAI,eAAe,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,eAAe,CAAC,KAAK,eAAe,EAAE,CAAC;YAC1F,MAAM,IAAI,KAAK,CAAC,qEAAqE,eAAe,EAAE,CAAC,CAAC;QACzG,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,cAAc,CAAC,CAAC;QACpD,UAAU,CAAC,cAAc,EAAE,SAAS,CAAC,CAAC;QACtC,IAAI,QAAQ,EAAE,KAAK,OAAO,EAAE,CAAC;YAC5B,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,SAAS,CAAC;IAClB,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;AAAA,CACD;AAED,iFAAiF;AACjF,+EAA+E;AAC/E,KAAK,UAAU,YAAY,CAAC,IAAiB,EAAmB;IAC/D,MAAM,IAAI,GAAG,QAAQ,EAAE,CAAC;IACxB,MAAM,YAAY,GAAG,IAAI,EAAE,CAAC;IAC5B,MAAM,QAAQ,GAAG,0BAA0B,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,CAAC;IAEpF,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,4EAA4E;IAC5E,iDAAiD;IACjD,MAAM,WAAW,GAAG,IAAI,CACvB,SAAS,EACT,YAAY,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GACtG,QAAQ,CAAC,WAAW,KAAK,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAC3C,EAAE,CACF,CAAC;IAEF,IAAI,CAAC;QACJ,MAAM,YAAY,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACjE,OAAO,MAAM,yBAAyB,CAAC;YACtC,WAAW;YACX,WAAW,EAAE,SAAS;YACtB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,WAAW,EAAE,QAAQ,CAAC,WAAW;YACjC,cAAc,EAAE,QAAQ,CAAC,MAAM;YAC/B,QAAQ,EAAE,QAAQ,CAAC,QAAQ;SAC3B,CAAC,CAAC;IACJ,CAAC;YAAS,CAAC;QACV,MAAM,CAAC,WAAW,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;AAAA,CACD;AAED,SAAS,gBAAgB,CAAC,SAAiB,EAAE,cAA+B,EAAW;IACtF,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;YAAE,OAAO,KAAK,CAAC;QAClC,6EAA6E;QAC7E,OAAO,cAAc,KAAK,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AAAA,CACD;AAED,iCAAiC;AACjC,MAAM,eAAe,GAA2B;IAC/C,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,SAAS;CACb,CAAC;AAOF;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC/B,IAAiB,EACjB,QAAuC,EACT;IAC9B,MAAM,YAAY,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,YAAY,EAAE,CAAC;QAClB,OAAO,YAAY,CAAC;IACrB,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAE9B,IAAI,oBAAoB,EAAE,EAAE,CAAC;QAC5B,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,sDAAsD,EAAE,CAAC,CAAC;QAC/G,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,mFAAmF;IACnF,8BAA8B;IAC9B,IAAI,QAAQ,EAAE,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;QAC9C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,yCAAyC,OAAO,EAAE,EAAE,CAAC,CAAC;QAC3G,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,+BAA+B;IAC/B,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,4BAA4B,EAAE,CAAC,CAAC;IAElF,IAAI,CAAC;QACJ,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QACtC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,iBAAiB,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC;IACb,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACZ,QAAQ,EAAE,CAAC;YACV,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,sBAAsB,MAAM,CAAC,IAAI,KAAK,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;SACnF,CAAC,CAAC;QACH,OAAO,SAAS,CAAC;IAClB,CAAC;AAAA,CACD","sourcesContent":["import { type SpawnSyncReturns, spawnSync } from \"child_process\";\nimport { createHash } from \"crypto\";\nimport {\n\tchmodSync,\n\tcreateReadStream,\n\tcreateWriteStream,\n\texistsSync,\n\tmkdirSync,\n\treaddirSync,\n\trenameSync,\n\trmSync,\n\tstatSync,\n} from \"fs\";\nimport { arch, platform } from \"os\";\nimport { basename, join, relative, resolve as resolvePath } from \"path\";\nimport { Readable } from \"stream\";\nimport { pipeline } from \"stream/promises\";\nimport { getBinDir } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\nconst TOOLS_DIR = getBinDir();\nconst DOWNLOAD_TIMEOUT_MS = 120_000;\n\nfunction isOfflineModeEnabled(): boolean {\n\tconst value = getApexEnvironment(\"APEX_CODE_OFFLINE\");\n\tif (!value) return false;\n\treturn value === \"1\" || value.toLowerCase() === \"true\" || value.toLowerCase() === \"yes\";\n}\n\ninterface ToolConfig {\n\tname: string;\n\trepo: string; // GitHub repo (e.g., \"sharkdp/fd\")\n\tbinaryName: string; // Name of the binary inside the archive\n\tsystemBinaryNames?: string[]; // Alternative system command names to try before downloading\n\ttagPrefix: string; // Prefix for tags (e.g., \"v\" for v1.0.0, \"\" for 1.0.0)\n}\n\n// Per-platform/architecture asset names live in MANAGED_TOOL_PINS (ADR 0017) as the\n// single reviewed source of truth, not derived here.\nconst TOOLS: Record<string, ToolConfig> = {\n\tfd: {\n\t\tname: \"fd\",\n\t\trepo: \"sharkdp/fd\",\n\t\tbinaryName: \"fd\",\n\t\tsystemBinaryNames: [\"fd\", \"fdfind\"],\n\t\ttagPrefix: \"v\",\n\t},\n\trg: {\n\t\tname: \"ripgrep\",\n\t\trepo: \"BurntSushi/ripgrep\",\n\t\tbinaryName: \"rg\",\n\t\ttagPrefix: \"\",\n\t},\n};\n\n// Check if a command exists in PATH by trying to run it\nfunction commandExists(cmd: string): boolean {\n\ttry {\n\t\tconst result = spawnSync(cmd, [\"--version\"], { stdio: \"pipe\" });\n\t\t// Check for ENOENT error (command not found)\n\t\treturn result.error === undefined || result.error === null;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Get the path to a tool (system-wide or in our tools dir)\nexport function getToolPath(tool: \"fd\" | \"rg\", options: { toolsDirectory?: string } = {}): string | null {\n\tconst config = TOOLS[tool];\n\tif (!config) return null;\n\n\t// Check our tools directory first. Existence alone is not enough: an interrupted\n\t// download or a half-finished install leaves an empty, non-executable file here,\n\t// and that file is not the tool.\n\tconst managedPath = join(\n\t\toptions.toolsDirectory ?? TOOLS_DIR,\n\t\tconfig.binaryName + (platform() === \"win32\" ? \".exe\" : \"\"),\n\t);\n\tif (isExecutableFile(managedPath, platform())) {\n\t\treturn managedPath;\n\t}\n\n\t// Check system PATH - if found, just return the command name (it's in PATH)\n\tconst systemBinaryNames = config.systemBinaryNames ?? [config.binaryName];\n\tfor (const systemBinaryName of systemBinaryNames) {\n\t\tif (commandExists(systemBinaryName)) {\n\t\t\treturn systemBinaryName;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * Reviewed, pinned release metadata for auto-downloaded executable tools (ADR 0017).\n * Never resolved from a mutable \"latest\" API call. Updating a version, asset name, or\n * digest here is itself the reviewed change that authorizes installing a new release.\n *\n * Every digest and byte size below was established by downloading the artifact,\n * computing its SHA-256 locally, and cross-checking that value against the\n * upstream-published digest (ripgrep's own `.sha256` release sidecars; GitHub's own\n * per-asset `digest` field for both projects) — not invented, and not taken from a\n * single unverified source.\n */\ninterface ManagedToolPin {\n\tversion: string;\n\tassetName: string;\n\tsha256: string;\n\texpectedBytes: number;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n}\n\nconst MANAGED_TOOL_PINS: Record<\"fd\" | \"rg\", Partial<Record<string, ManagedToolPin>>> = {\n\trg: {\n\t\t\"linux:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-unknown-linux-musl.tar.gz\",\n\t\t\tsha256: \"4cf9f2741e6c465ffdb7c26f38056a59e2a2544b51f7cc128ef28337eeae4d8e\",\n\t\t\texpectedBytes: 2566310,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"linux:arm64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-aarch64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"c827481c4ff4ea10c9dc7a4022c8de5db34a5737cb74484d62eb94a95841ab2f\",\n\t\t\texpectedBytes: 2047405,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"fc87e78f7cb3fea12d69072e7ef3b21509754717b746368fd40d88963630e2b3\",\n\t\t\texpectedBytes: 2082672,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:arm64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-aarch64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"24ad76777745fbff131c8fbc466742b011f925bfa4fffa2ded6def23b5b937be\",\n\t\t\texpectedBytes: 1787248,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"win32:x64\": {\n\t\t\tversion: \"14.1.1\",\n\t\t\tassetName: \"ripgrep-14.1.1-x86_64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"d0f534024c42afd6cb4d38907c25cd2b249b79bbe6cc1dbee8e3e37c2b6e25a1\",\n\t\t\texpectedBytes: 2058893,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t\t// No upstream win32/arm64 ripgrep release asset exists at 14.1.1; resolution\n\t\t// fails closed for that combination rather than constructing a URL that 404s.\n\t},\n\tfd: {\n\t\t\"linux:x64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-x86_64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"def59805cd14b5651b68990855f426ad087f3b96881296d963910431ba3143c8\",\n\t\t\texpectedBytes: 1700779,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"linux:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-unknown-linux-gnu.tar.gz\",\n\t\t\tsha256: \"6c51f7c5446b3338b1e401ff15dc194c590bb2fa64fd43ff3278300f073adec5\",\n\t\t\texpectedBytes: 1559490,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t// Upstream stopped shipping an Intel macOS fd binary after 10.3.0 (see\n\t\t// 7afd80d78, \"pin fd for macos x64\"); preserved here rather than special-cased.\n\t\t\"darwin:x64\": {\n\t\t\tversion: \"10.3.0\",\n\t\t\tassetName: \"fd-v10.3.0-x86_64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"50d30f13fe3d5914b14c4fff5abcbd4d0cdab4b855970a6956f4f006c17117a3\",\n\t\t\texpectedBytes: 1430203,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"darwin:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-apple-darwin.tar.gz\",\n\t\t\tsha256: \"623dc0afc81b92e4d4606b380d7bc91916ba7b97814263e554d50923a39e480a\",\n\t\t\texpectedBytes: 1328933,\n\t\t\tarchiveKind: \"tar.gz\",\n\t\t},\n\t\t\"win32:x64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-x86_64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"b2816e506390a89941c63c9187d58a3cc10e9a55f2ef0685f9ea0eccaf7c98c8\",\n\t\t\texpectedBytes: 1525898,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t\t\"win32:arm64\": {\n\t\t\tversion: \"10.4.2\",\n\t\t\tassetName: \"fd-v10.4.2-aarch64-pc-windows-msvc.zip\",\n\t\t\tsha256: \"4f9110c2d5b33a7f760bfa5510f4c113d828109f7277d421b1053a9943c0fc92\",\n\t\t\texpectedBytes: 1370473,\n\t\t\tarchiveKind: \"zip\",\n\t\t},\n\t},\n};\n\nexport interface ManagedToolArtifact {\n\tversion: string;\n\turl: string;\n\tsha256: string;\n\texpectedBytes: number;\n\t/** Bounded download ceiling (ADR 0017): four times the reviewed expected size. */\n\tmaxBytes: number;\n\tbinaryName: string;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n}\n\n/** Resolve reviewed, pinned metadata for a managed tool. Never queries \"latest\". */\nexport function resolveManagedToolArtifact(options: {\n\ttool: \"fd\" | \"rg\";\n\tplatform: NodeJS.Platform | string;\n\tarchitecture: string;\n}): ManagedToolArtifact {\n\tconst config = TOOLS[options.tool];\n\tconst pin = MANAGED_TOOL_PINS[options.tool]?.[`${options.platform}:${options.architecture}`];\n\tif (!config || !pin) {\n\t\tthrow new Error(`No reviewed pinned artifact for ${options.tool} on ${options.platform}/${options.architecture}`);\n\t}\n\treturn {\n\t\tversion: pin.version,\n\t\turl: `https://github.com/${config.repo}/releases/download/${config.tagPrefix}${pin.version}/${pin.assetName}`,\n\t\tsha256: pin.sha256,\n\t\texpectedBytes: pin.expectedBytes,\n\t\tmaxBytes: pin.expectedBytes * 4,\n\t\tbinaryName: config.binaryName,\n\t\tarchiveKind: pin.archiveKind,\n\t};\n}\n\n// Download a file from URL, aborting if the declared or received size exceeds the\n// bounded ceiling (ADR 0017) rather than writing an unbounded stream to disk.\nasync function downloadFile(url: string, dest: string, maxBytes: number): Promise<void> {\n\tconst response = await fetchWithRetry(url, undefined, { timeoutMs: DOWNLOAD_TIMEOUT_MS });\n\n\tif (!response.ok) {\n\t\tthrow new Error(`Failed to download: ${response.status}`);\n\t}\n\n\tif (!response.body) {\n\t\tthrow new Error(\"No response body\");\n\t}\n\n\tconst declaredLength = Number(response.headers.get(\"content-length\") ?? Number.NaN);\n\tif (Number.isFinite(declaredLength) && declaredLength > maxBytes) {\n\t\tthrow new Error(`Refusing download: declared size ${declaredLength} exceeds bounded limit ${maxBytes}`);\n\t}\n\n\tlet received = 0;\n\tconst source = Readable.fromWeb(response.body as any);\n\tsource.on(\"data\", (chunk: Buffer) => {\n\t\treceived += chunk.length;\n\t\tif (received > maxBytes) {\n\t\t\tsource.destroy(new Error(`Refusing download: exceeded bounded limit ${maxBytes} bytes`));\n\t\t}\n\t});\n\n\tconst fileStream = createWriteStream(dest);\n\ttry {\n\t\tawait pipeline(source, fileStream);\n\t} catch (error) {\n\t\trmSync(dest, { force: true });\n\t\tthrow error;\n\t}\n}\n\nfunction findBinaryRecursively(rootDir: string, binaryFileName: string): string | null {\n\tconst stack: string[] = [rootDir];\n\n\twhile (stack.length > 0) {\n\t\tconst currentDir = stack.pop();\n\t\tif (!currentDir) continue;\n\n\t\tconst entries = readdirSync(currentDir, { withFileTypes: true });\n\t\tfor (const entry of entries) {\n\t\t\tconst fullPath = join(currentDir, entry.name);\n\t\t\tif (entry.isFile() && entry.name === binaryFileName) {\n\t\t\t\treturn fullPath;\n\t\t\t}\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tstack.push(fullPath);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn null;\n}\n\nfunction formatSpawnFailure(result: SpawnSyncReturns<Buffer>): string {\n\tif (result.error?.message) {\n\t\treturn result.error.message;\n\t}\n\tconst stderr = result.stderr?.toString().trim();\n\tif (stderr) {\n\t\treturn stderr;\n\t}\n\tconst stdout = result.stdout?.toString().trim();\n\tif (stdout) {\n\t\treturn stdout;\n\t}\n\treturn `exit status ${result.status ?? \"unknown\"}`;\n}\n\nfunction runExtractionCommand(command: string, args: string[]): string | null {\n\tconst result = spawnSync(command, args, { stdio: \"pipe\" });\n\tif (!result.error && result.status === 0) {\n\t\treturn null;\n\t}\n\treturn `${command}: ${formatSpawnFailure(result)}`;\n}\n\nfunction extractTarGzArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failure = runExtractionCommand(\"tar\", [\"xzf\", archivePath, \"-C\", extractDir]);\n\tif (failure) {\n\t\tthrow new Error(`Failed to extract ${assetName}: ${failure}`);\n\t}\n}\n\nfunction getWindowsTarCommand(): string {\n\tconst systemRoot = process.env.SystemRoot ?? process.env.WINDIR;\n\tif (systemRoot) {\n\t\tconst systemTar = join(systemRoot, \"System32\", \"tar.exe\");\n\t\tif (existsSync(systemTar)) {\n\t\t\treturn systemTar;\n\t\t}\n\t}\n\treturn \"tar.exe\";\n}\n\nfunction extractZipArchive(archivePath: string, extractDir: string, assetName: string): void {\n\tconst failures: string[] = [];\n\n\tif (platform() === \"win32\") {\n\t\t// Windows ships bsdtar as tar.exe, which supports zip files. Prefer the\n\t\t// System32 binary over Git Bash's GNU tar, which does not handle zip archives.\n\t\tconst tarFailure = runExtractionCommand(getWindowsTarCommand(), [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\n\t\tconst script =\n\t\t\t\"& { param($archive, $destination) $ErrorActionPreference = 'Stop'; Expand-Archive -LiteralPath $archive -DestinationPath $destination -Force }\";\n\t\tconst powershellFailure = runExtractionCommand(\"powershell.exe\", [\n\t\t\t\"-NoLogo\",\n\t\t\t\"-NoProfile\",\n\t\t\t\"-NonInteractive\",\n\t\t\t\"-ExecutionPolicy\",\n\t\t\t\"Bypass\",\n\t\t\t\"-Command\",\n\t\t\tscript,\n\t\t\tarchivePath,\n\t\t\textractDir,\n\t\t]);\n\t\tif (!powershellFailure) return;\n\t\tfailures.push(powershellFailure);\n\t} else {\n\t\tconst unzipFailure = runExtractionCommand(\"unzip\", [\"-q\", archivePath, \"-d\", extractDir]);\n\t\tif (!unzipFailure) return;\n\t\tfailures.push(unzipFailure);\n\n\t\tconst tarFailure = runExtractionCommand(\"tar\", [\"xf\", archivePath, \"-C\", extractDir]);\n\t\tif (!tarFailure) return;\n\t\tfailures.push(tarFailure);\n\t}\n\n\tthrow new Error(`Failed to extract ${assetName}: ${failures.join(\"; \")}`);\n}\n\nasync function sha256File(path: string): Promise<string> {\n\tconst hash = createHash(\"sha256\");\n\tfor await (const chunk of createReadStream(path)) {\n\t\thash.update(chunk as Buffer);\n\t}\n\treturn hash.digest(\"hex\");\n}\n\nexport interface InstallManagedToolArchiveOptions {\n\tarchivePath: string;\n\tdestination: string;\n\tbinaryName: string;\n\tarchiveKind: \"tar.gz\" | \"zip\";\n\texpectedSha256: string;\n\tmaxBytes: number;\n}\n\n/**\n * Verify a downloaded tool archive against its pinned digest, extract it into a\n * per-install quarantine directory, confirm the located binary did not escape that\n * quarantine, and only then atomically promote it into the live tools directory\n * (ADR 0017). Rejects a digest mismatch or an out-of-bounds archive before anything\n * is written to `destination`.\n */\nexport async function installManagedToolArchive(options: InstallManagedToolArchiveOptions): Promise<string> {\n\tconst { archivePath, destination, binaryName, archiveKind, expectedSha256, maxBytes } = options;\n\n\tconst { size } = statSync(archivePath);\n\tif (size > maxBytes) {\n\t\tthrow new Error(`Refusing install: archive size ${size} exceeds bounded limit ${maxBytes}`);\n\t}\n\n\tconst actualSha256 = await sha256File(archivePath);\n\tif (actualSha256.toLowerCase() !== expectedSha256.toLowerCase()) {\n\t\tthrow new Error(`Refusing install: digest mismatch for ${basename(archivePath)}`);\n\t}\n\n\tmkdirSync(destination, { recursive: true });\n\tconst quarantineDirectory = join(\n\t\tdestination,\n\t\t`.quarantine_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`,\n\t);\n\tmkdirSync(quarantineDirectory, { recursive: true });\n\n\ttry {\n\t\tconst assetName = basename(archivePath);\n\t\tif (archiveKind === \"tar.gz\") {\n\t\t\textractTarGzArchive(archivePath, quarantineDirectory, assetName);\n\t\t} else {\n\t\t\textractZipArchive(archivePath, quarantineDirectory, assetName);\n\t\t}\n\n\t\tconst binaryFileName = binaryName + (platform() === \"win32\" ? \".exe\" : \"\");\n\t\tconst extractedBinary = findBinaryRecursively(quarantineDirectory, binaryFileName);\n\t\tif (!extractedBinary) {\n\t\t\tthrow new Error(`Binary not found in verified archive: expected ${binaryFileName}`);\n\t\t}\n\n\t\tconst resolvedBinary = resolvePath(extractedBinary);\n\t\tconst containmentPath = relative(resolvePath(quarantineDirectory), resolvedBinary);\n\t\tif (containmentPath.startsWith(\"..\") || resolvePath(containmentPath) === containmentPath) {\n\t\t\tthrow new Error(`Refusing install: archive entry escaped its quarantine directory: ${extractedBinary}`);\n\t\t}\n\n\t\tconst finalPath = join(destination, binaryFileName);\n\t\trenameSync(resolvedBinary, finalPath);\n\t\tif (platform() !== \"win32\") {\n\t\t\tchmodSync(finalPath, 0o755);\n\t\t}\n\t\treturn finalPath;\n\t} finally {\n\t\trmSync(quarantineDirectory, { recursive: true, force: true });\n\t}\n}\n\n// Download and install a tool from reviewed, pinned metadata (ADR 0017): bounded\n// download, digest verification, quarantine extraction, then atomic promotion.\nasync function downloadTool(tool: \"fd\" | \"rg\"): Promise<string> {\n\tconst plat = platform();\n\tconst architecture = arch();\n\tconst artifact = resolveManagedToolArtifact({ tool, platform: plat, architecture });\n\n\tmkdirSync(TOOLS_DIR, { recursive: true });\n\n\t// Unique download filename: fd and rg downloads can run concurrently during\n\t// startup, so sharing a fixed path causes races.\n\tconst archivePath = join(\n\t\tTOOLS_DIR,\n\t\t`download_${artifact.binaryName}_${process.pid}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}${\n\t\t\tartifact.archiveKind === \"zip\" ? \".zip\" : \".tar.gz\"\n\t\t}`,\n\t);\n\n\ttry {\n\t\tawait downloadFile(artifact.url, archivePath, artifact.maxBytes);\n\t\treturn await installManagedToolArchive({\n\t\t\tarchivePath,\n\t\t\tdestination: TOOLS_DIR,\n\t\t\tbinaryName: artifact.binaryName,\n\t\t\tarchiveKind: artifact.archiveKind,\n\t\t\texpectedSha256: artifact.sha256,\n\t\t\tmaxBytes: artifact.maxBytes,\n\t\t});\n\t} finally {\n\t\trmSync(archivePath, { force: true });\n\t}\n}\n\nfunction isExecutableFile(candidate: string, targetPlatform: NodeJS.Platform): boolean {\n\ttry {\n\t\tconst stats = statSync(candidate);\n\t\tif (!stats.isFile()) return false;\n\t\t// Windows has no POSIX execute bit; presence of the file is the only signal.\n\t\treturn targetPlatform === \"win32\" || (stats.mode & 0o111) !== 0;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n// Termux package names for tools\nconst TERMUX_PACKAGES: Record<string, string> = {\n\tfd: \"fd\",\n\trg: \"ripgrep\",\n};\n\nexport interface ToolStatus {\n\ttype: \"info\" | \"warning\";\n\tmessage: string;\n}\n\n/**\n * Ensure a tool is available, downloading if necessary.\n * Reports progress through `onStatus`; status messages are otherwise silent.\n * Returns the tool path, or undefined if unavailable.\n */\nexport async function ensureTool(\n\ttool: \"fd\" | \"rg\",\n\tonStatus?: (status: ToolStatus) => void,\n): Promise<string | undefined> {\n\tconst existingPath = getToolPath(tool);\n\tif (existingPath) {\n\t\treturn existingPath;\n\t}\n\n\tconst config = TOOLS[tool];\n\tif (!config) return undefined;\n\n\tif (isOfflineModeEnabled()) {\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Offline mode enabled, skipping download.` });\n\t\treturn undefined;\n\t}\n\n\t// On Android/Termux, Linux binaries don't work due to Bionic libc incompatibility.\n\t// Users must install via pkg.\n\tif (platform() === \"android\") {\n\t\tconst pkgName = TERMUX_PACKAGES[tool] ?? tool;\n\t\tonStatus?.({ type: \"warning\", message: `${config.name} not found. Install with: pkg install ${pkgName}` });\n\t\treturn undefined;\n\t}\n\n\t// Tool not found - download it\n\tonStatus?.({ type: \"info\", message: `${config.name} not found. Downloading...` });\n\n\ttry {\n\t\tconst path = await downloadTool(tool);\n\t\tonStatus?.({ type: \"info\", message: `${config.name} installed to ${path}` });\n\t\treturn path;\n\t} catch (e) {\n\t\tonStatus?.({\n\t\t\ttype: \"warning\",\n\t\t\tmessage: `Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,\n\t\t});\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -1,5 +1,3 @@
1
- /** Exported so the sandbox's default allowlist cannot drift from the URL used here. */
2
- export declare const VERSION_CHECK_HOST: string;
3
1
  export interface LatestApexCodeRelease {
4
2
  version: string;
5
3
  packageName?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"AAeA,uFAAuF;AACvF,eAAO,MAAM,kBAAkB,QAAmC,CAAC;AAGnE,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiB9D;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpG;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAM/F;AAED,wBAAsB,wBAAwB,CAC7C,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAiC5C;AAED,wBAAsB,wBAAwB,CAC7C,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED,wBAAsB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAYnH","sourcesContent":["import { compare, valid } from \"semver\";\nimport { PACKAGE_NAME } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { getApexCodeUserAgent } from \"./apex-code-user-agent.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\n/**\n * npm's own per-tag registry endpoint, not a custom API this project would need to\n * host and operate. Compares against \"latest\", which is the tag a release publishes\n * under and the tag a bare `npm install apex-code` resolves (ADR 0026). While no\n * stable version exists that is the newest prerelease; afterwards it is the newest\n * stable. Querying \"next\" instead would report no update available for the whole\n * pre-stable period, because \"next\" does not advance until a stable line exists.\n */\nconst LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\n/** Exported so the sandbox's default allowlist cannot drift from the URL used here. */\nexport const VERSION_CHECK_HOST = new URL(LATEST_VERSION_URL).host;\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestApexCodeRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\n/** Include useful errno details hidden behind Node's generic \"fetch failed\" error. */\nexport function formatVersionCheckError(error: unknown): string {\n\tconst rootMessage = error instanceof Error && error.message ? error.message : String(error);\n\tconst cause = error instanceof Error ? error.cause : undefined;\n\tconst causes = cause instanceof AggregateError ? cause.errors : cause === undefined ? [] : [cause];\n\tconst codes = causes\n\t\t.map((value) =>\n\t\t\ttypeof value === \"object\" && value !== null && \"code\" in value && typeof value.code === \"string\"\n\t\t\t\t? value.code\n\t\t\t\t: undefined,\n\t\t)\n\t\t.filter((code): code is string => code !== undefined);\n\n\tif (codes.length > 0) return `${rootMessage} (${[...new Set(codes)].join(\", \")})`;\n\tconst causeMessage = causes.find(\n\t\t(value): value is Error => value instanceof Error && Boolean(value.message),\n\t)?.message;\n\treturn causeMessage ? `${rootMessage} (cause: ${causeMessage})` : rootMessage;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestApexCodeRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_OFFLINE\")) return undefined;\n\n\tconst response = await fetchWithRetry(\n\t\tLATEST_VERSION_URL,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"User-Agent\": getApexCodeUserAgent(currentVersion),\n\t\t\t\taccept: \"application/json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmaxRetries: options.retry ? 2 : 0,\n\t\t\ttimeoutMs: options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS,\n\t\t},\n\t);\n\tif (!response.ok) return undefined;\n\n\t// npm's per-tag registry endpoint returns the full package manifest for that\n\t// version; \"packageName\"/\"note\" have no npm-registry equivalent and are left\n\t// undefined -- callers already treat both as optional with sensible fallbacks.\n\tconst data = (await response.json()) as {\n\t\tname?: unknown;\n\t\tversion?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName = typeof data.name === \"string\" && data.name.trim() ? data.name.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\t...(packageName && packageName !== PACKAGE_NAME ? { packageName } : {}),\n\t};\n}\n\nexport async function getLatestApexCodeVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestApexCodeRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewApexCodeVersion(currentVersion: string): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_SKIP_VERSION_CHECK\")) return undefined;\n\n\ttry {\n\t\tconst latestRelease = await getLatestApexCodeRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"version-check.d.ts","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"AAiBA,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAiB9D;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAOpG;AAED,wBAAgB,qBAAqB,CAAC,gBAAgB,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAM/F;AAED,wBAAsB,wBAAwB,CAC7C,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAiC5C;AAED,wBAAsB,wBAAwB,CAC7C,cAAc,EAAE,MAAM,EACtB,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACnD,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAE7B;AAED,wBAAsB,0BAA0B,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAYnH","sourcesContent":["import { compare, valid } from \"semver\";\nimport { PACKAGE_NAME } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { getApexCodeUserAgent } from \"./apex-code-user-agent.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\n/**\n * npm's own per-tag registry endpoint, not a custom API this project would need to\n * host and operate. Compares against \"latest\", which is the tag a release publishes\n * under and the tag a bare `npm install apex-code` resolves (ADR 0026). While no\n * stable version exists that is the newest prerelease; afterwards it is the newest\n * stable. Querying \"next\" instead would report no update available for the whole\n * pre-stable period, because \"next\" does not advance until a stable line exists.\n */\nconst LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestApexCodeRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\n/** Include useful errno details hidden behind Node's generic \"fetch failed\" error. */\nexport function formatVersionCheckError(error: unknown): string {\n\tconst rootMessage = error instanceof Error && error.message ? error.message : String(error);\n\tconst cause = error instanceof Error ? error.cause : undefined;\n\tconst causes = cause instanceof AggregateError ? cause.errors : cause === undefined ? [] : [cause];\n\tconst codes = causes\n\t\t.map((value) =>\n\t\t\ttypeof value === \"object\" && value !== null && \"code\" in value && typeof value.code === \"string\"\n\t\t\t\t? value.code\n\t\t\t\t: undefined,\n\t\t)\n\t\t.filter((code): code is string => code !== undefined);\n\n\tif (codes.length > 0) return `${rootMessage} (${[...new Set(codes)].join(\", \")})`;\n\tconst causeMessage = causes.find(\n\t\t(value): value is Error => value instanceof Error && Boolean(value.message),\n\t)?.message;\n\treturn causeMessage ? `${rootMessage} (cause: ${causeMessage})` : rootMessage;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestApexCodeRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_OFFLINE\")) return undefined;\n\n\tconst response = await fetchWithRetry(\n\t\tLATEST_VERSION_URL,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"User-Agent\": getApexCodeUserAgent(currentVersion),\n\t\t\t\taccept: \"application/json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmaxRetries: options.retry ? 2 : 0,\n\t\t\ttimeoutMs: options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS,\n\t\t},\n\t);\n\tif (!response.ok) return undefined;\n\n\t// npm's per-tag registry endpoint returns the full package manifest for that\n\t// version; \"packageName\"/\"note\" have no npm-registry equivalent and are left\n\t// undefined -- callers already treat both as optional with sensible fallbacks.\n\tconst data = (await response.json()) as {\n\t\tname?: unknown;\n\t\tversion?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName = typeof data.name === \"string\" && data.name.trim() ? data.name.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\t...(packageName && packageName !== PACKAGE_NAME ? { packageName } : {}),\n\t};\n}\n\nexport async function getLatestApexCodeVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestApexCodeRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewApexCodeVersion(currentVersion: string): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_SKIP_VERSION_CHECK\")) return undefined;\n\n\ttry {\n\t\tconst latestRelease = await getLatestApexCodeRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -12,8 +12,6 @@ import { fetchWithRetry } from "./management-http.js";
12
12
  * pre-stable period, because "next" does not advance until a stable line exists.
13
13
  */
14
14
  const LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
15
- /** Exported so the sandbox's default allowlist cannot drift from the URL used here. */
16
- export const VERSION_CHECK_HOST = new URL(LATEST_VERSION_URL).host;
17
15
  const DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;
18
16
  /** Include useful errno details hidden behind Node's generic "fetch failed" error. */
19
17
  export function formatVersionCheckError(error) {
@@ -1 +1 @@
1
- {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { compare, valid } from \"semver\";\nimport { PACKAGE_NAME } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { getApexCodeUserAgent } from \"./apex-code-user-agent.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\n/**\n * npm's own per-tag registry endpoint, not a custom API this project would need to\n * host and operate. Compares against \"latest\", which is the tag a release publishes\n * under and the tag a bare `npm install apex-code` resolves (ADR 0026). While no\n * stable version exists that is the newest prerelease; afterwards it is the newest\n * stable. Querying \"next\" instead would report no update available for the whole\n * pre-stable period, because \"next\" does not advance until a stable line exists.\n */\nconst LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\n/** Exported so the sandbox's default allowlist cannot drift from the URL used here. */\nexport const VERSION_CHECK_HOST = new URL(LATEST_VERSION_URL).host;\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestApexCodeRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\n/** Include useful errno details hidden behind Node's generic \"fetch failed\" error. */\nexport function formatVersionCheckError(error: unknown): string {\n\tconst rootMessage = error instanceof Error && error.message ? error.message : String(error);\n\tconst cause = error instanceof Error ? error.cause : undefined;\n\tconst causes = cause instanceof AggregateError ? cause.errors : cause === undefined ? [] : [cause];\n\tconst codes = causes\n\t\t.map((value) =>\n\t\t\ttypeof value === \"object\" && value !== null && \"code\" in value && typeof value.code === \"string\"\n\t\t\t\t? value.code\n\t\t\t\t: undefined,\n\t\t)\n\t\t.filter((code): code is string => code !== undefined);\n\n\tif (codes.length > 0) return `${rootMessage} (${[...new Set(codes)].join(\", \")})`;\n\tconst causeMessage = causes.find(\n\t\t(value): value is Error => value instanceof Error && Boolean(value.message),\n\t)?.message;\n\treturn causeMessage ? `${rootMessage} (cause: ${causeMessage})` : rootMessage;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestApexCodeRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_OFFLINE\")) return undefined;\n\n\tconst response = await fetchWithRetry(\n\t\tLATEST_VERSION_URL,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"User-Agent\": getApexCodeUserAgent(currentVersion),\n\t\t\t\taccept: \"application/json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmaxRetries: options.retry ? 2 : 0,\n\t\t\ttimeoutMs: options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS,\n\t\t},\n\t);\n\tif (!response.ok) return undefined;\n\n\t// npm's per-tag registry endpoint returns the full package manifest for that\n\t// version; \"packageName\"/\"note\" have no npm-registry equivalent and are left\n\t// undefined -- callers already treat both as optional with sensible fallbacks.\n\tconst data = (await response.json()) as {\n\t\tname?: unknown;\n\t\tversion?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName = typeof data.name === \"string\" && data.name.trim() ? data.name.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\t...(packageName && packageName !== PACKAGE_NAME ? { packageName } : {}),\n\t};\n}\n\nexport async function getLatestApexCodeVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestApexCodeRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewApexCodeVersion(currentVersion: string): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_SKIP_VERSION_CHECK\")) return undefined;\n\n\ttry {\n\t\tconst latestRelease = await getLatestApexCodeRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
1
+ {"version":3,"file":"version-check.js","sourceRoot":"","sources":["../../src/utils/version-check.ts"],"names":[],"mappings":"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","sourcesContent":["import { compare, valid } from \"semver\";\nimport { PACKAGE_NAME } from \"../config.ts\";\nimport { getApexEnvironment } from \"../core/environment.ts\";\nimport { getApexCodeUserAgent } from \"./apex-code-user-agent.ts\";\nimport { fetchWithRetry } from \"./management-http.ts\";\n\n/**\n * npm's own per-tag registry endpoint, not a custom API this project would need to\n * host and operate. Compares against \"latest\", which is the tag a release publishes\n * under and the tag a bare `npm install apex-code` resolves (ADR 0026). While no\n * stable version exists that is the newest prerelease; afterwards it is the newest\n * stable. Querying \"next\" instead would report no update available for the whole\n * pre-stable period, because \"next\" does not advance until a stable line exists.\n */\nconst LATEST_VERSION_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;\nconst DEFAULT_VERSION_CHECK_TIMEOUT_MS = 10000;\n\nexport interface LatestApexCodeRelease {\n\tversion: string;\n\tpackageName?: string;\n\tnote?: string;\n}\n\n/** Include useful errno details hidden behind Node's generic \"fetch failed\" error. */\nexport function formatVersionCheckError(error: unknown): string {\n\tconst rootMessage = error instanceof Error && error.message ? error.message : String(error);\n\tconst cause = error instanceof Error ? error.cause : undefined;\n\tconst causes = cause instanceof AggregateError ? cause.errors : cause === undefined ? [] : [cause];\n\tconst codes = causes\n\t\t.map((value) =>\n\t\t\ttypeof value === \"object\" && value !== null && \"code\" in value && typeof value.code === \"string\"\n\t\t\t\t? value.code\n\t\t\t\t: undefined,\n\t\t)\n\t\t.filter((code): code is string => code !== undefined);\n\n\tif (codes.length > 0) return `${rootMessage} (${[...new Set(codes)].join(\", \")})`;\n\tconst causeMessage = causes.find(\n\t\t(value): value is Error => value instanceof Error && Boolean(value.message),\n\t)?.message;\n\treturn causeMessage ? `${rootMessage} (cause: ${causeMessage})` : rootMessage;\n}\n\nexport function comparePackageVersions(leftVersion: string, rightVersion: string): number | undefined {\n\tconst left = valid(leftVersion.trim());\n\tconst right = valid(rightVersion.trim());\n\tif (!left || !right) {\n\t\treturn undefined;\n\t}\n\treturn compare(left, right);\n}\n\nexport function isNewerPackageVersion(candidateVersion: string, currentVersion: string): boolean {\n\tconst comparison = comparePackageVersions(candidateVersion, currentVersion);\n\tif (comparison !== undefined) {\n\t\treturn comparison > 0;\n\t}\n\treturn candidateVersion.trim() !== currentVersion.trim();\n}\n\nexport async function getLatestApexCodeRelease(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_OFFLINE\")) return undefined;\n\n\tconst response = await fetchWithRetry(\n\t\tLATEST_VERSION_URL,\n\t\t{\n\t\t\theaders: {\n\t\t\t\t\"User-Agent\": getApexCodeUserAgent(currentVersion),\n\t\t\t\taccept: \"application/json\",\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tmaxRetries: options.retry ? 2 : 0,\n\t\t\ttimeoutMs: options.timeoutMs ?? DEFAULT_VERSION_CHECK_TIMEOUT_MS,\n\t\t},\n\t);\n\tif (!response.ok) return undefined;\n\n\t// npm's per-tag registry endpoint returns the full package manifest for that\n\t// version; \"packageName\"/\"note\" have no npm-registry equivalent and are left\n\t// undefined -- callers already treat both as optional with sensible fallbacks.\n\tconst data = (await response.json()) as {\n\t\tname?: unknown;\n\t\tversion?: unknown;\n\t};\n\tif (typeof data.version !== \"string\" || !data.version.trim()) {\n\t\treturn undefined;\n\t}\n\tconst packageName = typeof data.name === \"string\" && data.name.trim() ? data.name.trim() : undefined;\n\treturn {\n\t\tversion: data.version.trim(),\n\t\t...(packageName && packageName !== PACKAGE_NAME ? { packageName } : {}),\n\t};\n}\n\nexport async function getLatestApexCodeVersion(\n\tcurrentVersion: string,\n\toptions: { timeoutMs?: number; retry?: boolean } = {},\n): Promise<string | undefined> {\n\treturn (await getLatestApexCodeRelease(currentVersion, options))?.version;\n}\n\nexport async function checkForNewApexCodeVersion(currentVersion: string): Promise<LatestApexCodeRelease | undefined> {\n\tif (getApexEnvironment(\"APEX_CODE_SKIP_VERSION_CHECK\")) return undefined;\n\n\ttry {\n\t\tconst latestRelease = await getLatestApexCodeRelease(currentVersion);\n\t\tif (latestRelease && isNewerPackageVersion(latestRelease.version, currentVersion)) {\n\t\t\treturn latestRelease;\n\t\t}\n\t\treturn undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n"]}
@@ -1,9 +1,13 @@
1
1
  # Containerization
2
2
 
3
- Apex Code has its own permission gate and, on Linux and macOS, an OS-level sandbox (see
4
- [Security](security.md)); Windows has no supported sandbox backend, and even where the
5
- built-in sandbox applies, you may want stronger or environment-level control over what
6
- directories Apex Code can write to and which accesses it has.
3
+ Apex Code ships no built-in sandbox. A session runs with the permissions of the account
4
+ that started it, on every platform, so containment is something you provide rather than
5
+ something to add on top. The permission gate decides whether a tool runs, but it is policy
6
+ rather than a boundary (see [Security](security.md)).
7
+
8
+ Read this page before pointing Apex Code at an untrusted repository, at generated code you
9
+ will not review, or at an unattended run. It is the answer to "how do I confine this",
10
+ not an optional hardening step.
7
11
 
8
12
  There are two general options. You can either
9
13
  1. run the whole `apex-code` process inside an isolated environment, or
package/docs/sdk.md CHANGED
@@ -49,30 +49,23 @@ The main factory function for a single `AgentSession`.
49
49
 
50
50
  `createAgentSession()` uses a `ResourceLoader` to supply extensions, skills, prompt templates, themes, and context files. If you do not provide one, it uses `DefaultResourceLoader` with standard discovery.
51
51
 
52
- #### Sandbox contract
53
-
54
- The SDK cannot determine whether its host process is contained. Choose the
55
- contract that describes how the embedding runs:
56
-
57
- ```typescript
58
- const { session, sandboxContract, sandboxDiagnostic } = await createAgentSession({
59
- sandbox: "required",
60
- });
61
- ```
62
-
63
- * `"required"` refuses to create the session unless Apex's enforcing supervisor
64
- marker is present. Use this when the SDK runs inside an Apex CLI child.
65
- * `"external"` creates the session and reports that containment belongs to
66
- another system. The SDK does not verify that system.
67
- * `"none"` creates the session without asserting OS containment. This is the
68
- default for compatibility.
69
-
70
- The result always reports `sandboxContract`. It includes `sandboxDiagnostic` for
71
- `"external"` and `"none"`. Delegated children inherit the selected contract.
72
- Do not treat a normal SDK process, a permission gate, or a read-only tool list
73
- as an OS sandbox. See [ADR 0031](../../docs/adr/0031-sdk-sandbox-contract.md)
74
- and [ADR 0005](../../docs/adr/0005-sandbox-boundary-guarantees.md) for the
75
- platform limits.
52
+ #### Isolation
53
+
54
+ The SDK asserts nothing about containment, because it has nothing to assert. A session
55
+ runs in your process, with your process's permissions, and no option changes that.
56
+
57
+ Earlier versions accepted a `sandbox` option of `"required"`, `"external"`, or `"none"`
58
+ and reported the resolved contract and a diagnostic on the result. All of it is removed.
59
+ `"required"` used to refuse to create the session unless the CLI had launched it inside
60
+ the old boundary, so if you depended on that refusal, note that nothing throws now and
61
+ the check has to move into your own startup. In TypeScript the removed option is a
62
+ compile error; in JavaScript it is ignored silently. The changelog entry for the release
63
+ names the exact fields. See [ADR 0032](../../../docs/adr/0032-no-built-in-sandbox.md).
64
+
65
+ Confine the process instead. Run the embedding inside a container or VM holding only the
66
+ files and credentials the task needs. Do not treat the permission gate or a read-only
67
+ tool list as containment: the gate decides whether a tool runs, not what an allowed tool
68
+ can reach once it does.
76
69
 
77
70
  ```typescript
78
71
  import { createAgentSession, SessionManager } from "apex-code";
package/docs/security.md CHANGED
@@ -30,15 +30,17 @@ Declining trust skips protected resources. Context files such as `AGENTS.overrid
30
30
 
31
31
  Non-interactive modes (`-p`, `--mode json`, and `--mode rpc`) do not show a trust prompt. Without an applicable saved trust decision, `defaultProjectTrust: "ask"` and `"never"` ignore such resources, while `"always"` trusts them. Use `--approve`/`-a` or `--no-approve`/`-na` to override project trust for one run.
32
32
 
33
- ## OS Sandbox and Permissions
33
+ ## Permissions and Isolation
34
34
 
35
- Unlike upstream Pi, Apex Code adds a permission gate in front of every tool call and an OS-level sandbox on Linux and macOS (Bubblewrap and Seatbelt respectively) — see [ADR 0005](https://github.com/Fchery87/apex-code/blob/main/docs/adr/0005-sandbox-boundary-guarantees.md) for exactly what that boundary does and does not guarantee, and the root [`SECURITY.md`](https://github.com/Fchery87/apex-code/blob/main/SECURITY.md) for what counts as a reportable bypass. Windows has no supported sandbox backend. Outside of the permission gate and OS sandbox, built-in tools, extensions, package installs, shell commands, language servers, and other developer tools still run with the permissions of the Apex Code process.
35
+ Unlike upstream Pi, Apex Code adds a permission gate in front of every tool call. It decides whether a tool runs, on every platform, in the process you started — see the root [`SECURITY.md`](https://github.com/Fchery87/apex-code/blob/main/SECURITY.md) for what counts as a reportable bypass.
36
36
 
37
- Project trust is only an input-loading guard, layered underneath the permission gate and sandbox. It prevents a repository from silently changing Apex Code's settings or extensions before you approve it. It does not make untrusted code, untrusted prompts, or untrusted model output safe by itself. Prompt injection from repository files, comments, documentation, context files, or build output is expected local-agent risk and cannot be reliably prevented by Apex Code.
37
+ Apex Code ships no built-in sandbox. There is no OS-level boundary between a session and the rest of your machine on any platform, and the permission gate is a policy layer rather than containment. Built-in tools, extensions, package installs, shell commands, language servers, and other developer tools run with the permissions of the account that started Apex Code. Containment is the operator's container or VM; [ADR 0032](https://github.com/Fchery87/apex-code/blob/main/docs/adr/0032-no-built-in-sandbox.md) records that decision and its consequences.
38
+
39
+ Project trust is only an input-loading guard, layered alongside the permission gate. It prevents a repository from silently changing Apex Code's settings or extensions before you approve it. It does not make untrusted code, untrusted prompts, or untrusted model output safe by itself. Prompt injection from repository files, comments, documentation, context files, or build output is expected local-agent risk and cannot be reliably prevented by Apex Code.
38
40
 
39
41
  ## Running Untrusted or Unmonitored Work
40
42
 
41
- For untrusted repositories, generated code you do not intend to monitor closely, or unattended automation, run Apex Code in a contained environment in addition to its own sandbox. Use a container, VM, micro-VM, remote sandbox, or policy-controlled sandbox with only the files and credentials required for the task.
43
+ For untrusted repositories, generated code you do not intend to monitor closely, or unattended automation, run Apex Code in a contained environment. It has no boundary of its own, so this is the containment boundary rather than an extra one. Use a container, VM, micro-VM, remote sandbox, or policy-controlled sandbox with only the files and credentials required for the task.
42
44
 
43
45
  Common patterns are documented in [Containerization](containerization.md):
44
46
 
@@ -56,4 +58,4 @@ If you bind-mount a host workspace read/write, writes from inside the container
56
58
 
57
59
  To report a security issue, follow Apex Code's own [Security Policy](https://github.com/Fchery87/apex-code/blob/main/SECURITY.md) — private disclosure via [GitHub private vulnerability reporting](https://github.com/Fchery87/apex-code/security/advisories/new). Do not open a public issue for security-sensitive reports.
58
60
 
59
- Expected local-agent behavior, prompt injection from untrusted content, and behavior of user-installed extensions or skills are generally outside the security boundary unless the report demonstrates a real bypass of the permission gate or OS sandbox, or shows how Apex Code grants access that the local user did not already have. See `SECURITY.md`'s "In scope"/"Out of scope" sections for the current, authoritative boundary.
61
+ Expected local-agent behavior, prompt injection from untrusted content, and behavior of user-installed extensions or skills are generally outside the security boundary unless the report demonstrates a real bypass of the permission gate, or shows how Apex Code grants access that the local user did not already have. See `SECURITY.md`'s "In scope"/"Out of scope" sections for the current, authoritative boundary.
package/docs/settings.md CHANGED
@@ -117,13 +117,8 @@ below only matter if you want to override a default.
117
117
  ```
118
118
 
119
119
  A credential stored under `exa` in `auth.json` is also used, and takes precedence over
120
- the environment variable. Note that nothing writes it for you today: every interactive
121
- session runs inside the OS sandbox, which mounts the credential file read-only, so
122
- `auth.json` can only be edited outside a running session. `EXA_API_KEY` is the
123
- supported path.
124
-
125
- The backend's host is added to the sandbox network allowlist automatically once a
126
- credential is configured, so `network.allowedHosts` needs no entry for it.
120
+ the environment variable. Note that nothing writes it for you today, so edit `auth.json`
121
+ yourself or use `EXA_API_KEY`, which is the supported path.
127
122
 
128
123
  ### Warnings
129
124
 
package/docs/skills.md CHANGED
@@ -34,13 +34,10 @@ Apex Code loads skills from:
34
34
  - Settings: `skills` array with files or directories
35
35
  - CLI: `--skill <path>` (repeatable, additive even with `--no-skills`)
36
36
 
37
- > **Sandbox note:** every session runs inside Apex Code's OS sandbox (see
38
- > [Security](security.md#os-sandbox-and-permissions)), which hides your home
39
- > directory from the child process by default. The two global roots above are
40
- > mounted back in, read-only, at their original host paths, so they work the same as
41
- > everywhere else. Project skills already work without a mount, since the workspace
42
- > itself is mounted read-write. Packages, settings, and `--skill` paths follow
43
- > whichever of these roots they resolve under.
37
+ > **Isolation note:** skills are read from these roots on the host, with no projection
38
+ > or remapping in between (see [Security](security.md#permissions-and-isolation)). If you
39
+ > run Apex Code inside a container, a global skill root only exists in the session when
40
+ > you mount it, and a skill runs with whatever access that environment grants.
44
41
 
45
42
  Discovery rules:
46
43
  - In `~/.apex-code/agent/skills/` and `.apex-code/skills/`, direct root `.md` files are discovered as individual skills when they have valid skill frontmatter with a non-empty `description`
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "apex-code",
3
- "version": "0.0.6",
3
+ "version": "0.1.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "apex-code",
9
- "version": "0.0.6",
9
+ "version": "0.1.1",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@earendil-works/pi-ai": "^0.84.4",
@@ -16,7 +16,7 @@
16
16
  "@modelcontextprotocol/client": "2.0.0",
17
17
  "@modelcontextprotocol/core": "2.0.0",
18
18
  "@silvia-odwyer/photon-node": "0.3.4",
19
- "apex-code-agent-core": "0.0.6",
19
+ "apex-code-agent-core": "0.1.1",
20
20
  "chalk": "5.6.2",
21
21
  "cross-spawn": "7.0.6",
22
22
  "diff": "9.0.0",
@@ -1100,8 +1100,8 @@
1100
1100
  }
1101
1101
  },
1102
1102
  "node_modules/apex-code-agent-core": {
1103
- "version": "0.0.6",
1104
- "resolved": "https://registry.npmjs.org/apex-code-agent-core/-/apex-code-agent-core-0.0.6.tgz",
1103
+ "version": "0.1.1",
1104
+ "resolved": "https://registry.npmjs.org/apex-code-agent-core/-/apex-code-agent-core-0.1.1.tgz",
1105
1105
  "license": "MIT",
1106
1106
  "dependencies": {
1107
1107
  "@earendil-works/pi-ai": "^0.84.4",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "apex-code",
3
- "version": "0.0.6",
3
+ "version": "0.1.1",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -50,7 +50,7 @@
50
50
  "@modelcontextprotocol/client": "2.0.0",
51
51
  "@modelcontextprotocol/core": "2.0.0",
52
52
  "@silvia-odwyer/photon-node": "0.3.4",
53
- "apex-code-agent-core": "0.0.6",
53
+ "apex-code-agent-core": "0.1.1",
54
54
  "chalk": "5.6.2",
55
55
  "cross-spawn": "7.0.6",
56
56
  "diff": "9.0.0",
@@ -1,41 +0,0 @@
1
- /**
2
- * The single source of the child's `bwrap` argv.
3
- *
4
- * Extracted when per-command escalation needed a second child. Two hand-maintained argv
5
- * builders is the divergence ADR 0010 exists to prevent for tool contracts, and it is
6
- * worse here: a mount tightened in the primary child and missed in the escalated one
7
- * would be invisible until the escalated path was the one that mattered. Everything that
8
- * differs between the two children is an input to this function, so there is nothing to
9
- * keep in sync by hand.
10
- */
11
- /** A unix socket projected from the host into the child. */
12
- export interface BwrapSocketMount {
13
- readonly hostPath: string;
14
- readonly childPath: string;
15
- }
16
- /** A host executable placed read-only at an exact path inside the child. */
17
- export interface BwrapBinaryMount {
18
- readonly source: string;
19
- readonly destination: string;
20
- }
21
- export interface BwrapSpec {
22
- /** The primary writable root, and where the child starts. */
23
- readonly workspace: string;
24
- /** Further writable roots, each bound exactly as the workspace is. */
25
- readonly additionalWritableRoots: readonly string[];
26
- /** Directories whose contents the child may read but not write. */
27
- readonly readOnlyPaths: readonly string[];
28
- /** Individual read-only files, projected by descriptor from index 3 upward. */
29
- readonly readOnlyFiles: readonly string[];
30
- readonly readOnlyBinaries: readonly BwrapBinaryMount[];
31
- readonly sockets: readonly BwrapSocketMount[];
32
- readonly environment: Readonly<Record<string, string>>;
33
- readonly command: string;
34
- readonly args: readonly string[];
35
- }
36
- export declare function readOnlyMountArguments(path: string): string[];
37
- /** Directory paths are mounted from matching fd 3 onward to preserve sibling exclusion. */
38
- export declare function descriptorBackedReadOnlyPaths(paths: readonly string[]): string[];
39
- /** Build the complete argv. Mount order below is load-bearing; the comments say why. */
40
- export declare function buildBwrapArguments(spec: BwrapSpec): string[];
41
- //# sourceMappingURL=bwrap-arguments.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bwrap-arguments.d.ts","sourceRoot":"","sources":["../../../src/core/sandbox/bwrap-arguments.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AAEH,4DAA4D;AAC5D,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;AAED,4EAA4E;AAC5E,MAAM,WAAW,gBAAgB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACzB,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,sEAAsE;IACtE,QAAQ,CAAC,uBAAuB,EAAE,SAAS,MAAM,EAAE,CAAC;IACpD,mEAAmE;IACnE,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1C,QAAQ,CAAC,gBAAgB,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IAC9C,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;CACjC;AAYD,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAI7D;AAED,2FAA2F;AAC3F,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAmED,wFAAwF;AACxF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,EAAE,CA+D7D","sourcesContent":["import { statSync } from \"node:fs\";\nimport { dirname, resolve, sep } from \"node:path\";\n\n/**\n * The single source of the child's `bwrap` argv.\n *\n * Extracted when per-command escalation needed a second child. Two hand-maintained argv\n * builders is the divergence ADR 0010 exists to prevent for tool contracts, and it is\n * worse here: a mount tightened in the primary child and missed in the escalated one\n * would be invisible until the escalated path was the one that mattered. Everything that\n * differs between the two children is an input to this function, so there is nothing to\n * keep in sync by hand.\n */\n\n/** A unix socket projected from the host into the child. */\nexport interface BwrapSocketMount {\n\treadonly hostPath: string;\n\treadonly childPath: string;\n}\n\n/** A host executable placed read-only at an exact path inside the child. */\nexport interface BwrapBinaryMount {\n\treadonly source: string;\n\treadonly destination: string;\n}\n\nexport interface BwrapSpec {\n\t/** The primary writable root, and where the child starts. */\n\treadonly workspace: string;\n\t/** Further writable roots, each bound exactly as the workspace is. */\n\treadonly additionalWritableRoots: readonly string[];\n\t/** Directories whose contents the child may read but not write. */\n\treadonly readOnlyPaths: readonly string[];\n\t/** Individual read-only files, projected by descriptor from index 3 upward. */\n\treadonly readOnlyFiles: readonly string[];\n\treadonly readOnlyBinaries: readonly BwrapBinaryMount[];\n\treadonly sockets: readonly BwrapSocketMount[];\n\treadonly environment: Readonly<Record<string, string>>;\n\treadonly command: string;\n\treadonly args: readonly string[];\n}\n\nfunction ancestorDirectoryArguments(directory: string): string[] {\n\tconst ancestors: string[] = [];\n\tlet current = directory;\n\twhile (current !== \"/\" && current !== \"/home\") {\n\t\tancestors.push(current);\n\t\tcurrent = dirname(current);\n\t}\n\treturn ancestors.reverse().flatMap((ancestor) => [\"--dir\", ancestor]);\n}\n\nexport function readOnlyMountArguments(path: string): string[] {\n\tconst target = resolve(path);\n\tconst parent = dirname(target);\n\treturn [...ancestorDirectoryArguments(parent), \"--ro-bind\", parent, parent];\n}\n\n/** Directory paths are mounted from matching fd 3 onward to preserve sibling exclusion. */\nexport function descriptorBackedReadOnlyPaths(paths: readonly string[]): string[] {\n\treturn paths.filter((path) => {\n\t\ttry {\n\t\t\treturn statSync(resolve(path)).isDirectory();\n\t\t} catch {\n\t\t\treturn false;\n\t\t}\n\t});\n}\n\ninterface DescriptorBackedDirectory {\n\treadonly path: string;\n\treadonly descriptor: number;\n}\n\ninterface DescriptorBackedFile extends DescriptorBackedDirectory {}\n\ninterface ProjectionGroup {\n\treadonly directories: DescriptorBackedDirectory[];\n\treadonly files: DescriptorBackedFile[];\n}\n\nfunction isWithin(path: string, root: string): boolean {\n\tconst target = resolve(path);\n\tconst boundary = resolve(root);\n\treturn target === boundary || target.startsWith(boundary.endsWith(sep) ? boundary : `${boundary}${sep}`);\n}\n\nfunction nestedReadOnlyMountArguments(projections: readonly DescriptorBackedDirectory[]): string[] {\n\treturn projections.flatMap(({ path, descriptor }) => {\n\t\tconst target = resolve(path);\n\t\treturn [\"--ro-bind\", `/proc/self/fd/${descriptor}`, target];\n\t});\n}\n\nfunction externalReadOnlyMountArguments(\n\tdirectories: readonly DescriptorBackedDirectory[],\n\tfiles: readonly DescriptorBackedFile[],\n\twritableRoots: readonly string[],\n): string[] {\n\tconst byParent = new Map<string, ProjectionGroup>();\n\tconst add = (kind: keyof ProjectionGroup, projection: DescriptorBackedDirectory): void => {\n\t\tconst parent = dirname(resolve(projection.path));\n\t\tconst group = byParent.get(parent) ?? { directories: [], files: [] };\n\t\tgroup[kind].push(projection);\n\t\tbyParent.set(parent, group);\n\t};\n\tfor (const directory of directories) add(\"directories\", directory);\n\tfor (const file of files) add(\"files\", file);\n\n\tconst groups = [...byParent].sort(([left], [right]) => left.split(sep).length - right.split(sep).length);\n\tconst laterMountpoints = [...byParent.keys(), ...writableRoots];\n\treturn groups.flatMap(([parent, group]) => [\n\t\t...ancestorDirectoryArguments(parent),\n\t\t\"--tmpfs\",\n\t\tparent,\n\t\t...laterMountpoints\n\t\t\t.filter((mountpoint) => mountpoint !== parent && isWithin(mountpoint, parent))\n\t\t\t.flatMap(ancestorDirectoryArguments),\n\t\t...group.directories.flatMap(({ path, descriptor }) => {\n\t\t\tconst target = resolve(path);\n\t\t\treturn [\"--dir\", target, \"--ro-bind\", `/proc/self/fd/${descriptor}`, target];\n\t\t}),\n\t\t...group.files.flatMap(({ path, descriptor }) => [\n\t\t\t\"--perms\",\n\t\t\t\"0400\",\n\t\t\t\"--file\",\n\t\t\tString(descriptor),\n\t\t\tresolve(path),\n\t\t]),\n\t\t\"--remount-ro\",\n\t\tparent,\n\t]);\n}\n\n/** Build the complete argv. Mount order below is load-bearing; the comments say why. */\nexport function buildBwrapArguments(spec: BwrapSpec): string[] {\n\tconst directoryPaths = descriptorBackedReadOnlyPaths(spec.readOnlyPaths);\n\tconst ordinaryPaths = spec.readOnlyPaths.filter((path) => !directoryPaths.includes(path));\n\tconst writableRoots = [spec.workspace, ...spec.additionalWritableRoots];\n\tconst directoryMounts = directoryPaths.map((path, index) => ({ path, descriptor: 3 + index }));\n\tconst fileMounts = spec.readOnlyFiles.map((path, index) => ({\n\t\tpath,\n\t\tdescriptor: 3 + directoryPaths.length + index,\n\t}));\n\tconst nestedDirectoryMounts = directoryMounts.filter(({ path }) =>\n\t\twritableRoots.some((root) => isWithin(path, root)),\n\t);\n\tconst externalDirectoryMounts = directoryMounts.filter(({ path }) =>\n\t\twritableRoots.every((root) => !isWithin(path, root)),\n\t);\n\tconst nestedFileMounts = fileMounts.filter(({ path }) => writableRoots.some((root) => isWithin(path, root)));\n\tconst externalFileMounts = fileMounts.filter(({ path }) => writableRoots.every((root) => !isWithin(path, root)));\n\treturn [\n\t\t\"--new-session\",\n\t\t\"--die-with-parent\",\n\t\t\"--unshare-user\",\n\t\t\"--unshare-pid\",\n\t\t\"--unshare-net\",\n\t\t\"--ro-bind\",\n\t\t\"/\",\n\t\t\"/\",\n\t\t\"--tmpfs\",\n\t\t\"/home\",\n\t\t// Immediately after the /home tmpfs: that is the only writable mount at this\n\t\t// point, so it is the only place bwrap can create a socket mountpoint.\n\t\t...spec.sockets.flatMap(({ hostPath, childPath }) => [\"--bind\", hostPath, childPath]),\n\t\t// External directory projections shadow their parent to exclude siblings. Do\n\t\t// that before writable roots. The root binds below restore paths that the\n\t\t// projection's parent shadow may have hidden.\n\t\t...externalReadOnlyMountArguments(externalDirectoryMounts, externalFileMounts, writableRoots),\n\t\t...writableRoots.flatMap((root) => [\"--dir\", root]),\n\t\t\"--bind\",\n\t\tspec.workspace,\n\t\tspec.workspace,\n\t\t// Each extra root is bound exactly as the workspace is, and only ever from an\n\t\t// argv-parsed flag: a repository that could name its own writable root would be\n\t\t// granting itself authority (ADR 0016).\n\t\t...spec.additionalWritableRoots.flatMap((root) => [\"--bind\", root, root]),\n\t\t// These mounts must follow the writable roots: otherwise binding a workspace\n\t\t// would mask a read-only projection nested inside it.\n\t\t...ordinaryPaths.flatMap((path) => readOnlyMountArguments(path)),\n\t\t// A directory already inside a writable root needs no parent shadow: that root\n\t\t// already exposes its siblings by policy. Rebind only the requested directory.\n\t\t...nestedReadOnlyMountArguments([...nestedDirectoryMounts, ...nestedFileMounts]),\n\t\t// After the workspace bind: these destinations sit inside it, and an earlier mount\n\t\t// would be masked when the workspace is bound over them.\n\t\t...spec.readOnlyBinaries.flatMap(({ source, destination }) => [\"--ro-bind\", source, destination]),\n\t\t\"--dev\",\n\t\t\"/dev\",\n\t\t\"--proc\",\n\t\t\"/proc\",\n\t\t\"--chdir\",\n\t\tspec.workspace,\n\t\t...Object.entries(spec.environment).flatMap(([name, value]) => [\"--setenv\", name, value]),\n\t\t\"--\",\n\t\tspec.command,\n\t\t...spec.args,\n\t];\n}\n"]}