@ic-reactor/mcp 0.0.0-stage → 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1041) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +77 -0
  4. package/.github/actions/setup-rust-wasm/action.yml +25 -0
  5. package/.github/dependabot.yml +107 -0
  6. package/.github/workflows/auth-latest.yml +67 -0
  7. package/.github/workflows/b3pay-site.yml +49 -0
  8. package/.github/workflows/ci.yml +430 -0
  9. package/.github/workflows/codeql.yml +41 -0
  10. package/.github/workflows/dependency-review.yml +68 -0
  11. package/.github/workflows/docs-check.yml +98 -0
  12. package/.github/workflows/docs.yml +265 -0
  13. package/.github/workflows/e2e.yml +90 -0
  14. package/.github/workflows/examples-published.yml +97 -0
  15. package/.github/workflows/release.yml +326 -0
  16. package/.husky/pre-commit +12 -0
  17. package/.prettierignore +109 -0
  18. package/.prettierrc +6 -0
  19. package/AGENTS.md +103 -0
  20. package/CHANGELOG.md +3268 -0
  21. package/CLAUDE.md +93 -0
  22. package/CNAME +1 -0
  23. package/CODE_OF_CONDUCT.md +7 -0
  24. package/CONTRIBUTING.md +433 -0
  25. package/LICENSE +21 -0
  26. package/README.md +2 -2
  27. package/SECURITY.md +74 -0
  28. package/b3pay-module.json +194 -0
  29. package/docs/.remarkrc.json +10 -0
  30. package/docs/README.md +64 -0
  31. package/docs/astro.config.mjs +205 -0
  32. package/docs/favicon.svg +15 -0
  33. package/docs/package.json +39 -0
  34. package/docs/public/favicon.svg +15 -0
  35. package/docs/public/ic-reactor-social-card.png +0 -0
  36. package/docs/scripts/b3pay-static.mjs +22 -0
  37. package/docs/scripts/check-example-docs.mjs +214 -0
  38. package/docs/scripts/check-links.mjs +212 -0
  39. package/docs/scripts/fix-dist-links.js +125 -0
  40. package/docs/scripts/fix-frontmatter.js +71 -0
  41. package/docs/scripts/remove-redundant-h1.cjs +48 -0
  42. package/docs/scripts/rewrite-typedoc-links.js +110 -0
  43. package/docs/scripts/split-index.js +69 -0
  44. package/docs/src/assets/ic-reactor-hero-dark.svg +23 -0
  45. package/docs/src/assets/ic-reactor-hero-light.svg +23 -0
  46. package/docs/src/assets/ic-reactor-lockup-dark.svg +15 -0
  47. package/docs/src/assets/ic-reactor-lockup-light.svg +15 -0
  48. package/docs/src/assets/ic-reactor-mark-dark.svg +14 -0
  49. package/docs/src/assets/ic-reactor-mark-light.svg +14 -0
  50. package/docs/src/assets/layers/candid-core-layer-icon-dark.svg +4 -0
  51. package/docs/src/assets/layers/candid-core-layer-icon-light.svg +4 -0
  52. package/docs/src/assets/layers/candid-profile-layer-icon-dark.svg +4 -0
  53. package/docs/src/assets/layers/candid-profile-layer-icon-light.svg +4 -0
  54. package/docs/src/assets/layers/ic-reactor-layer-icon-dark.svg +4 -0
  55. package/docs/src/assets/layers/ic-reactor-layer-icon-light.svg +4 -0
  56. package/docs/src/components/LibraryCards.astro +172 -0
  57. package/docs/src/components/LibraryIcon.astro +28 -0
  58. package/docs/src/components/SiteTitle.astro +241 -0
  59. package/docs/src/content/docs/404.mdx +19 -0
  60. package/docs/src/content/docs/build/agent-tools.mdx +593 -0
  61. package/docs/src/content/docs/build/icrc1-wallet.mdx +619 -0
  62. package/docs/src/content/docs/build/ssr-per-user.mdx +552 -0
  63. package/docs/src/content/docs/examples/canister-studio.mdx +95 -0
  64. package/docs/src/content/docs/examples/icrc-ledger.mdx +45 -0
  65. package/docs/src/content/docs/examples/index.mdx +38 -0
  66. package/docs/src/content/docs/examples/next-ssr.mdx +73 -0
  67. package/docs/src/content/docs/examples/node-agent-tool.mdx +80 -0
  68. package/docs/src/content/docs/examples/vite-wallet.mdx +71 -0
  69. package/docs/src/content/docs/guides/auth.mdx +1181 -0
  70. package/docs/src/content/docs/guides/client.mdx +551 -0
  71. package/docs/src/content/docs/guides/deploy.mdx +180 -0
  72. package/docs/src/content/docs/guides/errors.mdx +475 -0
  73. package/docs/src/content/docs/guides/getting-started.mdx +620 -0
  74. package/docs/src/content/docs/guides/reads.mdx +586 -0
  75. package/docs/src/content/docs/guides/ssr.mdx +529 -0
  76. package/docs/src/content/docs/guides/testing.mdx +401 -0
  77. package/docs/src/content/docs/guides/values.mdx +261 -0
  78. package/docs/src/content/docs/guides/vite-plugin.mdx +690 -0
  79. package/docs/src/content/docs/guides/writes.mdx +572 -0
  80. package/docs/src/content/docs/ic-reactor/index.mdx +167 -0
  81. package/docs/src/content/docs/index.mdx +65 -0
  82. package/docs/src/content/docs/migrating-from-3.mdx +535 -0
  83. package/docs/src/content/docs/packages/core.mdx +186 -0
  84. package/docs/src/content/docs/packages/mcp.mdx +163 -0
  85. package/docs/src/content/docs/packages/react.mdx +207 -0
  86. package/docs/src/content/docs/packages/vite-plugin.mdx +73 -0
  87. package/docs/src/content/docs/versioning.mdx +104 -0
  88. package/docs/src/content.config.ts +7 -0
  89. package/docs/src/styles/custom.css +189 -0
  90. package/docs/tsconfig.typedoc.json +26 -0
  91. package/docs/typedoc-entry.ts +17 -0
  92. package/docs/typedoc.json +37 -0
  93. package/e2e/Cargo.lock +646 -0
  94. package/e2e/Cargo.toml +3 -0
  95. package/e2e/README.md +101 -0
  96. package/e2e/frontends/build.mjs +94 -0
  97. package/e2e/frontends/index.html +10 -0
  98. package/e2e/global-setup.ts +65 -0
  99. package/e2e/icp.yaml +38 -0
  100. package/e2e/package.json +40 -0
  101. package/e2e/setup.ts +6 -0
  102. package/e2e/src/actor/Cargo.toml +14 -0
  103. package/e2e/src/actor/hello_actor.did +10 -0
  104. package/e2e/src/actor/src/lib.rs +73 -0
  105. package/e2e/src/calls.test.ts +132 -0
  106. package/e2e/src/declarations/hello_actor.envelope.json +175 -0
  107. package/e2e/src/declarations/hello_actor.ts +15 -0
  108. package/e2e/src/errors.test.ts +107 -0
  109. package/e2e/src/frontend-headers.test.ts +253 -0
  110. package/e2e/src/identity.test.ts +61 -0
  111. package/e2e/src/react.test.tsx +59 -0
  112. package/e2e/src/replica.ts +70 -0
  113. package/e2e/src/vite-env.test.ts +108 -0
  114. package/e2e/test.sh +47 -0
  115. package/e2e/tsconfig.json +20 -0
  116. package/e2e/tsconfig.typecheck.json +9 -0
  117. package/e2e/vitest.config.ts +22 -0
  118. package/eslint.config.mjs +100 -0
  119. package/evals/PREREGISTRATION.md +1346 -0
  120. package/evals/README.md +756 -0
  121. package/evals/conditions/thin/condition.json +8 -0
  122. package/evals/conditions/thin/docs/candid-core-cli.README.md +90 -0
  123. package/evals/conditions/thin/docs/candid-core-schema.README.md +339 -0
  124. package/evals/conditions/thin/package.json +25 -0
  125. package/evals/conditions/thin/starter/src/generated/icrc1.ts +57 -0
  126. package/evals/conditions/thin-guide/condition.json +10 -0
  127. package/evals/conditions/thin-guide/docs/llms.txt +292 -0
  128. package/evals/conditions/v3/condition.json +9 -0
  129. package/evals/conditions/v3/docs/ic-reactor-core.llms.txt +232 -0
  130. package/evals/conditions/v3/docs/ic-reactor-react.llms.txt +280 -0
  131. package/evals/conditions/v3/docs/llms-full.txt +1053 -0
  132. package/evals/conditions/v3/package.json +26 -0
  133. package/evals/conditions/v3/starter/src/declarations/icrc1.did.d.ts +95 -0
  134. package/evals/conditions/v3/starter/src/declarations/icrc1.did.js +140 -0
  135. package/evals/conditions/v4/PORTING.md +384 -0
  136. package/evals/conditions/v4/README.md +106 -0
  137. package/evals/conditions/v4/condition.json +5 -0
  138. package/evals/conditions/v4/docs/llms.txt +323 -0
  139. package/evals/conditions/v4/package.json +27 -0
  140. package/evals/conditions/v4/starter/src/generated/icrc1.ts +78 -0
  141. package/evals/conditions/v4-proto/condition.json +6 -0
  142. package/evals/conditions/v4-proto/docs/llms.txt +291 -0
  143. package/evals/conditions/v4-proto/lib/SHIM-NOTES.md +20 -0
  144. package/evals/conditions/v4-proto/lib/package.json +27 -0
  145. package/evals/conditions/v4-proto/lib/src/client.ts +471 -0
  146. package/evals/conditions/v4-proto/lib/src/errors.ts +214 -0
  147. package/evals/conditions/v4-proto/lib/src/index.ts +36 -0
  148. package/evals/conditions/v4-proto/lib/src/principal.ts +175 -0
  149. package/evals/conditions/v4-proto/lib/src/service.ts +69 -0
  150. package/evals/conditions/v4-proto/lib/src/types.ts +174 -0
  151. package/evals/conditions/v4-proto/lib/test/runtime.test.ts +170 -0
  152. package/evals/conditions/v4-proto/lib/test/tsconfig.json +10 -0
  153. package/evals/conditions/v4-proto/lib/test/types.test-d.ts +145 -0
  154. package/evals/conditions/v4-proto/lib/test/vitest.config.mjs +8 -0
  155. package/evals/conditions/v4-proto/lib/tsconfig.json +17 -0
  156. package/evals/conditions/v4-proto/package.json +26 -0
  157. package/evals/conditions/v4-proto/starter/src/generated/icrc1.service.ts +19 -0
  158. package/evals/conditions/v4-proto/starter/src/generated/icrc1.ts +57 -0
  159. package/evals/drive.mjs +1221 -0
  160. package/evals/gate.mjs +161 -0
  161. package/evals/harness/aggregate.mjs +534 -0
  162. package/evals/harness/aggregate.test.mjs +344 -0
  163. package/evals/harness/assemble.mjs +216 -0
  164. package/evals/harness/assemble.test.mjs +143 -0
  165. package/evals/harness/auth.mjs +91 -0
  166. package/evals/harness/check-docs.mjs +157 -0
  167. package/evals/harness/declarations/icrc1.did.d.ts +95 -0
  168. package/evals/harness/declarations/icrc1.did.js +140 -0
  169. package/evals/harness/drive-auth.test.mjs +584 -0
  170. package/evals/harness/drive.test.mjs +509 -0
  171. package/evals/harness/fake-auth.ts +67 -0
  172. package/evals/harness/fake-ledger.ts +178 -0
  173. package/evals/harness/gate-plan.mjs +104 -0
  174. package/evals/harness/gate-plan.test.mjs +243 -0
  175. package/evals/harness/icrc1.did +88 -0
  176. package/evals/harness/judge.mjs +63 -0
  177. package/evals/harness/judge.test.mjs +81 -0
  178. package/evals/harness/leak-scan.mjs +1373 -0
  179. package/evals/harness/leak-scan.test.mjs +1300 -0
  180. package/evals/harness/probes/amounts.ts +14 -0
  181. package/evals/harness/probes/node-tool.probe.ts +48 -0
  182. package/evals/harness/probes/probe.mjs +149 -0
  183. package/evals/harness/probes/react-wallet.probe.ts +96 -0
  184. package/evals/harness/public/support/auth.ts +38 -0
  185. package/evals/harness/public/support/fake-replica.d.ts +28 -0
  186. package/evals/harness/public/support/fake-replica.js +434 -0
  187. package/evals/harness/public/support/ledger.idl.d.ts +95 -0
  188. package/evals/harness/public/support/ledger.idl.js +140 -0
  189. package/evals/harness/public/support/world.ts +179 -0
  190. package/evals/harness/runs.mjs +139 -0
  191. package/evals/harness/sandbox.mjs +88 -0
  192. package/evals/harness/sandbox.test.mjs +134 -0
  193. package/evals/harness/ship.mjs +264 -0
  194. package/evals/harness/ship.test.mjs +552 -0
  195. package/evals/harness/world.ts +291 -0
  196. package/evals/package.json +26 -0
  197. package/evals/pnpm-lock.yaml +1537 -0
  198. package/evals/pnpm-workspace.yaml +12 -0
  199. package/evals/results/2026-09-30T14-22-05-924Z/pilot.json +384 -0
  200. package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.json +404 -0
  201. package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.rescored.json +404 -0
  202. package/evals/results/2026-09-30T14-22-05-924Z/plan.json +240 -0
  203. package/evals/results/2026-09-30T14-22-05-924Z/summary.json +1800 -0
  204. package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.json +1843 -0
  205. package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.rescored.json +2100 -0
  206. package/evals/results/2026-09-30T15-48-23-343Z/pilot.json +404 -0
  207. package/evals/results/2026-09-30T15-48-23-343Z/pilot.rescored.json +404 -0
  208. package/evals/results/2026-09-30T15-48-23-343Z/plan.json +242 -0
  209. package/evals/results/2026-09-30T15-48-23-343Z/summary.json +1843 -0
  210. package/evals/results/2026-09-30T15-48-23-343Z/summary.rescored.json +2195 -0
  211. package/evals/results/2026-10-03-beta1-gate/pilot.json +162 -0
  212. package/evals/results/2026-10-03-beta1-gate/plan.json +141 -0
  213. package/evals/results/2026-10-03-beta1-gate/summary.json +983 -0
  214. package/evals/results/2026-10-05-ga-gate/contaminated-run.json +55 -0
  215. package/evals/results/2026-10-05-ga-gate/pilot.json +162 -0
  216. package/evals/results/2026-10-05-ga-gate/plan.json +457 -0
  217. package/evals/results/2026-10-05-ga-gate/summary.json +1037 -0
  218. package/evals/results/2026-10-05-ga-gate/summary.rescanned.json +1037 -0
  219. package/evals/results/README.md +31 -0
  220. package/evals/runs-pilot.log +150 -0
  221. package/evals/runs-pilot2.log +150 -0
  222. package/evals/score.mjs +256 -0
  223. package/evals/setup.mjs +547 -0
  224. package/evals/tasks/node-tool/faulty/thin-accepts-exponent/meta.json +5 -0
  225. package/evals/tasks/node-tool/faulty/thin-accepts-exponent/src/index.ts +121 -0
  226. package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/meta.json +5 -0
  227. package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/src/index.ts +116 -0
  228. package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/meta.json +9 -0
  229. package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/src/index.ts +118 -0
  230. package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/meta.json +5 -0
  231. package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/src/index.ts +118 -0
  232. package/evals/tasks/node-tool/faulty/thin-ignores-root-key/meta.json +5 -0
  233. package/evals/tasks/node-tool/faulty/thin-ignores-root-key/src/index.ts +117 -0
  234. package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/meta.json +9 -0
  235. package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/src/index.ts +119 -0
  236. package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/meta.json +9 -0
  237. package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/src/index.ts +118 -0
  238. package/evals/tasks/node-tool/faulty/thin-number-amount/meta.json +10 -0
  239. package/evals/tasks/node-tool/faulty/thin-number-amount/src/index.ts +117 -0
  240. package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/meta.json +5 -0
  241. package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/src/index.ts +120 -0
  242. package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/meta.json +5 -0
  243. package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/src/index.ts +118 -0
  244. package/evals/tasks/node-tool/faulty/v3-retry-call-errors/meta.json +11 -0
  245. package/evals/tasks/node-tool/faulty/v3-retry-call-errors/src/index.ts +149 -0
  246. package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/meta.json +5 -0
  247. package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/src/index.ts +71 -0
  248. package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/meta.json +9 -0
  249. package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/src/index.ts +84 -0
  250. package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/meta.json +9 -0
  251. package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/src/index.ts +72 -0
  252. package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/meta.json +5 -0
  253. package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/src/index.ts +72 -0
  254. package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/meta.json +5 -0
  255. package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/src/index.ts +84 -0
  256. package/evals/tasks/node-tool/hidden/node-tool.test.ts +312 -0
  257. package/evals/tasks/node-tool/prompt.md +55 -0
  258. package/evals/tasks/node-tool/prompt.minimal.md +30 -0
  259. package/evals/tasks/node-tool/solutions/thin/reference/src/index.ts +118 -0
  260. package/evals/tasks/node-tool/solutions/thin/reference-module-scope/src/index.ts +145 -0
  261. package/evals/tasks/node-tool/solutions/thin-guide/reference/src/index.ts +118 -0
  262. package/evals/tasks/node-tool/solutions/thin-guide/reference-module-scope/src/index.ts +145 -0
  263. package/evals/tasks/node-tool/solutions/v3/reference/src/index.ts +140 -0
  264. package/evals/tasks/node-tool/solutions/v3/reference-module-scope/src/index.ts +153 -0
  265. package/evals/tasks/node-tool/solutions/v4/reference/src/index.ts +84 -0
  266. package/evals/tasks/node-tool/solutions/v4/reference-module-scope/src/index.ts +105 -0
  267. package/evals/tasks/node-tool/solutions/v4-proto/reference/src/index.ts +72 -0
  268. package/evals/tasks/node-tool/solutions/v4-proto/reference-module-scope/src/index.ts +87 -0
  269. package/evals/tasks/node-tool/starter/src/contract.ts +31 -0
  270. package/evals/tasks/node-tool/starter/src/index.ts +7 -0
  271. package/evals/tasks/node-tool/starter/test/smoke.test.ts +22 -0
  272. package/evals/tasks/node-tool/starter/tsconfig.json +17 -0
  273. package/evals/tasks/node-tool/task.json +63 -0
  274. package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/meta.json +5 -0
  275. package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/src/Wallet.tsx +216 -0
  276. package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/meta.json +5 -0
  277. package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/src/Wallet.tsx +212 -0
  278. package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/meta.json +5 -0
  279. package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/src/Wallet.tsx +210 -0
  280. package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/meta.json +8 -0
  281. package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/src/Wallet.tsx +213 -0
  282. package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/meta.json +9 -0
  283. package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/src/Wallet.tsx +213 -0
  284. package/evals/tasks/react-wallet/faulty/thin-number-balance/meta.json +5 -0
  285. package/evals/tasks/react-wallet/faulty/thin-number-balance/src/Wallet.tsx +213 -0
  286. package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/meta.json +5 -0
  287. package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/src/Wallet.tsx +254 -0
  288. package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/meta.json +5 -0
  289. package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/src/Wallet.tsx +215 -0
  290. package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/meta.json +5 -0
  291. package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/src/Wallet.tsx +214 -0
  292. package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/meta.json +5 -0
  293. package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/src/Wallet.tsx +224 -0
  294. package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/meta.json +12 -0
  295. package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/src/Wallet.tsx +225 -0
  296. package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/meta.json +11 -0
  297. package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/src/Wallet.tsx +182 -0
  298. package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/meta.json +5 -0
  299. package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/src/Wallet.tsx +160 -0
  300. package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/meta.json +5 -0
  301. package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/src/Wallet.tsx +166 -0
  302. package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/meta.json +5 -0
  303. package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/src/Wallet.tsx +161 -0
  304. package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/meta.json +5 -0
  305. package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/src/Wallet.tsx +163 -0
  306. package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/meta.json +12 -0
  307. package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/src/Wallet.tsx +162 -0
  308. package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/meta.json +5 -0
  309. package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/src/Wallet.tsx +163 -0
  310. package/evals/tasks/react-wallet/faulty/v4-retry-spread/meta.json +12 -0
  311. package/evals/tasks/react-wallet/faulty/v4-retry-spread/src/Wallet.tsx +163 -0
  312. package/evals/tasks/react-wallet/faulty/v4-status-not-idle/meta.json +5 -0
  313. package/evals/tasks/react-wallet/faulty/v4-status-not-idle/src/Wallet.tsx +162 -0
  314. package/evals/tasks/react-wallet/hidden/react-wallet.test.ts +536 -0
  315. package/evals/tasks/react-wallet/prompt.md +88 -0
  316. package/evals/tasks/react-wallet/prompt.minimal.md +51 -0
  317. package/evals/tasks/react-wallet/solutions/thin/reference/src/Wallet.tsx +213 -0
  318. package/evals/tasks/react-wallet/solutions/thin/reference-module-scope/src/Wallet.tsx +227 -0
  319. package/evals/tasks/react-wallet/solutions/thin-guide/reference/src/Wallet.tsx +213 -0
  320. package/evals/tasks/react-wallet/solutions/thin-guide/reference-module-scope/src/Wallet.tsx +227 -0
  321. package/evals/tasks/react-wallet/solutions/v3/reference/src/Wallet.tsx +224 -0
  322. package/evals/tasks/react-wallet/solutions/v3/reference-module-scope/src/Wallet.tsx +230 -0
  323. package/evals/tasks/react-wallet/solutions/v4/reference/src/Wallet.tsx +160 -0
  324. package/evals/tasks/react-wallet/solutions/v4/reference-module-scope/src/Wallet.tsx +178 -0
  325. package/evals/tasks/react-wallet/solutions/v4-proto/reference/src/Wallet.tsx +161 -0
  326. package/evals/tasks/react-wallet/solutions/v4-proto/reference-module-scope/src/Wallet.tsx +161 -0
  327. package/evals/tasks/react-wallet/starter/src/Wallet.tsx +12 -0
  328. package/evals/tasks/react-wallet/starter/src/auth.ts +16 -0
  329. package/evals/tasks/react-wallet/starter/src/config.ts +14 -0
  330. package/evals/tasks/react-wallet/starter/src/ii-auth.ts +43 -0
  331. package/evals/tasks/react-wallet/starter/src/main.tsx +15 -0
  332. package/evals/tasks/react-wallet/starter/test/smoke.test.ts +34 -0
  333. package/evals/tasks/react-wallet/starter/tsconfig.json +18 -0
  334. package/evals/tasks/react-wallet/task.json +84 -0
  335. package/examples/canister-studio/.stackblitzrc +4 -0
  336. package/examples/canister-studio/README.md +183 -0
  337. package/examples/canister-studio/did/ckbtc_minter.did +806 -0
  338. package/examples/canister-studio/did/icp_ledger.did +586 -0
  339. package/examples/canister-studio/did/icrc1.did +88 -0
  340. package/examples/canister-studio/package.json +45 -0
  341. package/examples/canister-studio/scripts/smoke.ts +188 -0
  342. package/examples/canister-studio/src/calls/CacheKeyPanel.tsx +104 -0
  343. package/examples/canister-studio/src/calls/ConfirmWrite.tsx +265 -0
  344. package/examples/canister-studio/src/calls/ErrorPanel.test.tsx +113 -0
  345. package/examples/canister-studio/src/calls/ErrorPanel.tsx +214 -0
  346. package/examples/canister-studio/src/calls/MethodRunner.test.tsx +354 -0
  347. package/examples/canister-studio/src/calls/MethodRunner.tsx +57 -0
  348. package/examples/canister-studio/src/calls/ReadRunner.tsx +244 -0
  349. package/examples/canister-studio/src/calls/ResendPanel.tsx +165 -0
  350. package/examples/canister-studio/src/calls/ShareLink.tsx +59 -0
  351. package/examples/canister-studio/src/calls/WriteRunner.test.tsx +678 -0
  352. package/examples/canister-studio/src/calls/WriteRunner.tsx +670 -0
  353. package/examples/canister-studio/src/calls/dedupe.ts +189 -0
  354. package/examples/canister-studio/src/calls/error-info.ts +168 -0
  355. package/examples/canister-studio/src/calls/history.test.ts +284 -0
  356. package/examples/canister-studio/src/calls/history.ts +287 -0
  357. package/examples/canister-studio/src/calls/kinds.ts +68 -0
  358. package/examples/canister-studio/src/calls/prepare.ts +32 -0
  359. package/examples/canister-studio/src/calls/query-options.ts +30 -0
  360. package/examples/canister-studio/src/calls/read.ts +47 -0
  361. package/examples/canister-studio/src/calls/readback.ts +100 -0
  362. package/examples/canister-studio/src/calls/record.ts +123 -0
  363. package/examples/canister-studio/src/calls/safe-calls.test.ts +299 -0
  364. package/examples/canister-studio/src/calls/share.ts +46 -0
  365. package/examples/canister-studio/src/calls/testing/doubles.tsx +108 -0
  366. package/examples/canister-studio/src/calls/testing/fixtures.ts +185 -0
  367. package/examples/canister-studio/src/calls/useCanisterHandle.ts +35 -0
  368. package/examples/canister-studio/src/canisters/ckbtc_minter.envelope.json +4228 -0
  369. package/examples/canister-studio/src/canisters/ckbtc_minter.ts +868 -0
  370. package/examples/canister-studio/src/canisters/icp_ledger.envelope.json +3454 -0
  371. package/examples/canister-studio/src/canisters/icp_ledger.ts +584 -0
  372. package/examples/canister-studio/src/canisters/icrc1.envelope.json +840 -0
  373. package/examples/canister-studio/src/canisters/icrc1.ts +78 -0
  374. package/examples/canister-studio/src/components/ActionButton.tsx +32 -0
  375. package/examples/canister-studio/src/components/AppHeader.tsx +83 -0
  376. package/examples/canister-studio/src/components/AppShell.tsx +29 -0
  377. package/examples/canister-studio/src/components/AuthButton.tsx +79 -0
  378. package/examples/canister-studio/src/components/Badge.tsx +79 -0
  379. package/examples/canister-studio/src/components/CanisterHeader.tsx +200 -0
  380. package/examples/canister-studio/src/components/CanisterJump.tsx +130 -0
  381. package/examples/canister-studio/src/components/Card.tsx +64 -0
  382. package/examples/canister-studio/src/components/Code.tsx +101 -0
  383. package/examples/canister-studio/src/components/CopyButton.tsx +126 -0
  384. package/examples/canister-studio/src/components/Disclosure.tsx +34 -0
  385. package/examples/canister-studio/src/components/EmptyState.tsx +34 -0
  386. package/examples/canister-studio/src/components/MethodList.test.tsx +137 -0
  387. package/examples/canister-studio/src/components/MethodList.tsx +244 -0
  388. package/examples/canister-studio/src/components/Notice.tsx +36 -0
  389. package/examples/canister-studio/src/components/ProvenanceBadge.tsx +44 -0
  390. package/examples/canister-studio/src/components/RouteError.tsx +42 -0
  391. package/examples/canister-studio/src/components/SectionBoundary.tsx +76 -0
  392. package/examples/canister-studio/src/components/Spinner.tsx +33 -0
  393. package/examples/canister-studio/src/components/Tabs.tsx +96 -0
  394. package/examples/canister-studio/src/components/canister/CanisterLoadFailure.test.tsx +56 -0
  395. package/examples/canister-studio/src/components/canister/CanisterLoadFailure.tsx +165 -0
  396. package/examples/canister-studio/src/components/canister/CanisterNotFound.tsx +59 -0
  397. package/examples/canister-studio/src/components/canister/CanisterPending.tsx +33 -0
  398. package/examples/canister-studio/src/components/canister/MethodOverview.tsx +93 -0
  399. package/examples/canister-studio/src/components/canister/MethodWorkspace.test.tsx +201 -0
  400. package/examples/canister-studio/src/components/canister/MethodWorkspace.tsx +187 -0
  401. package/examples/canister-studio/src/components/canister/shared-call.test.ts +186 -0
  402. package/examples/canister-studio/src/components/canister/shared-call.ts +133 -0
  403. package/examples/canister-studio/src/components/canister/shared-link-loader.test.ts +116 -0
  404. package/examples/canister-studio/src/components/canister/use-canister-page.ts +44 -0
  405. package/examples/canister-studio/src/components/canister/workspace-key.test.ts +27 -0
  406. package/examples/canister-studio/src/components/canister/workspace-key.ts +36 -0
  407. package/examples/canister-studio/src/components/canister-id-input.ts +69 -0
  408. package/examples/canister-studio/src/components/canister-names.ts +11 -0
  409. package/examples/canister-studio/src/components/download.ts +34 -0
  410. package/examples/canister-studio/src/components/fallbacks.tsx +33 -0
  411. package/examples/canister-studio/src/components/helpers.test.ts +107 -0
  412. package/examples/canister-studio/src/components/history/HistoryList.tsx +284 -0
  413. package/examples/canister-studio/src/components/home/FeaturedLedger.tsx +149 -0
  414. package/examples/canister-studio/src/components/home/OpenCanister.tsx +99 -0
  415. package/examples/canister-studio/src/components/home/featured.test.tsx +92 -0
  416. package/examples/canister-studio/src/components/home/featured.ts +64 -0
  417. package/examples/canister-studio/src/components/interface/CandidTab.tsx +38 -0
  418. package/examples/canister-studio/src/components/interface/CompatReport.tsx +92 -0
  419. package/examples/canister-studio/src/components/interface/CompatibilityTab.tsx +190 -0
  420. package/examples/canister-studio/src/components/interface/ProjectTab.tsx +196 -0
  421. package/examples/canister-studio/src/components/interface/TypeScriptTab.tsx +114 -0
  422. package/examples/canister-studio/src/components/interface/tools.test.tsx +169 -0
  423. package/examples/canister-studio/src/components/modes.ts +31 -0
  424. package/examples/canister-studio/src/components/playground/MethodPicker.tsx +67 -0
  425. package/examples/canister-studio/src/components/playground/PlaygroundForms.tsx +112 -0
  426. package/examples/canister-studio/src/components/playground/PlaygroundTools.tsx +219 -0
  427. package/examples/canister-studio/src/components/playground/sample.ts +43 -0
  428. package/examples/canister-studio/src/components/playground/target.test.tsx +86 -0
  429. package/examples/canister-studio/src/components/primitives.test.tsx +121 -0
  430. package/examples/canister-studio/src/components/provenance.ts +38 -0
  431. package/examples/canister-studio/src/components/route-render.test.tsx +71 -0
  432. package/examples/canister-studio/src/components/use-task.ts +56 -0
  433. package/examples/canister-studio/src/components/wallet/BalancePanel.tsx +110 -0
  434. package/examples/canister-studio/src/components/wallet/LedgerSummary.tsx +63 -0
  435. package/examples/canister-studio/src/components/wallet/MinterPanel.tsx +255 -0
  436. package/examples/canister-studio/src/components/wallet/SendPanel.test.tsx +297 -0
  437. package/examples/canister-studio/src/components/wallet/SendPanel.tsx +446 -0
  438. package/examples/canister-studio/src/components/wallet/ledgers.test.ts +36 -0
  439. package/examples/canister-studio/src/components/wallet/ledgers.ts +111 -0
  440. package/examples/canister-studio/src/forms/JsonPanel.tsx +157 -0
  441. package/examples/canister-studio/src/forms/MethodForm.test.tsx +369 -0
  442. package/examples/canister-studio/src/forms/MethodForm.tsx +368 -0
  443. package/examples/canister-studio/src/forms/TypedForm.tsx +55 -0
  444. package/examples/canister-studio/src/forms/context.ts +62 -0
  445. package/examples/canister-studio/src/forms/convert.ts +131 -0
  446. package/examples/canister-studio/src/forms/edge.test.ts +252 -0
  447. package/examples/canister-studio/src/forms/engine.test.ts +358 -0
  448. package/examples/canister-studio/src/forms/engine.ts +1492 -0
  449. package/examples/canister-studio/src/forms/fields/BytesField.tsx +210 -0
  450. package/examples/canister-studio/src/forms/fields/Field.tsx +106 -0
  451. package/examples/canister-studio/src/forms/fields/FieldShell.tsx +101 -0
  452. package/examples/canister-studio/src/forms/fields/Group.tsx +74 -0
  453. package/examples/canister-studio/src/forms/fields/ListField.tsx +256 -0
  454. package/examples/canister-studio/src/forms/fields/Segmented.tsx +78 -0
  455. package/examples/canister-studio/src/forms/fields/TimestampField.tsx +108 -0
  456. package/examples/canister-studio/src/forms/fields/UnitsField.tsx +119 -0
  457. package/examples/canister-studio/src/forms/fields/composites.tsx +389 -0
  458. package/examples/canister-studio/src/forms/fields/leaves.tsx +350 -0
  459. package/examples/canister-studio/src/forms/fields.test.tsx +396 -0
  460. package/examples/canister-studio/src/forms/helpers.test.ts +225 -0
  461. package/examples/canister-studio/src/forms/hints.ts +128 -0
  462. package/examples/canister-studio/src/forms/json.ts +119 -0
  463. package/examples/canister-studio/src/forms/parsing.test.ts +217 -0
  464. package/examples/canister-studio/src/forms/useMethodForm.ts +251 -0
  465. package/examples/canister-studio/src/interface/browser.test.ts +23 -0
  466. package/examples/canister-studio/src/interface/browser.ts +153 -0
  467. package/examples/canister-studio/src/interface/cache.ts +68 -0
  468. package/examples/canister-studio/src/interface/cli-cache.test.ts +155 -0
  469. package/examples/canister-studio/src/interface/cli-cache.ts +125 -0
  470. package/examples/canister-studio/src/interface/compile.server.test.ts +198 -0
  471. package/examples/canister-studio/src/interface/compile.server.ts +262 -0
  472. package/examples/canister-studio/src/interface/compile.test.ts +108 -0
  473. package/examples/canister-studio/src/interface/compile.ts +305 -0
  474. package/examples/canister-studio/src/interface/docs.test.ts +75 -0
  475. package/examples/canister-studio/src/interface/docs.ts +100 -0
  476. package/examples/canister-studio/src/interface/functions.test.ts +42 -0
  477. package/examples/canister-studio/src/interface/functions.ts +200 -0
  478. package/examples/canister-studio/src/interface/live-candid.server.ts +91 -0
  479. package/examples/canister-studio/src/interface/live-loader.test.ts +515 -0
  480. package/examples/canister-studio/src/interface/live-loader.ts +364 -0
  481. package/examples/canister-studio/src/interface/load.test.ts +328 -0
  482. package/examples/canister-studio/src/interface/load.ts +249 -0
  483. package/examples/canister-studio/src/interface/read-candid.test.ts +185 -0
  484. package/examples/canister-studio/src/interface/read-candid.ts +233 -0
  485. package/examples/canister-studio/src/interface/server-result.ts +42 -0
  486. package/examples/canister-studio/src/interface/typed.ts +68 -0
  487. package/examples/canister-studio/src/interface/types.ts +121 -0
  488. package/examples/canister-studio/src/lib/format.test.ts +107 -0
  489. package/examples/canister-studio/src/lib/format.ts +139 -0
  490. package/examples/canister-studio/src/lib/hex.test.ts +43 -0
  491. package/examples/canister-studio/src/lib/hex.ts +33 -0
  492. package/examples/canister-studio/src/lib/ids.test.ts +70 -0
  493. package/examples/canister-studio/src/lib/ids.ts +93 -0
  494. package/examples/canister-studio/src/lib/reactor.test.ts +55 -0
  495. package/examples/canister-studio/src/lib/reactor.ts +55 -0
  496. package/examples/canister-studio/src/lib/registry.test.ts +74 -0
  497. package/examples/canister-studio/src/lib/registry.ts +238 -0
  498. package/examples/canister-studio/src/lib/schema.ts +148 -0
  499. package/examples/canister-studio/src/routeTree.gen.ts +227 -0
  500. package/examples/canister-studio/src/router.test.ts +290 -0
  501. package/examples/canister-studio/src/router.tsx +127 -0
  502. package/examples/canister-studio/src/routes/__root.tsx +58 -0
  503. package/examples/canister-studio/src/routes/canister.$canisterId.index.tsx +172 -0
  504. package/examples/canister-studio/src/routes/canister.$canisterId.interface.tsx +98 -0
  505. package/examples/canister-studio/src/routes/canister.$canisterId.tsx +91 -0
  506. package/examples/canister-studio/src/routes/history.tsx +55 -0
  507. package/examples/canister-studio/src/routes/index.tsx +169 -0
  508. package/examples/canister-studio/src/routes/playground.tsx +286 -0
  509. package/examples/canister-studio/src/routes/safety.tsx +57 -0
  510. package/examples/canister-studio/src/routes/wallet.tsx +213 -0
  511. package/examples/canister-studio/src/safety/LabTransferForm.tsx +84 -0
  512. package/examples/canister-studio/src/safety/RequestLog.tsx +79 -0
  513. package/examples/canister-studio/src/safety/SafetyLab.test.tsx +89 -0
  514. package/examples/canister-studio/src/safety/SafetyLab.tsx +140 -0
  515. package/examples/canister-studio/src/safety/ScenarioCard.tsx +262 -0
  516. package/examples/canister-studio/src/safety/lab-run.ts +283 -0
  517. package/examples/canister-studio/src/safety/mock-ledger.test.ts +99 -0
  518. package/examples/canister-studio/src/safety/mock-ledger.ts +263 -0
  519. package/examples/canister-studio/src/safety/scenarios.test.ts +157 -0
  520. package/examples/canister-studio/src/safety/scenarios.ts +924 -0
  521. package/examples/canister-studio/src/styles.css +3453 -0
  522. package/examples/canister-studio/src/styles.test.ts +46 -0
  523. package/examples/canister-studio/src/values/CallPreview.test.tsx +86 -0
  524. package/examples/canister-studio/src/values/CallPreview.tsx +148 -0
  525. package/examples/canister-studio/src/values/ErrValueView.tsx +66 -0
  526. package/examples/canister-studio/src/values/Inspector.tsx +133 -0
  527. package/examples/canister-studio/src/values/ReplyView.test.tsx +341 -0
  528. package/examples/canister-studio/src/values/ReplyView.tsx +164 -0
  529. package/examples/canister-studio/src/values/candid-text.test.ts +516 -0
  530. package/examples/canister-studio/src/values/candid-text.ts +479 -0
  531. package/examples/canister-studio/src/values/hints.ts +58 -0
  532. package/examples/canister-studio/src/values/json.test.ts +66 -0
  533. package/examples/canister-studio/src/values/json.ts +31 -0
  534. package/examples/canister-studio/src/values/leaves.tsx +309 -0
  535. package/examples/canister-studio/src/values/schema.test.ts +123 -0
  536. package/examples/canister-studio/src/values/schema.ts +78 -0
  537. package/examples/canister-studio/src/values/test-router.tsx +21 -0
  538. package/examples/canister-studio/src/values/tree.tsx +670 -0
  539. package/examples/canister-studio/src/values/use-sha256.ts +29 -0
  540. package/examples/canister-studio/tsconfig.json +20 -0
  541. package/examples/canister-studio/vite.config.ts +50 -0
  542. package/examples/canister-studio/vitest.config.ts +20 -0
  543. package/examples/icrc-ledger/.stackblitzrc +4 -0
  544. package/examples/icrc-ledger/README.md +100 -0
  545. package/examples/icrc-ledger/ckbtc_minter.did +12 -0
  546. package/examples/icrc-ledger/icp_ledger.did +147 -0
  547. package/examples/icrc-ledger/icrc1.did +88 -0
  548. package/examples/icrc-ledger/index.html +13 -0
  549. package/examples/icrc-ledger/package.json +40 -0
  550. package/examples/icrc-ledger/scripts/smoke.ts +151 -0
  551. package/examples/icrc-ledger/src/App.tsx +71 -0
  552. package/examples/icrc-ledger/src/ErrorPanel.tsx +105 -0
  553. package/examples/icrc-ledger/src/MainnetTab.tsx +450 -0
  554. package/examples/icrc-ledger/src/SandboxTab.test.tsx +379 -0
  555. package/examples/icrc-ledger/src/SandboxTab.tsx +761 -0
  556. package/examples/icrc-ledger/src/blocks.test.ts +247 -0
  557. package/examples/icrc-ledger/src/blocks.ts +140 -0
  558. package/examples/icrc-ledger/src/canisters/ckbtc_minter.envelope.json +96 -0
  559. package/examples/icrc-ledger/src/canisters/ckbtc_minter.ts +16 -0
  560. package/examples/icrc-ledger/src/canisters/icp_ledger.envelope.json +690 -0
  561. package/examples/icrc-ledger/src/canisters/icp_ledger.ts +223 -0
  562. package/examples/icrc-ledger/src/canisters/icrc1.envelope.json +840 -0
  563. package/examples/icrc-ledger/src/canisters/icrc1.ts +78 -0
  564. package/examples/icrc-ledger/src/format.test.ts +41 -0
  565. package/examples/icrc-ledger/src/format.ts +68 -0
  566. package/examples/icrc-ledger/src/ledger.ts +15 -0
  567. package/examples/icrc-ledger/src/main.tsx +10 -0
  568. package/examples/icrc-ledger/src/minter.ts +16 -0
  569. package/examples/icrc-ledger/src/sandbox.test.ts +464 -0
  570. package/examples/icrc-ledger/src/sandbox.ts +281 -0
  571. package/examples/icrc-ledger/src/styles.css +391 -0
  572. package/examples/icrc-ledger/src/transfer-form.test.ts +62 -0
  573. package/examples/icrc-ledger/src/transfer-form.ts +97 -0
  574. package/examples/icrc-ledger/tsconfig.json +19 -0
  575. package/examples/icrc-ledger/vite.config.ts +44 -0
  576. package/examples/icrc-ledger/vitest.config.ts +12 -0
  577. package/examples/next-ssr/.stackblitzrc +4 -0
  578. package/examples/next-ssr/README.md +112 -0
  579. package/examples/next-ssr/icrc1.did +88 -0
  580. package/examples/next-ssr/next.config.ts +10 -0
  581. package/examples/next-ssr/package.json +41 -0
  582. package/examples/next-ssr/scripts/smoke.ts +169 -0
  583. package/examples/next-ssr/src/app/account/CertifiedBalances.tsx +58 -0
  584. package/examples/next-ssr/src/app/account/account.test.tsx +165 -0
  585. package/examples/next-ssr/src/app/account/page.tsx +82 -0
  586. package/examples/next-ssr/src/app/api/balance/[ledger]/[principal]/route.ts +10 -0
  587. package/examples/next-ssr/src/app/globals.css +278 -0
  588. package/examples/next-ssr/src/app/layout.tsx +34 -0
  589. package/examples/next-ssr/src/app/page.tsx +46 -0
  590. package/examples/next-ssr/src/app/providers.browser.test.tsx +127 -0
  591. package/examples/next-ssr/src/app/providers.test.tsx +73 -0
  592. package/examples/next-ssr/src/app/providers.tsx +60 -0
  593. package/examples/next-ssr/src/canisters/icrc1.envelope.json +840 -0
  594. package/examples/next-ssr/src/canisters/icrc1.ts +78 -0
  595. package/examples/next-ssr/src/components/BalanceTable.tsx +43 -0
  596. package/examples/next-ssr/src/components/LedgerCard.test.tsx +96 -0
  597. package/examples/next-ssr/src/components/LedgerCard.tsx +230 -0
  598. package/examples/next-ssr/src/components/LedgerError.tsx +32 -0
  599. package/examples/next-ssr/src/components/LedgerSections.tsx +22 -0
  600. package/examples/next-ssr/src/components/MyBalances.test.tsx +253 -0
  601. package/examples/next-ssr/src/components/MyBalances.tsx +129 -0
  602. package/examples/next-ssr/src/components/SessionBadge.tsx +18 -0
  603. package/examples/next-ssr/src/components/hydration.test.tsx +259 -0
  604. package/examples/next-ssr/src/components/server-data.ts +10 -0
  605. package/examples/next-ssr/src/format.ts +46 -0
  606. package/examples/next-ssr/src/ledgers.ts +38 -0
  607. package/examples/next-ssr/src/server/balance-route.test.ts +151 -0
  608. package/examples/next-ssr/src/server/balance-route.ts +157 -0
  609. package/examples/next-ssr/src/server/error-summary.test.ts +34 -0
  610. package/examples/next-ssr/src/server/error-summary.ts +37 -0
  611. package/examples/next-ssr/src/server/parse-owner.test.ts +39 -0
  612. package/examples/next-ssr/src/server/parse-owner.ts +34 -0
  613. package/examples/next-ssr/src/server/prefetch-ledgers.test.ts +137 -0
  614. package/examples/next-ssr/src/server/prefetch-ledgers.ts +70 -0
  615. package/examples/next-ssr/src/server/read-balances.test.ts +103 -0
  616. package/examples/next-ssr/src/server/read-balances.ts +78 -0
  617. package/examples/next-ssr/src/server/request-client.rsc.test.tsx +110 -0
  618. package/examples/next-ssr/src/server/request-client.ts +24 -0
  619. package/examples/next-ssr/src/testing/mock-ledgers.ts +115 -0
  620. package/examples/next-ssr/src/testing/react-server-dom-turbopack.d.ts +15 -0
  621. package/examples/next-ssr/src/testing/setup.ts +10 -0
  622. package/examples/next-ssr/tsconfig.json +31 -0
  623. package/examples/next-ssr/vitest.config.ts +59 -0
  624. package/examples/node-agent-tool/.stackblitzrc +4 -0
  625. package/examples/node-agent-tool/README.md +165 -0
  626. package/examples/node-agent-tool/icp.yaml +11 -0
  627. package/examples/node-agent-tool/icrc1.did +88 -0
  628. package/examples/node-agent-tool/package.json +32 -0
  629. package/examples/node-agent-tool/src/canisters/icrc1.envelope.json +840 -0
  630. package/examples/node-agent-tool/src/canisters/icrc1.ts +78 -0
  631. package/examples/node-agent-tool/src/cli.test.ts +64 -0
  632. package/examples/node-agent-tool/src/cli.ts +23 -0
  633. package/examples/node-agent-tool/src/command-line.ts +159 -0
  634. package/examples/node-agent-tool/src/commands/balance.ts +73 -0
  635. package/examples/node-agent-tool/src/commands/demo.test.ts +51 -0
  636. package/examples/node-agent-tool/src/commands/demo.ts +359 -0
  637. package/examples/node-agent-tool/src/commands/info.ts +69 -0
  638. package/examples/node-agent-tool/src/commands/reads.test.ts +133 -0
  639. package/examples/node-agent-tool/src/commands/resend.test.ts +363 -0
  640. package/examples/node-agent-tool/src/commands/transfer.test.ts +313 -0
  641. package/examples/node-agent-tool/src/commands/transfer.ts +415 -0
  642. package/examples/node-agent-tool/src/commands/watch.test.ts +159 -0
  643. package/examples/node-agent-tool/src/commands/watch.ts +135 -0
  644. package/examples/node-agent-tool/src/commands/whoami.ts +36 -0
  645. package/examples/node-agent-tool/src/context.ts +20 -0
  646. package/examples/node-agent-tool/src/dispatch.ts +70 -0
  647. package/examples/node-agent-tool/src/failure.test.ts +59 -0
  648. package/examples/node-agent-tool/src/failure.ts +176 -0
  649. package/examples/node-agent-tool/src/identity.test.ts +242 -0
  650. package/examples/node-agent-tool/src/identity.ts +228 -0
  651. package/examples/node-agent-tool/src/input.ts +110 -0
  652. package/examples/node-agent-tool/src/ledgers.test.ts +74 -0
  653. package/examples/node-agent-tool/src/ledgers.ts +62 -0
  654. package/examples/node-agent-tool/src/mock-ledger.ts +211 -0
  655. package/examples/node-agent-tool/src/network.test.ts +128 -0
  656. package/examples/node-agent-tool/src/network.ts +101 -0
  657. package/examples/node-agent-tool/src/output.test.ts +208 -0
  658. package/examples/node-agent-tool/src/output.ts +163 -0
  659. package/examples/node-agent-tool/src/run.ts +73 -0
  660. package/examples/node-agent-tool/src/test-kit.ts +167 -0
  661. package/examples/node-agent-tool/tsconfig.json +18 -0
  662. package/examples/node-agent-tool/vitest.config.ts +8 -0
  663. package/examples/vite-wallet/.stackblitzrc +4 -0
  664. package/examples/vite-wallet/Cargo.lock +646 -0
  665. package/examples/vite-wallet/Cargo.toml +5 -0
  666. package/examples/vite-wallet/README.md +107 -0
  667. package/examples/vite-wallet/backend/Cargo.toml +12 -0
  668. package/examples/vite-wallet/backend/backend.did +37 -0
  669. package/examples/vite-wallet/backend/src/lib.rs +133 -0
  670. package/examples/vite-wallet/icp.yaml +44 -0
  671. package/examples/vite-wallet/index.html +13 -0
  672. package/examples/vite-wallet/ledger.did +54 -0
  673. package/examples/vite-wallet/package.json +46 -0
  674. package/examples/vite-wallet/public/.ic-assets.json5 +26 -0
  675. package/examples/vite-wallet/scripts/faucet-args.test.ts +30 -0
  676. package/examples/vite-wallet/scripts/faucet-args.ts +41 -0
  677. package/examples/vite-wallet/scripts/faucet.ts +41 -0
  678. package/examples/vite-wallet/src/AddressBook.test.tsx +191 -0
  679. package/examples/vite-wallet/src/AddressBook.tsx +147 -0
  680. package/examples/vite-wallet/src/App.tsx +53 -0
  681. package/examples/vite-wallet/src/Balance.test.tsx +57 -0
  682. package/examples/vite-wallet/src/Balance.tsx +58 -0
  683. package/examples/vite-wallet/src/EnvironmentPanel.test.tsx +83 -0
  684. package/examples/vite-wallet/src/EnvironmentPanel.tsx +103 -0
  685. package/examples/vite-wallet/src/ErrorNote.tsx +70 -0
  686. package/examples/vite-wallet/src/Profile.test.tsx +145 -0
  687. package/examples/vite-wallet/src/Profile.tsx +92 -0
  688. package/examples/vite-wallet/src/SendIcp.test.tsx +231 -0
  689. package/examples/vite-wallet/src/SendIcp.tsx +244 -0
  690. package/examples/vite-wallet/src/SendToContact.test.tsx +113 -0
  691. package/examples/vite-wallet/src/SendToContact.tsx +102 -0
  692. package/examples/vite-wallet/src/SignIn.test.tsx +106 -0
  693. package/examples/vite-wallet/src/SignIn.tsx +120 -0
  694. package/examples/vite-wallet/src/Wallet.test.tsx +89 -0
  695. package/examples/vite-wallet/src/account-switch.test.tsx +108 -0
  696. package/examples/vite-wallet/src/auth/dev-accounts.test.ts +113 -0
  697. package/examples/vite-wallet/src/auth/dev-accounts.ts +216 -0
  698. package/examples/vite-wallet/src/auth/local-page.test.ts +28 -0
  699. package/examples/vite-wallet/src/auth/local-page.ts +20 -0
  700. package/examples/vite-wallet/src/auth/wallet-auth.test.ts +159 -0
  701. package/examples/vite-wallet/src/auth/wallet-auth.ts +131 -0
  702. package/examples/vite-wallet/src/canisters/backend.envelope.json +275 -0
  703. package/examples/vite-wallet/src/canisters/backend.ts +58 -0
  704. package/examples/vite-wallet/src/canisters/ledger.envelope.json +451 -0
  705. package/examples/vite-wallet/src/canisters/ledger.ts +56 -0
  706. package/examples/vite-wallet/src/client.ts +45 -0
  707. package/examples/vite-wallet/src/main.tsx +18 -0
  708. package/examples/vite-wallet/src/optimistic-contacts.ts +113 -0
  709. package/examples/vite-wallet/src/refusal.ts +17 -0
  710. package/examples/vite-wallet/src/styles.css +282 -0
  711. package/examples/vite-wallet/src/test/test-wallet.tsx +322 -0
  712. package/examples/vite-wallet/src/token.ts +50 -0
  713. package/examples/vite-wallet/src/transfer.test.ts +178 -0
  714. package/examples/vite-wallet/src/transfer.ts +110 -0
  715. package/examples/vite-wallet/src/use-canisters.ts +49 -0
  716. package/examples/vite-wallet/tsconfig.json +19 -0
  717. package/examples/vite-wallet/vite.config.ts +47 -0
  718. package/examples/vite-wallet/vitest.config.ts +17 -0
  719. package/package.json +9 -4
  720. package/packages/core/.size-limit.js +47 -0
  721. package/packages/core/LICENSE +21 -0
  722. package/packages/core/README.md +84 -0
  723. package/packages/core/llms.txt +610 -0
  724. package/packages/core/package.json +89 -0
  725. package/packages/core/src/call.ts +961 -0
  726. package/packages/core/src/canister.ts +259 -0
  727. package/packages/core/src/client.ts +2061 -0
  728. package/packages/core/src/errors.ts +1079 -0
  729. package/packages/core/src/hydration.ts +239 -0
  730. package/packages/core/src/index.ts +23 -0
  731. package/packages/core/src/keys.ts +108 -0
  732. package/packages/core/src/management.ts +138 -0
  733. package/packages/core/src/network.ts +1020 -0
  734. package/packages/core/src/options.ts +1000 -0
  735. package/packages/core/src/runtime.ts +50 -0
  736. package/packages/core/src/testing/fake-replica.ts +1166 -0
  737. package/packages/core/src/testing/index.ts +16 -0
  738. package/packages/core/src/testing/test-auth.ts +340 -0
  739. package/packages/core/src/testing/test-client.ts +559 -0
  740. package/packages/core/src/types.ts +541 -0
  741. package/packages/core/src/units.ts +299 -0
  742. package/packages/core/src/version.ts +4 -0
  743. package/packages/core/tests/canister-helpers.ts +187 -0
  744. package/packages/core/tests/canister-id.test.ts +158 -0
  745. package/packages/core/tests/canister-unwrap.test.ts +94 -0
  746. package/packages/core/tests/canister.test-d.ts +406 -0
  747. package/packages/core/tests/canister.test.ts +1921 -0
  748. package/packages/core/tests/client-account-change.test.ts +131 -0
  749. package/packages/core/tests/client-agents.test.ts +402 -0
  750. package/packages/core/tests/client-auth-network.test.ts +719 -0
  751. package/packages/core/tests/client-disposed.test.ts +403 -0
  752. package/packages/core/tests/client-helpers.ts +136 -0
  753. package/packages/core/tests/client-hydration.test.ts +166 -0
  754. package/packages/core/tests/client-pinned.test.ts +626 -0
  755. package/packages/core/tests/client-session.test.ts +348 -0
  756. package/packages/core/tests/client.test-d.ts +181 -0
  757. package/packages/core/tests/client.test.ts +1162 -0
  758. package/packages/core/tests/encode.test.ts +171 -0
  759. package/packages/core/tests/env-canister-id.test.ts +666 -0
  760. package/packages/core/tests/env-cookie-copies.test.ts +182 -0
  761. package/packages/core/tests/error-guard.test.ts +222 -0
  762. package/packages/core/tests/errors.test-d.ts +280 -0
  763. package/packages/core/tests/errors.test.ts +1359 -0
  764. package/packages/core/tests/fixtures/README.md +28 -0
  765. package/packages/core/tests/fixtures/accounts.did +21 -0
  766. package/packages/core/tests/fixtures/accounts.envelope.json +367 -0
  767. package/packages/core/tests/fixtures/accounts.ts +26 -0
  768. package/packages/core/tests/fixtures/archive.did +31 -0
  769. package/packages/core/tests/fixtures/archive.envelope.json +259 -0
  770. package/packages/core/tests/fixtures/archive.ts +55 -0
  771. package/packages/core/tests/fixtures/icrc1.did +88 -0
  772. package/packages/core/tests/fixtures/icrc1.envelope.json +840 -0
  773. package/packages/core/tests/fixtures/icrc1.ts +78 -0
  774. package/packages/core/tests/fixtures/management.did +724 -0
  775. package/packages/core/tests/fixtures/management.envelope.json +4455 -0
  776. package/packages/core/tests/fixtures/management.ts +486 -0
  777. package/packages/core/tests/fixtures/shapes.did +33 -0
  778. package/packages/core/tests/fixtures/shapes.envelope.json +440 -0
  779. package/packages/core/tests/fixtures/shapes.ts +56 -0
  780. package/packages/core/tests/fixtures/skippable.did +15 -0
  781. package/packages/core/tests/fixtures/skippable.envelope.json +182 -0
  782. package/packages/core/tests/fixtures/skippable.ts +33 -0
  783. package/packages/core/tests/func.test.ts +154 -0
  784. package/packages/core/tests/identity-attributes.test.ts +214 -0
  785. package/packages/core/tests/keys.test.ts +289 -0
  786. package/packages/core/tests/management.test.ts +206 -0
  787. package/packages/core/tests/network-helpers.ts +121 -0
  788. package/packages/core/tests/network.test-d.ts +78 -0
  789. package/packages/core/tests/network.test.ts +1051 -0
  790. package/packages/core/tests/options.test.ts +1594 -0
  791. package/packages/core/tests/redirect-sign-in.test.ts +393 -0
  792. package/packages/core/tests/resend.test-d.ts +81 -0
  793. package/packages/core/tests/resend.test.ts +723 -0
  794. package/packages/core/tests/skip.test-d.ts +125 -0
  795. package/packages/core/tests/testing-entry.test.ts +73 -0
  796. package/packages/core/tests/testing-fake-replica.test.ts +1242 -0
  797. package/packages/core/tests/testing-test-auth.test-d.ts +84 -0
  798. package/packages/core/tests/testing-test-auth.test.ts +483 -0
  799. package/packages/core/tests/testing-test-client.test-d.ts +184 -0
  800. package/packages/core/tests/testing-test-client.test.ts +1272 -0
  801. package/packages/core/tests/time-realm.test.ts +63 -0
  802. package/packages/core/tests/time.test.ts +238 -0
  803. package/packages/core/tests/traps/README.md +39 -0
  804. package/packages/core/tests/traps/anonymous-write.test.ts +145 -0
  805. package/packages/core/tests/traps/classification-table.test.ts +256 -0
  806. package/packages/core/tests/traps/err-arm.test.ts +144 -0
  807. package/packages/core/tests/traps/fetch-spy.ts +39 -0
  808. package/packages/core/tests/traps/invalidation-after-write.test.ts +178 -0
  809. package/packages/core/tests/traps/ledger.ts +248 -0
  810. package/packages/core/tests/traps/root-key-off-local.test.ts +130 -0
  811. package/packages/core/tests/traps/stale-data-across-users.test.ts +173 -0
  812. package/packages/core/tests/traps/unresolved-name.test.ts +155 -0
  813. package/packages/core/tests/traps/update-as-query.test.ts +244 -0
  814. package/packages/core/tests/traps/update-not-resent.test.ts +126 -0
  815. package/packages/core/tests/traps/update-resent-when-never-delivered.test.ts +159 -0
  816. package/packages/core/tests/traps.test-d.ts +476 -0
  817. package/packages/core/tests/units.test-d.ts +118 -0
  818. package/packages/core/tests/units.test.ts +634 -0
  819. package/packages/core/tsconfig.json +19 -0
  820. package/packages/core/tsconfig.typecheck.json +16 -0
  821. package/packages/core/vitest.config.ts +13 -0
  822. package/packages/mcp/LICENSE +21 -0
  823. package/packages/mcp/README.md +189 -0
  824. package/packages/mcp/package.json +62 -0
  825. package/packages/mcp/src/candid/candid-text.test.ts +455 -0
  826. package/packages/mcp/src/candid/candid-text.ts +501 -0
  827. package/packages/mcp/src/candid/contract.test.ts +398 -0
  828. package/packages/mcp/src/candid/formats.ts +155 -0
  829. package/packages/mcp/src/candid/hex.ts +29 -0
  830. package/packages/mcp/src/candid/json-schema.test.ts +758 -0
  831. package/packages/mcp/src/candid/json-schema.ts +454 -0
  832. package/packages/mcp/src/candid/json.test.ts +1207 -0
  833. package/packages/mcp/src/candid/json.ts +1053 -0
  834. package/packages/mcp/src/candid/schema-walk.ts +189 -0
  835. package/packages/mcp/src/candid/type-text.test.ts +179 -0
  836. package/packages/mcp/src/candid/type-text.ts +238 -0
  837. package/packages/mcp/src/cli.ts +134 -0
  838. package/packages/mcp/src/identity.ts +188 -0
  839. package/packages/mcp/src/interface/cache.test.ts +48 -0
  840. package/packages/mcp/src/interface/cache.ts +68 -0
  841. package/packages/mcp/src/interface/cli-cache.test.ts +150 -0
  842. package/packages/mcp/src/interface/cli-cache.ts +122 -0
  843. package/packages/mcp/src/interface/compile-worker.test.ts +246 -0
  844. package/packages/mcp/src/interface/compile-worker.ts +290 -0
  845. package/packages/mcp/src/interface/compile.test.ts +155 -0
  846. package/packages/mcp/src/interface/compile.ts +281 -0
  847. package/packages/mcp/src/interface/docs.test.ts +105 -0
  848. package/packages/mcp/src/interface/docs.ts +86 -0
  849. package/packages/mcp/src/interface/fixtures/icrc1.did +88 -0
  850. package/packages/mcp/src/interface/hex.ts +8 -0
  851. package/packages/mcp/src/interface/ids.test.ts +71 -0
  852. package/packages/mcp/src/interface/ids.ts +87 -0
  853. package/packages/mcp/src/interface/loader.test.ts +804 -0
  854. package/packages/mcp/src/interface/loader.ts +519 -0
  855. package/packages/mcp/src/interface/read-candid.test.ts +300 -0
  856. package/packages/mcp/src/interface/read-candid.ts +259 -0
  857. package/packages/mcp/src/interface/types.ts +113 -0
  858. package/packages/mcp/src/network.ts +122 -0
  859. package/packages/mcp/src/options.ts +149 -0
  860. package/packages/mcp/src/server/approval.ts +159 -0
  861. package/packages/mcp/src/server/naming.ts +43 -0
  862. package/packages/mcp/src/server/results.ts +102 -0
  863. package/packages/mcp/src/server/server.ts +797 -0
  864. package/packages/mcp/src/server/tools.ts +116 -0
  865. package/packages/mcp/src/usage.ts +40 -0
  866. package/packages/mcp/tests/approval.test.ts +60 -0
  867. package/packages/mcp/tests/identity.test.ts +234 -0
  868. package/packages/mcp/tests/naming.test.ts +62 -0
  869. package/packages/mcp/tests/options.test.ts +140 -0
  870. package/packages/mcp/tests/server.test.ts +997 -0
  871. package/packages/mcp/tsconfig.json +19 -0
  872. package/packages/mcp/tsconfig.typecheck.json +10 -0
  873. package/packages/mcp/vitest.config.ts +10 -0
  874. package/packages/react/LICENSE +21 -0
  875. package/packages/react/README.md +328 -0
  876. package/packages/react/llms.txt +95 -0
  877. package/packages/react/package.json +95 -0
  878. package/packages/react/src/index.tsx +678 -0
  879. package/packages/react/tests/abandoned-render.test.tsx +816 -0
  880. package/packages/react/tests/auth.test.tsx +315 -0
  881. package/packages/react/tests/caller-keys.test.tsx +2334 -0
  882. package/packages/react/tests/client-time.test.ts +40 -0
  883. package/packages/react/tests/entry.test.ts +52 -0
  884. package/packages/react/tests/helpers.tsx +291 -0
  885. package/packages/react/tests/hydration.test.tsx +262 -0
  886. package/packages/react/tests/ownership.test.tsx +260 -0
  887. package/packages/react/tests/provider.server.test.tsx +169 -0
  888. package/packages/react/tests/query-client.test.tsx +107 -0
  889. package/packages/react/tests/strict-mode.test.tsx +288 -0
  890. package/packages/react/tests/suspense.test-d.tsx +357 -0
  891. package/packages/react/tests/two-query-cores.test.tsx +135 -0
  892. package/packages/react/tests/types.test-d.tsx +63 -0
  893. package/packages/react/tsconfig.json +23 -0
  894. package/packages/react/tsconfig.test.json +8 -0
  895. package/packages/react/tsconfig.typecheck.json +16 -0
  896. package/packages/react/vitest.config.ts +39 -0
  897. package/packages/vite-plugin/LICENSE +21 -0
  898. package/packages/vite-plugin/README.md +532 -0
  899. package/packages/vite-plugin/package.json +76 -0
  900. package/packages/vite-plugin/src/describe-value.test.ts +43 -0
  901. package/packages/vite-plugin/src/describe-value.ts +167 -0
  902. package/packages/vite-plugin/src/dev-environment.test.ts +747 -0
  903. package/packages/vite-plugin/src/dev-environment.ts +510 -0
  904. package/packages/vite-plugin/src/env.ts +431 -0
  905. package/packages/vite-plugin/src/fetch.ts +348 -0
  906. package/packages/vite-plugin/src/file-identity.ts +106 -0
  907. package/packages/vite-plugin/src/generate.ts +691 -0
  908. package/packages/vite-plugin/src/index.test.ts +1243 -0
  909. package/packages/vite-plugin/src/index.ts +2565 -0
  910. package/packages/vite-plugin/src/interface.ts +339 -0
  911. package/packages/vite-plugin/src/local-host.ts +34 -0
  912. package/packages/vite-plugin/src/options.ts +641 -0
  913. package/packages/vite-plugin/src/principal-text.test.ts +87 -0
  914. package/packages/vite-plugin/src/principal-text.ts +98 -0
  915. package/packages/vite-plugin/tests/close.test.ts +141 -0
  916. package/packages/vite-plugin/tests/environment-mode.test.ts +136 -0
  917. package/packages/vite-plugin/tests/fake-ic.ts +214 -0
  918. package/packages/vite-plugin/tests/fetch-sdk.test.ts +27 -0
  919. package/packages/vite-plugin/tests/fetch.test.ts +1196 -0
  920. package/packages/vite-plugin/tests/fixtures/fake-cli/bin.js +90 -0
  921. package/packages/vite-plugin/tests/fixtures/fake-cli/package.json +8 -0
  922. package/packages/vite-plugin/tests/fixtures/icrc1.did +88 -0
  923. package/packages/vite-plugin/tests/generation.test.ts +1036 -0
  924. package/packages/vite-plugin/tests/icp-command.test.ts +194 -0
  925. package/packages/vite-plugin/tests/icp-timeout.test.ts +83 -0
  926. package/packages/vite-plugin/tests/no-process-exit.test.ts +83 -0
  927. package/packages/vite-plugin/tests/on-generated.test.ts +323 -0
  928. package/packages/vite-plugin/tests/output-collision.test.ts +329 -0
  929. package/packages/vite-plugin/tests/package.test.ts +62 -0
  930. package/packages/vite-plugin/tests/project.test.ts +2993 -0
  931. package/packages/vite-plugin/tests/support.ts +339 -0
  932. package/packages/vite-plugin/tests/timeout.test.ts +107 -0
  933. package/packages/vite-plugin/tsconfig.json +23 -0
  934. package/packages/vite-plugin/tsconfig.typecheck.json +16 -0
  935. package/packages/vite-plugin/vitest.config.ts +9 -0
  936. package/pnpm-workspace.yaml +23 -0
  937. package/scripts/ai-context-files.js +72 -0
  938. package/scripts/assemble-docs-site.mjs +485 -0
  939. package/scripts/assemble-docs-site.test.mjs +568 -0
  940. package/scripts/build-examples.js +58 -0
  941. package/scripts/check-ai-context.js +963 -0
  942. package/scripts/check-ai-context.test.mjs +811 -0
  943. package/scripts/check-exports.mjs +767 -0
  944. package/scripts/check-exports.test.mjs +1136 -0
  945. package/scripts/check-snippets/ambient.d.ts +428 -0
  946. package/scripts/check-snippets/app/generated/backend.did +7 -0
  947. package/scripts/check-snippets/app/generated/backend.ts +15 -0
  948. package/scripts/check-snippets/app/generated/icrc1.did +88 -0
  949. package/scripts/check-snippets/app/generated/icrc1.ts +78 -0
  950. package/scripts/check-snippets/app/globals.ts +26 -0
  951. package/scripts/check-snippets/docs/guides/App.tsx +21 -0
  952. package/scripts/check-snippets/docs/guides/app/providers.tsx +23 -0
  953. package/scripts/check-snippets/docs/guides/auth-client.ts +47 -0
  954. package/scripts/check-snippets/docs/guides/canisters/backend.ts +3 -0
  955. package/scripts/check-snippets/docs/guides/canisters/icrc1.ts +3 -0
  956. package/scripts/check-snippets/docs/guides/components/LedgerCard.tsx +26 -0
  957. package/scripts/check-snippets/docs/guides/components/shell.tsx +11 -0
  958. package/scripts/check-snippets/docs/guides/components/sign-in-page.tsx +29 -0
  959. package/scripts/check-snippets/docs/guides/components/slow-section.tsx +5 -0
  960. package/scripts/check-snippets/docs/guides/generated/accounts.ts +95 -0
  961. package/scripts/check-snippets/docs/guides/generated/ckbtc_minter.ts +40 -0
  962. package/scripts/check-snippets/docs/guides/generated/icp_ledger.ts +267 -0
  963. package/scripts/check-snippets/docs/guides/ledger.ts +10 -0
  964. package/scripts/check-snippets/docs/guides/server/request-client.ts +8 -0
  965. package/scripts/check-snippets.mjs +993 -0
  966. package/scripts/check-snippets.test.mjs +338 -0
  967. package/scripts/check-stack.mjs +501 -0
  968. package/scripts/check-stack.test.mjs +800 -0
  969. package/scripts/clean-node-modules.js +67 -0
  970. package/scripts/example-projects.js +58 -0
  971. package/scripts/export-budget.mjs +145 -0
  972. package/scripts/faults.json +6749 -0
  973. package/scripts/lib/fault-workspace.mjs +210 -0
  974. package/scripts/lib/io.mjs +29 -0
  975. package/scripts/lib/io.test.mjs +38 -0
  976. package/scripts/lib/markdown-fences.mjs +56 -0
  977. package/scripts/lib/markdown-fences.test.mjs +84 -0
  978. package/scripts/lib/package-exports.mjs +54 -0
  979. package/scripts/lib/package-exports.test.mjs +66 -0
  980. package/scripts/lib/release-pr-steps.mjs +47 -0
  981. package/scripts/release-tag.mjs +95 -0
  982. package/scripts/release-tag.test.mjs +278 -0
  983. package/scripts/release-workflow.test.mjs +98 -0
  984. package/scripts/release.js +167 -0
  985. package/scripts/release.test.mjs +347 -0
  986. package/scripts/removed-v3-names.js +125 -0
  987. package/scripts/size-app/.size-limit.js +90 -0
  988. package/scripts/size-app/core.js +3 -0
  989. package/scripts/size-app/package.json +24 -0
  990. package/scripts/size-app/react.js +4 -0
  991. package/scripts/size-app.test.mjs +73 -0
  992. package/scripts/sync-ai-context-versions.js +102 -0
  993. package/scripts/sync-example-versions.js +151 -0
  994. package/scripts/test-examples-published.mjs +1585 -0
  995. package/scripts/test-examples-published.test.mjs +1586 -0
  996. package/scripts/traps/any-method-vars-not-a-list.json +5 -0
  997. package/scripts/traps/canister-method-unknown.json +5 -0
  998. package/scripts/traps/client-says-who-calls-once.json +5 -0
  999. package/scripts/traps/client-says-who-calls.json +5 -0
  1000. package/scripts/traps/err-only-on-canister-err.json +14 -0
  1001. package/scripts/traps/format-units-refuses-missing-value.json +5 -0
  1002. package/scripts/traps/format-units-refuses-number.json +5 -0
  1003. package/scripts/traps/format-units-unknown-option.json +5 -0
  1004. package/scripts/traps/guard-err-is-the-methods.json +5 -0
  1005. package/scripts/traps/guard-false-keeps-every-kind.json +5 -0
  1006. package/scripts/traps/internals-not-exported.json +9 -0
  1007. package/scripts/traps/network-refuses-unknown-name.json +5 -0
  1008. package/scripts/traps/never-err-has-no-type.json +5 -0
  1009. package/scripts/traps/options-method-unknown.json +5 -0
  1010. package/scripts/traps/options-vars-wrong-type.json +5 -0
  1011. package/scripts/traps/parse-units-refuses-number.json +5 -0
  1012. package/scripts/traps/parse-units-unknown-option.json +5 -0
  1013. package/scripts/traps/principal-is-not-plain-text.json +14 -0
  1014. package/scripts/traps/query-options-refuses-oneway.json +5 -0
  1015. package/scripts/traps/query-options-refuses-update.json +5 -0
  1016. package/scripts/traps/refuse-next-unknown-option.json +5 -0
  1017. package/scripts/traps/resend-key-reads-the-methods-arg.json +5 -0
  1018. package/scripts/traps/resend-states-its-dedupe-key.json +5 -0
  1019. package/scripts/traps/suspense-refuses-argument-skip.json +5 -0
  1020. package/scripts/traps/suspense-refuses-skippable-read.json +5 -0
  1021. package/scripts/traps/suspense-refuses-vars-skip-fits.json +5 -0
  1022. package/scripts/traps/test-handler-method-unknown.json +5 -0
  1023. package/scripts/traps/test-handler-nat-is-bigint.json +5 -0
  1024. package/scripts/traps/test-handler-takes-the-domain-value.json +5 -0
  1025. package/scripts/traps/vars-of-two-arguments-are-the-tuple.json +5 -0
  1026. package/scripts/typecheck-examples.js +122 -0
  1027. package/scripts/verify-faults.mjs +375 -0
  1028. package/scripts/verify-faults.test.mjs +402 -0
  1029. package/scripts/verify-package-artifacts.mjs +590 -0
  1030. package/scripts/verify-peer-floors.js +399 -0
  1031. package/scripts/verify-peer-floors.test.mjs +422 -0
  1032. package/scripts/verify-test-fails-rules.mjs +104 -0
  1033. package/scripts/verify-test-fails-rules.test.mjs +215 -0
  1034. package/scripts/verify-test-fails.js +371 -0
  1035. package/scripts/verify-traps.mjs +505 -0
  1036. package/scripts/verify-traps.test.mjs +521 -0
  1037. package/scripts/verify-typescript-floor.js +239 -0
  1038. package/skill-packages/ic-reactor/SKILL.md +103 -0
  1039. package/stack.json +8 -0
  1040. package/tsconfig.base.json +23 -0
  1041. package/tsconfig.json +8 -0
