@tetherto/mdk-skill 0.0.0 → 0.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +133 -0
- package/dist/skills/mdk/SKILL.md +114 -0
- package/dist/skills/mdk/references/architecture.md +89 -0
- package/dist/skills/mdk/references/glossary.md +27 -0
- package/dist/skills/mdk/references/mdk-contract.schema.json +138 -0
- package/dist/skills/mdk/references/package-index.md +53 -0
- package/dist/skills/mdk/references/protocol.md +95 -0
- package/dist/skills/mdk-create-app/SKILL.md +231 -0
- package/dist/skills/mdk-deployment/SKILL.md +161 -0
- package/dist/skills/mdk-gateway-plugin/SKILL.md +192 -0
- package/dist/skills/mdk-gateway-plugin/references/controller-patterns.md +99 -0
- package/dist/skills/mdk-gateway-plugin/references/plugin-authoring.md +121 -0
- package/dist/skills/mdk-publish-security/SKILL.md +177 -0
- package/dist/skills/mdk-publish-security/references/vuln-classes.md +151 -0
- package/dist/skills/mdk-publish-security/scripts/scan-packages.mjs +544 -0
- package/dist/skills/mdk-site-sizing/SKILL.md +160 -0
- package/dist/skills/mdk-site-sizing/eval/held-out.json +104 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-100devices-2workers-mixed.json +24 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-100devices-2workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-10devices-1workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-15devices-2workers.json +24 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-200devices-4workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-500devices-4workers.json +23 -0
- package/dist/skills/mdk-site-sizing/eval/profiles/cap-50devices-2workers.json +23 -0
- package/dist/skills/mdk-site-sizing/references/envelope.json +284 -0
- package/dist/skills/mdk-site-sizing/references/envelope.md +42 -0
- package/dist/skills/mdk-site-sizing/scripts/compile-envelope.mjs +45 -0
- package/dist/skills/mdk-site-sizing/scripts/eval-held-out.mjs +50 -0
- package/dist/skills/mdk-site-sizing/scripts/lib.mjs +591 -0
- package/dist/skills/mdk-site-sizing/scripts/parse-site.mjs +25 -0
- package/dist/skills/mdk-site-sizing/scripts/size-site.mjs +49 -0
- package/dist/skills/mdk-ui-component/SKILL.md +207 -0
- package/dist/skills/mdk-ui-component/references/page-recipe.md +78 -0
- package/dist/skills/mdk-ui-component/references/ui-registry.json +1 -0
- package/dist/skills/mdk-worker-plugin/SKILL.md +198 -0
- package/dist/skills/mdk-worker-plugin/assets/mdk-contract.template.json +45 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/README.md +93 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/mdk-contract.json +26 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/mock/server.js +93 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/package.json +23 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/smoke.config.js +41 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/client.js +47 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/commands/reboot.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/commands/set-power-mode.js +10 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/hashrate-rt.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/power-mode.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/power.js +5 -0
- package/dist/skills/mdk-worker-plugin/assets/worker-template/src/telemetry/temperature.js +5 -0
- package/dist/skills/mdk-worker-plugin/references/contract-authoring.md +135 -0
- package/dist/skills/mdk-worker-plugin/references/device-families.md +96 -0
- package/dist/skills/mdk-worker-plugin/references/local-testing.md +134 -0
- package/dist/skills/mdk-worker-plugin/references/mdk-contract.schema.json +138 -0
- package/dist/skills/mdk-worker-plugin/references/worker-base-api.md +152 -0
- package/dist/skills/mdk-worker-plugin/scripts/validate-contract.mjs +129 -0
- package/dist/skills/mdk-worker-plugin/scripts/worker-smoke.mjs +256 -0
- package/package.json +47 -1
- package/src/assemble.mjs +11 -0
- package/src/index.d.ts +57 -0
- package/src/index.mjs +254 -0
- package/src/install.mjs +57 -0
- package/src/sources.map.json +34 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compiledAt": "1970-01-01T00:00:00.000Z",
|
|
3
|
+
"provenance": {
|
|
4
|
+
"kind": "fixture",
|
|
5
|
+
"note": "Fixture envelope for skill eval. Replace eval/profiles with committed harness reports (PE-1) and re-run compile-envelope.mjs."
|
|
6
|
+
},
|
|
7
|
+
"thresholds": {
|
|
8
|
+
"headroomGreenBelow": 0.7,
|
|
9
|
+
"headroomAmberBelow": 1,
|
|
10
|
+
"actionSubmitP99Ms": 2000,
|
|
11
|
+
"steadyCpuPctOfOneCore": 80
|
|
12
|
+
},
|
|
13
|
+
"profiles": [
|
|
14
|
+
{
|
|
15
|
+
"profileId": "cap-100devices-2workers",
|
|
16
|
+
"tier": "site-server",
|
|
17
|
+
"hardware": {
|
|
18
|
+
"referenceTier": "site-server",
|
|
19
|
+
"cpuCoresPhysical": 16,
|
|
20
|
+
"cpuCoresLogical": 32,
|
|
21
|
+
"ramGiB": 64,
|
|
22
|
+
"diskType": "nvme"
|
|
23
|
+
},
|
|
24
|
+
"deviceCount": 100,
|
|
25
|
+
"workerCount": 2,
|
|
26
|
+
"workers": [
|
|
27
|
+
{
|
|
28
|
+
"type": "mdk-worker-whatsminer",
|
|
29
|
+
"model": "m56s",
|
|
30
|
+
"deviceCount": 100
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"status": "green",
|
|
34
|
+
"headroomRatio": 0.55,
|
|
35
|
+
"rssSumMiB": 1200,
|
|
36
|
+
"cpuSumPct": 60,
|
|
37
|
+
"readP99Ms": 70,
|
|
38
|
+
"actionP99Ms": 110,
|
|
39
|
+
"source": "fixture"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"profileId": "cap-100devices-2workers-mixed",
|
|
43
|
+
"tier": "site-server",
|
|
44
|
+
"hardware": {
|
|
45
|
+
"referenceTier": "site-server",
|
|
46
|
+
"cpuCoresPhysical": 16,
|
|
47
|
+
"cpuCoresLogical": 32,
|
|
48
|
+
"ramGiB": 64,
|
|
49
|
+
"diskType": "nvme"
|
|
50
|
+
},
|
|
51
|
+
"deviceCount": 100,
|
|
52
|
+
"workerCount": 2,
|
|
53
|
+
"workers": [
|
|
54
|
+
{
|
|
55
|
+
"type": "mdk-worker-whatsminer",
|
|
56
|
+
"model": "m56s",
|
|
57
|
+
"deviceCount": 60
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"type": "mdk-worker-antminer",
|
|
61
|
+
"model": "s19xp",
|
|
62
|
+
"deviceCount": 40
|
|
63
|
+
}
|
|
64
|
+
],
|
|
65
|
+
"status": "green",
|
|
66
|
+
"headroomRatio": 0.5,
|
|
67
|
+
"rssSumMiB": 1300,
|
|
68
|
+
"cpuSumPct": 65,
|
|
69
|
+
"readP99Ms": 75,
|
|
70
|
+
"actionP99Ms": 120,
|
|
71
|
+
"source": "fixture"
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"profileId": "cap-10devices-1workers",
|
|
75
|
+
"tier": "edge",
|
|
76
|
+
"hardware": {
|
|
77
|
+
"referenceTier": "edge",
|
|
78
|
+
"cpuCoresPhysical": 4,
|
|
79
|
+
"cpuCoresLogical": 4,
|
|
80
|
+
"ramGiB": 8,
|
|
81
|
+
"diskType": "sata"
|
|
82
|
+
},
|
|
83
|
+
"deviceCount": 10,
|
|
84
|
+
"workerCount": 1,
|
|
85
|
+
"workers": [
|
|
86
|
+
{
|
|
87
|
+
"type": "mdk-worker-whatsminer",
|
|
88
|
+
"model": "m56s",
|
|
89
|
+
"deviceCount": 10
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"status": "green",
|
|
93
|
+
"headroomRatio": 0.15,
|
|
94
|
+
"rssSumMiB": 400,
|
|
95
|
+
"cpuSumPct": 20,
|
|
96
|
+
"readP99Ms": 40,
|
|
97
|
+
"actionP99Ms": 70,
|
|
98
|
+
"source": "fixture"
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
"profileId": "cap-15devices-2workers",
|
|
102
|
+
"tier": "custom",
|
|
103
|
+
"hardware": {
|
|
104
|
+
"referenceTier": "custom",
|
|
105
|
+
"cpuCoresPhysical": 8,
|
|
106
|
+
"cpuCoresLogical": 16,
|
|
107
|
+
"ramGiB": 32,
|
|
108
|
+
"diskType": "sata"
|
|
109
|
+
},
|
|
110
|
+
"deviceCount": 15,
|
|
111
|
+
"workerCount": 2,
|
|
112
|
+
"workers": [
|
|
113
|
+
{
|
|
114
|
+
"type": "mdk-worker-whatsminer",
|
|
115
|
+
"model": "m56s",
|
|
116
|
+
"deviceCount": 10
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"type": "mdk-worker-antminer",
|
|
120
|
+
"model": "s19xp",
|
|
121
|
+
"deviceCount": 5
|
|
122
|
+
}
|
|
123
|
+
],
|
|
124
|
+
"status": "green",
|
|
125
|
+
"headroomRatio": 0.2,
|
|
126
|
+
"rssSumMiB": 500,
|
|
127
|
+
"cpuSumPct": 25,
|
|
128
|
+
"readP99Ms": 50,
|
|
129
|
+
"actionP99Ms": 85,
|
|
130
|
+
"source": "fixture"
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"profileId": "cap-200devices-4workers",
|
|
134
|
+
"tier": "site-server",
|
|
135
|
+
"hardware": {
|
|
136
|
+
"referenceTier": "site-server",
|
|
137
|
+
"cpuCoresPhysical": 16,
|
|
138
|
+
"cpuCoresLogical": 32,
|
|
139
|
+
"ramGiB": 64,
|
|
140
|
+
"diskType": "nvme"
|
|
141
|
+
},
|
|
142
|
+
"deviceCount": 200,
|
|
143
|
+
"workerCount": 4,
|
|
144
|
+
"workers": [
|
|
145
|
+
{
|
|
146
|
+
"type": "mdk-worker-whatsminer",
|
|
147
|
+
"model": "m56s",
|
|
148
|
+
"deviceCount": 200
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"status": "amber",
|
|
152
|
+
"headroomRatio": 0.85,
|
|
153
|
+
"rssSumMiB": 2000,
|
|
154
|
+
"cpuSumPct": 90,
|
|
155
|
+
"readP99Ms": 140,
|
|
156
|
+
"actionP99Ms": 220,
|
|
157
|
+
"source": "fixture"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"profileId": "cap-500devices-4workers",
|
|
161
|
+
"tier": "site-server",
|
|
162
|
+
"hardware": {
|
|
163
|
+
"referenceTier": "site-server",
|
|
164
|
+
"cpuCoresPhysical": 16,
|
|
165
|
+
"cpuCoresLogical": 32,
|
|
166
|
+
"ramGiB": 64,
|
|
167
|
+
"diskType": "nvme"
|
|
168
|
+
},
|
|
169
|
+
"deviceCount": 500,
|
|
170
|
+
"workerCount": 4,
|
|
171
|
+
"workers": [
|
|
172
|
+
{
|
|
173
|
+
"type": "mdk-worker-whatsminer",
|
|
174
|
+
"model": "m56s",
|
|
175
|
+
"deviceCount": 500
|
|
176
|
+
}
|
|
177
|
+
],
|
|
178
|
+
"status": "red",
|
|
179
|
+
"headroomRatio": 1.4,
|
|
180
|
+
"rssSumMiB": 4500,
|
|
181
|
+
"cpuSumPct": 180,
|
|
182
|
+
"readP99Ms": 800,
|
|
183
|
+
"actionP99Ms": 2100,
|
|
184
|
+
"source": "fixture"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"profileId": "cap-50devices-2workers",
|
|
188
|
+
"tier": "site-server",
|
|
189
|
+
"hardware": {
|
|
190
|
+
"referenceTier": "site-server",
|
|
191
|
+
"cpuCoresPhysical": 16,
|
|
192
|
+
"cpuCoresLogical": 32,
|
|
193
|
+
"ramGiB": 64,
|
|
194
|
+
"diskType": "nvme"
|
|
195
|
+
},
|
|
196
|
+
"deviceCount": 50,
|
|
197
|
+
"workerCount": 2,
|
|
198
|
+
"workers": [
|
|
199
|
+
{
|
|
200
|
+
"type": "mdk-worker-whatsminer",
|
|
201
|
+
"model": "m56s",
|
|
202
|
+
"deviceCount": 50
|
|
203
|
+
}
|
|
204
|
+
],
|
|
205
|
+
"status": "green",
|
|
206
|
+
"headroomRatio": 0.35,
|
|
207
|
+
"rssSumMiB": 800,
|
|
208
|
+
"cpuSumPct": 40,
|
|
209
|
+
"readP99Ms": 55,
|
|
210
|
+
"actionP99Ms": 90,
|
|
211
|
+
"source": "fixture"
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
"supportedUpTo": {
|
|
215
|
+
"site-server": {
|
|
216
|
+
"mdk-worker-whatsminer": {
|
|
217
|
+
"devices": 100,
|
|
218
|
+
"workers": 2,
|
|
219
|
+
"profileId": "cap-100devices-2workers"
|
|
220
|
+
},
|
|
221
|
+
"mdk-worker-antminer,mdk-worker-whatsminer": {
|
|
222
|
+
"devices": 100,
|
|
223
|
+
"workers": 2,
|
|
224
|
+
"profileId": "cap-100devices-2workers-mixed"
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"edge": {
|
|
228
|
+
"mdk-worker-whatsminer": {
|
|
229
|
+
"devices": 10,
|
|
230
|
+
"workers": 1,
|
|
231
|
+
"profileId": "cap-10devices-1workers"
|
|
232
|
+
}
|
|
233
|
+
},
|
|
234
|
+
"custom": {
|
|
235
|
+
"mdk-worker-antminer,mdk-worker-whatsminer": {
|
|
236
|
+
"devices": 15,
|
|
237
|
+
"workers": 2,
|
|
238
|
+
"profileId": "cap-15devices-2workers"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
},
|
|
242
|
+
"formulas": {
|
|
243
|
+
"site-server|mdk-worker-whatsminer": {
|
|
244
|
+
"rssMiB": {
|
|
245
|
+
"base": 400,
|
|
246
|
+
"perDevice": 8
|
|
247
|
+
},
|
|
248
|
+
"cpuPct": {
|
|
249
|
+
"base": 23.333333333333336,
|
|
250
|
+
"perDevice": 0.3333333333333333
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"site-server|mdk-worker-antminer,mdk-worker-whatsminer": {
|
|
254
|
+
"rssMiB": {
|
|
255
|
+
"base": 0,
|
|
256
|
+
"perDevice": 13
|
|
257
|
+
},
|
|
258
|
+
"cpuPct": {
|
|
259
|
+
"base": 0,
|
|
260
|
+
"perDevice": 0.65
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"edge|mdk-worker-whatsminer": {
|
|
264
|
+
"rssMiB": {
|
|
265
|
+
"base": 0,
|
|
266
|
+
"perDevice": 40
|
|
267
|
+
},
|
|
268
|
+
"cpuPct": {
|
|
269
|
+
"base": 0,
|
|
270
|
+
"perDevice": 2
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
"custom|mdk-worker-antminer,mdk-worker-whatsminer": {
|
|
274
|
+
"rssMiB": {
|
|
275
|
+
"base": 0,
|
|
276
|
+
"perDevice": 33.333333333333336
|
|
277
|
+
},
|
|
278
|
+
"cpuPct": {
|
|
279
|
+
"base": 0,
|
|
280
|
+
"perDevice": 1.6666666666666667
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Measured envelope
|
|
2
|
+
|
|
3
|
+
`envelope.json` is the only number source this skill may quote. It is compiled
|
|
4
|
+
from capacity profiles (harness reports or the fixture set under `eval/profiles/`).
|
|
5
|
+
|
|
6
|
+
## Profile shape
|
|
7
|
+
|
|
8
|
+
Each profile is either a full harness report (`backend/tests/benchmark/results/<id>.json`)
|
|
9
|
+
or the slim fields `compile-envelope.mjs` keeps:
|
|
10
|
+
|
|
11
|
+
- `profileId`, `tier`, `hardware`
|
|
12
|
+
- `deviceCount`, `workerCount`, `workers[]` (`type`, `model`, `deviceCount`)
|
|
13
|
+
- `status` (`green` / `amber` / `red`)
|
|
14
|
+
- `headroomRatio`, `rssSumMiB`, `cpuSumPct`, `readP99Ms`, `actionP99Ms`
|
|
15
|
+
- `source` (`measured` or `fixture`)
|
|
16
|
+
|
|
17
|
+
Pass/fail uses the same bars as the harness (`headroomGreenBelow` 0.7,
|
|
18
|
+
`headroomAmberBelow` 1.0, action submit p99 2000 ms, steady CPU 80% of one
|
|
19
|
+
core). **Supported up to** is the densest green profile on that family mix and
|
|
20
|
+
tier, matching the [capacity metrics template](../../../../../../docs/guides/deployment/capacity-metrics-template.md).
|
|
21
|
+
|
|
22
|
+
## Compile
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# fixtures (default) — what the held-out bar runs against until PE-1
|
|
26
|
+
node scripts/compile-envelope.mjs
|
|
27
|
+
|
|
28
|
+
# a real results/ directory from npm run benchmark
|
|
29
|
+
node scripts/compile-envelope.mjs --from <repo>/backend/tests/benchmark/results
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`--from` must contain one `.json` per profile. Replacing fixtures with
|
|
33
|
+
committed harness reports (the 1.0 PE-1 gate) is how this envelope becomes
|
|
34
|
+
measured. After that, re-run `eval-held-out.mjs` and keep any case whose
|
|
35
|
+
expected window still matches; drop or rewrite cases the new data invalidates.
|
|
36
|
+
|
|
37
|
+
## Lookup
|
|
38
|
+
|
|
39
|
+
`size-site.mjs` does not invent a topology. It picks the smallest covering
|
|
40
|
+
profile (`deviceCount >= requested`) at the best status, interpolates RSS/CPU
|
|
41
|
+
between the next-smaller green point and that cover, and refuses to label
|
|
42
|
+
anything past the last non-red point as supported.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Compile measured (or fixture) capacity profiles into references/envelope.json.
|
|
3
|
+
//
|
|
4
|
+
// node compile-envelope.mjs
|
|
5
|
+
// node compile-envelope.mjs --from ../../../../../backend/tests/benchmark/results
|
|
6
|
+
// node compile-envelope.mjs --from ./eval/profiles --out ../references/envelope.json
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
import { compileEnvelope } from './lib.mjs'
|
|
12
|
+
|
|
13
|
+
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url))
|
|
14
|
+
const SKILL_DIR = path.resolve(SCRIPT_DIR, '..')
|
|
15
|
+
const DEFAULT_FROM = path.join(SKILL_DIR, 'eval', 'profiles')
|
|
16
|
+
const DEFAULT_OUT = path.join(SKILL_DIR, 'references', 'envelope.json')
|
|
17
|
+
|
|
18
|
+
const args = process.argv.slice(2)
|
|
19
|
+
let fromDir = DEFAULT_FROM
|
|
20
|
+
let outPath = DEFAULT_OUT
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
if (args[i] === '--from') fromDir = path.resolve(args[++i])
|
|
23
|
+
else if (args[i] === '--out') outPath = path.resolve(args[++i])
|
|
24
|
+
else {
|
|
25
|
+
console.error(`unknown flag ${args[i]}`)
|
|
26
|
+
process.exit(2)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (!fs.existsSync(fromDir)) {
|
|
31
|
+
console.error(`compile-envelope: source dir missing: ${fromDir}`)
|
|
32
|
+
process.exit(1)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const files = fs.readdirSync(fromDir).filter((n) => n.endsWith('.json')).sort()
|
|
36
|
+
if (!files.length) {
|
|
37
|
+
console.error(`compile-envelope: no .json profiles in ${fromDir}`)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const raw = files.map((n) => JSON.parse(fs.readFileSync(path.join(fromDir, n), 'utf8')))
|
|
42
|
+
const envelope = compileEnvelope(raw, { compiledAt: '1970-01-01T00:00:00.000Z' })
|
|
43
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true })
|
|
44
|
+
fs.writeFileSync(outPath, JSON.stringify(envelope, null, 2) + '\n')
|
|
45
|
+
console.error(`compiled ${envelope.profiles.length} profile(s) -> ${outPath} (${envelope.provenance.kind})`)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Held-out bar: parse each site description, size it, match the measured envelope.
|
|
3
|
+
// Exit 0 only when every case matches. This is the skill's acceptance test.
|
|
4
|
+
//
|
|
5
|
+
// node eval-held-out.mjs
|
|
6
|
+
// node eval-held-out.mjs --held-out ../eval/held-out.json --envelope ../references/envelope.json
|
|
7
|
+
|
|
8
|
+
import fs from 'node:fs'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { fileURLToPath } from 'node:url'
|
|
11
|
+
import { parseSiteDescription, sizeSite, matchRecommendation } from './lib.mjs'
|
|
12
|
+
|
|
13
|
+
const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url))
|
|
14
|
+
const SKILL_DIR = path.resolve(SCRIPT_DIR, '..')
|
|
15
|
+
|
|
16
|
+
const args = process.argv.slice(2)
|
|
17
|
+
let heldOutPath = path.join(SKILL_DIR, 'eval', 'held-out.json')
|
|
18
|
+
let envelopePath = path.join(SKILL_DIR, 'references', 'envelope.json')
|
|
19
|
+
for (let i = 0; i < args.length; i++) {
|
|
20
|
+
if (args[i] === '--held-out') heldOutPath = path.resolve(args[++i])
|
|
21
|
+
else if (args[i] === '--envelope') envelopePath = path.resolve(args[++i])
|
|
22
|
+
else {
|
|
23
|
+
console.error(`unknown flag ${args[i]}`)
|
|
24
|
+
process.exit(2)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const heldOut = JSON.parse(fs.readFileSync(heldOutPath, 'utf8'))
|
|
29
|
+
const envelope = JSON.parse(fs.readFileSync(envelopePath, 'utf8'))
|
|
30
|
+
const cases = heldOut.cases || []
|
|
31
|
+
|
|
32
|
+
let failed = 0
|
|
33
|
+
for (const c of cases) {
|
|
34
|
+
const facts = parseSiteDescription(c.description)
|
|
35
|
+
const rec = sizeSite(facts, envelope)
|
|
36
|
+
const result = matchRecommendation(rec, c.expected)
|
|
37
|
+
if (result.ok) {
|
|
38
|
+
console.log(`PASS ${c.id}`)
|
|
39
|
+
} else {
|
|
40
|
+
failed++
|
|
41
|
+
console.log(`FAIL ${c.id}`)
|
|
42
|
+
for (const e of result.errors) console.log(` ${e}`)
|
|
43
|
+
console.log(` facts: ${JSON.stringify({ devices: facts.devices, deviceCount: facts.deviceCount, host: facts.host })}`)
|
|
44
|
+
console.log(` rec: ${JSON.stringify({ status: rec.status, tier: rec.tier, workerCount: rec.workerCount, matchingProfileId: rec.matchingProfileId, rssSumMiB: rec.rssSumMiB, supportedUpTo: rec.envelope?.supportedUpTo })}`)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const n = cases.length
|
|
49
|
+
console.log(`${n - failed}/${n} held-out descriptions match the measured envelope`)
|
|
50
|
+
process.exit(failed ? 1 : 0)
|