vet-skills 0.5.2 → 0.7.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/dist/index.js +1114 -216
- package/package.json +11 -10
package/dist/index.js
CHANGED
|
@@ -111,7 +111,7 @@ var require_visit = __commonJS({
|
|
|
111
111
|
"use strict";
|
|
112
112
|
var identity = require_identity();
|
|
113
113
|
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
114
|
-
var
|
|
114
|
+
var SKIP3 = /* @__PURE__ */ Symbol("skip children");
|
|
115
115
|
var REMOVE = /* @__PURE__ */ Symbol("remove node");
|
|
116
116
|
function visit(node, visitor) {
|
|
117
117
|
const visitor_ = initVisitor(visitor);
|
|
@@ -123,7 +123,7 @@ var require_visit = __commonJS({
|
|
|
123
123
|
visit_(null, node, visitor_, Object.freeze([]));
|
|
124
124
|
}
|
|
125
125
|
visit.BREAK = BREAK;
|
|
126
|
-
visit.SKIP =
|
|
126
|
+
visit.SKIP = SKIP3;
|
|
127
127
|
visit.REMOVE = REMOVE;
|
|
128
128
|
function visit_(key, node, visitor, path) {
|
|
129
129
|
const ctrl = callVisitor(key, node, visitor, path);
|
|
@@ -171,7 +171,7 @@ var require_visit = __commonJS({
|
|
|
171
171
|
await visitAsync_(null, node, visitor_, Object.freeze([]));
|
|
172
172
|
}
|
|
173
173
|
visitAsync.BREAK = BREAK;
|
|
174
|
-
visitAsync.SKIP =
|
|
174
|
+
visitAsync.SKIP = SKIP3;
|
|
175
175
|
visitAsync.REMOVE = REMOVE;
|
|
176
176
|
async function visitAsync_(key, node, visitor, path) {
|
|
177
177
|
const ctrl = await callVisitor(key, node, visitor, path);
|
|
@@ -995,13 +995,13 @@ var require_Collection = __commonJS({
|
|
|
995
995
|
var require_stringifyComment = __commonJS({
|
|
996
996
|
"../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/stringify/stringifyComment.js"(exports) {
|
|
997
997
|
"use strict";
|
|
998
|
-
var stringifyComment = (
|
|
998
|
+
var stringifyComment = (str2) => str2.replace(/^(?!$)(?: $)?/gm, "#");
|
|
999
999
|
function indentComment(comment, indent) {
|
|
1000
1000
|
if (/^\n+$/.test(comment))
|
|
1001
1001
|
return comment.substring(1);
|
|
1002
1002
|
return indent ? comment.replace(/^(?! *$)/gm, indent) : comment;
|
|
1003
1003
|
}
|
|
1004
|
-
var lineComment = (
|
|
1004
|
+
var lineComment = (str2, indent, comment) => str2.endsWith("\n") ? indentComment(comment, indent) : comment.includes("\n") ? "\n" + indentComment(comment, indent) : (str2.endsWith(" ") ? "" : " ") + comment;
|
|
1005
1005
|
exports.indentComment = indentComment;
|
|
1006
1006
|
exports.lineComment = lineComment;
|
|
1007
1007
|
exports.stringifyComment = stringifyComment;
|
|
@@ -1155,16 +1155,16 @@ var require_stringifyString = __commonJS({
|
|
|
1155
1155
|
lineWidth: ctx.options.lineWidth,
|
|
1156
1156
|
minContentWidth: ctx.options.minContentWidth
|
|
1157
1157
|
});
|
|
1158
|
-
var containsDocumentMarker = (
|
|
1159
|
-
function lineLengthOverLimit(
|
|
1158
|
+
var containsDocumentMarker = (str2) => /^(%|---|\.\.\.)/m.test(str2);
|
|
1159
|
+
function lineLengthOverLimit(str2, lineWidth, indentLength) {
|
|
1160
1160
|
if (!lineWidth || lineWidth < 0)
|
|
1161
1161
|
return false;
|
|
1162
1162
|
const limit = lineWidth - indentLength;
|
|
1163
|
-
const strLen =
|
|
1163
|
+
const strLen = str2.length;
|
|
1164
1164
|
if (strLen <= limit)
|
|
1165
1165
|
return false;
|
|
1166
1166
|
for (let i = 0, start = 0; i < strLen; ++i) {
|
|
1167
|
-
if (
|
|
1167
|
+
if (str2[i] === "\n") {
|
|
1168
1168
|
if (i - start > limit)
|
|
1169
1169
|
return true;
|
|
1170
1170
|
start = i + 1;
|
|
@@ -1181,11 +1181,11 @@ var require_stringifyString = __commonJS({
|
|
|
1181
1181
|
const { implicitKey } = ctx;
|
|
1182
1182
|
const minMultiLineLength = ctx.options.doubleQuotedMinMultiLineLength;
|
|
1183
1183
|
const indent = ctx.indent || (containsDocumentMarker(value) ? " " : "");
|
|
1184
|
-
let
|
|
1184
|
+
let str2 = "";
|
|
1185
1185
|
let start = 0;
|
|
1186
1186
|
for (let i = 0, ch = json[i]; ch; ch = json[++i]) {
|
|
1187
1187
|
if (ch === " " && json[i + 1] === "\\" && json[i + 2] === "n") {
|
|
1188
|
-
|
|
1188
|
+
str2 += json.slice(start, i) + "\\ ";
|
|
1189
1189
|
i += 1;
|
|
1190
1190
|
start = i;
|
|
1191
1191
|
ch = "\\";
|
|
@@ -1194,38 +1194,38 @@ var require_stringifyString = __commonJS({
|
|
|
1194
1194
|
switch (json[i + 1]) {
|
|
1195
1195
|
case "u":
|
|
1196
1196
|
{
|
|
1197
|
-
|
|
1197
|
+
str2 += json.slice(start, i);
|
|
1198
1198
|
const code = json.substr(i + 2, 4);
|
|
1199
1199
|
switch (code) {
|
|
1200
1200
|
case "0000":
|
|
1201
|
-
|
|
1201
|
+
str2 += "\\0";
|
|
1202
1202
|
break;
|
|
1203
1203
|
case "0007":
|
|
1204
|
-
|
|
1204
|
+
str2 += "\\a";
|
|
1205
1205
|
break;
|
|
1206
1206
|
case "000b":
|
|
1207
|
-
|
|
1207
|
+
str2 += "\\v";
|
|
1208
1208
|
break;
|
|
1209
1209
|
case "001b":
|
|
1210
|
-
|
|
1210
|
+
str2 += "\\e";
|
|
1211
1211
|
break;
|
|
1212
1212
|
case "0085":
|
|
1213
|
-
|
|
1213
|
+
str2 += "\\N";
|
|
1214
1214
|
break;
|
|
1215
1215
|
case "00a0":
|
|
1216
|
-
|
|
1216
|
+
str2 += "\\_";
|
|
1217
1217
|
break;
|
|
1218
1218
|
case "2028":
|
|
1219
|
-
|
|
1219
|
+
str2 += "\\L";
|
|
1220
1220
|
break;
|
|
1221
1221
|
case "2029":
|
|
1222
|
-
|
|
1222
|
+
str2 += "\\P";
|
|
1223
1223
|
break;
|
|
1224
1224
|
default:
|
|
1225
1225
|
if (code.substr(0, 2) === "00")
|
|
1226
|
-
|
|
1226
|
+
str2 += "\\x" + code.substr(2);
|
|
1227
1227
|
else
|
|
1228
|
-
|
|
1228
|
+
str2 += json.substr(i, 6);
|
|
1229
1229
|
}
|
|
1230
1230
|
i += 5;
|
|
1231
1231
|
start = i + 1;
|
|
@@ -1235,14 +1235,14 @@ var require_stringifyString = __commonJS({
|
|
|
1235
1235
|
if (implicitKey || json[i + 2] === '"' || json.length < minMultiLineLength) {
|
|
1236
1236
|
i += 1;
|
|
1237
1237
|
} else {
|
|
1238
|
-
|
|
1238
|
+
str2 += json.slice(start, i) + "\n\n";
|
|
1239
1239
|
while (json[i + 2] === "\\" && json[i + 3] === "n" && json[i + 4] !== '"') {
|
|
1240
|
-
|
|
1240
|
+
str2 += "\n";
|
|
1241
1241
|
i += 2;
|
|
1242
1242
|
}
|
|
1243
|
-
|
|
1243
|
+
str2 += indent;
|
|
1244
1244
|
if (json[i + 2] === " ")
|
|
1245
|
-
|
|
1245
|
+
str2 += "\\";
|
|
1246
1246
|
i += 1;
|
|
1247
1247
|
start = i + 1;
|
|
1248
1248
|
}
|
|
@@ -1251,8 +1251,8 @@ var require_stringifyString = __commonJS({
|
|
|
1251
1251
|
i += 1;
|
|
1252
1252
|
}
|
|
1253
1253
|
}
|
|
1254
|
-
|
|
1255
|
-
return implicitKey ?
|
|
1254
|
+
str2 = start ? str2 + json.slice(start) : json;
|
|
1255
|
+
return implicitKey ? str2 : foldFlowLines.foldFlowLines(str2, indent, foldFlowLines.FOLD_QUOTED, getFoldOptions(ctx, false));
|
|
1256
1256
|
}
|
|
1257
1257
|
function singleQuotedString(value, ctx) {
|
|
1258
1258
|
if (ctx.options.singleQuote === false || ctx.implicitKey && value.includes("\n") || /[ \t]\n|\n[ \t]/.test(value))
|
|
@@ -1380,15 +1380,15 @@ ${indent}${start}${value}${end}`;
|
|
|
1380
1380
|
return quotedString(value, ctx);
|
|
1381
1381
|
}
|
|
1382
1382
|
}
|
|
1383
|
-
const
|
|
1383
|
+
const str2 = value.replace(/\n+/g, `$&
|
|
1384
1384
|
${indent}`);
|
|
1385
1385
|
if (actualString) {
|
|
1386
|
-
const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(
|
|
1386
|
+
const test = (tag) => tag.default && tag.tag !== "tag:yaml.org,2002:str" && tag.test?.test(str2);
|
|
1387
1387
|
const { compat, tags } = ctx.doc.schema;
|
|
1388
1388
|
if (tags.some(test) || compat?.some(test))
|
|
1389
1389
|
return quotedString(value, ctx);
|
|
1390
1390
|
}
|
|
1391
|
-
return implicitKey ?
|
|
1391
|
+
return implicitKey ? str2 : foldFlowLines.foldFlowLines(str2, indent, foldFlowLines.FOLD_FLOW, getFoldOptions(ctx, false));
|
|
1392
1392
|
}
|
|
1393
1393
|
function stringifyString(item, ctx, onComment, onChompKeep) {
|
|
1394
1394
|
const { implicitKey, inFlow } = ctx;
|
|
@@ -1540,11 +1540,11 @@ var require_stringify = __commonJS({
|
|
|
1540
1540
|
const props = stringifyProps(node, tagObj, ctx);
|
|
1541
1541
|
if (props.length > 0)
|
|
1542
1542
|
ctx.indentAtStart = (ctx.indentAtStart ?? 0) + props.length + 1;
|
|
1543
|
-
const
|
|
1543
|
+
const str2 = typeof tagObj.stringify === "function" ? tagObj.stringify(node, ctx, onComment, onChompKeep) : identity.isScalar(node) ? stringifyString.stringifyString(node, ctx, onComment, onChompKeep) : node.toString(ctx, onComment, onChompKeep);
|
|
1544
1544
|
if (!props)
|
|
1545
|
-
return
|
|
1546
|
-
return identity.isScalar(node) ||
|
|
1547
|
-
${ctx.indent}${
|
|
1545
|
+
return str2;
|
|
1546
|
+
return identity.isScalar(node) || str2[0] === "{" || str2[0] === "[" ? `${props} ${str2}` : `${props}
|
|
1547
|
+
${ctx.indent}${str2}`;
|
|
1548
1548
|
}
|
|
1549
1549
|
exports.createStringifyContext = createStringifyContext;
|
|
1550
1550
|
exports.stringify = stringify;
|
|
@@ -1579,8 +1579,8 @@ var require_stringifyPair = __commonJS({
|
|
|
1579
1579
|
});
|
|
1580
1580
|
let keyCommentDone = false;
|
|
1581
1581
|
let chompKeep = false;
|
|
1582
|
-
let
|
|
1583
|
-
if (!explicitKey && !ctx.inFlow &&
|
|
1582
|
+
let str2 = stringify.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
|
|
1583
|
+
if (!explicitKey && !ctx.inFlow && str2.length > 1024) {
|
|
1584
1584
|
if (simpleKeys)
|
|
1585
1585
|
throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
|
|
1586
1586
|
explicitKey = true;
|
|
@@ -1589,27 +1589,27 @@ var require_stringifyPair = __commonJS({
|
|
|
1589
1589
|
if (allNullValues || value == null) {
|
|
1590
1590
|
if (keyCommentDone && onComment)
|
|
1591
1591
|
onComment();
|
|
1592
|
-
return
|
|
1592
|
+
return str2 === "" ? "?" : explicitKey ? `? ${str2}` : str2;
|
|
1593
1593
|
}
|
|
1594
1594
|
} else if (allNullValues && !simpleKeys || value == null && explicitKey) {
|
|
1595
|
-
|
|
1595
|
+
str2 = `? ${str2}`;
|
|
1596
1596
|
if (keyComment && !keyCommentDone) {
|
|
1597
|
-
|
|
1597
|
+
str2 += stringifyComment.lineComment(str2, ctx.indent, commentString(keyComment));
|
|
1598
1598
|
} else if (chompKeep && onChompKeep)
|
|
1599
1599
|
onChompKeep();
|
|
1600
|
-
return
|
|
1600
|
+
return str2;
|
|
1601
1601
|
}
|
|
1602
1602
|
if (keyCommentDone)
|
|
1603
1603
|
keyComment = null;
|
|
1604
1604
|
if (explicitKey) {
|
|
1605
1605
|
if (keyComment)
|
|
1606
|
-
|
|
1607
|
-
|
|
1606
|
+
str2 += stringifyComment.lineComment(str2, ctx.indent, commentString(keyComment));
|
|
1607
|
+
str2 = `? ${str2}
|
|
1608
1608
|
${indent}:`;
|
|
1609
1609
|
} else {
|
|
1610
|
-
|
|
1610
|
+
str2 = `${str2}:`;
|
|
1611
1611
|
if (keyComment)
|
|
1612
|
-
|
|
1612
|
+
str2 += stringifyComment.lineComment(str2, ctx.indent, commentString(keyComment));
|
|
1613
1613
|
}
|
|
1614
1614
|
let vsb, vcb, valueComment;
|
|
1615
1615
|
if (identity.isNode(value)) {
|
|
@@ -1625,7 +1625,7 @@ ${indent}:`;
|
|
|
1625
1625
|
}
|
|
1626
1626
|
ctx.implicitKey = false;
|
|
1627
1627
|
if (!explicitKey && !keyComment && identity.isScalar(value))
|
|
1628
|
-
ctx.indentAtStart =
|
|
1628
|
+
ctx.indentAtStart = str2.length + 1;
|
|
1629
1629
|
chompKeep = false;
|
|
1630
1630
|
if (!indentSeq && indentStep.length >= 2 && !ctx.inFlow && !explicitKey && identity.isSeq(value) && !value.flow && !value.tag && !value.anchor) {
|
|
1631
1631
|
ctx.indent = ctx.indent.substring(2);
|
|
@@ -1669,16 +1669,16 @@ ${ctx.indent}`;
|
|
|
1669
1669
|
} else if (valueStr === "" || valueStr[0] === "\n") {
|
|
1670
1670
|
ws = "";
|
|
1671
1671
|
}
|
|
1672
|
-
|
|
1672
|
+
str2 += ws + valueStr;
|
|
1673
1673
|
if (ctx.inFlow) {
|
|
1674
1674
|
if (valueCommentDone && onComment)
|
|
1675
1675
|
onComment();
|
|
1676
1676
|
} else if (valueComment && !valueCommentDone) {
|
|
1677
|
-
|
|
1677
|
+
str2 += stringifyComment.lineComment(str2, ctx.indent, commentString(valueComment));
|
|
1678
1678
|
} else if (chompKeep && onChompKeep) {
|
|
1679
1679
|
onChompKeep();
|
|
1680
1680
|
}
|
|
1681
|
-
return
|
|
1681
|
+
return str2;
|
|
1682
1682
|
}
|
|
1683
1683
|
exports.stringifyPair = stringifyPair;
|
|
1684
1684
|
}
|
|
@@ -1905,31 +1905,31 @@ var require_stringifyCollection = __commonJS({
|
|
|
1905
1905
|
}
|
|
1906
1906
|
}
|
|
1907
1907
|
chompKeep = false;
|
|
1908
|
-
let
|
|
1908
|
+
let str3 = stringify.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
|
|
1909
1909
|
if (comment2)
|
|
1910
|
-
|
|
1910
|
+
str3 += stringifyComment.lineComment(str3, itemIndent, commentString(comment2));
|
|
1911
1911
|
if (chompKeep && comment2)
|
|
1912
1912
|
chompKeep = false;
|
|
1913
|
-
lines.push(blockItemPrefix +
|
|
1913
|
+
lines.push(blockItemPrefix + str3);
|
|
1914
1914
|
}
|
|
1915
|
-
let
|
|
1915
|
+
let str2;
|
|
1916
1916
|
if (lines.length === 0) {
|
|
1917
|
-
|
|
1917
|
+
str2 = flowChars.start + flowChars.end;
|
|
1918
1918
|
} else {
|
|
1919
|
-
|
|
1919
|
+
str2 = lines[0];
|
|
1920
1920
|
for (let i = 1; i < lines.length; ++i) {
|
|
1921
1921
|
const line = lines[i];
|
|
1922
|
-
|
|
1922
|
+
str2 += line ? `
|
|
1923
1923
|
${indent}${line}` : "\n";
|
|
1924
1924
|
}
|
|
1925
1925
|
}
|
|
1926
1926
|
if (comment) {
|
|
1927
|
-
|
|
1927
|
+
str2 += "\n" + stringifyComment.indentComment(commentString(comment), indent);
|
|
1928
1928
|
if (onComment)
|
|
1929
1929
|
onComment();
|
|
1930
1930
|
} else if (chompKeep && onChompKeep)
|
|
1931
1931
|
onChompKeep();
|
|
1932
|
-
return
|
|
1932
|
+
return str2;
|
|
1933
1933
|
}
|
|
1934
1934
|
function stringifyFlowCollection({ items }, ctx, { flowChars, itemIndent }) {
|
|
1935
1935
|
const { indent, indentStep, flowCollectionPadding: fcPadding, options: { commentString } } = ctx;
|
|
@@ -1972,21 +1972,21 @@ ${indent}${line}` : "\n";
|
|
|
1972
1972
|
}
|
|
1973
1973
|
if (comment)
|
|
1974
1974
|
reqNewline = true;
|
|
1975
|
-
let
|
|
1976
|
-
reqNewline || (reqNewline = lines.length > linesAtValue ||
|
|
1975
|
+
let str2 = stringify.stringify(item, itemCtx, () => comment = null);
|
|
1976
|
+
reqNewline || (reqNewline = lines.length > linesAtValue || str2.includes("\n"));
|
|
1977
1977
|
if (i < items.length - 1) {
|
|
1978
|
-
|
|
1978
|
+
str2 += ",";
|
|
1979
1979
|
} else if (ctx.options.trailingComma) {
|
|
1980
1980
|
if (ctx.options.lineWidth > 0) {
|
|
1981
|
-
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (
|
|
1981
|
+
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str2.length + 2) > ctx.options.lineWidth);
|
|
1982
1982
|
}
|
|
1983
1983
|
if (reqNewline) {
|
|
1984
|
-
|
|
1984
|
+
str2 += ",";
|
|
1985
1985
|
}
|
|
1986
1986
|
}
|
|
1987
1987
|
if (comment)
|
|
1988
|
-
|
|
1989
|
-
lines.push(
|
|
1988
|
+
str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment));
|
|
1989
|
+
lines.push(str2);
|
|
1990
1990
|
linesAtValue = lines.length;
|
|
1991
1991
|
}
|
|
1992
1992
|
const { start, end } = flowChars;
|
|
@@ -1998,11 +1998,11 @@ ${indent}${line}` : "\n";
|
|
|
1998
1998
|
reqNewline = ctx.options.lineWidth > 0 && len > ctx.options.lineWidth;
|
|
1999
1999
|
}
|
|
2000
2000
|
if (reqNewline) {
|
|
2001
|
-
let
|
|
2001
|
+
let str2 = start;
|
|
2002
2002
|
for (const line of lines)
|
|
2003
|
-
|
|
2003
|
+
str2 += line ? `
|
|
2004
2004
|
${indentStep}${indent}${line}` : "\n";
|
|
2005
|
-
return `${
|
|
2005
|
+
return `${str2}
|
|
2006
2006
|
${indent}${end}`;
|
|
2007
2007
|
} else {
|
|
2008
2008
|
return `${start}${fcPadding}${lines.join(" ")}${fcPadding}${end}`;
|
|
@@ -2334,7 +2334,7 @@ var require_string = __commonJS({
|
|
|
2334
2334
|
identify: (value) => typeof value === "string",
|
|
2335
2335
|
default: true,
|
|
2336
2336
|
tag: "tag:yaml.org,2002:str",
|
|
2337
|
-
resolve: (
|
|
2337
|
+
resolve: (str2) => str2,
|
|
2338
2338
|
stringify(item, ctx, onComment, onChompKeep) {
|
|
2339
2339
|
ctx = Object.assign({ actualString: true }, ctx);
|
|
2340
2340
|
return stringifyString.stringifyString(item, ctx, onComment, onChompKeep);
|
|
@@ -2372,7 +2372,7 @@ var require_bool = __commonJS({
|
|
|
2372
2372
|
default: true,
|
|
2373
2373
|
tag: "tag:yaml.org,2002:bool",
|
|
2374
2374
|
test: /^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/,
|
|
2375
|
-
resolve: (
|
|
2375
|
+
resolve: (str2) => new Scalar.Scalar(str2[0] === "t" || str2[0] === "T"),
|
|
2376
2376
|
stringify({ source, value }, ctx) {
|
|
2377
2377
|
if (source && boolTag.test.test(source)) {
|
|
2378
2378
|
const sv = source[0] === "t" || source[0] === "T";
|
|
@@ -2424,7 +2424,7 @@ var require_float = __commonJS({
|
|
|
2424
2424
|
default: true,
|
|
2425
2425
|
tag: "tag:yaml.org,2002:float",
|
|
2426
2426
|
test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/,
|
|
2427
|
-
resolve: (
|
|
2427
|
+
resolve: (str2) => str2.slice(-3).toLowerCase() === "nan" ? NaN : str2[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY,
|
|
2428
2428
|
stringify: stringifyNumber.stringifyNumber
|
|
2429
2429
|
};
|
|
2430
2430
|
var floatExp = {
|
|
@@ -2433,7 +2433,7 @@ var require_float = __commonJS({
|
|
|
2433
2433
|
tag: "tag:yaml.org,2002:float",
|
|
2434
2434
|
format: "EXP",
|
|
2435
2435
|
test: /^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,
|
|
2436
|
-
resolve: (
|
|
2436
|
+
resolve: (str2) => parseFloat(str2),
|
|
2437
2437
|
stringify(node) {
|
|
2438
2438
|
const num = Number(node.value);
|
|
2439
2439
|
return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
|
|
@@ -2444,11 +2444,11 @@ var require_float = __commonJS({
|
|
|
2444
2444
|
default: true,
|
|
2445
2445
|
tag: "tag:yaml.org,2002:float",
|
|
2446
2446
|
test: /^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/,
|
|
2447
|
-
resolve(
|
|
2448
|
-
const node = new Scalar.Scalar(parseFloat(
|
|
2449
|
-
const dot =
|
|
2450
|
-
if (dot !== -1 &&
|
|
2451
|
-
node.minFractionDigits =
|
|
2447
|
+
resolve(str2) {
|
|
2448
|
+
const node = new Scalar.Scalar(parseFloat(str2));
|
|
2449
|
+
const dot = str2.indexOf(".");
|
|
2450
|
+
if (dot !== -1 && str2[str2.length - 1] === "0")
|
|
2451
|
+
node.minFractionDigits = str2.length - dot - 1;
|
|
2452
2452
|
return node;
|
|
2453
2453
|
},
|
|
2454
2454
|
stringify: stringifyNumber.stringifyNumber
|
|
@@ -2465,7 +2465,7 @@ var require_int = __commonJS({
|
|
|
2465
2465
|
"use strict";
|
|
2466
2466
|
var stringifyNumber = require_stringifyNumber();
|
|
2467
2467
|
var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
|
|
2468
|
-
var intResolve = (
|
|
2468
|
+
var intResolve = (str2, offset, radix, { intAsBigInt }) => intAsBigInt ? BigInt(str2) : parseInt(str2.substring(offset), radix);
|
|
2469
2469
|
function intStringify(node, radix, prefix) {
|
|
2470
2470
|
const { value } = node;
|
|
2471
2471
|
if (intIdentify(value) && value >= 0)
|
|
@@ -2478,7 +2478,7 @@ var require_int = __commonJS({
|
|
|
2478
2478
|
tag: "tag:yaml.org,2002:int",
|
|
2479
2479
|
format: "OCT",
|
|
2480
2480
|
test: /^0o[0-7]+$/,
|
|
2481
|
-
resolve: (
|
|
2481
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 2, 8, opt),
|
|
2482
2482
|
stringify: (node) => intStringify(node, 8, "0o")
|
|
2483
2483
|
};
|
|
2484
2484
|
var int = {
|
|
@@ -2486,7 +2486,7 @@ var require_int = __commonJS({
|
|
|
2486
2486
|
default: true,
|
|
2487
2487
|
tag: "tag:yaml.org,2002:int",
|
|
2488
2488
|
test: /^[-+]?[0-9]+$/,
|
|
2489
|
-
resolve: (
|
|
2489
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 0, 10, opt),
|
|
2490
2490
|
stringify: stringifyNumber.stringifyNumber
|
|
2491
2491
|
};
|
|
2492
2492
|
var intHex = {
|
|
@@ -2495,7 +2495,7 @@ var require_int = __commonJS({
|
|
|
2495
2495
|
tag: "tag:yaml.org,2002:int",
|
|
2496
2496
|
format: "HEX",
|
|
2497
2497
|
test: /^0x[0-9a-fA-F]+$/,
|
|
2498
|
-
resolve: (
|
|
2498
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 2, 16, opt),
|
|
2499
2499
|
stringify: (node) => intStringify(node, 16, "0x")
|
|
2500
2500
|
};
|
|
2501
2501
|
exports.int = int;
|
|
@@ -2548,7 +2548,7 @@ var require_schema2 = __commonJS({
|
|
|
2548
2548
|
identify: (value) => typeof value === "string",
|
|
2549
2549
|
default: true,
|
|
2550
2550
|
tag: "tag:yaml.org,2002:str",
|
|
2551
|
-
resolve: (
|
|
2551
|
+
resolve: (str2) => str2,
|
|
2552
2552
|
stringify: stringifyJSON
|
|
2553
2553
|
},
|
|
2554
2554
|
{
|
|
@@ -2565,7 +2565,7 @@ var require_schema2 = __commonJS({
|
|
|
2565
2565
|
default: true,
|
|
2566
2566
|
tag: "tag:yaml.org,2002:bool",
|
|
2567
2567
|
test: /^true$|^false$/,
|
|
2568
|
-
resolve: (
|
|
2568
|
+
resolve: (str2) => str2 === "true",
|
|
2569
2569
|
stringify: stringifyJSON
|
|
2570
2570
|
},
|
|
2571
2571
|
{
|
|
@@ -2573,7 +2573,7 @@ var require_schema2 = __commonJS({
|
|
|
2573
2573
|
default: true,
|
|
2574
2574
|
tag: "tag:yaml.org,2002:int",
|
|
2575
2575
|
test: /^-?(?:0|[1-9][0-9]*)$/,
|
|
2576
|
-
resolve: (
|
|
2576
|
+
resolve: (str2, _onError, { intAsBigInt }) => intAsBigInt ? BigInt(str2) : parseInt(str2, 10),
|
|
2577
2577
|
stringify: ({ value }) => intIdentify(value) ? value.toString() : JSON.stringify(value)
|
|
2578
2578
|
},
|
|
2579
2579
|
{
|
|
@@ -2581,7 +2581,7 @@ var require_schema2 = __commonJS({
|
|
|
2581
2581
|
default: true,
|
|
2582
2582
|
tag: "tag:yaml.org,2002:float",
|
|
2583
2583
|
test: /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/,
|
|
2584
|
-
resolve: (
|
|
2584
|
+
resolve: (str2) => parseFloat(str2),
|
|
2585
2585
|
stringify: stringifyJSON
|
|
2586
2586
|
}
|
|
2587
2587
|
];
|
|
@@ -2589,9 +2589,9 @@ var require_schema2 = __commonJS({
|
|
|
2589
2589
|
default: true,
|
|
2590
2590
|
tag: "",
|
|
2591
2591
|
test: /^/,
|
|
2592
|
-
resolve(
|
|
2593
|
-
onError(`Unresolved plain scalar ${JSON.stringify(
|
|
2594
|
-
return
|
|
2592
|
+
resolve(str2, onError) {
|
|
2593
|
+
onError(`Unresolved plain scalar ${JSON.stringify(str2)}`);
|
|
2594
|
+
return str2;
|
|
2595
2595
|
}
|
|
2596
2596
|
};
|
|
2597
2597
|
var schema = [map.map, seq.seq].concat(jsonScalars, jsonError);
|
|
@@ -2623,10 +2623,10 @@ var require_binary = __commonJS({
|
|
|
2623
2623
|
if (typeof node_buffer.Buffer === "function") {
|
|
2624
2624
|
return node_buffer.Buffer.from(src, "base64");
|
|
2625
2625
|
} else if (typeof atob === "function") {
|
|
2626
|
-
const
|
|
2627
|
-
const buffer = new Uint8Array(
|
|
2628
|
-
for (let i = 0; i <
|
|
2629
|
-
buffer[i] =
|
|
2626
|
+
const str2 = atob(src.replace(/[\n\r]/g, ""));
|
|
2627
|
+
const buffer = new Uint8Array(str2.length);
|
|
2628
|
+
for (let i = 0; i < str2.length; ++i)
|
|
2629
|
+
buffer[i] = str2.charCodeAt(i);
|
|
2630
2630
|
return buffer;
|
|
2631
2631
|
} else {
|
|
2632
2632
|
onError("This environment does not support reading binary tags; either Buffer or atob is required");
|
|
@@ -2637,28 +2637,28 @@ var require_binary = __commonJS({
|
|
|
2637
2637
|
if (!value)
|
|
2638
2638
|
return "";
|
|
2639
2639
|
const buf = value;
|
|
2640
|
-
let
|
|
2640
|
+
let str2;
|
|
2641
2641
|
if (typeof node_buffer.Buffer === "function") {
|
|
2642
|
-
|
|
2642
|
+
str2 = buf instanceof node_buffer.Buffer ? buf.toString("base64") : node_buffer.Buffer.from(buf.buffer).toString("base64");
|
|
2643
2643
|
} else if (typeof btoa === "function") {
|
|
2644
2644
|
let s = "";
|
|
2645
2645
|
for (let i = 0; i < buf.length; ++i)
|
|
2646
2646
|
s += String.fromCharCode(buf[i]);
|
|
2647
|
-
|
|
2647
|
+
str2 = btoa(s);
|
|
2648
2648
|
} else {
|
|
2649
2649
|
throw new Error("This environment does not support writing binary tags; either Buffer or btoa is required");
|
|
2650
2650
|
}
|
|
2651
2651
|
type ?? (type = Scalar.Scalar.BLOCK_LITERAL);
|
|
2652
2652
|
if (type !== Scalar.Scalar.QUOTE_DOUBLE) {
|
|
2653
2653
|
const lineWidth = Math.max(ctx.options.lineWidth - ctx.indent.length, ctx.options.minContentWidth);
|
|
2654
|
-
const n = Math.ceil(
|
|
2654
|
+
const n = Math.ceil(str2.length / lineWidth);
|
|
2655
2655
|
const lines = new Array(n);
|
|
2656
2656
|
for (let i = 0, o = 0; i < n; ++i, o += lineWidth) {
|
|
2657
|
-
lines[i] =
|
|
2657
|
+
lines[i] = str2.substr(o, lineWidth);
|
|
2658
2658
|
}
|
|
2659
|
-
|
|
2659
|
+
str2 = lines.join(type === Scalar.Scalar.BLOCK_LITERAL ? "\n" : " ");
|
|
2660
2660
|
}
|
|
2661
|
-
return stringifyString.stringifyString({ comment, type, value:
|
|
2661
|
+
return stringifyString.stringifyString({ comment, type, value: str2 }, ctx, onComment, onChompKeep);
|
|
2662
2662
|
}
|
|
2663
2663
|
};
|
|
2664
2664
|
exports.binary = binary;
|
|
@@ -2864,7 +2864,7 @@ var require_float2 = __commonJS({
|
|
|
2864
2864
|
default: true,
|
|
2865
2865
|
tag: "tag:yaml.org,2002:float",
|
|
2866
2866
|
test: /^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/,
|
|
2867
|
-
resolve: (
|
|
2867
|
+
resolve: (str2) => str2.slice(-3).toLowerCase() === "nan" ? NaN : str2[0] === "-" ? Number.NEGATIVE_INFINITY : Number.POSITIVE_INFINITY,
|
|
2868
2868
|
stringify: stringifyNumber.stringifyNumber
|
|
2869
2869
|
};
|
|
2870
2870
|
var floatExp = {
|
|
@@ -2873,7 +2873,7 @@ var require_float2 = __commonJS({
|
|
|
2873
2873
|
tag: "tag:yaml.org,2002:float",
|
|
2874
2874
|
format: "EXP",
|
|
2875
2875
|
test: /^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,
|
|
2876
|
-
resolve: (
|
|
2876
|
+
resolve: (str2) => parseFloat(str2.replace(/_/g, "")),
|
|
2877
2877
|
stringify(node) {
|
|
2878
2878
|
const num = Number(node.value);
|
|
2879
2879
|
return isFinite(num) ? num.toExponential() : stringifyNumber.stringifyNumber(node);
|
|
@@ -2884,11 +2884,11 @@ var require_float2 = __commonJS({
|
|
|
2884
2884
|
default: true,
|
|
2885
2885
|
tag: "tag:yaml.org,2002:float",
|
|
2886
2886
|
test: /^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/,
|
|
2887
|
-
resolve(
|
|
2888
|
-
const node = new Scalar.Scalar(parseFloat(
|
|
2889
|
-
const dot =
|
|
2887
|
+
resolve(str2) {
|
|
2888
|
+
const node = new Scalar.Scalar(parseFloat(str2.replace(/_/g, "")));
|
|
2889
|
+
const dot = str2.indexOf(".");
|
|
2890
2890
|
if (dot !== -1) {
|
|
2891
|
-
const f =
|
|
2891
|
+
const f = str2.substring(dot + 1).replace(/_/g, "");
|
|
2892
2892
|
if (f[f.length - 1] === "0")
|
|
2893
2893
|
node.minFractionDigits = f.length;
|
|
2894
2894
|
}
|
|
@@ -2908,34 +2908,34 @@ var require_int2 = __commonJS({
|
|
|
2908
2908
|
"use strict";
|
|
2909
2909
|
var stringifyNumber = require_stringifyNumber();
|
|
2910
2910
|
var intIdentify = (value) => typeof value === "bigint" || Number.isInteger(value);
|
|
2911
|
-
function intResolve(
|
|
2912
|
-
const sign =
|
|
2911
|
+
function intResolve(str2, offset, radix, { intAsBigInt }) {
|
|
2912
|
+
const sign = str2[0];
|
|
2913
2913
|
if (sign === "-" || sign === "+")
|
|
2914
2914
|
offset += 1;
|
|
2915
|
-
|
|
2915
|
+
str2 = str2.substring(offset).replace(/_/g, "");
|
|
2916
2916
|
if (intAsBigInt) {
|
|
2917
2917
|
switch (radix) {
|
|
2918
2918
|
case 2:
|
|
2919
|
-
|
|
2919
|
+
str2 = `0b${str2}`;
|
|
2920
2920
|
break;
|
|
2921
2921
|
case 8:
|
|
2922
|
-
|
|
2922
|
+
str2 = `0o${str2}`;
|
|
2923
2923
|
break;
|
|
2924
2924
|
case 16:
|
|
2925
|
-
|
|
2925
|
+
str2 = `0x${str2}`;
|
|
2926
2926
|
break;
|
|
2927
2927
|
}
|
|
2928
|
-
const n2 = BigInt(
|
|
2928
|
+
const n2 = BigInt(str2);
|
|
2929
2929
|
return sign === "-" ? BigInt(-1) * n2 : n2;
|
|
2930
2930
|
}
|
|
2931
|
-
const n = parseInt(
|
|
2931
|
+
const n = parseInt(str2, radix);
|
|
2932
2932
|
return sign === "-" ? -1 * n : n;
|
|
2933
2933
|
}
|
|
2934
2934
|
function intStringify(node, radix, prefix) {
|
|
2935
2935
|
const { value } = node;
|
|
2936
2936
|
if (intIdentify(value)) {
|
|
2937
|
-
const
|
|
2938
|
-
return value < 0 ? "-" + prefix +
|
|
2937
|
+
const str2 = value.toString(radix);
|
|
2938
|
+
return value < 0 ? "-" + prefix + str2.substr(1) : prefix + str2;
|
|
2939
2939
|
}
|
|
2940
2940
|
return stringifyNumber.stringifyNumber(node);
|
|
2941
2941
|
}
|
|
@@ -2945,7 +2945,7 @@ var require_int2 = __commonJS({
|
|
|
2945
2945
|
tag: "tag:yaml.org,2002:int",
|
|
2946
2946
|
format: "BIN",
|
|
2947
2947
|
test: /^[-+]?0b[0-1_]+$/,
|
|
2948
|
-
resolve: (
|
|
2948
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 2, 2, opt),
|
|
2949
2949
|
stringify: (node) => intStringify(node, 2, "0b")
|
|
2950
2950
|
};
|
|
2951
2951
|
var intOct = {
|
|
@@ -2954,7 +2954,7 @@ var require_int2 = __commonJS({
|
|
|
2954
2954
|
tag: "tag:yaml.org,2002:int",
|
|
2955
2955
|
format: "OCT",
|
|
2956
2956
|
test: /^[-+]?0[0-7_]+$/,
|
|
2957
|
-
resolve: (
|
|
2957
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 1, 8, opt),
|
|
2958
2958
|
stringify: (node) => intStringify(node, 8, "0")
|
|
2959
2959
|
};
|
|
2960
2960
|
var int = {
|
|
@@ -2962,7 +2962,7 @@ var require_int2 = __commonJS({
|
|
|
2962
2962
|
default: true,
|
|
2963
2963
|
tag: "tag:yaml.org,2002:int",
|
|
2964
2964
|
test: /^[-+]?[0-9][0-9_]*$/,
|
|
2965
|
-
resolve: (
|
|
2965
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 0, 10, opt),
|
|
2966
2966
|
stringify: stringifyNumber.stringifyNumber
|
|
2967
2967
|
};
|
|
2968
2968
|
var intHex = {
|
|
@@ -2971,7 +2971,7 @@ var require_int2 = __commonJS({
|
|
|
2971
2971
|
tag: "tag:yaml.org,2002:int",
|
|
2972
2972
|
format: "HEX",
|
|
2973
2973
|
test: /^[-+]?0x[0-9a-fA-F_]+$/,
|
|
2974
|
-
resolve: (
|
|
2974
|
+
resolve: (str2, _onError, opt) => intResolve(str2, 2, 16, opt),
|
|
2975
2975
|
stringify: (node) => intStringify(node, 16, "0x")
|
|
2976
2976
|
};
|
|
2977
2977
|
exports.int = int;
|
|
@@ -3075,9 +3075,9 @@ var require_timestamp = __commonJS({
|
|
|
3075
3075
|
"../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js"(exports) {
|
|
3076
3076
|
"use strict";
|
|
3077
3077
|
var stringifyNumber = require_stringifyNumber();
|
|
3078
|
-
function parseSexagesimal(
|
|
3079
|
-
const sign =
|
|
3080
|
-
const parts = sign === "-" || sign === "+" ?
|
|
3078
|
+
function parseSexagesimal(str2, asBigInt) {
|
|
3079
|
+
const sign = str2[0];
|
|
3080
|
+
const parts = sign === "-" || sign === "+" ? str2.substring(1) : str2;
|
|
3081
3081
|
const num = (n) => asBigInt ? BigInt(n) : Number(n);
|
|
3082
3082
|
const res = parts.replace(/_/g, "").split(":").reduce((res2, p) => res2 * num(60) + num(p), num(0));
|
|
3083
3083
|
return sign === "-" ? num(-1) * res : res;
|
|
@@ -3114,7 +3114,7 @@ var require_timestamp = __commonJS({
|
|
|
3114
3114
|
tag: "tag:yaml.org,2002:int",
|
|
3115
3115
|
format: "TIME",
|
|
3116
3116
|
test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/,
|
|
3117
|
-
resolve: (
|
|
3117
|
+
resolve: (str2, _onError, { intAsBigInt }) => parseSexagesimal(str2, intAsBigInt),
|
|
3118
3118
|
stringify: stringifySexagesimal
|
|
3119
3119
|
};
|
|
3120
3120
|
var floatTime = {
|
|
@@ -3123,7 +3123,7 @@ var require_timestamp = __commonJS({
|
|
|
3123
3123
|
tag: "tag:yaml.org,2002:float",
|
|
3124
3124
|
format: "TIME",
|
|
3125
3125
|
test: /^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/,
|
|
3126
|
-
resolve: (
|
|
3126
|
+
resolve: (str2) => parseSexagesimal(str2, false),
|
|
3127
3127
|
stringify: stringifySexagesimal
|
|
3128
3128
|
};
|
|
3129
3129
|
var timestamp = {
|
|
@@ -3134,8 +3134,8 @@ var require_timestamp = __commonJS({
|
|
|
3134
3134
|
// may be omitted altogether, resulting in a date format. In such a case, the time part is
|
|
3135
3135
|
// assumed to be 00:00:00Z (start of day, UTC).
|
|
3136
3136
|
test: RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"),
|
|
3137
|
-
resolve(
|
|
3138
|
-
const match =
|
|
3137
|
+
resolve(str2) {
|
|
3138
|
+
const match = str2.match(timestamp.test);
|
|
3139
3139
|
if (!match)
|
|
3140
3140
|
throw new Error("!!timestamp expects a date, starting with yyyy-mm-dd");
|
|
3141
3141
|
const [, year, month, day, hour, minute, second] = match.map(Number);
|
|
@@ -4011,10 +4011,10 @@ var require_resolve_block_map = __commonJS({
|
|
|
4011
4011
|
let offset = bm.offset;
|
|
4012
4012
|
let commentEnd = null;
|
|
4013
4013
|
for (const collItem of bm.items) {
|
|
4014
|
-
const { start, key, sep, value } = collItem;
|
|
4014
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
4015
4015
|
const keyProps = resolveProps.resolveProps(start, {
|
|
4016
4016
|
indicator: "explicit-key-ind",
|
|
4017
|
-
next: key ??
|
|
4017
|
+
next: key ?? sep2?.[0],
|
|
4018
4018
|
offset,
|
|
4019
4019
|
onError,
|
|
4020
4020
|
parentIndent: bm.indent,
|
|
@@ -4028,7 +4028,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4028
4028
|
else if ("indent" in key && key.indent !== bm.indent)
|
|
4029
4029
|
onError(offset, "BAD_INDENT", startColMsg);
|
|
4030
4030
|
}
|
|
4031
|
-
if (!keyProps.anchor && !keyProps.tag && !
|
|
4031
|
+
if (!keyProps.anchor && !keyProps.tag && !sep2) {
|
|
4032
4032
|
commentEnd = keyProps.end;
|
|
4033
4033
|
if (keyProps.comment) {
|
|
4034
4034
|
if (map.comment)
|
|
@@ -4052,7 +4052,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4052
4052
|
ctx.atKey = false;
|
|
4053
4053
|
if (utilMapIncludes.mapIncludes(ctx, map.items, keyNode))
|
|
4054
4054
|
onError(keyStart, "DUPLICATE_KEY", "Map keys must be unique");
|
|
4055
|
-
const valueProps = resolveProps.resolveProps(
|
|
4055
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
4056
4056
|
indicator: "map-value-ind",
|
|
4057
4057
|
next: value,
|
|
4058
4058
|
offset: keyNode.range[2],
|
|
@@ -4068,7 +4068,7 @@ var require_resolve_block_map = __commonJS({
|
|
|
4068
4068
|
if (ctx.options.strict && keyProps.start < valueProps.found.offset - 1024)
|
|
4069
4069
|
onError(keyNode.range, "KEY_OVER_1024_CHARS", "The : indicator must be at most 1024 chars after the start of an implicit block mapping key");
|
|
4070
4070
|
}
|
|
4071
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset,
|
|
4071
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : composeEmptyNode(ctx, offset, sep2, null, valueProps, onError);
|
|
4072
4072
|
if (ctx.schema.compat)
|
|
4073
4073
|
utilFlowIndentCheck.flowIndentCheck(bm.indent, value, onError);
|
|
4074
4074
|
offset = valueNode.range[2];
|
|
@@ -4159,7 +4159,7 @@ var require_resolve_end = __commonJS({
|
|
|
4159
4159
|
let comment = "";
|
|
4160
4160
|
if (end) {
|
|
4161
4161
|
let hasSpace = false;
|
|
4162
|
-
let
|
|
4162
|
+
let sep2 = "";
|
|
4163
4163
|
for (const token of end) {
|
|
4164
4164
|
const { source, type } = token;
|
|
4165
4165
|
switch (type) {
|
|
@@ -4173,13 +4173,13 @@ var require_resolve_end = __commonJS({
|
|
|
4173
4173
|
if (!comment)
|
|
4174
4174
|
comment = cb;
|
|
4175
4175
|
else
|
|
4176
|
-
comment +=
|
|
4177
|
-
|
|
4176
|
+
comment += sep2 + cb;
|
|
4177
|
+
sep2 = "";
|
|
4178
4178
|
break;
|
|
4179
4179
|
}
|
|
4180
4180
|
case "newline":
|
|
4181
4181
|
if (comment)
|
|
4182
|
-
|
|
4182
|
+
sep2 += source;
|
|
4183
4183
|
hasSpace = true;
|
|
4184
4184
|
break;
|
|
4185
4185
|
default:
|
|
@@ -4222,18 +4222,18 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4222
4222
|
let offset = fc.offset + fc.start.source.length;
|
|
4223
4223
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
4224
4224
|
const collItem = fc.items[i];
|
|
4225
|
-
const { start, key, sep, value } = collItem;
|
|
4225
|
+
const { start, key, sep: sep2, value } = collItem;
|
|
4226
4226
|
const props = resolveProps.resolveProps(start, {
|
|
4227
4227
|
flow: fcName,
|
|
4228
4228
|
indicator: "explicit-key-ind",
|
|
4229
|
-
next: key ??
|
|
4229
|
+
next: key ?? sep2?.[0],
|
|
4230
4230
|
offset,
|
|
4231
4231
|
onError,
|
|
4232
4232
|
parentIndent: fc.indent,
|
|
4233
4233
|
startOnNewline: false
|
|
4234
4234
|
});
|
|
4235
4235
|
if (!props.found) {
|
|
4236
|
-
if (!props.anchor && !props.tag && !
|
|
4236
|
+
if (!props.anchor && !props.tag && !sep2 && !value) {
|
|
4237
4237
|
if (i === 0 && props.comma)
|
|
4238
4238
|
onError(props.comma, "UNEXPECTED_TOKEN", `Unexpected , in ${fcName}`);
|
|
4239
4239
|
else if (i < fc.items.length - 1)
|
|
@@ -4287,8 +4287,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4287
4287
|
}
|
|
4288
4288
|
}
|
|
4289
4289
|
}
|
|
4290
|
-
if (!isMap && !
|
|
4291
|
-
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end,
|
|
4290
|
+
if (!isMap && !sep2 && !props.found) {
|
|
4291
|
+
const valueNode = value ? composeNode(ctx, value, props, onError) : composeEmptyNode(ctx, props.end, sep2, null, props, onError);
|
|
4292
4292
|
coll.items.push(valueNode);
|
|
4293
4293
|
offset = valueNode.range[2];
|
|
4294
4294
|
if (isBlock(value))
|
|
@@ -4300,7 +4300,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4300
4300
|
if (isBlock(key))
|
|
4301
4301
|
onError(keyNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
4302
4302
|
ctx.atKey = false;
|
|
4303
|
-
const valueProps = resolveProps.resolveProps(
|
|
4303
|
+
const valueProps = resolveProps.resolveProps(sep2 ?? [], {
|
|
4304
4304
|
flow: fcName,
|
|
4305
4305
|
indicator: "map-value-ind",
|
|
4306
4306
|
next: value,
|
|
@@ -4311,8 +4311,8 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4311
4311
|
});
|
|
4312
4312
|
if (valueProps.found) {
|
|
4313
4313
|
if (!isMap && !props.found && ctx.options.strict) {
|
|
4314
|
-
if (
|
|
4315
|
-
for (const st of
|
|
4314
|
+
if (sep2)
|
|
4315
|
+
for (const st of sep2) {
|
|
4316
4316
|
if (st === valueProps.found)
|
|
4317
4317
|
break;
|
|
4318
4318
|
if (st.type === "newline") {
|
|
@@ -4329,7 +4329,7 @@ var require_resolve_flow_collection = __commonJS({
|
|
|
4329
4329
|
else
|
|
4330
4330
|
onError(valueProps.start, "MISSING_CHAR", `Missing , or : between ${fcName} items`);
|
|
4331
4331
|
}
|
|
4332
|
-
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end,
|
|
4332
|
+
const valueNode = value ? composeNode(ctx, value, valueProps, onError) : valueProps.found ? composeEmptyNode(ctx, valueProps.end, sep2, null, valueProps, onError) : null;
|
|
4333
4333
|
if (valueNode) {
|
|
4334
4334
|
if (isBlock(value))
|
|
4335
4335
|
onError(valueNode.range, "BLOCK_IN_FLOW", blockMsg);
|
|
@@ -4509,7 +4509,7 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4509
4509
|
chompStart = i + 1;
|
|
4510
4510
|
}
|
|
4511
4511
|
let value = "";
|
|
4512
|
-
let
|
|
4512
|
+
let sep2 = "";
|
|
4513
4513
|
let prevMoreIndented = false;
|
|
4514
4514
|
for (let i = 0; i < contentStart; ++i)
|
|
4515
4515
|
value += lines[i][0].slice(trimIndent) + "\n";
|
|
@@ -4526,24 +4526,24 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4526
4526
|
indent = "";
|
|
4527
4527
|
}
|
|
4528
4528
|
if (type === Scalar.Scalar.BLOCK_LITERAL) {
|
|
4529
|
-
value +=
|
|
4530
|
-
|
|
4529
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
4530
|
+
sep2 = "\n";
|
|
4531
4531
|
} else if (indent.length > trimIndent || content[0] === " ") {
|
|
4532
|
-
if (
|
|
4533
|
-
|
|
4534
|
-
else if (!prevMoreIndented &&
|
|
4535
|
-
|
|
4536
|
-
value +=
|
|
4537
|
-
|
|
4532
|
+
if (sep2 === " ")
|
|
4533
|
+
sep2 = "\n";
|
|
4534
|
+
else if (!prevMoreIndented && sep2 === "\n")
|
|
4535
|
+
sep2 = "\n\n";
|
|
4536
|
+
value += sep2 + indent.slice(trimIndent) + content;
|
|
4537
|
+
sep2 = "\n";
|
|
4538
4538
|
prevMoreIndented = true;
|
|
4539
4539
|
} else if (content === "") {
|
|
4540
|
-
if (
|
|
4540
|
+
if (sep2 === "\n")
|
|
4541
4541
|
value += "\n";
|
|
4542
4542
|
else
|
|
4543
|
-
|
|
4543
|
+
sep2 = "\n";
|
|
4544
4544
|
} else {
|
|
4545
|
-
value +=
|
|
4546
|
-
|
|
4545
|
+
value += sep2 + content;
|
|
4546
|
+
sep2 = " ";
|
|
4547
4547
|
prevMoreIndented = false;
|
|
4548
4548
|
}
|
|
4549
4549
|
}
|
|
@@ -4726,25 +4726,25 @@ var require_resolve_flow_scalar = __commonJS({
|
|
|
4726
4726
|
trimBoth = /^[ \t]+|[ \t]+$/g;
|
|
4727
4727
|
}
|
|
4728
4728
|
let res = match[1].replace(trimEnd, "");
|
|
4729
|
-
let
|
|
4729
|
+
let sep2 = " ";
|
|
4730
4730
|
let pos = line.lastIndex;
|
|
4731
4731
|
while (match = line.exec(source)) {
|
|
4732
4732
|
const lm = match[1].replace(trimBoth, "");
|
|
4733
4733
|
if (lm === "") {
|
|
4734
|
-
if (
|
|
4735
|
-
res +=
|
|
4734
|
+
if (sep2 === "\n")
|
|
4735
|
+
res += sep2;
|
|
4736
4736
|
else
|
|
4737
|
-
|
|
4737
|
+
sep2 = "\n";
|
|
4738
4738
|
} else {
|
|
4739
|
-
res +=
|
|
4740
|
-
|
|
4739
|
+
res += sep2 + lm;
|
|
4740
|
+
sep2 = " ";
|
|
4741
4741
|
}
|
|
4742
4742
|
pos = line.lastIndex;
|
|
4743
4743
|
}
|
|
4744
4744
|
const last = /[ \t]*(.*)/sy;
|
|
4745
4745
|
last.lastIndex = pos;
|
|
4746
4746
|
match = last.exec(source);
|
|
4747
|
-
return res +
|
|
4747
|
+
return res + sep2 + (match?.[1] ?? "");
|
|
4748
4748
|
}
|
|
4749
4749
|
function doubleQuotedValue(source, onError) {
|
|
4750
4750
|
let res = "";
|
|
@@ -5554,14 +5554,14 @@ var require_cst_stringify = __commonJS({
|
|
|
5554
5554
|
}
|
|
5555
5555
|
}
|
|
5556
5556
|
}
|
|
5557
|
-
function stringifyItem({ start, key, sep, value }) {
|
|
5557
|
+
function stringifyItem({ start, key, sep: sep2, value }) {
|
|
5558
5558
|
let res = "";
|
|
5559
5559
|
for (const st of start)
|
|
5560
5560
|
res += st.source;
|
|
5561
5561
|
if (key)
|
|
5562
5562
|
res += stringifyToken(key);
|
|
5563
|
-
if (
|
|
5564
|
-
for (const st of
|
|
5563
|
+
if (sep2)
|
|
5564
|
+
for (const st of sep2)
|
|
5565
5565
|
res += st.source;
|
|
5566
5566
|
if (value)
|
|
5567
5567
|
res += stringifyToken(value);
|
|
@@ -5576,7 +5576,7 @@ var require_cst_visit = __commonJS({
|
|
|
5576
5576
|
"../../node_modules/.pnpm/yaml@2.9.1/node_modules/yaml/dist/parse/cst-visit.js"(exports) {
|
|
5577
5577
|
"use strict";
|
|
5578
5578
|
var BREAK = /* @__PURE__ */ Symbol("break visit");
|
|
5579
|
-
var
|
|
5579
|
+
var SKIP3 = /* @__PURE__ */ Symbol("skip children");
|
|
5580
5580
|
var REMOVE = /* @__PURE__ */ Symbol("remove item");
|
|
5581
5581
|
function visit(cst, visitor) {
|
|
5582
5582
|
if ("type" in cst && cst.type === "document")
|
|
@@ -5584,7 +5584,7 @@ var require_cst_visit = __commonJS({
|
|
|
5584
5584
|
_visit(Object.freeze([]), cst, visitor);
|
|
5585
5585
|
}
|
|
5586
5586
|
visit.BREAK = BREAK;
|
|
5587
|
-
visit.SKIP =
|
|
5587
|
+
visit.SKIP = SKIP3;
|
|
5588
5588
|
visit.REMOVE = REMOVE;
|
|
5589
5589
|
visit.itemAtPath = (cst, path) => {
|
|
5590
5590
|
let item = cst;
|
|
@@ -6728,18 +6728,18 @@ var require_parser = __commonJS({
|
|
|
6728
6728
|
if (this.type === "map-value-ind") {
|
|
6729
6729
|
const prev = getPrevProps(this.peek(2));
|
|
6730
6730
|
const start = getFirstKeyStartProps(prev);
|
|
6731
|
-
let
|
|
6731
|
+
let sep2;
|
|
6732
6732
|
if (scalar.end) {
|
|
6733
|
-
|
|
6734
|
-
|
|
6733
|
+
sep2 = scalar.end;
|
|
6734
|
+
sep2.push(this.sourceToken);
|
|
6735
6735
|
delete scalar.end;
|
|
6736
6736
|
} else
|
|
6737
|
-
|
|
6737
|
+
sep2 = [this.sourceToken];
|
|
6738
6738
|
const map = {
|
|
6739
6739
|
type: "block-map",
|
|
6740
6740
|
offset: scalar.offset,
|
|
6741
6741
|
indent: scalar.indent,
|
|
6742
|
-
items: [{ start, key: scalar, sep }]
|
|
6742
|
+
items: [{ start, key: scalar, sep: sep2 }]
|
|
6743
6743
|
};
|
|
6744
6744
|
this.onKeyLine = true;
|
|
6745
6745
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -6892,15 +6892,15 @@ var require_parser = __commonJS({
|
|
|
6892
6892
|
} else if (isFlowToken(it.key) && !includesToken(it.sep, "newline")) {
|
|
6893
6893
|
const start2 = getFirstKeyStartProps(it.start);
|
|
6894
6894
|
const key = it.key;
|
|
6895
|
-
const
|
|
6896
|
-
|
|
6895
|
+
const sep2 = it.sep;
|
|
6896
|
+
sep2.push(this.sourceToken);
|
|
6897
6897
|
delete it.key;
|
|
6898
6898
|
delete it.sep;
|
|
6899
6899
|
this.stack.push({
|
|
6900
6900
|
type: "block-map",
|
|
6901
6901
|
offset: this.offset,
|
|
6902
6902
|
indent: this.indent,
|
|
6903
|
-
items: [{ start: start2, key, sep }]
|
|
6903
|
+
items: [{ start: start2, key, sep: sep2 }]
|
|
6904
6904
|
});
|
|
6905
6905
|
} else if (start.length > 0) {
|
|
6906
6906
|
it.sep = it.sep.concat(start, this.sourceToken);
|
|
@@ -7094,13 +7094,13 @@ var require_parser = __commonJS({
|
|
|
7094
7094
|
const prev = getPrevProps(parent);
|
|
7095
7095
|
const start = getFirstKeyStartProps(prev);
|
|
7096
7096
|
fixFlowSeqItems(fc);
|
|
7097
|
-
const
|
|
7098
|
-
|
|
7097
|
+
const sep2 = fc.end.splice(1, fc.end.length);
|
|
7098
|
+
sep2.push(this.sourceToken);
|
|
7099
7099
|
const map = {
|
|
7100
7100
|
type: "block-map",
|
|
7101
7101
|
offset: fc.offset,
|
|
7102
7102
|
indent: fc.indent,
|
|
7103
|
-
items: [{ start, key: fc, sep }]
|
|
7103
|
+
items: [{ start, key: fc, sep: sep2 }]
|
|
7104
7104
|
};
|
|
7105
7105
|
this.onKeyLine = true;
|
|
7106
7106
|
this.stack[this.stack.length - 1] = map;
|
|
@@ -7378,37 +7378,317 @@ var require_dist = __commonJS({
|
|
|
7378
7378
|
}
|
|
7379
7379
|
});
|
|
7380
7380
|
|
|
7381
|
+
// ../policy/dist/discover.js
|
|
7382
|
+
function isAgentConfigPath(path) {
|
|
7383
|
+
return PROJECT_FILES.some((f) => f.re.test(path));
|
|
7384
|
+
}
|
|
7385
|
+
function agentFor(path) {
|
|
7386
|
+
const p = PROJECT_FILES.find((f) => f.re.test(path));
|
|
7387
|
+
if (p)
|
|
7388
|
+
return p.agent;
|
|
7389
|
+
const u = USER_CONFIG_FILES.find((f) => path.endsWith(f.path));
|
|
7390
|
+
return u?.agent ?? "any";
|
|
7391
|
+
}
|
|
7392
|
+
function parseJsonc(text) {
|
|
7393
|
+
try {
|
|
7394
|
+
return JSON.parse(text);
|
|
7395
|
+
} catch {
|
|
7396
|
+
}
|
|
7397
|
+
const noComments = text.replace(/("(?:\\.|[^"\\])*")|\/\/[^\n]*|\/\*[\s\S]*?\*\//g, (_m, s1) => s1 ?? "");
|
|
7398
|
+
try {
|
|
7399
|
+
return JSON.parse(noComments.replace(/,(\s*[}\]])/g, "$1"));
|
|
7400
|
+
} catch {
|
|
7401
|
+
return null;
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7404
|
+
function stdioPackage(command, args) {
|
|
7405
|
+
const bin = command.split(/[\\/]/).pop()?.replace(/\.(cmd|exe)$/i, "") ?? "";
|
|
7406
|
+
const strip = (s) => s.replace(/(.)@[^@/]+$/, "$1");
|
|
7407
|
+
if (/^(npx|bunx|pnpx)$/.test(bin) || bin === "pnpm" && args[0] === "dlx") {
|
|
7408
|
+
const pkg = args.find((a) => !a.startsWith("-") && a !== "dlx" && a !== "exec");
|
|
7409
|
+
return pkg && /^(@[\w.-]+\/)?[\w.-]+(@[\w.^~<>=*-]+)?$/.test(pkg) ? { registry: "npm", name: strip(pkg) } : void 0;
|
|
7410
|
+
}
|
|
7411
|
+
if (/^(uvx|pipx)$/.test(bin) || bin === "uv" && args[0] === "tool") {
|
|
7412
|
+
const pkg = args.find((a) => !a.startsWith("-") && !["run", "tool"].includes(a));
|
|
7413
|
+
return pkg && /^[\w.\-[\]]+(==[\w.]+)?$/.test(pkg) ? { registry: "pypi", name: pkg.toLowerCase().replace(/\[.*\]$/, "").replace(/==.*$/, "").replace(/_/g, "-") } : void 0;
|
|
7414
|
+
}
|
|
7415
|
+
if (bin === "docker") {
|
|
7416
|
+
const img = args.find((a, i) => i > 0 && !a.startsWith("-") && /^([\w.-]+\.[\w]+\/)?[\w.-]+\/[\w.-]+(:[\w.-]+)?$/.test(a) && !/^-/.test(args[i - 1] ?? ""));
|
|
7417
|
+
if (!img)
|
|
7418
|
+
return void 0;
|
|
7419
|
+
return { registry: "docker", name: img.replace(/:[\w.-]+$/, "").replace(/^(docker\.io|ghcr\.io)\//, "") };
|
|
7420
|
+
}
|
|
7421
|
+
return void 0;
|
|
7422
|
+
}
|
|
7423
|
+
function hostOf(u) {
|
|
7424
|
+
if (!u)
|
|
7425
|
+
return void 0;
|
|
7426
|
+
try {
|
|
7427
|
+
return new URL(u).host.toLowerCase();
|
|
7428
|
+
} catch {
|
|
7429
|
+
return void 0;
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
function serverComponents(servers, path, scope, agent) {
|
|
7433
|
+
if (!isObj(servers))
|
|
7434
|
+
return [];
|
|
7435
|
+
const out = [];
|
|
7436
|
+
for (const [name, raw] of Object.entries(servers).slice(0, 200)) {
|
|
7437
|
+
if (!isObj(raw))
|
|
7438
|
+
continue;
|
|
7439
|
+
const url = str(raw.url) ?? str(raw.serverUrl) ?? str(raw.httpUrl);
|
|
7440
|
+
const command = str(raw.command);
|
|
7441
|
+
const args = Array.isArray(raw.args) ? raw.args.filter((a) => typeof a === "string") : [];
|
|
7442
|
+
const envKeys = keysOf(raw.env);
|
|
7443
|
+
const headerKeys = keysOf(raw.headers);
|
|
7444
|
+
if (url)
|
|
7445
|
+
out.push({ kind: "mcp_server", name, path, scope, agent, transport: "http", urlHost: hostOf(url), envKeys, headerKeys });
|
|
7446
|
+
else if (command) {
|
|
7447
|
+
const parts = args.length ? [command, ...args] : command.split(/\s+/);
|
|
7448
|
+
const bin = parts[0];
|
|
7449
|
+
out.push({ kind: "mcp_server", name, path, scope, agent, transport: "stdio", command: bin.split(/[\\/]/).pop(), package: stdioPackage(bin, parts.slice(1)), envKeys, headerKeys });
|
|
7450
|
+
}
|
|
7451
|
+
}
|
|
7452
|
+
return out;
|
|
7453
|
+
}
|
|
7454
|
+
function claudeSettings(j, path, scope) {
|
|
7455
|
+
const out = [];
|
|
7456
|
+
const agent = "claude_code";
|
|
7457
|
+
if (isObj(j.enabledPlugins)) {
|
|
7458
|
+
for (const [id, on2] of Object.entries(j.enabledPlugins).slice(0, 200)) {
|
|
7459
|
+
if (on2 === false)
|
|
7460
|
+
continue;
|
|
7461
|
+
const [plugin, marketplace] = id.split("@");
|
|
7462
|
+
out.push({ kind: "plugin", name: plugin, path, scope, agent, envKeys: [], headerKeys: [], detail: marketplace ? `from ${marketplace}` : void 0 });
|
|
7463
|
+
}
|
|
7464
|
+
}
|
|
7465
|
+
if (isObj(j.hooks)) {
|
|
7466
|
+
for (const [event, groups] of Object.entries(j.hooks)) {
|
|
7467
|
+
if (!Array.isArray(groups))
|
|
7468
|
+
continue;
|
|
7469
|
+
for (const g of groups) {
|
|
7470
|
+
if (!isObj(g) || !Array.isArray(g.hooks))
|
|
7471
|
+
continue;
|
|
7472
|
+
for (const h of g.hooks) {
|
|
7473
|
+
if (!isObj(h))
|
|
7474
|
+
continue;
|
|
7475
|
+
const cmd2 = str(h.command);
|
|
7476
|
+
if (!cmd2)
|
|
7477
|
+
continue;
|
|
7478
|
+
const bin = cmd2.trim().split(/\s+/)[0];
|
|
7479
|
+
out.push({ kind: "hook", name: event, path, scope, agent, command: bin.split(/[\\/]/).pop(), envKeys: [], headerKeys: [], detail: str(g.matcher) ? `on ${str(g.matcher)}` : "on every tool call" });
|
|
7480
|
+
}
|
|
7481
|
+
}
|
|
7482
|
+
}
|
|
7483
|
+
}
|
|
7484
|
+
out.push(...serverComponents(j.mcpServers, path, scope, agent));
|
|
7485
|
+
return out;
|
|
7486
|
+
}
|
|
7487
|
+
function codexToml(text, path, scope) {
|
|
7488
|
+
const out = [];
|
|
7489
|
+
const blocks = text.split(/^\s*\[/m).slice(1);
|
|
7490
|
+
for (const b of blocks) {
|
|
7491
|
+
const head = b.match(/^mcp_servers\.("?)([\w.-]+)\1\]/);
|
|
7492
|
+
if (head) {
|
|
7493
|
+
const body = b.slice(b.indexOf("]") + 1);
|
|
7494
|
+
const command = body.match(/^\s*command\s*=\s*"([^"]+)"/m)?.[1];
|
|
7495
|
+
const args = [...(body.match(/^\s*args\s*=\s*\[([^\]]*)\]/m)?.[1] ?? "").matchAll(/"([^"]*)"/g)].map((m) => m[1]);
|
|
7496
|
+
const url = body.match(/^\s*url\s*=\s*"([^"]+)"/m)?.[1] ?? null;
|
|
7497
|
+
const envKeys = [...(body.match(/^\s*env\s*=\s*\{([^}]*)\}/m)?.[1] ?? "").matchAll(/([\w.-]+)\s*=/g)].map((m) => m[1]);
|
|
7498
|
+
if (url)
|
|
7499
|
+
out.push({ kind: "mcp_server", name: head[2], path, scope, agent: "codex", transport: "http", urlHost: hostOf(url), envKeys, headerKeys: [] });
|
|
7500
|
+
else if (command)
|
|
7501
|
+
out.push({ kind: "mcp_server", name: head[2], path, scope, agent: "codex", transport: "stdio", command: command.split(/[\\/]/).pop(), package: stdioPackage(command, args), envKeys, headerKeys: [] });
|
|
7502
|
+
}
|
|
7503
|
+
const envTable = b.match(/^mcp_servers\.("?)([\w.-]+)\1\.env\]/);
|
|
7504
|
+
if (envTable) {
|
|
7505
|
+
const target = out.find((c2) => c2.name === envTable[2]);
|
|
7506
|
+
if (target)
|
|
7507
|
+
target.envKeys.push(...[...b.slice(b.indexOf("]") + 1).matchAll(/^\s*([\w.-]+)\s*=/gm)].map((m) => m[1]));
|
|
7508
|
+
}
|
|
7509
|
+
}
|
|
7510
|
+
return out;
|
|
7511
|
+
}
|
|
7512
|
+
function skillName(path, text) {
|
|
7513
|
+
const fm = text.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
7514
|
+
const n = fm?.[1].match(/^name:\s*["']?([^"'\n]+)["']?\s*$/m)?.[1]?.trim();
|
|
7515
|
+
return n || path.split("/").slice(-2, -1)[0] || "skill";
|
|
7516
|
+
}
|
|
7517
|
+
function parseAgentConfig(path, text, scope = "project") {
|
|
7518
|
+
const p = path.replace(/\\/g, "/");
|
|
7519
|
+
const agent = agentFor(p);
|
|
7520
|
+
const base = p.split("/").pop() ?? p;
|
|
7521
|
+
if (/\/SKILL\.md$/i.test(p))
|
|
7522
|
+
return [{ kind: "skill", name: skillName(p, text), path: p, scope, agent: "claude_code", envKeys: [], headerKeys: [] }];
|
|
7523
|
+
if (/\.(md|mdc)$/i.test(p) || /^\.(cursorrules|windsurfrules|clinerules)$/.test(base)) {
|
|
7524
|
+
const kind = /\/\.claude\/commands\//.test(`/${p}`) ? "slash command" : /\/\.claude\/agents\//.test(`/${p}`) ? "subagent" : "instructions";
|
|
7525
|
+
return [{ kind: "instructions", name: kind === "instructions" ? base : `${kind}: ${base.replace(/\.md$/i, "")}`, path: p, scope, agent, envKeys: [], headerKeys: [], detail: `${text.length.toLocaleString("en-US")} characters` }];
|
|
7526
|
+
}
|
|
7527
|
+
if (/config\.toml$/.test(p))
|
|
7528
|
+
return codexToml(text, p, scope);
|
|
7529
|
+
const j = parseJsonc(text);
|
|
7530
|
+
if (!isObj(j))
|
|
7531
|
+
return [];
|
|
7532
|
+
if (/\.claude\/settings(\.local)?\.json$/.test(p))
|
|
7533
|
+
return claudeSettings(j, p, scope);
|
|
7534
|
+
if (/\.vscode\/mcp\.json$/.test(p))
|
|
7535
|
+
return serverComponents(j.servers, p, scope, "vscode");
|
|
7536
|
+
if (base === ".claude.json") {
|
|
7537
|
+
const out = serverComponents(j.mcpServers, p, scope, "claude_code");
|
|
7538
|
+
if (isObj(j.projects)) {
|
|
7539
|
+
for (const [proj, cfg] of Object.entries(j.projects).slice(0, 100))
|
|
7540
|
+
if (isObj(cfg))
|
|
7541
|
+
for (const c2 of serverComponents(cfg.mcpServers, p, "project", "claude_code"))
|
|
7542
|
+
out.push({ ...c2, detail: `project ${proj.split(/[\\/]/).slice(-2).join("/")}` });
|
|
7543
|
+
}
|
|
7544
|
+
return out;
|
|
7545
|
+
}
|
|
7546
|
+
return serverComponents(j.mcpServers ?? j.servers, p, scope, agent);
|
|
7547
|
+
}
|
|
7548
|
+
function classifyCredential(name) {
|
|
7549
|
+
for (const [re, c2] of CRED_RULES)
|
|
7550
|
+
if (re.test(name))
|
|
7551
|
+
return c2;
|
|
7552
|
+
return null;
|
|
7553
|
+
}
|
|
7554
|
+
function blastRadius({ component: c2, verdict, trace }) {
|
|
7555
|
+
const reasons = [];
|
|
7556
|
+
const credentials = [...c2.envKeys, ...c2.headerKeys].map((name) => ({ name, class: classifyCredential(name) })).filter((x) => !!x.class);
|
|
7557
|
+
const hosts = /* @__PURE__ */ new Set();
|
|
7558
|
+
if (c2.urlHost)
|
|
7559
|
+
hosts.add(c2.urlHost);
|
|
7560
|
+
for (const h of trace?.egressHosts ?? [])
|
|
7561
|
+
hosts.add(h);
|
|
7562
|
+
let score = 0;
|
|
7563
|
+
const local = c2.transport === "stdio" || c2.kind === "hook";
|
|
7564
|
+
if (c2.kind === "hook") {
|
|
7565
|
+
score += 2;
|
|
7566
|
+
reasons.push(`runs \`${c2.command ?? "a command"}\` ${c2.detail ?? "on agent events"}, as the developer`);
|
|
7567
|
+
} else if (local) {
|
|
7568
|
+
score += 2;
|
|
7569
|
+
reasons.push("runs on the developer's machine with their files, network and shell");
|
|
7570
|
+
}
|
|
7571
|
+
if (c2.kind === "skill" || c2.kind === "plugin") {
|
|
7572
|
+
score += 1;
|
|
7573
|
+
reasons.push("adds instructions and scripts the agent will follow");
|
|
7574
|
+
}
|
|
7575
|
+
const strong = credentials.filter((x) => x.class !== "secret");
|
|
7576
|
+
if (credentials.length) {
|
|
7577
|
+
score += strong.some((x) => ["cloud", "source_control", "payments", "database"].includes(x.class)) ? 3 : 2;
|
|
7578
|
+
reasons.unshift(`is handed ${credentials.length === 1 ? "a credential" : `${credentials.length} credentials`}: ${credentials.slice(0, 4).map((x) => x.name).join(", ")}${credentials.length > 4 ? ", \u2026" : ""}`);
|
|
7579
|
+
}
|
|
7580
|
+
if (c2.transport === "http" && c2.urlHost) {
|
|
7581
|
+
score += 1;
|
|
7582
|
+
reasons.push(`sends the agent's tool calls and their data to ${c2.urlHost}`);
|
|
7583
|
+
}
|
|
7584
|
+
if (verdict?.label === "blocked") {
|
|
7585
|
+
score += 4;
|
|
7586
|
+
reasons.unshift(`its verdict is Blocked (${verdict.score}/100)`);
|
|
7587
|
+
} else if (verdict?.label === "caution") {
|
|
7588
|
+
score += 1;
|
|
7589
|
+
reasons.push(`its verdict is Caution (${verdict.score}/100)`);
|
|
7590
|
+
}
|
|
7591
|
+
if (trace?.credentialPathsRead.length) {
|
|
7592
|
+
score += 3;
|
|
7593
|
+
reasons.unshift(`read ${trace.credentialPathsRead.slice(0, 2).join(", ")} in the honeypot`);
|
|
7594
|
+
}
|
|
7595
|
+
if (trace?.memoryWrites) {
|
|
7596
|
+
score += 2;
|
|
7597
|
+
reasons.push("wrote to the agent's memory or instruction files in the honeypot");
|
|
7598
|
+
}
|
|
7599
|
+
if (c2.kind === "instructions")
|
|
7600
|
+
reasons.push("is read by the agent as instructions on every session");
|
|
7601
|
+
if (!verdict && c2.kind !== "instructions" && c2.kind !== "hook") {
|
|
7602
|
+
score += 1;
|
|
7603
|
+
reasons.push(c2.package || c2.kind !== "mcp_server" ? "has no KillerSkills verdict yet" : "is not a registry package, so there is no verdict to check");
|
|
7604
|
+
}
|
|
7605
|
+
const level = score >= 5 ? "high" : score >= 3 ? "medium" : "low";
|
|
7606
|
+
return { level, reasons, credentials, hosts: [...hosts].slice(0, 10) };
|
|
7607
|
+
}
|
|
7608
|
+
var PROJECT_FILES, USER_CONFIG_FILES, USER_SKILL_DIRS, isObj, str, keysOf, CRED_RULES;
|
|
7609
|
+
var init_discover = __esm({
|
|
7610
|
+
"../policy/dist/discover.js"() {
|
|
7611
|
+
"use strict";
|
|
7612
|
+
PROJECT_FILES = [
|
|
7613
|
+
{ re: /(^|\/)\.mcp\.json$/, agent: "claude_code" },
|
|
7614
|
+
{ re: /(^|\/)\.claude\/settings(\.local)?\.json$/, agent: "claude_code" },
|
|
7615
|
+
{ re: /(^|\/)\.claude\/skills\/[^/]+\/SKILL\.md$/i, agent: "claude_code" },
|
|
7616
|
+
{ re: /(^|\/)\.claude\/(commands|agents)\/[^/]+\.md$/i, agent: "claude_code" },
|
|
7617
|
+
{ re: /(^|\/)\.cursor\/mcp\.json$/, agent: "cursor" },
|
|
7618
|
+
{ re: /(^|\/)\.cursor\/rules\/[^/]+\.mdc?$/, agent: "cursor" },
|
|
7619
|
+
{ re: /(^|\/)\.vscode\/mcp\.json$/, agent: "vscode" },
|
|
7620
|
+
{ re: /(^|\/)\.gemini\/settings\.json$/, agent: "gemini_cli" },
|
|
7621
|
+
{ re: /(^|\/)\.codex\/config\.toml$/, agent: "codex" },
|
|
7622
|
+
{ re: /(^|\/)\.github\/copilot-instructions\.md$/, agent: "copilot" },
|
|
7623
|
+
{ re: /(^|\/)(CLAUDE|AGENTS|GEMINI)\.md$/, agent: "any" },
|
|
7624
|
+
{ re: /(^|\/)\.(cursorrules|windsurfrules|clinerules)$/, agent: "any" }
|
|
7625
|
+
];
|
|
7626
|
+
USER_CONFIG_FILES = [
|
|
7627
|
+
{ path: ".claude.json", agent: "claude_code" },
|
|
7628
|
+
{ path: ".claude/settings.json", agent: "claude_code" },
|
|
7629
|
+
{ path: ".claude/CLAUDE.md", agent: "claude_code" },
|
|
7630
|
+
{ path: ".cursor/mcp.json", agent: "cursor" },
|
|
7631
|
+
{ path: ".codeium/windsurf/mcp_config.json", agent: "windsurf" },
|
|
7632
|
+
{ path: ".gemini/settings.json", agent: "gemini_cli" },
|
|
7633
|
+
{ path: ".codex/config.toml", agent: "codex" },
|
|
7634
|
+
{ path: "Library/Application Support/Claude/claude_desktop_config.json", agent: "claude_desktop" },
|
|
7635
|
+
{ path: ".config/Claude/claude_desktop_config.json", agent: "claude_desktop" },
|
|
7636
|
+
{ path: "AppData/Roaming/Claude/claude_desktop_config.json", agent: "claude_desktop" }
|
|
7637
|
+
];
|
|
7638
|
+
USER_SKILL_DIRS = [".claude/skills"];
|
|
7639
|
+
isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
|
|
7640
|
+
str = (v) => typeof v === "string" ? v : null;
|
|
7641
|
+
keysOf = (v) => isObj(v) ? Object.keys(v).filter((k) => /^[\w.-]{1,120}$/.test(k)).slice(0, 50) : [];
|
|
7642
|
+
CRED_RULES = [
|
|
7643
|
+
[/^(AWS_|AZURE_|ARM_|GOOGLE_|GCP_|GCLOUD|CLOUDFLARE_|DIGITALOCEAN|DO_TOKEN|VERCEL_|NETLIFY_|HEROKU_|FLY_)/i, "cloud"],
|
|
7644
|
+
[/^(GITHUB_|GH_|GITLAB_|GL_|BITBUCKET_|GIT_)/i, "source_control"],
|
|
7645
|
+
[/^(STRIPE_|PAYPAL_|BRAINTREE_|ADYEN_|SQUARE_)/i, "payments"],
|
|
7646
|
+
[/(DATABASE_URL|_DSN$|^POSTGRES|^PG|^MYSQL|^MONGO|^REDIS|^SUPABASE_|^NEON_|^PLANETSCALE|^SNOWFLAKE_|^BIGQUERY)/i, "database"],
|
|
7647
|
+
[/^(OPENAI_|ANTHROPIC_|GEMINI_|MISTRAL_|COHERE_|GROQ_|HF_|HUGGINGFACE)/i, "ai_provider"],
|
|
7648
|
+
[/^(SLACK_|DISCORD_|TWILIO_|SENDGRID_|RESEND_|MAILGUN_|POSTMARK_)/i, "messaging"],
|
|
7649
|
+
[/(TOKEN|SECRET|PASSWORD|PASSWD|API_?KEY|PRIVATE_KEY|CREDENTIAL|AUTH)/i, "secret"]
|
|
7650
|
+
];
|
|
7651
|
+
}
|
|
7652
|
+
});
|
|
7653
|
+
|
|
7381
7654
|
// ../policy/dist/index.js
|
|
7382
7655
|
var dist_exports = {};
|
|
7383
7656
|
__export(dist_exports, {
|
|
7384
7657
|
DEFAULT_POLICY: () => DEFAULT_POLICY,
|
|
7385
7658
|
FLAG_LABEL: () => FLAG_LABEL,
|
|
7386
7659
|
SOLO_POLICY: () => SOLO_POLICY,
|
|
7660
|
+
USER_CONFIG_FILES: () => USER_CONFIG_FILES,
|
|
7661
|
+
USER_SKILL_DIRS: () => USER_SKILL_DIRS,
|
|
7662
|
+
blastRadius: () => blastRadius,
|
|
7663
|
+
classifyCredential: () => classifyCredential,
|
|
7387
7664
|
evaluate: () => evaluate,
|
|
7388
7665
|
flagsFromTrace: () => flagsFromTrace,
|
|
7666
|
+
isAgentConfigPath: () => isAgentConfigPath,
|
|
7389
7667
|
labelFor: () => labelFor,
|
|
7668
|
+
parseAgentConfig: () => parseAgentConfig,
|
|
7390
7669
|
parsePolicyYaml: () => parsePolicyYaml,
|
|
7391
7670
|
serializePolicyYaml: () => serializePolicyYaml,
|
|
7392
|
-
simulate: () => simulate
|
|
7671
|
+
simulate: () => simulate,
|
|
7672
|
+
stdioPackage: () => stdioPackage
|
|
7393
7673
|
});
|
|
7394
7674
|
function parsePolicyYaml(text) {
|
|
7395
7675
|
const y = import_yaml.default.parse(text) ?? {};
|
|
7396
7676
|
const num = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
|
|
7397
|
-
const
|
|
7677
|
+
const str2 = (v, allowed, d) => allowed.includes(v) ? v : d;
|
|
7398
7678
|
const list = (v) => Array.isArray(v) ? v.map(String) : [];
|
|
7399
7679
|
const b = y.behavior ?? {};
|
|
7400
7680
|
const p = y.publishers ?? {};
|
|
7401
7681
|
const a = y.approvals ?? {};
|
|
7402
7682
|
const behavior = { ...DEFAULT_POLICY.behavior };
|
|
7403
7683
|
for (const f of FLAGS)
|
|
7404
|
-
behavior[f] =
|
|
7684
|
+
behavior[f] = str2(b[f], ["block", "approval", "warn"], DEFAULT_POLICY.behavior[f]);
|
|
7405
7685
|
return {
|
|
7406
7686
|
name: typeof y.name === "string" ? y.name : DEFAULT_POLICY.name,
|
|
7407
7687
|
version: num(y.version, 1),
|
|
7408
7688
|
minScore: Math.min(100, Math.max(0, num(y.min_score, DEFAULT_POLICY.minScore))),
|
|
7409
|
-
cautionAction:
|
|
7410
|
-
unknownAction:
|
|
7411
|
-
offlineAction:
|
|
7689
|
+
cautionAction: str2(y.caution, ["sandbox", "approval", "allow", "block"], DEFAULT_POLICY.cautionAction),
|
|
7690
|
+
unknownAction: str2(y.unknown, ["detonate", "block", "approval", "allow"], DEFAULT_POLICY.unknownAction),
|
|
7691
|
+
offlineAction: str2(y.offline, ["allow", "block", "approval"], DEFAULT_POLICY.offlineAction),
|
|
7412
7692
|
behavior,
|
|
7413
7693
|
publishers: {
|
|
7414
7694
|
allow: list(p.allow),
|
|
@@ -7572,6 +7852,7 @@ var init_dist = __esm({
|
|
|
7572
7852
|
"../policy/dist/index.js"() {
|
|
7573
7853
|
"use strict";
|
|
7574
7854
|
import_yaml = __toESM(require_dist(), 1);
|
|
7855
|
+
init_discover();
|
|
7575
7856
|
DEFAULT_POLICY = {
|
|
7576
7857
|
name: "default-strict",
|
|
7577
7858
|
version: 1,
|
|
@@ -7614,11 +7895,11 @@ var init_dist = __esm({
|
|
|
7614
7895
|
init_dist();
|
|
7615
7896
|
import { parseArgs } from "node:util";
|
|
7616
7897
|
import { spawnSync } from "node:child_process";
|
|
7617
|
-
import { readFileSync as
|
|
7618
|
-
import { resolve, join as
|
|
7898
|
+
import { readFileSync as readFileSync6, existsSync as existsSync7 } from "node:fs";
|
|
7899
|
+
import { resolve as resolve2, join as join7, dirname, basename } from "node:path";
|
|
7619
7900
|
import { hostname, platform as platform2, userInfo } from "node:os";
|
|
7620
|
-
import { createHash } from "node:crypto";
|
|
7621
|
-
import { createInterface } from "node:readline";
|
|
7901
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
7902
|
+
import { createInterface as createInterface2 } from "node:readline";
|
|
7622
7903
|
|
|
7623
7904
|
// src/check.ts
|
|
7624
7905
|
init_dist();
|
|
@@ -7629,7 +7910,7 @@ import { mkdirSync, readFileSync, writeFileSync, existsSync, appendFileSync, chm
|
|
|
7629
7910
|
import { homedir } from "node:os";
|
|
7630
7911
|
import { join } from "node:path";
|
|
7631
7912
|
import { randomUUID } from "node:crypto";
|
|
7632
|
-
var VERSION = "0.
|
|
7913
|
+
var VERSION = "0.7.0";
|
|
7633
7914
|
var HOME = process.env.VET_SKILLS_HOME ?? join(homedir(), ".vet-skills");
|
|
7634
7915
|
var API = (process.env.KILLERSKILLS_API ?? "https://killerskills.ai").replace(/\/$/, "");
|
|
7635
7916
|
var CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -7805,6 +8086,11 @@ function report(body) {
|
|
|
7805
8086
|
if (!org) return Promise.resolve({ ok: false, error: "not joined", offline: false });
|
|
7806
8087
|
return call(`/api/v1/orgs/${org.slug}/agents/${org.agentId}/report`, { method: "POST", body: JSON.stringify(body), headers: orgHeaders() }, 8e3);
|
|
7807
8088
|
}
|
|
8089
|
+
function runtimeReport(body) {
|
|
8090
|
+
const org = readOrg();
|
|
8091
|
+
if (!org) return Promise.resolve({ ok: false, error: "not joined", offline: false });
|
|
8092
|
+
return call(`/api/v1/orgs/${org.slug}/agents/${org.agentId}/runtime`, { method: "POST", body: JSON.stringify(body), headers: orgHeaders() }, 6e3);
|
|
8093
|
+
}
|
|
7808
8094
|
function confirmRemoval(removalId, body) {
|
|
7809
8095
|
const org = readOrg();
|
|
7810
8096
|
if (!org) return Promise.resolve({ ok: false, error: "not joined", offline: false });
|
|
@@ -7822,6 +8108,11 @@ function detonate(tgz, opts = {}) {
|
|
|
7822
8108
|
function sendTelemetry(body) {
|
|
7823
8109
|
return call("/api/v1/telemetry", { method: "POST", body: JSON.stringify(body) }, 1500);
|
|
7824
8110
|
}
|
|
8111
|
+
function discoverReport(components) {
|
|
8112
|
+
const org = readOrg();
|
|
8113
|
+
if (!org) return Promise.resolve({ ok: false, error: "not joined", offline: false });
|
|
8114
|
+
return call(`/api/v1/orgs/${org.slug}/agents/${org.agentId}/discover`, { method: "POST", body: JSON.stringify({ components }), headers: orgHeaders() }, 15e3);
|
|
8115
|
+
}
|
|
7825
8116
|
|
|
7826
8117
|
// src/check.ts
|
|
7827
8118
|
import { platform } from "node:os";
|
|
@@ -7864,6 +8155,151 @@ function fmtInstalls(n) {
|
|
|
7864
8155
|
return n.toLocaleString("en-US");
|
|
7865
8156
|
}
|
|
7866
8157
|
|
|
8158
|
+
// src/attest.ts
|
|
8159
|
+
import { createHash, createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
8160
|
+
import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8161
|
+
import { homedir as homedir2 } from "node:os";
|
|
8162
|
+
import { join as join2, relative, resolve, sep } from "node:path";
|
|
8163
|
+
var LOCKFILE = "vet-skills.lock";
|
|
8164
|
+
var PAYLOAD_TYPE = "application/vnd.killerskills.verdict+json";
|
|
8165
|
+
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git", ".pglite", ".next", "__pycache__", ".venv"]);
|
|
8166
|
+
function bundleHash(root) {
|
|
8167
|
+
const h = createHash("sha256");
|
|
8168
|
+
const walk2 = (dir) => {
|
|
8169
|
+
for (const name of readdirSync(dir).sort()) {
|
|
8170
|
+
if (SKIP.has(name)) continue;
|
|
8171
|
+
const p = join2(dir, name);
|
|
8172
|
+
const st = statSync2(p);
|
|
8173
|
+
if (st.isDirectory()) walk2(p);
|
|
8174
|
+
else if (st.isFile() && st.size <= 2 * 1024 * 1024) {
|
|
8175
|
+
h.update(relative(root, p).split(sep).join("/"));
|
|
8176
|
+
h.update("\0");
|
|
8177
|
+
h.update(readFileSync2(p));
|
|
8178
|
+
h.update("\0");
|
|
8179
|
+
}
|
|
8180
|
+
}
|
|
8181
|
+
};
|
|
8182
|
+
walk2(root);
|
|
8183
|
+
return h.digest("hex");
|
|
8184
|
+
}
|
|
8185
|
+
function pae(payloadType, payload) {
|
|
8186
|
+
const t = Buffer.from(payloadType);
|
|
8187
|
+
return Buffer.concat([Buffer.from(`DSSEv1 ${t.length} `), t, Buffer.from(` ${payload.length} `), payload]);
|
|
8188
|
+
}
|
|
8189
|
+
function verifyEnvelope(env, keys) {
|
|
8190
|
+
if (env.payloadType !== PAYLOAD_TYPE) return { ok: false, statement: null, kid: null, reason: `unexpected payload type ${env.payloadType}` };
|
|
8191
|
+
const payload = Buffer.from(env.payload, "base64");
|
|
8192
|
+
const trusted = (process.env.VET_SKILLS_TRUSTED_KEYS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
8193
|
+
for (const sig of env.signatures) {
|
|
8194
|
+
const k = keys.find((x) => x.kid === sig.keyid);
|
|
8195
|
+
if (!k) continue;
|
|
8196
|
+
if (trusted.length && !trusted.includes(k.kid)) return { ok: false, statement: null, kid: k.kid, reason: `key ${k.kid} is not in VET_SKILLS_TRUSTED_KEYS` };
|
|
8197
|
+
if (k.dev) return { ok: false, statement: null, kid: k.kid, reason: "signed with a development key" };
|
|
8198
|
+
try {
|
|
8199
|
+
const pub = createPublicKey({ key: { kty: k.kty, crv: k.crv, x: k.x }, format: "jwk" });
|
|
8200
|
+
if (cryptoVerify(null, pae(env.payloadType, payload), pub, Buffer.from(sig.sig, "base64"))) return { ok: true, statement: JSON.parse(payload.toString("utf8")), kid: k.kid, reason: "signature valid" };
|
|
8201
|
+
return { ok: false, statement: null, kid: k.kid, reason: "signature does not verify" };
|
|
8202
|
+
} catch (e) {
|
|
8203
|
+
return { ok: false, statement: null, kid: k.kid, reason: e.message };
|
|
8204
|
+
}
|
|
8205
|
+
}
|
|
8206
|
+
return { ok: false, statement: null, kid: env.signatures[0]?.keyid ?? null, reason: "no known key for this signature" };
|
|
8207
|
+
}
|
|
8208
|
+
async function getJson(url) {
|
|
8209
|
+
try {
|
|
8210
|
+
const r = await fetch(url, { headers: { accept: "application/json", "user-agent": `vet-skills/${VERSION}` }, signal: AbortSignal.timeout(8e3) });
|
|
8211
|
+
return r.ok ? await r.json() : null;
|
|
8212
|
+
} catch {
|
|
8213
|
+
return null;
|
|
8214
|
+
}
|
|
8215
|
+
}
|
|
8216
|
+
async function fetchKeys() {
|
|
8217
|
+
const r = await getJson(`${API}/.well-known/killerskills-keys.json`);
|
|
8218
|
+
return r?.keys ?? [];
|
|
8219
|
+
}
|
|
8220
|
+
async function fetchAttestationByHash(hash) {
|
|
8221
|
+
return getJson(`${API}/api/v1/verdicts/${hash}/attestation`);
|
|
8222
|
+
}
|
|
8223
|
+
function installedSkills() {
|
|
8224
|
+
const out = [];
|
|
8225
|
+
for (const base of [join2(process.cwd(), ".claude", "skills"), join2(homedir2(), ".claude", "skills"), join2(homedir2(), ".claude", "plugins")]) {
|
|
8226
|
+
if (!existsSync2(base)) continue;
|
|
8227
|
+
for (const name of readdirSync(base).sort()) {
|
|
8228
|
+
const p = join2(base, name);
|
|
8229
|
+
if (statSync2(p).isDirectory() && !out.some((x) => x.name === name)) out.push({ name, path: p });
|
|
8230
|
+
}
|
|
8231
|
+
}
|
|
8232
|
+
return out;
|
|
8233
|
+
}
|
|
8234
|
+
async function verifyOne(target, keys) {
|
|
8235
|
+
const dir = resolve(target);
|
|
8236
|
+
let hash;
|
|
8237
|
+
let subject = target;
|
|
8238
|
+
let page = null;
|
|
8239
|
+
if (existsSync2(dir) && statSync2(dir).isDirectory()) {
|
|
8240
|
+
hash = bundleHash(dir);
|
|
8241
|
+
subject = `${target} (${hash.slice(0, 12)}\u2026)`;
|
|
8242
|
+
} else {
|
|
8243
|
+
const [name, ver] = target.split("@").length > 2 ? [target.slice(0, target.lastIndexOf("@")), target.slice(target.lastIndexOf("@") + 1)] : target.includes("@") && !target.startsWith("@") ? target.split("@") : [target, void 0];
|
|
8244
|
+
const r = await resolveSkill(name);
|
|
8245
|
+
if (!r.ok) return { subject, hash: "", ok: false, detail: r.offline ? "offline" : `not found: ${r.error}`, statement: null };
|
|
8246
|
+
if (ver && r.data.version.semver !== ver) return { subject, hash: "", ok: false, detail: `latest scored version is ${r.data.version.semver}; verify a local checkout of ${ver} with a path instead`, statement: null };
|
|
8247
|
+
hash = r.data.version.artifact_hash;
|
|
8248
|
+
page = r.data.skill.page;
|
|
8249
|
+
subject = `${r.data.skill.name}@${r.data.version.semver}`;
|
|
8250
|
+
const local = installedSkills().find((s) => s.name === r.data.skill.name);
|
|
8251
|
+
if (local) {
|
|
8252
|
+
const lh = bundleHash(local.path);
|
|
8253
|
+
if (lh !== hash) return { subject, hash: lh, ok: false, detail: `installed copy at ${local.path} hashes to ${lh.slice(0, 12)}\u2026, attested version is ${hash.slice(0, 12)}\u2026`, statement: null };
|
|
8254
|
+
}
|
|
8255
|
+
}
|
|
8256
|
+
const env = await fetchAttestationByHash(hash);
|
|
8257
|
+
if (!env) return { subject, hash, ok: false, detail: `no attestation for ${hash.slice(0, 12)}\u2026 (unscored, demo or signing not configured)${page ? ` \xB7 ${API}${page}` : ""}`, statement: null };
|
|
8258
|
+
const v = verifyEnvelope(env, keys);
|
|
8259
|
+
if (!v.ok || !v.statement) return { subject, hash, ok: false, detail: v.reason, statement: null };
|
|
8260
|
+
if (v.statement.subject.artifact_hash !== hash) return { subject, hash, ok: false, detail: "attestation is for a different artifact", statement: v.statement };
|
|
8261
|
+
return { subject, hash, ok: true, detail: `${v.statement.predicate.label} ${v.statement.predicate.score} \xB7 engine ${v.statement.predicate.engine_version} \xB7 signed by ${v.kid}${v.statement.predicate.superseded ? " \xB7 superseded" : ""}`, statement: v.statement };
|
|
8262
|
+
}
|
|
8263
|
+
async function writeLock(dir = process.cwd()) {
|
|
8264
|
+
const keys = await fetchKeys();
|
|
8265
|
+
const skills = [];
|
|
8266
|
+
const missing = [];
|
|
8267
|
+
for (const s of installedSkills()) {
|
|
8268
|
+
const hash = bundleHash(s.path);
|
|
8269
|
+
const env = await fetchAttestationByHash(hash);
|
|
8270
|
+
const v = env ? verifyEnvelope(env, keys) : null;
|
|
8271
|
+
if (!v?.ok || !v.statement) missing.push(s.name);
|
|
8272
|
+
skills.push({ name: s.name, path: s.path, artifact_hash: hash, verdict: v?.statement ? { id: v.statement.predicate.verdict_id, label: v.statement.predicate.label, score: v.statement.predicate.score, engine_version: v.statement.predicate.engine_version } : null, attestation: v?.ok ? env : null });
|
|
8273
|
+
}
|
|
8274
|
+
const lock = { version: 1, generated_at: (/* @__PURE__ */ new Date()).toISOString(), generated_by: `vet-skills ${VERSION}`, issuer: API, keys: keys.filter((k) => !k.dev), skills };
|
|
8275
|
+
const file = join2(dir, LOCKFILE);
|
|
8276
|
+
writeFileSync2(file, `${JSON.stringify(lock, null, 2)}
|
|
8277
|
+
`);
|
|
8278
|
+
return { file, lock, missing };
|
|
8279
|
+
}
|
|
8280
|
+
function readLock(dir = process.cwd()) {
|
|
8281
|
+
const file = join2(dir, LOCKFILE);
|
|
8282
|
+
if (!existsSync2(file)) return null;
|
|
8283
|
+
try {
|
|
8284
|
+
return JSON.parse(readFileSync2(file, "utf8"));
|
|
8285
|
+
} catch {
|
|
8286
|
+
return null;
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
function verifyLock(lock) {
|
|
8290
|
+
return lock.skills.map((e) => {
|
|
8291
|
+
const path = existsSync2(e.path) ? e.path : installedSkills().find((s) => s.name === e.name)?.path;
|
|
8292
|
+
if (!path) return { subject: e.name, hash: e.artifact_hash, ok: false, detail: "not installed", statement: null };
|
|
8293
|
+
const hash = bundleHash(path);
|
|
8294
|
+
if (hash !== e.artifact_hash) return { subject: e.name, hash, ok: false, detail: `changed on disk: ${hash.slice(0, 12)}\u2026 (locked ${e.artifact_hash.slice(0, 12)}\u2026)`, statement: null };
|
|
8295
|
+
if (!e.attestation) return { subject: e.name, hash, ok: true, detail: "hash matches \xB7 pinned by hash only (no attestation when locked)", statement: null };
|
|
8296
|
+
const v = verifyEnvelope(e.attestation, lock.keys);
|
|
8297
|
+
if (!v.ok || !v.statement) return { subject: e.name, hash, ok: false, detail: v.reason, statement: null };
|
|
8298
|
+
if (v.statement.subject.artifact_hash !== hash) return { subject: e.name, hash, ok: false, detail: "attestation is for a different artifact", statement: v.statement };
|
|
8299
|
+
return { subject: e.name, hash, ok: true, detail: `${v.statement.predicate.label} ${v.statement.predicate.score} \xB7 engine ${v.statement.predicate.engine_version} \xB7 signed by ${v.kid}`, statement: v.statement };
|
|
8300
|
+
});
|
|
8301
|
+
}
|
|
8302
|
+
|
|
7867
8303
|
// src/check.ts
|
|
7868
8304
|
function toVerdictInput(r) {
|
|
7869
8305
|
if (!r.verdict) return null;
|
|
@@ -7945,6 +8381,12 @@ async function check(q, opts = {}) {
|
|
|
7945
8381
|
if (telemetryNoticePending()) process.stderr.write(c.muted("vet-skills sends anonymous usage (skill name, version, decision, agent type; never code, prompts or paths). Opt out: vet-skills telemetry off") + "\n");
|
|
7946
8382
|
await sendTelemetry({ skill: resolved?.skill.name ?? q, semver: resolved?.version.semver ?? null, registry: resolved?.skill.registry ?? null, decision: decision.decision, would_be: decision.wouldBe ?? null, agent_kind: opts.agentKind ?? "other", hook_version: VERSION, os: platform(), offline }).catch(() => null);
|
|
7947
8383
|
}
|
|
8384
|
+
const lock = readLock();
|
|
8385
|
+
const pinned = lock && resolved ? lock.skills.find((e) => e.name === resolved.skill.name) : null;
|
|
8386
|
+
if (pinned && resolved && pinned.artifact_hash !== resolved.version.artifact_hash) {
|
|
8387
|
+
const why = `vet-skills.lock pins ${resolved.skill.name} to ${pinned.artifact_hash.slice(0, 12)}\u2026; registry has ${resolved.version.artifact_hash.slice(0, 12)}\u2026 (${resolved.version.semver}). Run \`vet-skills lock\` to accept it.`;
|
|
8388
|
+
return { resolved, decision: { ...decision, decision: "block", wouldBe: "block", reason: why }, source, cacheAge, remote, offline, policyLabel };
|
|
8389
|
+
}
|
|
7948
8390
|
return { resolved, decision, source, cacheAge, remote, offline, policyLabel };
|
|
7949
8391
|
}
|
|
7950
8392
|
function renderCheck(r) {
|
|
@@ -8027,18 +8469,183 @@ function restrictionText(r) {
|
|
|
8027
8469
|
return parts.join(" \xB7 ") || "no restrictions";
|
|
8028
8470
|
}
|
|
8029
8471
|
|
|
8030
|
-
// src/
|
|
8031
|
-
|
|
8472
|
+
// src/discover.ts
|
|
8473
|
+
init_dist();
|
|
8474
|
+
import { existsSync as existsSync3, readdirSync as readdirSync2, readFileSync as readFileSync3, statSync as statSync3 } from "node:fs";
|
|
8032
8475
|
import { homedir as homedir3 } from "node:os";
|
|
8033
8476
|
import { join as join3 } from "node:path";
|
|
8477
|
+
var MAX_BYTES = 2 * 1024 * 1024;
|
|
8478
|
+
function read(path) {
|
|
8479
|
+
try {
|
|
8480
|
+
const st = statSync3(path);
|
|
8481
|
+
if (!st.isFile() || st.size > MAX_BYTES) return null;
|
|
8482
|
+
return readFileSync3(path, "utf8");
|
|
8483
|
+
} catch {
|
|
8484
|
+
return null;
|
|
8485
|
+
}
|
|
8486
|
+
}
|
|
8487
|
+
function discoverLocal(opts = {}) {
|
|
8488
|
+
const home = opts.home ?? homedir3();
|
|
8489
|
+
const out = [];
|
|
8490
|
+
for (const f of USER_CONFIG_FILES) {
|
|
8491
|
+
const text = read(join3(home, f.path));
|
|
8492
|
+
if (text !== null) out.push(...parseAgentConfig(f.path, text, "user"));
|
|
8493
|
+
}
|
|
8494
|
+
for (const dir of USER_SKILL_DIRS) {
|
|
8495
|
+
const abs = join3(home, dir);
|
|
8496
|
+
if (!existsSync3(abs)) continue;
|
|
8497
|
+
for (const name of safeList(abs)) {
|
|
8498
|
+
const rel = `${dir}/${name}/SKILL.md`;
|
|
8499
|
+
const text = read(join3(home, rel));
|
|
8500
|
+
if (text !== null) out.push(...parseAgentConfig(rel, text, "user"));
|
|
8501
|
+
}
|
|
8502
|
+
}
|
|
8503
|
+
if (opts.project) for (const rel of walk(opts.project, "", 0)) {
|
|
8504
|
+
const text = read(join3(opts.project, rel));
|
|
8505
|
+
if (text !== null) out.push(...parseAgentConfig(rel, text, "project"));
|
|
8506
|
+
}
|
|
8507
|
+
const seen = /* @__PURE__ */ new Set();
|
|
8508
|
+
return out.filter((c2) => {
|
|
8509
|
+
const k = `${c2.kind}|${c2.name}|${c2.path}`;
|
|
8510
|
+
if (seen.has(k)) return false;
|
|
8511
|
+
seen.add(k);
|
|
8512
|
+
return true;
|
|
8513
|
+
});
|
|
8514
|
+
}
|
|
8515
|
+
function safeList(dir) {
|
|
8516
|
+
try {
|
|
8517
|
+
return readdirSync2(dir);
|
|
8518
|
+
} catch {
|
|
8519
|
+
return [];
|
|
8520
|
+
}
|
|
8521
|
+
}
|
|
8522
|
+
var SKIP2 = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", "vendor", "target", ".venv", "venv"]);
|
|
8523
|
+
function walk(root, rel, depth) {
|
|
8524
|
+
if (depth > 6) return [];
|
|
8525
|
+
const out = [];
|
|
8526
|
+
for (const name of safeList(join3(root, rel))) {
|
|
8527
|
+
if (SKIP2.has(name)) continue;
|
|
8528
|
+
const r = rel ? `${rel}/${name}` : name;
|
|
8529
|
+
let st;
|
|
8530
|
+
try {
|
|
8531
|
+
st = statSync3(join3(root, r));
|
|
8532
|
+
} catch {
|
|
8533
|
+
continue;
|
|
8534
|
+
}
|
|
8535
|
+
if (st.isDirectory()) out.push(...walk(root, r, depth + 1));
|
|
8536
|
+
else if (isAgentConfigPath(r)) out.push(r);
|
|
8537
|
+
if (out.length > 200) break;
|
|
8538
|
+
}
|
|
8539
|
+
return out;
|
|
8540
|
+
}
|
|
8541
|
+
|
|
8542
|
+
// src/mcp.ts
|
|
8543
|
+
import { createInterface } from "node:readline";
|
|
8544
|
+
var TOOLS = [
|
|
8545
|
+
{
|
|
8546
|
+
name: "check_skill",
|
|
8547
|
+
title: "Check a skill or MCP server",
|
|
8548
|
+
description: 'Look up the KillerSkills security verdict for an agent skill, MCP server or plugin BEFORE installing it. Returns the score (0-100), label (verified / caution / blocked), what the sandbox saw it do (network hosts contacted, credential files read, memory writes), the top findings, and the last safe version when the latest is blocked. Use the package name for npm/PyPI MCP servers (e.g. "@modelcontextprotocol/server-github" with registry "npm").',
|
|
8549
|
+
inputSchema: {
|
|
8550
|
+
type: "object",
|
|
8551
|
+
properties: {
|
|
8552
|
+
name: { type: "string", description: 'Skill or package name: "name", "publisher/name", "@scope/pkg", optionally "@version".' },
|
|
8553
|
+
registry: { type: "string", enum: ["skills_sh", "clawhub", "npm", "pypi", "docker", "plugins", "partner"], description: "Registry, when known. Omit to search all public registries." }
|
|
8554
|
+
},
|
|
8555
|
+
required: ["name"]
|
|
8556
|
+
},
|
|
8557
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
8558
|
+
},
|
|
8559
|
+
{
|
|
8560
|
+
name: "search_skills",
|
|
8561
|
+
title: "Search vetted skills",
|
|
8562
|
+
description: "Search the KillerSkills index (every public agent skill and MCP server, each detonated in a sandbox) by keyword. Returns the most-installed matches with their verdicts, so you can pick a verified option instead of an unvetted one.",
|
|
8563
|
+
inputSchema: { type: "object", properties: { query: { type: "string", description: 'Keywords, e.g. "postgres", "github mcp", "pdf".' } }, required: ["query"] },
|
|
8564
|
+
annotations: { readOnlyHint: true, openWorldHint: true }
|
|
8565
|
+
}
|
|
8566
|
+
];
|
|
8567
|
+
async function checkSkill(name, registry) {
|
|
8568
|
+
const r = await resolveSkill(name, registry);
|
|
8569
|
+
if (!r.ok) return r.status === 404 ? `No skill or MCP server named "${name}" is indexed by KillerSkills${registry ? ` in ${registry}` : ""}. Treat it as unvetted.` : `KillerSkills lookup failed (${r.error}). Treat "${name}" as unvetted.`;
|
|
8570
|
+
const d = r.data;
|
|
8571
|
+
const page = `${API}${d.skill.page}`;
|
|
8572
|
+
if (!d.verdict) return `${d.skill.name} (${d.skill.registry_label}, publisher ${d.publisher.handle}) is indexed but not scored yet: unvetted. ${page}`;
|
|
8573
|
+
const lines = [
|
|
8574
|
+
`${d.skill.name}@${d.version.semver} (${d.skill.registry_label}, publisher ${d.publisher.handle}${d.publisher.verified ? ", verified publisher" : ""})`,
|
|
8575
|
+
`Verdict: ${d.verdict.label.toUpperCase()} \xB7 score ${d.verdict.score}/100 \xB7 ${d.verdict.scenarios_run} sandbox scenarios`
|
|
8576
|
+
];
|
|
8577
|
+
const t = d.trace_summary;
|
|
8578
|
+
if (t) {
|
|
8579
|
+
const undeclared = t.egress_hosts.filter((h) => !h.declared).map((h) => h.host);
|
|
8580
|
+
lines.push(`Sandbox: contacted ${t.egress_hosts.length} host(s)${undeclared.length ? ` (undeclared: ${undeclared.slice(0, 5).join(", ")})` : ""}; read ${t.files_read.length} file(s); ${t.memory_writes.length} memory write(s)${t.tool_shadowing.length ? `; shadows tools: ${t.tool_shadowing.map((x) => x.tool).join(", ")}` : ""}.`);
|
|
8581
|
+
}
|
|
8582
|
+
for (const f of d.findings.slice(0, 5)) lines.push(`- [${f.severity}] ${f.title}`);
|
|
8583
|
+
if (d.verdict.label === "blocked" && d.last_safe_version) lines.push(`Last safe version: ${d.last_safe_version.semver} (score ${d.last_safe_version.score}).`);
|
|
8584
|
+
lines.push(d.verdict.label === "verified" ? "OK to install." : d.verdict.label === "caution" ? "Install only if the findings above are acceptable; tell the user what they are." : "Do not install. Tell the user why.");
|
|
8585
|
+
lines.push(`Evidence: ${page}`);
|
|
8586
|
+
return lines.join("\n");
|
|
8587
|
+
}
|
|
8588
|
+
async function searchSkills(query) {
|
|
8589
|
+
try {
|
|
8590
|
+
const res = await fetch(`${API}/api/v1/leaderboard?q=${encodeURIComponent(query)}`, { headers: { accept: "application/json", "user-agent": `vet-skills/${VERSION} mcp` }, signal: AbortSignal.timeout(1e4) });
|
|
8591
|
+
if (!res.ok) return `Search failed (HTTP ${res.status}).`;
|
|
8592
|
+
const d = await res.json();
|
|
8593
|
+
if (!d.rows.length) return `Nothing in the KillerSkills index matches "${query}".`;
|
|
8594
|
+
return [`${d.total} match(es) for "${query}", most installed first:`, ...d.rows.slice(0, 15).map((x) => `- ${x.skill.name} (${x.skill.registry}, ${x.skill.publisher}) \xB7 ${x.latest_verdict.label} ${x.latest_verdict.score} \xB7 ${x.install_count.toLocaleString()} installs \xB7 ${API}${x.skill.page}`)].join("\n");
|
|
8595
|
+
} catch (e) {
|
|
8596
|
+
return `Search failed (${e.message}).`;
|
|
8597
|
+
}
|
|
8598
|
+
}
|
|
8599
|
+
async function serveMcp() {
|
|
8600
|
+
const send = (msg) => process.stdout.write(JSON.stringify(msg) + "\n");
|
|
8601
|
+
const rl = createInterface({ input: process.stdin });
|
|
8602
|
+
for await (const line of rl) {
|
|
8603
|
+
if (!line.trim()) continue;
|
|
8604
|
+
let req;
|
|
8605
|
+
try {
|
|
8606
|
+
req = JSON.parse(line);
|
|
8607
|
+
} catch {
|
|
8608
|
+
send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
|
|
8609
|
+
continue;
|
|
8610
|
+
}
|
|
8611
|
+
if (req.id === void 0) continue;
|
|
8612
|
+
const ok = (result) => send({ jsonrpc: "2.0", id: req.id, result });
|
|
8613
|
+
switch (req.method) {
|
|
8614
|
+
case "initialize":
|
|
8615
|
+
ok({ protocolVersion: req.params?.protocolVersion ?? "2025-06-18", capabilities: { tools: {} }, serverInfo: { name: "killerskills", title: "KillerSkills", version: VERSION }, instructions: "Before installing any agent skill, plugin or MCP server, call check_skill. Do not install anything labelled blocked." });
|
|
8616
|
+
break;
|
|
8617
|
+
case "ping":
|
|
8618
|
+
ok({});
|
|
8619
|
+
break;
|
|
8620
|
+
case "tools/list":
|
|
8621
|
+
ok({ tools: TOOLS });
|
|
8622
|
+
break;
|
|
8623
|
+
case "tools/call": {
|
|
8624
|
+
const name = req.params?.name;
|
|
8625
|
+
const args = req.params?.arguments ?? {};
|
|
8626
|
+
const text = name === "check_skill" ? await checkSkill(String(args.name ?? ""), args.registry || void 0) : name === "search_skills" ? await searchSkills(String(args.query ?? "")) : null;
|
|
8627
|
+
if (text === null) send({ jsonrpc: "2.0", id: req.id, error: { code: -32602, message: `unknown tool ${name}` } });
|
|
8628
|
+
else ok({ content: [{ type: "text", text }], isError: false });
|
|
8629
|
+
break;
|
|
8630
|
+
}
|
|
8631
|
+
default:
|
|
8632
|
+
send({ jsonrpc: "2.0", id: req.id, error: { code: -32601, message: `method not found: ${req.method}` } });
|
|
8633
|
+
}
|
|
8634
|
+
}
|
|
8635
|
+
}
|
|
8636
|
+
|
|
8637
|
+
// src/hook.ts
|
|
8638
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8639
|
+
import { homedir as homedir5 } from "node:os";
|
|
8640
|
+
import { join as join5 } from "node:path";
|
|
8034
8641
|
|
|
8035
8642
|
// src/sync.ts
|
|
8036
|
-
import { existsSync as
|
|
8037
|
-
import { homedir as
|
|
8038
|
-
import { join as
|
|
8643
|
+
import { existsSync as existsSync4, rmSync } from "node:fs";
|
|
8644
|
+
import { homedir as homedir4 } from "node:os";
|
|
8645
|
+
import { join as join4 } from "node:path";
|
|
8039
8646
|
function skillDirs(name) {
|
|
8040
8647
|
const bare = name.replace(/^.*\//, "").replace(/@.*$/, "");
|
|
8041
|
-
return [
|
|
8648
|
+
return [join4(homedir4(), ".claude", "skills", bare), join4(process.cwd(), ".claude", "skills", bare), join4(homedir4(), ".claude", "plugins", bare)];
|
|
8042
8649
|
}
|
|
8043
8650
|
async function applyRemovals(pending, log = () => {
|
|
8044
8651
|
}) {
|
|
@@ -8049,7 +8656,7 @@ async function applyRemovals(pending, log = () => {
|
|
|
8049
8656
|
const detail = [];
|
|
8050
8657
|
let failed = false;
|
|
8051
8658
|
for (const it of items) {
|
|
8052
|
-
const dirs = skillDirs(it.skill).filter((d) =>
|
|
8659
|
+
const dirs = skillDirs(it.skill).filter((d) => existsSync4(d));
|
|
8053
8660
|
if (!dirs.length) {
|
|
8054
8661
|
detail.push(`${it.skill}: not on disk`);
|
|
8055
8662
|
continue;
|
|
@@ -8200,24 +8807,201 @@ ${out}
|
|
|
8200
8807
|
return 0;
|
|
8201
8808
|
}
|
|
8202
8809
|
function installClaudeCodeHook() {
|
|
8203
|
-
const dir =
|
|
8204
|
-
const path =
|
|
8205
|
-
if (!
|
|
8810
|
+
const dir = join5(homedir5(), ".claude");
|
|
8811
|
+
const path = join5(dir, "settings.json");
|
|
8812
|
+
if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
|
|
8206
8813
|
let settings = {};
|
|
8207
|
-
if (
|
|
8814
|
+
if (existsSync5(path)) {
|
|
8208
8815
|
try {
|
|
8209
|
-
settings = JSON.parse(
|
|
8816
|
+
settings = JSON.parse(readFileSync4(path, "utf8"));
|
|
8210
8817
|
} catch {
|
|
8211
8818
|
settings = {};
|
|
8212
8819
|
}
|
|
8213
8820
|
}
|
|
8214
8821
|
const hooks = settings.hooks ??= {};
|
|
8215
8822
|
const pre = hooks.PreToolUse ??= [];
|
|
8216
|
-
const
|
|
8217
|
-
|
|
8218
|
-
pre.
|
|
8219
|
-
|
|
8220
|
-
|
|
8823
|
+
const post = hooks.PostToolUse ??= [];
|
|
8824
|
+
let changed = false;
|
|
8825
|
+
if (!pre.some((h) => h.hooks?.some((x) => /vet-skills hook eval/.test(x.command)))) {
|
|
8826
|
+
pre.push({ matcher: "Bash", hooks: [{ type: "command", command: "vet-skills hook eval" }] });
|
|
8827
|
+
changed = true;
|
|
8828
|
+
}
|
|
8829
|
+
if (!post.some((h) => h.hooks?.some((x) => /vet-skills hook observe/.test(x.command)))) {
|
|
8830
|
+
post.push({ matcher: "Bash|Read|Write|Edit|MultiEdit|NotebookEdit|WebFetch|Skill|mcp__.*", hooks: [{ type: "command", command: "vet-skills hook observe" }] });
|
|
8831
|
+
changed = true;
|
|
8832
|
+
}
|
|
8833
|
+
if (changed) writeFileSync3(path, JSON.stringify(settings, null, 2) + "\n");
|
|
8834
|
+
return { path, changed };
|
|
8835
|
+
}
|
|
8836
|
+
|
|
8837
|
+
// src/runtime.ts
|
|
8838
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync5, readdirSync as readdirSync3, statSync as statSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8839
|
+
import { homedir as homedir6 } from "node:os";
|
|
8840
|
+
import { join as join6 } from "node:path";
|
|
8841
|
+
var ACTIVE_MS = 30 * 6e4;
|
|
8842
|
+
var CRED = /(^|\/)(\.ssh|\.aws|\.npmrc|\.netrc|\.config\/gh|\.docker\/config\.json|\.kube\/config|\.gnupg|credentials|\.env(\.|$)|id_(rsa|ed25519|ecdsa)|keychain)/i;
|
|
8843
|
+
var MEMORY = /(^|\/)(MEMORY\.md|CLAUDE\.md|AGENTS\.md|\.cursorrules|\.clinerules|\.windsurfrules|GEMINI\.md|\.github\/copilot-instructions\.md)$/i;
|
|
8844
|
+
var DIR = join6(HOME, "runtime");
|
|
8845
|
+
function sessionFile(id) {
|
|
8846
|
+
return join6(DIR, `${id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80)}.json`);
|
|
8847
|
+
}
|
|
8848
|
+
function readSession(id) {
|
|
8849
|
+
try {
|
|
8850
|
+
return JSON.parse(readFileSync5(sessionFile(id), "utf8"));
|
|
8851
|
+
} catch {
|
|
8852
|
+
return { active: null, verdicts: {}, reported: [] };
|
|
8853
|
+
}
|
|
8854
|
+
}
|
|
8855
|
+
function writeSession(id, s) {
|
|
8856
|
+
if (!existsSync6(DIR)) mkdirSync3(DIR, { recursive: true });
|
|
8857
|
+
writeFileSync4(sessionFile(id), JSON.stringify(s));
|
|
8858
|
+
}
|
|
8859
|
+
function hostsIn(cmd2) {
|
|
8860
|
+
const out = /* @__PURE__ */ new Set();
|
|
8861
|
+
for (const m of cmd2.matchAll(/https?:\/\/([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|\d{1,3}(?:\.\d{1,3}){3})(?::\d+)?/g)) out.add(m[1].toLowerCase());
|
|
8862
|
+
for (const m of cmd2.matchAll(/\b(?:ssh|scp|sftp|nc|ncat|netcat|telnet)\s+(?:-\S+\s+)*(?:[\w.-]+@)?([a-zA-Z0-9.-]+\.[a-zA-Z]{2,}|\d{1,3}(?:\.\d{1,3}){3})/g)) out.add(m[1].toLowerCase());
|
|
8863
|
+
return [...out];
|
|
8864
|
+
}
|
|
8865
|
+
function pathsIn(cmd2) {
|
|
8866
|
+
const read2 = /* @__PURE__ */ new Set();
|
|
8867
|
+
const written = /* @__PURE__ */ new Set();
|
|
8868
|
+
const norm = (p) => p.replace(/^\$HOME/, "~").replace(/^["']|["']$/g, "");
|
|
8869
|
+
for (const m of cmd2.matchAll(/(?:>>?|tee\s+(?:-a\s+)?)\s*["']?((?:~|\/|\$HOME)[^\s"'|;&]+)/g)) written.add(norm(m[1]));
|
|
8870
|
+
for (const m of cmd2.matchAll(/(?:^|[\s|;&(])(?:cat|less|head|tail|grep|cp|scp|base64|xxd|od|strings|python3?|node)\s+(?:-\S+\s+)*["']?((?:~|\/|\$HOME)[^\s"'|;&]+)/g)) read2.add(norm(m[1]));
|
|
8871
|
+
for (const m of cmd2.matchAll(/["']?((?:~|\$HOME)\/\.(?:ssh|aws|npmrc|netrc|config\/gh|docker|kube|gnupg)[^\s"'|;&]*)/g)) if (!written.has(norm(m[1]))) read2.add(norm(m[1]));
|
|
8872
|
+
return { read: [...read2], written: [...written] };
|
|
8873
|
+
}
|
|
8874
|
+
var isOutsideProject = (p) => (p.startsWith("~") || p.startsWith("/")) && !p.startsWith(process.cwd()) && !p.startsWith("/tmp") && !p.startsWith("/dev");
|
|
8875
|
+
var tilde = (p) => p.replace(homedir6(), "~");
|
|
8876
|
+
function attributed(session, toolName) {
|
|
8877
|
+
const m = toolName.match(/^mcp__([^_]+(?:_[^_]+)*?)__/);
|
|
8878
|
+
if (m) return m[1];
|
|
8879
|
+
if (session.active && Date.now() - session.active.since < ACTIVE_MS) return session.active.skill;
|
|
8880
|
+
return null;
|
|
8881
|
+
}
|
|
8882
|
+
async function verdictFor(session, skill) {
|
|
8883
|
+
const c2 = session.verdicts[skill];
|
|
8884
|
+
if (c2 && Date.now() - c2.at < 36e5) return c2.r;
|
|
8885
|
+
const r = await resolveSkill(skill).catch(() => null);
|
|
8886
|
+
session.verdicts[skill] = { at: Date.now(), r: r && r.ok ? r.data : null };
|
|
8887
|
+
return session.verdicts[skill].r;
|
|
8888
|
+
}
|
|
8889
|
+
function parseToolEvent(toolName, input) {
|
|
8890
|
+
const o = { kind: "other", hosts: [], read: [], written: [], skillInvoked: null };
|
|
8891
|
+
if (toolName === "Skill") {
|
|
8892
|
+
o.kind = "Skill";
|
|
8893
|
+
o.skillInvoked = String(input.skill ?? input.name ?? input.command ?? "").replace(/^\//, "").split(/\s/)[0] || null;
|
|
8894
|
+
return o;
|
|
8895
|
+
}
|
|
8896
|
+
if (toolName === "Bash" && typeof input.command === "string") {
|
|
8897
|
+
o.kind = "Bash";
|
|
8898
|
+
o.hosts = hostsIn(input.command);
|
|
8899
|
+
const p = pathsIn(input.command);
|
|
8900
|
+
o.read = p.read;
|
|
8901
|
+
o.written = p.written;
|
|
8902
|
+
return o;
|
|
8903
|
+
}
|
|
8904
|
+
if (toolName === "Read" && typeof input.file_path === "string") {
|
|
8905
|
+
o.kind = "Read";
|
|
8906
|
+
o.read = [tilde(input.file_path)];
|
|
8907
|
+
return o;
|
|
8908
|
+
}
|
|
8909
|
+
if ((toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit" || toolName === "NotebookEdit") && typeof input.file_path === "string") {
|
|
8910
|
+
o.kind = "Write";
|
|
8911
|
+
o.written = [tilde(input.file_path)];
|
|
8912
|
+
return o;
|
|
8913
|
+
}
|
|
8914
|
+
if (toolName === "WebFetch" && typeof input.url === "string") {
|
|
8915
|
+
o.kind = "WebFetch";
|
|
8916
|
+
try {
|
|
8917
|
+
o.hosts = [new URL(input.url).hostname.toLowerCase()];
|
|
8918
|
+
} catch {
|
|
8919
|
+
}
|
|
8920
|
+
return o;
|
|
8921
|
+
}
|
|
8922
|
+
if (toolName.startsWith("mcp__")) o.kind = "mcp";
|
|
8923
|
+
return o;
|
|
8924
|
+
}
|
|
8925
|
+
function deviations(skill, r, o, tool, session) {
|
|
8926
|
+
const out = [];
|
|
8927
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
8928
|
+
const semver = r?.version.semver ?? null;
|
|
8929
|
+
const page = r?.skill.page ?? null;
|
|
8930
|
+
const trace = r?.trace_summary ?? null;
|
|
8931
|
+
const declared = new Set((r?.version.declared_permissions.network ?? []).map((h) => h.toLowerCase()));
|
|
8932
|
+
const seenHosts = new Set((trace?.egress_hosts ?? []).map((h) => h.host.toLowerCase()));
|
|
8933
|
+
const seenReads = trace?.files_read ?? [];
|
|
8934
|
+
const seenWrites = trace?.files_written ?? [];
|
|
8935
|
+
const mk = (kind, detail) => out.push({ kind, skill, semver, detail, tool, at, session, page });
|
|
8936
|
+
for (const h of o.hosts) if (!declared.has(h) && !seenHosts.has(h) && !/^(localhost|127\.0\.0\.1|registry\.npmjs\.org|pypi\.org|files\.pythonhosted\.org|github\.com|api\.github\.com|raw\.githubusercontent\.com|killerskills\.ai)$/.test(h)) mk("egress_unexpected", `contacted ${h}; the sandbox saw ${seenHosts.size ? [...seenHosts].slice(0, 3).join(", ") : "no network activity"}${declared.size ? ` and it declares ${[...declared].join(", ")}` : ""}`);
|
|
8937
|
+
for (const p of o.read) if (CRED.test(p) && !seenReads.some((s) => s.includes(p.replace(/^~\//, "")))) mk("credential_read", `read ${p}; the sandbox never saw a credential path read`);
|
|
8938
|
+
for (const p of o.written) {
|
|
8939
|
+
if (MEMORY.test(p) && !trace?.memory_writes.length) mk("memory_write", `wrote ${p}; the sandbox saw no memory or instruction-file writes`);
|
|
8940
|
+
else if (isOutsideProject(p) && !CRED.test(p) && !MEMORY.test(p) && !seenWrites.some((s) => s.includes(p.replace(/^~\//, "")))) mk("outside_project_write", `wrote ${p}, outside the project; not seen in the sandbox`);
|
|
8941
|
+
}
|
|
8942
|
+
return out;
|
|
8943
|
+
}
|
|
8944
|
+
var LOG = join6(HOME, "runtime", "deviations.jsonl");
|
|
8945
|
+
function appendDeviations(ds) {
|
|
8946
|
+
if (!ds.length) return;
|
|
8947
|
+
if (!existsSync6(DIR)) mkdirSync3(DIR, { recursive: true });
|
|
8948
|
+
writeFileSync4(LOG, ds.map((d) => JSON.stringify(d)).join("\n") + "\n", { flag: "a" });
|
|
8949
|
+
}
|
|
8950
|
+
function readDeviations(days = 7) {
|
|
8951
|
+
if (!existsSync6(LOG)) return [];
|
|
8952
|
+
const since = Date.now() - days * 864e5;
|
|
8953
|
+
return readFileSync5(LOG, "utf8").split("\n").filter(Boolean).map((l) => {
|
|
8954
|
+
try {
|
|
8955
|
+
return JSON.parse(l);
|
|
8956
|
+
} catch {
|
|
8957
|
+
return null;
|
|
8958
|
+
}
|
|
8959
|
+
}).filter((d) => !!d && Date.parse(d.at) >= since);
|
|
8960
|
+
}
|
|
8961
|
+
async function observe(stdin) {
|
|
8962
|
+
let payload = {};
|
|
8963
|
+
try {
|
|
8964
|
+
payload = JSON.parse(stdin);
|
|
8965
|
+
} catch {
|
|
8966
|
+
return { code: 0, lines: [] };
|
|
8967
|
+
}
|
|
8968
|
+
const tool = payload.tool_name ?? "";
|
|
8969
|
+
const input = payload.tool_input ?? {};
|
|
8970
|
+
const sid = payload.session_id ?? "default";
|
|
8971
|
+
const session = readSession(sid);
|
|
8972
|
+
const o = parseToolEvent(tool, input);
|
|
8973
|
+
if (o.kind === "Skill") {
|
|
8974
|
+
if (o.skillInvoked) session.active = { skill: o.skillInvoked, since: Date.now() };
|
|
8975
|
+
writeSession(sid, session);
|
|
8976
|
+
return { code: 0, lines: [] };
|
|
8977
|
+
}
|
|
8978
|
+
const skill = attributed(session, tool);
|
|
8979
|
+
if (!skill || !o.hosts.length && !o.read.length && !o.written.length) return { code: 0, lines: [] };
|
|
8980
|
+
const r = await verdictFor(session, skill);
|
|
8981
|
+
const ds = deviations(skill, r, o, tool, payload.session_id ?? null).filter((d) => {
|
|
8982
|
+
const k = `${d.skill}|${d.kind}|${d.detail.split(";")[0]}`;
|
|
8983
|
+
if (session.reported.includes(k)) return false;
|
|
8984
|
+
session.reported.push(k);
|
|
8985
|
+
return true;
|
|
8986
|
+
});
|
|
8987
|
+
writeSession(sid, session);
|
|
8988
|
+
if (!ds.length) return { code: 0, lines: [] };
|
|
8989
|
+
appendDeviations(ds);
|
|
8990
|
+
if (readOrg()) await runtimeReport({ events: ds.map((d) => ({ skill: d.skill, semver: d.semver, kind: d.kind, detail: d.detail, tool: d.tool, at: d.at, session: d.session })) }).catch(() => null);
|
|
8991
|
+
const lines = ds.map((d) => `vet-skills \u26A0 ${d.skill}${d.semver ? `@${d.semver}` : ""} ${d.detail}. Not what its verdict is based on${d.page ? `: https://killerskills.ai${d.page}` : ""}`);
|
|
8992
|
+
const serious = ds.some((d) => d.kind === "egress_unexpected" || d.kind === "credential_read");
|
|
8993
|
+
return { code: serious ? 2 : 0, lines };
|
|
8994
|
+
}
|
|
8995
|
+
function pruneSessions() {
|
|
8996
|
+
if (!existsSync6(DIR)) return;
|
|
8997
|
+
for (const f of readdirSync3(DIR)) {
|
|
8998
|
+
if (!f.endsWith(".json")) continue;
|
|
8999
|
+
const p = join6(DIR, f);
|
|
9000
|
+
try {
|
|
9001
|
+
if (Date.now() - statSync4(p).mtimeMs > 864e5) writeFileSync4(p, "{}");
|
|
9002
|
+
} catch {
|
|
9003
|
+
}
|
|
9004
|
+
}
|
|
8221
9005
|
}
|
|
8222
9006
|
|
|
8223
9007
|
// src/index.ts
|
|
@@ -8228,10 +9012,13 @@ var { values, positionals } = parseArgs({
|
|
|
8228
9012
|
reason: { type: "string" },
|
|
8229
9013
|
org: { type: "string" },
|
|
8230
9014
|
yes: { type: "boolean", short: "y" },
|
|
9015
|
+
lock: { type: "boolean" },
|
|
8231
9016
|
json: { type: "boolean" },
|
|
8232
9017
|
ci: { type: "boolean" },
|
|
8233
9018
|
email: { type: "string" },
|
|
8234
9019
|
help: { type: "boolean", short: "h" },
|
|
9020
|
+
project: { type: "boolean" },
|
|
9021
|
+
quiet: { type: "boolean", short: "q" },
|
|
8235
9022
|
version: { type: "boolean", short: "v" },
|
|
8236
9023
|
agent: { type: "string" },
|
|
8237
9024
|
publisher: { type: "string" },
|
|
@@ -8240,7 +9027,7 @@ var { values, positionals } = parseArgs({
|
|
|
8240
9027
|
});
|
|
8241
9028
|
var [cmd, ...rest] = positionals;
|
|
8242
9029
|
async function ask(question, choices) {
|
|
8243
|
-
const rl =
|
|
9030
|
+
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
8244
9031
|
return new Promise((res) => rl.question(question, (a) => {
|
|
8245
9032
|
rl.close();
|
|
8246
9033
|
const k = a.trim().toLowerCase()[0];
|
|
@@ -8264,9 +9051,15 @@ Get started:
|
|
|
8264
9051
|
vet-skills policy show|set <file> view or replace the local policy
|
|
8265
9052
|
vet-skills block <skill> [--reason] add to the personal block list
|
|
8266
9053
|
vet-skills status org, policy version, last report, cache size
|
|
9054
|
+
vet-skills discover [--project] [--json] every MCP server, skill, plugin and hook on this machine, with verdicts
|
|
9055
|
+
vet-skills mcp run as an MCP server (check_skill, search_skills) for any agent
|
|
8267
9056
|
vet-skills sync pull the org policy and apply pending removals
|
|
9057
|
+
vet-skills verify <skill|path> check the signed verdict against what is on disk
|
|
9058
|
+
vet-skills lock pin every installed skill's hash + attestation in vet-skills.lock
|
|
9059
|
+
vet-skills verify --lock offline: installed skills still match the lock and signatures verify
|
|
8268
9060
|
vet-skills hook install claude-code register the Claude Code PreToolUse hook
|
|
8269
9061
|
vet-skills hook eval (called by the agent) read hook JSON on stdin
|
|
9062
|
+
vet-skills runtime what installed skills did in real sessions that the sandbox never saw
|
|
8270
9063
|
|
|
8271
9064
|
Docs: https://killerskills.ai/docs/cli
|
|
8272
9065
|
`;
|
|
@@ -8344,8 +9137,8 @@ async function main() {
|
|
|
8344
9137
|
usage();
|
|
8345
9138
|
return 1;
|
|
8346
9139
|
}
|
|
8347
|
-
const dir =
|
|
8348
|
-
if (!
|
|
9140
|
+
const dir = resolve2(rest[0]);
|
|
9141
|
+
if (!existsSync7(join7(dir, "SKILL.md")) && !existsSync7(join7(dir, "package.json"))) {
|
|
8349
9142
|
console.log(`${c.blocked("\u2717")} ${dir} has no SKILL.md or package.json`);
|
|
8350
9143
|
return 1;
|
|
8351
9144
|
}
|
|
@@ -8387,8 +9180,8 @@ async function main() {
|
|
|
8387
9180
|
console.log(`${c.blocked("\u2717")} not enrolled in an organization. Run ${c.bold("vet-skills join <org> --token \u2026")} first.`);
|
|
8388
9181
|
return 1;
|
|
8389
9182
|
}
|
|
8390
|
-
const dir =
|
|
8391
|
-
if (!
|
|
9183
|
+
const dir = resolve2(rest[0]);
|
|
9184
|
+
if (!existsSync7(join7(dir, "SKILL.md"))) {
|
|
8392
9185
|
console.log(`${c.blocked("\u2717")} ${dir} has no SKILL.md (name and version come from its frontmatter)`);
|
|
8393
9186
|
return 1;
|
|
8394
9187
|
}
|
|
@@ -8432,7 +9225,7 @@ async function main() {
|
|
|
8432
9225
|
console.error("usage: vet-skills join <org> --token <enroll token>");
|
|
8433
9226
|
return 1;
|
|
8434
9227
|
}
|
|
8435
|
-
const machineId =
|
|
9228
|
+
const machineId = createHash2("sha256").update(`${hostname()}:${userInfo().username}`).digest("hex");
|
|
8436
9229
|
const res = await enroll(slug, values.token, {
|
|
8437
9230
|
developer_email: values.email,
|
|
8438
9231
|
machine_id: machineId,
|
|
@@ -8480,7 +9273,7 @@ async function main() {
|
|
|
8480
9273
|
console.error("usage: vet-skills policy set <file.yaml>");
|
|
8481
9274
|
return 1;
|
|
8482
9275
|
}
|
|
8483
|
-
const text =
|
|
9276
|
+
const text = readFileSync6(rest[1], "utf8");
|
|
8484
9277
|
parsePolicyYaml(text);
|
|
8485
9278
|
writePolicy(text);
|
|
8486
9279
|
console.log(`${c.verified("\u2713")} Personal policy updated.`);
|
|
@@ -8550,6 +9343,99 @@ async function main() {
|
|
|
8550
9343
|
}
|
|
8551
9344
|
return 0;
|
|
8552
9345
|
}
|
|
9346
|
+
case "verify": {
|
|
9347
|
+
if (values.lock) {
|
|
9348
|
+
const lock = readLock();
|
|
9349
|
+
if (!lock) {
|
|
9350
|
+
console.log(c.caution(`no ${LOCKFILE} in ${process.cwd()}; run \`vet-skills lock\` first`));
|
|
9351
|
+
return 1;
|
|
9352
|
+
}
|
|
9353
|
+
const rs = verifyLock(lock);
|
|
9354
|
+
for (const r2 of rs) console.log(`${r2.ok ? c.verified("\u2713") : c.blocked("\u2717")} ${r2.subject} ${c.muted(r2.detail)}`);
|
|
9355
|
+
const bad = rs.filter((r2) => !r2.ok).length;
|
|
9356
|
+
console.log(bad ? c.blocked(`${bad} of ${rs.length} failed`) : c.verified(`${rs.length} skill${rs.length === 1 ? "" : "s"} verified against ${LOCKFILE} (offline, ${lock.keys.length} pinned key${lock.keys.length === 1 ? "" : "s"})`));
|
|
9357
|
+
if (values.json) console.log(JSON.stringify(rs, null, 2));
|
|
9358
|
+
return bad ? 1 : 0;
|
|
9359
|
+
}
|
|
9360
|
+
if (!rest[0]) {
|
|
9361
|
+
console.log(c.caution("usage: vet-skills verify <skill>[@version] | <path> | --lock"));
|
|
9362
|
+
return 1;
|
|
9363
|
+
}
|
|
9364
|
+
const keys = await fetchKeys();
|
|
9365
|
+
if (!keys.length) console.log(c.caution(`could not fetch signing keys from ${API}/.well-known/killerskills-keys.json`));
|
|
9366
|
+
const r = await verifyOne(rest[0], keys);
|
|
9367
|
+
console.log(`${r.ok ? c.verified("\u2713 verified") : c.blocked("\u2717 not verified")} ${r.subject}
|
|
9368
|
+
${r.detail}`);
|
|
9369
|
+
if (values.json) console.log(JSON.stringify(r, null, 2));
|
|
9370
|
+
return r.ok ? 0 : 1;
|
|
9371
|
+
}
|
|
9372
|
+
case "lock": {
|
|
9373
|
+
const { file, lock, missing } = await writeLock();
|
|
9374
|
+
console.log(`${c.verified("\u2713")} wrote ${file}: ${lock.skills.length} skill${lock.skills.length === 1 ? "" : "s"}, ${lock.keys.length} signing key${lock.keys.length === 1 ? "" : "s"} pinned`);
|
|
9375
|
+
for (const s of lock.skills) console.log(` ${s.attestation ? c.verified("\u2713") : c.caution("\xB7")} ${s.name} ${c.muted(s.verdict ? `${s.verdict.label} ${s.verdict.score}` : "no attestation (unscored or never crawled)")}`);
|
|
9376
|
+
if (missing.length) console.log(c.muted(`${missing.length} without an attestation are pinned by hash only; \`vet-skills verify --lock\` reports them.`));
|
|
9377
|
+
return 0;
|
|
9378
|
+
}
|
|
9379
|
+
case "runtime": {
|
|
9380
|
+
const ds = readDeviations(7);
|
|
9381
|
+
if (!ds.length) {
|
|
9382
|
+
console.log(c.muted("No deviations in the last 7 days: installed skills behaved the way the sandbox saw them (or no skill ran)."));
|
|
9383
|
+
return 0;
|
|
9384
|
+
}
|
|
9385
|
+
const by = /* @__PURE__ */ new Map();
|
|
9386
|
+
for (const d of ds) by.set(d.skill, [...by.get(d.skill) ?? [], d]);
|
|
9387
|
+
for (const [skill, list] of by) {
|
|
9388
|
+
console.log(`${c.caution("\u26A0")} ${skill}${list[0].semver ? `@${list[0].semver}` : ""} ${c.muted(`${list.length} deviation${list.length === 1 ? "" : "s"}${list[0].page ? ` \xB7 killerskills.ai${list[0].page}` : ""}`)}`);
|
|
9389
|
+
for (const d of list.slice(-6)) console.log(` ${d.at.slice(0, 16).replace("T", " ")} ${d.kind.replace("_", " ")} ${d.detail}`);
|
|
9390
|
+
}
|
|
9391
|
+
if (values.json) console.log(JSON.stringify(ds, null, 2));
|
|
9392
|
+
return 0;
|
|
9393
|
+
}
|
|
9394
|
+
case "discover": {
|
|
9395
|
+
const comps = discoverLocal({ project: values.project ? process.cwd() : void 0 });
|
|
9396
|
+
if (values.json) {
|
|
9397
|
+
console.log(JSON.stringify(comps, null, 2));
|
|
9398
|
+
} else if (!values.quiet) {
|
|
9399
|
+
if (!comps.length) console.log(c.muted("No agent configuration found in the usual places (~/.claude.json, ~/.cursor/mcp.json, Claude Desktop, Windsurf, Gemini CLI, Codex, ~/.claude/skills)."));
|
|
9400
|
+
const runnable = comps.filter((x) => x.kind === "mcp_server" || x.kind === "skill");
|
|
9401
|
+
const verdicts = /* @__PURE__ */ new Map();
|
|
9402
|
+
await Promise.all(runnable.slice(0, 40).map(async (x) => {
|
|
9403
|
+
const q = x.package ? x.package.name.replace(/^@/, "") : x.name;
|
|
9404
|
+
const r = await resolveSkill(q, x.package?.registry);
|
|
9405
|
+
const v = r.ok ? r.data.verdict : null;
|
|
9406
|
+
verdicts.set(`${x.kind}|${x.name}`, v?.label ? `${v.label} ${v.score ?? ""}`.trim() : "not vetted");
|
|
9407
|
+
}));
|
|
9408
|
+
const kinds = [["mcp_server", "MCP servers"], ["skill", "Skills"], ["plugin", "Plugins"], ["hook", "Hooks"], ["instructions", "Instruction files"]];
|
|
9409
|
+
for (const [k, label] of kinds) {
|
|
9410
|
+
const list = comps.filter((x) => x.kind === k);
|
|
9411
|
+
if (!list.length) continue;
|
|
9412
|
+
console.log(`
|
|
9413
|
+
${label} (${list.length})`);
|
|
9414
|
+
for (const x of list) {
|
|
9415
|
+
const v = verdicts.get(`${x.kind}|${x.name}`);
|
|
9416
|
+
const tone = !v ? c.muted : v.startsWith("verified") ? c.verified : v.startsWith("blocked") ? c.blocked : c.caution;
|
|
9417
|
+
const what = x.package ? `${x.package.registry}:${x.package.name}` : x.urlHost ?? x.command ?? x.detail ?? "";
|
|
9418
|
+
const keys = x.envKeys.length || x.headerKeys.length ? c.muted(` \xB7 ${[...x.envKeys, ...x.headerKeys].slice(0, 4).join(", ")}`) : "";
|
|
9419
|
+
console.log(` ${x.name.padEnd(28)} ${tone((v ?? "").padEnd(14))} ${c.muted(what)}${keys} ${c.muted(x.path)}`);
|
|
9420
|
+
}
|
|
9421
|
+
}
|
|
9422
|
+
}
|
|
9423
|
+
if (readOrg()) {
|
|
9424
|
+
const r = await discoverReport(comps);
|
|
9425
|
+
if (r.ok) {
|
|
9426
|
+
if (!values.json) console.log(`
|
|
9427
|
+
${c.verified("\u2713")} ${r.data.components} component${r.data.components === 1 ? "" : "s"} reported to ${readOrg().slug} (names only) \xB7 ${API}${r.data.dashboard}`);
|
|
9428
|
+
} else {
|
|
9429
|
+
console.error(c.caution(`report failed: ${r.error}`));
|
|
9430
|
+
return 1;
|
|
9431
|
+
}
|
|
9432
|
+
} else if (!values.json && !values.quiet) console.log(c.muted("\nNot enrolled: nothing was sent. `vet-skills join <org> --token <t>` reports this to your team's Discovery page."));
|
|
9433
|
+
return 0;
|
|
9434
|
+
}
|
|
9435
|
+
case "mcp": {
|
|
9436
|
+
await serveMcp();
|
|
9437
|
+
return 0;
|
|
9438
|
+
}
|
|
8553
9439
|
case "sync": {
|
|
8554
9440
|
if (!readOrg()) {
|
|
8555
9441
|
console.log(c.muted("Not joined to an org; nothing to sync."));
|
|
@@ -8570,6 +9456,18 @@ async function main() {
|
|
|
8570
9456
|
for await (const ch of process.stdin) chunks.push(ch);
|
|
8571
9457
|
return hookEval(Buffer.concat(chunks).toString("utf8"));
|
|
8572
9458
|
}
|
|
9459
|
+
if (sub === "observe") {
|
|
9460
|
+
const chunks = [];
|
|
9461
|
+
for await (const ch of process.stdin) chunks.push(ch);
|
|
9462
|
+
try {
|
|
9463
|
+
const r = await observe(Buffer.concat(chunks).toString("utf8"));
|
|
9464
|
+
for (const l of r.lines) process.stderr.write(l + "\n");
|
|
9465
|
+
if (Math.random() < 0.02) pruneSessions();
|
|
9466
|
+
return r.code;
|
|
9467
|
+
} catch {
|
|
9468
|
+
return 0;
|
|
9469
|
+
}
|
|
9470
|
+
}
|
|
8573
9471
|
if (sub === "install") {
|
|
8574
9472
|
const target = rest[1] ?? "claude-code";
|
|
8575
9473
|
if (target !== "claude-code") {
|
|
@@ -8578,7 +9476,7 @@ async function main() {
|
|
|
8578
9476
|
}
|
|
8579
9477
|
const r = installClaudeCodeHook();
|
|
8580
9478
|
console.log(r.changed ? `${c.verified("\u2713")} Claude Code hook installed in ${r.path}` : c.muted(`Hook already present in ${r.path}`));
|
|
8581
|
-
console.log(c.muted("Every `skills add`, `claude plugin install`, and `claude mcp add` is now checked before it runs."));
|
|
9479
|
+
console.log(c.muted("Every `skills add`, `claude plugin install`, and `claude mcp add` is now checked before it runs, and what skills do in sessions is compared with their sandbox trace (vet-skills runtime)."));
|
|
8582
9480
|
return 0;
|
|
8583
9481
|
}
|
|
8584
9482
|
usage();
|