@@ -0,0 +1,1373 @@
1
+ // Leak audit for one agent run, the second line of defence behind the OS
2
+ // sandbox (harness/sandbox.mjs): finds every tool call in the agent's
3
+ // transcript whose path or command reaches outside the run directory, and
4
+ // whether it succeeded.
5
+ //
6
+ // The transcript is what Claude Code prints with `--output-format stream-json
7
+ // --verbose`: one JSON message per line, tool calls as content blocks
8
+ // `{ type: "tool_use", id, name, input }` and their outcomes as
9
+ // `{ type: "tool_result", tool_use_id, is_error, content }`. The scan walks
10
+ // every JSON value for such blocks rather than trusting one exact envelope, so
11
+ // a single JSON document or an array of messages works too.
12
+ //
13
+ // A tool call is a violation when it
14
+ // - names a file or directory (`file_path`, `path`, `notebook_path`, a Glob
15
+ // `pattern`, a Grep `glob`) that resolves outside the run directory;
16
+ // - runs a shell command that reaches outside. Commands are lexed as a shell
17
+ // would (quotes, escapes, operators, redirections, heredocs, `$(…)` and
18
+ // backticks), and each word is judged by its position:
19
+ // - arguments and redirection targets of ordinary commands are paths:
20
+ // normalised (`./../x`, `a/../..`, `$PWD/..`, `${PWD}/..`, `~`, `~user`,
21
+ // `$HOME`) and resolved from the shell's current directory, which `cd`
22
+ // moves; `$TMPDIR`, `$TMP`, `$TEMP`, `$OLDPWD`, any other variable
23
+ // followed by `/`, and a command substitution that builds a path
24
+ // (`$(pwd)/..`, `$(dirname $PWD)`, `$(printf …)`) cannot be resolved and
25
+ // are flagged;
26
+ // - the pattern/program argument of grep, egrep, fgrep, rg, ag, sed, awk,
27
+ // gawk and jq is not a path (`grep "/call" file` reads `file`);
28
+ // - code given to an interpreter (`node -e '…'`, `python -c '…'`, a heredoc
29
+ // fed to one) is not shell: only its string literals are checked — ones
30
+ // under a user-data root (`/Users`, `/home`, `/private`, `/var`, `/tmp`,
31
+ // `/etc`, `/Volumes`, `/opt`, `/root`), `~`, or climbing with `..`;
32
+ // - the text of a heredoc written to a file is data, checked only for
33
+ // string literals under a user-data root (a script written now and run
34
+ // later);
35
+ // - `sh -c` / `bash -c` bodies, `xargs` commands and command substitutions
36
+ // run, so they are scanned as shell in turn;
37
+ // - uses a network tool (WebFetch, WebSearch);
38
+ // - uses the network from the shell. The OS sandbox leaves the network open
39
+ // (the agent CLI needs its API), and `node` runs in it, so this is the only
40
+ // line there. Flagged: a network command (curl, wget, nc, ssh, …); a
41
+ // package manager or git command that reaches a registry or remote (`npm
42
+ // view`, `pnpm add`, `npx <a package the run does not have>`, `pip
43
+ // install`, `git clone`, …); a URL of a non-local host given to an
44
+ // interpreter; and interpreter code (`node -e`, `python -c`, a heredoc or
45
+ // pipe fed to one, or a script written in the run by Write, Edit or a
46
+ // heredoc and then run with an interpreter) that calls the network
47
+ // (`fetch(`, `http(s).get/request`, `net`/`tls`, WebSocket, `urllib`,
48
+ // `requests`, …) towards a non-local host or a host it does not name, or
49
+ // that runs a network command through `child_process` / `subprocess`.
50
+ // String literals are data, not calls: an edit script whose text holds
51
+ // `fetch(` or a URL is not network use, and neither is code that only
52
+ // names local hosts (`localhost`, `127.*`, `[::1]`). Source written to a
53
+ // file is not flagged until it is run. Not followed: tests run by vitest
54
+ // (the agent's own tests name mainnet hosts, which the public scaffold's
55
+ // fake replica answers; a test fetching another URL is not seen) and a
56
+ // host reached through a library such as an `HttpAgent`.
57
+ // A violation is `blocked` when the call failed (`is_error`) or its output
58
+ // reports a permission refusal (`Operation not permitted`, `EPERM`, `EACCES`,
59
+ // `Permission denied`) naming that path — or, when it is the call's only
60
+ // violation, reports one at all. The run is contaminated when any violation
61
+ // was NOT blocked — a successful outside read — or when the transcript holds
62
+ // no parseable JSON (it cannot be audited). The run's own node_modules counts
63
+ // as inside, including the target of a node_modules symlink.
64
+ //
65
+ // The CLI's persisted tool output (PREREGISTRATION.md, Addendum 5). When a
66
+ // tool's output is too large, the CLI saves it under the run's own home and
67
+ // answers the call with a notice instead. A Read (`file_path`) or Grep
68
+ // (`path`) of such a file is not a violation when all of these hold, and is
69
+ // judged as any other path otherwise:
70
+ // - the run directory is `<run>/work`, and the path, resolved as every path
71
+ // here is (lexically: `.`, `..`, `//`, `~`, `$HOME`; no symlink is
72
+ // followed), is exactly
73
+ // `<run>/home/.claude/projects/<one segment>/<one segment>/tool-results/<one file name>`;
74
+ // - the path as written has no `..` segment;
75
+ // - an earlier top-level user message of the transcript is the CLI's answer
76
+ // to a call whose output it saved: its content is one tool_result block,
77
+ // whose whole content is the notice — `<persisted-output>`, then "Output
78
+ // too large (<size>). Full output saved to: <path>", …,
79
+ // `</persisted-output>` — and the message's `tool_use_result`, the CLI's
80
+ // own record of the call, has `persistedOutputPath` equal to that <path>,
81
+ // and `session_id` equal to its session segment. A tool's output is text
82
+ // inside `content` or inside a field of `tool_use_result`; it cannot add
83
+ // that field, so notice-shaped output of a Bash `printf`, a `cat` or a
84
+ // subagent is not a notice;
85
+ // - the notice's <path> is the read's path, byte for byte;
86
+ // - the notice answers an earlier tool call that is not a Read and in which
87
+ // the scan found no violation, blocked or not;
88
+ // - no component of the path below `<run>` is a symlink, as far as the path
89
+ // exists when the scan runs (the driver scans before it removes the run).
90
+ // A Bash command that names the path (`cat <path>`) is still a violation, and
91
+ // so is any other tool or field naming it (Write, Edit, Glob, a Read `path`).
92
+ import { existsSync, lstatSync, realpathSync } from "node:fs"
93
+ import { basename, dirname, isAbsolute, join, resolve, sep } from "node:path"
94
+ import { homedir } from "node:os"
95
+
96
+ const SAFE = new Set([
97
+ "/dev/null",
98
+ "/dev/stdout",
99
+ "/dev/stderr",
100
+ "/dev/stdin",
101
+ "/dev/tty",
102
+ ])
103
+ const NETWORK_TOOLS = new Set(["WebFetch", "WebSearch"])
104
+ const PATH_FIELDS = ["file_path", "path", "notebook_path"]
105
+ const OUTSIDE_VARS = new Set(["TMPDIR", "TMP", "TEMP", "OLDPWD"])
106
+ const PERMISSION = /Operation not permitted|EPERM|EACCES|Permission denied/i
107
+ const PATHY_SUBST = /pwd|dirname|realpath|readlink|printf|echo|cd\b|\.\.|\/|\$/
108
+ // Where user data lives: an absolute string literal in code is only suspect
109
+ // under one of these (not `/call`, `/**`, `/api/v2/...`).
110
+ const USER_DATA_ROOT =
111
+ /^\/(Users|home|private|var|tmp|etc|Volumes|opt|root)(\/|$)/
112
+ const PATTERN_COMMANDS = new Set([
113
+ "grep",
114
+ "egrep",
115
+ "fgrep",
116
+ "rg",
117
+ "ag",
118
+ "sed",
119
+ "awk",
120
+ "gawk",
121
+ "jq",
122
+ ])
123
+ const INTERPRETERS = new Set([
124
+ "node",
125
+ "nodejs",
126
+ "bun",
127
+ "deno",
128
+ "tsx",
129
+ "ts-node",
130
+ "python",
131
+ "python3",
132
+ "ruby",
133
+ "perl",
134
+ "php",
135
+ ])
136
+ const CODE_FLAGS = new Set([
137
+ "-e",
138
+ "--eval",
139
+ "-p",
140
+ "--print",
141
+ "-c",
142
+ "-r",
143
+ "--exec",
144
+ ])
145
+ const SHELLS = new Set(["sh", "bash", "zsh", "dash"])
146
+ // The CLI's persisted-output notice, as the whole content of a tool_result.
147
+ const PERSISTED_NOTICE =
148
+ /^<persisted-output>\nOutput too large \([^()\n]+\)\. Full output saved to: ([^\n]+)\n[\s\S]*\n<\/persisted-output>$/
149
+ // Where the CLI persists it, relative to `<run>`: one project, one session.
150
+ const PERSISTED_PATH =
151
+ /^home\/\.claude\/projects\/[^/]+\/[^/]+\/tool-results\/[^/]+$/
152
+ // The tool and field that may read a persisted output back.
153
+ const PERSISTED_READERS = { Read: "file_path", Grep: "path" }
154
+
155
+ // ---------------------------------------------------------------- network
156
+ const words = (text) => new Set(text.trim().split(/\s+/))
157
+ // Commands that reach the network whatever their arguments.
158
+ const NETWORK_COMMANDS = words(`
159
+ curl wget nc ncat netcat telnet socat ssh scp sftp ftp http https xh aria2c
160
+ lynx w3m links elinks dig nslookup host whois
161
+ `)
162
+ // Package-manager subcommands that reach the registry (npm's aliases too).
163
+ const REGISTRY_SUBCOMMANDS = words(`
164
+ install i in ins inst insta instal isnt isnta isntal isntall add a ci
165
+ clean-install ic install-clean install-test it install-ci-test cit update up
166
+ upgrade udpate view v info show search s se find outdated audit publish
167
+ unpublish deprecate dist-tag dist-tags docs home repo bugs issues create
168
+ login adduser logout whoami ping owner access team token star unstar stars
169
+ doctor fetch dedupe ddp
170
+ `)
171
+ const PACKAGE_MANAGERS = words("npm pnpm yarn bun")
172
+ // Subcommands that run a package, installing it first when the run lacks it.
173
+ const PACKAGE_RUN_SUBCOMMANDS = {
174
+ npm: words("exec x"),
175
+ pnpm: words("dlx"),
176
+ yarn: words("dlx"),
177
+ bun: words("x"),
178
+ }
179
+ const PACKAGE_RUNNERS = words("npx pnpx bunx")
180
+ // What every starter has installed: `npx tsc`, `npx vitest` stay local.
181
+ const LOCAL_PACKAGES = words("tsc vitest typescript")
182
+ const GIT_REMOTE = words("clone fetch pull push ls-remote submodule")
183
+ const PIP_REMOTE = words("install download index search wheel")
184
+ // Prefixes that run the command after them.
185
+ const WRAPPERS = words(
186
+ "env command exec nohup time timeout nice stdbuf sudo doas"
187
+ )
188
+ // In interpreted code with its string literals removed: calls that reach the
189
+ // network (JS, Python, Ruby, Perl, PHP). A member call `x.fetch(` is not a
190
+ // global fetch.
191
+ const NETWORK_CALLS = new RegExp(
192
+ [
193
+ String.raw`(?<![\w$.])fetch\s*\(`,
194
+ String.raw`\b(globalThis|window|self)\s*\.\s*fetch\s*\(`,
195
+ String.raw`\bnew\s+(WebSocket|EventSource|XMLHttpRequest)\b`,
196
+ String.raw`\b(https?|http2|net|tls|dgram|dns)\s*\.\s*(get|request|connect|createConnection|lookup|resolve\w*)\s*\(`,
197
+ String.raw`\burlopen\s*\(`,
198
+ String.raw`\brequests\s*\.\s*(get|post|put|patch|head|delete|request|Session)\b`,
199
+ String.raw`^[ \t]*import\s+(urllib|urllib3|http\.client|requests|httpx|aiohttp|socket|ftplib)\b`,
200
+ String.raw`^[ \t]*from\s+(urllib|urllib3|http|requests|httpx|aiohttp|socket|ftplib)(\.\w+)*\s+import\b`,
201
+ String.raw`Net::HTTP|URI\.open|LWP::|HTTP::Tiny|IO::Socket|\bcurl_exec\b|\bfsockopen\b|\bfile_get_contents\s*\(`,
202
+ ].join("|"),
203
+ "m"
204
+ )
205
+ // Modules whose loading in JS code means network use, or running commands.
206
+ const NETWORK_MODULES =
207
+ /^(node:)?(https?|http2|net|tls|dgram|dns(\/promises)?|undici|axios|node-fetch|got|ky|ws|superagent|request|open-uri)$/
208
+ const SPAWN_MODULES = /^(node:)?child_process$/
209
+ const SPAWN_CALLS =
210
+ /^[ \t]*(import\s+subprocess\b|from\s+subprocess\s+import\b)|\bos\s*\.\s*(system|popen)\s*\(/m
211
+ // `require("x")`, `import("x")`, `from "x"`, `import "x"` over a skeleton in
212
+ // which every string literal is `"S<n>"`.
213
+ const MODULE_SPECIFIERS =
214
+ /\b(?:require|import)\s*\(\s*"S(\d+)"|\bfrom\s*"S(\d+)"|\bimport\s*"S(\d+)"/g
215
+ const URLS = /\b(?:https?|wss?|ftp):\/\/([^\s/?#'"`<>\\]*)/gi
216
+
217
+ function realpathOrSelf(p) {
218
+ try {
219
+ return realpathSync(p)
220
+ } catch {
221
+ return p
222
+ }
223
+ }
224
+
225
+ function roots(runDir) {
226
+ const list = new Set([resolve(runDir), realpathOrSelf(resolve(runDir))])
227
+ const nm = join(runDir, "node_modules")
228
+ if (existsSync(nm)) list.add(realpathOrSelf(nm))
229
+ return [...list]
230
+ }
231
+
232
+ const inside = (p, allowed) =>
233
+ allowed.some((root) => p === root || p.startsWith(root + sep))
234
+
235
+ /** `~`, `~user`, `$HOME`, `$PWD`, then resolution from `cwd`. */
236
+ function expand(raw, cwd) {
237
+ let p = raw
238
+ if (p === "~" || p.startsWith("~/")) p = join(homedir(), p.slice(1))
239
+ else if (/^~[A-Za-z_][\w.-]*/.test(p)) {
240
+ const [user, ...rest] = p.slice(1).split("/")
241
+ p = join(dirname(homedir()), user, ...rest)
242
+ }
243
+ p = p
244
+ .replace(/\$\{HOME\}|\$HOME\b/g, homedir())
245
+ .replace(/\$\{PWD\}|\$PWD\b/g, cwd)
246
+ return isAbsolute(p) ? resolve(p) : resolve(cwd, p)
247
+ }
248
+
249
+ /** A file's key in the map of files the run wrote: /private/var = /var. */
250
+ const fileKey = (p) => p.replace(/^\/private(?=\/(var|tmp|etc)\/)/, "")
251
+
252
+ /** The part of a glob pattern before its first wildcard. */
253
+ const globBase = (pattern) => pattern.split(/[*?[{]/)[0] || "."
254
+
255
+ // ---------------------------------------------------------------- the lexer
256
+
257
+ /** Reads a balanced `$( … )` starting after `$(`; returns [body, end]. */
258
+ function readParen(s, i) {
259
+ let depth = 1
260
+ let j = i
261
+ let quote = ""
262
+ for (; j < s.length; j += 1) {
263
+ const ch = s[j]
264
+ if (quote) {
265
+ if (ch === "\\" && quote === '"') j += 1
266
+ else if (ch === quote) quote = ""
267
+ continue
268
+ }
269
+ if (ch === "'" || ch === '"') quote = ch
270
+ else if (ch === "(") depth += 1
271
+ else if (ch === ")") {
272
+ depth -= 1
273
+ if (depth === 0) break
274
+ }
275
+ }
276
+ return [s.slice(i, j), j + 1]
277
+ }
278
+
279
+ /**
280
+ * Lexes a command line into words and operators. A word is
281
+ * `{ text, subs, vars, quoted }`: `text` its value with quotes removed,
282
+ * `subs` the command substitutions active in it, `vars` the variables
283
+ * expanded in it. Heredoc bodies are returned separately, each tied to the
284
+ * segment (by index) whose command reads it.
285
+ */
286
+ export function lexShell(s) {
287
+ const tokens = []
288
+ const heredocs = []
289
+ const pending = []
290
+ let word = null
291
+ const start = () =>
292
+ (word ??= { text: "", subs: [], vars: [], quoted: false, raw: "" })
293
+ const end = () => {
294
+ if (word) tokens.push({ type: "word", ...word })
295
+ word = null
296
+ }
297
+ const op = (text) => {
298
+ end()
299
+ tokens.push({ type: "op", text })
300
+ }
301
+ const readVar = (i, w) => {
302
+ const m = /^\$(\{[^}]*\}|[A-Za-z_][A-Za-z0-9_]*|[0-9#?@*$!-])/.exec(
303
+ s.slice(i)
304
+ )
305
+ if (!m) return i
306
+ const name = m[1].replace(/^\{|\}$/g, "").replace(/[:#%/].*$/, "")
307
+ w.vars.push({ name, followedBySlash: s[i + m[0].length] === "/" })
308
+ w.text += m[0]
309
+ w.raw += m[0]
310
+ return i + m[0].length - 1
311
+ }
312
+ for (let i = 0; i < s.length; i += 1) {
313
+ const ch = s[i]
314
+ if (ch === "\n") {
315
+ op("\n")
316
+ // Heredoc bodies start on the line after their `<<` operator.
317
+ while (pending.length > 0) {
318
+ const { delimiter, strip, segment } = pending.shift()
319
+ const lines = []
320
+ let j = i + 1
321
+ for (;;) {
322
+ const next = s.indexOf("\n", j)
323
+ const line = s.slice(j, next === -1 ? s.length : next)
324
+ const endLine = strip ? line.replace(/^\t+/, "") : line
325
+ if (endLine.trim() === delimiter) {
326
+ i = next === -1 ? s.length : next
327
+ break
328
+ }
329
+ lines.push(line)
330
+ if (next === -1) {
331
+ i = s.length
332
+ break
333
+ }
334
+ j = next + 1
335
+ }
336
+ heredocs.push({ segment, body: lines.join("\n") })
337
+ }
338
+ continue
339
+ }
340
+ if (ch === " " || ch === "\t") {
341
+ end()
342
+ continue
343
+ }
344
+ if (ch === "#" && !word) {
345
+ // A comment runs to the end of the line.
346
+ const next = s.indexOf("\n", i)
347
+ i = (next === -1 ? s.length : next) - 1
348
+ continue
349
+ }
350
+ if (ch === "\\") {
351
+ start()
352
+ word.text += s[i + 1] ?? ""
353
+ word.raw += ch + (s[i + 1] ?? "")
354
+ i += 1
355
+ continue
356
+ }
357
+ if (ch === "'") {
358
+ const close = s.indexOf("'", i + 1)
359
+ const body = s.slice(i + 1, close === -1 ? s.length : close)
360
+ start()
361
+ word.text += body
362
+ word.raw += `'${body}'`
363
+ word.quoted = true
364
+ i = close === -1 ? s.length : close
365
+ continue
366
+ }
367
+ if (ch === '"') {
368
+ start()
369
+ word.quoted = true
370
+ let j = i + 1
371
+ for (; j < s.length && s[j] !== '"'; j += 1) {
372
+ if (s[j] === "\\") {
373
+ word.text += s[j + 1] ?? ""
374
+ j += 1
375
+ } else if (s[j] === "$" && s[j + 1] === "(") {
376
+ const [body, next] = readParen(s, j + 2)
377
+ word.subs.push({ body, followedBySlash: s[next] === "/" })
378
+ word.text += `$(${body})`
379
+ j = next - 1
380
+ } else if (s[j] === "`") {
381
+ const close = s.indexOf("`", j + 1)
382
+ const body = s.slice(j + 1, close === -1 ? s.length : close)
383
+ word.subs.push({ body, followedBySlash: s[close + 1] === "/" })
384
+ word.text += `\`${body}\``
385
+ j = close === -1 ? s.length : close
386
+ } else if (s[j] === "$") {
387
+ j = readVar(j, word)
388
+ } else word.text += s[j]
389
+ }
390
+ word.raw += s.slice(i, j + 1)
391
+ i = j
392
+ continue
393
+ }
394
+ if (ch === "$" && s[i + 1] === "(") {
395
+ const [body, next] = readParen(s, i + 2)
396
+ start()
397
+ word.subs.push({ body, followedBySlash: s[next] === "/" })
398
+ word.text += `$(${body})`
399
+ word.raw += `$(${body})`
400
+ i = next - 1
401
+ continue
402
+ }
403
+ if (ch === "`") {
404
+ const close = s.indexOf("`", i + 1)
405
+ const body = s.slice(i + 1, close === -1 ? s.length : close)
406
+ start()
407
+ word.subs.push({ body, followedBySlash: s[close + 1] === "/" })
408
+ word.text += `\`${body}\``
409
+ word.raw += `\`${body}\``
410
+ i = close === -1 ? s.length : close
411
+ continue
412
+ }
413
+ if (ch === "$") {
414
+ start()
415
+ i = readVar(i, word)
416
+ continue
417
+ }
418
+ if (ch === "<" && s[i + 1] === "<" && s[i + 2] !== "<") {
419
+ const m = /^<<(-?)\s*(['"]?)([A-Za-z_][\w-]*)\2/.exec(s.slice(i))
420
+ if (m) {
421
+ end()
422
+ const segment = tokens.filter(
423
+ (t) => t.type === "op" && t.text !== "redir"
424
+ ).length
425
+ pending.push({ delimiter: m[3], strip: m[1] === "-", segment })
426
+ i += m[0].length - 1
427
+ continue
428
+ }
429
+ }
430
+ if (ch === "<" && s.slice(i, i + 3) === "<<<") {
431
+ op("herestring")
432
+ i += 2
433
+ continue
434
+ }
435
+ if (ch === ">" || ch === "<") {
436
+ // A leading fd number (`2>`) belongs to the redirection.
437
+ if (word && /^\d+$/.test(word.text) && !word.quoted) word = null
438
+ end()
439
+ // `out`: the target is written (`>`, `>>`, not `>&` / `<`).
440
+ const out = ch === ">" && s[i + 1] !== "&"
441
+ const append = ch === ">" && s[i + 1] === ">"
442
+ if (s[i + 1] === ">" || s[i + 1] === "&") i += 1
443
+ tokens.push({ type: "op", text: "redir", out, append })
444
+ continue
445
+ }
446
+ if (ch === "&" || ch === "|" || ch === ";" || ch === "(" || ch === ")") {
447
+ const two = s.slice(i, i + 2)
448
+ if (two === "&&" || two === "||") {
449
+ op(two)
450
+ i += 1
451
+ } else if (ch === "&" && s[i + 1] === ">") {
452
+ end()
453
+ tokens.push({ type: "op", text: "redir", out: true, append: false })
454
+ i += 1
455
+ } else op(ch)
456
+ continue
457
+ }
458
+ start()
459
+ word.text += ch
460
+ word.raw += ch
461
+ }
462
+ end()
463
+ return { tokens, heredocs }
464
+ }
465
+
466
+ /**
467
+ * Splits tokens into simple commands:
468
+ * { words, redirects, outputs, herestrings, piped }. `outputs` are the
469
+ * redirection targets written to (`{ word, append }`); `piped` says the
470
+ * command reads the previous one's output.
471
+ */
472
+ function segmentsOf(tokens) {
473
+ const segment = (piped) => ({
474
+ words: [],
475
+ redirects: [],
476
+ outputs: [],
477
+ herestrings: [],
478
+ piped,
479
+ })
480
+ const segments = [segment(false)]
481
+ for (let i = 0; i < tokens.length; i += 1) {
482
+ const t = tokens[i]
483
+ const seg = segments[segments.length - 1]
484
+ if (t.type === "op" && t.text === "redir") {
485
+ const target = tokens[i + 1]
486
+ if (target?.type === "word") {
487
+ seg.redirects.push(target)
488
+ if (t.out) seg.outputs.push({ word: target, append: t.append })
489
+ i += 1
490
+ }
491
+ } else if (t.type === "op" && t.text === "herestring") {
492
+ const body = tokens[i + 1]
493
+ if (body?.type === "word") {
494
+ seg.herestrings.push(body)
495
+ i += 1
496
+ }
497
+ } else if (t.type === "op") {
498
+ segments.push(segment(t.text === "|"))
499
+ } else seg.words.push(t)
500
+ }
501
+ return segments
502
+ }
503
+
504
+ // ---------------------------------------------------------------- checks
505
+
506
+ /** String literals in code that point at user data or climb out. */
507
+ function scanCode(code, cwd, check, { relative }) {
508
+ for (const m of code.matchAll(/(['"`])((?:\\.|(?!\1)[^\\\n])*)\1/g)) {
509
+ const literal = m[2].replace(/\$\{[^}]*\}/g, "")
510
+ if (!literal) continue
511
+ const absolute = USER_DATA_ROOT.test(literal)
512
+ const home = /^~($|\/|[A-Za-z_])/.test(literal)
513
+ const climbs = relative && /(^|\/)\.\.(\/|$)/.test(literal)
514
+ if (absolute || home || climbs) {
515
+ check(literal, cwd, "path in interpreted code")
516
+ }
517
+ }
518
+ }
519
+
520
+ // ---------------------------------------------------------------- network
521
+
522
+ /** `localhost`, `*.localhost`, `127.*`, `[::1]`, `0.0.0.0`. */
523
+ const isLocalHost = (host) =>
524
+ /^(localhost|.+\.localhost|127\.\d+\.\d+\.\d+|\[::1?\]|0\.0\.0\.0)$/i.test(
525
+ host
526
+ )
527
+
528
+ /**
529
+ * The URLs in a text, each with its host. A host the text builds
530
+ * (`${host}`, `$HOST`, empty) is not local: it cannot be resolved.
531
+ */
532
+ function urlsIn(text) {
533
+ return [...text.matchAll(URLS)].map((m) => {
534
+ const authority = m[1].replace(/^[^@]*@/, "")
535
+ const host = authority.startsWith("[")
536
+ ? authority.slice(0, authority.indexOf("]") + 1)
537
+ : authority.split(":")[0]
538
+ return { url: m[0], local: isLocalHost(host) }
539
+ })
540
+ }
541
+
542
+ /**
543
+ * Code split into its string literals and a skeleton in which each literal is
544
+ * `"S<n>"`: '…', "…" (to the end of the line at most), `…` and Python's
545
+ * triple quotes. The skeleton is what the code does; the literals are data.
546
+ */
547
+ function splitCode(code) {
548
+ const literals = []
549
+ let skeleton = ""
550
+ for (let i = 0; i < code.length; i += 1) {
551
+ const ch = code[i]
552
+ const triple = code.slice(i, i + 3)
553
+ if (triple === "'''" || triple === '"""') {
554
+ const close = code.indexOf(triple, i + 3)
555
+ literals.push(code.slice(i + 3, close === -1 ? code.length : close))
556
+ skeleton += `"S${literals.length - 1}"`
557
+ i = close === -1 ? code.length : close + 2
558
+ continue
559
+ }
560
+ if (ch === "'" || ch === '"' || ch === "`") {
561
+ let j = i + 1
562
+ for (; j < code.length && code[j] !== ch; j += 1) {
563
+ if (code[j] === "\\") j += 1
564
+ else if (code[j] === "\n" && ch !== "`") break
565
+ }
566
+ literals.push(code.slice(i + 1, j))
567
+ skeleton += `"S${literals.length - 1}"` + (code[j] === "\n" ? "\n" : "")
568
+ i = j
569
+ continue
570
+ }
571
+ skeleton += ch
572
+ }
573
+ // Comments are prose: `/* … */`, `// …` and `# …` (a `#` after a space).
574
+ skeleton = skeleton
575
+ .replace(/\/\*[\s\S]*?\*\//g, " ")
576
+ .replace(/(^|[\s;{}()])\/\/.*$/gm, "$1")
577
+ .replace(/(^|\s)#.*$/gm, "$1")
578
+ return { skeleton, literals }
579
+ }
580
+
581
+ /**
582
+ * Network use in interpreter code: a network call towards a non-local host,
583
+ * or towards no host the code names (it builds or receives it); and network
584
+ * commands it runs through child_process / subprocess (`shellNet` scans a
585
+ * string literal as a command). Code that names only local hosts is clean.
586
+ */
587
+ function networkInCode(code, shellNet) {
588
+ const { skeleton, literals } = splitCode(code)
589
+ const modules = [...skeleton.matchAll(MODULE_SPECIFIERS)].map(
590
+ (m) => literals[Number(m[1] ?? m[2] ?? m[3])] ?? ""
591
+ )
592
+ const found = []
593
+ if (SPAWN_CALLS.test(skeleton) || modules.some((m) => SPAWN_MODULES.test(m)))
594
+ for (const literal of literals) found.push(...shellNet(literal))
595
+ const call =
596
+ NETWORK_CALLS.exec(skeleton)?.[0] ??
597
+ modules.find((m) => NETWORK_MODULES.test(m))
598
+ if (call === undefined) return found
599
+ const urls = literals.flatMap(urlsIn)
600
+ if (urls.length === 0) {
601
+ found.push({
602
+ value: call,
603
+ reason: "network call in interpreted code to a host it does not name",
604
+ network: true,
605
+ })
606
+ }
607
+ for (const u of urls) {
608
+ if (!u.local) {
609
+ found.push({
610
+ value: u.url,
611
+ reason: "network call in interpreted code to a non-local host",
612
+ network: true,
613
+ })
614
+ }
615
+ }
616
+ return found
617
+ }
618
+
619
+ /** The words from the command name on: assignments and wrappers skipped. */
620
+ function commandOf(words) {
621
+ let k = 0
622
+ for (;;) {
623
+ while (k < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[k].text))
624
+ k += 1
625
+ if (k >= words.length || !WRAPPERS.has(basename(words[k].text)))
626
+ return words.slice(k)
627
+ k += 1
628
+ // The wrapper's options, assignments (`env`) and durations (`timeout`).
629
+ while (
630
+ k < words.length &&
631
+ /^(-|[A-Za-z_][A-Za-z0-9_]*=|\d+(\.\d+)?[smhd]?$)/.test(words[k].text)
632
+ )
633
+ k += 1
634
+ }
635
+ }
636
+
637
+ /** Whether a package runner's arguments name a package the run lacks. */
638
+ function runsRemotePackage(args) {
639
+ if (args.some((a) => ["--no-install", "--no", "--offline"].includes(a)))
640
+ return false
641
+ const packages = []
642
+ for (let j = 0; j < args.length; j += 1) {
643
+ const a = args[j]
644
+ if (a === "-p" || a === "--package") {
645
+ if (args[j + 1]) packages.push(args[j + 1])
646
+ j += 1
647
+ } else if (a.startsWith("--package=")) packages.push(a.slice(10))
648
+ else if (!a.startsWith("-")) {
649
+ if (packages.length === 0) packages.push(a)
650
+ break
651
+ }
652
+ }
653
+ return packages.some((p) => !LOCAL_PACKAGES.has(p) && !/^[./]/.test(p))
654
+ }
655
+
656
+ /**
657
+ * Network use by a simple command itself: a network command, a package
658
+ * manager or git reaching a remote, a URL of a non-local host given to an
659
+ * interpreter. (Interpreter code is judged by `networkInCode`.)
660
+ */
661
+ function networkCommand(words) {
662
+ const cmd = commandOf(words)
663
+ if (cmd.length === 0) return []
664
+ const name = basename(cmd[0].text)
665
+ const args = cmd.slice(1).map((w) => w.text)
666
+ const hit = (reason) => [
667
+ {
668
+ value: cmd
669
+ .map((w) => w.text)
670
+ .join(" ")
671
+ .slice(0, 200),
672
+ reason,
673
+ network: true,
674
+ },
675
+ ]
676
+ const sub = args.find((a) => !a.startsWith("-"))
677
+ if (NETWORK_COMMANDS.has(name)) return hit("network command")
678
+ if (
679
+ name === "rsync" &&
680
+ args.some((a) => /^(rsync:\/\/|[\w.-]+(@[\w.-]+)?::?)/.test(a))
681
+ )
682
+ return hit("network command")
683
+ if (PACKAGE_RUNNERS.has(name) || name === "uvx" || name === "pipx") {
684
+ return name === "uvx" || name === "pipx" || runsRemotePackage(args)
685
+ ? hit("package runner that installs from the registry")
686
+ : []
687
+ }
688
+ if (PACKAGE_MANAGERS.has(name)) {
689
+ if (args.includes("--offline")) return []
690
+ const rest = args.slice(args.indexOf(sub) + 1)
691
+ if (name === "yarn" && sub === undefined)
692
+ return hit("package manager command that reaches the registry")
693
+ if (PACKAGE_RUN_SUBCOMMANDS[name].has(sub)) {
694
+ return runsRemotePackage(rest)
695
+ ? hit("package runner that installs from the registry")
696
+ : []
697
+ }
698
+ if (
699
+ REGISTRY_SUBCOMMANDS.has(sub) ||
700
+ (sub === "init" && rest.some((a) => !a.startsWith("-")))
701
+ )
702
+ return hit("package manager command that reaches the registry")
703
+ }
704
+ if ((name === "pip" || name === "pip3") && PIP_REMOTE.has(sub))
705
+ return hit("package manager command that reaches the registry")
706
+ if (name === "git") {
707
+ let j = 0
708
+ while (j < args.length && args[j].startsWith("-")) {
709
+ if (args[j] === "-C" || args[j] === "-c") j += 1
710
+ j += 1
711
+ }
712
+ if (
713
+ GIT_REMOTE.has(args[j]) ||
714
+ (args[j] === "remote" && args.slice(j + 1).includes("update"))
715
+ )
716
+ return hit("git command that reaches a remote")
717
+ }
718
+ if (INTERPRETERS.has(name)) {
719
+ const found = []
720
+ for (let j = 0; j < args.length; j += 1) {
721
+ const a = args[j]
722
+ if (a === "-m" && /^pip3?$/.test(args[j + 1] ?? "")) {
723
+ return PIP_REMOTE.has(args[j + 2])
724
+ ? hit("package manager command that reaches the registry")
725
+ : []
726
+ }
727
+ // Code (`-e …`, `--eval=…`) is judged by networkInCode.
728
+ if (CODE_FLAGS.has(a)) j += 1
729
+ else if (/^--(eval|print)=/.test(a)) continue
730
+ else {
731
+ for (const u of urlsIn(a).filter((u) => !u.local)) {
732
+ found.push({
733
+ value: u.url,
734
+ reason: "URL of a non-local host given to an interpreter",
735
+ network: true,
736
+ })
737
+ }
738
+ }
739
+ }
740
+ return found
741
+ }
742
+ return []
743
+ }
744
+
745
+ /** The words of a simple command that are paths, by command. */
746
+ function pathWords(words) {
747
+ let k = 0
748
+ // Leading `NAME=value` assignments: their values are paths too.
749
+ const out = []
750
+ while (k < words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[k].text)) {
751
+ out.push({ ...words[k], text: words[k].text.replace(/^[^=]*=/, "") })
752
+ k += 1
753
+ }
754
+ const rest = words.slice(k)
755
+ if (rest.length === 0) return { kind: "plain", paths: out }
756
+ const cmd = basename(rest[0].text)
757
+ const args = rest.slice(1)
758
+ if (cmd === "cd") return { kind: "cd", target: args[0], paths: out }
759
+ if (cmd === "xargs") {
760
+ let j = 0
761
+ while (j < args.length && args[j].text.startsWith("-")) j += 1
762
+ return { kind: "nested", words: args.slice(j), paths: out }
763
+ }
764
+ if (SHELLS.has(cmd)) {
765
+ const c = args.findIndex((a) => a.text === "-c")
766
+ if (c !== -1 && args[c + 1])
767
+ return { kind: "shell", script: args[c + 1].text, paths: out }
768
+ return {
769
+ kind: "plain",
770
+ paths: [...out, ...args.filter((a) => !a.text.startsWith("-"))],
771
+ }
772
+ }
773
+ if (
774
+ INTERPRETERS.has(cmd) ||
775
+ (cmd === "npx" && INTERPRETERS.has(basename(args[0]?.text ?? "")))
776
+ ) {
777
+ const code = []
778
+ const paths = [...out]
779
+ for (let j = 0; j < args.length; j += 1) {
780
+ const a = args[j].text
781
+ if (CODE_FLAGS.has(a) && args[j + 1]) {
782
+ code.push(args[j + 1].text)
783
+ j += 1
784
+ } else if (/^--(eval|print)=/.test(a)) code.push(a.replace(/^[^=]*=/, ""))
785
+ else if (!a.startsWith("-")) paths.push(args[j])
786
+ }
787
+ return { kind: "interpreter", code, paths }
788
+ }
789
+ if (PATTERN_COMMANDS.has(cmd)) {
790
+ const paths = [...out]
791
+ let patternSeen = false
792
+ for (let j = 0; j < args.length; j += 1) {
793
+ const a = args[j].text
794
+ if (a === "-e" || a === "--regexp" || a === "--expression") {
795
+ patternSeen = true
796
+ j += 1
797
+ } else if (a === "-f" || a === "--file") {
798
+ if (args[j + 1]) paths.push(args[j + 1])
799
+ j += 1
800
+ } else if (cmd.endsWith("awk") && a === "-v") {
801
+ j += 1
802
+ } else if (a.startsWith("-")) {
803
+ // an option (its numeric value, if any, is not path-like)
804
+ } else if (!patternSeen) patternSeen = true
805
+ else paths.push(args[j])
806
+ }
807
+ return { kind: "plain", paths }
808
+ }
809
+ return { kind: "plain", paths: [...out, ...args] }
810
+ }
811
+
812
+ /** Checks one shell word in a path position. */
813
+ function checkWord(word, cwd, check, flag, scanShell) {
814
+ for (const sub of word.subs) {
815
+ scanShell(sub.body, cwd)
816
+ if (PATHY_SUBST.test(sub.body) || sub.followedBySlash) {
817
+ flag({
818
+ value: `$(${sub.body})`,
819
+ reason: "command substitution in a path",
820
+ })
821
+ }
822
+ }
823
+ for (const v of word.vars) {
824
+ if (OUTSIDE_VARS.has(v.name)) {
825
+ flag({
826
+ value: `$${v.name}`,
827
+ reason: `$${v.name} points outside the run directory`,
828
+ })
829
+ } else if (v.name !== "HOME" && v.name !== "PWD" && v.followedBySlash) {
830
+ flag({ value: `$${v.name}`, reason: "unresolved variable in a path" })
831
+ }
832
+ }
833
+ const text = word.text
834
+ if (/^[a-z][a-z0-9+.-]*:\/\//i.test(text)) return // a URL
835
+ for (const part of text.split(/[=:,]/)) {
836
+ if (!part || part.startsWith("//")) continue
837
+ if (/\$\(|`/.test(part)) continue // reported as a substitution
838
+ if (/\$\{?(?!HOME\b|PWD\b)[A-Za-z_]/.test(part)) continue // reported as a variable
839
+ const pathy =
840
+ part.includes("/") ||
841
+ part === ".." ||
842
+ part.startsWith("~") ||
843
+ /\$\{?(HOME|PWD)\b/.test(part)
844
+ if (!pathy) continue
845
+ // Expand variables before cutting at a glob character (`${PWD}` has a `{`).
846
+ const expanded = part
847
+ .replace(/\$\{HOME\}|\$HOME\b/g, homedir())
848
+ .replace(/\$\{PWD\}|\$PWD\b/g, cwd)
849
+ check(globBase(expanded), cwd, "path outside the run directory", part)
850
+ }
851
+ }
852
+
853
+ /**
854
+ * Scans a shell command, starting in `cwd`; returns the cwd it leaves.
855
+ * `check(path, cwd, reason, shown?)` records a violation when `path`
856
+ * resolves outside; `flag(v)` records one that cannot be resolved, and
857
+ * network use. `written` maps each file the run has written (Write, Edit, a
858
+ * heredoc or echo into a file) to its text, so that a script written in the
859
+ * run and then run is judged as code; this command's writes are added to it.
860
+ */
861
+ function scanShell(
862
+ command,
863
+ startCwd,
864
+ allowed,
865
+ check,
866
+ flag,
867
+ written = new Map()
868
+ ) {
869
+ let cwd = startCwd
870
+ const { tokens, heredocs } = lexShell(command)
871
+ const segments = segmentsOf(tokens)
872
+ const recurse = (body, at) =>
873
+ scanShell(body, at, allowed, check, flag, written)
874
+ // Network use only, in text that runs as commands (a string given to
875
+ // child_process, a shell script written in the run): its words are not
876
+ // judged as paths here.
877
+ const networkOnly = (body, at) => {
878
+ const found = []
879
+ scanShell(
880
+ body,
881
+ at,
882
+ allowed,
883
+ () => {},
884
+ (v) => v.network && found.push(v),
885
+ new Map(written)
886
+ )
887
+ return found
888
+ }
889
+ const codeNetwork = (code, at, script) => {
890
+ for (const v of networkInCode(code, (text) => networkOnly(text, at))) {
891
+ flag(
892
+ script === undefined
893
+ ? v
894
+ : {
895
+ ...v,
896
+ value: `${script}: ${v.value}`,
897
+ reason: `${v.reason} (a script written in this run)`,
898
+ }
899
+ )
900
+ }
901
+ }
902
+ const writtenText = (word, at) => written.get(fileKey(expand(word.text, at)))
903
+ // What the previous command printed, when the scan knows it (an echo, a
904
+ // heredoc through cat, a cat of a file the run wrote): input to a pipe.
905
+ let printed
906
+ segments.forEach((seg, index) => {
907
+ const role = pathWords(seg.words)
908
+ const stdin = seg.piped ? printed : undefined
909
+ printed = undefined
910
+ if (role.kind === "cd") {
911
+ const target = role.target
912
+ if (
913
+ target &&
914
+ (target.subs.length > 0 ||
915
+ target.vars.some((v) => v.name !== "HOME" && v.name !== "PWD"))
916
+ ) {
917
+ checkWord(target, cwd, check, flag, recurse)
918
+ return
919
+ }
920
+ const next = expand(target?.text ?? "~", cwd)
921
+ if (!inside(next, allowed)) {
922
+ flag({
923
+ value: `cd ${target?.text ?? ""}`.trim(),
924
+ resolved: next,
925
+ reason: "cd out of the run directory",
926
+ })
927
+ }
928
+ cwd = next
929
+ return
930
+ }
931
+ if (seg.words.length > 0 && !inside(cwd, allowed)) {
932
+ flag({
933
+ value: seg.words
934
+ .map((w) => w.text)
935
+ .join(" ")
936
+ .slice(0, 200),
937
+ resolved: cwd,
938
+ reason: "command runs outside the run directory",
939
+ })
940
+ }
941
+ for (const w of role.paths) checkWord(w, cwd, check, flag, recurse)
942
+ for (const w of seg.redirects) checkWord(w, cwd, check, flag, recurse)
943
+ for (const v of networkCommand(seg.words)) flag(v)
944
+ if (role.kind === "nested") {
945
+ const nested = pathWords(role.words)
946
+ for (const w of nested.paths) checkWord(w, cwd, check, flag, recurse)
947
+ for (const v of networkCommand(role.words)) flag(v)
948
+ if (nested.kind === "interpreter") {
949
+ for (const code of nested.code) {
950
+ scanCode(code, cwd, check, { relative: true })
951
+ codeNetwork(code, cwd)
952
+ }
953
+ }
954
+ if (nested.kind === "shell") {
955
+ for (const v of networkOnly(nested.script, cwd)) flag(v)
956
+ }
957
+ }
958
+ if (role.kind === "shell") recurse(role.script, cwd)
959
+ if (role.kind === "interpreter") {
960
+ for (const code of role.code)
961
+ scanCode(code, cwd, check, { relative: true })
962
+ }
963
+ // Network use by code: judged on the command behind any wrapper
964
+ // (`env node -e …`). A script the run wrote and runs now is code too, and
965
+ // so is text piped into an interpreter or a shell.
966
+ const command = commandOf(seg.words)
967
+ const cmd = basename(command[0]?.text ?? "")
968
+ const run = pathWords(command)
969
+ if (run.kind === "interpreter") {
970
+ for (const code of run.code) codeNetwork(code, cwd)
971
+ for (const w of run.paths) {
972
+ const text = writtenText(w, cwd)
973
+ if (text !== undefined) codeNetwork(text, cwd, w.text)
974
+ }
975
+ if (stdin !== undefined && run.code.length === 0) codeNetwork(stdin, cwd)
976
+ }
977
+ if (run.kind === "shell") {
978
+ for (const v of networkOnly(run.script, cwd)) {
979
+ if (role.kind !== "shell") flag(v) // else `recurse` above found it
980
+ }
981
+ }
982
+ if (SHELLS.has(cmd) && run.kind === "plain") {
983
+ const scripts = run.paths
984
+ .map((w) => [w.text, writtenText(w, cwd)])
985
+ .filter(([, text]) => text !== undefined)
986
+ if (stdin !== undefined) scripts.push(["(stdin)", stdin])
987
+ for (const [name, text] of scripts) {
988
+ for (const v of networkOnly(text, cwd))
989
+ flag({ ...v, value: `${name}: ${v.value}` })
990
+ }
991
+ }
992
+ // Here-strings and heredocs: fed to a shell they are shell, fed to an
993
+ // interpreter they are code, otherwise they are data being written.
994
+ const fed = [
995
+ ...seg.herestrings.map((w) => w.text),
996
+ ...heredocs.filter((h) => h.segment === index).map((h) => h.body),
997
+ ]
998
+ const fedTo = basename(
999
+ seg.words.find((w) => !/^[A-Za-z_]\w*=/.test(w.text))?.text ?? ""
1000
+ )
1001
+ for (const body of fed) {
1002
+ if (SHELLS.has(fedTo)) recurse(body, cwd)
1003
+ else if (INTERPRETERS.has(fedTo))
1004
+ scanCode(body, cwd, check, { relative: true })
1005
+ else scanCode(body, cwd, check, { relative: false })
1006
+ // `cmd` sees through wrappers (`env node <<EOF`); paths keep `fedTo`.
1007
+ if (INTERPRETERS.has(cmd)) codeNetwork(body, cwd)
1008
+ else if (SHELLS.has(cmd) && !SHELLS.has(fedTo)) {
1009
+ for (const v of networkOnly(body, cwd)) flag(v)
1010
+ }
1011
+ }
1012
+ // What this command prints, and the files it writes with it.
1013
+ const args = command.slice(1)
1014
+ if (cmd === "echo" || cmd === "printf") {
1015
+ printed = args
1016
+ .filter((w) => !w.text.startsWith("-"))
1017
+ .map((w) => w.text)
1018
+ .join(" ")
1019
+ } else if (cmd === "cat" || cmd === "tee" || cmd === "") {
1020
+ const input =
1021
+ fed.length > 0
1022
+ ? fed.join("\n")
1023
+ : cmd === "cat"
1024
+ ? args.map((w) => writtenText(w, cwd)).find((t) => t !== undefined)
1025
+ : stdin
1026
+ printed = input
1027
+ if (cmd === "tee" && input !== undefined) {
1028
+ for (const w of args.filter((w) => !w.text.startsWith("-")))
1029
+ written.set(fileKey(expand(w.text, cwd)), input)
1030
+ }
1031
+ }
1032
+ for (const { word, append } of seg.outputs) {
1033
+ const path = fileKey(expand(word.text, cwd))
1034
+ if (printed === undefined) written.delete(path)
1035
+ else
1036
+ written.set(
1037
+ path,
1038
+ append ? `${written.get(path) ?? ""}${printed}\n` : `${printed}\n`
1039
+ )
1040
+ }
1041
+ if (seg.outputs.length > 0) printed = undefined
1042
+ })
1043
+ return cwd
1044
+ }
1045
+
1046
+ /** Applies an Edit (or one edit of a MultiEdit) to a file the run wrote. */
1047
+ function applyEdit(written, path, edit) {
1048
+ if (typeof edit?.new_string !== "string") return
1049
+ const before = written.get(path)
1050
+ const old = edit.old_string
1051
+ if (
1052
+ before !== undefined &&
1053
+ typeof old === "string" &&
1054
+ old !== "" &&
1055
+ before.includes(old)
1056
+ ) {
1057
+ written.set(
1058
+ path,
1059
+ edit.replace_all
1060
+ ? before.split(old).join(edit.new_string)
1061
+ : before.replace(old, () => edit.new_string)
1062
+ )
1063
+ } else {
1064
+ // A file the scan has not seen whole (one of the starter's): keep what
1065
+ // was put into it.
1066
+ written.set(path, `${before ?? ""}\n${edit.new_string}`)
1067
+ }
1068
+ }
1069
+
1070
+ // ---------------------------------------------------------------- transcripts
1071
+
1072
+ function* toolBlocks(value) {
1073
+ if (Array.isArray(value)) {
1074
+ for (const item of value) yield* toolBlocks(item)
1075
+ } else if (value && typeof value === "object") {
1076
+ if (value.type === "tool_use" || value.type === "tool_result") yield value
1077
+ for (const child of Object.values(value)) yield* toolBlocks(child)
1078
+ }
1079
+ }
1080
+
1081
+ /**
1082
+ * The CLI's persisted-output notice in one transcript message (header
1083
+ * comment), as `{ block, path }`: a top-level user message whose content is
1084
+ * one tool_result block, whose whole content is the notice, and whose
1085
+ * `tool_use_result` (the CLI's own record of the call, beside the content the
1086
+ * agent receives) has `persistedOutputPath` equal to the path the notice
1087
+ * names, with the message's `session_id` as that path's session segment.
1088
+ */
1089
+ function cliNotice(doc) {
1090
+ const content = doc?.type === "user" ? doc.message?.content : undefined
1091
+ if (!Array.isArray(content) || content.length !== 1) return undefined
1092
+ const [block] = content
1093
+ if (block?.type !== "tool_result" || typeof block.tool_use_id !== "string")
1094
+ return undefined
1095
+ const inner = block.content
1096
+ const text =
1097
+ typeof inner === "string"
1098
+ ? inner
1099
+ : Array.isArray(inner) &&
1100
+ inner.length === 1 &&
1101
+ inner[0]?.type === "text" &&
1102
+ typeof inner[0].text === "string"
1103
+ ? inner[0].text
1104
+ : undefined
1105
+ const match = text === undefined ? null : PERSISTED_NOTICE.exec(text)
1106
+ if (!match) return undefined
1107
+ const path = match[1]
1108
+ const meta = doc.tool_use_result
1109
+ if (
1110
+ !meta ||
1111
+ typeof meta !== "object" ||
1112
+ meta.persistedOutputPath !== path ||
1113
+ typeof doc.session_id !== "string" ||
1114
+ path.split("/").at(-3) !== doc.session_id
1115
+ )
1116
+ return undefined
1117
+ return { block, path }
1118
+ }
1119
+
1120
+ /**
1121
+ * Whether no part of `path` below `<run>` is a symlink, as far as the path
1122
+ * exists now: each component is lstat'ed, so a link is refused even when its
1123
+ * target is gone. What does not exist (the run removed, in a re-audit) has no
1124
+ * link to find.
1125
+ */
1126
+ function linkFree(path, run) {
1127
+ let at = run
1128
+ for (const part of path.slice(run.length + 1).split("/")) {
1129
+ at = join(at, part)
1130
+ let stat
1131
+ try {
1132
+ stat = lstatSync(at)
1133
+ } catch {
1134
+ return true
1135
+ }
1136
+ if (stat.isSymbolicLink()) return false
1137
+ }
1138
+ return true
1139
+ }
1140
+
1141
+ /** `<run>` for a run directory `<run>/work`, in each spelling roots() has. */
1142
+ function runRoots(runDir) {
1143
+ const work = [resolve(runDir), realpathOrSelf(resolve(runDir))]
1144
+ return [...new Set(work.filter((w) => basename(w) === "work").map(dirname))]
1145
+ }
1146
+
1147
+ function parseTranscript(text) {
1148
+ const docs = []
1149
+ const trimmed = text.trim()
1150
+ if (trimmed.startsWith("[")) {
1151
+ try {
1152
+ docs.push(JSON.parse(trimmed))
1153
+ return docs
1154
+ } catch {
1155
+ // fall through to line-by-line
1156
+ }
1157
+ }
1158
+ for (const line of text.split("\n")) {
1159
+ const l = line.trim()
1160
+ if (!l.startsWith("{") && !l.startsWith("[")) continue
1161
+ try {
1162
+ docs.push(JSON.parse(l))
1163
+ } catch {
1164
+ // a partial or non-JSON line
1165
+ }
1166
+ }
1167
+ return docs
1168
+ }
1169
+
1170
+ const resultText = (result) =>
1171
+ typeof result.content === "string"
1172
+ ? result.content
1173
+ : JSON.stringify(result.content ?? "")
1174
+
1175
+ /** The run directory a transcript's init message names, if any. */
1176
+ export function transcriptCwd(transcript) {
1177
+ for (const doc of parseTranscript(transcript)) {
1178
+ if (
1179
+ doc?.type === "system" &&
1180
+ doc.subtype === "init" &&
1181
+ typeof doc.cwd === "string"
1182
+ ) {
1183
+ return doc.cwd
1184
+ }
1185
+ }
1186
+ return undefined
1187
+ }
1188
+
1189
+ /**
1190
+ * @param {string} transcript the agent's stdout
1191
+ * @param {{ runDir: string }} options
1192
+ * @returns {{ contaminated: boolean, auditable: boolean, toolCalls: number,
1193
+ * attempts: number, violations: Array<{ tool: string, value: string,
1194
+ * resolved?: string, reason: string, blocked: boolean }>,
1195
+ * persistedReads: Array<{ tool: string, value: string }> }}
1196
+ * `persistedReads` are the reads of the CLI's persisted output that were
1197
+ * not counted as violations (header comment).
1198
+ */
1199
+ export function scanTranscript(transcript, { runDir }) {
1200
+ const allowed = roots(runDir)
1201
+ const docs = parseTranscript(transcript)
1202
+ if (docs.length === 0) {
1203
+ return {
1204
+ contaminated: true,
1205
+ auditable: false,
1206
+ toolCalls: 0,
1207
+ attempts: 0,
1208
+ violations: [
1209
+ {
1210
+ tool: "-",
1211
+ value: "",
1212
+ reason: "transcript holds no JSON: cannot be audited",
1213
+ blocked: false,
1214
+ },
1215
+ ],
1216
+ persistedReads: [],
1217
+ }
1218
+ }
1219
+ const uses = []
1220
+ const results = new Map()
1221
+ // Transcript order of every block, and the CLI's persisted-output notices.
1222
+ const order = new Map()
1223
+ const notices = []
1224
+ for (const doc of docs.flat()) {
1225
+ const notice = cliNotice(doc)
1226
+ for (const block of toolBlocks(doc)) {
1227
+ order.set(block, order.size)
1228
+ if (block.type === "tool_use" && typeof block.name === "string")
1229
+ uses.push(block)
1230
+ if (
1231
+ block.type === "tool_result" &&
1232
+ typeof block.tool_use_id === "string"
1233
+ ) {
1234
+ results.set(block.tool_use_id, block)
1235
+ if (block === notice?.block)
1236
+ notices.push({
1237
+ path: notice.path,
1238
+ at: order.get(block),
1239
+ id: block.tool_use_id,
1240
+ })
1241
+ }
1242
+ }
1243
+ }
1244
+ const violations = []
1245
+ const persistedReads = []
1246
+ const written = new Map()
1247
+ const usesById = new Map()
1248
+ const cleanCalls = new Set()
1249
+ const homes = runRoots(runDir)
1250
+ /** A Read or Grep of the CLI's persisted output (header comment). */
1251
+ const persistedRead = (use, input, field) => {
1252
+ const raw = input[field]
1253
+ if (PERSISTED_READERS[use.name] !== field || raw.split("/").includes(".."))
1254
+ return false
1255
+ const path = expand(raw, resolve(runDir))
1256
+ const at = order.get(use)
1257
+ const run = homes.find(
1258
+ (home) =>
1259
+ path.startsWith(home + sep) &&
1260
+ PERSISTED_PATH.test(path.slice(home.length + 1))
1261
+ )
1262
+ if (run === undefined || !linkFree(path, run)) return false
1263
+ return notices.some((notice) => {
1264
+ const producer = usesById.get(notice.id)
1265
+ return (
1266
+ notice.path === path &&
1267
+ notice.at < at &&
1268
+ producer !== undefined &&
1269
+ order.get(producer) < notice.at &&
1270
+ producer.name !== "Read" &&
1271
+ cleanCalls.has(producer)
1272
+ )
1273
+ })
1274
+ }
1275
+ let shellCwd = resolve(runDir)
1276
+ for (const use of uses) {
1277
+ if (typeof use.id === "string" && !usesById.has(use.id))
1278
+ usesById.set(use.id, use)
1279
+ const found = []
1280
+ const input = use.input && typeof use.input === "object" ? use.input : {}
1281
+ const flag = (v) => found.push(v)
1282
+ const check = (value, cwd, reason, shown = value) => {
1283
+ if (SAFE.has(value)) return
1284
+ const resolved = expand(value, cwd)
1285
+ if (SAFE.has(resolved) || inside(resolved, allowed)) return
1286
+ found.push({ value: shown, resolved, reason })
1287
+ }
1288
+ if (NETWORK_TOOLS.has(use.name)) {
1289
+ found.push({
1290
+ value: JSON.stringify(input).slice(0, 200),
1291
+ reason: "network tool",
1292
+ })
1293
+ }
1294
+ // Accepted reads of persisted output, listed only if the call is clean.
1295
+ const reads = []
1296
+ for (const field of PATH_FIELDS) {
1297
+ if (typeof input[field] === "string" && input[field] !== "") {
1298
+ if (persistedRead(use, input, field)) {
1299
+ reads.push({ tool: use.name, value: input[field] })
1300
+ continue
1301
+ }
1302
+ check(
1303
+ input[field],
1304
+ resolve(runDir),
1305
+ `${field} outside the run directory`
1306
+ )
1307
+ }
1308
+ }
1309
+ if (use.name === "Glob" && typeof input.pattern === "string") {
1310
+ check(
1311
+ globBase(input.pattern),
1312
+ resolve(runDir),
1313
+ "pattern outside the run directory"
1314
+ )
1315
+ }
1316
+ if (use.name === "Grep" && typeof input.glob === "string") {
1317
+ check(
1318
+ globBase(input.glob),
1319
+ resolve(runDir),
1320
+ "glob outside the run directory"
1321
+ )
1322
+ }
1323
+ if (typeof input.command === "string") {
1324
+ shellCwd = scanShell(
1325
+ input.command,
1326
+ shellCwd,
1327
+ allowed,
1328
+ check,
1329
+ flag,
1330
+ written
1331
+ )
1332
+ }
1333
+ // What the file tools write, for a script run later (scanShell).
1334
+ if (typeof input.file_path === "string" && input.file_path !== "") {
1335
+ const path = fileKey(expand(input.file_path, resolve(runDir)))
1336
+ if (use.name === "Write" && typeof input.content === "string")
1337
+ written.set(path, input.content)
1338
+ else if (use.name === "Edit") applyEdit(written, path, input)
1339
+ else if (use.name === "MultiEdit" && Array.isArray(input.edits)) {
1340
+ for (const edit of input.edits) applyEdit(written, path, edit)
1341
+ }
1342
+ }
1343
+ if (found.length === 0) {
1344
+ persistedReads.push(...reads)
1345
+ cleanCalls.add(use)
1346
+ continue
1347
+ }
1348
+ const result = results.get(use.id)
1349
+ const text = result === undefined ? "" : resultText(result)
1350
+ const refusals = text.split("\n").filter((line) => PERMISSION.test(line))
1351
+ for (const v of found) {
1352
+ const named = refusals.some(
1353
+ (line) =>
1354
+ (v.value && line.includes(v.value)) ||
1355
+ (v.resolved && line.includes(v.resolved))
1356
+ )
1357
+ const blocked =
1358
+ result !== undefined &&
1359
+ (result.is_error === true ||
1360
+ named ||
1361
+ (found.length === 1 && refusals.length > 0))
1362
+ violations.push({ tool: use.name, ...v, blocked })
1363
+ }
1364
+ }
1365
+ return {
1366
+ contaminated: violations.some((v) => !v.blocked),
1367
+ auditable: true,
1368
+ toolCalls: uses.length,
1369
+ attempts: violations.length,
1370
+ violations,
1371
+ persistedReads,
1372
+ }
1373
+ }