@vibe-agent-toolkit/agent-skills 0.2.0-rc.5 → 0.2.0-rc.6
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/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -2
- package/dist/index.js.map +1 -1
- package/dist/post-build-checks.d.ts +29 -0
- package/dist/post-build-checks.d.ts.map +1 -1
- package/dist/post-build-checks.js +33 -0
- package/dist/post-build-checks.js.map +1 -1
- package/dist/skill-identity.d.ts +10 -0
- package/dist/skill-identity.d.ts.map +1 -1
- package/dist/skill-identity.js +12 -0
- package/dist/skill-identity.js.map +1 -1
- package/dist/skill-packager.d.ts.map +1 -1
- package/dist/skill-packager.js +26 -1
- package/dist/skill-packager.js.map +1 -1
- package/dist/skill-test/eval-fragment.d.ts +26 -26
- package/dist/validators/mcp-tool-qualification.d.ts +158 -0
- package/dist/validators/mcp-tool-qualification.d.ts.map +1 -0
- package/dist/validators/mcp-tool-qualification.js +467 -0
- package/dist/validators/mcp-tool-qualification.js.map +1 -0
- package/dist/validators/packaged-size-limit.d.ts +296 -0
- package/dist/validators/packaged-size-limit.d.ts.map +1 -0
- package/dist/validators/packaged-size-limit.js +494 -0
- package/dist/validators/packaged-size-limit.js.map +1 -0
- package/dist/validators/packaging-validator.d.ts +10 -0
- package/dist/validators/packaging-validator.d.ts.map +1 -1
- package/dist/validators/packaging-validator.js +72 -4
- package/dist/validators/packaging-validator.js.map +1 -1
- package/dist/validators/referenced-path-missing.d.ts +202 -0
- package/dist/validators/referenced-path-missing.d.ts.map +1 -0
- package/dist/validators/referenced-path-missing.js +358 -0
- package/dist/validators/referenced-path-missing.js.map +1 -0
- package/dist/validators/skill-validator.d.ts.map +1 -1
- package/dist/validators/skill-validator.js +29 -0
- package/dist/validators/skill-validator.js.map +1 -1
- package/package.json +5 -5
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PACKAGED_SIZE_EXCEEDS_API_LIMIT — the packaged skill is too large to upload to
|
|
3
|
+
* the Anthropic Skills API.
|
|
4
|
+
*
|
|
5
|
+
* ## Why this needs its own detector rather than a threshold on an existing one
|
|
6
|
+
*
|
|
7
|
+
* VAT ships two checks whose names suggest they already answer this, and neither
|
|
8
|
+
* can:
|
|
9
|
+
*
|
|
10
|
+
* - `SKILL_TOTAL_SIZE_LARGE` counts **lines** across bundled markdown (≤2000).
|
|
11
|
+
* - `SKILL_TOO_MANY_FILES` counts **files** (≤6).
|
|
12
|
+
*
|
|
13
|
+
* The shape that actually blocks a publish is one large binary, which has no
|
|
14
|
+
* lines and is one file. The measured instance is a 35.7 MB `.wasm` runtime
|
|
15
|
+
* bundled by three skills in a single adopter marketplace: over the ceiling on
|
|
16
|
+
* its own, before a byte of markdown counts, and invisible to both checks above.
|
|
17
|
+
* Before this module there was no byte measurement anywhere in the validators.
|
|
18
|
+
*
|
|
19
|
+
* ## Why the message names the largest files
|
|
20
|
+
*
|
|
21
|
+
* A total alone ("34.9 MB, limit 30 MB") tells an author they have a problem and
|
|
22
|
+
* not where it is; a bundle at that size has hundreds of files and the true cause
|
|
23
|
+
* is nearly always one or two of them. The adopter who reported this built the
|
|
24
|
+
* same thing locally and said naming the largest files "is the thing that made
|
|
25
|
+
* the cause obvious in one line". So the finding leads with the offenders.
|
|
26
|
+
*
|
|
27
|
+
* ## Built phase only
|
|
28
|
+
*
|
|
29
|
+
* The bytes that matter are the bytes that ship, and a `files:` config entry
|
|
30
|
+
* materializes files at build time — a source tree can lack the very artifact
|
|
31
|
+
* that blows the ceiling. Measuring the source directory would answer a
|
|
32
|
+
* different question and get it wrong in both directions.
|
|
33
|
+
*
|
|
34
|
+
* ## This module is the ONLY thing that stats the packaged bytes
|
|
35
|
+
*
|
|
36
|
+
* That is why an entry it cannot weigh gets a receipt rather than a zero — see
|
|
37
|
+
* {@link walkBundle}. No other validator revisits these files, so a silent zero
|
|
38
|
+
* here is a silent zero everywhere.
|
|
39
|
+
*/
|
|
40
|
+
import { readdirSync, statSync } from 'node:fs';
|
|
41
|
+
import { CODE_REGISTRY } from '@vibe-agent-toolkit/schema';
|
|
42
|
+
import { safePath } from '@vibe-agent-toolkit/utils';
|
|
43
|
+
/**
|
|
44
|
+
* The Skills API's refusal ceiling, in bytes. The vendor documents it as "Total
|
|
45
|
+
* upload size must be under 30 MB (uncompressed)" and the 413 says "requests up
|
|
46
|
+
* to 30MBs", neither of which says whether MB means 10^6 or 2^20 — a 1.4 MB gap.
|
|
47
|
+
*
|
|
48
|
+
* **This is measured, not read.** VAT first assumed decimal (30,000,000) as the
|
|
49
|
+
* conservative reading. Uploading against the live API disproved it:
|
|
50
|
+
*
|
|
51
|
+
* | Bundle content bytes | Result |
|
|
52
|
+
* |---|---|
|
|
53
|
+
* | 30,700,000 | **accepted** — above decimal 30 MB, so decimal is wrong |
|
|
54
|
+
* | 31,500,000 | 413 refused |
|
|
55
|
+
* | 35,900,338 | 413 refused |
|
|
56
|
+
*
|
|
57
|
+
* That brackets the ceiling to (30,700,000, 31,500,000], and 2^20 × 30 =
|
|
58
|
+
* 31,457,280 is the only round number in the window. So the limit is mebibytes,
|
|
59
|
+
* and the old decimal constant warned ~1.46 MB early — a false-positive band on
|
|
60
|
+
* every bundle between 30.0 and 31.4 MB, which is exactly where a big bundle
|
|
61
|
+
* lands.
|
|
62
|
+
*
|
|
63
|
+
* ## What this counts versus what the API counts
|
|
64
|
+
*
|
|
65
|
+
* The API measures the REQUEST. This check measures the bundle's file bytes,
|
|
66
|
+
* because at build time there is no request to weigh — see
|
|
67
|
+
* {@link checkPackagedSizeLimit} for why that is left as it is rather than
|
|
68
|
+
* modelled.
|
|
69
|
+
*
|
|
70
|
+
* The gap between the two is not small. `buildMultipartFormData` frames every
|
|
71
|
+
* file part with a 51-byte boundary line, a `Content-Disposition` header of
|
|
72
|
+
* 61 bytes plus the filename, a 42-byte content-type-and-blank-line, and a
|
|
73
|
+
* 2-byte trailing CRLF — **156 bytes plus the filename's length, per file** —
|
|
74
|
+
* and closes the body with a 53-byte terminator. Measured: one part with a
|
|
75
|
+
* 14-character filename costs 170 bytes of framing, and a 1,000-file bundle
|
|
76
|
+
* with `resources/`-shaped names costs ~185,000. So a bundle whose FILES sit
|
|
77
|
+
* just under the ceiling can be a REQUEST comfortably over it. An earlier
|
|
78
|
+
* version of this comment put the residue at "a few hundred bytes… roughly a
|
|
79
|
+
* kilobyte", which understates it by two orders of magnitude on a many-file
|
|
80
|
+
* bundle.
|
|
81
|
+
*
|
|
82
|
+
* The uploader therefore weighs the body it built rather than the sum of its
|
|
83
|
+
* file bytes, and says so in its own message ({@link OversizeMeasure}). Neither
|
|
84
|
+
* lane pads its threshold with a fudge factor: an invented margin is a number
|
|
85
|
+
* nobody can re-derive.
|
|
86
|
+
*
|
|
87
|
+
* ## The two lanes compare DIFFERENTLY, and that is the correct answer
|
|
88
|
+
*
|
|
89
|
+
* The ceiling is INCLUSIVE, measured: a request body of exactly 31,457,280 bytes
|
|
90
|
+
* (31,456,735 file bytes plus 545 of framing) returned `status: success`, and one
|
|
91
|
+
* byte more returned `413`. So:
|
|
92
|
+
*
|
|
93
|
+
* - **Request bytes: `>` refuses.** The uploader knows the exact body and must
|
|
94
|
+
* not refuse one the API would take.
|
|
95
|
+
* - **File bytes: `>=` refuses** — this lane, {@link checkPackagedSizeLimit}.
|
|
96
|
+
* Framing is never zero, so files totalling exactly the limit ALWAYS frame up
|
|
97
|
+
* into a request larger than the limit and earn a real 413.
|
|
98
|
+
*
|
|
99
|
+
* An earlier version of this comment claimed "both fire AT the ceiling rather
|
|
100
|
+
* than only above it" and offered that as the conservative reading. It was
|
|
101
|
+
* reasoned, never measured, and the measurement above refutes it for the request
|
|
102
|
+
* lane. Do not harmonise the two operators: they are applied to different
|
|
103
|
+
* quantities and each is right about the one it can see.
|
|
104
|
+
*
|
|
105
|
+
* ## If the bracket ever moves, these surfaces do NOT follow automatically
|
|
106
|
+
*
|
|
107
|
+
* Nothing derives the prose from this constant, so a re-measurement is a
|
|
108
|
+
* multi-file edit and the list belongs here, next to the number, rather than in
|
|
109
|
+
* whichever file the next maintainer happens to open:
|
|
110
|
+
*
|
|
111
|
+
* 1. This constant.
|
|
112
|
+
* 2. `docs/validation-codes.md`, `PACKAGED_SIZE_EXCEEDS_API_LIMIT` — several
|
|
113
|
+
* bullets restate both the MiB figure and the exact byte count.
|
|
114
|
+
* 3. The `PACKAGED_SIZE_EXCEEDS_API_LIMIT` entry in
|
|
115
|
+
* `packages/schema/src/validation-codes.ts` (its `description`).
|
|
116
|
+
* 4. The `vat-skill-review` skill's checklist item for the same code.
|
|
117
|
+
*
|
|
118
|
+
* The two `vat claude org skills` help texts interpolate this constant and need
|
|
119
|
+
* no edit. `CHANGELOG.md` is history and is never restated.
|
|
120
|
+
*
|
|
121
|
+
* @vendor-claim reviewed=2026-09-06 verify=Re-measure rather than re-read — the docs do not state the unit. Upload two skill bundles via `vat claude org skills install`, one just under and one just over this constant, and confirm the first is accepted and the second returns 413. Adjust only if the bracket moves; if it does, update the four surfaces listed above.
|
|
122
|
+
*/
|
|
123
|
+
export const API_SKILL_MAX_UPLOAD_BYTES = 31_457_280;
|
|
124
|
+
/**
|
|
125
|
+
* Directory names that never reach the Skills API, so their bytes never count
|
|
126
|
+
* toward its ceiling.
|
|
127
|
+
*
|
|
128
|
+
* 🔑 **This must stay identical to the uploader's exclusion set**, because the
|
|
129
|
+
* two halves apply the SAME constant and the SAME sentence and must therefore
|
|
130
|
+
* apply them to the same file set. They did not: the walk summed everything
|
|
131
|
+
* under the output directory while `vat claude org skills install` dropped these
|
|
132
|
+
* three directories from the multipart body, so a bundle of 31.0 MB of shipped
|
|
133
|
+
* content plus a 1.0 MB `evals/` fired at build and uploaded fine — a false
|
|
134
|
+
* positive on the only lane that shows the finding to the AUTHOR, which is the
|
|
135
|
+
* lane whose credibility the check depends on.
|
|
136
|
+
*
|
|
137
|
+
* The case is reachable and documented into existence: a root `evals/` holding
|
|
138
|
+
* no `evals.json` is ordinary content, so `vat build` packages it and the
|
|
139
|
+
* uploader drops it unconditionally.
|
|
140
|
+
*
|
|
141
|
+
* The exclusion is by DIRECTORY, so a regular FILE named `evals` is weighed here
|
|
142
|
+
* because it is sent there. The uploader spells its own test
|
|
143
|
+
* `directoryLike && NEVER_UPLOADED_DIR_NAMES.has(entry.name)`, where
|
|
144
|
+
* `directoryLike` is a real directory OR a symbolic link resolving to one — this
|
|
145
|
+
* walk cannot descend a linked directory at all (`Dirent.isDirectory()` is
|
|
146
|
+
* lstat-based), so it weighs one as zero and files a `SCAN_PATH_UNREADABLE`
|
|
147
|
+
* receipt for it. Both lanes therefore leave a linked `evals` out of the total;
|
|
148
|
+
* they differ only in whether they say so.
|
|
149
|
+
*
|
|
150
|
+
* ⚠️ ONE asymmetry remains, and it can only under-count: the uploader also
|
|
151
|
+
* withholds the skill's DECLARED eval-suite unit, which it resolves from the
|
|
152
|
+
* governing VAT config asynchronously. This walk is synchronous and knows only
|
|
153
|
+
* an output directory. In a built bundle that unit is normally absent anyway —
|
|
154
|
+
* the packager drops declared test input on the way in and reports what it
|
|
155
|
+
* dropped — so the residue is a bundle weighed slightly heavy, never light.
|
|
156
|
+
*/
|
|
157
|
+
export const NEVER_UPLOADED_DIR_NAMES = new Set(['evals', 'node_modules', '.git']);
|
|
158
|
+
/** Naive English pluralisation; every noun this file counts is regular. */
|
|
159
|
+
function plural(noun, count) {
|
|
160
|
+
return count === 1 ? noun : `${noun}s`;
|
|
161
|
+
}
|
|
162
|
+
/** How many offenders the message names before it stops listing. */
|
|
163
|
+
const LARGEST_FILES_NAMED = 3;
|
|
164
|
+
/**
|
|
165
|
+
* Binary units, because the only threshold any of these numbers is read against
|
|
166
|
+
* is binary: {@link API_SKILL_MAX_UPLOAD_BYTES} is exactly 30 × 2^20, measured.
|
|
167
|
+
* Rendered in decimal, a total and the ceiling it is compared to could not be
|
|
168
|
+
* compared as written — "51.7 MB … over the 30 MiB ceiling" makes the reader do
|
|
169
|
+
* the conversion before they know how far over they are. The base and the labels
|
|
170
|
+
* move together: a function dividing by 1024 must not print "MB".
|
|
171
|
+
*/
|
|
172
|
+
const BYTE_UNITS = ['KiB', 'MiB', 'GiB', 'TiB'];
|
|
173
|
+
/** Human-readable bytes, in the binary units the API's ceiling is expressed in. */
|
|
174
|
+
export function formatBytes(bytes) {
|
|
175
|
+
if (bytes < 1024)
|
|
176
|
+
return `${bytes} B`;
|
|
177
|
+
let value = bytes / 1024;
|
|
178
|
+
let unit = 0;
|
|
179
|
+
while (value >= 1024 && unit < BYTE_UNITS.length - 1) {
|
|
180
|
+
value /= 1024;
|
|
181
|
+
unit += 1;
|
|
182
|
+
}
|
|
183
|
+
// `?? ''` is unreachable — `unit` is bounded by the loop condition — but
|
|
184
|
+
// noUncheckedIndexedAccess types the read as possibly-undefined and a
|
|
185
|
+
// non-null assertion would hide a real bug if that bound ever moved.
|
|
186
|
+
return `${value.toFixed(1)} ${BYTE_UNITS[unit] ?? ''}`.trimEnd();
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* A size AND its exact byte count, for the two numbers a refusal compares.
|
|
190
|
+
*
|
|
191
|
+
* {@link formatBytes} rounds to one decimal, so a bundle one byte over the
|
|
192
|
+
* ceiling read `30.0 MiB … over the 30.0 MiB ceiling` — a sentence that refutes
|
|
193
|
+
* itself and leaves an author with no way to know how much to cut. Rounded for
|
|
194
|
+
* scanning, exact for acting on; the ceiling is quoted the same way so the two
|
|
195
|
+
* can actually be compared as written.
|
|
196
|
+
*/
|
|
197
|
+
function sizeWithExactBytes(bytes) {
|
|
198
|
+
return `${formatBytes(bytes)} (${bytes.toLocaleString('en-US')} bytes)`;
|
|
199
|
+
}
|
|
200
|
+
/** Whatever the platform gave us, as text a reader can act on. */
|
|
201
|
+
function causeOf(error) {
|
|
202
|
+
return error instanceof Error ? error.message : String(error);
|
|
203
|
+
}
|
|
204
|
+
/** A path as the finding spells it: bundle-relative POSIX, and `.` for the root. */
|
|
205
|
+
function bundleRelative(root, target) {
|
|
206
|
+
return safePath.relative(root, target) || '.';
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* The half of `SCAN_PATH_UNREADABLE`'s registry `fix` that is FALSE for every
|
|
210
|
+
* entry this walk skips by policy, stated once so the two remedies below cannot
|
|
211
|
+
* drift from each other. See {@link UnweighedEntry.fix}.
|
|
212
|
+
*/
|
|
213
|
+
const NOT_A_PERMISSION_PROBLEM = "Nothing about this entry's permissions is wrong, and `--exclude` is an `audit` flag "
|
|
214
|
+
+ 'that the `vat build` / `vat skills package` lane this check runs in does not accept.';
|
|
215
|
+
/** Remedy for a symlink: skipped because the UPLOADER refuses it, not because it is unreadable. */
|
|
216
|
+
const SYMLINK_SKIPPED_FIX = 'Replace the link with a real copy of the file if the skill needs it, or remove it from the '
|
|
217
|
+
+ 'packaged output — the uploader refuses symbolic links outright, so this entry can never '
|
|
218
|
+
+ `contribute bytes to a published skill. ${NOT_A_PERMISSION_PROBLEM}`;
|
|
219
|
+
/** Remedy for a device, socket or FIFO: nothing to weigh and nothing that can be uploaded. */
|
|
220
|
+
const NOT_A_REGULAR_FILE_FIX = 'Remove it from the packaged output — a device, socket or FIFO has no bytes to weigh and '
|
|
221
|
+
+ 'cannot be uploaded, and one reaching a build output almost always came from a source tree '
|
|
222
|
+
+ `that should not have contained it. ${NOT_A_PERMISSION_PROBLEM}`;
|
|
223
|
+
/**
|
|
224
|
+
* Every regular file under `root` with its size, plus every entry whose size
|
|
225
|
+
* could NOT be established.
|
|
226
|
+
*
|
|
227
|
+
* ## Why "could not weigh it" is a return value and not a zero
|
|
228
|
+
*
|
|
229
|
+
* Every unreadable entry used to be swallowed into `[]`/`undefined` and summed
|
|
230
|
+
* as ZERO. That under-counts in exactly the direction that produces a clean bill
|
|
231
|
+
* of health: `statSync` failing on the 35.7 MB `.wasm` (an ELOOP symlink cycle, a
|
|
232
|
+
* Windows file lock, a file removed between readdir and stat) took the total from
|
|
233
|
+
* ~36 MB to ~2 MB, the build reported no warnings, and the upload ate a 413
|
|
234
|
+
* eleven seconds later — precisely the outcome this module exists to prevent.
|
|
235
|
+
*
|
|
236
|
+
* Nothing else was going to say so. `RESOURCE_UNREADABLE` — which an earlier
|
|
237
|
+
* version of this comment claimed "already owns 'VAT could not read this'" — is
|
|
238
|
+
* emitted by `walk-link-graph.ts` over the SOURCE link graph and never stats the
|
|
239
|
+
* packaged output. This walk is the only thing that touches these bytes, so the
|
|
240
|
+
* receipt is its own to emit (`SCAN_PATH_UNREADABLE`, warning: a size check must
|
|
241
|
+
* not be the thing that fails a build, but it must never be silent about what it
|
|
242
|
+
* could not put on the scale).
|
|
243
|
+
*
|
|
244
|
+
* ## Symlinks, exactly
|
|
245
|
+
*
|
|
246
|
+
* **No symlink is weighed, whatever it resolves to** — each becomes an unweighed
|
|
247
|
+
* entry naming the link, and the receipt says the uploader refuses it outright.
|
|
248
|
+
*
|
|
249
|
+
* ⚠️ An earlier version of this comment defended measuring a linked FILE
|
|
250
|
+
* *through* the link — "a link resolving to a 30 MB file contributes 30 MB,
|
|
251
|
+
* which matches the uploader, whose `readFileSync` reads the target's bytes."
|
|
252
|
+
* The two lanes did agree, and both were wrong: reading through the link is how
|
|
253
|
+
* `notes.md -> /etc/passwd` got published into a shared workspace. The uploader
|
|
254
|
+
* now refuses any symlink, so there is no such upload to weigh, and pricing one
|
|
255
|
+
* here would put a number on bytes that can never be sent.
|
|
256
|
+
*
|
|
257
|
+
* A symlinked DIRECTORY was never walked and contributed nothing, because
|
|
258
|
+
* `Dirent.isDirectory()` is lstat-based and answers `false` for it. Now it is
|
|
259
|
+
* reported for the same reason as the linked file, alongside any other
|
|
260
|
+
* non-regular entry (device, socket, FIFO) and any dangling link.
|
|
261
|
+
*/
|
|
262
|
+
function walkBundle(root) {
|
|
263
|
+
const acc = { files: [], unweighed: [] };
|
|
264
|
+
const stack = [root];
|
|
265
|
+
while (stack.length > 0) {
|
|
266
|
+
const dir = stack.pop();
|
|
267
|
+
if (dir === undefined)
|
|
268
|
+
break;
|
|
269
|
+
for (const dirent of readDirOrRecord(dir, root, acc)) {
|
|
270
|
+
const full = safePath.join(dir, dirent.name);
|
|
271
|
+
if (dirent.isDirectory()) {
|
|
272
|
+
if (!NEVER_UPLOADED_DIR_NAMES.has(dirent.name))
|
|
273
|
+
stack.push(full);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
if (dirent.isSymbolicLink()) {
|
|
277
|
+
acc.unweighed.push({
|
|
278
|
+
path: bundleRelative(root, full),
|
|
279
|
+
reason: 'it is a symbolic link, which the uploader refuses rather than following — '
|
|
280
|
+
+ 'no byte of its target can be sent, so none is weighed here',
|
|
281
|
+
fix: SYMLINK_SKIPPED_FIX,
|
|
282
|
+
});
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
weighEntry(full, bundleRelative(root, full), acc);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return acc;
|
|
289
|
+
}
|
|
290
|
+
/** Directory entries, recording a receipt when the directory cannot be read. */
|
|
291
|
+
function readDirOrRecord(dir, root, acc) {
|
|
292
|
+
try {
|
|
293
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- caller-supplied packaged output dir
|
|
294
|
+
return readdirSync(dir, { withFileTypes: true });
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
acc.unweighed.push({
|
|
298
|
+
path: bundleRelative(root, dir),
|
|
299
|
+
reason: `VAT could not read the directory (${causeOf(error)})`,
|
|
300
|
+
});
|
|
301
|
+
return [];
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/** Record one non-directory entry: its size, or why it has none VAT can use. */
|
|
305
|
+
function weighEntry(full, rel, acc) {
|
|
306
|
+
let stats;
|
|
307
|
+
try {
|
|
308
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- as above
|
|
309
|
+
stats = statSync(full);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
acc.unweighed.push({ path: rel, reason: `VAT could not stat it (${causeOf(error)})` });
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
if (stats.isFile()) {
|
|
316
|
+
acc.files.push({ path: rel, bytes: stats.size });
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
acc.unweighed.push({
|
|
320
|
+
path: rel,
|
|
321
|
+
// Symbolic links never reach here — they are caught by the walk, which does
|
|
322
|
+
// not follow one. What is left is a device, a socket or a FIFO.
|
|
323
|
+
reason: 'it is not a regular file (a device, a socket, or a FIFO)',
|
|
324
|
+
fix: NOT_A_REGULAR_FILE_FIX,
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The bundle's files, heaviest first — the order both the message and the anchor
|
|
329
|
+
* want — with EQUAL sizes broken by path, in code-point order.
|
|
330
|
+
*
|
|
331
|
+
* 🔑 The tiebreak is not cosmetic: the heaviest file becomes the finding's `link`,
|
|
332
|
+
* and `link` is the key an adopter's `validation.allow` entry matches. Sorting on
|
|
333
|
+
* bytes alone left ties in the order `readdir` happened to return them, so two
|
|
334
|
+
* equal-sized files — a vendored runtime shipped twice, which is exactly the shape
|
|
335
|
+
* that motivated this check — could swap between a developer's macOS box and a
|
|
336
|
+
* Linux CI runner. The waiver then matched locally, missed in CI, and was reported
|
|
337
|
+
* `ALLOW_UNUSED` in the run that needed it.
|
|
338
|
+
*
|
|
339
|
+
* The comparator is explicit code points rather than `localeCompare`, for the same
|
|
340
|
+
* reason `walkableChildren` in the sibling module gives: the order only has to be
|
|
341
|
+
* the SAME everywhere, and `localeCompare` is locale- and ICU-dependent.
|
|
342
|
+
*/
|
|
343
|
+
function largestFirst(files) {
|
|
344
|
+
return [...files].sort((a, b) => b.bytes - a.bytes || (a.path < b.path ? -1 : Number(a.path > b.path)));
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* The one sentence describing an over-ceiling upload, shared by the build-time
|
|
348
|
+
* validator and by `vat claude org skills install`'s pre-flight refusal.
|
|
349
|
+
*
|
|
350
|
+
* Both refuse for the same external reason, so neither owns the wording: the
|
|
351
|
+
* uploader's copy would otherwise drift from the validator's, and an author who
|
|
352
|
+
* met the warning at build would meet a differently-worded version of it at
|
|
353
|
+
* upload. It takes already-measured files rather than a directory because the
|
|
354
|
+
* uploader knows something the walker cannot — which files it EXCLUDED (the
|
|
355
|
+
* declared eval suite) — and must describe the bundle it is actually sending.
|
|
356
|
+
* The exclusions it can state up front are shared as
|
|
357
|
+
* {@link NEVER_UPLOADED_DIR_NAMES}, so both sides weigh the same file set.
|
|
358
|
+
*
|
|
359
|
+
* The two lanes differ in exactly one place, {@link OversizeMeasure}: the request
|
|
360
|
+
* variant reports the body it weighed AND the file bytes inside it, so the
|
|
361
|
+
* framing is visible as the difference rather than as an unexplained discrepancy.
|
|
362
|
+
*/
|
|
363
|
+
export function describeOversizeBundle(files, measure, limitBytes = API_SKILL_MAX_UPLOAD_BYTES) {
|
|
364
|
+
const largest = largestFirst(files).slice(0, LARGEST_FILES_NAMED);
|
|
365
|
+
const named = largest.map(file => `${file.path} (${formatBytes(file.bytes)})`).join(', ');
|
|
366
|
+
const remainder = files.length - largest.length;
|
|
367
|
+
const tail = remainder > 0 ? `, and ${remainder} more ${plural('file', remainder)}` : '';
|
|
368
|
+
const across = `${files.length} ${plural('file', files.length)}`;
|
|
369
|
+
const fileBytes = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
370
|
+
const subject = measure.of === 'upload-request'
|
|
371
|
+
? `Upload request body is ${sizeWithExactBytes(measure.bytes)} — ${formatBytes(fileBytes)} `
|
|
372
|
+
+ `of file content across ${across}, plus per-part multipart framing —`
|
|
373
|
+
: `Packaged skill is ${sizeWithExactBytes(measure.bytes)} across ${across},`;
|
|
374
|
+
return (`${subject} over the ${sizeWithExactBytes(limitBytes)} Anthropic Skills API ` +
|
|
375
|
+
`upload ceiling. Largest: ${named}${tail}`);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* One receipt per entry the walk could not put on the scale.
|
|
379
|
+
*
|
|
380
|
+
* The `fix` is PER INSTANCE, falling back to the registry's only for the entries
|
|
381
|
+
* the registry's text actually describes — a readdir or stat FAILURE. Everything
|
|
382
|
+
* else here is a deliberate skip, and the registry advice ("make the path
|
|
383
|
+
* readable… or pass `--exclude`") sends its author to fix permissions on a
|
|
384
|
+
* healthy file with a flag this command does not have. See
|
|
385
|
+
* {@link UnweighedEntry.fix}.
|
|
386
|
+
*/
|
|
387
|
+
function unweighedIssues(entries) {
|
|
388
|
+
const registryEntry = CODE_REGISTRY.SCAN_PATH_UNREADABLE;
|
|
389
|
+
return entries.map(entry => ({
|
|
390
|
+
severity: registryEntry.defaultSeverity,
|
|
391
|
+
code: 'SCAN_PATH_UNREADABLE',
|
|
392
|
+
message: `Not weighed against the ${formatBytes(API_SKILL_MAX_UPLOAD_BYTES)} Anthropic Skills API upload ceiling: `
|
|
393
|
+
+ `${entry.reason}. The packaged size VAT measured is a LOWER BOUND.`,
|
|
394
|
+
location: entry.path,
|
|
395
|
+
fix: entry.fix ?? registryEntry.fix,
|
|
396
|
+
reference: registryEntry.reference,
|
|
397
|
+
}));
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Emit `PACKAGED_SIZE_EXCEEDS_API_LIMIT` when the packaged bundle at `outputDir`
|
|
401
|
+
* is at or over the API upload ceiling, plus one `SCAN_PATH_UNREADABLE` for every
|
|
402
|
+
* entry whose bytes VAT could not establish.
|
|
403
|
+
*
|
|
404
|
+
* ## This weighs FILES, and deliberately not the request
|
|
405
|
+
*
|
|
406
|
+
* The API refuses on the size of the multipart REQUEST, and the uploader weighs
|
|
407
|
+
* exactly that — the body it is about to send. This lane cannot: there is no
|
|
408
|
+
* request at build time, and the framing is not a property of the bundle. Its
|
|
409
|
+
* per-part cost is `156 + len(filename)`, where the filename is not the packaged
|
|
410
|
+
* path but `<declared-name>/<packaged-path>` as the uploader keys it, and the
|
|
411
|
+
* body also carries whatever fields that endpoint takes — a `display_title` on
|
|
412
|
+
* create, none on a version add. Every one of those is decided at upload time.
|
|
413
|
+
*
|
|
414
|
+
* Adding an ESTIMATE of it here would mean re-deriving the multipart builder's
|
|
415
|
+
* arithmetic in a second place, where it would silently stop matching the first —
|
|
416
|
+
* which is the same defect as a fudge factor, only harder to spot. So this check
|
|
417
|
+
* measures what it can see, the message says "Packaged skill is …", and the
|
|
418
|
+
* uploader's says "Upload request body is …". They are different numbers on
|
|
419
|
+
* purpose, and a bundle that clears this one can still be refused there.
|
|
420
|
+
*
|
|
421
|
+
* One size issue for the bundle, not one per file: unlike a per-reference
|
|
422
|
+
* finding, the defect here IS the total — no single file is "the" problem unless
|
|
423
|
+
* it alone exceeds the ceiling, and emitting per file would hand an adopter a
|
|
424
|
+
* waiver granularity that cannot express the actual fix.
|
|
425
|
+
*
|
|
426
|
+
* ## The anchor, and why the largest file is `link`
|
|
427
|
+
*
|
|
428
|
+
* `location` is the bundle root (`.`) because the finding is about the bundle —
|
|
429
|
+
* but `.` is the SAME STRING for every skill in a multi-skill build, so an allow
|
|
430
|
+
* glob written against it waives all of them or none. That left one escape hatch,
|
|
431
|
+
* `severity.PACKAGED_SIZE_EXCEEDS_API_LIMIT: ignore`, which is project-wide: an
|
|
432
|
+
* adopter with 40 skills, one legitimately bundling a 32 MB runtime it never
|
|
433
|
+
* publishes to the API, had to blind themselves on the other 39.
|
|
434
|
+
*
|
|
435
|
+
* So the largest file rides as `link` — the same convention the rest of this lane
|
|
436
|
+
* uses (`referenced-path-missing`, `mcp-tool-qualification`), where `link` is the
|
|
437
|
+
* specific thing an instance is about and `location` stays the file you open.
|
|
438
|
+
* `applyAllowFilter` matches an allow glob against `location` OR `link`, so:
|
|
439
|
+
*
|
|
440
|
+
* ```yaml
|
|
441
|
+
* validation:
|
|
442
|
+
* allow:
|
|
443
|
+
* PACKAGED_SIZE_EXCEEDS_API_LIMIT:
|
|
444
|
+
* - paths: ["scripts/duckdb-eh.wasm"]
|
|
445
|
+
* reason: "Runtime this skill needs; never published to the Skills API."
|
|
446
|
+
* ```
|
|
447
|
+
*
|
|
448
|
+
* …waives exactly the bundles whose weight that file explains, and every other
|
|
449
|
+
* skill keeps the check.
|
|
450
|
+
*
|
|
451
|
+
* ⚠️ Two skills bundling the SAME oversized artifact share one anchor, so one
|
|
452
|
+
* entry waives both. That is the right grain for the case that produced this
|
|
453
|
+
* check (one `.wasm`, three skills, one decision), but it is NOT per-skill
|
|
454
|
+
* precision. Per-skill would need an anchor naming the skill, which this call
|
|
455
|
+
* site cannot mint: `location` must be a project-relative path and the packager
|
|
456
|
+
* passes only an absolute output directory. A caller that wants it should pass
|
|
457
|
+
* the bundle root as `issueLocation(outputPath, projectRoot)` and have this
|
|
458
|
+
* function use it in place of `.`.
|
|
459
|
+
*
|
|
460
|
+
* @param outputDir Absolute path to the packaged skill output.
|
|
461
|
+
* @param limitBytes Ceiling to compare against. Defaults to the documented API
|
|
462
|
+
* limit; parameterized so a test can drive it with a few kilobytes instead of
|
|
463
|
+
* writing 30 MB of fixture to disk.
|
|
464
|
+
*/
|
|
465
|
+
export function checkPackagedSizeLimit(outputDir, limitBytes = API_SKILL_MAX_UPLOAD_BYTES) {
|
|
466
|
+
const { files, unweighed } = walkBundle(outputDir);
|
|
467
|
+
const issues = unweighedIssues(unweighed);
|
|
468
|
+
const total = files.reduce((sum, file) => sum + file.bytes, 0);
|
|
469
|
+
// `>=`, deliberately unlike the uploader's `>`. This weighs FILE bytes, and
|
|
470
|
+
// per-part framing is never zero, so a bundle whose files total exactly the
|
|
471
|
+
// limit is always a request LARGER than the limit — a real 413. See
|
|
472
|
+
// {@link API_SKILL_MAX_UPLOAD_BYTES} for the measured boundary and why the two
|
|
473
|
+
// operators must not be made to match.
|
|
474
|
+
if (total < limitBytes)
|
|
475
|
+
return issues;
|
|
476
|
+
const registryEntry = CODE_REGISTRY.PACKAGED_SIZE_EXCEEDS_API_LIMIT;
|
|
477
|
+
const heaviest = largestFirst(files)[0];
|
|
478
|
+
issues.push({
|
|
479
|
+
severity: registryEntry.defaultSeverity,
|
|
480
|
+
code: 'PACKAGED_SIZE_EXCEEDS_API_LIMIT',
|
|
481
|
+
message: describeOversizeBundle(files, { of: 'packaged-files', bytes: total }, limitBytes),
|
|
482
|
+
// The bundle root, because the finding is about the bundle. Naming the
|
|
483
|
+
// largest file here would claim that one file is the defect, which is only
|
|
484
|
+
// true when it alone exceeds the ceiling — it rides as `link` instead.
|
|
485
|
+
location: '.',
|
|
486
|
+
// Absent only for a bundle with no weighable file at all, which needs a
|
|
487
|
+
// zero or negative limit to reach this line.
|
|
488
|
+
...(heaviest === undefined ? {} : { link: heaviest.path }),
|
|
489
|
+
fix: registryEntry.fix,
|
|
490
|
+
reference: registryEntry.reference,
|
|
491
|
+
});
|
|
492
|
+
return issues;
|
|
493
|
+
}
|
|
494
|
+
//# sourceMappingURL=packaged-size-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"packaged-size-limit.js","sourceRoot":"","sources":["../../src/validators/packaged-size-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -356,6 +356,16 @@ export declare function validateSkillForPackaging(skillPath: string, packagingCo
|
|
|
356
356
|
* with values derived from the skill path.
|
|
357
357
|
*/
|
|
358
358
|
export declare function detectNameMismatchIssue(frontmatterName: unknown, parentDir: string, skillLocation: string): ValidationIssue | null;
|
|
359
|
+
/**
|
|
360
|
+
* Collect NON_PORTABLE_ASSET_REFERENCE issues — scan one skill document for any
|
|
361
|
+
* member of the non-portable asset-reference family.
|
|
362
|
+
*/
|
|
363
|
+
export declare function collectNonPortableAssetReferenceIssues(content: string, docLocation: string, issues: ValidationIssue[]): void;
|
|
364
|
+
/**
|
|
365
|
+
* Collect NON_PORTABLE_COMMAND issues — scan one skill document for any member of
|
|
366
|
+
* the non-portable shell-command family.
|
|
367
|
+
*/
|
|
368
|
+
export declare function collectNonPortableCommandIssues(content: string, docLocation: string, issues: ValidationIssue[]): void;
|
|
359
369
|
/**
|
|
360
370
|
* Process links from parsed markdown and return resolved .md file paths.
|
|
361
371
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packaging-validator.d.ts","sourceRoot":"","sources":["../../src/validators/packaging-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAMH,OAAO,EAAkD,gBAAgB,EAAE,KAAK,wBAAwB,EAAE,KAAK,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAC3K,OAAO,EAIL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAEhB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,4BAA4B,CAAC;AAQpC,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAEhE,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExE,OAAO,EAML,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACvB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"packaging-validator.d.ts","sourceRoot":"","sources":["../../src/validators/packaging-validator.ts"],"names":[],"mappings":"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"}
|