brepjs-verify 0.2.1 → 0.3.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 +13 -0
- package/README.md +17 -0
- package/dist/brepjs-verify.cjs +1 -1
- package/dist/brepjs-verify.js +1 -1
- package/dist/cli/main.cjs +1 -1
- package/dist/cli/main.js +1 -1
- package/dist/{diff-D7ZBNRJG.js → diff-Covv1XuJ.js} +12 -4
- package/dist/{diff-CZ4mLtrf.cjs → diff-mxAOOl_m.cjs} +12 -4
- package/dist/snapshot/registry.cjs +1 -1
- package/dist/snapshot/registry.js +1 -1
- package/dist/snapshot/static.cjs +1 -1
- package/dist/snapshot/static.d.ts +1 -1
- package/dist/snapshot/static.js +1 -1
- package/dist/verify/brepjsRuntime.d.ts +1 -0
- package/package.json +5 -2
- package/viewer/dist/assets/{brepjs-CDZqKweN.js → brepjs-CI5VXw8W.js} +19 -19
- package/viewer/dist/assets/{index-B8QUQDqM.js → index-CiN0lKoi.js} +1 -1
- package/viewer/dist/assets/{kernelWorker-C6s5i9JH.js → kernelWorker-BtcMpY8t.js} +1 -1
- package/viewer/dist/index.html +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.0](https://github.com/andymai/brepjs/compare/brepjs-verify-v0.2.1...brepjs-verify-v0.3.0) (2026-06-04)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* **brepjs-verify:** live text-to-cad eval flywheel ([#1215](https://github.com/andymai/brepjs/issues/1215)) ([4e81fc4](https://github.com/andymai/brepjs/commit/4e81fc4053491ce3e08182d57d76bd649252ea3c))
|
|
9
|
+
* **brepjs-verify:** standalone bundled CLI + rename from brepjs-cad ([#1211](https://github.com/andymai/brepjs/issues/1211)) ([05b3799](https://github.com/andymai/brepjs/commit/05b3799a0e9ee4968d4cac92f3a2ea236e39cd35))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **brepjs-verify:** correct fillet/chamfer arg order in no-edges hints ([#1218](https://github.com/andymai/brepjs/issues/1218)) ([835f13a](https://github.com/andymai/brepjs/commit/835f13ac966b4264ba56a5cfc371bbbbbd1a0f01))
|
|
15
|
+
|
|
3
16
|
## [0.2.1](https://github.com/andymai/brepjs/compare/brepjs-cad-v0.2.0...brepjs-cad-v0.2.1) (2026-06-04)
|
|
4
17
|
|
|
5
18
|
|
package/README.md
CHANGED
|
@@ -71,6 +71,23 @@ Few-shot examples live under `skill/examples/<name>.brep.ts`, each with a `<name
|
|
|
71
71
|
|
|
72
72
|
`npm run eval` (`bench/run.ts`) replays every `skill/examples/*.brep.ts` with a sibling `*.expected.json` through the public `runPart` runtime, compares measured volume/area/validity/shape-type against the recorded baseline within each file's tolerance (default 0.5%), prints a PASS/FAIL scorecard, and exits non-zero on any regression. It is deterministic — no LLM or API key — so it runs in CI as the package's regression net. Refresh a baseline by re-recording the example's `*.expected.json` after an intentional geometry change.
|
|
73
73
|
|
|
74
|
+
### Live eval (`npm run eval:live`)
|
|
75
|
+
|
|
76
|
+
The measurement flywheel: sends ~18 natural-language part prompts (`bench/prompts.ts`) to a real model — using the **deployed `SKILL.md` as the system prompt**, so it measures the actual skill — then verifies each generated part two ways:
|
|
77
|
+
|
|
78
|
+
- **Auto (objective):** `runPart --check` → valid solid + any pinned dims within tolerance.
|
|
79
|
+
- **Judge (intent):** a multimodal Claude call looks at the rendered iso/front/top/right snapshots and decides whether the part matches the request + rubric.
|
|
80
|
+
|
|
81
|
+
The scorecard reports per-category `valid` / `judge` / `both` rates and stamps the model + **resolved brepjs version** + date (so trend lines don't mix kernel versions).
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
ANTHROPIC_API_KEY=sk-... npm run eval:live -w brepjs-verify # opus by default
|
|
85
|
+
ANTHROPIC_API_KEY=sk-... npm run eval:live -w brepjs-verify -- --model claude-sonnet-4-6
|
|
86
|
+
# --only <id|category> run a subset --keep keep the generated parts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Opt-in and **billed** (real API calls), so it does _not_ run in CI — the deterministic replay above is the CI gate. Snapshots (hence the judge) need `puppeteer`/Chrome; without them the run scores on auto-verify alone and notes the skipped judge.
|
|
90
|
+
|
|
74
91
|
## Programmatic API
|
|
75
92
|
|
|
76
93
|
```ts
|
package/dist/brepjs-verify.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_diff = require("./diff-
|
|
2
|
+
const require_diff = require("./diff-mxAOOl_m.cjs");
|
|
3
3
|
exports.DEFAULT_TOLERANCE_PCT = require_diff.DEFAULT_TOLERANCE_PCT;
|
|
4
4
|
exports.TYPECHECK_CODE = require_diff.TYPECHECK_CODE;
|
|
5
5
|
exports.emptyReport = require_diff.emptyReport;
|
package/dist/brepjs-verify.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-
|
|
1
|
+
import { a as typecheckPart, c as isExpectedDims, d as emptyReport, i as TYPECHECK_CODE, l as pctDelta, m as serializeReport, n as runMeasure, o as DEFAULT_TOLERANCE_PCT, r as runPart, s as evaluateExpected, t as runDiff, u as runChecks } from "./diff-Covv1XuJ.js";
|
|
2
2
|
export { DEFAULT_TOLERANCE_PCT, TYPECHECK_CODE, emptyReport, evaluateExpected, isExpectedDims, pctDelta, runChecks, runDiff, runMeasure, runPart, serializeReport, typecheckPart };
|
package/dist/cli/main.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const require_diff = require("../diff-
|
|
3
|
+
const require_diff = require("../diff-mxAOOl_m.cjs");
|
|
4
4
|
let node_url = require("node:url");
|
|
5
5
|
let node_fs = require("node:fs");
|
|
6
6
|
let node_path = require("node:path");
|
package/dist/cli/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-
|
|
2
|
+
import { f as pushError, h as loadBrep, m as serializeReport, n as runMeasure, p as reportOk, r as runPart, t as runDiff } from "../diff-Covv1XuJ.js";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { existsSync, mkdirSync, realpathSync, watch, writeFileSync } from "node:fs";
|
|
5
5
|
import { basename, dirname, join, resolve } from "node:path";
|
|
@@ -39,6 +39,14 @@ function loadBrep() {
|
|
|
39
39
|
}
|
|
40
40
|
return cached;
|
|
41
41
|
}
|
|
42
|
+
var kernelReady;
|
|
43
|
+
function initOcctWasm(brep) {
|
|
44
|
+
if (!kernelReady) kernelReady = (async () => {
|
|
45
|
+
const kernel = await (await import("occt-wasm")).OcctKernel.init();
|
|
46
|
+
brep.registerKernel("occt-wasm", brep.OcctWasmAdapter.fromKernel(kernel));
|
|
47
|
+
})();
|
|
48
|
+
return kernelReady;
|
|
49
|
+
}
|
|
42
50
|
//#endregion
|
|
43
51
|
//#region src/verify/report.ts
|
|
44
52
|
function emptyReport() {
|
|
@@ -60,7 +68,7 @@ function reportOk(r) {
|
|
|
60
68
|
return r.errors.length === 0 && r.checks.every((c) => c.passed) && r.assertions.every((a) => a.passed);
|
|
61
69
|
}
|
|
62
70
|
/**
|
|
63
|
-
* Local, brepjs-
|
|
71
|
+
* Local, brepjs-verify-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
|
|
64
72
|
* `BrepErrorCode`). Intentionally not importing the library's internal `getSuggestionForCode`:
|
|
65
73
|
* this table is the agent loop's own actionable `fix` + `nextStep` guidance, and the library's
|
|
66
74
|
* public `BrepError.suggestion` is still surfaced alongside it on each hint.
|
|
@@ -68,11 +76,11 @@ function reportOk(r) {
|
|
|
68
76
|
var HINT_TABLE = {
|
|
69
77
|
FILLET_NO_EDGES: {
|
|
70
78
|
fix: "Select real edges before filleting — pass an edge query (e.g. find edges by direction/position) or a non-empty edge list, not the whole solid.",
|
|
71
|
-
nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid,
|
|
79
|
+
nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, edges, radius)."
|
|
72
80
|
},
|
|
73
81
|
CHAMFER_NO_EDGES: {
|
|
74
82
|
fix: "Select real edges before chamfering — pass a non-empty edge query/list rather than relying on a default that matched nothing.",
|
|
75
|
-
nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid,
|
|
83
|
+
nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, edges, distance)."
|
|
76
84
|
},
|
|
77
85
|
INVALID_FILLET_RADIUS: {
|
|
78
86
|
fix: "Use a fillet radius that is > 0 and small enough to fit the adjacent faces (well under half the thinnest wall).",
|
|
@@ -499,7 +507,7 @@ async function runPart(modulePath, opts = {}) {
|
|
|
499
507
|
let brep;
|
|
500
508
|
try {
|
|
501
509
|
brep = await loadBrep();
|
|
502
|
-
await brep
|
|
510
|
+
await initOcctWasm(brep);
|
|
503
511
|
} catch (e) {
|
|
504
512
|
pushError(report, toErrorInfo("kernel init failed", e));
|
|
505
513
|
return finalize({
|
|
@@ -41,6 +41,14 @@ function loadBrep() {
|
|
|
41
41
|
}
|
|
42
42
|
return cached;
|
|
43
43
|
}
|
|
44
|
+
var kernelReady;
|
|
45
|
+
function initOcctWasm(brep) {
|
|
46
|
+
if (!kernelReady) kernelReady = (async () => {
|
|
47
|
+
const kernel = await (await import("occt-wasm")).OcctKernel.init();
|
|
48
|
+
brep.registerKernel("occt-wasm", brep.OcctWasmAdapter.fromKernel(kernel));
|
|
49
|
+
})();
|
|
50
|
+
return kernelReady;
|
|
51
|
+
}
|
|
44
52
|
//#endregion
|
|
45
53
|
//#region src/verify/report.ts
|
|
46
54
|
function emptyReport() {
|
|
@@ -62,7 +70,7 @@ function reportOk(r) {
|
|
|
62
70
|
return r.errors.length === 0 && r.checks.every((c) => c.passed) && r.assertions.every((a) => a.passed);
|
|
63
71
|
}
|
|
64
72
|
/**
|
|
65
|
-
* Local, brepjs-
|
|
73
|
+
* Local, brepjs-verify-owned advice keyed on `BrepErrorCode` values (see `brepjs`'s public
|
|
66
74
|
* `BrepErrorCode`). Intentionally not importing the library's internal `getSuggestionForCode`:
|
|
67
75
|
* this table is the agent loop's own actionable `fix` + `nextStep` guidance, and the library's
|
|
68
76
|
* public `BrepError.suggestion` is still surfaced alongside it on each hint.
|
|
@@ -70,11 +78,11 @@ function reportOk(r) {
|
|
|
70
78
|
var HINT_TABLE = {
|
|
71
79
|
FILLET_NO_EDGES: {
|
|
72
80
|
fix: "Select real edges before filleting — pass an edge query (e.g. find edges by direction/position) or a non-empty edge list, not the whole solid.",
|
|
73
|
-
nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid,
|
|
81
|
+
nextStep: "List the solid’s edges, pick the ones to round, then call fillet(solid, edges, radius)."
|
|
74
82
|
},
|
|
75
83
|
CHAMFER_NO_EDGES: {
|
|
76
84
|
fix: "Select real edges before chamfering — pass a non-empty edge query/list rather than relying on a default that matched nothing.",
|
|
77
|
-
nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid,
|
|
85
|
+
nextStep: "Enumerate the solid’s edges, choose the target edges, then call chamfer(solid, edges, distance)."
|
|
78
86
|
},
|
|
79
87
|
INVALID_FILLET_RADIUS: {
|
|
80
88
|
fix: "Use a fillet radius that is > 0 and small enough to fit the adjacent faces (well under half the thinnest wall).",
|
|
@@ -501,7 +509,7 @@ async function runPart(modulePath, opts = {}) {
|
|
|
501
509
|
let brep;
|
|
502
510
|
try {
|
|
503
511
|
brep = await loadBrep();
|
|
504
|
-
await brep
|
|
512
|
+
await initOcctWasm(brep);
|
|
505
513
|
} catch (e) {
|
|
506
514
|
pushError(report, toErrorInfo("kernel init failed", e));
|
|
507
515
|
return finalize({
|
|
@@ -11,7 +11,7 @@ async function probe(port) {
|
|
|
11
11
|
const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
|
|
12
12
|
if (!res.ok) return false;
|
|
13
13
|
const d = await res.json();
|
|
14
|
-
return d.app === "brepjs-
|
|
14
|
+
return d.app === "brepjs-verify-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
|
|
15
15
|
} catch {
|
|
16
16
|
return false;
|
|
17
17
|
} finally {
|
|
@@ -10,7 +10,7 @@ async function probe(port) {
|
|
|
10
10
|
const res = await fetch(`http://127.0.0.1:${port}/__cad/server`, { signal: ctrl.signal });
|
|
11
11
|
if (!res.ok) return false;
|
|
12
12
|
const d = await res.json();
|
|
13
|
-
return d.app === "brepjs-
|
|
13
|
+
return d.app === "brepjs-verify-viewer" && d.dynamicRoot === true && typeof d.serverApiVersion === "number" && d.serverApiVersion >= 1;
|
|
14
14
|
} catch {
|
|
15
15
|
return false;
|
|
16
16
|
} finally {
|
package/dist/snapshot/static.cjs
CHANGED
|
@@ -45,7 +45,7 @@ async function handle(req, res, port) {
|
|
|
45
45
|
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
46
46
|
if (url.pathname === "/__cad/server") {
|
|
47
47
|
const d = {
|
|
48
|
-
app: "brepjs-
|
|
48
|
+
app: "brepjs-verify-viewer",
|
|
49
49
|
port,
|
|
50
50
|
dynamicRoot: true,
|
|
51
51
|
serverApiVersion: 1
|
package/dist/snapshot/static.js
CHANGED
|
@@ -44,7 +44,7 @@ async function handle(req, res, port) {
|
|
|
44
44
|
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
45
45
|
if (url.pathname === "/__cad/server") {
|
|
46
46
|
const d = {
|
|
47
|
-
app: "brepjs-
|
|
47
|
+
app: "brepjs-verify-viewer",
|
|
48
48
|
port,
|
|
49
49
|
dynamicRoot: true,
|
|
50
50
|
serverApiVersion: 1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brepjs-verify",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Agent skill + verify/preview tooling for authoring parametric brepjs CAD code",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"lint": "eslint src tests viewer bench",
|
|
45
45
|
"test": "vitest run",
|
|
46
46
|
"eval": "tsx bench/run.ts",
|
|
47
|
+
"eval:live": "tsx bench/live.ts",
|
|
47
48
|
"smoke": "node dist/cli/main.js verify tests/fixtures/validBox.brep.ts | node -e \"const r=JSON.parse(require('fs').readFileSync(0,'utf8'));if(r.ok!==true||!(r.measurements.volume>0))process.exit(1)\"",
|
|
48
49
|
"smoke:standalone": "node scripts/smokeStandalone.mjs",
|
|
49
50
|
"prepack": "npm run build"
|
|
@@ -58,6 +59,7 @@
|
|
|
58
59
|
"puppeteer": "^25.0.4"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
62
|
+
"@anthropic-ai/sdk": "0.100.1",
|
|
61
63
|
"@react-three/drei": "^10.7.7",
|
|
62
64
|
"@react-three/fiber": "^9.6.1",
|
|
63
65
|
"@types/node": "^25.9.1",
|
|
@@ -73,6 +75,7 @@
|
|
|
73
75
|
"tsx": "^4.22.3",
|
|
74
76
|
"vite": "^8.0.0",
|
|
75
77
|
"vite-plugin-dts": "^5.0.1",
|
|
76
|
-
"vitest": "^4.0.0"
|
|
78
|
+
"vitest": "^4.0.0",
|
|
79
|
+
"zod": "4.4.3"
|
|
77
80
|
}
|
|
78
81
|
}
|