brepjs-verify 0.4.0 → 0.13.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.
- package/CHANGELOG.md +223 -0
- package/README.md +55 -25
- package/dist/brepjs-verify.cjs +1 -1
- package/dist/brepjs-verify.js +1 -1
- package/dist/cli/main.cjs +42 -3
- package/dist/cli/main.js +42 -3
- package/dist/cli/openBrowser.d.ts +23 -0
- package/dist/{diff-BNmCp_8I.cjs → diff-3ivpxBET.cjs} +83 -10
- package/dist/{diff-D5U3Ie2F.js → diff-DyilrTFJ.js} +83 -10
- package/dist/index.d.ts +1 -1
- package/dist/mcp/server.cjs +489 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.js +489 -0
- package/dist/mcp/tools.d.ts +77 -0
- package/dist/sandbox/runProgram.d.ts +76 -0
- package/dist/sandbox/runRecord.d.ts +20 -0
- package/dist/snapshot/shoot.cjs +2 -1
- package/dist/snapshot/shoot.d.ts +2 -0
- package/dist/snapshot/shoot.js +2 -1
- package/dist/verify/report.d.ts +13 -0
- package/package.json +12 -11
- package/viewer/dist/assets/brepjs-cDw_z4Rj.js +60 -0
- package/viewer/dist/assets/index-BBdw65cO.js +4167 -0
- package/viewer/dist/assets/kernelWorker-DeA3Hcd0.js +1 -0
- package/viewer/dist/index.html +1 -1
- package/viewer/dist/wasm/occt-wasm.wasm +0 -0
- package/viewer/dist/assets/brepjs-CI5VXw8W.js +0 -57
- package/viewer/dist/assets/index-CiN0lKoi.js +0 -4167
- package/viewer/dist/assets/kernelWorker-BtcMpY8t.js +0 -1
|
@@ -0,0 +1,489 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { spawn } from "node:child_process";
|
|
7
|
+
import { appendFile, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
8
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
9
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
10
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
//#region src/sandbox/runProgram.ts
|
|
13
|
+
/**
|
|
14
|
+
* Sandbox executor — run agent-authored brepjs TypeScript in an isolated child process.
|
|
15
|
+
*
|
|
16
|
+
* The program is written to a temp ESM directory and executed by the verify CLI in a separate
|
|
17
|
+
* process, with a wall-clock timeout and a memory cap. Out-of-process execution is what makes the
|
|
18
|
+
* loop *unattended-safe*: a runaway (CPU-bound infinite loop) or memory-hungry part is killed
|
|
19
|
+
* rather than hanging or crashing the host. Results cross the boundary as serialized JSON (never
|
|
20
|
+
* live WASM handles), and the temp program directory is always cleaned up.
|
|
21
|
+
*/
|
|
22
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
23
|
+
var DEFAULT_MAX_MEMORY_MB = 2048;
|
|
24
|
+
var MAX_OUTPUT_BYTES = 8 * 1024 * 1024;
|
|
25
|
+
/**
|
|
26
|
+
* In-flight sandbox process *groups*, keyed by group-leader pid. Tracked so a dying host can reap
|
|
27
|
+
* its runs (see installSandboxShutdownHandlers) — the per-run timeout can't, since its timer dies
|
|
28
|
+
* with the host.
|
|
29
|
+
*/
|
|
30
|
+
var activeGroups = /* @__PURE__ */ new Set();
|
|
31
|
+
/** SIGKILL (or `signal`) an entire detached process group by its leader pid; ignore if already gone. */
|
|
32
|
+
function killGroup(pid, signal) {
|
|
33
|
+
try {
|
|
34
|
+
process.kill(process.platform === "win32" ? pid : -pid, signal);
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Clamp a caller-supplied limit to a positive, finite value, falling back to the default otherwise.
|
|
39
|
+
* Critical for the timeout: Node's `execFile` treats `timeout: 0` (and it ignores negatives) as
|
|
40
|
+
* "no timeout", which would silently disable the sandbox's only runaway protection.
|
|
41
|
+
*/
|
|
42
|
+
function positiveOrDefault(value, fallback) {
|
|
43
|
+
return value !== void 0 && Number.isFinite(value) && value > 0 ? value : fallback;
|
|
44
|
+
}
|
|
45
|
+
function defaultCliEntry() {
|
|
46
|
+
const builtJs = fileURLToPath(new URL("data:video/mp2t;base64,IyEvdXNyL2Jpbi9lbnYgbm9kZQppbXBvcnQgeyBDb21tYW5kIH0gZnJvbSAnY29tbWFuZGVyJzsKaW1wb3J0IHsgd3JpdGVGaWxlU3luYywgd2F0Y2ggYXMgZnNXYXRjaCwgcmVhbHBhdGhTeW5jIH0gZnJvbSAnbm9kZTpmcyc7CmltcG9ydCB7IHJlc29sdmUsIGpvaW4sIGJhc2VuYW1lLCBkaXJuYW1lIH0gZnJvbSAnbm9kZTpwYXRoJzsKaW1wb3J0IHsgdG1wZGlyIH0gZnJvbSAnbm9kZTpvcyc7CmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7CmltcG9ydCB7IHJ1blBhcnQgfSBmcm9tICcuLi92ZXJpZnkvcnVuUGFydC5qcyc7CmltcG9ydCB7IHB1c2hFcnJvciwgcmVwb3J0T2ssIHNlcmlhbGl6ZVJlcG9ydCB9IGZyb20gJy4uL3ZlcmlmeS9yZXBvcnQuanMnOwppbXBvcnQgeyBydW5NZWFzdXJlIH0gZnJvbSAnLi4vdmVyaWZ5L21lYXN1cmUuanMnOwppbXBvcnQgeyBydW5EaWZmIH0gZnJvbSAnLi4vdmVyaWZ5L2RpZmYuanMnOwppbXBvcnQgeyBzY2FmZm9sZFBhcnQgfSBmcm9tICcuL3NjYWZmb2xkLmpzJzsKaW1wb3J0IHsgZGVib3VuY2UsIERFRkFVTFRfREVCT1VOQ0VfTVMgfSBmcm9tICcuL3dhdGNoLmpzJzsKaW1wb3J0IHsgZXhwb3J0UGFydCB9IGZyb20gJy4vZXhwb3J0UGFydC5qcyc7CmltcG9ydCB7IG9wZW5Ccm93c2VyLCBzaG91bGRBdXRvT3BlbiB9IGZyb20gJy4vb3BlbkJyb3dzZXIuanMnOwppbXBvcnQgeyBkaXNwb3NlU2hhcGUgfSBmcm9tICcuLi9kaXNwb3NlU2hhcGUuanMnOwppbXBvcnQgdHlwZSB7IHNob290IGFzIFNob290Rm4gfSBmcm9tICcuLi9zbmFwc2hvdC9zaG9vdC5qcyc7CgovLyBPQ0NUJ3MgV0FTTSBTVEVQIHdyaXRlciBlbWl0cyBhICJTdGF0aXN0aWNzIG9uIFRyYW5zZmVyIiBiYW5uZXIgdmlhIGNvbnNvbGUubG9nCi8vIChFbXNjcmlwdGVuJ3MgZGVmYXVsdCBzdGRvdXQgc2luaykuIFRoZSBDTEkgb3ducyBzdGRvdXQgZm9yIG1hY2hpbmUtcmVhZGFibGUgSlNPTiwKLy8gc28gZGl2ZXJ0IHRoYXQga2VybmVsIGNoYXR0ZXIgdG8gc3RkZXJyIHRvIGtlZXAgc3Rkb3V0IGEgc2luZ2xlIGNsZWFuIEpTT04gZG9jdW1lbnQuCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlIC0tIHJlcm91dGUga2VybmVsIHN0ZG91dCBjaGF0dGVyIG9mZiB0aGUgSlNPTiBjaGFubmVsCmNvbnNvbGUubG9nID0gKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gewogIHByb2Nlc3Muc3RkZXJyLndyaXRlKGFyZ3MubWFwKFN0cmluZykuam9pbignICcpICsgJ1xuJyk7Cn07CgpleHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZFNuYXBzaG90U2hvb3QoKTogUHJvbWlzZTx0eXBlb2YgU2hvb3RGbiB8IHVuZGVmaW5lZD4gewogIHRyeSB7CiAgICBjb25zdCBtb2QgPSBhd2FpdCBpbXBvcnQoJy4uL3NuYXBzaG90L3Nob290LmpzJyk7CiAgICByZXR1cm4gbW9kLnNob290OwogIH0gY2F0Y2ggewogICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoJ3NuYXBzaG90cyBuZWVkIHB1cHBldGVlci9DaHJvbWUg4oCUIHJ1bjogbnBtIGkgcHVwcGV0ZWVyXG4nKTsKICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9Cn0KCmNvbnN0IHByb2dyYW0gPSBuZXcgQ29tbWFuZCgpOwpwcm9ncmFtLm5hbWUoJ2JyZXBqcy12ZXJpZnknKTsKCnByb2dyYW0KICAuY29tbWFuZCgndmVyaWZ5JywgeyBpc0RlZmF1bHQ6IHRydWUgfSkKICAuYXJndW1lbnQoJzxmaWxlPicsICdwYXRoIHRvIGEgLmJyZXAudHMgbW9kdWxlIHdpdGggYSBkZWZhdWx0LWV4cG9ydGVkIHBhcnQgZnVuY3Rpb24nKQogIC5vcHRpb24oJy0tc3RlcCA8b3V0PicsICd3cml0ZSB0aGUgcHJpbWFyeSBTVEVQIGFydGlmYWN0IHRvIHRoaXMgcGF0aCcpCiAgLm9wdGlvbignLS1nbGIgPG91dD4nLCAnd3JpdGUgYSBkZXJpdmVkIEdMQiBwcmV2aWV3IHRvIHRoaXMgcGF0aCcpCiAgLm9wdGlvbignLS1qc29uIDxvdXQ+JywgJ3dyaXRlIHRoZSBKU09OIHJlcG9ydCB0byB0aGlzIHBhdGgnKQogIC5vcHRpb24oCiAgICAnLS1jaGVjaycsCiAgICAndHlwZS1jaGVjayB0aGUgcGFydCAoYWdhaW5zdCBicmVwanMgdHlwZXMpIGJlZm9yZSBydW5uaW5nOyBza2lwIGV4ZWN1dGlvbiBvbiB0eXBlIGVycm9ycycKICApCiAgLm9wdGlvbignLS1zbmFwc2hvdCA8ZGlyPicsICdyZW5kZXIgaXNvL2Zyb250L3RvcC9yaWdodCBQTkdzIHRvIHRoaXMgZGlyIChyZXF1aXJlcyBidWlsdCB2aWV3ZXIpJykKICAub3B0aW9uKAogICAgJy0tc2VydmUnLAogICAgJ2FmdGVyIHZlcmlmeWluZywgc3RhcnQgYSBwcmV2aWV3IHNlcnZlciBhbmQgcHJpbnQgYSA/ZGlyPSZmaWxlPSBkZWVwIGxpbmsgKHN0YXlzIHJ1bm5pbmcpJwogICkKICAub3B0aW9uKCctLW5vLW9wZW4nLCAnd2l0aCAtLXNlcnZlLCBkbyBub3QgYXV0by1vcGVuIHRoZSBicm93c2VyIChqdXN0IHByaW50IHRoZSB2aWV3ZXIgVVJMKScpCiAgLmFjdGlvbigKICAgIGFzeW5jICgKICAgICAgZmlsZTogc3RyaW5nLAogICAgICBvcHRzOiB7CiAgICAgICAgc3RlcD86IHN0cmluZzsKICAgICAgICBnbGI/OiBzdHJpbmc7CiAgICAgICAganNvbj86IHN0cmluZzsKICAgICAgICBjaGVjaz86IGJvb2xlYW47CiAgICAgICAgc25hcHNob3Q/OiBzdHJpbmc7CiAgICAgICAgc2VydmU/OiBib29sZWFuOwogICAgICAgIC8vIENvbW1hbmRlciBtYXRlcmlhbGl6ZXMgYSBuZWdhdGVkIGZsYWcgYXMgYSBjb25jcmV0ZSBib29sZWFuOiB0cnVlIGJ5CiAgICAgICAgLy8gZGVmYXVsdCwgZmFsc2Ugd2hlbiAtLW5vLW9wZW4gaXMgcGFzc2VkLgogICAgICAgIG9wZW46IGJvb2xlYW47CiAgICAgIH0KICAgICkgPT4gewogICAgICAvLyBUaGUgV0FTTSB2aWV3ZXIgbG9hZHMgYSBDQUQgZmlsZSAoaXQgY2FuJ3QgcnVuIGEgLmJyZXAudHMpLCBzbyAtLXNuYXBzaG90Ly0tc2VydmUKICAgICAgLy8gc3RhZ2UgdGhlIHByaW1hcnkgU1RFUCBhbmQgcG9pbnQgdGhlIHZpZXdlciBhdCBpdCB2aWEgP2Rpcj0mZmlsZT0uIC0tZ2xiIGlzIGl0cyBvd24gYXJ0aWZhY3QuCiAgICAgIGNvbnN0IHdhbnRTdGVwID0gQm9vbGVhbihvcHRzLnN0ZXApIHx8IEJvb2xlYW4ob3B0cy5zbmFwc2hvdCkgfHwgQm9vbGVhbihvcHRzLnNlcnZlKTsKICAgICAgY29uc3QgeyByZXBvcnQsIHN0ZXAsIGdsYiwgc2hhcGUgfSA9IGF3YWl0IHJ1blBhcnQocmVzb2x2ZShmaWxlKSwgewogICAgICAgIHN0ZXA6IHdhbnRTdGVwLAogICAgICAgIGdsYjogQm9vbGVhbihvcHRzLmdsYiksCiAgICAgICAgY2hlY2s6IEJvb2xlYW4ob3B0cy5jaGVjayksCiAgICAgIH0pOwogICAgICBsZXQgc3RlcFBhdGg6IHN0cmluZyB8IHVuZGVmaW5lZCA9IG9wdHMuc3RlcDsKICAgICAgdHJ5IHsKICAgICAgICBpZiAob3B0cy5nbGIgJiYgZ2xiKSB3cml0ZUZpbGVTeW5jKG9wdHMuZ2xiLCBCdWZmZXIuZnJvbShnbGIpKTsKCiAgICAgICAgaWYgKHdhbnRTdGVwICYmIHN0ZXApIHsKICAgICAgICAgIHN0ZXBQYXRoID0gb3B0cy5zdGVwID8/IGpvaW4odG1wZGlyKCksIGBicmVwanMtdmVyaWZ5LSR7YmFzZW5hbWUoZmlsZSl9LnN0ZXBgKTsKICAgICAgICAgIHdyaXRlRmlsZVN5bmMoc3RlcFBhdGgsIEJ1ZmZlci5mcm9tKHN0ZXApKTsKICAgICAgICB9CiAgICAgICAgaWYgKG9wdHMuc25hcHNob3QgJiYgc3RlcFBhdGgpIHsKICAgICAgICAgIGNvbnN0IHNob290ID0gYXdhaXQgbG9hZFNuYXBzaG90U2hvb3QoKTsgLy8gbGF6eToga2VlcHMgcHVwcGV0ZWVyIG9mZiB0aGUgZGVmYXVsdCBwYXRoCiAgICAgICAgICBpZiAoc2hvb3QpIHsKICAgICAgICAgICAgY29uc3QgeyBwbmdzIH0gPSBhd2FpdCBzaG9vdCh7IGZpbGU6IHN0ZXBQYXRoLCBvdXREaXI6IG9wdHMuc25hcHNob3QgfSk7CiAgICAgICAgICAgIC8vIERpYWdub3N0aWMgcGF0aHMgZ28gdG8gc3RkZXJyIOKAlCBzdGRvdXQgc3RheXMgYSBzaW5nbGUgY2xlYW4gSlNPTiBkb2N1bWVudC4KICAgICAgICAgICAgZm9yIChjb25zdCBwIG9mIHBuZ3MpIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBzbmFwc2hvdDogJHtwfVxuYCk7CiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIGlmIChvcHRzLnNuYXBzaG90KSB7CiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZSgnc25hcHNob3Qgc2tpcHBlZDogU1RFUCBleHBvcnQgcHJvZHVjZWQgbm8gYXJ0aWZhY3RcbicpOwogICAgICAgIH0KICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIHB1c2hFcnJvcihyZXBvcnQsIHsgbWVzc2FnZTogYGFydGlmYWN0IHdyaXRlIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gIH0pOwogICAgICB9IGZpbmFsbHkgewogICAgICAgIC8vIFRoZSBzaGFwZSBpcyBhIGxpdmUgV0FTTSBoYW5kbGU7IHJlbGVhc2UgaXQgYmVmb3JlIHRoZSBzZXJ2ZXIgdGFrZXMgb3ZlciAodGhlCiAgICAgICAgLy8gLS1zZXJ2ZSBwYXRoIHN0YXlzIHJ1bm5pbmcsIHNvIGxlYWtpbmcgaGVyZSB3b3VsZCBwZXJzaXN0IGZvciB0aGUgc2VydmVyJ3MgbGlmZXRpbWUpLgogICAgICAgIGRpc3Bvc2VTaGFwZShzaGFwZSk7CiAgICAgIH0KICAgICAgLy8gU2VyaWFsaXplIG9uY2UgYWZ0ZXIgYWxsIGFydGlmYWN0IHdyaXRlcyBzbyB0aGUgLS1qc29uIGZpbGUgYW5kIHN0ZG91dAogICAgICAvLyByZWZsZWN0IHRoZSBzYW1lIHJlcG9ydCAoaW5jbC4gYW55ICJhcnRpZmFjdCB3cml0ZSBmYWlsZWQiIGVycm9yKS4KICAgICAgY29uc3QganNvbiA9IHNlcmlhbGl6ZVJlcG9ydChyZXBvcnQpOwogICAgICBpZiAob3B0cy5qc29uKSB3cml0ZUZpbGVTeW5jKG9wdHMuanNvbiwganNvbik7CiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKGpzb24gKyAnXG4nKTsKICAgICAgaWYgKCFyZXBvcnRPayhyZXBvcnQpKSBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgICAgY29uc3Qgd2lsbFNlcnZlID0gQm9vbGVhbihvcHRzLnNlcnZlKSAmJiBzdGVwUGF0aCAhPT0gdW5kZWZpbmVkICYmIHJlcG9ydE9rKHJlcG9ydCk7CiAgICAgIGlmICh3aWxsU2VydmUgJiYgc3RlcFBhdGgpIHsKICAgICAgICBjb25zdCB7IHNlcnZlIH0gPSBhd2FpdCBpbXBvcnQoJy4uL3NuYXBzaG90L3NlcnZlLmpzJyk7IC8vIGxhenk6IG5vIHNlcnZlciBkZXBzIG9uIHRoZSBkZWZhdWx0IHBhdGgKICAgICAgICBjb25zdCB7IHVybCwgcmV1c2VkIH0gPSBhd2FpdCBzZXJ2ZSh7IGZpbGU6IHN0ZXBQYXRoIH0pOyAvLyBidWlsZHMgYSA/ZGlyPSZmaWxlPSBVUkw7IHNlcnZlciBydW5zIHVudGlsIEN0cmwtQwogICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGB2aWV3ZXI6ICR7dXJsfVxuYCk7CiAgICAgICAgLy8gQXV0by1vcGVuIG9ubHkgZm9yIGEgZnJlc2hseSBzdGFydGVkIHNlcnZlciAoYSByZXVzZWQgb25lIGFscmVhZHkgaGFzCiAgICAgICAgLy8gYSB0YWIpIGluIGFuIGludGVyYWN0aXZlIHNlc3Npb247IGAtLW5vLW9wZW5gIChvcHRzLm9wZW4gPT09IGZhbHNlKQogICAgICAgIC8vIGFsd2F5cyBzdXBwcmVzc2VzIGl0LgogICAgICAgIGlmICghcmV1c2VkICYmIG9wdHMub3BlbiAmJiBzaG91bGRBdXRvT3BlbigpKSB7CiAgICAgICAgICBvcGVuQnJvd3Nlcih1cmwpOwogICAgICAgIH0KICAgICAgfQogICAgfQogICk7Cgpwcm9ncmFtCiAgLmNvbW1hbmQoJ21lYXN1cmUnKQogIC5hcmd1bWVudCgnPGE+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGUnKQogIC5hcmd1bWVudCgnW2JdJywgJ29wdGlvbmFsIHNlY29uZCBtb2R1bGU7IGlmIGdpdmVuLCBtZWFzdXJlcyBkaXN0YW5jZSBiZXR3ZWVuIHRoZSB0d28gcGFydHMnKQogIC5hY3Rpb24oYXN5bmMgKGE6IHN0cmluZywgYj86IHN0cmluZykgPT4gewogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcnVuTWVhc3VyZShyZXNvbHZlKGEpLCBiID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiByZXNvbHZlKGIpKTsKICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICBKU09OLnN0cmluZ2lmeSh7IG9rOiByZXN1bHQuZXJyb3JzLmxlbmd0aCA9PT0gMCwgLi4ucmVzdWx0IH0sIG51bGwsIDIpICsgJ1xuJwogICAgKTsKICAgIGlmIChyZXN1bHQuZXJyb3JzLmxlbmd0aCA+IDApIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdkaWZmJykKICAuYXJndW1lbnQoJzxhPicsICdwYXRoIHRvIHRoZSBiYXNlbGluZSAuYnJlcC50cyBtb2R1bGUnKQogIC5hcmd1bWVudCgnPGI+JywgJ3BhdGggdG8gdGhlIGNvbXBhcmlzb24gLmJyZXAudHMgbW9kdWxlJykKICAuYWN0aW9uKGFzeW5jIChhOiBzdHJpbmcsIGI6IHN0cmluZykgPT4gewogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcnVuRGlmZihyZXNvbHZlKGEpLCByZXNvbHZlKGIpKTsKICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICBKU09OLnN0cmluZ2lmeSh7IG9rOiByZXN1bHQuZXJyb3JzLmxlbmd0aCA9PT0gMCwgLi4ucmVzdWx0IH0sIG51bGwsIDIpICsgJ1xuJwogICAgKTsKICAgIGlmIChyZXN1bHQuZXJyb3JzLmxlbmd0aCA+IDApIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdpbml0JykKICAuYXJndW1lbnQoJzxuYW1lPicsICdwYXJ0IG5hbWU7IHNjYWZmb2xkcyA8bmFtZT4uYnJlcC50cyArIHRzY29uZmlnLmpzb24gKyBSRUFETUUubWQnKQogIC5vcHRpb24oJy0tb3V0IDxkaXI+JywgJ3RhcmdldCBkaXJlY3RvcnkgKGRlZmF1bHRzIHRvIC4vPG5hbWU+KScpCiAgLmFjdGlvbigobmFtZTogc3RyaW5nLCBvcHRzOiB7IG91dD86IHN0cmluZyB9KSA9PiB7CiAgICBjb25zdCBkaXIgPSByZXNvbHZlKG9wdHMub3V0ID8/IG5hbWUpOwogICAgY29uc3QgcmVzdWx0ID0gc2NhZmZvbGRQYXJ0KG5hbWUsIGRpcik7CiAgICBmb3IgKGNvbnN0IGYgb2YgcmVzdWx0LmZpbGVzKSB7CiAgICAgIGNvbnN0IHRhZyA9IGYuY3JlYXRlZCA/ICdjcmVhdGVkJyA6ICdleGlzdHMgKGtlcHQpJzsKICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYCR7dGFnfTogJHtmLnBhdGh9XG5gKTsKICAgIH0KICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKEpTT04uc3RyaW5naWZ5KHJlc3VsdCwgbnVsbCwgMikgKyAnXG4nKTsKICB9KTsKCnByb2dyYW0KICAuY29tbWFuZCgnd2F0Y2gnKQogIC5hcmd1bWVudCgnPGZpbGU+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGU7IHJlLXZlcmlmaWVzIG9uIGVhY2ggc2F2ZSB1bnRpbCBDdHJsLUMnKQogIC5hY3Rpb24oKGZpbGU6IHN0cmluZykgPT4gewogICAgY29uc3QgcGF0aCA9IHJlc29sdmUoZmlsZSk7CiAgICBjb25zdCBydW4gPSBhc3luYyAoKSA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgY29uc3QgeyByZXBvcnQsIHNoYXBlIH0gPSBhd2FpdCBydW5QYXJ0KHBhdGgpOwogICAgICAgIHRyeSB7CiAgICAgICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShzZXJpYWxpemVSZXBvcnQocmVwb3J0KSArICdcbicpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICBkaXNwb3NlU2hhcGUoc2hhcGUpOyAvLyBsaXZlIFdBU00gaGFuZGxlOyB0aGUgbG9vcCBydW5zIGluZGVmaW5pdGVseQogICAgICAgIH0KICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGB3YXRjaCBydW4gZmFpbGVkOiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfVxuYCk7CiAgICAgIH0KICAgIH07CiAgICBjb25zdCB7IHRyaWdnZXIgfSA9IGRlYm91bmNlKHJ1biwgREVGQVVMVF9ERUJPVU5DRV9NUyk7CiAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgd2F0Y2hpbmcgJHtwYXRofSAoQ3RybC1DIHRvIHN0b3ApXG5gKTsKICAgIHZvaWQgcnVuKCk7IC8vIGluaXRpYWwgdmVyaWZ5CiAgICAvLyBXYXRjaCB0aGUgcGFyZW50IGRpcjogZWRpdG9ycyBvZnRlbiByZXBsYWNlIHRoZSBmaWxlIChyZW5hbWUpIG9uIHNhdmUsCiAgICAvLyB3aGljaCBkcm9wcyBhIHdhdGNoZXIgYm91bmQgdG8gdGhlIGZpbGUgaW5vZGUgaXRzZWxmLgogICAgY29uc3Qgd2F0Y2hlciA9IGZzV2F0Y2goZGlybmFtZShwYXRoKSwgKF9ldmVudCwgZmlsZW5hbWUpID0+IHsKICAgICAgaWYgKGZpbGVuYW1lID09PSB1bmRlZmluZWQgfHwgZmlsZW5hbWUgPT09IG51bGwpIHsKICAgICAgICB0cmlnZ2VyKCk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGlmIChiYXNlbmFtZShwYXRoKSA9PT0gZmlsZW5hbWUudG9TdHJpbmcoKSkgdHJpZ2dlcigpOwogICAgfSk7CiAgICBjb25zdCBzdG9wID0gKCkgPT4gewogICAgICB3YXRjaGVyLmNsb3NlKCk7CiAgICAgIHByb2Nlc3MuZXhpdCgwKTsKICAgIH07CiAgICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBzdG9wKTsKICAgIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBzdG9wKTsgLy8gc3VwZXJ2aXNvcnMgKGRvY2tlciBzdG9wLCBzeXN0ZW1jdGwpIHNlbmQgU0lHVEVSTQogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdleHBvcnQnKQogIC5hcmd1bWVudCgnPGZpbGU+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGUnKQogIC5vcHRpb24oJy0tc3RlcCcsICd3cml0ZSBhIFNURVAgYXJ0aWZhY3QnKQogIC5vcHRpb24oJy0tZ2xiJywgJ3dyaXRlIGEgR0xCIGFydGlmYWN0JykKICAub3B0aW9uKCctLXN0bCcsICd3cml0ZSBhbiBTVEwgYXJ0aWZhY3QnKQogIC5vcHRpb24oJy0tYWxsJywgJ3dyaXRlIFNURVAgKyBHTEIgKyBTVEwnKQogIC5vcHRpb24oJy0tb3V0IDxkaXI+JywgJ291dHB1dCBkaXJlY3RvcnknLCAnLicpCiAgLmFjdGlvbigKICAgIGFzeW5jICgKICAgICAgZmlsZTogc3RyaW5nLAogICAgICBvcHRzOiB7IHN0ZXA/OiBib29sZWFuOyBnbGI/OiBib29sZWFuOyBzdGw/OiBib29sZWFuOyBhbGw/OiBib29sZWFuOyBvdXQ6IHN0cmluZyB9CiAgICApID0+IHsKICAgICAgY29uc3QgZm9ybWF0cyA9IG9wdHMuYWxsCiAgICAgICAgPyB7IHN0ZXA6IHRydWUsIGdsYjogdHJ1ZSwgc3RsOiB0cnVlIH0KICAgICAgICA6IHsgc3RlcDogQm9vbGVhbihvcHRzLnN0ZXApLCBnbGI6IEJvb2xlYW4ob3B0cy5nbGIpLCBzdGw6IEJvb2xlYW4ob3B0cy5zdGwpIH07CiAgICAgIGlmICghZm9ybWF0cy5zdGVwICYmICFmb3JtYXRzLmdsYiAmJiAhZm9ybWF0cy5zdGwpIHsKICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZSgnbm8gZm9ybWF0cyByZXF1ZXN0ZWQg4oCUIHBhc3MgLS1zdGVwLy0tZ2xiLy0tc3RsIG9yIC0tYWxsXG4nKTsKICAgICAgICBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZXhwb3J0UGFydChyZXNvbHZlKGZpbGUpLCBmb3JtYXRzLCByZXNvbHZlKG9wdHMub3V0KSk7CiAgICAgIGZvciAoY29uc3QgcCBvZiByZXN1bHQud3JpdHRlbikgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYHdyb3RlOiAke3B9XG5gKTsKICAgICAgZm9yIChjb25zdCBlIG9mIHJlc3VsdC5lcnJvcnMpIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBlcnJvcjogJHtlfVxuYCk7CiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICAgIEpTT04uc3RyaW5naWZ5KHsgb2s6IHJlc3VsdC5vaywgd3JpdHRlbjogcmVzdWx0LndyaXR0ZW4sIGVycm9yczogcmVzdWx0LmVycm9ycyB9LCBudWxsLCAyKSArCiAgICAgICAgICAnXG4nCiAgICAgICk7CiAgICAgIGlmICghcmVzdWx0Lm9rKSBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgIH0KICApOwoKLy8gT25seSBkcml2ZSB0aGUgQ0xJIHdoZW4gcnVuIGFzIHRoZSBlbnRyeSBzY3JpcHQsIHNvIHRlc3RzIGNhbiBpbXBvcnQgdGhlCi8vIGd1YXJkZWQgbG9hZGVycyB3aXRob3V0IGNvbW1hbmRlciBwYXJzaW5nIHRoZSB0ZXN0IHJ1bm5lcidzIGFyZ3YuCi8vIFJlc29sdmUgc3ltbGlua3Mgb24gYm90aCBzaWRlczogdGhlIG5wbS1pbnN0YWxsZWQgYmluIChub2RlX21vZHVsZXMvLmJpbi9icmVwanMpCi8vIGlzIGEgc3ltbGluaywgc28gcHJvY2Vzcy5hcmd2WzFdIHdvdWxkIG90aGVyd2lzZSBuZXZlciBlcXVhbCB0aGUgcmVhbCBtb2R1bGUgcGF0aC4KZXhwb3J0IGZ1bmN0aW9uIGlzRW50cnlwb2ludChhcmd2MTogc3RyaW5nIHwgdW5kZWZpbmVkLCBtb2R1bGVVcmw6IHN0cmluZyk6IGJvb2xlYW4gewogIGlmICghYXJndjEpIHJldHVybiBmYWxzZTsKICB0cnkgewogICAgcmV0dXJuIHJlYWxwYXRoU3luYyhhcmd2MSkgPT09IHJlYWxwYXRoU3luYyhmaWxlVVJMVG9QYXRoKG1vZHVsZVVybCkpOwogIH0gY2F0Y2ggewogICAgcmV0dXJuIGZhbHNlOwogIH0KfQoKaWYgKGlzRW50cnlwb2ludChwcm9jZXNzLmFyZ3ZbMV0sIGltcG9ydC5tZXRhLnVybCkpIHsKICB2b2lkIHByb2dyYW0ucGFyc2VBc3luYygpOwp9Cg==", "" + import.meta.url));
|
|
47
|
+
if (existsSync(builtJs)) return builtJs;
|
|
48
|
+
return fileURLToPath(new URL("data:video/mp2t;base64,IyEvdXNyL2Jpbi9lbnYgbm9kZQppbXBvcnQgeyBDb21tYW5kIH0gZnJvbSAnY29tbWFuZGVyJzsKaW1wb3J0IHsgd3JpdGVGaWxlU3luYywgd2F0Y2ggYXMgZnNXYXRjaCwgcmVhbHBhdGhTeW5jIH0gZnJvbSAnbm9kZTpmcyc7CmltcG9ydCB7IHJlc29sdmUsIGpvaW4sIGJhc2VuYW1lLCBkaXJuYW1lIH0gZnJvbSAnbm9kZTpwYXRoJzsKaW1wb3J0IHsgdG1wZGlyIH0gZnJvbSAnbm9kZTpvcyc7CmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tICdub2RlOnVybCc7CmltcG9ydCB7IHJ1blBhcnQgfSBmcm9tICcuLi92ZXJpZnkvcnVuUGFydC5qcyc7CmltcG9ydCB7IHB1c2hFcnJvciwgcmVwb3J0T2ssIHNlcmlhbGl6ZVJlcG9ydCB9IGZyb20gJy4uL3ZlcmlmeS9yZXBvcnQuanMnOwppbXBvcnQgeyBydW5NZWFzdXJlIH0gZnJvbSAnLi4vdmVyaWZ5L21lYXN1cmUuanMnOwppbXBvcnQgeyBydW5EaWZmIH0gZnJvbSAnLi4vdmVyaWZ5L2RpZmYuanMnOwppbXBvcnQgeyBzY2FmZm9sZFBhcnQgfSBmcm9tICcuL3NjYWZmb2xkLmpzJzsKaW1wb3J0IHsgZGVib3VuY2UsIERFRkFVTFRfREVCT1VOQ0VfTVMgfSBmcm9tICcuL3dhdGNoLmpzJzsKaW1wb3J0IHsgZXhwb3J0UGFydCB9IGZyb20gJy4vZXhwb3J0UGFydC5qcyc7CmltcG9ydCB7IG9wZW5Ccm93c2VyLCBzaG91bGRBdXRvT3BlbiB9IGZyb20gJy4vb3BlbkJyb3dzZXIuanMnOwppbXBvcnQgeyBkaXNwb3NlU2hhcGUgfSBmcm9tICcuLi9kaXNwb3NlU2hhcGUuanMnOwppbXBvcnQgdHlwZSB7IHNob290IGFzIFNob290Rm4gfSBmcm9tICcuLi9zbmFwc2hvdC9zaG9vdC5qcyc7CgovLyBPQ0NUJ3MgV0FTTSBTVEVQIHdyaXRlciBlbWl0cyBhICJTdGF0aXN0aWNzIG9uIFRyYW5zZmVyIiBiYW5uZXIgdmlhIGNvbnNvbGUubG9nCi8vIChFbXNjcmlwdGVuJ3MgZGVmYXVsdCBzdGRvdXQgc2luaykuIFRoZSBDTEkgb3ducyBzdGRvdXQgZm9yIG1hY2hpbmUtcmVhZGFibGUgSlNPTiwKLy8gc28gZGl2ZXJ0IHRoYXQga2VybmVsIGNoYXR0ZXIgdG8gc3RkZXJyIHRvIGtlZXAgc3Rkb3V0IGEgc2luZ2xlIGNsZWFuIEpTT04gZG9jdW1lbnQuCi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1jb25zb2xlIC0tIHJlcm91dGUga2VybmVsIHN0ZG91dCBjaGF0dGVyIG9mZiB0aGUgSlNPTiBjaGFubmVsCmNvbnNvbGUubG9nID0gKC4uLmFyZ3M6IHVua25vd25bXSkgPT4gewogIHByb2Nlc3Muc3RkZXJyLndyaXRlKGFyZ3MubWFwKFN0cmluZykuam9pbignICcpICsgJ1xuJyk7Cn07CgpleHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZFNuYXBzaG90U2hvb3QoKTogUHJvbWlzZTx0eXBlb2YgU2hvb3RGbiB8IHVuZGVmaW5lZD4gewogIHRyeSB7CiAgICBjb25zdCBtb2QgPSBhd2FpdCBpbXBvcnQoJy4uL3NuYXBzaG90L3Nob290LmpzJyk7CiAgICByZXR1cm4gbW9kLnNob290OwogIH0gY2F0Y2ggewogICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoJ3NuYXBzaG90cyBuZWVkIHB1cHBldGVlci9DaHJvbWUg4oCUIHJ1bjogbnBtIGkgcHVwcGV0ZWVyXG4nKTsKICAgIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogICAgcmV0dXJuIHVuZGVmaW5lZDsKICB9Cn0KCmNvbnN0IHByb2dyYW0gPSBuZXcgQ29tbWFuZCgpOwpwcm9ncmFtLm5hbWUoJ2JyZXBqcy12ZXJpZnknKTsKCnByb2dyYW0KICAuY29tbWFuZCgndmVyaWZ5JywgeyBpc0RlZmF1bHQ6IHRydWUgfSkKICAuYXJndW1lbnQoJzxmaWxlPicsICdwYXRoIHRvIGEgLmJyZXAudHMgbW9kdWxlIHdpdGggYSBkZWZhdWx0LWV4cG9ydGVkIHBhcnQgZnVuY3Rpb24nKQogIC5vcHRpb24oJy0tc3RlcCA8b3V0PicsICd3cml0ZSB0aGUgcHJpbWFyeSBTVEVQIGFydGlmYWN0IHRvIHRoaXMgcGF0aCcpCiAgLm9wdGlvbignLS1nbGIgPG91dD4nLCAnd3JpdGUgYSBkZXJpdmVkIEdMQiBwcmV2aWV3IHRvIHRoaXMgcGF0aCcpCiAgLm9wdGlvbignLS1qc29uIDxvdXQ+JywgJ3dyaXRlIHRoZSBKU09OIHJlcG9ydCB0byB0aGlzIHBhdGgnKQogIC5vcHRpb24oCiAgICAnLS1jaGVjaycsCiAgICAndHlwZS1jaGVjayB0aGUgcGFydCAoYWdhaW5zdCBicmVwanMgdHlwZXMpIGJlZm9yZSBydW5uaW5nOyBza2lwIGV4ZWN1dGlvbiBvbiB0eXBlIGVycm9ycycKICApCiAgLm9wdGlvbignLS1zbmFwc2hvdCA8ZGlyPicsICdyZW5kZXIgaXNvL2Zyb250L3RvcC9yaWdodCBQTkdzIHRvIHRoaXMgZGlyIChyZXF1aXJlcyBidWlsdCB2aWV3ZXIpJykKICAub3B0aW9uKAogICAgJy0tc2VydmUnLAogICAgJ2FmdGVyIHZlcmlmeWluZywgc3RhcnQgYSBwcmV2aWV3IHNlcnZlciBhbmQgcHJpbnQgYSA/ZGlyPSZmaWxlPSBkZWVwIGxpbmsgKHN0YXlzIHJ1bm5pbmcpJwogICkKICAub3B0aW9uKCctLW5vLW9wZW4nLCAnd2l0aCAtLXNlcnZlLCBkbyBub3QgYXV0by1vcGVuIHRoZSBicm93c2VyIChqdXN0IHByaW50IHRoZSB2aWV3ZXIgVVJMKScpCiAgLmFjdGlvbigKICAgIGFzeW5jICgKICAgICAgZmlsZTogc3RyaW5nLAogICAgICBvcHRzOiB7CiAgICAgICAgc3RlcD86IHN0cmluZzsKICAgICAgICBnbGI/OiBzdHJpbmc7CiAgICAgICAganNvbj86IHN0cmluZzsKICAgICAgICBjaGVjaz86IGJvb2xlYW47CiAgICAgICAgc25hcHNob3Q/OiBzdHJpbmc7CiAgICAgICAgc2VydmU/OiBib29sZWFuOwogICAgICAgIC8vIENvbW1hbmRlciBtYXRlcmlhbGl6ZXMgYSBuZWdhdGVkIGZsYWcgYXMgYSBjb25jcmV0ZSBib29sZWFuOiB0cnVlIGJ5CiAgICAgICAgLy8gZGVmYXVsdCwgZmFsc2Ugd2hlbiAtLW5vLW9wZW4gaXMgcGFzc2VkLgogICAgICAgIG9wZW46IGJvb2xlYW47CiAgICAgIH0KICAgICkgPT4gewogICAgICAvLyBUaGUgV0FTTSB2aWV3ZXIgbG9hZHMgYSBDQUQgZmlsZSAoaXQgY2FuJ3QgcnVuIGEgLmJyZXAudHMpLCBzbyAtLXNuYXBzaG90Ly0tc2VydmUKICAgICAgLy8gc3RhZ2UgdGhlIHByaW1hcnkgU1RFUCBhbmQgcG9pbnQgdGhlIHZpZXdlciBhdCBpdCB2aWEgP2Rpcj0mZmlsZT0uIC0tZ2xiIGlzIGl0cyBvd24gYXJ0aWZhY3QuCiAgICAgIGNvbnN0IHdhbnRTdGVwID0gQm9vbGVhbihvcHRzLnN0ZXApIHx8IEJvb2xlYW4ob3B0cy5zbmFwc2hvdCkgfHwgQm9vbGVhbihvcHRzLnNlcnZlKTsKICAgICAgY29uc3QgeyByZXBvcnQsIHN0ZXAsIGdsYiwgc2hhcGUgfSA9IGF3YWl0IHJ1blBhcnQocmVzb2x2ZShmaWxlKSwgewogICAgICAgIHN0ZXA6IHdhbnRTdGVwLAogICAgICAgIGdsYjogQm9vbGVhbihvcHRzLmdsYiksCiAgICAgICAgY2hlY2s6IEJvb2xlYW4ob3B0cy5jaGVjayksCiAgICAgIH0pOwogICAgICBsZXQgc3RlcFBhdGg6IHN0cmluZyB8IHVuZGVmaW5lZCA9IG9wdHMuc3RlcDsKICAgICAgdHJ5IHsKICAgICAgICBpZiAob3B0cy5nbGIgJiYgZ2xiKSB3cml0ZUZpbGVTeW5jKG9wdHMuZ2xiLCBCdWZmZXIuZnJvbShnbGIpKTsKCiAgICAgICAgaWYgKHdhbnRTdGVwICYmIHN0ZXApIHsKICAgICAgICAgIHN0ZXBQYXRoID0gb3B0cy5zdGVwID8/IGpvaW4odG1wZGlyKCksIGBicmVwanMtdmVyaWZ5LSR7YmFzZW5hbWUoZmlsZSl9LnN0ZXBgKTsKICAgICAgICAgIHdyaXRlRmlsZVN5bmMoc3RlcFBhdGgsIEJ1ZmZlci5mcm9tKHN0ZXApKTsKICAgICAgICB9CiAgICAgICAgaWYgKG9wdHMuc25hcHNob3QgJiYgc3RlcFBhdGgpIHsKICAgICAgICAgIGNvbnN0IHNob290ID0gYXdhaXQgbG9hZFNuYXBzaG90U2hvb3QoKTsgLy8gbGF6eToga2VlcHMgcHVwcGV0ZWVyIG9mZiB0aGUgZGVmYXVsdCBwYXRoCiAgICAgICAgICBpZiAoc2hvb3QpIHsKICAgICAgICAgICAgY29uc3QgeyBwbmdzIH0gPSBhd2FpdCBzaG9vdCh7IGZpbGU6IHN0ZXBQYXRoLCBvdXREaXI6IG9wdHMuc25hcHNob3QgfSk7CiAgICAgICAgICAgIC8vIERpYWdub3N0aWMgcGF0aHMgZ28gdG8gc3RkZXJyIOKAlCBzdGRvdXQgc3RheXMgYSBzaW5nbGUgY2xlYW4gSlNPTiBkb2N1bWVudC4KICAgICAgICAgICAgZm9yIChjb25zdCBwIG9mIHBuZ3MpIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBzbmFwc2hvdDogJHtwfVxuYCk7CiAgICAgICAgICB9CiAgICAgICAgfSBlbHNlIGlmIChvcHRzLnNuYXBzaG90KSB7CiAgICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZSgnc25hcHNob3Qgc2tpcHBlZDogU1RFUCBleHBvcnQgcHJvZHVjZWQgbm8gYXJ0aWZhY3RcbicpOwogICAgICAgIH0KICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIHB1c2hFcnJvcihyZXBvcnQsIHsgbWVzc2FnZTogYGFydGlmYWN0IHdyaXRlIGZhaWxlZDogJHsoZSBhcyBFcnJvcikubWVzc2FnZX1gIH0pOwogICAgICB9IGZpbmFsbHkgewogICAgICAgIC8vIFRoZSBzaGFwZSBpcyBhIGxpdmUgV0FTTSBoYW5kbGU7IHJlbGVhc2UgaXQgYmVmb3JlIHRoZSBzZXJ2ZXIgdGFrZXMgb3ZlciAodGhlCiAgICAgICAgLy8gLS1zZXJ2ZSBwYXRoIHN0YXlzIHJ1bm5pbmcsIHNvIGxlYWtpbmcgaGVyZSB3b3VsZCBwZXJzaXN0IGZvciB0aGUgc2VydmVyJ3MgbGlmZXRpbWUpLgogICAgICAgIGRpc3Bvc2VTaGFwZShzaGFwZSk7CiAgICAgIH0KICAgICAgLy8gU2VyaWFsaXplIG9uY2UgYWZ0ZXIgYWxsIGFydGlmYWN0IHdyaXRlcyBzbyB0aGUgLS1qc29uIGZpbGUgYW5kIHN0ZG91dAogICAgICAvLyByZWZsZWN0IHRoZSBzYW1lIHJlcG9ydCAoaW5jbC4gYW55ICJhcnRpZmFjdCB3cml0ZSBmYWlsZWQiIGVycm9yKS4KICAgICAgY29uc3QganNvbiA9IHNlcmlhbGl6ZVJlcG9ydChyZXBvcnQpOwogICAgICBpZiAob3B0cy5qc29uKSB3cml0ZUZpbGVTeW5jKG9wdHMuanNvbiwganNvbik7CiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKGpzb24gKyAnXG4nKTsKICAgICAgaWYgKCFyZXBvcnRPayhyZXBvcnQpKSBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgICAgY29uc3Qgd2lsbFNlcnZlID0gQm9vbGVhbihvcHRzLnNlcnZlKSAmJiBzdGVwUGF0aCAhPT0gdW5kZWZpbmVkICYmIHJlcG9ydE9rKHJlcG9ydCk7CiAgICAgIGlmICh3aWxsU2VydmUgJiYgc3RlcFBhdGgpIHsKICAgICAgICBjb25zdCB7IHNlcnZlIH0gPSBhd2FpdCBpbXBvcnQoJy4uL3NuYXBzaG90L3NlcnZlLmpzJyk7IC8vIGxhenk6IG5vIHNlcnZlciBkZXBzIG9uIHRoZSBkZWZhdWx0IHBhdGgKICAgICAgICBjb25zdCB7IHVybCwgcmV1c2VkIH0gPSBhd2FpdCBzZXJ2ZSh7IGZpbGU6IHN0ZXBQYXRoIH0pOyAvLyBidWlsZHMgYSA/ZGlyPSZmaWxlPSBVUkw7IHNlcnZlciBydW5zIHVudGlsIEN0cmwtQwogICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGB2aWV3ZXI6ICR7dXJsfVxuYCk7CiAgICAgICAgLy8gQXV0by1vcGVuIG9ubHkgZm9yIGEgZnJlc2hseSBzdGFydGVkIHNlcnZlciAoYSByZXVzZWQgb25lIGFscmVhZHkgaGFzCiAgICAgICAgLy8gYSB0YWIpIGluIGFuIGludGVyYWN0aXZlIHNlc3Npb247IGAtLW5vLW9wZW5gIChvcHRzLm9wZW4gPT09IGZhbHNlKQogICAgICAgIC8vIGFsd2F5cyBzdXBwcmVzc2VzIGl0LgogICAgICAgIGlmICghcmV1c2VkICYmIG9wdHMub3BlbiAmJiBzaG91bGRBdXRvT3BlbigpKSB7CiAgICAgICAgICBvcGVuQnJvd3Nlcih1cmwpOwogICAgICAgIH0KICAgICAgfQogICAgfQogICk7Cgpwcm9ncmFtCiAgLmNvbW1hbmQoJ21lYXN1cmUnKQogIC5hcmd1bWVudCgnPGE+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGUnKQogIC5hcmd1bWVudCgnW2JdJywgJ29wdGlvbmFsIHNlY29uZCBtb2R1bGU7IGlmIGdpdmVuLCBtZWFzdXJlcyBkaXN0YW5jZSBiZXR3ZWVuIHRoZSB0d28gcGFydHMnKQogIC5hY3Rpb24oYXN5bmMgKGE6IHN0cmluZywgYj86IHN0cmluZykgPT4gewogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcnVuTWVhc3VyZShyZXNvbHZlKGEpLCBiID09PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiByZXNvbHZlKGIpKTsKICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICBKU09OLnN0cmluZ2lmeSh7IG9rOiByZXN1bHQuZXJyb3JzLmxlbmd0aCA9PT0gMCwgLi4ucmVzdWx0IH0sIG51bGwsIDIpICsgJ1xuJwogICAgKTsKICAgIGlmIChyZXN1bHQuZXJyb3JzLmxlbmd0aCA+IDApIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdkaWZmJykKICAuYXJndW1lbnQoJzxhPicsICdwYXRoIHRvIHRoZSBiYXNlbGluZSAuYnJlcC50cyBtb2R1bGUnKQogIC5hcmd1bWVudCgnPGI+JywgJ3BhdGggdG8gdGhlIGNvbXBhcmlzb24gLmJyZXAudHMgbW9kdWxlJykKICAuYWN0aW9uKGFzeW5jIChhOiBzdHJpbmcsIGI6IHN0cmluZykgPT4gewogICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgcnVuRGlmZihyZXNvbHZlKGEpLCByZXNvbHZlKGIpKTsKICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICBKU09OLnN0cmluZ2lmeSh7IG9rOiByZXN1bHQuZXJyb3JzLmxlbmd0aCA9PT0gMCwgLi4ucmVzdWx0IH0sIG51bGwsIDIpICsgJ1xuJwogICAgKTsKICAgIGlmIChyZXN1bHQuZXJyb3JzLmxlbmd0aCA+IDApIHByb2Nlc3MuZXhpdENvZGUgPSAxOwogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdpbml0JykKICAuYXJndW1lbnQoJzxuYW1lPicsICdwYXJ0IG5hbWU7IHNjYWZmb2xkcyA8bmFtZT4uYnJlcC50cyArIHRzY29uZmlnLmpzb24gKyBSRUFETUUubWQnKQogIC5vcHRpb24oJy0tb3V0IDxkaXI+JywgJ3RhcmdldCBkaXJlY3RvcnkgKGRlZmF1bHRzIHRvIC4vPG5hbWU+KScpCiAgLmFjdGlvbigobmFtZTogc3RyaW5nLCBvcHRzOiB7IG91dD86IHN0cmluZyB9KSA9PiB7CiAgICBjb25zdCBkaXIgPSByZXNvbHZlKG9wdHMub3V0ID8/IG5hbWUpOwogICAgY29uc3QgcmVzdWx0ID0gc2NhZmZvbGRQYXJ0KG5hbWUsIGRpcik7CiAgICBmb3IgKGNvbnN0IGYgb2YgcmVzdWx0LmZpbGVzKSB7CiAgICAgIGNvbnN0IHRhZyA9IGYuY3JlYXRlZCA/ICdjcmVhdGVkJyA6ICdleGlzdHMgKGtlcHQpJzsKICAgICAgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYCR7dGFnfTogJHtmLnBhdGh9XG5gKTsKICAgIH0KICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKEpTT04uc3RyaW5naWZ5KHJlc3VsdCwgbnVsbCwgMikgKyAnXG4nKTsKICB9KTsKCnByb2dyYW0KICAuY29tbWFuZCgnd2F0Y2gnKQogIC5hcmd1bWVudCgnPGZpbGU+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGU7IHJlLXZlcmlmaWVzIG9uIGVhY2ggc2F2ZSB1bnRpbCBDdHJsLUMnKQogIC5hY3Rpb24oKGZpbGU6IHN0cmluZykgPT4gewogICAgY29uc3QgcGF0aCA9IHJlc29sdmUoZmlsZSk7CiAgICBjb25zdCBydW4gPSBhc3luYyAoKSA9PiB7CiAgICAgIHRyeSB7CiAgICAgICAgY29uc3QgeyByZXBvcnQsIHNoYXBlIH0gPSBhd2FpdCBydW5QYXJ0KHBhdGgpOwogICAgICAgIHRyeSB7CiAgICAgICAgICBwcm9jZXNzLnN0ZG91dC53cml0ZShzZXJpYWxpemVSZXBvcnQocmVwb3J0KSArICdcbicpOwogICAgICAgIH0gZmluYWxseSB7CiAgICAgICAgICBkaXNwb3NlU2hhcGUoc2hhcGUpOyAvLyBsaXZlIFdBU00gaGFuZGxlOyB0aGUgbG9vcCBydW5zIGluZGVmaW5pdGVseQogICAgICAgIH0KICAgICAgfSBjYXRjaCAoZSkgewogICAgICAgIHByb2Nlc3Muc3RkZXJyLndyaXRlKGB3YXRjaCBydW4gZmFpbGVkOiAkeyhlIGFzIEVycm9yKS5tZXNzYWdlfVxuYCk7CiAgICAgIH0KICAgIH07CiAgICBjb25zdCB7IHRyaWdnZXIgfSA9IGRlYm91bmNlKHJ1biwgREVGQVVMVF9ERUJPVU5DRV9NUyk7CiAgICBwcm9jZXNzLnN0ZGVyci53cml0ZShgd2F0Y2hpbmcgJHtwYXRofSAoQ3RybC1DIHRvIHN0b3ApXG5gKTsKICAgIHZvaWQgcnVuKCk7IC8vIGluaXRpYWwgdmVyaWZ5CiAgICAvLyBXYXRjaCB0aGUgcGFyZW50IGRpcjogZWRpdG9ycyBvZnRlbiByZXBsYWNlIHRoZSBmaWxlIChyZW5hbWUpIG9uIHNhdmUsCiAgICAvLyB3aGljaCBkcm9wcyBhIHdhdGNoZXIgYm91bmQgdG8gdGhlIGZpbGUgaW5vZGUgaXRzZWxmLgogICAgY29uc3Qgd2F0Y2hlciA9IGZzV2F0Y2goZGlybmFtZShwYXRoKSwgKF9ldmVudCwgZmlsZW5hbWUpID0+IHsKICAgICAgaWYgKGZpbGVuYW1lID09PSB1bmRlZmluZWQgfHwgZmlsZW5hbWUgPT09IG51bGwpIHsKICAgICAgICB0cmlnZ2VyKCk7CiAgICAgICAgcmV0dXJuOwogICAgICB9CiAgICAgIGlmIChiYXNlbmFtZShwYXRoKSA9PT0gZmlsZW5hbWUudG9TdHJpbmcoKSkgdHJpZ2dlcigpOwogICAgfSk7CiAgICBjb25zdCBzdG9wID0gKCkgPT4gewogICAgICB3YXRjaGVyLmNsb3NlKCk7CiAgICAgIHByb2Nlc3MuZXhpdCgwKTsKICAgIH07CiAgICBwcm9jZXNzLm9uKCdTSUdJTlQnLCBzdG9wKTsKICAgIHByb2Nlc3Mub24oJ1NJR1RFUk0nLCBzdG9wKTsgLy8gc3VwZXJ2aXNvcnMgKGRvY2tlciBzdG9wLCBzeXN0ZW1jdGwpIHNlbmQgU0lHVEVSTQogIH0pOwoKcHJvZ3JhbQogIC5jb21tYW5kKCdleHBvcnQnKQogIC5hcmd1bWVudCgnPGZpbGU+JywgJ3BhdGggdG8gYSAuYnJlcC50cyBtb2R1bGUnKQogIC5vcHRpb24oJy0tc3RlcCcsICd3cml0ZSBhIFNURVAgYXJ0aWZhY3QnKQogIC5vcHRpb24oJy0tZ2xiJywgJ3dyaXRlIGEgR0xCIGFydGlmYWN0JykKICAub3B0aW9uKCctLXN0bCcsICd3cml0ZSBhbiBTVEwgYXJ0aWZhY3QnKQogIC5vcHRpb24oJy0tYWxsJywgJ3dyaXRlIFNURVAgKyBHTEIgKyBTVEwnKQogIC5vcHRpb24oJy0tb3V0IDxkaXI+JywgJ291dHB1dCBkaXJlY3RvcnknLCAnLicpCiAgLmFjdGlvbigKICAgIGFzeW5jICgKICAgICAgZmlsZTogc3RyaW5nLAogICAgICBvcHRzOiB7IHN0ZXA/OiBib29sZWFuOyBnbGI/OiBib29sZWFuOyBzdGw/OiBib29sZWFuOyBhbGw/OiBib29sZWFuOyBvdXQ6IHN0cmluZyB9CiAgICApID0+IHsKICAgICAgY29uc3QgZm9ybWF0cyA9IG9wdHMuYWxsCiAgICAgICAgPyB7IHN0ZXA6IHRydWUsIGdsYjogdHJ1ZSwgc3RsOiB0cnVlIH0KICAgICAgICA6IHsgc3RlcDogQm9vbGVhbihvcHRzLnN0ZXApLCBnbGI6IEJvb2xlYW4ob3B0cy5nbGIpLCBzdGw6IEJvb2xlYW4ob3B0cy5zdGwpIH07CiAgICAgIGlmICghZm9ybWF0cy5zdGVwICYmICFmb3JtYXRzLmdsYiAmJiAhZm9ybWF0cy5zdGwpIHsKICAgICAgICBwcm9jZXNzLnN0ZGVyci53cml0ZSgnbm8gZm9ybWF0cyByZXF1ZXN0ZWQg4oCUIHBhc3MgLS1zdGVwLy0tZ2xiLy0tc3RsIG9yIC0tYWxsXG4nKTsKICAgICAgICBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgICAgICByZXR1cm47CiAgICAgIH0KICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgZXhwb3J0UGFydChyZXNvbHZlKGZpbGUpLCBmb3JtYXRzLCByZXNvbHZlKG9wdHMub3V0KSk7CiAgICAgIGZvciAoY29uc3QgcCBvZiByZXN1bHQud3JpdHRlbikgcHJvY2Vzcy5zdGRlcnIud3JpdGUoYHdyb3RlOiAke3B9XG5gKTsKICAgICAgZm9yIChjb25zdCBlIG9mIHJlc3VsdC5lcnJvcnMpIHByb2Nlc3Muc3RkZXJyLndyaXRlKGBlcnJvcjogJHtlfVxuYCk7CiAgICAgIHByb2Nlc3Muc3Rkb3V0LndyaXRlKAogICAgICAgIEpTT04uc3RyaW5naWZ5KHsgb2s6IHJlc3VsdC5vaywgd3JpdHRlbjogcmVzdWx0LndyaXR0ZW4sIGVycm9yczogcmVzdWx0LmVycm9ycyB9LCBudWxsLCAyKSArCiAgICAgICAgICAnXG4nCiAgICAgICk7CiAgICAgIGlmICghcmVzdWx0Lm9rKSBwcm9jZXNzLmV4aXRDb2RlID0gMTsKICAgIH0KICApOwoKLy8gT25seSBkcml2ZSB0aGUgQ0xJIHdoZW4gcnVuIGFzIHRoZSBlbnRyeSBzY3JpcHQsIHNvIHRlc3RzIGNhbiBpbXBvcnQgdGhlCi8vIGd1YXJkZWQgbG9hZGVycyB3aXRob3V0IGNvbW1hbmRlciBwYXJzaW5nIHRoZSB0ZXN0IHJ1bm5lcidzIGFyZ3YuCi8vIFJlc29sdmUgc3ltbGlua3Mgb24gYm90aCBzaWRlczogdGhlIG5wbS1pbnN0YWxsZWQgYmluIChub2RlX21vZHVsZXMvLmJpbi9icmVwanMpCi8vIGlzIGEgc3ltbGluaywgc28gcHJvY2Vzcy5hcmd2WzFdIHdvdWxkIG90aGVyd2lzZSBuZXZlciBlcXVhbCB0aGUgcmVhbCBtb2R1bGUgcGF0aC4KZXhwb3J0IGZ1bmN0aW9uIGlzRW50cnlwb2ludChhcmd2MTogc3RyaW5nIHwgdW5kZWZpbmVkLCBtb2R1bGVVcmw6IHN0cmluZyk6IGJvb2xlYW4gewogIGlmICghYXJndjEpIHJldHVybiBmYWxzZTsKICB0cnkgewogICAgcmV0dXJuIHJlYWxwYXRoU3luYyhhcmd2MSkgPT09IHJlYWxwYXRoU3luYyhmaWxlVVJMVG9QYXRoKG1vZHVsZVVybCkpOwogIH0gY2F0Y2ggewogICAgcmV0dXJuIGZhbHNlOwogIH0KfQoKaWYgKGlzRW50cnlwb2ludChwcm9jZXNzLmFyZ3ZbMV0sIGltcG9ydC5tZXRhLnVybCkpIHsKICB2b2lkIHByb2dyYW0ucGFyc2VBc3luYygpOwp9Cg==", "" + import.meta.url));
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Write `code` to a temp ESM directory, run the verify CLI with `makeArgs(partPath)` in a
|
|
52
|
+
* resource-bounded child process, and return the raw outcome. The temp *program* directory is
|
|
53
|
+
* always cleaned up; any artifacts the CLI writes elsewhere (e.g. an `--out` dir) are the caller's.
|
|
54
|
+
*/
|
|
55
|
+
async function runVerifyCli(code, makeArgs, opts) {
|
|
56
|
+
const timeoutMs = positiveOrDefault(opts.timeoutMs, DEFAULT_TIMEOUT_MS);
|
|
57
|
+
const maxMemoryMb = positiveOrDefault(opts.maxMemoryMb, DEFAULT_MAX_MEMORY_MB);
|
|
58
|
+
const cliEntry = opts.cliEntry ?? defaultCliEntry();
|
|
59
|
+
const useTsx = cliEntry.endsWith(".ts");
|
|
60
|
+
const dir = await mkdtemp(join(tmpdir(), "brepjs-run-"));
|
|
61
|
+
try {
|
|
62
|
+
await writeFile(join(dir, "package.json"), "{\"type\":\"module\"}\n");
|
|
63
|
+
const partPath = join(dir, "part.brep.ts");
|
|
64
|
+
await writeFile(partPath, code);
|
|
65
|
+
const cliArgs = makeArgs(partPath);
|
|
66
|
+
return await spawnCliOutcome(useTsx ? "npx" : process.execPath, useTsx ? [
|
|
67
|
+
"tsx",
|
|
68
|
+
cliEntry,
|
|
69
|
+
...cliArgs
|
|
70
|
+
] : [cliEntry, ...cliArgs], {
|
|
71
|
+
...process.env,
|
|
72
|
+
NODE_OPTIONS: [process.env["NODE_OPTIONS"], `--max-old-space-size=${maxMemoryMb}`].filter(Boolean).join(" ")
|
|
73
|
+
}, timeoutMs);
|
|
74
|
+
} finally {
|
|
75
|
+
await rm(dir, {
|
|
76
|
+
recursive: true,
|
|
77
|
+
force: true
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Spawn the verify CLI in its OWN process group and enforce the wall-clock budget by SIGKILLing the
|
|
83
|
+
* WHOLE group on timeout — not just the direct child.
|
|
84
|
+
*
|
|
85
|
+
* Why a process group rather than Node's built-in `execFile` timeout: in dev/test the CLI runs as
|
|
86
|
+
* `npx tsx <main.ts>`, so the process that actually executes the part (and can spin on a CPU-bound
|
|
87
|
+
* OCCT op) is a *grandchild* behind npx+tsx. `child_process`' `timeout`/`killSignal` signals only
|
|
88
|
+
* the direct child, so a fired timeout SIGKILLs `npx` and orphans the still-spinning grandchild
|
|
89
|
+
* forever (it reparents to init and keeps burning a core). Spawning `detached` makes the child a
|
|
90
|
+
* process-group leader that npx's descendants inherit, so `process.kill(-pid, …)` reaps the entire
|
|
91
|
+
* tree. The production path (`node dist/cli/main.js`) has no intermediary, but the group kill is
|
|
92
|
+
* equally correct there.
|
|
93
|
+
*/
|
|
94
|
+
function spawnCliOutcome(cmd, args, env, timeoutMs) {
|
|
95
|
+
return new Promise((resolve) => {
|
|
96
|
+
const child = spawn(cmd, args, {
|
|
97
|
+
env,
|
|
98
|
+
detached: true,
|
|
99
|
+
stdio: [
|
|
100
|
+
"ignore",
|
|
101
|
+
"pipe",
|
|
102
|
+
"pipe"
|
|
103
|
+
]
|
|
104
|
+
});
|
|
105
|
+
if (child.pid !== void 0) activeGroups.add(child.pid);
|
|
106
|
+
let stdout = "";
|
|
107
|
+
let stderr = "";
|
|
108
|
+
let stdoutBytes = 0;
|
|
109
|
+
let timedOut = false;
|
|
110
|
+
let outputTooLarge = false;
|
|
111
|
+
let settled = false;
|
|
112
|
+
const killTree = (signal) => {
|
|
113
|
+
if (child.pid !== void 0) killGroup(child.pid, signal);
|
|
114
|
+
};
|
|
115
|
+
const timer = setTimeout(() => {
|
|
116
|
+
timedOut = true;
|
|
117
|
+
killTree("SIGKILL");
|
|
118
|
+
}, timeoutMs);
|
|
119
|
+
child.stdout?.on("data", (chunk) => {
|
|
120
|
+
if (outputTooLarge) return;
|
|
121
|
+
stdoutBytes += chunk.length;
|
|
122
|
+
if (stdoutBytes > MAX_OUTPUT_BYTES) {
|
|
123
|
+
outputTooLarge = true;
|
|
124
|
+
killTree("SIGKILL");
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
stdout += chunk.toString();
|
|
128
|
+
});
|
|
129
|
+
let stderrBytes = 0;
|
|
130
|
+
child.stderr?.on("data", (chunk) => {
|
|
131
|
+
stderrBytes += chunk.length;
|
|
132
|
+
if (stderrBytes <= MAX_OUTPUT_BYTES) stderr += chunk.toString();
|
|
133
|
+
});
|
|
134
|
+
const finish = (exitCode) => {
|
|
135
|
+
if (settled) return;
|
|
136
|
+
settled = true;
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
if (child.pid !== void 0) activeGroups.delete(child.pid);
|
|
139
|
+
resolve({
|
|
140
|
+
stdout,
|
|
141
|
+
stderr,
|
|
142
|
+
timedOut,
|
|
143
|
+
outputTooLarge,
|
|
144
|
+
exitCode
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
child.on("error", (err) => {
|
|
148
|
+
if (!stderr) stderr = err.message;
|
|
149
|
+
finish(null);
|
|
150
|
+
});
|
|
151
|
+
child.on("close", (code) => finish(code));
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
/** SIGKILL every in-flight sandbox process group now. Exported so a host can reap explicitly. */
|
|
155
|
+
function killActiveSandboxes(signal = "SIGKILL") {
|
|
156
|
+
for (const pid of activeGroups) killGroup(pid, signal);
|
|
157
|
+
activeGroups.clear();
|
|
158
|
+
}
|
|
159
|
+
var shutdownHandlersInstalled = false;
|
|
160
|
+
/**
|
|
161
|
+
* Install process-shutdown hooks that reap any in-flight sandbox process groups when THIS process
|
|
162
|
+
* (the host — e.g. the MCP server) terminates. Idempotent; call once at startup from an entrypoint.
|
|
163
|
+
*
|
|
164
|
+
* Rationale: the per-run timeout (`spawnCliOutcome`) only protects a run while the host is alive —
|
|
165
|
+
* its timer dies with the host. If the host is stopped (the agent disconnects) before a run's
|
|
166
|
+
* budget elapses, the `detached` sandbox group is in its own session and survives, burning a core
|
|
167
|
+
* indefinitely. These hooks SIGKILL every tracked group on the way down so a dying host doesn't
|
|
168
|
+
* leak its children. (A hard SIGKILL of the host can't be trapped — that residual needs the kernel's
|
|
169
|
+
* PR_SET_PDEATHSIG, which Node doesn't expose.)
|
|
170
|
+
*/
|
|
171
|
+
function installSandboxShutdownHandlers() {
|
|
172
|
+
if (shutdownHandlersInstalled) return;
|
|
173
|
+
shutdownHandlersInstalled = true;
|
|
174
|
+
process.on("exit", () => killActiveSandboxes("SIGKILL"));
|
|
175
|
+
process.once("SIGTERM", () => {
|
|
176
|
+
killActiveSandboxes("SIGKILL");
|
|
177
|
+
process.exit(143);
|
|
178
|
+
});
|
|
179
|
+
process.once("SIGINT", () => {
|
|
180
|
+
killActiveSandboxes("SIGKILL");
|
|
181
|
+
process.exit(130);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function tryParse(out) {
|
|
185
|
+
if (!out.trim()) return null;
|
|
186
|
+
try {
|
|
187
|
+
return JSON.parse(out);
|
|
188
|
+
} catch {
|
|
189
|
+
return null;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/** Execute `code` (an agent-authored `.brep.ts` module) in an isolated, resource-bounded child. */
|
|
193
|
+
async function runProgram(code, opts = {}) {
|
|
194
|
+
const o = await runVerifyCli(code, (partPath) => [partPath, "--check"], opts);
|
|
195
|
+
const report = tryParse(o.stdout);
|
|
196
|
+
if (report) return {
|
|
197
|
+
outcome: "completed",
|
|
198
|
+
report
|
|
199
|
+
};
|
|
200
|
+
if (o.timedOut) return {
|
|
201
|
+
outcome: "timeout",
|
|
202
|
+
timeoutMs: positiveOrDefault(opts.timeoutMs, DEFAULT_TIMEOUT_MS)
|
|
203
|
+
};
|
|
204
|
+
if (o.outputTooLarge) return {
|
|
205
|
+
outcome: "crashed",
|
|
206
|
+
exitCode: null,
|
|
207
|
+
detail: "output exceeded size limit"
|
|
208
|
+
};
|
|
209
|
+
return {
|
|
210
|
+
outcome: "crashed",
|
|
211
|
+
exitCode: o.exitCode,
|
|
212
|
+
detail: o.stderr.slice(0, 2e3) || "no JSON report on stdout"
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Execute `code` and export artifacts to `outDir`. Artifacts persist in `outDir` (the caller owns
|
|
217
|
+
* it); only the temp program directory is cleaned up. Returns the written paths and any errors.
|
|
218
|
+
*/
|
|
219
|
+
async function exportProgram(code, outDir, formats = {
|
|
220
|
+
step: true,
|
|
221
|
+
glb: true,
|
|
222
|
+
stl: true
|
|
223
|
+
}, opts = {}) {
|
|
224
|
+
const formatFlags = [
|
|
225
|
+
formats.step ? "--step" : "",
|
|
226
|
+
formats.glb ? "--glb" : "",
|
|
227
|
+
formats.stl ? "--stl" : ""
|
|
228
|
+
].filter(Boolean);
|
|
229
|
+
if (formatFlags.length === 0) return {
|
|
230
|
+
outcome: "crashed",
|
|
231
|
+
exitCode: 1,
|
|
232
|
+
detail: "no formats selected; pass at least one of step/glb/stl"
|
|
233
|
+
};
|
|
234
|
+
const o = await runVerifyCli(code, (partPath) => [
|
|
235
|
+
"export",
|
|
236
|
+
partPath,
|
|
237
|
+
...formatFlags,
|
|
238
|
+
"--out",
|
|
239
|
+
outDir
|
|
240
|
+
], opts);
|
|
241
|
+
const parsed = tryParse(o.stdout);
|
|
242
|
+
if (parsed) return {
|
|
243
|
+
outcome: "completed",
|
|
244
|
+
ok: parsed.ok,
|
|
245
|
+
written: parsed.written,
|
|
246
|
+
errors: parsed.errors
|
|
247
|
+
};
|
|
248
|
+
if (o.timedOut) return {
|
|
249
|
+
outcome: "timeout",
|
|
250
|
+
timeoutMs: positiveOrDefault(opts.timeoutMs, DEFAULT_TIMEOUT_MS)
|
|
251
|
+
};
|
|
252
|
+
if (o.outputTooLarge) return {
|
|
253
|
+
outcome: "crashed",
|
|
254
|
+
exitCode: null,
|
|
255
|
+
detail: "output exceeded size limit"
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
outcome: "crashed",
|
|
259
|
+
exitCode: o.exitCode,
|
|
260
|
+
detail: o.stderr.slice(0, 2e3) || "no JSON result on stdout"
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
//#endregion
|
|
264
|
+
//#region src/sandbox/runRecord.ts
|
|
265
|
+
/**
|
|
266
|
+
* Run-record — a lightweight, append-only provenance envelope for sandbox executions.
|
|
267
|
+
*
|
|
268
|
+
* Every `run_program` call can emit one JSONL record capturing what ran and what came back
|
|
269
|
+
* (program source, outcome, validity, the content-addressed result hash, and the geometric
|
|
270
|
+
* summary). It is deliberately small and side-channel: it keeps a dataset/eval option open
|
|
271
|
+
* (program → outcome pairs) without changing the agent-facing contract.
|
|
272
|
+
*/
|
|
273
|
+
/** Build a {@link RunRecord} from a program and its sandbox result. `now` is injectable for tests. */
|
|
274
|
+
function buildRunRecord(program, result, now = () => /* @__PURE__ */ new Date()) {
|
|
275
|
+
const ok = result.outcome === "completed" && result.report.ok;
|
|
276
|
+
const measurements = result.outcome === "completed" ? result.report.measurements : void 0;
|
|
277
|
+
const topology = result.outcome === "completed" ? result.report.topology : void 0;
|
|
278
|
+
const digest = JSON.stringify({
|
|
279
|
+
outcome: result.outcome,
|
|
280
|
+
ok,
|
|
281
|
+
measurements,
|
|
282
|
+
topology
|
|
283
|
+
});
|
|
284
|
+
const resultHash = createHash("sha256").update(`${program}${digest}`).digest("hex");
|
|
285
|
+
return {
|
|
286
|
+
timestamp: now().toISOString(),
|
|
287
|
+
program,
|
|
288
|
+
outcome: result.outcome,
|
|
289
|
+
ok,
|
|
290
|
+
resultHash,
|
|
291
|
+
...measurements !== void 0 ? { measurements } : {},
|
|
292
|
+
...topology !== void 0 ? { topology } : {}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
/** Append a record as one JSON line to `filePath` (creating it if absent). */
|
|
296
|
+
async function appendRunRecord(filePath, record) {
|
|
297
|
+
await appendFile(filePath, `${JSON.stringify(record)}\n`);
|
|
298
|
+
}
|
|
299
|
+
//#endregion
|
|
300
|
+
//#region src/mcp/tools.ts
|
|
301
|
+
/** JSON Schema for `run_program` input (the low-level Server API takes plain JSON Schema). */
|
|
302
|
+
var RUN_PROGRAM_INPUT_SCHEMA = {
|
|
303
|
+
type: "object",
|
|
304
|
+
properties: {
|
|
305
|
+
code: {
|
|
306
|
+
type: "string",
|
|
307
|
+
description: "A brepjs .brep.ts module with a default-exported part function."
|
|
308
|
+
},
|
|
309
|
+
timeoutMs: {
|
|
310
|
+
type: "number",
|
|
311
|
+
description: "Optional wall-clock budget in milliseconds (default 30000)."
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
required: ["code"]
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Execute an agent-authored brepjs program in the sandbox and return its verification report.
|
|
318
|
+
* `isError` is set when the part failed checks, timed out, or crashed — so the agent can branch.
|
|
319
|
+
*/
|
|
320
|
+
async function runProgramTool(args) {
|
|
321
|
+
if (!args.code || !args.code.trim()) return {
|
|
322
|
+
content: [{
|
|
323
|
+
type: "text",
|
|
324
|
+
text: "run_program requires a non-empty \"code\" string."
|
|
325
|
+
}],
|
|
326
|
+
isError: true
|
|
327
|
+
};
|
|
328
|
+
const result = await runProgram(args.code, args.timeoutMs !== void 0 ? { timeoutMs: args.timeoutMs } : {});
|
|
329
|
+
const recordPath = process.env["BREPJS_RUN_RECORD_PATH"];
|
|
330
|
+
if (recordPath) appendRunRecord(recordPath, buildRunRecord(args.code, result)).catch((err) => {
|
|
331
|
+
console.warn(`run-record append failed (BREPJS_RUN_RECORD_PATH=${recordPath}):`, err);
|
|
332
|
+
});
|
|
333
|
+
if (result.outcome === "completed") {
|
|
334
|
+
const payload = {
|
|
335
|
+
outcome: "completed",
|
|
336
|
+
ok: result.report.ok,
|
|
337
|
+
report: result.report
|
|
338
|
+
};
|
|
339
|
+
return {
|
|
340
|
+
content: [{
|
|
341
|
+
type: "text",
|
|
342
|
+
text: JSON.stringify(payload, null, 2)
|
|
343
|
+
}],
|
|
344
|
+
isError: !result.report.ok
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
content: [{
|
|
349
|
+
type: "text",
|
|
350
|
+
text: JSON.stringify(result, null, 2)
|
|
351
|
+
}],
|
|
352
|
+
isError: true
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/** JSON Schema for `export_part` input. */
|
|
356
|
+
var EXPORT_PART_INPUT_SCHEMA = {
|
|
357
|
+
type: "object",
|
|
358
|
+
properties: {
|
|
359
|
+
code: {
|
|
360
|
+
type: "string",
|
|
361
|
+
description: "A brepjs .brep.ts module with a default-exported part function."
|
|
362
|
+
},
|
|
363
|
+
outDir: {
|
|
364
|
+
type: "string",
|
|
365
|
+
description: "Directory to write artifacts into."
|
|
366
|
+
},
|
|
367
|
+
formats: {
|
|
368
|
+
type: "object",
|
|
369
|
+
description: "Which artifacts to write; omit for all.",
|
|
370
|
+
properties: {
|
|
371
|
+
step: { type: "boolean" },
|
|
372
|
+
glb: { type: "boolean" },
|
|
373
|
+
stl: { type: "boolean" }
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
timeoutMs: {
|
|
377
|
+
type: "number",
|
|
378
|
+
description: "Optional wall-clock budget in ms (default 30000)."
|
|
379
|
+
}
|
|
380
|
+
},
|
|
381
|
+
required: ["code", "outDir"]
|
|
382
|
+
};
|
|
383
|
+
/**
|
|
384
|
+
* Build a part in the sandbox and export it to `outDir` (STEP/GLB/STL). Returns the written paths;
|
|
385
|
+
* `isError` is set when the export produced no valid solid, timed out, or crashed. The artifacts
|
|
386
|
+
* persist in `outDir` for the agent to hand off.
|
|
387
|
+
*/
|
|
388
|
+
async function exportPartTool(args) {
|
|
389
|
+
if (!args.code || !args.code.trim()) return {
|
|
390
|
+
content: [{
|
|
391
|
+
type: "text",
|
|
392
|
+
text: "export_part requires a non-empty \"code\" string."
|
|
393
|
+
}],
|
|
394
|
+
isError: true
|
|
395
|
+
};
|
|
396
|
+
if (!args.outDir || !args.outDir.trim()) return {
|
|
397
|
+
content: [{
|
|
398
|
+
type: "text",
|
|
399
|
+
text: "export_part requires an \"outDir\" string."
|
|
400
|
+
}],
|
|
401
|
+
isError: true
|
|
402
|
+
};
|
|
403
|
+
const result = await exportProgram(args.code, args.outDir, args.formats, args.timeoutMs !== void 0 ? { timeoutMs: args.timeoutMs } : {});
|
|
404
|
+
if (result.outcome === "completed") {
|
|
405
|
+
const payload = {
|
|
406
|
+
outcome: "completed",
|
|
407
|
+
ok: result.ok,
|
|
408
|
+
written: result.written,
|
|
409
|
+
errors: result.errors
|
|
410
|
+
};
|
|
411
|
+
return {
|
|
412
|
+
content: [{
|
|
413
|
+
type: "text",
|
|
414
|
+
text: JSON.stringify(payload, null, 2)
|
|
415
|
+
}],
|
|
416
|
+
isError: !result.ok
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
return {
|
|
420
|
+
content: [{
|
|
421
|
+
type: "text",
|
|
422
|
+
text: JSON.stringify(result, null, 2)
|
|
423
|
+
}],
|
|
424
|
+
isError: true
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/mcp/server.ts
|
|
429
|
+
/**
|
|
430
|
+
* brepjs-verify MCP server (stdio).
|
|
431
|
+
*
|
|
432
|
+
* Exposes the verify substrate to MCP-capable agents. The first tool, `run_program`, executes an
|
|
433
|
+
* agent-authored brepjs program in the sandbox and returns the verification report — the closed
|
|
434
|
+
* "build → verify" step the agent loop is built on. Uses the SDK's low-level `Server` with plain
|
|
435
|
+
* JSON-Schema tool definitions (no direct zod dependency in this package).
|
|
436
|
+
*/
|
|
437
|
+
installSandboxShutdownHandlers();
|
|
438
|
+
var pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "package.json");
|
|
439
|
+
var server = new Server({
|
|
440
|
+
name: "brepjs-verify",
|
|
441
|
+
version: JSON.parse(readFileSync(pkgPath, "utf8")).version
|
|
442
|
+
}, { capabilities: { tools: {} } });
|
|
443
|
+
server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: [{
|
|
444
|
+
name: "run_program",
|
|
445
|
+
description: "Execute an agent-authored brepjs program in an isolated sandbox and return the verification report (validity, measurements, topology). Use this to build a part and check it in one step.",
|
|
446
|
+
inputSchema: RUN_PROGRAM_INPUT_SCHEMA
|
|
447
|
+
}, {
|
|
448
|
+
name: "export_part",
|
|
449
|
+
description: "Build a brepjs part in the sandbox and export it to a directory (STEP/GLB/STL). Use this to hand off the final artifact once a part verifies.",
|
|
450
|
+
inputSchema: EXPORT_PART_INPUT_SCHEMA
|
|
451
|
+
}] }));
|
|
452
|
+
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
453
|
+
if (req.params.name === "run_program") {
|
|
454
|
+
const a = req.params.arguments ?? {};
|
|
455
|
+
const code = typeof a["code"] === "string" ? a["code"] : "";
|
|
456
|
+
const timeoutMs = typeof a["timeoutMs"] === "number" && a["timeoutMs"] > 0 ? a["timeoutMs"] : void 0;
|
|
457
|
+
return runProgramTool({
|
|
458
|
+
code,
|
|
459
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
if (req.params.name === "export_part") {
|
|
463
|
+
const a = req.params.arguments ?? {};
|
|
464
|
+
const code = typeof a["code"] === "string" ? a["code"] : "";
|
|
465
|
+
const outDir = typeof a["outDir"] === "string" ? a["outDir"] : "";
|
|
466
|
+
const timeoutMs = typeof a["timeoutMs"] === "number" && a["timeoutMs"] > 0 ? a["timeoutMs"] : void 0;
|
|
467
|
+
const f = typeof a["formats"] === "object" && a["formats"] !== null ? a["formats"] : void 0;
|
|
468
|
+
const formats = f && (f["step"] === true || f["glb"] === true || f["stl"] === true) ? {
|
|
469
|
+
...f["step"] === true ? { step: true } : {},
|
|
470
|
+
...f["glb"] === true ? { glb: true } : {},
|
|
471
|
+
...f["stl"] === true ? { stl: true } : {}
|
|
472
|
+
} : void 0;
|
|
473
|
+
return exportPartTool({
|
|
474
|
+
code,
|
|
475
|
+
outDir,
|
|
476
|
+
...formats !== void 0 ? { formats } : {},
|
|
477
|
+
...timeoutMs !== void 0 ? { timeoutMs } : {}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
throw new Error(`Unknown tool: ${req.params.name}`);
|
|
481
|
+
});
|
|
482
|
+
async function main() {
|
|
483
|
+
await server.connect(new StdioServerTransport());
|
|
484
|
+
}
|
|
485
|
+
main().catch((err) => {
|
|
486
|
+
console.error("brepjs-verify MCP server failed to start:", err);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
});
|
|
489
|
+
//#endregion
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { ExportFormats } from '../sandbox/runProgram.js';
|
|
3
|
+
export interface RunProgramToolArgs {
|
|
4
|
+
/** A brepjs `.brep.ts` module source with a default-exported part function. */
|
|
5
|
+
code: string;
|
|
6
|
+
/** Optional wall-clock budget in ms (default 30000). */
|
|
7
|
+
timeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
/** JSON Schema for `run_program` input (the low-level Server API takes plain JSON Schema). */
|
|
10
|
+
export declare const RUN_PROGRAM_INPUT_SCHEMA: {
|
|
11
|
+
type: "object";
|
|
12
|
+
properties: {
|
|
13
|
+
code: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
timeoutMs: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Execute an agent-authored brepjs program in the sandbox and return its verification report.
|
|
26
|
+
* `isError` is set when the part failed checks, timed out, or crashed — so the agent can branch.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runProgramTool(args: RunProgramToolArgs): Promise<CallToolResult>;
|
|
29
|
+
export interface ExportPartToolArgs {
|
|
30
|
+
/** A brepjs `.brep.ts` module source with a default-exported part function. */
|
|
31
|
+
code: string;
|
|
32
|
+
/** Directory to write artifacts into (the caller owns it; artifacts persist there). */
|
|
33
|
+
outDir: string;
|
|
34
|
+
/** Which formats to write (default: all of STEP/GLB/STL). */
|
|
35
|
+
formats?: ExportFormats;
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
}
|
|
38
|
+
/** JSON Schema for `export_part` input. */
|
|
39
|
+
export declare const EXPORT_PART_INPUT_SCHEMA: {
|
|
40
|
+
type: "object";
|
|
41
|
+
properties: {
|
|
42
|
+
code: {
|
|
43
|
+
type: string;
|
|
44
|
+
description: string;
|
|
45
|
+
};
|
|
46
|
+
outDir: {
|
|
47
|
+
type: string;
|
|
48
|
+
description: string;
|
|
49
|
+
};
|
|
50
|
+
formats: {
|
|
51
|
+
type: string;
|
|
52
|
+
description: string;
|
|
53
|
+
properties: {
|
|
54
|
+
step: {
|
|
55
|
+
type: string;
|
|
56
|
+
};
|
|
57
|
+
glb: {
|
|
58
|
+
type: string;
|
|
59
|
+
};
|
|
60
|
+
stl: {
|
|
61
|
+
type: string;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
timeoutMs: {
|
|
66
|
+
type: string;
|
|
67
|
+
description: string;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
required: string[];
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Build a part in the sandbox and export it to `outDir` (STEP/GLB/STL). Returns the written paths;
|
|
74
|
+
* `isError` is set when the export produced no valid solid, timed out, or crashed. The artifacts
|
|
75
|
+
* persist in `outDir` for the agent to hand off.
|
|
76
|
+
*/
|
|
77
|
+
export declare function exportPartTool(args: ExportPartToolArgs): Promise<CallToolResult>;
|