coderifts 1.9.0 → 3.0.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/README.md +134 -0
- package/bin/coderifts.js +119 -1
- package/dist/cli.js +2873 -103
- package/package.json +5 -3
- package/scripts/assert-publish-gate.sh +42 -0
package/dist/cli.js
CHANGED
|
@@ -3007,7 +3007,7 @@ var require_package = __commonJS({
|
|
|
3007
3007
|
"package.json"(exports2, module2) {
|
|
3008
3008
|
module2.exports = {
|
|
3009
3009
|
name: "coderifts",
|
|
3010
|
-
version: "
|
|
3010
|
+
version: "3.0.0",
|
|
3011
3011
|
description: "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
3012
3012
|
author: "CodeRifts <hello@coderifts.com>",
|
|
3013
3013
|
license: "MIT",
|
|
@@ -3055,7 +3055,7 @@ var require_package = __commonJS({
|
|
|
3055
3055
|
"cli-table3": "^0.6.4",
|
|
3056
3056
|
commander: "^12.0.0",
|
|
3057
3057
|
inquirer: "^8.2.6",
|
|
3058
|
-
"js-yaml": "^4.
|
|
3058
|
+
"js-yaml": "^4.3.1",
|
|
3059
3059
|
"openapi-diff": "^0.24.1",
|
|
3060
3060
|
ora: "^5.4.1"
|
|
3061
3061
|
},
|
|
@@ -3063,7 +3063,9 @@ var require_package = __commonJS({
|
|
|
3063
3063
|
"z-schema": "^7.2.0",
|
|
3064
3064
|
"json-schema-ref-parser": "npm:@apidevtools/json-schema-ref-parser@^11.7.3",
|
|
3065
3065
|
"form-data": "^4.0.6",
|
|
3066
|
-
axios: ">=1.18.0"
|
|
3066
|
+
axios: ">=1.18.0",
|
|
3067
|
+
"fast-uri": ">=3.1.5",
|
|
3068
|
+
"brace-expansion": "5.0.9"
|
|
3067
3069
|
},
|
|
3068
3070
|
devDependencies: {
|
|
3069
3071
|
esbuild: "^0.28.1"
|
|
@@ -4367,7 +4369,7 @@ var require_templates = __commonJS({
|
|
|
4367
4369
|
if (!Number.isNaN(number)) {
|
|
4368
4370
|
results.push(number);
|
|
4369
4371
|
} else if (matches = chunk.match(STRING_REGEX)) {
|
|
4370
|
-
results.push(matches[2].replace(ESCAPE_REGEX, (m,
|
|
4372
|
+
results.push(matches[2].replace(ESCAPE_REGEX, (m, escape, character) => escape ? unescape2(escape) : character));
|
|
4371
4373
|
} else {
|
|
4372
4374
|
throw new Error(`Invalid Chalk template style argument: ${chunk} (in style '${name}')`);
|
|
4373
4375
|
}
|
|
@@ -11256,7 +11258,7 @@ var require_omap = __commonJS({
|
|
|
11256
11258
|
var _toString = Object.prototype.toString;
|
|
11257
11259
|
function resolveYamlOmap(data) {
|
|
11258
11260
|
if (data === null) return true;
|
|
11259
|
-
const objectKeys =
|
|
11261
|
+
const objectKeys = {};
|
|
11260
11262
|
const object = data;
|
|
11261
11263
|
for (let index = 0, length = object.length; index < length; index += 1) {
|
|
11262
11264
|
const pair = object[index];
|
|
@@ -11270,8 +11272,8 @@ var require_omap = __commonJS({
|
|
|
11270
11272
|
}
|
|
11271
11273
|
}
|
|
11272
11274
|
if (!pairHasKey) return false;
|
|
11273
|
-
if (
|
|
11274
|
-
|
|
11275
|
+
if (_hasOwnProperty.call(objectKeys, pairKey)) return false;
|
|
11276
|
+
Object.defineProperty(objectKeys, pairKey, { value: true });
|
|
11275
11277
|
}
|
|
11276
11278
|
return true;
|
|
11277
11279
|
}
|
|
@@ -13491,33 +13493,39 @@ var require_cloud = __commonJS({
|
|
|
13491
13493
|
"src/cloud.js"(exports2, module2) {
|
|
13492
13494
|
"use strict";
|
|
13493
13495
|
var https = require("https");
|
|
13494
|
-
var API_BASE = "https://app.coderifts.com";
|
|
13495
|
-
function
|
|
13496
|
+
var API_BASE = process.env.CODERIFTS_API_BASE || "https://app.coderifts.com";
|
|
13497
|
+
function cloudRequest(method, pathname, apiKey, bodyObj = null) {
|
|
13496
13498
|
return new Promise((resolve, reject) => {
|
|
13497
|
-
const body = JSON.stringify(
|
|
13498
|
-
const
|
|
13499
|
+
const body = bodyObj != null ? JSON.stringify(bodyObj) : null;
|
|
13500
|
+
const base = API_BASE.replace(/\/$/, "");
|
|
13501
|
+
const url = new URL(pathname.startsWith("http") ? pathname : base + pathname);
|
|
13499
13502
|
const options = {
|
|
13500
13503
|
hostname: url.hostname,
|
|
13501
|
-
port: 443,
|
|
13502
|
-
path: url.pathname,
|
|
13503
|
-
method
|
|
13504
|
+
port: url.port || (url.protocol === "http:" ? 80 : 443),
|
|
13505
|
+
path: url.pathname + url.search,
|
|
13506
|
+
method,
|
|
13504
13507
|
headers: {
|
|
13505
|
-
|
|
13506
|
-
|
|
13507
|
-
"
|
|
13508
|
-
"
|
|
13508
|
+
Accept: "application/json",
|
|
13509
|
+
Authorization: `Bearer ${apiKey}`,
|
|
13510
|
+
"User-Agent": "@coderifts/cli",
|
|
13511
|
+
...body ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(body) } : {}
|
|
13509
13512
|
}
|
|
13510
13513
|
};
|
|
13511
|
-
const
|
|
13514
|
+
const transport = url.protocol === "http:" ? require("http") : https;
|
|
13515
|
+
const req = transport.request(options, (res) => {
|
|
13512
13516
|
let data = "";
|
|
13513
13517
|
res.on("data", (chunk) => {
|
|
13514
13518
|
data += chunk;
|
|
13515
13519
|
});
|
|
13516
13520
|
res.on("end", () => {
|
|
13517
13521
|
try {
|
|
13518
|
-
const parsed = JSON.parse(data);
|
|
13522
|
+
const parsed = data ? JSON.parse(data) : {};
|
|
13519
13523
|
if (res.statusCode >= 400) {
|
|
13520
|
-
|
|
13524
|
+
const err = new Error(parsed.message || parsed.error || `HTTP ${res.statusCode}`);
|
|
13525
|
+
err.statusCode = res.statusCode;
|
|
13526
|
+
err.code = parsed.error || null;
|
|
13527
|
+
err.body = parsed;
|
|
13528
|
+
reject(err);
|
|
13521
13529
|
} else {
|
|
13522
13530
|
resolve(parsed);
|
|
13523
13531
|
}
|
|
@@ -13527,11 +13535,26 @@ var require_cloud = __commonJS({
|
|
|
13527
13535
|
});
|
|
13528
13536
|
});
|
|
13529
13537
|
req.on("error", reject);
|
|
13530
|
-
req.write(body);
|
|
13538
|
+
if (body) req.write(body);
|
|
13531
13539
|
req.end();
|
|
13532
13540
|
});
|
|
13533
13541
|
}
|
|
13534
|
-
|
|
13542
|
+
function cloudDiff(oldSpec, newSpec, apiKey) {
|
|
13543
|
+
return cloudRequest("POST", "/api/v1/diff", apiKey, {
|
|
13544
|
+
old_spec: oldSpec,
|
|
13545
|
+
new_spec: newSpec
|
|
13546
|
+
});
|
|
13547
|
+
}
|
|
13548
|
+
function cloudGetEnforcementStatus(repo, apiKey) {
|
|
13549
|
+
const q = encodeURIComponent(String(repo || ""));
|
|
13550
|
+
return cloudRequest("GET", `/api/v1/enforcement-status?repo=${q}`, apiKey);
|
|
13551
|
+
}
|
|
13552
|
+
module2.exports = {
|
|
13553
|
+
cloudDiff,
|
|
13554
|
+
cloudRequest,
|
|
13555
|
+
cloudGetEnforcementStatus,
|
|
13556
|
+
API_BASE
|
|
13557
|
+
};
|
|
13535
13558
|
}
|
|
13536
13559
|
});
|
|
13537
13560
|
|
|
@@ -24778,7 +24801,7 @@ var require_lodash = __commonJS({
|
|
|
24778
24801
|
position -= target.length;
|
|
24779
24802
|
return position >= 0 && string.slice(position, end) == target;
|
|
24780
24803
|
}
|
|
24781
|
-
function
|
|
24804
|
+
function escape(string) {
|
|
24782
24805
|
string = toString(string);
|
|
24783
24806
|
return string && reHasUnescapedHtml.test(string) ? string.replace(reUnescapedHtml, escapeHtmlChar) : string;
|
|
24784
24807
|
}
|
|
@@ -25389,7 +25412,7 @@ var require_lodash = __commonJS({
|
|
|
25389
25412
|
lodash.divide = divide;
|
|
25390
25413
|
lodash.endsWith = endsWith;
|
|
25391
25414
|
lodash.eq = eq;
|
|
25392
|
-
lodash.escape =
|
|
25415
|
+
lodash.escape = escape;
|
|
25393
25416
|
lodash.escapeRegExp = escapeRegExp;
|
|
25394
25417
|
lodash.every = every;
|
|
25395
25418
|
lodash.find = find;
|
|
@@ -29363,6 +29386,7 @@ var require_utils2 = __commonJS({
|
|
|
29363
29386
|
var isHexPair = RegExp.prototype.test.bind(/^[\da-f]{2}$/iu);
|
|
29364
29387
|
var isUnreserved = RegExp.prototype.test.bind(/^[\da-z\-._~]$/iu);
|
|
29365
29388
|
var isPathCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/]$/iu);
|
|
29389
|
+
var isQueryFragmentCharacter = RegExp.prototype.test.bind(/^[\da-z\-._~!$&'()*+,;=:@/?]$/iu);
|
|
29366
29390
|
function stringArrayToHexStripped(input) {
|
|
29367
29391
|
let acc = "";
|
|
29368
29392
|
let code = 0;
|
|
@@ -29505,7 +29529,7 @@ var require_utils2 = __commonJS({
|
|
|
29505
29529
|
continue;
|
|
29506
29530
|
}
|
|
29507
29531
|
} else if (input[0] === "/") {
|
|
29508
|
-
if (input[1] === "."
|
|
29532
|
+
if (input[1] === ".") {
|
|
29509
29533
|
output.push("/");
|
|
29510
29534
|
break;
|
|
29511
29535
|
}
|
|
@@ -29587,10 +29611,30 @@ var require_utils2 = __commonJS({
|
|
|
29587
29611
|
}
|
|
29588
29612
|
return output;
|
|
29589
29613
|
}
|
|
29614
|
+
var BYTE_HEX = new Array(256);
|
|
29615
|
+
{
|
|
29616
|
+
const HEX_DIGITS = "0123456789ABCDEF";
|
|
29617
|
+
for (let i = 0; i < 256; i++) {
|
|
29618
|
+
BYTE_HEX[i] = "%" + HEX_DIGITS[i >> 4] + HEX_DIGITS[i & 15];
|
|
29619
|
+
}
|
|
29620
|
+
}
|
|
29621
|
+
function isEscapeSafe(cp) {
|
|
29622
|
+
return cp >= 48 && cp <= 57 || cp >= 65 && cp <= 90 || cp >= 97 && cp <= 122 || cp === 42 || cp === 43 || cp === 45 || cp === 46 || cp === 47 || cp === 64 || cp === 95;
|
|
29623
|
+
}
|
|
29624
|
+
function percentEncodeNonAscii(cp) {
|
|
29625
|
+
if (cp < 2048) {
|
|
29626
|
+
return BYTE_HEX[192 | cp >> 6] + BYTE_HEX[128 | cp & 63];
|
|
29627
|
+
}
|
|
29628
|
+
if (cp < 65536) {
|
|
29629
|
+
return BYTE_HEX[224 | cp >> 12] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
29630
|
+
}
|
|
29631
|
+
return BYTE_HEX[240 | cp >> 18] + BYTE_HEX[128 | cp >> 12 & 63] + BYTE_HEX[128 | cp >> 6 & 63] + BYTE_HEX[128 | cp & 63];
|
|
29632
|
+
}
|
|
29590
29633
|
function normalizePathEncoding(input) {
|
|
29591
29634
|
let output = "";
|
|
29592
29635
|
for (let i = 0; i < input.length; i++) {
|
|
29593
|
-
|
|
29636
|
+
const ch = input[i];
|
|
29637
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
29594
29638
|
const hex = input.slice(i + 1, i + 3);
|
|
29595
29639
|
if (isHexPair(hex)) {
|
|
29596
29640
|
const normalizedHex = hex.toUpperCase();
|
|
@@ -29604,10 +29648,66 @@ var require_utils2 = __commonJS({
|
|
|
29604
29648
|
continue;
|
|
29605
29649
|
}
|
|
29606
29650
|
}
|
|
29607
|
-
if (isPathCharacter(
|
|
29608
|
-
output +=
|
|
29651
|
+
if (isPathCharacter(ch)) {
|
|
29652
|
+
output += ch;
|
|
29609
29653
|
} else {
|
|
29610
|
-
|
|
29654
|
+
const code = input.charCodeAt(i);
|
|
29655
|
+
if (code < 128) {
|
|
29656
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
29657
|
+
} else if (code < 55296 || code > 57343) {
|
|
29658
|
+
output += percentEncodeNonAscii(code);
|
|
29659
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
29660
|
+
const low = input.charCodeAt(i + 1);
|
|
29661
|
+
if (low >= 56320 && low <= 57343) {
|
|
29662
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
29663
|
+
i++;
|
|
29664
|
+
} else {
|
|
29665
|
+
output += percentEncodeNonAscii(65533);
|
|
29666
|
+
}
|
|
29667
|
+
} else {
|
|
29668
|
+
output += percentEncodeNonAscii(65533);
|
|
29669
|
+
}
|
|
29670
|
+
}
|
|
29671
|
+
}
|
|
29672
|
+
return output;
|
|
29673
|
+
}
|
|
29674
|
+
function normalizeQueryFragmentEncoding(input) {
|
|
29675
|
+
let output = "";
|
|
29676
|
+
for (let i = 0; i < input.length; i++) {
|
|
29677
|
+
const ch = input[i];
|
|
29678
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
29679
|
+
const hex = input.slice(i + 1, i + 3);
|
|
29680
|
+
if (isHexPair(hex)) {
|
|
29681
|
+
const normalizedHex = hex.toUpperCase();
|
|
29682
|
+
const decoded = String.fromCharCode(parseInt(normalizedHex, 16));
|
|
29683
|
+
if (isUnreserved(decoded)) {
|
|
29684
|
+
output += decoded;
|
|
29685
|
+
} else {
|
|
29686
|
+
output += "%" + normalizedHex;
|
|
29687
|
+
}
|
|
29688
|
+
i += 2;
|
|
29689
|
+
continue;
|
|
29690
|
+
}
|
|
29691
|
+
}
|
|
29692
|
+
if (isQueryFragmentCharacter(ch)) {
|
|
29693
|
+
output += ch;
|
|
29694
|
+
} else {
|
|
29695
|
+
const code = input.charCodeAt(i);
|
|
29696
|
+
if (code < 128) {
|
|
29697
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
29698
|
+
} else if (code < 55296 || code > 57343) {
|
|
29699
|
+
output += percentEncodeNonAscii(code);
|
|
29700
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
29701
|
+
const low = input.charCodeAt(i + 1);
|
|
29702
|
+
if (low >= 56320 && low <= 57343) {
|
|
29703
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
29704
|
+
i++;
|
|
29705
|
+
} else {
|
|
29706
|
+
output += percentEncodeNonAscii(65533);
|
|
29707
|
+
}
|
|
29708
|
+
} else {
|
|
29709
|
+
output += percentEncodeNonAscii(65533);
|
|
29710
|
+
}
|
|
29611
29711
|
}
|
|
29612
29712
|
}
|
|
29613
29713
|
return output;
|
|
@@ -29615,7 +29715,8 @@ var require_utils2 = __commonJS({
|
|
|
29615
29715
|
function escapePreservingEscapes(input) {
|
|
29616
29716
|
let output = "";
|
|
29617
29717
|
for (let i = 0; i < input.length; i++) {
|
|
29618
|
-
|
|
29718
|
+
const ch = input[i];
|
|
29719
|
+
if (ch === "%" && i + 2 < input.length) {
|
|
29619
29720
|
const hex = input.slice(i + 1, i + 3);
|
|
29620
29721
|
if (isHexPair(hex)) {
|
|
29621
29722
|
output += "%" + hex.toUpperCase();
|
|
@@ -29623,7 +29724,22 @@ var require_utils2 = __commonJS({
|
|
|
29623
29724
|
continue;
|
|
29624
29725
|
}
|
|
29625
29726
|
}
|
|
29626
|
-
|
|
29727
|
+
const code = input.charCodeAt(i);
|
|
29728
|
+
if (code < 128) {
|
|
29729
|
+
output += isEscapeSafe(code) ? ch : BYTE_HEX[code];
|
|
29730
|
+
} else if (code < 55296 || code > 57343) {
|
|
29731
|
+
output += percentEncodeNonAscii(code);
|
|
29732
|
+
} else if (code <= 56319 && i + 1 < input.length) {
|
|
29733
|
+
const low = input.charCodeAt(i + 1);
|
|
29734
|
+
if (low >= 56320 && low <= 57343) {
|
|
29735
|
+
output += percentEncodeNonAscii(65536 + (code - 55296 << 10) + (low - 56320));
|
|
29736
|
+
i++;
|
|
29737
|
+
} else {
|
|
29738
|
+
output += percentEncodeNonAscii(65533);
|
|
29739
|
+
}
|
|
29740
|
+
} else {
|
|
29741
|
+
output += percentEncodeNonAscii(65533);
|
|
29742
|
+
}
|
|
29627
29743
|
}
|
|
29628
29744
|
return output;
|
|
29629
29745
|
}
|
|
@@ -29657,6 +29773,7 @@ var require_utils2 = __commonJS({
|
|
|
29657
29773
|
reescapeHostDelimiters,
|
|
29658
29774
|
normalizePercentEncoding,
|
|
29659
29775
|
normalizePathEncoding,
|
|
29776
|
+
normalizeQueryFragmentEncoding,
|
|
29660
29777
|
escapePreservingEscapes,
|
|
29661
29778
|
removeDotSegments,
|
|
29662
29779
|
isIPv4,
|
|
@@ -29881,7 +29998,7 @@ var require_schemes = __commonJS({
|
|
|
29881
29998
|
var require_fast_uri = __commonJS({
|
|
29882
29999
|
"node_modules/fast-uri/index.js"(exports2, module2) {
|
|
29883
30000
|
"use strict";
|
|
29884
|
-
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils2();
|
|
30001
|
+
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizePercentEncoding, normalizePathEncoding, normalizeQueryFragmentEncoding, escapePreservingEscapes, reescapeHostDelimiters, isIPv4, nonSimpleDomain } = require_utils2();
|
|
29885
30002
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
29886
30003
|
function normalize(uri, options) {
|
|
29887
30004
|
if (typeof uri === "string") {
|
|
@@ -29895,7 +30012,12 @@ var require_fast_uri = __commonJS({
|
|
|
29895
30012
|
}
|
|
29896
30013
|
function resolve(baseURI, relativeURI, options) {
|
|
29897
30014
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
29898
|
-
const
|
|
30015
|
+
const { parsed: baseParsed, malformedAuthorityOrPort: baseMalformed } = parseWithStatus(baseURI, schemelessOptions);
|
|
30016
|
+
const { parsed: relativeParsed, malformedAuthorityOrPort: relativeMalformed } = parseWithStatus(relativeURI, schemelessOptions);
|
|
30017
|
+
if (baseMalformed || relativeMalformed) {
|
|
30018
|
+
throw new Error(baseParsed.error || relativeParsed.error || "URI is malformed.");
|
|
30019
|
+
}
|
|
30020
|
+
const resolved = resolveComponent(baseParsed, relativeParsed, schemelessOptions, true);
|
|
29899
30021
|
schemelessOptions.skipEscape = true;
|
|
29900
30022
|
return serialize(resolved, schemelessOptions);
|
|
29901
30023
|
}
|
|
@@ -30021,6 +30143,7 @@ var require_fast_uri = __commonJS({
|
|
|
30021
30143
|
}
|
|
30022
30144
|
var URI_PARSE = /^(?:([^#/:?]+):)?(?:\/\/((?:([^#/?@]*)@)?(\[[^#/?\]]+\]|[^#/:?]*)(?::(\d*))?))?([^#?]*)(?:\?([^#]*))?(?:#((?:.|[\n\r])*))?/u;
|
|
30023
30145
|
var AUTHORITY_PREFIX = /^(?:[^#/:?]+:)?\/\/([^/?#]*)/;
|
|
30146
|
+
var AUTHORITY_INTRODUCER_REGION = /^(?:[^#/:?]+:)?([/\\\t\n\r]*)/;
|
|
30024
30147
|
function getParseError(parsed, matches) {
|
|
30025
30148
|
if (matches[2] !== void 0 && parsed.path && parsed.path[0] !== "/") {
|
|
30026
30149
|
return 'URI path must start with "/" when authority is present.';
|
|
@@ -30055,9 +30178,23 @@ var require_fast_uri = __commonJS({
|
|
|
30055
30178
|
parsed.error = "URI authority must not contain a literal backslash.";
|
|
30056
30179
|
malformedAuthorityOrPort = true;
|
|
30057
30180
|
}
|
|
30181
|
+
const introducerMatch = uri.match(AUTHORITY_INTRODUCER_REGION);
|
|
30182
|
+
if (introducerMatch !== null) {
|
|
30183
|
+
const region = introducerMatch[1];
|
|
30184
|
+
const normalizedRegion = region.replace(/[\t\n\r]/g, "");
|
|
30185
|
+
if (normalizedRegion.length >= 2) {
|
|
30186
|
+
if (normalizedRegion.slice(0, 2) !== "//") {
|
|
30187
|
+
parsed.error = parsed.error || "URI authority must not contain a literal backslash.";
|
|
30188
|
+
malformedAuthorityOrPort = true;
|
|
30189
|
+
} else if (region.length !== normalizedRegion.length) {
|
|
30190
|
+
parsed.error = parsed.error || "URI authority introducer must not contain whitespace.";
|
|
30191
|
+
malformedAuthorityOrPort = true;
|
|
30192
|
+
}
|
|
30193
|
+
}
|
|
30194
|
+
}
|
|
30058
30195
|
const matches = uri.match(URI_PARSE);
|
|
30059
30196
|
if (matches) {
|
|
30060
|
-
parsed.scheme = matches[1];
|
|
30197
|
+
parsed.scheme = matches[1] === void 0 ? void 0 : matches[1].toLowerCase();
|
|
30061
30198
|
parsed.userinfo = matches[3];
|
|
30062
30199
|
parsed.host = matches[4];
|
|
30063
30200
|
parsed.port = parseInt(matches[5], 10);
|
|
@@ -30116,12 +30253,11 @@ var require_fast_uri = __commonJS({
|
|
|
30116
30253
|
if (parsed.path) {
|
|
30117
30254
|
parsed.path = normalizePathEncoding(parsed.path);
|
|
30118
30255
|
}
|
|
30256
|
+
if (parsed.query) {
|
|
30257
|
+
parsed.query = normalizeQueryFragmentEncoding(parsed.query);
|
|
30258
|
+
}
|
|
30119
30259
|
if (parsed.fragment) {
|
|
30120
|
-
|
|
30121
|
-
parsed.fragment = encodeURI(decodeURIComponent(parsed.fragment));
|
|
30122
|
-
} catch {
|
|
30123
|
-
parsed.error = parsed.error || "URI malformed";
|
|
30124
|
-
}
|
|
30260
|
+
parsed.fragment = normalizeQueryFragmentEncoding(parsed.fragment);
|
|
30125
30261
|
}
|
|
30126
30262
|
}
|
|
30127
30263
|
if (schemeHandler && schemeHandler.parse) {
|
|
@@ -65406,9 +65542,9 @@ var require_diff = __commonJS({
|
|
|
65406
65542
|
}
|
|
65407
65543
|
});
|
|
65408
65544
|
|
|
65409
|
-
//
|
|
65545
|
+
// node_modules/@coderifts/sdk/dist/cjs/errors.js
|
|
65410
65546
|
var require_errors5 = __commonJS({
|
|
65411
|
-
"
|
|
65547
|
+
"node_modules/@coderifts/sdk/dist/cjs/errors.js"(exports2) {
|
|
65412
65548
|
"use strict";
|
|
65413
65549
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
65414
65550
|
exports2.AuthError = exports2.RateLimitError = exports2.TimeoutError = exports2.ApiError = exports2.CodeRiftsError = void 0;
|
|
@@ -65458,9 +65594,9 @@ var require_errors5 = __commonJS({
|
|
|
65458
65594
|
}
|
|
65459
65595
|
});
|
|
65460
65596
|
|
|
65461
|
-
//
|
|
65597
|
+
// node_modules/@coderifts/sdk/dist/cjs/client.js
|
|
65462
65598
|
var require_client = __commonJS({
|
|
65463
|
-
"
|
|
65599
|
+
"node_modules/@coderifts/sdk/dist/cjs/client.js"(exports2) {
|
|
65464
65600
|
"use strict";
|
|
65465
65601
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
65466
65602
|
exports2.CodeRifts = void 0;
|
|
@@ -65708,9 +65844,9 @@ var require_client = __commonJS({
|
|
|
65708
65844
|
}
|
|
65709
65845
|
});
|
|
65710
65846
|
|
|
65711
|
-
//
|
|
65847
|
+
// node_modules/@coderifts/sdk/dist/cjs/decision.js
|
|
65712
65848
|
var require_decision = __commonJS({
|
|
65713
|
-
"
|
|
65849
|
+
"node_modules/@coderifts/sdk/dist/cjs/decision.js"(exports2) {
|
|
65714
65850
|
"use strict";
|
|
65715
65851
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
65716
65852
|
exports2.readDecision = readDecision;
|
|
@@ -65756,9 +65892,9 @@ var require_decision = __commonJS({
|
|
|
65756
65892
|
}
|
|
65757
65893
|
});
|
|
65758
65894
|
|
|
65759
|
-
//
|
|
65895
|
+
// node_modules/@coderifts/sdk/dist/cjs/index.js
|
|
65760
65896
|
var require_cjs3 = __commonJS({
|
|
65761
|
-
"
|
|
65897
|
+
"node_modules/@coderifts/sdk/dist/cjs/index.js"(exports2) {
|
|
65762
65898
|
"use strict";
|
|
65763
65899
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
65764
65900
|
exports2.readDecision = exports2.AuthError = exports2.RateLimitError = exports2.TimeoutError = exports2.ApiError = exports2.CodeRiftsError = exports2.CodeRifts = void 0;
|
|
@@ -65789,9 +65925,9 @@ var require_cjs3 = __commonJS({
|
|
|
65789
65925
|
}
|
|
65790
65926
|
});
|
|
65791
65927
|
|
|
65792
|
-
//
|
|
65928
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/detector.js
|
|
65793
65929
|
var require_detector = __commonJS({
|
|
65794
|
-
"
|
|
65930
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/detector.js"(exports2) {
|
|
65795
65931
|
"use strict";
|
|
65796
65932
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
65797
65933
|
exports2.builtinDetector = exports2.DETECTOR_VERSION = void 0;
|
|
@@ -66322,9 +66458,9 @@ var require_detector = __commonJS({
|
|
|
66322
66458
|
}
|
|
66323
66459
|
});
|
|
66324
66460
|
|
|
66325
|
-
//
|
|
66461
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/receipt-binding.js
|
|
66326
66462
|
var require_receipt_binding = __commonJS({
|
|
66327
|
-
"
|
|
66463
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/receipt-binding.js"(exports2) {
|
|
66328
66464
|
"use strict";
|
|
66329
66465
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66330
66466
|
exports2.canonicalJson = canonicalJson;
|
|
@@ -66396,9 +66532,9 @@ var require_receipt_binding = __commonJS({
|
|
|
66396
66532
|
}
|
|
66397
66533
|
});
|
|
66398
66534
|
|
|
66399
|
-
//
|
|
66535
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/enforcement-gate.js
|
|
66400
66536
|
var require_enforcement_gate = __commonJS({
|
|
66401
|
-
"
|
|
66537
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/enforcement-gate.js"(exports2) {
|
|
66402
66538
|
"use strict";
|
|
66403
66539
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66404
66540
|
exports2.computeArtifactDigest = computeArtifactDigest;
|
|
@@ -66472,9 +66608,9 @@ var require_enforcement_gate = __commonJS({
|
|
|
66472
66608
|
}
|
|
66473
66609
|
});
|
|
66474
66610
|
|
|
66475
|
-
//
|
|
66611
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/guard.js
|
|
66476
66612
|
var require_guard = __commonJS({
|
|
66477
|
-
"
|
|
66613
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/guard.js"(exports2) {
|
|
66478
66614
|
"use strict";
|
|
66479
66615
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66480
66616
|
exports2.guardToolCall = guardToolCall;
|
|
@@ -66494,6 +66630,25 @@ var require_guard = __commonJS({
|
|
|
66494
66630
|
}
|
|
66495
66631
|
}
|
|
66496
66632
|
}
|
|
66633
|
+
function resolvePreviousReceipt(config) {
|
|
66634
|
+
const pr = config.previousReceipt;
|
|
66635
|
+
if (pr === void 0 || pr === null)
|
|
66636
|
+
return void 0;
|
|
66637
|
+
let raw;
|
|
66638
|
+
if (typeof pr === "function") {
|
|
66639
|
+
try {
|
|
66640
|
+
raw = pr();
|
|
66641
|
+
} catch {
|
|
66642
|
+
return void 0;
|
|
66643
|
+
}
|
|
66644
|
+
} else {
|
|
66645
|
+
raw = pr;
|
|
66646
|
+
}
|
|
66647
|
+
if (typeof raw !== "string")
|
|
66648
|
+
return void 0;
|
|
66649
|
+
const s = raw.trim();
|
|
66650
|
+
return s.length > 0 ? s : void 0;
|
|
66651
|
+
}
|
|
66497
66652
|
function fingerprint(call) {
|
|
66498
66653
|
const canon = JSON.stringify({ toolName: call.toolName, arguments: call.arguments, artifacts: call.artifacts, filesTouched: call.filesTouched, diff: call.diff });
|
|
66499
66654
|
return "sha256:" + (0, node_crypto_1.createHash)("sha256").update(canon).digest("hex");
|
|
@@ -66663,7 +66818,7 @@ var require_guard = __commonJS({
|
|
|
66663
66818
|
const request = {
|
|
66664
66819
|
artifacts: detection.artifacts,
|
|
66665
66820
|
context: { operation: config.operation ?? "tool_call", environment: config.environment, audience: config.audience },
|
|
66666
|
-
previous_receipt:
|
|
66821
|
+
previous_receipt: resolvePreviousReceipt(config),
|
|
66667
66822
|
idempotency_key: void 0
|
|
66668
66823
|
};
|
|
66669
66824
|
const cap = config.maxPayloadBytes ?? 1e6;
|
|
@@ -66800,9 +66955,84 @@ var require_guard = __commonJS({
|
|
|
66800
66955
|
}
|
|
66801
66956
|
});
|
|
66802
66957
|
|
|
66803
|
-
//
|
|
66958
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/receipt-chain.js
|
|
66959
|
+
var require_receipt_chain = __commonJS({
|
|
66960
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/receipt-chain.js"(exports2) {
|
|
66961
|
+
"use strict";
|
|
66962
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66963
|
+
exports2.RECEIPT_PREV_NULL = void 0;
|
|
66964
|
+
exports2.previousReceiptCommitment = previousReceiptCommitment;
|
|
66965
|
+
exports2.decodeReceiptBodyPrev = decodeReceiptBodyPrev;
|
|
66966
|
+
exports2.verifyReceiptChainLinkage = verifyReceiptChainLinkage;
|
|
66967
|
+
var node_crypto_1 = require("node:crypto");
|
|
66968
|
+
exports2.RECEIPT_PREV_NULL = "null";
|
|
66969
|
+
function previousReceiptCommitment(previousToken) {
|
|
66970
|
+
return "sha256:" + (0, node_crypto_1.createHash)("sha256").update(previousToken, "utf8").digest("hex");
|
|
66971
|
+
}
|
|
66972
|
+
function decodeReceiptBodyPrev(token) {
|
|
66973
|
+
if (typeof token !== "string" || token.length === 0)
|
|
66974
|
+
return null;
|
|
66975
|
+
const parts = token.split(".");
|
|
66976
|
+
if (parts.length !== 2 || !parts[0] || !parts[1])
|
|
66977
|
+
return null;
|
|
66978
|
+
try {
|
|
66979
|
+
const json = Buffer.from(parts[0], "base64url").toString("utf8");
|
|
66980
|
+
const body = JSON.parse(json);
|
|
66981
|
+
if (typeof body.prev !== "string")
|
|
66982
|
+
return null;
|
|
66983
|
+
return { prev: body.prev };
|
|
66984
|
+
} catch {
|
|
66985
|
+
return null;
|
|
66986
|
+
}
|
|
66987
|
+
}
|
|
66988
|
+
function verifyReceiptChainLinkage(tokens) {
|
|
66989
|
+
const length = tokens.length;
|
|
66990
|
+
if (length === 0) {
|
|
66991
|
+
return { ok: true, length: 0 };
|
|
66992
|
+
}
|
|
66993
|
+
for (let i = 0; i < length; i++) {
|
|
66994
|
+
const decoded = decodeReceiptBodyPrev(tokens[i]);
|
|
66995
|
+
if (!decoded) {
|
|
66996
|
+
return {
|
|
66997
|
+
ok: false,
|
|
66998
|
+
length,
|
|
66999
|
+
failedAt: i,
|
|
67000
|
+
reason: "malformed_token"
|
|
67001
|
+
};
|
|
67002
|
+
}
|
|
67003
|
+
if (i === 0) {
|
|
67004
|
+
if (decoded.prev !== exports2.RECEIPT_PREV_NULL) {
|
|
67005
|
+
return {
|
|
67006
|
+
ok: false,
|
|
67007
|
+
length,
|
|
67008
|
+
failedAt: 0,
|
|
67009
|
+
reason: "unexpected_predecessor",
|
|
67010
|
+
expected: exports2.RECEIPT_PREV_NULL,
|
|
67011
|
+
actual: decoded.prev
|
|
67012
|
+
};
|
|
67013
|
+
}
|
|
67014
|
+
continue;
|
|
67015
|
+
}
|
|
67016
|
+
const expected = previousReceiptCommitment(tokens[i - 1]);
|
|
67017
|
+
if (decoded.prev !== expected) {
|
|
67018
|
+
return {
|
|
67019
|
+
ok: false,
|
|
67020
|
+
length,
|
|
67021
|
+
failedAt: i,
|
|
67022
|
+
reason: "broken_link",
|
|
67023
|
+
expected,
|
|
67024
|
+
actual: decoded.prev
|
|
67025
|
+
};
|
|
67026
|
+
}
|
|
67027
|
+
}
|
|
67028
|
+
return { ok: true, length };
|
|
67029
|
+
}
|
|
67030
|
+
}
|
|
67031
|
+
});
|
|
67032
|
+
|
|
67033
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/session-taint.js
|
|
66804
67034
|
var require_session_taint = __commonJS({
|
|
66805
|
-
"
|
|
67035
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/session-taint.js"(exports2) {
|
|
66806
67036
|
"use strict";
|
|
66807
67037
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
66808
67038
|
exports2.SessionTaintTracker = exports2.SESSION_TAINT_VERSION = void 0;
|
|
@@ -67107,9 +67337,9 @@ var require_session_taint = __commonJS({
|
|
|
67107
67337
|
}
|
|
67108
67338
|
});
|
|
67109
67339
|
|
|
67110
|
-
//
|
|
67340
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/resolver-yaml.js
|
|
67111
67341
|
var require_resolver_yaml = __commonJS({
|
|
67112
|
-
"
|
|
67342
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/resolver-yaml.js"(exports2) {
|
|
67113
67343
|
"use strict";
|
|
67114
67344
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
67115
67345
|
exports2.YamlLiteError = void 0;
|
|
@@ -67328,9 +67558,9 @@ var require_resolver_yaml = __commonJS({
|
|
|
67328
67558
|
}
|
|
67329
67559
|
});
|
|
67330
67560
|
|
|
67331
|
-
//
|
|
67561
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/resolver-glob.js
|
|
67332
67562
|
var require_resolver_glob = __commonJS({
|
|
67333
|
-
"
|
|
67563
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/resolver-glob.js"(exports2) {
|
|
67334
67564
|
"use strict";
|
|
67335
67565
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
67336
67566
|
exports2.globToRegExp = globToRegExp;
|
|
@@ -67383,11 +67613,12 @@ var require_resolver_glob = __commonJS({
|
|
|
67383
67613
|
}
|
|
67384
67614
|
});
|
|
67385
67615
|
|
|
67386
|
-
//
|
|
67616
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/artifact-resolver.js
|
|
67387
67617
|
var require_artifact_resolver = __commonJS({
|
|
67388
|
-
"
|
|
67618
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/artifact-resolver.js"(exports2) {
|
|
67389
67619
|
"use strict";
|
|
67390
67620
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
67621
|
+
exports2.classifyByName = classifyByName;
|
|
67391
67622
|
exports2.resolve = resolve;
|
|
67392
67623
|
var resolver_yaml_js_1 = require_resolver_yaml();
|
|
67393
67624
|
var resolver_glob_js_1 = require_resolver_glob();
|
|
@@ -67788,14 +68019,15 @@ var require_artifact_resolver = __commonJS({
|
|
|
67788
68019
|
}
|
|
67789
68020
|
});
|
|
67790
68021
|
|
|
67791
|
-
//
|
|
68022
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/tool-registry.js
|
|
67792
68023
|
var require_tool_registry = __commonJS({
|
|
67793
|
-
"
|
|
68024
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/tool-registry.js"(exports2) {
|
|
67794
68025
|
"use strict";
|
|
67795
68026
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
67796
68027
|
exports2.RegistryConstructionError = void 0;
|
|
67797
68028
|
exports2.guardToolRegistry = guardToolRegistry;
|
|
67798
68029
|
var guard_js_1 = require_guard();
|
|
68030
|
+
var artifact_resolver_js_1 = require_artifact_resolver();
|
|
67799
68031
|
var RegistryConstructionError = class extends Error {
|
|
67800
68032
|
code;
|
|
67801
68033
|
toolName;
|
|
@@ -67872,7 +68104,50 @@ var require_tool_registry = __commonJS({
|
|
|
67872
68104
|
}
|
|
67873
68105
|
}
|
|
67874
68106
|
function defaultBinder(tool, args) {
|
|
67875
|
-
|
|
68107
|
+
const d = { toolName: tool.name, arguments: args };
|
|
68108
|
+
if (!args || typeof args !== "object")
|
|
68109
|
+
return d;
|
|
68110
|
+
const a = args;
|
|
68111
|
+
if (Array.isArray(a.artifacts)) {
|
|
68112
|
+
d.artifacts = a.artifacts;
|
|
68113
|
+
return d;
|
|
68114
|
+
}
|
|
68115
|
+
const path = typeof a.path === "string" ? a.path : "";
|
|
68116
|
+
if (!path)
|
|
68117
|
+
return d;
|
|
68118
|
+
const type = (0, artifact_resolver_js_1.classifyByName)(path);
|
|
68119
|
+
if (!type)
|
|
68120
|
+
return d;
|
|
68121
|
+
const bothSides = (oldS, newS) => typeof oldS === "string" && oldS.length > 0 && typeof newS === "string" && newS.length > 0;
|
|
68122
|
+
if (Array.isArray(a.edits)) {
|
|
68123
|
+
const lifted = [];
|
|
68124
|
+
for (let i = 0; i < a.edits.length; i++) {
|
|
68125
|
+
const e = a.edits[i];
|
|
68126
|
+
if (!e || typeof e !== "object")
|
|
68127
|
+
continue;
|
|
68128
|
+
const er = e;
|
|
68129
|
+
if (!bothSides(er.old_string, er.new_string))
|
|
68130
|
+
continue;
|
|
68131
|
+
lifted.push({
|
|
68132
|
+
id: `${type}:${path}#${i}`,
|
|
68133
|
+
type,
|
|
68134
|
+
before: er.old_string,
|
|
68135
|
+
after: er.new_string
|
|
68136
|
+
});
|
|
68137
|
+
}
|
|
68138
|
+
if (lifted.length > 0)
|
|
68139
|
+
d.artifacts = lifted;
|
|
68140
|
+
return d;
|
|
68141
|
+
}
|
|
68142
|
+
if (bothSides(a.old_string, a.new_string)) {
|
|
68143
|
+
d.artifacts = [{
|
|
68144
|
+
id: `${type}:${path}`,
|
|
68145
|
+
type,
|
|
68146
|
+
before: a.old_string,
|
|
68147
|
+
after: a.new_string
|
|
68148
|
+
}];
|
|
68149
|
+
}
|
|
68150
|
+
return d;
|
|
67876
68151
|
}
|
|
67877
68152
|
var RAW_EXECUTORS = /* @__PURE__ */ new WeakMap();
|
|
67878
68153
|
function freezeTool(t) {
|
|
@@ -68025,9 +68300,9 @@ var require_tool_registry = __commonJS({
|
|
|
68025
68300
|
}
|
|
68026
68301
|
});
|
|
68027
68302
|
|
|
68028
|
-
//
|
|
68303
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/merge-gate.js
|
|
68029
68304
|
var require_merge_gate = __commonJS({
|
|
68030
|
-
"
|
|
68305
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/merge-gate.js"(exports2) {
|
|
68031
68306
|
"use strict";
|
|
68032
68307
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
68033
68308
|
exports2.gateDecision = gateDecision;
|
|
@@ -68121,6 +68396,13 @@ var require_merge_gate = __commonJS({
|
|
|
68121
68396
|
residual = "protection_advisory_only";
|
|
68122
68397
|
else
|
|
68123
68398
|
residual = "protection_not_configured";
|
|
68399
|
+
} else {
|
|
68400
|
+
if (protection.required_check_app_bound === true) {
|
|
68401
|
+
} else if (protection.required_check_app_bound === false) {
|
|
68402
|
+
residual = "required_check_app_not_bound";
|
|
68403
|
+
} else {
|
|
68404
|
+
residual = "required_check_app_binding_unknown";
|
|
68405
|
+
}
|
|
68124
68406
|
}
|
|
68125
68407
|
return {
|
|
68126
68408
|
merge_allowed: true,
|
|
@@ -68135,9 +68417,9 @@ var require_merge_gate = __commonJS({
|
|
|
68135
68417
|
}
|
|
68136
68418
|
});
|
|
68137
68419
|
|
|
68138
|
-
//
|
|
68420
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/deploy-gate.js
|
|
68139
68421
|
var require_deploy_gate = __commonJS({
|
|
68140
|
-
"
|
|
68422
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/deploy-gate.js"(exports2) {
|
|
68141
68423
|
"use strict";
|
|
68142
68424
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
68143
68425
|
exports2.deployGate = deployGate;
|
|
@@ -68176,8 +68458,8 @@ var require_deploy_gate = __commonJS({
|
|
|
68176
68458
|
const enf = rc.enforcement || { enforcement: "UNKNOWN", bypass_possible: true };
|
|
68177
68459
|
const enforcement_state = enf.enforcement;
|
|
68178
68460
|
const opRequired = rc.operation ?? "deploy";
|
|
68179
|
-
const requireEnv = rc.require_bound_environment !== false;
|
|
68180
|
-
const requireArt = rc.require_bound_artifact !== false;
|
|
68461
|
+
const requireEnv = enforcement_state === "ENFORCING" || rc.require_bound_environment !== false;
|
|
68462
|
+
const requireArt = enforcement_state === "ENFORCING" || rc.require_bound_artifact !== false;
|
|
68181
68463
|
const allowPending = input.allowPending ?? rc.allowPending ?? false;
|
|
68182
68464
|
const allowWarnDeploy = input.allowWarnDeploy ?? rc.allowWarnDeploy ?? false;
|
|
68183
68465
|
const allowPrefix = input.allowPrefixCompare ?? rc.allowPrefixCompare ?? false;
|
|
@@ -68260,9 +68542,9 @@ var require_deploy_gate = __commonJS({
|
|
|
68260
68542
|
}
|
|
68261
68543
|
});
|
|
68262
68544
|
|
|
68263
|
-
//
|
|
68545
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/coverage-report.js
|
|
68264
68546
|
var require_coverage_report = __commonJS({
|
|
68265
|
-
"
|
|
68547
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/coverage-report.js"(exports2) {
|
|
68266
68548
|
"use strict";
|
|
68267
68549
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
68268
68550
|
exports2.coverageReport = coverageReport;
|
|
@@ -68422,12 +68704,133 @@ var require_coverage_report = __commonJS({
|
|
|
68422
68704
|
}
|
|
68423
68705
|
});
|
|
68424
68706
|
|
|
68425
|
-
//
|
|
68707
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/with-coderifts.js
|
|
68708
|
+
var require_with_coderifts = __commonJS({
|
|
68709
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/with-coderifts.js"(exports2) {
|
|
68710
|
+
"use strict";
|
|
68711
|
+
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
68712
|
+
exports2.withCodeRifts = withCodeRifts;
|
|
68713
|
+
var tool_registry_js_1 = require_tool_registry();
|
|
68714
|
+
var COMPOSITION_CALL_POLICY_COMPLETE = false;
|
|
68715
|
+
var RESIDUAL_CALL_POLICY_INCOMPLETE = "composition_call_policy_incomplete";
|
|
68716
|
+
var RESIDUAL_FORCED_READONLY = "composition_forced_readonly_on_heuristic_mutator";
|
|
68717
|
+
var RESIDUAL_UNKNOWN_READONLY = "composition_unknown_treated_as_readonly";
|
|
68718
|
+
var COVERAGE_STRENGTH = {
|
|
68719
|
+
COMPLETE: 3,
|
|
68720
|
+
PARTIAL: 2,
|
|
68721
|
+
BYPASSED: 1,
|
|
68722
|
+
UNKNOWN: 0
|
|
68723
|
+
};
|
|
68724
|
+
function coverageRank(coverage) {
|
|
68725
|
+
return Object.prototype.hasOwnProperty.call(COVERAGE_STRENGTH, coverage) ? COVERAGE_STRENGTH[coverage] : void 0;
|
|
68726
|
+
}
|
|
68727
|
+
async function safeOnOutcome(onOutcome, payload) {
|
|
68728
|
+
try {
|
|
68729
|
+
await Promise.resolve(onOutcome(payload));
|
|
68730
|
+
} catch {
|
|
68731
|
+
}
|
|
68732
|
+
}
|
|
68733
|
+
function wrapGuardedForObservation(tool, onOutcome) {
|
|
68734
|
+
const innerExecute = tool.execute;
|
|
68735
|
+
const toolName = tool.name;
|
|
68736
|
+
const shell = {
|
|
68737
|
+
name: tool.name,
|
|
68738
|
+
description: tool.description,
|
|
68739
|
+
inputSchema: tool.inputSchema,
|
|
68740
|
+
meta: tool.meta,
|
|
68741
|
+
_coderifts: tool._coderifts,
|
|
68742
|
+
execute: async (args) => {
|
|
68743
|
+
const outcome = await innerExecute(args);
|
|
68744
|
+
await safeOnOutcome(onOutcome, {
|
|
68745
|
+
toolName,
|
|
68746
|
+
// Guarded execute always returns a GuardOutcome from guardToolCall; assert the type for callers.
|
|
68747
|
+
outcome
|
|
68748
|
+
});
|
|
68749
|
+
return outcome;
|
|
68750
|
+
}
|
|
68751
|
+
};
|
|
68752
|
+
if (!Object.isFrozen(shell._coderifts))
|
|
68753
|
+
Object.freeze(shell._coderifts);
|
|
68754
|
+
return Object.freeze(shell);
|
|
68755
|
+
}
|
|
68756
|
+
function withCodeRifts(input) {
|
|
68757
|
+
if (!input || typeof input !== "object") {
|
|
68758
|
+
throw new Error("withCodeRifts: input object is required");
|
|
68759
|
+
}
|
|
68760
|
+
const problems = [];
|
|
68761
|
+
if (typeof input.operation !== "string" || input.operation.trim() === "") {
|
|
68762
|
+
problems.push("`operation` is required and must be a non-empty string (receipts bind to an operation; merge != deploy, so there is no safe default)");
|
|
68763
|
+
}
|
|
68764
|
+
if (input.client == null) {
|
|
68765
|
+
problems.push("`client` is required at construction (guardToolRegistry needs config.guard.client to wrap any mutating tool)");
|
|
68766
|
+
}
|
|
68767
|
+
if (input.requireCoverage !== void 0 && coverageRank(input.requireCoverage) === void 0) {
|
|
68768
|
+
problems.push(`\`requireCoverage\` must be one of COMPLETE | PARTIAL | BYPASSED | UNKNOWN (got ${JSON.stringify(input.requireCoverage)})`);
|
|
68769
|
+
}
|
|
68770
|
+
if (problems.length > 0) {
|
|
68771
|
+
throw new Error(`withCodeRifts: construction aborted \u2014 ${problems.length} condition(s):
|
|
68772
|
+
` + problems.map((p) => ` - ${p}`).join("\n"));
|
|
68773
|
+
}
|
|
68774
|
+
const reg = input.registry ?? {};
|
|
68775
|
+
const guard = { client: input.client, operation: input.operation };
|
|
68776
|
+
if (input.onEvent !== void 0) {
|
|
68777
|
+
guard.onEvent = input.onEvent;
|
|
68778
|
+
}
|
|
68779
|
+
if (input.previousReceipt !== void 0) {
|
|
68780
|
+
guard.previousReceipt = input.previousReceipt;
|
|
68781
|
+
}
|
|
68782
|
+
const config = {
|
|
68783
|
+
guard,
|
|
68784
|
+
unknownToolPolicy: reg.unknownToolPolicy ?? "mutating",
|
|
68785
|
+
classify: reg.classify,
|
|
68786
|
+
binders: reg.binders,
|
|
68787
|
+
forceReadonly: reg.forceReadonly,
|
|
68788
|
+
failOnUnguardedMutator: reg.failOnUnguardedMutator
|
|
68789
|
+
};
|
|
68790
|
+
const { tools, report } = (0, tool_registry_js_1.guardToolRegistry)(input.tools, config);
|
|
68791
|
+
if (input.requireCoverage !== void 0) {
|
|
68792
|
+
const requiredRank = coverageRank(input.requireCoverage);
|
|
68793
|
+
const actualRank = coverageRank(report.coverage) ?? -1;
|
|
68794
|
+
if (requiredRank !== void 0 && actualRank < requiredRank) {
|
|
68795
|
+
throw new Error(`withCodeRifts: requireCoverage not met \u2014 registry coverage '${report.coverage}' is weaker than required '${input.requireCoverage}' (strength ordering COMPLETE > PARTIAL > BYPASSED > UNKNOWN). requireCoverage constrains the REGISTRY tool-boundary surface ONLY; a green construction here is NOT a product-level runtime-inescapability guarantee \u2014 composition_assurance.inescapable_runtime stays false until receipt carry-forward and a freshness-safe prior for write-style calls land.`);
|
|
68796
|
+
}
|
|
68797
|
+
}
|
|
68798
|
+
const compositionInescapableRuntime = report.claim.inescapable_runtime && COMPOSITION_CALL_POLICY_COMPLETE;
|
|
68799
|
+
const residuals = [RESIDUAL_CALL_POLICY_INCOMPLETE];
|
|
68800
|
+
if (report.warnings.some((w) => w.startsWith("force_readonly_on_mutator_heuristic:"))) {
|
|
68801
|
+
residuals.push(RESIDUAL_FORCED_READONLY);
|
|
68802
|
+
}
|
|
68803
|
+
if (report.warnings.includes("unknown_treated_as_readonly")) {
|
|
68804
|
+
residuals.push(RESIDUAL_UNKNOWN_READONLY);
|
|
68805
|
+
}
|
|
68806
|
+
const composition_assurance = {
|
|
68807
|
+
coverage: "PARTIAL",
|
|
68808
|
+
inescapable_runtime: compositionInescapableRuntime,
|
|
68809
|
+
residuals
|
|
68810
|
+
};
|
|
68811
|
+
let toolsOut = tools;
|
|
68812
|
+
if (input.onOutcome) {
|
|
68813
|
+
const onOutcome = input.onOutcome;
|
|
68814
|
+
toolsOut = Object.freeze(tools.map((t) => t._coderifts.guarded ? wrapGuardedForObservation(t, onOutcome) : t));
|
|
68815
|
+
}
|
|
68816
|
+
const result = {
|
|
68817
|
+
tools: toolsOut,
|
|
68818
|
+
registry_report: report,
|
|
68819
|
+
composition_assurance
|
|
68820
|
+
};
|
|
68821
|
+
if (input.repository !== void 0)
|
|
68822
|
+
result.repository = input.repository;
|
|
68823
|
+
return result;
|
|
68824
|
+
}
|
|
68825
|
+
}
|
|
68826
|
+
});
|
|
68827
|
+
|
|
68828
|
+
// node_modules/@coderifts/agent-guard/dist/cjs/index.js
|
|
68426
68829
|
var require_cjs4 = __commonJS({
|
|
68427
|
-
"
|
|
68830
|
+
"node_modules/@coderifts/agent-guard/dist/cjs/index.js"(exports2) {
|
|
68428
68831
|
"use strict";
|
|
68429
68832
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
68430
|
-
exports2.coverageReport = exports2.deployGate = exports2.gateDecision = exports2.RegistryConstructionError = exports2.guardToolRegistry = exports2.globToRegExp = exports2.matchGlob = exports2.resolveArtifacts = exports2.deriveKeySignal = exports2.pathClass = exports2.classifyCommand = exports2.projectState = exports2.emptySessionState = exports2.computeTainted = exports2.evaluate = exports2.updateSession = exports2.SESSION_TAINT_VERSION = exports2.SessionTaintTracker = exports2.readDecision = exports2.computeBundleFingerprint = exports2.computeArtifactDigest = exports2.evaluateEnvelope = exports2.canonicalJson = exports2.computeBodyHash = exports2.bindReceiptToEnvelope = exports2.DETECTOR_VERSION = exports2.builtinDetector = exports2.guardToolCall = void 0;
|
|
68833
|
+
exports2.withCodeRifts = exports2.coverageReport = exports2.deployGate = exports2.gateDecision = exports2.RegistryConstructionError = exports2.guardToolRegistry = exports2.globToRegExp = exports2.matchGlob = exports2.classifyByName = exports2.resolveArtifacts = exports2.deriveKeySignal = exports2.pathClass = exports2.classifyCommand = exports2.projectState = exports2.emptySessionState = exports2.computeTainted = exports2.evaluate = exports2.updateSession = exports2.SESSION_TAINT_VERSION = exports2.SessionTaintTracker = exports2.readDecision = exports2.computeBundleFingerprint = exports2.computeArtifactDigest = exports2.evaluateEnvelope = exports2.RECEIPT_PREV_NULL = exports2.decodeReceiptBodyPrev = exports2.previousReceiptCommitment = exports2.verifyReceiptChainLinkage = exports2.canonicalJson = exports2.computeBodyHash = exports2.bindReceiptToEnvelope = exports2.DETECTOR_VERSION = exports2.builtinDetector = exports2.guardToolCall = void 0;
|
|
68431
68834
|
var guard_js_1 = require_guard();
|
|
68432
68835
|
Object.defineProperty(exports2, "guardToolCall", { enumerable: true, get: function() {
|
|
68433
68836
|
return guard_js_1.guardToolCall;
|
|
@@ -68449,6 +68852,19 @@ var require_cjs4 = __commonJS({
|
|
|
68449
68852
|
Object.defineProperty(exports2, "canonicalJson", { enumerable: true, get: function() {
|
|
68450
68853
|
return receipt_binding_js_1.canonicalJson;
|
|
68451
68854
|
} });
|
|
68855
|
+
var receipt_chain_js_1 = require_receipt_chain();
|
|
68856
|
+
Object.defineProperty(exports2, "verifyReceiptChainLinkage", { enumerable: true, get: function() {
|
|
68857
|
+
return receipt_chain_js_1.verifyReceiptChainLinkage;
|
|
68858
|
+
} });
|
|
68859
|
+
Object.defineProperty(exports2, "previousReceiptCommitment", { enumerable: true, get: function() {
|
|
68860
|
+
return receipt_chain_js_1.previousReceiptCommitment;
|
|
68861
|
+
} });
|
|
68862
|
+
Object.defineProperty(exports2, "decodeReceiptBodyPrev", { enumerable: true, get: function() {
|
|
68863
|
+
return receipt_chain_js_1.decodeReceiptBodyPrev;
|
|
68864
|
+
} });
|
|
68865
|
+
Object.defineProperty(exports2, "RECEIPT_PREV_NULL", { enumerable: true, get: function() {
|
|
68866
|
+
return receipt_chain_js_1.RECEIPT_PREV_NULL;
|
|
68867
|
+
} });
|
|
68452
68868
|
var enforcement_gate_js_1 = require_enforcement_gate();
|
|
68453
68869
|
Object.defineProperty(exports2, "evaluateEnvelope", { enumerable: true, get: function() {
|
|
68454
68870
|
return enforcement_gate_js_1.evaluateEnvelope;
|
|
@@ -68498,6 +68914,9 @@ var require_cjs4 = __commonJS({
|
|
|
68498
68914
|
Object.defineProperty(exports2, "resolveArtifacts", { enumerable: true, get: function() {
|
|
68499
68915
|
return artifact_resolver_js_1.resolve;
|
|
68500
68916
|
} });
|
|
68917
|
+
Object.defineProperty(exports2, "classifyByName", { enumerable: true, get: function() {
|
|
68918
|
+
return artifact_resolver_js_1.classifyByName;
|
|
68919
|
+
} });
|
|
68501
68920
|
var resolver_glob_js_1 = require_resolver_glob();
|
|
68502
68921
|
Object.defineProperty(exports2, "matchGlob", { enumerable: true, get: function() {
|
|
68503
68922
|
return resolver_glob_js_1.matchGlob;
|
|
@@ -68524,6 +68943,10 @@ var require_cjs4 = __commonJS({
|
|
|
68524
68943
|
Object.defineProperty(exports2, "coverageReport", { enumerable: true, get: function() {
|
|
68525
68944
|
return coverage_report_js_1.coverageReport;
|
|
68526
68945
|
} });
|
|
68946
|
+
var with_coderifts_js_1 = require_with_coderifts();
|
|
68947
|
+
Object.defineProperty(exports2, "withCodeRifts", { enumerable: true, get: function() {
|
|
68948
|
+
return with_coderifts_js_1.withCodeRifts;
|
|
68949
|
+
} });
|
|
68527
68950
|
}
|
|
68528
68951
|
});
|
|
68529
68952
|
|
|
@@ -68556,12 +68979,15 @@ var require_deploy_gate2 = __commonJS({
|
|
|
68556
68979
|
attestation_source: "cli_flag"
|
|
68557
68980
|
};
|
|
68558
68981
|
}
|
|
68559
|
-
function deployReportResiduals(state,
|
|
68982
|
+
function deployReportResiduals(state, enforcement_inescapable, enforcement, change_set_rebound) {
|
|
68560
68983
|
const out = [];
|
|
68561
|
-
if (state
|
|
68984
|
+
if (state !== "success") return out;
|
|
68985
|
+
if (enforcement_inescapable !== true) {
|
|
68562
68986
|
if (enforcement === "ENFORCING") out.push("bypass_open");
|
|
68563
68987
|
else if (enforcement === "ADVISORY") out.push("deploy_gate_advisory");
|
|
68564
68988
|
else if (enforcement === "ABSENT") out.push("deploy_path_ungated");
|
|
68989
|
+
} else if (change_set_rebound !== true) {
|
|
68990
|
+
out.push("change_set_not_rebound");
|
|
68565
68991
|
}
|
|
68566
68992
|
return out;
|
|
68567
68993
|
}
|
|
@@ -68589,19 +69015,24 @@ var require_deploy_gate2 = __commonJS({
|
|
|
68589
69015
|
bypass_possible: !(observed_cd_enforcement && observed_cd_enforcement.bypass_possible === false)
|
|
68590
69016
|
}
|
|
68591
69017
|
};
|
|
69018
|
+
let change_set_rebound = false;
|
|
68592
69019
|
if (attested_enforcement === "ENFORCING") {
|
|
68593
|
-
if (expected_fingerprint != null)
|
|
69020
|
+
if (expected_fingerprint != null) {
|
|
69021
|
+
requiredContext.expected_fingerprint = expected_fingerprint;
|
|
69022
|
+
change_set_rebound = true;
|
|
69023
|
+
}
|
|
68594
69024
|
if (expected_body_hash != null) requiredContext.expected_body_hash = expected_body_hash;
|
|
68595
69025
|
}
|
|
68596
69026
|
const gate = deployGate({ deployTarget: { environment, artifact_id }, receipt, requiredContext });
|
|
68597
|
-
const
|
|
69027
|
+
const enforcement_inescapable = gate.inescapable_deploy === true;
|
|
69028
|
+
const inescapable_deploy = enforcement_inescapable && change_set_rebound === true;
|
|
68598
69029
|
return {
|
|
68599
69030
|
deploy_check_status: gate.state,
|
|
68600
69031
|
reason: gate.reason,
|
|
68601
69032
|
must_re_preflight: REPAIRABLE.has(gate.reason),
|
|
68602
69033
|
attested_enforcement,
|
|
68603
69034
|
gate: { deploy_allowed: gate.deploy_allowed, reason: gate.reason, inescapable_deploy },
|
|
68604
|
-
report_residuals: deployReportResiduals(gate.state,
|
|
69035
|
+
report_residuals: deployReportResiduals(gate.state, enforcement_inescapable, attested_enforcement, change_set_rebound),
|
|
68605
69036
|
coverage_deploy_input: deployCoverageInput(attested_enforcement, inescapable_deploy)
|
|
68606
69037
|
};
|
|
68607
69038
|
}
|
|
@@ -68667,6 +69098,1004 @@ var require_deploy_gate2 = __commonJS({
|
|
|
68667
69098
|
}
|
|
68668
69099
|
});
|
|
68669
69100
|
|
|
69101
|
+
// src/commands/publish-gate.js
|
|
69102
|
+
var require_publish_gate = __commonJS({
|
|
69103
|
+
"src/commands/publish-gate.js"(exports2, module2) {
|
|
69104
|
+
"use strict";
|
|
69105
|
+
var fs = require("fs");
|
|
69106
|
+
var path = require("path");
|
|
69107
|
+
var { execFileSync } = require("child_process");
|
|
69108
|
+
var chalk = require_source();
|
|
69109
|
+
var { getApiKey } = require_config();
|
|
69110
|
+
var { cloudDiff } = require_cloud();
|
|
69111
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
69112
|
+
var ZERO_SHA = "0000000000000000000000000000000000000000";
|
|
69113
|
+
var CLOSED_ACTIONS = /* @__PURE__ */ new Set([
|
|
69114
|
+
"CONTINUE",
|
|
69115
|
+
"CONTINUE_WITH_MONITORING",
|
|
69116
|
+
"REQUEST_APPROVAL",
|
|
69117
|
+
"STOP"
|
|
69118
|
+
]);
|
|
69119
|
+
var PERMIT_ACTIONS = /* @__PURE__ */ new Set(["CONTINUE", "CONTINUE_WITH_MONITORING"]);
|
|
69120
|
+
function defaultGit(args, cwd) {
|
|
69121
|
+
return execFileSync("git", args, {
|
|
69122
|
+
cwd: cwd || process.cwd(),
|
|
69123
|
+
encoding: "utf8",
|
|
69124
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
69125
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
69126
|
+
}).trim();
|
|
69127
|
+
}
|
|
69128
|
+
function gitShow(ref, filePath, { gitImpl = defaultGit, cwd } = {}) {
|
|
69129
|
+
try {
|
|
69130
|
+
const out = gitImpl(["show", `${ref}:${filePath}`], cwd);
|
|
69131
|
+
return { ok: true, content: out == null ? "" : String(out) };
|
|
69132
|
+
} catch (err) {
|
|
69133
|
+
const msg = err && err.stderr ? String(err.stderr) : err && err.message || String(err);
|
|
69134
|
+
return {
|
|
69135
|
+
ok: false,
|
|
69136
|
+
code: "GIT_ERROR",
|
|
69137
|
+
message: `git error reading ${ref}:${filePath}: ${msg.slice(0, 300)}`
|
|
69138
|
+
};
|
|
69139
|
+
}
|
|
69140
|
+
}
|
|
69141
|
+
function readPackageVersion(cwd, readFile = fs.readFileSync) {
|
|
69142
|
+
const pkgPath = path.join(cwd || process.cwd(), "package.json");
|
|
69143
|
+
try {
|
|
69144
|
+
const raw = readFile(pkgPath, "utf8");
|
|
69145
|
+
const pkg2 = JSON.parse(raw);
|
|
69146
|
+
return pkg2 && typeof pkg2.version === "string" ? pkg2.version : null;
|
|
69147
|
+
} catch {
|
|
69148
|
+
return null;
|
|
69149
|
+
}
|
|
69150
|
+
}
|
|
69151
|
+
function resolveBeforeSpec(specPath, {
|
|
69152
|
+
gitImpl = defaultGit,
|
|
69153
|
+
cwd = process.cwd(),
|
|
69154
|
+
readFile = fs.readFileSync,
|
|
69155
|
+
packageVersion = null
|
|
69156
|
+
} = {}) {
|
|
69157
|
+
const version = packageVersion != null ? packageVersion : readPackageVersion(cwd, readFile);
|
|
69158
|
+
const tried = [];
|
|
69159
|
+
if (version) {
|
|
69160
|
+
const tags = [`v${version}`, version];
|
|
69161
|
+
for (const tag of tags) {
|
|
69162
|
+
tried.push(`tag:${tag}`);
|
|
69163
|
+
try {
|
|
69164
|
+
gitImpl(["rev-parse", "--verify", `${tag}^{commit}`], cwd);
|
|
69165
|
+
} catch {
|
|
69166
|
+
continue;
|
|
69167
|
+
}
|
|
69168
|
+
const shown = gitShow(tag, specPath, { gitImpl, cwd });
|
|
69169
|
+
if (!shown.ok) {
|
|
69170
|
+
return {
|
|
69171
|
+
ok: false,
|
|
69172
|
+
code: shown.code || "GIT_ERROR",
|
|
69173
|
+
message: shown.message,
|
|
69174
|
+
tried
|
|
69175
|
+
};
|
|
69176
|
+
}
|
|
69177
|
+
if (shown.content.trim() === "") {
|
|
69178
|
+
return {
|
|
69179
|
+
ok: false,
|
|
69180
|
+
code: "EMPTY_BEFORE",
|
|
69181
|
+
message: `Empty contract artifact at tag ${tag}:${specPath}. Refusing to treat empty before as NEW_ARTIFACT (would false-pass enforce).`,
|
|
69182
|
+
tried
|
|
69183
|
+
};
|
|
69184
|
+
}
|
|
69185
|
+
return { ok: true, content: shown.content, source: `tag:${tag}`, tried };
|
|
69186
|
+
}
|
|
69187
|
+
} else {
|
|
69188
|
+
tried.push("package.json:version (missing)");
|
|
69189
|
+
}
|
|
69190
|
+
const bases = ["origin/main", "origin/master", "main", "master"];
|
|
69191
|
+
for (const base of bases) {
|
|
69192
|
+
tried.push(`merge-base:${base}`);
|
|
69193
|
+
let mb;
|
|
69194
|
+
try {
|
|
69195
|
+
mb = gitImpl(["merge-base", "HEAD", base], cwd);
|
|
69196
|
+
} catch {
|
|
69197
|
+
continue;
|
|
69198
|
+
}
|
|
69199
|
+
if (!mb || mb === ZERO_SHA) continue;
|
|
69200
|
+
const shown = gitShow(mb, specPath, { gitImpl, cwd });
|
|
69201
|
+
if (!shown.ok) {
|
|
69202
|
+
return {
|
|
69203
|
+
ok: false,
|
|
69204
|
+
code: shown.code || "GIT_ERROR",
|
|
69205
|
+
message: shown.message,
|
|
69206
|
+
tried
|
|
69207
|
+
};
|
|
69208
|
+
}
|
|
69209
|
+
if (shown.content.trim() === "") {
|
|
69210
|
+
return {
|
|
69211
|
+
ok: false,
|
|
69212
|
+
code: "EMPTY_BEFORE",
|
|
69213
|
+
message: `Empty contract artifact at merge-base ${mb}:${specPath} (${base}). Refusing to treat empty before as NEW_ARTIFACT (would false-pass enforce).`,
|
|
69214
|
+
tried
|
|
69215
|
+
};
|
|
69216
|
+
}
|
|
69217
|
+
return {
|
|
69218
|
+
ok: true,
|
|
69219
|
+
content: shown.content,
|
|
69220
|
+
source: `merge-base:${base}@${mb.slice(0, 12)}`,
|
|
69221
|
+
tried
|
|
69222
|
+
};
|
|
69223
|
+
}
|
|
69224
|
+
return {
|
|
69225
|
+
ok: false,
|
|
69226
|
+
code: "BEFORE_UNRESOLVED",
|
|
69227
|
+
message: `Could not resolve a non-empty before-spec for ${specPath}. Tried: (a) git tag of package.json version, (b) merge-base with origin/main. Attempts: ${tried.join(", ")}. Fail-closed \u2014 will not publish without a baseline.`,
|
|
69228
|
+
tried
|
|
69229
|
+
};
|
|
69230
|
+
}
|
|
69231
|
+
function resolveAfterSpec(specPath, {
|
|
69232
|
+
cwd = process.cwd(),
|
|
69233
|
+
readFile = fs.readFileSync,
|
|
69234
|
+
exists = fs.existsSync
|
|
69235
|
+
} = {}) {
|
|
69236
|
+
const resolved = path.isAbsolute(specPath) ? specPath : path.join(cwd, specPath);
|
|
69237
|
+
try {
|
|
69238
|
+
if (!exists(resolved)) {
|
|
69239
|
+
return {
|
|
69240
|
+
ok: false,
|
|
69241
|
+
code: "AFTER_MISSING",
|
|
69242
|
+
message: `Working-tree contract artifact not found: ${specPath}`
|
|
69243
|
+
};
|
|
69244
|
+
}
|
|
69245
|
+
const content = readFile(resolved, "utf8");
|
|
69246
|
+
if (content == null || String(content).trim() === "") {
|
|
69247
|
+
return {
|
|
69248
|
+
ok: false,
|
|
69249
|
+
code: "AFTER_EMPTY",
|
|
69250
|
+
message: `Working-tree contract artifact is empty: ${specPath}`
|
|
69251
|
+
};
|
|
69252
|
+
}
|
|
69253
|
+
return { ok: true, content: String(content), path: resolved };
|
|
69254
|
+
} catch (err) {
|
|
69255
|
+
return {
|
|
69256
|
+
ok: false,
|
|
69257
|
+
code: "AFTER_READ_ERROR",
|
|
69258
|
+
message: `Failed to read working-tree ${specPath}: ${err && err.message}`
|
|
69259
|
+
};
|
|
69260
|
+
}
|
|
69261
|
+
}
|
|
69262
|
+
function evaluatePublishPermission(result) {
|
|
69263
|
+
if (!result || typeof result !== "object") {
|
|
69264
|
+
return {
|
|
69265
|
+
allow: false,
|
|
69266
|
+
execution_action: null,
|
|
69267
|
+
decision: null,
|
|
69268
|
+
policy: "fail_closed:unreadable_response"
|
|
69269
|
+
};
|
|
69270
|
+
}
|
|
69271
|
+
const env = result.decision_result && typeof result.decision_result === "object" ? result.decision_result : null;
|
|
69272
|
+
let ea = null;
|
|
69273
|
+
if (env && typeof env.execution_action === "string") ea = env.execution_action;
|
|
69274
|
+
else if (typeof result.execution_action === "string") ea = result.execution_action;
|
|
69275
|
+
const decision = env && env.decision || result.decision || result.omega_decision || null;
|
|
69276
|
+
if (ea && CLOSED_ACTIONS.has(ea)) {
|
|
69277
|
+
const allow = PERMIT_ACTIONS.has(ea);
|
|
69278
|
+
return {
|
|
69279
|
+
allow,
|
|
69280
|
+
execution_action: ea,
|
|
69281
|
+
decision: decision || null,
|
|
69282
|
+
policy: allow ? `permit:execution_action=${ea}` : `block:execution_action=${ea}`
|
|
69283
|
+
};
|
|
69284
|
+
}
|
|
69285
|
+
if (ea != null && ea !== "" && !CLOSED_ACTIONS.has(ea)) {
|
|
69286
|
+
return {
|
|
69287
|
+
allow: false,
|
|
69288
|
+
execution_action: ea,
|
|
69289
|
+
decision: decision || null,
|
|
69290
|
+
policy: `block:unrecognised_execution_action=${ea}`
|
|
69291
|
+
};
|
|
69292
|
+
}
|
|
69293
|
+
if (decision === "BLOCK" || decision === "REQUIRE_APPROVAL") {
|
|
69294
|
+
return {
|
|
69295
|
+
allow: false,
|
|
69296
|
+
execution_action: null,
|
|
69297
|
+
decision,
|
|
69298
|
+
policy: `block:decision=${decision}`
|
|
69299
|
+
};
|
|
69300
|
+
}
|
|
69301
|
+
if (decision === "ALLOW" || decision === "WARN" || decision === "PASS") {
|
|
69302
|
+
return {
|
|
69303
|
+
allow: true,
|
|
69304
|
+
execution_action: null,
|
|
69305
|
+
decision,
|
|
69306
|
+
policy: `permit:decision=${decision}`
|
|
69307
|
+
};
|
|
69308
|
+
}
|
|
69309
|
+
const omega = result.omega_decision;
|
|
69310
|
+
if (omega === "BLOCK" || omega === "REQUIRE_APPROVAL") {
|
|
69311
|
+
return {
|
|
69312
|
+
allow: false,
|
|
69313
|
+
execution_action: null,
|
|
69314
|
+
decision: omega,
|
|
69315
|
+
policy: `block:omega_decision=${omega}`
|
|
69316
|
+
};
|
|
69317
|
+
}
|
|
69318
|
+
return {
|
|
69319
|
+
allow: false,
|
|
69320
|
+
execution_action: ea,
|
|
69321
|
+
decision: decision || omega || null,
|
|
69322
|
+
policy: "fail_closed:no_permission_signal"
|
|
69323
|
+
};
|
|
69324
|
+
}
|
|
69325
|
+
function extractReceiptRef(result) {
|
|
69326
|
+
if (!result || typeof result !== "object") return null;
|
|
69327
|
+
const env = result.decision_result;
|
|
69328
|
+
if (env && env.receipt && typeof env.receipt.token === "string") {
|
|
69329
|
+
return env.receipt.token.slice(0, 24) + (env.receipt.token.length > 24 ? "\u2026" : "");
|
|
69330
|
+
}
|
|
69331
|
+
if (env && typeof env.decision_id === "string") return env.decision_id;
|
|
69332
|
+
if (typeof result.decision_id === "string") return result.decision_id;
|
|
69333
|
+
if (typeof result.fingerprint === "string") return result.fingerprint;
|
|
69334
|
+
if (env && typeof env.fingerprint === "string") return env.fingerprint;
|
|
69335
|
+
return null;
|
|
69336
|
+
}
|
|
69337
|
+
async function defaultPreflight(before, after, { apiKey } = {}) {
|
|
69338
|
+
const key = process.env.CODERIFTS_FORCE_LOCAL_PREFLIGHT ? null : apiKey != null ? apiKey : getApiKey();
|
|
69339
|
+
if (key) {
|
|
69340
|
+
return cloudDiff(before, after, key);
|
|
69341
|
+
}
|
|
69342
|
+
const yaml = require_js_yaml();
|
|
69343
|
+
const { diffSpecs } = require_api2();
|
|
69344
|
+
let oldSpec;
|
|
69345
|
+
let newSpec;
|
|
69346
|
+
try {
|
|
69347
|
+
oldSpec = yaml.load(before);
|
|
69348
|
+
newSpec = yaml.load(after);
|
|
69349
|
+
} catch (e) {
|
|
69350
|
+
const err = new Error(`Failed to parse specs: ${e.message}`);
|
|
69351
|
+
err.code = "PREFLIGHT_UNREACHABLE";
|
|
69352
|
+
throw err;
|
|
69353
|
+
}
|
|
69354
|
+
let diffResult;
|
|
69355
|
+
try {
|
|
69356
|
+
diffResult = await diffSpecs({
|
|
69357
|
+
sourceSpec: { content: JSON.stringify(oldSpec), location: "before.json", format: "openapi3" },
|
|
69358
|
+
destinationSpec: { content: JSON.stringify(newSpec), location: "after.json", format: "openapi3" }
|
|
69359
|
+
});
|
|
69360
|
+
} catch (e) {
|
|
69361
|
+
const err = new Error(`Local preflight engine error: ${e.message}`);
|
|
69362
|
+
err.code = "PREFLIGHT_UNREACHABLE";
|
|
69363
|
+
throw err;
|
|
69364
|
+
}
|
|
69365
|
+
const breaking = (diffResult.breakingDifferences || []).length;
|
|
69366
|
+
const decision = breaking > 0 ? "BLOCK" : "ALLOW";
|
|
69367
|
+
const execution_action = breaking > 0 ? "STOP" : "CONTINUE";
|
|
69368
|
+
return {
|
|
69369
|
+
decision,
|
|
69370
|
+
omega_decision: decision,
|
|
69371
|
+
execution_action,
|
|
69372
|
+
decision_result: {
|
|
69373
|
+
decision,
|
|
69374
|
+
execution_action,
|
|
69375
|
+
decision_id: `local-${Date.now()}`
|
|
69376
|
+
},
|
|
69377
|
+
breaking_changes: diffResult.breakingDifferences || [],
|
|
69378
|
+
risk_score: Math.min(breaking * 15, 100)
|
|
69379
|
+
};
|
|
69380
|
+
}
|
|
69381
|
+
async function runPublishGate(options = {}, deps = {}) {
|
|
69382
|
+
const cwd = deps.cwd || process.cwd();
|
|
69383
|
+
const gitImpl = deps.gitImpl || defaultGit;
|
|
69384
|
+
const readFile = deps.readFile || fs.readFileSync.bind(fs);
|
|
69385
|
+
const exists = deps.exists || fs.existsSync.bind(fs);
|
|
69386
|
+
const preflightFn = deps.preflightFn || defaultPreflight;
|
|
69387
|
+
const log = deps.log || console.log.bind(console);
|
|
69388
|
+
const logErr = deps.logErr || console.error.bind(console);
|
|
69389
|
+
let specPath = options.spec;
|
|
69390
|
+
if (!specPath) {
|
|
69391
|
+
try {
|
|
69392
|
+
specPath = gitImpl(["config", "coderifts.specPath"], cwd);
|
|
69393
|
+
} catch {
|
|
69394
|
+
specPath = "";
|
|
69395
|
+
}
|
|
69396
|
+
}
|
|
69397
|
+
if (!specPath) specPath = "api/openapi.yaml";
|
|
69398
|
+
try {
|
|
69399
|
+
gitImpl(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
69400
|
+
} catch (err) {
|
|
69401
|
+
const msg = "CodeRifts publish-gate: GIT_ERROR \u2014 not a git repository (or git unavailable). Cannot resolve before-spec without git. Fail-closed.";
|
|
69402
|
+
logErr(chalk.red(msg));
|
|
69403
|
+
return finish({
|
|
69404
|
+
ok: false,
|
|
69405
|
+
exitCode: 1,
|
|
69406
|
+
code: "GIT_ERROR",
|
|
69407
|
+
message: msg,
|
|
69408
|
+
policy: "fail_closed:git_error"
|
|
69409
|
+
}, { log, json: options.json });
|
|
69410
|
+
}
|
|
69411
|
+
const beforeRes = resolveBeforeSpec(specPath, {
|
|
69412
|
+
gitImpl,
|
|
69413
|
+
cwd,
|
|
69414
|
+
readFile,
|
|
69415
|
+
packageVersion: deps.packageVersion
|
|
69416
|
+
});
|
|
69417
|
+
if (!beforeRes.ok) {
|
|
69418
|
+
logErr(chalk.red(`CodeRifts publish-gate: ${beforeRes.code} \u2014 ${beforeRes.message}`));
|
|
69419
|
+
return finish({
|
|
69420
|
+
ok: false,
|
|
69421
|
+
exitCode: 1,
|
|
69422
|
+
code: beforeRes.code,
|
|
69423
|
+
message: beforeRes.message,
|
|
69424
|
+
policy: `fail_closed:${beforeRes.code}`,
|
|
69425
|
+
tried: beforeRes.tried
|
|
69426
|
+
}, { log, json: options.json });
|
|
69427
|
+
}
|
|
69428
|
+
const afterRes = resolveAfterSpec(specPath, { cwd, readFile, exists });
|
|
69429
|
+
if (!afterRes.ok) {
|
|
69430
|
+
logErr(chalk.red(`CodeRifts publish-gate: ${afterRes.code} \u2014 ${afterRes.message}`));
|
|
69431
|
+
return finish({
|
|
69432
|
+
ok: false,
|
|
69433
|
+
exitCode: 1,
|
|
69434
|
+
code: afterRes.code,
|
|
69435
|
+
message: afterRes.message,
|
|
69436
|
+
policy: `fail_closed:${afterRes.code}`
|
|
69437
|
+
}, { log, json: options.json });
|
|
69438
|
+
}
|
|
69439
|
+
if (beforeRes.content === afterRes.content) {
|
|
69440
|
+
const payload2 = {
|
|
69441
|
+
ok: true,
|
|
69442
|
+
exitCode: 0,
|
|
69443
|
+
code: "UNCHANGED",
|
|
69444
|
+
message: "Contract artifact unchanged vs baseline; publish permitted.",
|
|
69445
|
+
policy: "permit:unchanged",
|
|
69446
|
+
before_source: beforeRes.source,
|
|
69447
|
+
execution_action: "CONTINUE",
|
|
69448
|
+
receipt: null
|
|
69449
|
+
};
|
|
69450
|
+
if (!options.json) {
|
|
69451
|
+
log(chalk.green("CodeRifts publish-gate: ALLOW (unchanged)"));
|
|
69452
|
+
log(` baseline: ${beforeRes.source}`);
|
|
69453
|
+
log(` spec: ${specPath}`);
|
|
69454
|
+
}
|
|
69455
|
+
return finish(payload2, { log, json: options.json });
|
|
69456
|
+
}
|
|
69457
|
+
let result;
|
|
69458
|
+
try {
|
|
69459
|
+
result = await preflightFn(beforeRes.content, afterRes.content, {
|
|
69460
|
+
apiKey: deps.apiKey,
|
|
69461
|
+
cwd
|
|
69462
|
+
});
|
|
69463
|
+
} catch (err) {
|
|
69464
|
+
const msg = `CodeRifts publish-gate: PREFLIGHT_UNREACHABLE \u2014 ${err && err.message}`;
|
|
69465
|
+
logErr(chalk.red(msg));
|
|
69466
|
+
return finish({
|
|
69467
|
+
ok: false,
|
|
69468
|
+
exitCode: 1,
|
|
69469
|
+
code: "PREFLIGHT_UNREACHABLE",
|
|
69470
|
+
message: msg,
|
|
69471
|
+
policy: "fail_closed:preflight_unreachable"
|
|
69472
|
+
}, { log, json: options.json });
|
|
69473
|
+
}
|
|
69474
|
+
const perm = evaluatePublishPermission(result);
|
|
69475
|
+
const receipt = extractReceiptRef(result);
|
|
69476
|
+
if (!perm.allow) {
|
|
69477
|
+
const payload2 = {
|
|
69478
|
+
ok: false,
|
|
69479
|
+
exitCode: 1,
|
|
69480
|
+
code: "BLOCK",
|
|
69481
|
+
message: "Publish not permitted by execution_action / decision.",
|
|
69482
|
+
policy: perm.policy,
|
|
69483
|
+
execution_action: perm.execution_action,
|
|
69484
|
+
decision: perm.decision,
|
|
69485
|
+
before_source: beforeRes.source,
|
|
69486
|
+
receipt,
|
|
69487
|
+
fail_policy: "exit_1_on_block_or_resolver_error_or_unreachable"
|
|
69488
|
+
};
|
|
69489
|
+
if (!options.json) {
|
|
69490
|
+
logErr("");
|
|
69491
|
+
logErr(chalk.red("========================================"));
|
|
69492
|
+
logErr(chalk.red(" CodeRifts: PUBLISH BLOCKED"));
|
|
69493
|
+
logErr(chalk.red("========================================"));
|
|
69494
|
+
logErr(` Policy: ${perm.policy}`);
|
|
69495
|
+
logErr(` execution_action: ${perm.execution_action || "(none)"}`);
|
|
69496
|
+
logErr(` decision: ${perm.decision || "(none)"}`);
|
|
69497
|
+
logErr(` baseline: ${beforeRes.source}`);
|
|
69498
|
+
logErr(` fail policy: exit 1 on BLOCK / resolver error / preflight unreachability`);
|
|
69499
|
+
logErr(chalk.red("========================================"));
|
|
69500
|
+
logErr("");
|
|
69501
|
+
}
|
|
69502
|
+
return finish(payload2, { log, json: options.json });
|
|
69503
|
+
}
|
|
69504
|
+
const payload = {
|
|
69505
|
+
ok: true,
|
|
69506
|
+
exitCode: 0,
|
|
69507
|
+
code: "ALLOW",
|
|
69508
|
+
message: "Publish permitted.",
|
|
69509
|
+
policy: perm.policy,
|
|
69510
|
+
execution_action: perm.execution_action,
|
|
69511
|
+
decision: perm.decision,
|
|
69512
|
+
before_source: beforeRes.source,
|
|
69513
|
+
receipt
|
|
69514
|
+
};
|
|
69515
|
+
if (!options.json) {
|
|
69516
|
+
log(chalk.green("CodeRifts publish-gate: ALLOW"));
|
|
69517
|
+
log(` Policy: ${perm.policy}`);
|
|
69518
|
+
log(` execution_action: ${perm.execution_action || "(mapped from decision)"}`);
|
|
69519
|
+
log(` baseline: ${beforeRes.source}`);
|
|
69520
|
+
if (receipt) log(` Receipt reference: ${receipt}`);
|
|
69521
|
+
else log(" Receipt reference: (none issued on this path)");
|
|
69522
|
+
}
|
|
69523
|
+
return finish(payload, { log, json: options.json });
|
|
69524
|
+
}
|
|
69525
|
+
function finish(payload, { log, json }) {
|
|
69526
|
+
if (json) {
|
|
69527
|
+
log(JSON.stringify(payload, null, 2));
|
|
69528
|
+
}
|
|
69529
|
+
return payload;
|
|
69530
|
+
}
|
|
69531
|
+
module2.exports = {
|
|
69532
|
+
runPublishGate,
|
|
69533
|
+
resolveBeforeSpec,
|
|
69534
|
+
resolveAfterSpec,
|
|
69535
|
+
evaluatePublishPermission,
|
|
69536
|
+
extractReceiptRef,
|
|
69537
|
+
gitShow,
|
|
69538
|
+
defaultGit,
|
|
69539
|
+
PERMIT_ACTIONS,
|
|
69540
|
+
CLOSED_ACTIONS
|
|
69541
|
+
};
|
|
69542
|
+
}
|
|
69543
|
+
});
|
|
69544
|
+
|
|
69545
|
+
// src/registry-validation-core.js
|
|
69546
|
+
var require_registry_validation_core = __commonJS({
|
|
69547
|
+
"src/registry-validation-core.js"(exports2, module2) {
|
|
69548
|
+
"use strict";
|
|
69549
|
+
var yaml = require_js_yaml();
|
|
69550
|
+
function safeParse(content) {
|
|
69551
|
+
if (!content) return null;
|
|
69552
|
+
try {
|
|
69553
|
+
const trimmed = content.trim();
|
|
69554
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
69555
|
+
return JSON.parse(trimmed);
|
|
69556
|
+
}
|
|
69557
|
+
return yaml.load(trimmed);
|
|
69558
|
+
} catch (_) {
|
|
69559
|
+
return null;
|
|
69560
|
+
}
|
|
69561
|
+
}
|
|
69562
|
+
function validateOpenApiSpec(parsed) {
|
|
69563
|
+
if (!parsed || typeof parsed !== "object") {
|
|
69564
|
+
return { valid: false, error: "Not a valid YAML or JSON object" };
|
|
69565
|
+
}
|
|
69566
|
+
if (parsed.openapi) {
|
|
69567
|
+
const ver = String(parsed.openapi);
|
|
69568
|
+
if (ver.startsWith("3.")) {
|
|
69569
|
+
return { valid: true, version: ver };
|
|
69570
|
+
}
|
|
69571
|
+
return { valid: false, error: `Unsupported OpenAPI version: ${ver}` };
|
|
69572
|
+
}
|
|
69573
|
+
if (parsed.swagger) {
|
|
69574
|
+
const ver = String(parsed.swagger);
|
|
69575
|
+
if (ver.startsWith("2.")) {
|
|
69576
|
+
return { valid: true, version: ver };
|
|
69577
|
+
}
|
|
69578
|
+
return { valid: false, error: `Unsupported Swagger version: ${ver}` };
|
|
69579
|
+
}
|
|
69580
|
+
return { valid: false, error: "Missing required field: 'openapi' or 'swagger'" };
|
|
69581
|
+
}
|
|
69582
|
+
function extractEndpoints(parsed) {
|
|
69583
|
+
const endpoints = [];
|
|
69584
|
+
const paths = parsed.paths || {};
|
|
69585
|
+
const httpMethods = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
69586
|
+
for (const [path, pathItem] of Object.entries(paths)) {
|
|
69587
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
69588
|
+
for (const method of httpMethods) {
|
|
69589
|
+
if (pathItem[method]) {
|
|
69590
|
+
endpoints.push({
|
|
69591
|
+
path,
|
|
69592
|
+
method: method.toUpperCase(),
|
|
69593
|
+
operationId: pathItem[method].operationId || ""
|
|
69594
|
+
});
|
|
69595
|
+
}
|
|
69596
|
+
}
|
|
69597
|
+
}
|
|
69598
|
+
return endpoints;
|
|
69599
|
+
}
|
|
69600
|
+
function extractSchemaNames(parsed) {
|
|
69601
|
+
const schemas = [];
|
|
69602
|
+
const components = parsed.components?.schemas || {};
|
|
69603
|
+
for (const [name, schema] of Object.entries(components)) {
|
|
69604
|
+
const hash = JSON.stringify(schema);
|
|
69605
|
+
schemas.push({ name, hash });
|
|
69606
|
+
}
|
|
69607
|
+
return schemas;
|
|
69608
|
+
}
|
|
69609
|
+
function extractDefinedScopes(parsed) {
|
|
69610
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
69611
|
+
const schemes = parsed.components?.securitySchemes || {};
|
|
69612
|
+
for (const scheme of Object.values(schemes)) {
|
|
69613
|
+
if (scheme.type === "oauth2" && scheme.flows) {
|
|
69614
|
+
for (const flow of Object.values(scheme.flows)) {
|
|
69615
|
+
if (flow.scopes) {
|
|
69616
|
+
for (const scope of Object.keys(flow.scopes)) {
|
|
69617
|
+
scopes.add(scope);
|
|
69618
|
+
}
|
|
69619
|
+
}
|
|
69620
|
+
}
|
|
69621
|
+
}
|
|
69622
|
+
}
|
|
69623
|
+
return scopes;
|
|
69624
|
+
}
|
|
69625
|
+
function extractUsedScopes(parsed) {
|
|
69626
|
+
const scopes = /* @__PURE__ */ new Set();
|
|
69627
|
+
if (Array.isArray(parsed.security)) {
|
|
69628
|
+
for (const req of parsed.security) {
|
|
69629
|
+
for (const scopeList of Object.values(req)) {
|
|
69630
|
+
if (Array.isArray(scopeList)) {
|
|
69631
|
+
for (const s of scopeList) scopes.add(s);
|
|
69632
|
+
}
|
|
69633
|
+
}
|
|
69634
|
+
}
|
|
69635
|
+
}
|
|
69636
|
+
const paths = parsed.paths || {};
|
|
69637
|
+
const httpMethods = ["get", "post", "put", "patch", "delete", "head", "options"];
|
|
69638
|
+
for (const pathItem of Object.values(paths)) {
|
|
69639
|
+
if (!pathItem || typeof pathItem !== "object") continue;
|
|
69640
|
+
for (const method of httpMethods) {
|
|
69641
|
+
const op = pathItem[method];
|
|
69642
|
+
if (op?.security && Array.isArray(op.security)) {
|
|
69643
|
+
for (const req of op.security) {
|
|
69644
|
+
for (const scopeList of Object.values(req)) {
|
|
69645
|
+
if (Array.isArray(scopeList)) {
|
|
69646
|
+
for (const s of scopeList) scopes.add(s);
|
|
69647
|
+
}
|
|
69648
|
+
}
|
|
69649
|
+
}
|
|
69650
|
+
}
|
|
69651
|
+
}
|
|
69652
|
+
}
|
|
69653
|
+
return scopes;
|
|
69654
|
+
}
|
|
69655
|
+
function extractRefs(obj, refs = /* @__PURE__ */ new Set()) {
|
|
69656
|
+
if (!obj || typeof obj !== "object") return refs;
|
|
69657
|
+
if (Array.isArray(obj)) {
|
|
69658
|
+
for (const item of obj) extractRefs(item, refs);
|
|
69659
|
+
return refs;
|
|
69660
|
+
}
|
|
69661
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
69662
|
+
if (key === "$ref" && typeof value === "string") {
|
|
69663
|
+
refs.add(value);
|
|
69664
|
+
} else {
|
|
69665
|
+
extractRefs(value, refs);
|
|
69666
|
+
}
|
|
69667
|
+
}
|
|
69668
|
+
return refs;
|
|
69669
|
+
}
|
|
69670
|
+
function refResolves(parsed, ref) {
|
|
69671
|
+
if (!ref.startsWith("#/")) return true;
|
|
69672
|
+
const parts = ref.replace("#/", "").split("/");
|
|
69673
|
+
let current = parsed;
|
|
69674
|
+
for (const part of parts) {
|
|
69675
|
+
if (!current || typeof current !== "object") return false;
|
|
69676
|
+
current = current[part];
|
|
69677
|
+
}
|
|
69678
|
+
return current !== void 0;
|
|
69679
|
+
}
|
|
69680
|
+
function validateRegistry(specs) {
|
|
69681
|
+
if (!Array.isArray(specs) || specs.length === 0) {
|
|
69682
|
+
return { valid: true, issues: [], stats: { specs_count: 0, endpoints_count: 0, schemas_count: 0 } };
|
|
69683
|
+
}
|
|
69684
|
+
const issues = [];
|
|
69685
|
+
const parsedSpecs = [];
|
|
69686
|
+
for (const { name, spec } of specs) {
|
|
69687
|
+
const parsed = safeParse(spec);
|
|
69688
|
+
if (!parsed) {
|
|
69689
|
+
issues.push({
|
|
69690
|
+
severity: "error",
|
|
69691
|
+
type: "parse_error",
|
|
69692
|
+
message: `Could not parse spec '${name}' as valid YAML or JSON`,
|
|
69693
|
+
specs: [name]
|
|
69694
|
+
});
|
|
69695
|
+
continue;
|
|
69696
|
+
}
|
|
69697
|
+
const validation = validateOpenApiSpec(parsed);
|
|
69698
|
+
if (!validation.valid) {
|
|
69699
|
+
issues.push({
|
|
69700
|
+
severity: "error",
|
|
69701
|
+
type: "invalid_spec",
|
|
69702
|
+
message: `Spec '${name}' is not a valid OpenAPI document: ${validation.error}`,
|
|
69703
|
+
specs: [name]
|
|
69704
|
+
});
|
|
69705
|
+
continue;
|
|
69706
|
+
}
|
|
69707
|
+
parsedSpecs.push({ name, parsed });
|
|
69708
|
+
}
|
|
69709
|
+
const endpointMap = /* @__PURE__ */ new Map();
|
|
69710
|
+
for (const { name, parsed } of parsedSpecs) {
|
|
69711
|
+
const endpoints = extractEndpoints(parsed);
|
|
69712
|
+
for (const ep of endpoints) {
|
|
69713
|
+
const key = `${ep.method} ${ep.path}`;
|
|
69714
|
+
if (!endpointMap.has(key)) endpointMap.set(key, []);
|
|
69715
|
+
endpointMap.get(key).push({ spec: name, operationId: ep.operationId });
|
|
69716
|
+
}
|
|
69717
|
+
}
|
|
69718
|
+
for (const [endpoint, owners] of endpointMap) {
|
|
69719
|
+
if (owners.length > 1) {
|
|
69720
|
+
const specNames = owners.map((o) => o.spec);
|
|
69721
|
+
issues.push({
|
|
69722
|
+
severity: "warning",
|
|
69723
|
+
type: "endpoint_collision",
|
|
69724
|
+
message: `Endpoint '${endpoint}' is defined in multiple specs: ${specNames.join(", ")}`,
|
|
69725
|
+
specs: specNames
|
|
69726
|
+
});
|
|
69727
|
+
}
|
|
69728
|
+
}
|
|
69729
|
+
const schemaMap = /* @__PURE__ */ new Map();
|
|
69730
|
+
for (const { name, parsed } of parsedSpecs) {
|
|
69731
|
+
const schemas = extractSchemaNames(parsed);
|
|
69732
|
+
for (const s of schemas) {
|
|
69733
|
+
if (!schemaMap.has(s.name)) schemaMap.set(s.name, []);
|
|
69734
|
+
schemaMap.get(s.name).push({ spec: name, hash: s.hash });
|
|
69735
|
+
}
|
|
69736
|
+
}
|
|
69737
|
+
for (const [schemaName, definitions] of schemaMap) {
|
|
69738
|
+
if (definitions.length > 1) {
|
|
69739
|
+
const uniqueHashes = new Set(definitions.map((d) => d.hash));
|
|
69740
|
+
if (uniqueHashes.size > 1) {
|
|
69741
|
+
const specNames = definitions.map((d) => d.spec);
|
|
69742
|
+
issues.push({
|
|
69743
|
+
severity: "warning",
|
|
69744
|
+
type: "schema_conflict",
|
|
69745
|
+
message: `Schema '${schemaName}' has conflicting definitions across specs: ${specNames.join(", ")}`,
|
|
69746
|
+
specs: specNames
|
|
69747
|
+
});
|
|
69748
|
+
}
|
|
69749
|
+
}
|
|
69750
|
+
}
|
|
69751
|
+
for (const { name, parsed } of parsedSpecs) {
|
|
69752
|
+
const defined = extractDefinedScopes(parsed);
|
|
69753
|
+
const used = extractUsedScopes(parsed);
|
|
69754
|
+
for (const scope of used) {
|
|
69755
|
+
if (!defined.has(scope)) {
|
|
69756
|
+
issues.push({
|
|
69757
|
+
severity: "warning",
|
|
69758
|
+
type: "undefined_scope",
|
|
69759
|
+
message: `Scope '${scope}' is used in '${name}' but not defined in securitySchemes`,
|
|
69760
|
+
specs: [name]
|
|
69761
|
+
});
|
|
69762
|
+
}
|
|
69763
|
+
}
|
|
69764
|
+
for (const scope of defined) {
|
|
69765
|
+
if (!used.has(scope)) {
|
|
69766
|
+
issues.push({
|
|
69767
|
+
severity: "info",
|
|
69768
|
+
type: "unused_scope",
|
|
69769
|
+
message: `Scope '${scope}' is defined in '${name}' but never used in any operation`,
|
|
69770
|
+
specs: [name]
|
|
69771
|
+
});
|
|
69772
|
+
}
|
|
69773
|
+
}
|
|
69774
|
+
}
|
|
69775
|
+
for (const { name, parsed } of parsedSpecs) {
|
|
69776
|
+
const refs = extractRefs(parsed);
|
|
69777
|
+
for (const ref of refs) {
|
|
69778
|
+
if (!refResolves(parsed, ref)) {
|
|
69779
|
+
issues.push({
|
|
69780
|
+
severity: "error",
|
|
69781
|
+
type: "unresolved_ref",
|
|
69782
|
+
message: `$ref '${ref}' in '${name}' does not resolve`,
|
|
69783
|
+
specs: [name]
|
|
69784
|
+
});
|
|
69785
|
+
}
|
|
69786
|
+
}
|
|
69787
|
+
}
|
|
69788
|
+
let totalEndpoints = 0;
|
|
69789
|
+
let totalSchemas = 0;
|
|
69790
|
+
for (const { parsed } of parsedSpecs) {
|
|
69791
|
+
totalEndpoints += extractEndpoints(parsed).length;
|
|
69792
|
+
totalSchemas += extractSchemaNames(parsed).length;
|
|
69793
|
+
}
|
|
69794
|
+
const hasErrors = issues.some((i) => i.severity === "error");
|
|
69795
|
+
return {
|
|
69796
|
+
valid: !hasErrors,
|
|
69797
|
+
issues,
|
|
69798
|
+
stats: {
|
|
69799
|
+
specs_count: parsedSpecs.length,
|
|
69800
|
+
endpoints_count: totalEndpoints,
|
|
69801
|
+
schemas_count: totalSchemas
|
|
69802
|
+
}
|
|
69803
|
+
};
|
|
69804
|
+
}
|
|
69805
|
+
module2.exports = {
|
|
69806
|
+
validateRegistry,
|
|
69807
|
+
safeParse,
|
|
69808
|
+
validateOpenApiSpec,
|
|
69809
|
+
// Exported for testing
|
|
69810
|
+
extractEndpoints,
|
|
69811
|
+
extractSchemaNames,
|
|
69812
|
+
extractDefinedScopes,
|
|
69813
|
+
extractUsedScopes,
|
|
69814
|
+
extractRefs,
|
|
69815
|
+
refResolves
|
|
69816
|
+
};
|
|
69817
|
+
}
|
|
69818
|
+
});
|
|
69819
|
+
|
|
69820
|
+
// src/commands/registry-gate.js
|
|
69821
|
+
var require_registry_gate = __commonJS({
|
|
69822
|
+
"src/commands/registry-gate.js"(exports2, module2) {
|
|
69823
|
+
"use strict";
|
|
69824
|
+
var fs = require("fs");
|
|
69825
|
+
var path = require("path");
|
|
69826
|
+
var chalk = require_source();
|
|
69827
|
+
var { matchGlob } = require_cjs4();
|
|
69828
|
+
var {
|
|
69829
|
+
validateRegistry,
|
|
69830
|
+
safeParse,
|
|
69831
|
+
validateOpenApiSpec
|
|
69832
|
+
} = require_registry_validation_core();
|
|
69833
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
69834
|
+
var SPEC_EXT = /* @__PURE__ */ new Set([".yaml", ".yml", ".json"]);
|
|
69835
|
+
function walkSpecCandidates(rootDir, {
|
|
69836
|
+
readdirSync = fs.readdirSync.bind(fs),
|
|
69837
|
+
statSync = fs.statSync.bind(fs)
|
|
69838
|
+
} = {}) {
|
|
69839
|
+
const out = [];
|
|
69840
|
+
function walk(absDir) {
|
|
69841
|
+
let entries;
|
|
69842
|
+
try {
|
|
69843
|
+
entries = readdirSync(absDir, { withFileTypes: true });
|
|
69844
|
+
} catch (err) {
|
|
69845
|
+
const e = new Error(`Cannot read directory ${absDir}: ${err && err.message}`);
|
|
69846
|
+
e.code = "GATE_ERROR";
|
|
69847
|
+
e.path = absDir;
|
|
69848
|
+
throw e;
|
|
69849
|
+
}
|
|
69850
|
+
for (const ent of entries) {
|
|
69851
|
+
const name = ent.name;
|
|
69852
|
+
if (name === "node_modules" || name.startsWith(".")) continue;
|
|
69853
|
+
const abs = path.join(absDir, name);
|
|
69854
|
+
let isDir = ent.isDirectory && ent.isDirectory();
|
|
69855
|
+
let isFile = ent.isFile && ent.isFile();
|
|
69856
|
+
if (!isDir && !isFile) {
|
|
69857
|
+
try {
|
|
69858
|
+
const st = statSync(abs);
|
|
69859
|
+
isDir = st.isDirectory();
|
|
69860
|
+
isFile = st.isFile();
|
|
69861
|
+
} catch (err) {
|
|
69862
|
+
const e = new Error(`Cannot stat ${abs}: ${err && err.message}`);
|
|
69863
|
+
e.code = "GATE_ERROR";
|
|
69864
|
+
e.path = abs;
|
|
69865
|
+
throw e;
|
|
69866
|
+
}
|
|
69867
|
+
}
|
|
69868
|
+
if (isDir) {
|
|
69869
|
+
walk(abs);
|
|
69870
|
+
continue;
|
|
69871
|
+
}
|
|
69872
|
+
if (isFile) {
|
|
69873
|
+
const ext = path.extname(name).toLowerCase();
|
|
69874
|
+
if (SPEC_EXT.has(ext)) out.push(abs);
|
|
69875
|
+
}
|
|
69876
|
+
}
|
|
69877
|
+
}
|
|
69878
|
+
walk(rootDir);
|
|
69879
|
+
return out.sort();
|
|
69880
|
+
}
|
|
69881
|
+
function relPosix(rootDir, absPath) {
|
|
69882
|
+
let rel = path.relative(rootDir, absPath);
|
|
69883
|
+
if (path.sep !== "/") rel = rel.split(path.sep).join("/");
|
|
69884
|
+
return rel;
|
|
69885
|
+
}
|
|
69886
|
+
function readFileThreeState(absPath, readFileSync = fs.readFileSync.bind(fs)) {
|
|
69887
|
+
try {
|
|
69888
|
+
const content = readFileSync(absPath, "utf8");
|
|
69889
|
+
return { ok: true, content: content == null ? "" : String(content) };
|
|
69890
|
+
} catch (err) {
|
|
69891
|
+
const msg = err && err.message ? String(err.message) : String(err);
|
|
69892
|
+
return {
|
|
69893
|
+
ok: false,
|
|
69894
|
+
code: "GATE_ERROR",
|
|
69895
|
+
message: `unreadable file: ${absPath} (${msg.slice(0, 200)})`,
|
|
69896
|
+
path: absPath
|
|
69897
|
+
};
|
|
69898
|
+
}
|
|
69899
|
+
}
|
|
69900
|
+
function discoverRegistrySpecs(dir, {
|
|
69901
|
+
glob = null,
|
|
69902
|
+
readdirSync = fs.readdirSync.bind(fs),
|
|
69903
|
+
readFileSync = fs.readFileSync.bind(fs),
|
|
69904
|
+
statSync = fs.statSync.bind(fs)
|
|
69905
|
+
} = {}) {
|
|
69906
|
+
const rootDir = path.resolve(dir);
|
|
69907
|
+
let candidates;
|
|
69908
|
+
try {
|
|
69909
|
+
candidates = walkSpecCandidates(rootDir, { readdirSync, statSync });
|
|
69910
|
+
} catch (err) {
|
|
69911
|
+
return {
|
|
69912
|
+
ok: false,
|
|
69913
|
+
code: err.code || "GATE_ERROR",
|
|
69914
|
+
message: err.message || String(err),
|
|
69915
|
+
path: err.path
|
|
69916
|
+
};
|
|
69917
|
+
}
|
|
69918
|
+
if (glob) {
|
|
69919
|
+
candidates = candidates.filter((abs) => matchGlob(glob, relPosix(rootDir, abs)));
|
|
69920
|
+
}
|
|
69921
|
+
const specs = [];
|
|
69922
|
+
let skippedNonSpec = 0;
|
|
69923
|
+
for (const abs of candidates) {
|
|
69924
|
+
const read = readFileThreeState(abs, readFileSync);
|
|
69925
|
+
if (!read.ok) {
|
|
69926
|
+
return {
|
|
69927
|
+
ok: false,
|
|
69928
|
+
code: "GATE_ERROR",
|
|
69929
|
+
message: read.message,
|
|
69930
|
+
path: read.path || abs
|
|
69931
|
+
};
|
|
69932
|
+
}
|
|
69933
|
+
const name = relPosix(rootDir, abs);
|
|
69934
|
+
const parsed = safeParse(read.content);
|
|
69935
|
+
if (!parsed || typeof parsed !== "object") {
|
|
69936
|
+
specs.push({ name, spec: read.content });
|
|
69937
|
+
continue;
|
|
69938
|
+
}
|
|
69939
|
+
const shape = validateOpenApiSpec(parsed);
|
|
69940
|
+
if (!shape.valid) {
|
|
69941
|
+
if (shape.error && shape.error.includes("Missing required field: 'openapi' or 'swagger'")) {
|
|
69942
|
+
skippedNonSpec += 1;
|
|
69943
|
+
continue;
|
|
69944
|
+
}
|
|
69945
|
+
specs.push({ name, spec: read.content });
|
|
69946
|
+
continue;
|
|
69947
|
+
}
|
|
69948
|
+
specs.push({ name, spec: read.content });
|
|
69949
|
+
}
|
|
69950
|
+
return {
|
|
69951
|
+
ok: true,
|
|
69952
|
+
specs,
|
|
69953
|
+
skippedNonSpec,
|
|
69954
|
+
candidates: candidates.length,
|
|
69955
|
+
rootDir
|
|
69956
|
+
};
|
|
69957
|
+
}
|
|
69958
|
+
function findingsFailGate(issues, { warnOnly = false, errorsOnly = false } = {}) {
|
|
69959
|
+
if (warnOnly) return false;
|
|
69960
|
+
for (const i of issues || []) {
|
|
69961
|
+
if (i.severity === "error") return true;
|
|
69962
|
+
if (i.severity === "warning" && !errorsOnly) return true;
|
|
69963
|
+
}
|
|
69964
|
+
return false;
|
|
69965
|
+
}
|
|
69966
|
+
function countBySeverity(issues) {
|
|
69967
|
+
const c = { error: 0, warning: 0, info: 0 };
|
|
69968
|
+
for (const i of issues || []) {
|
|
69969
|
+
if (i.severity === "error") c.error += 1;
|
|
69970
|
+
else if (i.severity === "warning") c.warning += 1;
|
|
69971
|
+
else if (i.severity === "info") c.info += 1;
|
|
69972
|
+
}
|
|
69973
|
+
return c;
|
|
69974
|
+
}
|
|
69975
|
+
function severityColor(sev) {
|
|
69976
|
+
if (sev === "error") return chalk.red;
|
|
69977
|
+
if (sev === "warning") return chalk.yellow;
|
|
69978
|
+
return chalk.cyan;
|
|
69979
|
+
}
|
|
69980
|
+
function printFindings(issues, log) {
|
|
69981
|
+
for (const i of issues || []) {
|
|
69982
|
+
const color = severityColor(i.severity);
|
|
69983
|
+
const files = Array.isArray(i.specs) ? i.specs.join(", ") : "";
|
|
69984
|
+
log(color(`[${i.severity}] ${i.type}`) + (files ? chalk.dim(` ${files}`) : ""));
|
|
69985
|
+
log(` ${i.message}`);
|
|
69986
|
+
}
|
|
69987
|
+
}
|
|
69988
|
+
function runRegistryGate(options = {}, deps = {}) {
|
|
69989
|
+
const log = deps.log || console.log.bind(console);
|
|
69990
|
+
const logErr = deps.logErr || console.error.bind(console);
|
|
69991
|
+
const cwd = deps.cwd || process.cwd();
|
|
69992
|
+
const dirArg = options.dir != null && options.dir !== "" ? options.dir : ".";
|
|
69993
|
+
const rootDir = path.isAbsolute(dirArg) ? dirArg : path.resolve(cwd, dirArg);
|
|
69994
|
+
const readdirSync = deps.readdirSync || fs.readdirSync.bind(fs);
|
|
69995
|
+
const readFileSync = deps.readFileSync || fs.readFileSync.bind(fs);
|
|
69996
|
+
const statSync = deps.statSync || fs.statSync.bind(fs);
|
|
69997
|
+
const existsSync = deps.existsSync || fs.existsSync.bind(fs);
|
|
69998
|
+
if (!existsSync(rootDir)) {
|
|
69999
|
+
const msg = `CodeRifts registry-gate: GATE_ERROR \u2014 directory not found: ${rootDir}`;
|
|
70000
|
+
logErr(chalk.red(msg));
|
|
70001
|
+
return { ok: false, exitCode: 1, code: "GATE_ERROR", message: msg };
|
|
70002
|
+
}
|
|
70003
|
+
let st;
|
|
70004
|
+
try {
|
|
70005
|
+
st = statSync(rootDir);
|
|
70006
|
+
} catch (err) {
|
|
70007
|
+
const msg = `CodeRifts registry-gate: GATE_ERROR \u2014 cannot access ${rootDir}: ${err && err.message}`;
|
|
70008
|
+
logErr(chalk.red(msg));
|
|
70009
|
+
return { ok: false, exitCode: 1, code: "GATE_ERROR", message: msg };
|
|
70010
|
+
}
|
|
70011
|
+
if (!st.isDirectory()) {
|
|
70012
|
+
const msg = `CodeRifts registry-gate: GATE_ERROR \u2014 not a directory: ${rootDir}`;
|
|
70013
|
+
logErr(chalk.red(msg));
|
|
70014
|
+
return { ok: false, exitCode: 1, code: "GATE_ERROR", message: msg };
|
|
70015
|
+
}
|
|
70016
|
+
const discovered = discoverRegistrySpecs(rootDir, {
|
|
70017
|
+
glob: options.glob || null,
|
|
70018
|
+
readdirSync,
|
|
70019
|
+
readFileSync,
|
|
70020
|
+
statSync
|
|
70021
|
+
});
|
|
70022
|
+
if (!discovered.ok) {
|
|
70023
|
+
const msg = `CodeRifts registry-gate: ${discovered.code} \u2014 ${discovered.message}`;
|
|
70024
|
+
logErr(chalk.red(msg));
|
|
70025
|
+
return {
|
|
70026
|
+
ok: false,
|
|
70027
|
+
exitCode: 1,
|
|
70028
|
+
code: discovered.code || "GATE_ERROR",
|
|
70029
|
+
message: discovered.message,
|
|
70030
|
+
path: discovered.path
|
|
70031
|
+
};
|
|
70032
|
+
}
|
|
70033
|
+
if (discovered.specs.length === 0) {
|
|
70034
|
+
const msg = `CodeRifts registry-gate: REGISTRY_EMPTY \u2014 no OpenAPI/Swagger specs discovered (scanned ${discovered.candidates} yaml/json file(s), skipped non-spec: ${discovered.skippedNonSpec}). An admission gate guarding nothing must fail, not pass.`;
|
|
70035
|
+
logErr(chalk.red(msg));
|
|
70036
|
+
return {
|
|
70037
|
+
ok: false,
|
|
70038
|
+
exitCode: 1,
|
|
70039
|
+
code: "REGISTRY_EMPTY",
|
|
70040
|
+
message: msg,
|
|
70041
|
+
skippedNonSpec: discovered.skippedNonSpec,
|
|
70042
|
+
candidates: discovered.candidates
|
|
70043
|
+
};
|
|
70044
|
+
}
|
|
70045
|
+
const result = validateRegistry(discovered.specs);
|
|
70046
|
+
const counts = countBySeverity(result.issues);
|
|
70047
|
+
const mode = {
|
|
70048
|
+
warnOnly: !!options.warnOnly,
|
|
70049
|
+
errorsOnly: !!options.errorsOnly
|
|
70050
|
+
};
|
|
70051
|
+
const fail = findingsFailGate(result.issues, mode);
|
|
70052
|
+
if (result.issues.length > 0) {
|
|
70053
|
+
printFindings(result.issues, log);
|
|
70054
|
+
}
|
|
70055
|
+
const summary = [
|
|
70056
|
+
`specs=${result.stats.specs_count}`,
|
|
70057
|
+
`endpoints=${result.stats.endpoints_count}`,
|
|
70058
|
+
`schemas=${result.stats.schemas_count}`,
|
|
70059
|
+
`errors=${counts.error}`,
|
|
70060
|
+
`warnings=${counts.warning}`,
|
|
70061
|
+
`info=${counts.info}`,
|
|
70062
|
+
discovered.skippedNonSpec ? `skipped_non_spec=${discovered.skippedNonSpec}` : null,
|
|
70063
|
+
mode.warnOnly ? "mode=warn-only" : mode.errorsOnly ? "mode=errors-only" : "mode=default",
|
|
70064
|
+
fail ? "FAIL" : "PASS"
|
|
70065
|
+
].filter(Boolean).join(" ");
|
|
70066
|
+
if (fail) {
|
|
70067
|
+
log(chalk.red(`CodeRifts registry-gate: ${summary}`));
|
|
70068
|
+
} else {
|
|
70069
|
+
log(chalk.green(`CodeRifts registry-gate: ${summary}`));
|
|
70070
|
+
}
|
|
70071
|
+
if (counts.error === 0 && counts.warning === 0 && counts.info === 0) {
|
|
70072
|
+
log(chalk.dim(" checks: endpoint_collision, schema_conflict, scopes, unresolved_ref \u2014 no findings"));
|
|
70073
|
+
}
|
|
70074
|
+
return {
|
|
70075
|
+
ok: !fail,
|
|
70076
|
+
exitCode: fail ? 1 : 0,
|
|
70077
|
+
code: fail ? "REGISTRY_FINDINGS" : "REGISTRY_OK",
|
|
70078
|
+
issues: result.issues,
|
|
70079
|
+
stats: result.stats,
|
|
70080
|
+
counts,
|
|
70081
|
+
skippedNonSpec: discovered.skippedNonSpec,
|
|
70082
|
+
candidates: discovered.candidates,
|
|
70083
|
+
warnOnly: mode.warnOnly,
|
|
70084
|
+
errorsOnly: mode.errorsOnly
|
|
70085
|
+
};
|
|
70086
|
+
}
|
|
70087
|
+
module2.exports = {
|
|
70088
|
+
runRegistryGate,
|
|
70089
|
+
discoverRegistrySpecs,
|
|
70090
|
+
walkSpecCandidates,
|
|
70091
|
+
findingsFailGate,
|
|
70092
|
+
countBySeverity,
|
|
70093
|
+
readFileThreeState,
|
|
70094
|
+
relPosix
|
|
70095
|
+
};
|
|
70096
|
+
}
|
|
70097
|
+
});
|
|
70098
|
+
|
|
68670
70099
|
// src/commands/init.js
|
|
68671
70100
|
var require_init = __commonJS({
|
|
68672
70101
|
"src/commands/init.js"(exports2, module2) {
|
|
@@ -68926,6 +70355,63 @@ notifications:
|
|
|
68926
70355
|
on_breaking: true
|
|
68927
70356
|
on_risk_above: 60
|
|
68928
70357
|
|
|
70358
|
+
overlap_detection: true
|
|
70359
|
+
generator_detection: true
|
|
70360
|
+
`
|
|
70361
|
+
},
|
|
70362
|
+
"ai-agent-platform": {
|
|
70363
|
+
aliases: ["ai-agent-platform", "ai-agent", "agent", "mcp"],
|
|
70364
|
+
label: "AI Agent Platform",
|
|
70365
|
+
description: "Zero-tolerance removals for agent/MCP-consumed APIs",
|
|
70366
|
+
yaml: `# CodeRifts Policy: AI Agent Platform
|
|
70367
|
+
# For APIs consumed by AI agents and MCP tool surfaces.
|
|
70368
|
+
# Agent consumers cannot renegotiate contracts at runtime \u2014 a removed field
|
|
70369
|
+
# or endpoint is a hard break for automated callers (no human can "adapt").
|
|
70370
|
+
# Portable policy vocabulary only (same keys as other templates).
|
|
70371
|
+
|
|
70372
|
+
failOnBreaking: true
|
|
70373
|
+
|
|
70374
|
+
policy:
|
|
70375
|
+
# Zero tolerance: any breaking change blocks the check.
|
|
70376
|
+
max_breaking_changes: 0
|
|
70377
|
+
# Removals that break tool schemas / agent bindings are never silent.
|
|
70378
|
+
no_delete_endpoints: true
|
|
70379
|
+
no_delete_required_fields: true
|
|
70380
|
+
require_deprecation_before_removal: true
|
|
70381
|
+
require_version_bump_on_breaking: true
|
|
70382
|
+
# Freeze merges when risk is elevated (agents amplify blast radius).
|
|
70383
|
+
freeze_on_risk_score: 55
|
|
70384
|
+
|
|
70385
|
+
# Who must approve high-impact contract changes for agent-facing surfaces.
|
|
70386
|
+
approval_matrix:
|
|
70387
|
+
endpoint_removal:
|
|
70388
|
+
- agent-platform-owners
|
|
70389
|
+
- api-governance
|
|
70390
|
+
field_removal:
|
|
70391
|
+
- agent-platform-owners
|
|
70392
|
+
auth_change:
|
|
70393
|
+
- security-team
|
|
70394
|
+
- agent-platform-owners
|
|
70395
|
+
type_change:
|
|
70396
|
+
- agent-platform-owners
|
|
70397
|
+
|
|
70398
|
+
freeze_periods: []
|
|
70399
|
+
|
|
70400
|
+
risk_scoring:
|
|
70401
|
+
# dimension_weights is the key the engine reads (0-100 scale, relative weights)
|
|
70402
|
+
dimension_weights:
|
|
70403
|
+
revenue_impact: 20
|
|
70404
|
+
blast_radius: 35
|
|
70405
|
+
app_compatibility: 30
|
|
70406
|
+
security: 15
|
|
70407
|
+
|
|
70408
|
+
linting:
|
|
70409
|
+
enabled: true
|
|
70410
|
+
rules:
|
|
70411
|
+
naming_convention: true
|
|
70412
|
+
consistent_errors: true
|
|
70413
|
+
pagination_pattern: true
|
|
70414
|
+
|
|
68929
70415
|
overlap_detection: true
|
|
68930
70416
|
generator_detection: true
|
|
68931
70417
|
`
|
|
@@ -68947,7 +70433,8 @@ generator_detection: true
|
|
|
68947
70433
|
["growth", "Balanced between speed and safety"],
|
|
68948
70434
|
["fintech", "Maximum governance for regulated industries"],
|
|
68949
70435
|
["public-api", "Backward compatibility for external consumers"],
|
|
68950
|
-
["microservices", "Internal service-to-service with blast radius focus"]
|
|
70436
|
+
["microservices", "Internal service-to-service with blast radius focus"],
|
|
70437
|
+
["ai-agent", "Zero-tolerance removals for agent/MCP-consumed APIs"]
|
|
68951
70438
|
];
|
|
68952
70439
|
for (const [name, desc] of entries) {
|
|
68953
70440
|
console.log(` ${chalk.cyan(name.padEnd(16))}${chalk.dim(desc)}`);
|
|
@@ -68983,9 +70470,10 @@ generator_detection: true
|
|
|
68983
70470
|
console.log("");
|
|
68984
70471
|
console.log(chalk.green(` Created .coderifts.yml with ${tmpl.label} policy template.`));
|
|
68985
70472
|
console.log(chalk.dim(` Edit the file to customize approval teams, freeze periods, and domain mappings.`));
|
|
70473
|
+
console.log(chalk.dim(` Agent-using repo? Run: coderifts agent-setup`));
|
|
68986
70474
|
console.log("");
|
|
68987
70475
|
}
|
|
68988
|
-
module2.exports = { init, TEMPLATES };
|
|
70476
|
+
module2.exports = { init, TEMPLATES, resolveTemplate };
|
|
68989
70477
|
}
|
|
68990
70478
|
});
|
|
68991
70479
|
|
|
@@ -94342,6 +95830,679 @@ var require_login = __commonJS({
|
|
|
94342
95830
|
}
|
|
94343
95831
|
});
|
|
94344
95832
|
|
|
95833
|
+
// src/commands/setup-required-check.js
|
|
95834
|
+
var require_setup_required_check = __commonJS({
|
|
95835
|
+
"src/commands/setup-required-check.js"(exports2, module2) {
|
|
95836
|
+
"use strict";
|
|
95837
|
+
var { execFileSync } = require("child_process");
|
|
95838
|
+
var chalk = require_source();
|
|
95839
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
95840
|
+
var CHECK_NAME = "CodeRifts / contract-gate";
|
|
95841
|
+
var EXIT = {
|
|
95842
|
+
OK: 0,
|
|
95843
|
+
/** Target already required or dry-run printed successfully. */
|
|
95844
|
+
NEEDS_APPLY: 0,
|
|
95845
|
+
/** Unknown / permission / gh missing / verify failed. */
|
|
95846
|
+
ERROR: 1,
|
|
95847
|
+
PERMISSION: 2
|
|
95848
|
+
};
|
|
95849
|
+
function extractRequiredContexts(protection) {
|
|
95850
|
+
if (!protection || typeof protection !== "object") return [];
|
|
95851
|
+
const rsc = protection.required_status_checks;
|
|
95852
|
+
if (!rsc || typeof rsc !== "object") return [];
|
|
95853
|
+
if (Array.isArray(rsc.contexts) && rsc.contexts.length) {
|
|
95854
|
+
return rsc.contexts.map((c) => String(c));
|
|
95855
|
+
}
|
|
95856
|
+
if (Array.isArray(rsc.checks)) {
|
|
95857
|
+
return rsc.checks.map((c) => c && c.context != null ? String(c.context) : "").filter(Boolean);
|
|
95858
|
+
}
|
|
95859
|
+
return [];
|
|
95860
|
+
}
|
|
95861
|
+
function classifyObservation(read, contextName = CHECK_NAME) {
|
|
95862
|
+
const status = read && read.status != null ? Number(read.status) : null;
|
|
95863
|
+
if (status === 404) {
|
|
95864
|
+
return {
|
|
95865
|
+
state: "ABSENT",
|
|
95866
|
+
context_is_required: false,
|
|
95867
|
+
required_contexts: [],
|
|
95868
|
+
protection: null,
|
|
95869
|
+
observation_error: null
|
|
95870
|
+
};
|
|
95871
|
+
}
|
|
95872
|
+
if (status === 403) {
|
|
95873
|
+
return {
|
|
95874
|
+
state: "UNKNOWN",
|
|
95875
|
+
context_is_required: false,
|
|
95876
|
+
required_contexts: [],
|
|
95877
|
+
protection: null,
|
|
95878
|
+
observation_error: "403",
|
|
95879
|
+
permission_hint: "administration:read (or repo admin) required to read branch protection"
|
|
95880
|
+
};
|
|
95881
|
+
}
|
|
95882
|
+
if (status != null && status >= 400) {
|
|
95883
|
+
return {
|
|
95884
|
+
state: "UNKNOWN",
|
|
95885
|
+
context_is_required: false,
|
|
95886
|
+
required_contexts: [],
|
|
95887
|
+
protection: null,
|
|
95888
|
+
observation_error: String(status),
|
|
95889
|
+
permission_hint: read.errorMessage || `GitHub API returned HTTP ${status}`
|
|
95890
|
+
};
|
|
95891
|
+
}
|
|
95892
|
+
const protection = read && read.body && typeof read.body === "object" ? read.body : null;
|
|
95893
|
+
if (!protection) {
|
|
95894
|
+
return {
|
|
95895
|
+
state: "ABSENT",
|
|
95896
|
+
context_is_required: false,
|
|
95897
|
+
required_contexts: [],
|
|
95898
|
+
protection: null,
|
|
95899
|
+
observation_error: null
|
|
95900
|
+
};
|
|
95901
|
+
}
|
|
95902
|
+
const required_contexts = extractRequiredContexts(protection);
|
|
95903
|
+
const context_is_required = required_contexts.some((c) => c === contextName);
|
|
95904
|
+
if (context_is_required) {
|
|
95905
|
+
return {
|
|
95906
|
+
state: "REQUIRED",
|
|
95907
|
+
context_is_required: true,
|
|
95908
|
+
required_contexts,
|
|
95909
|
+
protection,
|
|
95910
|
+
observation_error: null
|
|
95911
|
+
};
|
|
95912
|
+
}
|
|
95913
|
+
return {
|
|
95914
|
+
state: "PRESENT_NOT_REQUIRED",
|
|
95915
|
+
context_is_required: false,
|
|
95916
|
+
required_contexts,
|
|
95917
|
+
protection,
|
|
95918
|
+
observation_error: null
|
|
95919
|
+
};
|
|
95920
|
+
}
|
|
95921
|
+
function buildProtectionUpdatePayload(protection, contextName = CHECK_NAME) {
|
|
95922
|
+
const p = protection && typeof protection === "object" ? protection : {};
|
|
95923
|
+
const prev = p.required_status_checks && typeof p.required_status_checks === "object" ? p.required_status_checks : {};
|
|
95924
|
+
const contexts = extractRequiredContexts(p);
|
|
95925
|
+
const nextContexts = contexts.includes(contextName) ? contexts.slice() : contexts.concat([contextName]);
|
|
95926
|
+
const body = {
|
|
95927
|
+
required_status_checks: {
|
|
95928
|
+
strict: prev.strict === true,
|
|
95929
|
+
contexts: nextContexts,
|
|
95930
|
+
// Prefer also sending checks[] when the API used that shape so app ids survive when present.
|
|
95931
|
+
...Array.isArray(prev.checks) && prev.checks.length ? {
|
|
95932
|
+
checks: nextContexts.map((ctx) => {
|
|
95933
|
+
const existing = prev.checks.find((c) => c && c.context === ctx);
|
|
95934
|
+
return existing && existing.app_id != null ? { context: ctx, app_id: existing.app_id } : { context: ctx };
|
|
95935
|
+
})
|
|
95936
|
+
} : {}
|
|
95937
|
+
},
|
|
95938
|
+
enforce_admins: !!(p.enforce_admins && p.enforce_admins.enabled),
|
|
95939
|
+
required_pull_request_reviews: p.required_pull_request_reviews ? serializePrReviews(p.required_pull_request_reviews) : null,
|
|
95940
|
+
restrictions: p.restrictions ? {
|
|
95941
|
+
users: (p.restrictions.users || []).map((u) => u.login || u).filter(Boolean),
|
|
95942
|
+
teams: (p.restrictions.teams || []).map((t) => t.slug || t).filter(Boolean),
|
|
95943
|
+
apps: (p.restrictions.apps || []).map((a) => a.slug || a).filter(Boolean)
|
|
95944
|
+
} : null
|
|
95945
|
+
};
|
|
95946
|
+
if (typeof p.required_linear_history === "boolean") {
|
|
95947
|
+
body.required_linear_history = p.required_linear_history;
|
|
95948
|
+
} else if (p.required_linear_history && typeof p.required_linear_history.enabled === "boolean") {
|
|
95949
|
+
body.required_linear_history = p.required_linear_history.enabled;
|
|
95950
|
+
}
|
|
95951
|
+
if (typeof p.allow_force_pushes === "boolean") {
|
|
95952
|
+
body.allow_force_pushes = p.allow_force_pushes;
|
|
95953
|
+
} else if (p.allow_force_pushes && typeof p.allow_force_pushes.enabled === "boolean") {
|
|
95954
|
+
body.allow_force_pushes = p.allow_force_pushes.enabled;
|
|
95955
|
+
}
|
|
95956
|
+
if (typeof p.allow_deletions === "boolean") {
|
|
95957
|
+
body.allow_deletions = p.allow_deletions;
|
|
95958
|
+
} else if (p.allow_deletions && typeof p.allow_deletions.enabled === "boolean") {
|
|
95959
|
+
body.allow_deletions = p.allow_deletions.enabled;
|
|
95960
|
+
}
|
|
95961
|
+
if (typeof p.block_creations === "boolean") {
|
|
95962
|
+
body.block_creations = p.block_creations;
|
|
95963
|
+
} else if (p.block_creations && typeof p.block_creations.enabled === "boolean") {
|
|
95964
|
+
body.block_creations = p.block_creations.enabled;
|
|
95965
|
+
}
|
|
95966
|
+
if (typeof p.required_conversation_resolution === "boolean") {
|
|
95967
|
+
body.required_conversation_resolution = p.required_conversation_resolution;
|
|
95968
|
+
} else if (p.required_conversation_resolution && typeof p.required_conversation_resolution.enabled === "boolean") {
|
|
95969
|
+
body.required_conversation_resolution = p.required_conversation_resolution.enabled;
|
|
95970
|
+
}
|
|
95971
|
+
return body;
|
|
95972
|
+
}
|
|
95973
|
+
function serializePrReviews(rpr) {
|
|
95974
|
+
if (!rpr || typeof rpr !== "object") return null;
|
|
95975
|
+
return {
|
|
95976
|
+
dismiss_stale_reviews: !!rpr.dismiss_stale_reviews,
|
|
95977
|
+
require_code_owner_reviews: !!rpr.require_code_owner_reviews,
|
|
95978
|
+
required_approving_review_count: Number(rpr.required_approving_review_count) || 0,
|
|
95979
|
+
require_last_push_approval: !!rpr.require_last_push_approval,
|
|
95980
|
+
...Array.isArray(rpr.bypass_pull_request_allowances?.users) ? {
|
|
95981
|
+
bypass_pull_request_allowances: {
|
|
95982
|
+
users: (rpr.bypass_pull_request_allowances.users || []).map((u) => u.login || u).filter(Boolean),
|
|
95983
|
+
teams: (rpr.bypass_pull_request_allowances.teams || []).map((t) => t.slug || t).filter(Boolean),
|
|
95984
|
+
apps: (rpr.bypass_pull_request_allowances.apps || []).map((a) => a.slug || a).filter(Boolean)
|
|
95985
|
+
}
|
|
95986
|
+
} : {}
|
|
95987
|
+
};
|
|
95988
|
+
}
|
|
95989
|
+
function detectRulesetRequiredCheck(rulesets, contextName = CHECK_NAME) {
|
|
95990
|
+
const names = [];
|
|
95991
|
+
const list = Array.isArray(rulesets) ? rulesets : [];
|
|
95992
|
+
for (const rs of list) {
|
|
95993
|
+
if (!rs || typeof rs !== "object") continue;
|
|
95994
|
+
const rules = Array.isArray(rs.rules) ? rs.rules : [];
|
|
95995
|
+
for (const rule of rules) {
|
|
95996
|
+
if (!rule || rule.type !== "required_status_checks") continue;
|
|
95997
|
+
const params = rule.parameters || {};
|
|
95998
|
+
const checks = Array.isArray(params.required_status_checks) ? params.required_status_checks : Array.isArray(params.contexts) ? params.contexts.map((c) => ({ context: c })) : [];
|
|
95999
|
+
for (const c of checks) {
|
|
96000
|
+
const ctx = typeof c === "string" ? c : c && c.context;
|
|
96001
|
+
if (ctx === contextName) {
|
|
96002
|
+
names.push(String(rs.name || rs.id || "ruleset"));
|
|
96003
|
+
}
|
|
96004
|
+
}
|
|
96005
|
+
}
|
|
96006
|
+
}
|
|
96007
|
+
return { enforced: names.length > 0, ruleset_names: [...new Set(names)] };
|
|
96008
|
+
}
|
|
96009
|
+
function defaultGit(args, cwd) {
|
|
96010
|
+
return execFileSync("git", args, {
|
|
96011
|
+
cwd: cwd || process.cwd(),
|
|
96012
|
+
encoding: "utf8",
|
|
96013
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
96014
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
96015
|
+
}).trim();
|
|
96016
|
+
}
|
|
96017
|
+
function parseGitHubRemote(remoteUrl) {
|
|
96018
|
+
const s = String(remoteUrl || "").trim();
|
|
96019
|
+
let m = s.match(/^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/i);
|
|
96020
|
+
if (m) return { owner: m[1], repo: m[2].replace(/\.git$/i, "") };
|
|
96021
|
+
m = s.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
96022
|
+
if (m) return { owner: m[1], repo: m[2].replace(/\.git$/i, "") };
|
|
96023
|
+
m = s.match(/^ssh:\/\/git@github\.com\/([^/]+)\/([^/]+?)(?:\.git)?\/?$/i);
|
|
96024
|
+
if (m) return { owner: m[1], repo: m[2].replace(/\.git$/i, "") };
|
|
96025
|
+
return null;
|
|
96026
|
+
}
|
|
96027
|
+
function resolveOwnerRepo(cwd, gitImpl = defaultGit) {
|
|
96028
|
+
let url;
|
|
96029
|
+
try {
|
|
96030
|
+
url = gitImpl(["remote", "get-url", "origin"], cwd);
|
|
96031
|
+
} catch {
|
|
96032
|
+
try {
|
|
96033
|
+
url = gitImpl(["config", "--get", "remote.origin.url"], cwd);
|
|
96034
|
+
} catch {
|
|
96035
|
+
return null;
|
|
96036
|
+
}
|
|
96037
|
+
}
|
|
96038
|
+
return parseGitHubRemote(url);
|
|
96039
|
+
}
|
|
96040
|
+
function resolveDefaultBranch(cwd, gitImpl = defaultGit) {
|
|
96041
|
+
try {
|
|
96042
|
+
const ref = gitImpl(["symbolic-ref", "refs/remotes/origin/HEAD"], cwd);
|
|
96043
|
+
const m = ref.match(/refs\/remotes\/origin\/(.+)$/);
|
|
96044
|
+
if (m) return m[1];
|
|
96045
|
+
} catch {
|
|
96046
|
+
}
|
|
96047
|
+
for (const b of ["main", "master"]) {
|
|
96048
|
+
try {
|
|
96049
|
+
gitImpl(["rev-parse", "--verify", `origin/${b}`], cwd);
|
|
96050
|
+
return b;
|
|
96051
|
+
} catch {
|
|
96052
|
+
}
|
|
96053
|
+
}
|
|
96054
|
+
try {
|
|
96055
|
+
return gitImpl(["branch", "--show-current"], cwd) || "main";
|
|
96056
|
+
} catch {
|
|
96057
|
+
return "main";
|
|
96058
|
+
}
|
|
96059
|
+
}
|
|
96060
|
+
function defaultGhAvailable() {
|
|
96061
|
+
try {
|
|
96062
|
+
execFileSync("gh", ["--version"], { stdio: ["ignore", "pipe", "pipe"], encoding: "utf8" });
|
|
96063
|
+
return true;
|
|
96064
|
+
} catch {
|
|
96065
|
+
return false;
|
|
96066
|
+
}
|
|
96067
|
+
}
|
|
96068
|
+
function runGhApi(args, { cwd, ghRunner } = {}) {
|
|
96069
|
+
const runner = ghRunner || ((a, c) => execFileSync("gh", a, {
|
|
96070
|
+
cwd: c || process.cwd(),
|
|
96071
|
+
encoding: "utf8",
|
|
96072
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
96073
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
96074
|
+
}));
|
|
96075
|
+
try {
|
|
96076
|
+
const raw = runner(["api", ...args], cwd);
|
|
96077
|
+
let body = null;
|
|
96078
|
+
try {
|
|
96079
|
+
body = JSON.parse(raw);
|
|
96080
|
+
} catch {
|
|
96081
|
+
body = raw;
|
|
96082
|
+
}
|
|
96083
|
+
return { ok: true, status: 200, body, raw: String(raw) };
|
|
96084
|
+
} catch (err) {
|
|
96085
|
+
const stderr = err && err.stderr ? String(err.stderr) : "";
|
|
96086
|
+
const stdout = err && err.stdout ? String(err.stdout) : "";
|
|
96087
|
+
const msg = stderr || err && err.message || String(err);
|
|
96088
|
+
const m = msg.match(/HTTP\s+(\d{3})/i) || stdout.match(/"status"\s*:\s*"(\d{3})"/);
|
|
96089
|
+
const status = m ? Number(m[1]) : err && err.status === 1 ? null : null;
|
|
96090
|
+
let body = null;
|
|
96091
|
+
try {
|
|
96092
|
+
body = JSON.parse(stdout);
|
|
96093
|
+
} catch {
|
|
96094
|
+
}
|
|
96095
|
+
let httpStatus = status;
|
|
96096
|
+
if (httpStatus == null) {
|
|
96097
|
+
if (/403|Forbidden|Resource not accessible/i.test(msg)) httpStatus = 403;
|
|
96098
|
+
else if (/404|Not Found/i.test(msg)) httpStatus = 404;
|
|
96099
|
+
else httpStatus = 500;
|
|
96100
|
+
}
|
|
96101
|
+
return {
|
|
96102
|
+
ok: false,
|
|
96103
|
+
status: httpStatus,
|
|
96104
|
+
body,
|
|
96105
|
+
raw: stdout || msg,
|
|
96106
|
+
errorMessage: msg.slice(0, 400)
|
|
96107
|
+
};
|
|
96108
|
+
}
|
|
96109
|
+
}
|
|
96110
|
+
function protectionGetPath(owner, repo, branch) {
|
|
96111
|
+
return `repos/${owner}/${repo}/branches/${encodeURIComponent(branch)}/protection`;
|
|
96112
|
+
}
|
|
96113
|
+
function protectionPutArgs(owner, repo, branch, payload) {
|
|
96114
|
+
return {
|
|
96115
|
+
args: [
|
|
96116
|
+
"--method",
|
|
96117
|
+
"PUT",
|
|
96118
|
+
protectionGetPath(owner, repo, branch),
|
|
96119
|
+
"--input",
|
|
96120
|
+
"-"
|
|
96121
|
+
],
|
|
96122
|
+
input: JSON.stringify(payload)
|
|
96123
|
+
};
|
|
96124
|
+
}
|
|
96125
|
+
function printApplyCommand(owner, repo, branch, payload) {
|
|
96126
|
+
const path = protectionGetPath(owner, repo, branch);
|
|
96127
|
+
const json = JSON.stringify(payload, null, 2);
|
|
96128
|
+
return [
|
|
96129
|
+
`# Read-modify-write: add '${CHECK_NAME}' to required status checks (preserves other settings)`,
|
|
96130
|
+
`gh api --method PUT ${path} --input - <<'EOF'`,
|
|
96131
|
+
json,
|
|
96132
|
+
"EOF"
|
|
96133
|
+
].join("\n");
|
|
96134
|
+
}
|
|
96135
|
+
async function runSetupRequiredCheck(options = {}, deps = {}) {
|
|
96136
|
+
const cwd = deps.cwd || process.cwd();
|
|
96137
|
+
const gitImpl = deps.gitImpl || defaultGit;
|
|
96138
|
+
const log = deps.log || console.log.bind(console);
|
|
96139
|
+
const logErr = deps.logErr || console.error.bind(console);
|
|
96140
|
+
const ghAvailable = deps.ghAvailable != null ? deps.ghAvailable : defaultGhAvailable;
|
|
96141
|
+
const ghRunner = deps.ghRunner;
|
|
96142
|
+
const runApi = deps.runGhApi || ((args, o) => runGhApi(args, { ...o, ghRunner }));
|
|
96143
|
+
const doExit = deps.exit !== false;
|
|
96144
|
+
const finish = (code, payload2) => {
|
|
96145
|
+
if (options.json) log(JSON.stringify(payload2, null, 2));
|
|
96146
|
+
if (doExit) process.exit(code);
|
|
96147
|
+
return { exitCode: code, ...payload2 };
|
|
96148
|
+
};
|
|
96149
|
+
const ownerRepo = options.repo ? (() => {
|
|
96150
|
+
const m = String(options.repo).match(/^([^/]+)\/([^/]+)$/);
|
|
96151
|
+
return m ? { owner: m[1], repo: m[2] } : null;
|
|
96152
|
+
})() : resolveOwnerRepo(cwd, gitImpl);
|
|
96153
|
+
if (!ownerRepo) {
|
|
96154
|
+
logErr(chalk.red("Could not resolve owner/repo from git remote origin (or --repo OWNER/REPO)."));
|
|
96155
|
+
return finish(EXIT.ERROR, { ok: false, code: "REPO_UNRESOLVED" });
|
|
96156
|
+
}
|
|
96157
|
+
const { owner, repo } = ownerRepo;
|
|
96158
|
+
const branch = options.branch || resolveDefaultBranch(cwd, gitImpl);
|
|
96159
|
+
if (!ghAvailable()) {
|
|
96160
|
+
logErr(chalk.yellow("GitHub CLI (`gh`) not found on PATH."));
|
|
96161
|
+
logErr("Install: https://cli.github.com/ then: gh auth login");
|
|
96162
|
+
logErr("");
|
|
96163
|
+
logErr("Manual check (your credentials, not a CodeRifts key):");
|
|
96164
|
+
logErr(` gh api ${protectionGetPath(owner, repo, branch)}`);
|
|
96165
|
+
logErr("");
|
|
96166
|
+
logErr(`Required check name: ${CHECK_NAME}`);
|
|
96167
|
+
logErr("The CodeRifts App never writes branch protection (needs administration:write).");
|
|
96168
|
+
return finish(EXIT.ERROR, { ok: false, code: "GH_MISSING", owner, repo, branch, check: CHECK_NAME });
|
|
96169
|
+
}
|
|
96170
|
+
const getPath = protectionGetPath(owner, repo, branch);
|
|
96171
|
+
const read = runApi([getPath], { cwd });
|
|
96172
|
+
const obs = classifyObservation(read, CHECK_NAME);
|
|
96173
|
+
let ruleset = { enforced: false, ruleset_names: [] };
|
|
96174
|
+
try {
|
|
96175
|
+
const rsList = runApi([`repos/${owner}/${repo}/rulesets`], { cwd });
|
|
96176
|
+
if (rsList.ok && Array.isArray(rsList.body)) {
|
|
96177
|
+
const detailed = [];
|
|
96178
|
+
for (const rs of rsList.body) {
|
|
96179
|
+
if (!rs || rs.id == null) continue;
|
|
96180
|
+
const one = runApi([`repos/${owner}/${repo}/rulesets/${rs.id}`], { cwd });
|
|
96181
|
+
if (one.ok && one.body) detailed.push(one.body);
|
|
96182
|
+
else detailed.push(rs);
|
|
96183
|
+
}
|
|
96184
|
+
ruleset = detectRulesetRequiredCheck(detailed.length ? detailed : rsList.body, CHECK_NAME);
|
|
96185
|
+
}
|
|
96186
|
+
} catch {
|
|
96187
|
+
}
|
|
96188
|
+
const basePayload = {
|
|
96189
|
+
ok: true,
|
|
96190
|
+
owner,
|
|
96191
|
+
repo,
|
|
96192
|
+
branch,
|
|
96193
|
+
check: CHECK_NAME,
|
|
96194
|
+
classic: obs.state,
|
|
96195
|
+
context_is_required: obs.context_is_required,
|
|
96196
|
+
required_contexts: obs.required_contexts,
|
|
96197
|
+
ruleset_enforced: ruleset.enforced,
|
|
96198
|
+
ruleset_names: ruleset.ruleset_names
|
|
96199
|
+
};
|
|
96200
|
+
if (ruleset.enforced) {
|
|
96201
|
+
if (!options.json) {
|
|
96202
|
+
log(chalk.green(`Required via repository ruleset: ${CHECK_NAME}`));
|
|
96203
|
+
log(` repo: ${owner}/${repo}`);
|
|
96204
|
+
log(` branch: ${branch}`);
|
|
96205
|
+
log(` rulesets: ${ruleset.ruleset_names.join(", ") || "(named)"}`);
|
|
96206
|
+
log(" Classic branch protection may still be ABSENT \u2014 rulesets enforce separately.");
|
|
96207
|
+
}
|
|
96208
|
+
return finish(EXIT.OK, { ...basePayload, code: "RULESET_REQUIRED" });
|
|
96209
|
+
}
|
|
96210
|
+
if (obs.state === "REQUIRED") {
|
|
96211
|
+
if (!options.json) {
|
|
96212
|
+
log(chalk.green(`Already required: '${CHECK_NAME}'`));
|
|
96213
|
+
log(` repo: ${owner}/${repo}`);
|
|
96214
|
+
log(` branch: ${branch}`);
|
|
96215
|
+
log(" (classic branch protection \u2014 idempotent; nothing to do)");
|
|
96216
|
+
}
|
|
96217
|
+
return finish(EXIT.OK, { ...basePayload, code: "ALREADY_REQUIRED" });
|
|
96218
|
+
}
|
|
96219
|
+
if (obs.state === "UNKNOWN") {
|
|
96220
|
+
if (!options.json) {
|
|
96221
|
+
logErr(chalk.red("Cannot observe branch protection (UNKNOWN)."));
|
|
96222
|
+
logErr(` HTTP: ${obs.observation_error || "unknown"}`);
|
|
96223
|
+
logErr(` ${obs.permission_hint || "An admin with administration:read must run this command."}`);
|
|
96224
|
+
logErr(" The CodeRifts App never writes protection; an admin must grant or run this.");
|
|
96225
|
+
}
|
|
96226
|
+
return finish(EXIT.PERMISSION, { ...basePayload, ok: false, code: "PERMISSION", observation_error: obs.observation_error });
|
|
96227
|
+
}
|
|
96228
|
+
let payload;
|
|
96229
|
+
if (obs.state === "ABSENT" || !obs.protection) {
|
|
96230
|
+
payload = {
|
|
96231
|
+
required_status_checks: {
|
|
96232
|
+
strict: false,
|
|
96233
|
+
contexts: [CHECK_NAME]
|
|
96234
|
+
},
|
|
96235
|
+
enforce_admins: false,
|
|
96236
|
+
required_pull_request_reviews: null,
|
|
96237
|
+
restrictions: null
|
|
96238
|
+
};
|
|
96239
|
+
} else {
|
|
96240
|
+
payload = buildProtectionUpdatePayload(obs.protection, CHECK_NAME);
|
|
96241
|
+
}
|
|
96242
|
+
const cmdText = printApplyCommand(owner, repo, branch, payload);
|
|
96243
|
+
if (!options.apply) {
|
|
96244
|
+
if (!options.json) {
|
|
96245
|
+
log(chalk.yellow(`Status: ${obs.state} \u2014 '${CHECK_NAME}' is not a required check.`));
|
|
96246
|
+
log(` repo: ${owner}/${repo}`);
|
|
96247
|
+
log(` branch: ${branch}`);
|
|
96248
|
+
log("");
|
|
96249
|
+
log("Dry-run (default). Exact command to add the required check with your credentials:");
|
|
96250
|
+
log("");
|
|
96251
|
+
log(cmdText);
|
|
96252
|
+
log("");
|
|
96253
|
+
log("Re-run with --apply to execute that PUT and re-verify.");
|
|
96254
|
+
log("Why the App never writes this: administration:write is a trust jump we refuse (audit 3.4/1, 3.5).");
|
|
96255
|
+
}
|
|
96256
|
+
return finish(EXIT.NEEDS_APPLY, {
|
|
96257
|
+
...basePayload,
|
|
96258
|
+
code: "NEEDS_APPLY",
|
|
96259
|
+
apply_command: cmdText,
|
|
96260
|
+
apply_payload: payload
|
|
96261
|
+
});
|
|
96262
|
+
}
|
|
96263
|
+
const put = protectionPutArgs(owner, repo, branch, payload);
|
|
96264
|
+
let putResult;
|
|
96265
|
+
if (deps.ghApply) {
|
|
96266
|
+
putResult = deps.ghApply(put, { cwd });
|
|
96267
|
+
} else {
|
|
96268
|
+
try {
|
|
96269
|
+
const raw = execFileSync("gh", ["api", ...put.args], {
|
|
96270
|
+
cwd,
|
|
96271
|
+
encoding: "utf8",
|
|
96272
|
+
input: put.input,
|
|
96273
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
96274
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
96275
|
+
});
|
|
96276
|
+
putResult = { ok: true, status: 200, body: JSON.parse(raw || "{}"), raw };
|
|
96277
|
+
} catch (err) {
|
|
96278
|
+
const msg = err && err.stderr ? String(err.stderr) : err && err.message || String(err);
|
|
96279
|
+
const m = msg.match(/HTTP\s+(\d{3})/i);
|
|
96280
|
+
putResult = {
|
|
96281
|
+
ok: false,
|
|
96282
|
+
status: m ? Number(m[1]) : 500,
|
|
96283
|
+
errorMessage: msg.slice(0, 400),
|
|
96284
|
+
raw: msg
|
|
96285
|
+
};
|
|
96286
|
+
}
|
|
96287
|
+
}
|
|
96288
|
+
if (!putResult.ok) {
|
|
96289
|
+
if (!options.json) {
|
|
96290
|
+
logErr(chalk.red("Apply failed."));
|
|
96291
|
+
logErr(` ${putResult.errorMessage || putResult.status}`);
|
|
96292
|
+
if (putResult.status === 403) {
|
|
96293
|
+
logErr(" Need administration:write on the repository (admin).");
|
|
96294
|
+
}
|
|
96295
|
+
}
|
|
96296
|
+
return finish(
|
|
96297
|
+
putResult.status === 403 ? EXIT.PERMISSION : EXIT.ERROR,
|
|
96298
|
+
{ ...basePayload, ok: false, code: "APPLY_FAILED", apply_status: putResult.status }
|
|
96299
|
+
);
|
|
96300
|
+
}
|
|
96301
|
+
const reRead = runApi([getPath], { cwd });
|
|
96302
|
+
const reObs = classifyObservation(reRead, CHECK_NAME);
|
|
96303
|
+
if (reObs.state !== "REQUIRED") {
|
|
96304
|
+
if (!options.json) {
|
|
96305
|
+
logErr(chalk.red("Apply returned success but re-read did not show the check as required."));
|
|
96306
|
+
logErr(` classic state after apply: ${reObs.state}`);
|
|
96307
|
+
logErr(" Unverified apply is not success.");
|
|
96308
|
+
}
|
|
96309
|
+
return finish(EXIT.ERROR, {
|
|
96310
|
+
...basePayload,
|
|
96311
|
+
ok: false,
|
|
96312
|
+
code: "VERIFY_FAILED",
|
|
96313
|
+
classic_after: reObs.state
|
|
96314
|
+
});
|
|
96315
|
+
}
|
|
96316
|
+
if (!options.json) {
|
|
96317
|
+
log(chalk.green(`Success: '${CHECK_NAME}' is now required.`));
|
|
96318
|
+
log(` repo: ${owner}/${repo}`);
|
|
96319
|
+
log(` branch: ${branch}`);
|
|
96320
|
+
}
|
|
96321
|
+
return finish(EXIT.OK, {
|
|
96322
|
+
...basePayload,
|
|
96323
|
+
code: "APPLIED",
|
|
96324
|
+
classic: "REQUIRED",
|
|
96325
|
+
context_is_required: true
|
|
96326
|
+
});
|
|
96327
|
+
}
|
|
96328
|
+
module2.exports = {
|
|
96329
|
+
runSetupRequiredCheck,
|
|
96330
|
+
CHECK_NAME,
|
|
96331
|
+
EXIT,
|
|
96332
|
+
extractRequiredContexts,
|
|
96333
|
+
classifyObservation,
|
|
96334
|
+
buildProtectionUpdatePayload,
|
|
96335
|
+
detectRulesetRequiredCheck,
|
|
96336
|
+
parseGitHubRemote,
|
|
96337
|
+
resolveOwnerRepo,
|
|
96338
|
+
resolveDefaultBranch,
|
|
96339
|
+
printApplyCommand,
|
|
96340
|
+
protectionGetPath,
|
|
96341
|
+
protectionPutArgs,
|
|
96342
|
+
runGhApi
|
|
96343
|
+
};
|
|
96344
|
+
}
|
|
96345
|
+
});
|
|
96346
|
+
|
|
96347
|
+
// src/commands/status.js
|
|
96348
|
+
var require_status = __commonJS({
|
|
96349
|
+
"src/commands/status.js"(exports2, module2) {
|
|
96350
|
+
"use strict";
|
|
96351
|
+
var chalk = require_source();
|
|
96352
|
+
var { getApiKey } = require_config();
|
|
96353
|
+
var { cloudGetEnforcementStatus } = require_cloud();
|
|
96354
|
+
var { renderJson } = require_json2();
|
|
96355
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
96356
|
+
var USAGE = [
|
|
96357
|
+
"Usage: coderifts status --repo owner/repo",
|
|
96358
|
+
" or: coderifts status owner/repo",
|
|
96359
|
+
"",
|
|
96360
|
+
"Read-only: prints the cross-layer enforcement report from the CodeRifts API.",
|
|
96361
|
+
"Requires a cloud API key (coderifts login or CODERIFTS_API_KEY)."
|
|
96362
|
+
].join("\n");
|
|
96363
|
+
function isValidRepo(full) {
|
|
96364
|
+
const parts = String(full || "").split("/");
|
|
96365
|
+
if (parts.length !== 2) return false;
|
|
96366
|
+
const [owner, repo] = parts.map((p) => p.trim());
|
|
96367
|
+
return !!(owner && repo);
|
|
96368
|
+
}
|
|
96369
|
+
function colorStatus(status) {
|
|
96370
|
+
const s = String(status == null ? "" : status);
|
|
96371
|
+
const upper = s.toUpperCase();
|
|
96372
|
+
if (upper === "ENFORCING") return chalk.green(s);
|
|
96373
|
+
if (upper === "ADVISORY" || s === "declared_required" || s === "declared_optional") {
|
|
96374
|
+
return chalk.yellow(s);
|
|
96375
|
+
}
|
|
96376
|
+
if (upper === "ABSENT" || s === "no_config" || s === "not_observable_from_server") {
|
|
96377
|
+
return chalk.dim(s);
|
|
96378
|
+
}
|
|
96379
|
+
if (upper === "UNKNOWN" || s === "unknown") return chalk.red(s);
|
|
96380
|
+
if (upper === "PARTIAL") return chalk.yellow(s);
|
|
96381
|
+
return chalk.white(s);
|
|
96382
|
+
}
|
|
96383
|
+
function residualsFromReport(report) {
|
|
96384
|
+
const residuals = [];
|
|
96385
|
+
const legs = report && report.legs || {};
|
|
96386
|
+
const merge = legs.merge;
|
|
96387
|
+
if (merge) {
|
|
96388
|
+
if (merge.enforcing !== true) {
|
|
96389
|
+
residuals.push(`merge:${merge.status || "UNKNOWN"}`);
|
|
96390
|
+
}
|
|
96391
|
+
} else {
|
|
96392
|
+
residuals.push("merge:missing");
|
|
96393
|
+
}
|
|
96394
|
+
const deploy = legs.deploy;
|
|
96395
|
+
if (deploy) {
|
|
96396
|
+
residuals.push(`deploy:${deploy.status || "unknown"}`);
|
|
96397
|
+
} else {
|
|
96398
|
+
residuals.push("deploy:missing");
|
|
96399
|
+
}
|
|
96400
|
+
const runtime = legs.runtime;
|
|
96401
|
+
if (runtime) {
|
|
96402
|
+
residuals.push(`runtime:${runtime.status || "not_observable_from_server"}`);
|
|
96403
|
+
}
|
|
96404
|
+
const content = legs.content;
|
|
96405
|
+
if (content) {
|
|
96406
|
+
residuals.push(`content:${content.status || "not_observable_from_server"}`);
|
|
96407
|
+
}
|
|
96408
|
+
return residuals;
|
|
96409
|
+
}
|
|
96410
|
+
function renderStatusReport(report) {
|
|
96411
|
+
const lines = [];
|
|
96412
|
+
const repo = report && report.repo || "(unknown repo)";
|
|
96413
|
+
const legs = report && report.legs || {};
|
|
96414
|
+
const summary = report && report.summary || {};
|
|
96415
|
+
lines.push(chalk.bold(`CodeRifts enforcement \u2014 ${repo}`));
|
|
96416
|
+
if (report && report.timestamp) {
|
|
96417
|
+
lines.push(chalk.dim(` as of ${report.timestamp}`));
|
|
96418
|
+
}
|
|
96419
|
+
lines.push("");
|
|
96420
|
+
const order = [
|
|
96421
|
+
["Runtime", legs.runtime],
|
|
96422
|
+
["Merge", legs.merge],
|
|
96423
|
+
["Deploy", legs.deploy],
|
|
96424
|
+
["Content", legs.content]
|
|
96425
|
+
];
|
|
96426
|
+
for (const [label, leg] of order) {
|
|
96427
|
+
if (!leg) {
|
|
96428
|
+
lines.push(` ${label.padEnd(10)} ${chalk.dim("\u2014")}`);
|
|
96429
|
+
continue;
|
|
96430
|
+
}
|
|
96431
|
+
const statusStr = colorStatus(leg.status);
|
|
96432
|
+
const enforcing = leg.enforcing === true ? chalk.green("enforcing=true") : chalk.dim("enforcing=false");
|
|
96433
|
+
const epi = leg.epistemic_status ? chalk.dim(` [${leg.epistemic_status}]`) : "";
|
|
96434
|
+
lines.push(` ${label.padEnd(10)} ${statusStr} ${enforcing}${epi}`);
|
|
96435
|
+
if (leg.note) {
|
|
96436
|
+
lines.push(chalk.dim(` ${leg.note.slice(0, 100)}${leg.note.length > 100 ? "\u2026" : ""}`));
|
|
96437
|
+
}
|
|
96438
|
+
if (leg.epistemic_note && leg.leg === "deploy") {
|
|
96439
|
+
lines.push(chalk.dim(` ${leg.epistemic_note.slice(0, 100)}\u2026`));
|
|
96440
|
+
}
|
|
96441
|
+
}
|
|
96442
|
+
const residuals = residualsFromReport(report);
|
|
96443
|
+
lines.push("");
|
|
96444
|
+
lines.push(` Residuals: ${residuals.length ? residuals.map((r) => chalk.yellow(r)).join(", ") : chalk.green("(none \u2014 merge enforcing; other legs still not server-observable)")}`);
|
|
96445
|
+
if (summary.status) {
|
|
96446
|
+
lines.push("");
|
|
96447
|
+
lines.push(chalk.dim(` summary.status: ${summary.status}`));
|
|
96448
|
+
if (summary.can_claim_fully_enforced === false) {
|
|
96449
|
+
lines.push(chalk.dim(" can_claim_fully_enforced: false (server ceiling)"));
|
|
96450
|
+
}
|
|
96451
|
+
}
|
|
96452
|
+
lines.push("");
|
|
96453
|
+
lines.push(chalk.dim(" Read-only report. To close gaps:"));
|
|
96454
|
+
lines.push(chalk.dim(" merge \u2192 coderifts setup-required-check"));
|
|
96455
|
+
lines.push(chalk.dim(" deploy \u2192 coderifts deploy-gate --enforce (CD step)"));
|
|
96456
|
+
lines.push(chalk.dim(" runtime/content \u2192 wire @coderifts/agent-guard in the agent host"));
|
|
96457
|
+
return lines.join("\n");
|
|
96458
|
+
}
|
|
96459
|
+
async function runStatus(options = {}, deps = {}) {
|
|
96460
|
+
const getKey = deps.getApiKey || getApiKey;
|
|
96461
|
+
const fetchStatus = deps.cloudGetEnforcementStatus || cloudGetEnforcementStatus;
|
|
96462
|
+
const log = deps.log || console.log;
|
|
96463
|
+
const errLog = deps.errLog || console.error;
|
|
96464
|
+
const repo = options.repo != null && String(options.repo).trim() ? String(options.repo).trim() : null;
|
|
96465
|
+
if (!repo) {
|
|
96466
|
+
errLog(chalk.red("Error: missing repo"));
|
|
96467
|
+
errLog(USAGE);
|
|
96468
|
+
return { exitCode: 1, error: "missing_repo" };
|
|
96469
|
+
}
|
|
96470
|
+
if (!isValidRepo(repo)) {
|
|
96471
|
+
errLog(chalk.red("Error: repo must be in owner/repo form (e.g. coderifts/app)"));
|
|
96472
|
+
return { exitCode: 1, error: "invalid_repo" };
|
|
96473
|
+
}
|
|
96474
|
+
const apiKey = getKey();
|
|
96475
|
+
if (!apiKey) {
|
|
96476
|
+
errLog(chalk.red("Error: no API key. Run `coderifts login` or set CODERIFTS_API_KEY."));
|
|
96477
|
+
return { exitCode: 1, error: "missing_api_key" };
|
|
96478
|
+
}
|
|
96479
|
+
let report;
|
|
96480
|
+
try {
|
|
96481
|
+
report = await fetchStatus(repo, apiKey);
|
|
96482
|
+
} catch (e) {
|
|
96483
|
+
const msg = e && e.message ? String(e.message) : "request failed";
|
|
96484
|
+
errLog(chalk.red(`Error: ${msg}`));
|
|
96485
|
+
if (e && e.code) errLog(chalk.dim(` (${e.code})`));
|
|
96486
|
+
return { exitCode: 1, error: msg };
|
|
96487
|
+
}
|
|
96488
|
+
if (options.json) {
|
|
96489
|
+
log(renderJson(report));
|
|
96490
|
+
} else {
|
|
96491
|
+
log(renderStatusReport(report));
|
|
96492
|
+
}
|
|
96493
|
+
return { exitCode: 0, report };
|
|
96494
|
+
}
|
|
96495
|
+
module2.exports = {
|
|
96496
|
+
runStatus,
|
|
96497
|
+
renderStatusReport,
|
|
96498
|
+
residualsFromReport,
|
|
96499
|
+
colorStatus,
|
|
96500
|
+
isValidRepo,
|
|
96501
|
+
USAGE
|
|
96502
|
+
};
|
|
96503
|
+
}
|
|
96504
|
+
});
|
|
96505
|
+
|
|
94345
96506
|
// src/commands/hook.js
|
|
94346
96507
|
var require_hook = __commonJS({
|
|
94347
96508
|
"src/commands/hook.js"(exports2, module2) {
|
|
@@ -94355,6 +96516,8 @@ ${HOOK_MARKER}
|
|
|
94355
96516
|
# Checks API spec changes before pushing.
|
|
94356
96517
|
# The pre-push hook receives lines on stdin:
|
|
94357
96518
|
# <local ref> <local sha> <remote ref> <remote sha>
|
|
96519
|
+
#
|
|
96520
|
+
# Re-install after CLI upgrades: coderifts hook install
|
|
94358
96521
|
|
|
94359
96522
|
CODERIFTS_API_KEY=$(git config coderifts.apiKey)
|
|
94360
96523
|
SPEC_PATH=$(git config coderifts.specPath || echo "api/openapi.yaml")
|
|
@@ -94365,6 +96528,53 @@ if [ -z "$CODERIFTS_API_KEY" ]; then
|
|
|
94365
96528
|
exit 0 # Don't block if not configured
|
|
94366
96529
|
fi
|
|
94367
96530
|
|
|
96531
|
+
# --- helpers: three-state git blob read (present | absent | error) ---
|
|
96532
|
+
# Sets: BLOB_KIND, BLOB_CONTENT, BLOB_ERR. Never maps unreadable \u2192 empty string.
|
|
96533
|
+
git_blob_at() {
|
|
96534
|
+
_ref="$1"
|
|
96535
|
+
_path="$2"
|
|
96536
|
+
BLOB_CONTENT=""
|
|
96537
|
+
BLOB_ERR=""
|
|
96538
|
+
BLOB_KIND=""
|
|
96539
|
+
if ! git rev-parse --verify "$_ref^{commit}" >/dev/null 2>&1; then
|
|
96540
|
+
BLOB_KIND=error
|
|
96541
|
+
BLOB_ERR="GIT_ERROR: cannot resolve ref $_ref"
|
|
96542
|
+
return 1
|
|
96543
|
+
fi
|
|
96544
|
+
_errf=$(mktemp 2>/dev/null || echo "/tmp/coderifts-hook-err.$$")
|
|
96545
|
+
if BLOB_CONTENT=$(git show "$_ref:$_path" 2>"$_errf"); then
|
|
96546
|
+
BLOB_KIND=present
|
|
96547
|
+
rm -f "$_errf"
|
|
96548
|
+
return 0
|
|
96549
|
+
fi
|
|
96550
|
+
_err=$(cat "$_errf" 2>/dev/null)
|
|
96551
|
+
rm -f "$_errf"
|
|
96552
|
+
case "$_err" in
|
|
96553
|
+
*"does not exist in"*|*"exists on disk, but not in"*)
|
|
96554
|
+
BLOB_KIND=absent
|
|
96555
|
+
BLOB_CONTENT=""
|
|
96556
|
+
return 0
|
|
96557
|
+
;;
|
|
96558
|
+
esac
|
|
96559
|
+
BLOB_KIND=error
|
|
96560
|
+
BLOB_ERR="GIT_ERROR: git show $_ref:$_path failed"
|
|
96561
|
+
return 1
|
|
96562
|
+
}
|
|
96563
|
+
|
|
96564
|
+
# New-branch baseline: merge-base with default branch (not empty before).
|
|
96565
|
+
# Prints merge-base SHA on success; nonzero if none can be resolved.
|
|
96566
|
+
resolve_new_branch_base_ref() {
|
|
96567
|
+
_local="$1"
|
|
96568
|
+
for _cand in origin/HEAD origin/main origin/master main master; do
|
|
96569
|
+
_mb=$(git merge-base "$_local" "$_cand" 2>/dev/null) || continue
|
|
96570
|
+
if [ -n "$_mb" ] && [ "$_mb" != "$ZERO" ]; then
|
|
96571
|
+
echo "$_mb"
|
|
96572
|
+
return 0
|
|
96573
|
+
fi
|
|
96574
|
+
done
|
|
96575
|
+
return 1
|
|
96576
|
+
}
|
|
96577
|
+
|
|
94368
96578
|
# Read stdin lines provided by git pre-push
|
|
94369
96579
|
while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do
|
|
94370
96580
|
# Skip delete pushes
|
|
@@ -94372,41 +96582,93 @@ while read LOCAL_REF LOCAL_SHA REMOTE_REF REMOTE_SHA; do
|
|
|
94372
96582
|
continue
|
|
94373
96583
|
fi
|
|
94374
96584
|
|
|
94375
|
-
#
|
|
94376
|
-
|
|
94377
|
-
|
|
96585
|
+
# after = local (about-to-be-pushed) commit \u2014 three-state
|
|
96586
|
+
if ! git_blob_at "$LOCAL_SHA" "$SPEC_PATH"; then
|
|
96587
|
+
echo "CodeRifts: $BLOB_ERR (after=$SPEC_PATH at $LOCAL_SHA). Fail-closed."
|
|
96588
|
+
exit 1
|
|
96589
|
+
fi
|
|
96590
|
+
if [ "$BLOB_KIND" = "absent" ]; then
|
|
94378
96591
|
continue # Spec doesn't exist in local commit, skip
|
|
94379
96592
|
fi
|
|
96593
|
+
HEAD_SPEC=$BLOB_CONTENT
|
|
94380
96594
|
|
|
94381
|
-
#
|
|
96595
|
+
# before = remote tip, or for new branch (zero remote SHA) a merge-base baseline
|
|
94382
96596
|
if [ "$REMOTE_SHA" = "$ZERO" ]; then
|
|
94383
|
-
|
|
94384
|
-
|
|
96597
|
+
BASE_REF=$(resolve_new_branch_base_ref "$LOCAL_SHA") || {
|
|
96598
|
+
echo "CodeRifts: GIT_ERROR \u2014 cannot resolve merge-base baseline for new branch (tried origin/HEAD, origin/main, origin/master, main, master). Fail-closed."
|
|
96599
|
+
exit 1
|
|
96600
|
+
}
|
|
94385
96601
|
else
|
|
94386
|
-
|
|
96602
|
+
BASE_REF=$REMOTE_SHA
|
|
94387
96603
|
fi
|
|
94388
96604
|
|
|
94389
|
-
|
|
94390
|
-
|
|
94391
|
-
|
|
96605
|
+
if ! git_blob_at "$BASE_REF" "$SPEC_PATH"; then
|
|
96606
|
+
echo "CodeRifts: $BLOB_ERR (before=$SPEC_PATH at $BASE_REF). Fail-closed \u2014 not treating as new spec."
|
|
96607
|
+
exit 1
|
|
96608
|
+
fi
|
|
96609
|
+
if [ "$BLOB_KIND" = "absent" ]; then
|
|
96610
|
+
# Honest NEW_ARTIFACT: path genuinely not at baseline
|
|
96611
|
+
echo "CodeRifts: New artifact (spec absent at baseline $BASE_REF:$SPEC_PATH); allowing push."
|
|
94392
96612
|
continue
|
|
94393
96613
|
fi
|
|
96614
|
+
BASE_SPEC=$BLOB_CONTENT
|
|
94394
96615
|
|
|
94395
96616
|
# If specs are identical, nothing to check
|
|
94396
96617
|
if [ "$BASE_SPEC" = "$HEAD_SPEC" ]; then
|
|
94397
96618
|
continue
|
|
94398
96619
|
fi
|
|
94399
96620
|
|
|
94400
|
-
echo "CodeRifts: Checking API spec changes..."
|
|
96621
|
+
echo "CodeRifts: Checking API spec changes (baseline $BASE_REF)..."
|
|
94401
96622
|
|
|
94402
96623
|
RESULT=$(curl -s -X POST https://app.coderifts.com/api/v1/diff \\
|
|
94403
96624
|
-H "Authorization: Bearer $CODERIFTS_API_KEY" \\
|
|
94404
96625
|
-H "Content-Type: application/json" \\
|
|
94405
96626
|
-d "{\\"before\\": $(echo "$BASE_SPEC" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))'), \\"after\\": $(echo "$HEAD_SPEC" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))')}")
|
|
94406
96627
|
|
|
94407
|
-
|
|
94408
|
-
|
|
94409
|
-
|
|
96628
|
+
# Prefer closed-set execution_action; omega_decision only when action is absent.
|
|
96629
|
+
# Severity (unchanged): BLOCK/STOP \u2192 exit 1; REQUIRE_APPROVAL/WARN \u2192 warn; unknown action \u2192 halt.
|
|
96630
|
+
DECISION=$(echo "$RESULT" | python3 -c '
|
|
96631
|
+
import sys, json
|
|
96632
|
+
try:
|
|
96633
|
+
d = json.load(sys.stdin)
|
|
96634
|
+
except Exception:
|
|
96635
|
+
print("ALLOW")
|
|
96636
|
+
raise SystemExit(0)
|
|
96637
|
+
ea = None
|
|
96638
|
+
dr = d.get("decision_result")
|
|
96639
|
+
if isinstance(dr, dict) and isinstance(dr.get("execution_action"), str):
|
|
96640
|
+
ea = dr["execution_action"]
|
|
96641
|
+
elif isinstance(d.get("execution_action"), str):
|
|
96642
|
+
ea = d["execution_action"]
|
|
96643
|
+
closed = {"CONTINUE", "CONTINUE_WITH_MONITORING", "REQUEST_APPROVAL", "STOP"}
|
|
96644
|
+
if ea is not None and ea != "":
|
|
96645
|
+
if ea not in closed:
|
|
96646
|
+
print("UNKNOWN")
|
|
96647
|
+
raise SystemExit(0)
|
|
96648
|
+
if ea in ("CONTINUE", "CONTINUE_WITH_MONITORING"):
|
|
96649
|
+
print("ALLOW")
|
|
96650
|
+
elif ea == "STOP":
|
|
96651
|
+
print("BLOCK")
|
|
96652
|
+
else:
|
|
96653
|
+
print("REQUIRE_APPROVAL")
|
|
96654
|
+
raise SystemExit(0)
|
|
96655
|
+
od = d.get("omega_decision") or d.get("decision") or "ALLOW"
|
|
96656
|
+
print(od)
|
|
96657
|
+
' 2>/dev/null)
|
|
96658
|
+
OMEGA=$(echo "$RESULT" | python3 -c "import sys,json; print(json.load(sys.stdin).get('omega_api',0))" 2>/dev/null)
|
|
96659
|
+
BREAKING=$(echo "$RESULT" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('breaking_changes',[])))" 2>/dev/null)
|
|
96660
|
+
|
|
96661
|
+
if [ "$DECISION" = "UNKNOWN" ]; then
|
|
96662
|
+
echo ""
|
|
96663
|
+
echo "========================================"
|
|
96664
|
+
echo " CodeRifts: PUSH BLOCKED"
|
|
96665
|
+
echo "========================================"
|
|
96666
|
+
echo " Reason: unrecognised execution_action is not permission"
|
|
96667
|
+
echo " Fail-closed (unknown present action)."
|
|
96668
|
+
echo "========================================"
|
|
96669
|
+
echo ""
|
|
96670
|
+
exit 1
|
|
96671
|
+
fi
|
|
94410
96672
|
|
|
94411
96673
|
if [ "$DECISION" = "BLOCK" ]; then
|
|
94412
96674
|
echo ""
|
|
@@ -94474,6 +96736,12 @@ exit 0
|
|
|
94474
96736
|
console.log("");
|
|
94475
96737
|
console.log("Optionally set spec path (default: api/openapi.yaml):");
|
|
94476
96738
|
console.log(" git config coderifts.specPath path/to/openapi.yaml");
|
|
96739
|
+
console.log("");
|
|
96740
|
+
console.log("After upgrading the coderifts CLI, re-run this command so the installed");
|
|
96741
|
+
console.log("hook matches the package (already-installed hooks are not auto-updated):");
|
|
96742
|
+
console.log(" coderifts hook install");
|
|
96743
|
+
console.log("");
|
|
96744
|
+
console.log("Agent-using repo? Run: coderifts agent-setup");
|
|
94477
96745
|
}
|
|
94478
96746
|
function uninstall() {
|
|
94479
96747
|
const gitDir = findGitDir();
|
|
@@ -94530,6 +96798,437 @@ exit 0
|
|
|
94530
96798
|
}
|
|
94531
96799
|
});
|
|
94532
96800
|
|
|
96801
|
+
// src/commands/enforce.js
|
|
96802
|
+
var require_enforce = __commonJS({
|
|
96803
|
+
"src/commands/enforce.js"(exports2, module2) {
|
|
96804
|
+
"use strict";
|
|
96805
|
+
var chalk = require_source();
|
|
96806
|
+
var { getApiKey } = require_config();
|
|
96807
|
+
var { cloudGetEnforcementStatus } = require_cloud();
|
|
96808
|
+
var {
|
|
96809
|
+
renderStatusReport,
|
|
96810
|
+
isValidRepo
|
|
96811
|
+
} = require_status();
|
|
96812
|
+
var { runSetupRequiredCheck } = require_setup_required_check();
|
|
96813
|
+
var path = require("path");
|
|
96814
|
+
var hookMod = require_hook();
|
|
96815
|
+
var hookInstall = hookMod.install;
|
|
96816
|
+
var isCodeRiftsHook = hookMod.isCodeRiftsHook;
|
|
96817
|
+
var findGitDir = hookMod.findGitDir;
|
|
96818
|
+
var getHookPath = typeof hookMod.getHookPath === "function" ? hookMod.getHookPath : (gitDir) => path.join(gitDir, "hooks", "pre-push");
|
|
96819
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
96820
|
+
var USAGE = [
|
|
96821
|
+
"Usage: coderifts enforce --repo owner/repo [--apply]",
|
|
96822
|
+
" or: coderifts enforce owner/repo [--apply]",
|
|
96823
|
+
"",
|
|
96824
|
+
"Cross-layer orchestrator: reads enforcement-status, then closes gaps by calling existing",
|
|
96825
|
+
"setup commands (setup-required-check, hook install, deploy-gate guidance).",
|
|
96826
|
+
"",
|
|
96827
|
+
"DRY-RUN BY DEFAULT \u2014 without --apply, prints what WOULD run and mutates NOTHING.",
|
|
96828
|
+
"With --apply, threads apply into each underlying command (their own safety still applies).",
|
|
96829
|
+
"",
|
|
96830
|
+
"Requires a cloud API key for the status read (coderifts login / CODERIFTS_API_KEY).",
|
|
96831
|
+
"Merge apply uses your local `gh` credentials, not the CodeRifts API key."
|
|
96832
|
+
].join("\n");
|
|
96833
|
+
var AGENT_GUARD_GUIDANCE = [
|
|
96834
|
+
"Runtime is not_observable_from_server \u2014 wire @coderifts/agent-guard in the agent host:",
|
|
96835
|
+
" - wrap mutating tools with guardToolCall / withCodeRifts",
|
|
96836
|
+
" - see: https://coderifts.com/docs (agent-guard) and `coderifts agent-setup`",
|
|
96837
|
+
" Local pre-push hook is complementary (git path), not a substitute for agent-guard."
|
|
96838
|
+
].join("\n");
|
|
96839
|
+
var DEPLOY_GUIDANCE = [
|
|
96840
|
+
"Deploy is declared-only on the server (never server-observed ENFORCING).",
|
|
96841
|
+
"Add a CD step that runs: coderifts deploy-gate --env <env> --artifact <id> --receipt <file> --enforce",
|
|
96842
|
+
" (phase-1 default is advisory; --enforce attests ENFORCING for the pipeline).",
|
|
96843
|
+
"Also set policy.require_source_binding: true in .coderifts.yml for the deploy declaration leg."
|
|
96844
|
+
].join("\n");
|
|
96845
|
+
var CONTENT_GUIDANCE = [
|
|
96846
|
+
"Content/freshness is not_observable_from_server (registry path at resolve time).",
|
|
96847
|
+
"No server-side enforce action is available \u2014 configure registry/freshness in the agent host."
|
|
96848
|
+
].join("\n");
|
|
96849
|
+
function isMergeEnforcing(leg) {
|
|
96850
|
+
return !!(leg && leg.enforcing === true && String(leg.status).toUpperCase() === "ENFORCING");
|
|
96851
|
+
}
|
|
96852
|
+
function runHookInstall(installFn) {
|
|
96853
|
+
const prev = process.exitCode;
|
|
96854
|
+
process.exitCode = 0;
|
|
96855
|
+
try {
|
|
96856
|
+
installFn();
|
|
96857
|
+
const code = typeof process.exitCode === "number" ? process.exitCode : 0;
|
|
96858
|
+
return {
|
|
96859
|
+
ok: code === 0,
|
|
96860
|
+
detail: code === 0 ? "hook.install completed" : `hook.install set exitCode=${code}`
|
|
96861
|
+
};
|
|
96862
|
+
} catch (e) {
|
|
96863
|
+
return { ok: false, detail: e && e.message || String(e) };
|
|
96864
|
+
} finally {
|
|
96865
|
+
process.exitCode = prev;
|
|
96866
|
+
}
|
|
96867
|
+
}
|
|
96868
|
+
async function runEnforce(options = {}, deps = {}) {
|
|
96869
|
+
const apply = options.apply === true;
|
|
96870
|
+
const getKey = deps.getApiKey || getApiKey;
|
|
96871
|
+
const fetchStatus = deps.cloudGetEnforcementStatus || cloudGetEnforcementStatus;
|
|
96872
|
+
const setupCheck = deps.runSetupRequiredCheck || runSetupRequiredCheck;
|
|
96873
|
+
const installHook = deps.hookInstall || hookInstall;
|
|
96874
|
+
const isHookInstalled = deps.isCodeRiftsHook || isCodeRiftsHook;
|
|
96875
|
+
const findGit = deps.findGitDir || findGitDir;
|
|
96876
|
+
const hookPathOf = deps.getHookPath || getHookPath;
|
|
96877
|
+
const log = deps.log || console.log.bind(console);
|
|
96878
|
+
const errLog = deps.errLog || console.error.bind(console);
|
|
96879
|
+
const repo = options.repo != null && String(options.repo).trim() ? String(options.repo).trim() : null;
|
|
96880
|
+
if (!repo) {
|
|
96881
|
+
errLog(chalk.red("Error: missing repo"));
|
|
96882
|
+
errLog(USAGE);
|
|
96883
|
+
return { exitCode: 1, error: "missing_repo", outcomes: [] };
|
|
96884
|
+
}
|
|
96885
|
+
if (!isValidRepo(repo)) {
|
|
96886
|
+
errLog(chalk.red("Error: repo must be in owner/repo form (e.g. coderifts/app)"));
|
|
96887
|
+
return { exitCode: 1, error: "invalid_repo", outcomes: [] };
|
|
96888
|
+
}
|
|
96889
|
+
const apiKey = getKey();
|
|
96890
|
+
if (!apiKey) {
|
|
96891
|
+
errLog(chalk.red("Error: no API key. Run `coderifts login` or set CODERIFTS_API_KEY."));
|
|
96892
|
+
return { exitCode: 1, error: "missing_api_key", outcomes: [] };
|
|
96893
|
+
}
|
|
96894
|
+
log(chalk.bold(`CodeRifts enforce \u2014 ${repo}`));
|
|
96895
|
+
log(chalk.dim(apply ? " Mode: --apply (will mutate via underlying commands where applicable)" : " Mode: dry-run (default) \u2014 no mutations; showing what WOULD run"));
|
|
96896
|
+
log("");
|
|
96897
|
+
let report;
|
|
96898
|
+
try {
|
|
96899
|
+
report = await fetchStatus(repo, apiKey);
|
|
96900
|
+
} catch (e) {
|
|
96901
|
+
const msg = e && e.message ? String(e.message) : "status request failed";
|
|
96902
|
+
errLog(chalk.red(`Error: ${msg}`));
|
|
96903
|
+
return { exitCode: 1, error: msg, outcomes: [] };
|
|
96904
|
+
}
|
|
96905
|
+
const legs = report && report.legs || {};
|
|
96906
|
+
const outcomes = [];
|
|
96907
|
+
if (isMergeEnforcing(legs.merge)) {
|
|
96908
|
+
outcomes.push({
|
|
96909
|
+
layer: "merge",
|
|
96910
|
+
action: "skip",
|
|
96911
|
+
apply,
|
|
96912
|
+
ok: true,
|
|
96913
|
+
detail: `already ENFORCING (status=${legs.merge.status})`
|
|
96914
|
+
});
|
|
96915
|
+
log(chalk.green(" Merge skip \u2014 already ENFORCING"));
|
|
96916
|
+
} else {
|
|
96917
|
+
const mergeStatus = legs.merge && legs.merge.status || "UNKNOWN";
|
|
96918
|
+
log(chalk.yellow(` Merge ${apply ? "apply" : "dry-run"} \u2014 setup-required-check (current: ${mergeStatus})`));
|
|
96919
|
+
try {
|
|
96920
|
+
const r = await setupCheck(
|
|
96921
|
+
{ repo, apply: apply === true },
|
|
96922
|
+
{
|
|
96923
|
+
exit: false,
|
|
96924
|
+
log: deps.setupLog || log,
|
|
96925
|
+
logErr: deps.setupLogErr || errLog,
|
|
96926
|
+
...deps.setupDeps || {}
|
|
96927
|
+
}
|
|
96928
|
+
);
|
|
96929
|
+
const code = r && typeof r.exitCode === "number" ? r.exitCode : 1;
|
|
96930
|
+
outcomes.push({
|
|
96931
|
+
layer: "merge",
|
|
96932
|
+
action: "setup-required-check",
|
|
96933
|
+
apply,
|
|
96934
|
+
ok: code === 0,
|
|
96935
|
+
exitCode: code,
|
|
96936
|
+
detail: r && r.code ? String(r.code) : `exit ${code}`
|
|
96937
|
+
});
|
|
96938
|
+
if (code !== 0) {
|
|
96939
|
+
errLog(chalk.red(` Merge failed (exit ${code}${r && r.code ? `, ${r.code}` : ""})`));
|
|
96940
|
+
}
|
|
96941
|
+
} catch (e) {
|
|
96942
|
+
outcomes.push({
|
|
96943
|
+
layer: "merge",
|
|
96944
|
+
action: "setup-required-check",
|
|
96945
|
+
apply,
|
|
96946
|
+
ok: false,
|
|
96947
|
+
detail: e && e.message || String(e)
|
|
96948
|
+
});
|
|
96949
|
+
errLog(chalk.red(` Merge error: ${e && e.message || e}`));
|
|
96950
|
+
}
|
|
96951
|
+
}
|
|
96952
|
+
{
|
|
96953
|
+
const gitDir = findGit && findGit();
|
|
96954
|
+
let alreadyHook = false;
|
|
96955
|
+
try {
|
|
96956
|
+
if (gitDir) {
|
|
96957
|
+
const hp = hookPathOf(gitDir);
|
|
96958
|
+
alreadyHook = !!(isHookInstalled && isHookInstalled(hp));
|
|
96959
|
+
}
|
|
96960
|
+
} catch {
|
|
96961
|
+
}
|
|
96962
|
+
if (alreadyHook && apply) {
|
|
96963
|
+
outcomes.push({
|
|
96964
|
+
layer: "runtime",
|
|
96965
|
+
action: "hook.install",
|
|
96966
|
+
apply: true,
|
|
96967
|
+
ok: true,
|
|
96968
|
+
detail: "hook already installed (idempotent skip); not server-observable ENFORCING",
|
|
96969
|
+
server_enforcing: false
|
|
96970
|
+
});
|
|
96971
|
+
log(chalk.green(" Runtime skip \u2014 CodeRifts hook already installed (local)"));
|
|
96972
|
+
} else if (apply) {
|
|
96973
|
+
log(chalk.yellow(" Runtime apply \u2014 hook.install (local; not server ENFORCING)"));
|
|
96974
|
+
const r = runHookInstall(installHook);
|
|
96975
|
+
outcomes.push({
|
|
96976
|
+
layer: "runtime",
|
|
96977
|
+
action: "hook.install",
|
|
96978
|
+
apply: true,
|
|
96979
|
+
ok: r.ok,
|
|
96980
|
+
detail: `${r.detail}; not server-observable ENFORCING`,
|
|
96981
|
+
server_enforcing: false
|
|
96982
|
+
});
|
|
96983
|
+
if (!r.ok) errLog(chalk.red(` Runtime hook install failed: ${r.detail}`));
|
|
96984
|
+
} else {
|
|
96985
|
+
outcomes.push({
|
|
96986
|
+
layer: "runtime",
|
|
96987
|
+
action: "hook.install",
|
|
96988
|
+
apply: false,
|
|
96989
|
+
ok: true,
|
|
96990
|
+
detail: alreadyHook ? "dry-run: hook already present; would re-run install (idempotent)" : "dry-run: would run coderifts hook install",
|
|
96991
|
+
server_enforcing: false
|
|
96992
|
+
});
|
|
96993
|
+
log(chalk.dim(` Runtime dry-run \u2014 would ${alreadyHook ? "re-run" : "run"} hook install (local)`));
|
|
96994
|
+
}
|
|
96995
|
+
log(chalk.dim(AGENT_GUARD_GUIDANCE.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
96996
|
+
}
|
|
96997
|
+
{
|
|
96998
|
+
const st = legs.deploy && legs.deploy.status || "unknown";
|
|
96999
|
+
outcomes.push({
|
|
97000
|
+
layer: "deploy",
|
|
97001
|
+
action: "guidance",
|
|
97002
|
+
apply: false,
|
|
97003
|
+
// never mutates GitHub here
|
|
97004
|
+
ok: true,
|
|
97005
|
+
detail: `server status=${st} (declared-only); print CD step instruction`,
|
|
97006
|
+
server_enforcing: false
|
|
97007
|
+
});
|
|
97008
|
+
log(chalk.dim(` Deploy guidance \u2014 current: ${st} (not server-enforcing)`));
|
|
97009
|
+
log(chalk.dim(DEPLOY_GUIDANCE.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
97010
|
+
}
|
|
97011
|
+
{
|
|
97012
|
+
outcomes.push({
|
|
97013
|
+
layer: "content",
|
|
97014
|
+
action: "guidance",
|
|
97015
|
+
apply: false,
|
|
97016
|
+
ok: true,
|
|
97017
|
+
detail: "not_observable_from_server; guidance only",
|
|
97018
|
+
server_enforcing: false
|
|
97019
|
+
});
|
|
97020
|
+
log(chalk.dim(" Content guidance \u2014 not_observable_from_server"));
|
|
97021
|
+
log(chalk.dim(CONTENT_GUIDANCE.split("\n").map((l) => ` ${l}`).join("\n")));
|
|
97022
|
+
}
|
|
97023
|
+
log("");
|
|
97024
|
+
log(chalk.bold(" Per-layer outcomes"));
|
|
97025
|
+
for (const o of outcomes) {
|
|
97026
|
+
const mark = o.ok ? chalk.green("ok") : chalk.red("FAIL");
|
|
97027
|
+
const mode = o.apply === true ? "apply" : o.action === "skip" ? "skip" : "dry-run";
|
|
97028
|
+
log(` ${String(o.layer).padEnd(8)} ${mark} ${mode.padEnd(7)} ${o.action} ${chalk.dim(o.detail || "")}`);
|
|
97029
|
+
}
|
|
97030
|
+
log("");
|
|
97031
|
+
log(chalk.bold(" Cross-layer report" + (apply ? " (after actions)" : " (current / dry-run)")));
|
|
97032
|
+
let finalReport = report;
|
|
97033
|
+
if (apply) {
|
|
97034
|
+
try {
|
|
97035
|
+
finalReport = await fetchStatus(repo, apiKey);
|
|
97036
|
+
} catch (e) {
|
|
97037
|
+
errLog(chalk.yellow(` Warning: re-fetch status failed: ${e && e.message || e}`));
|
|
97038
|
+
}
|
|
97039
|
+
}
|
|
97040
|
+
if (options.json) {
|
|
97041
|
+
log(JSON.stringify({ command: "enforce", apply, repo, outcomes, report: finalReport }, null, 2));
|
|
97042
|
+
} else {
|
|
97043
|
+
log(renderStatusReport(finalReport));
|
|
97044
|
+
}
|
|
97045
|
+
const anyFail = outcomes.some((o) => o.ok === false);
|
|
97046
|
+
return {
|
|
97047
|
+
exitCode: anyFail ? 1 : 0,
|
|
97048
|
+
apply,
|
|
97049
|
+
outcomes,
|
|
97050
|
+
report: finalReport
|
|
97051
|
+
};
|
|
97052
|
+
}
|
|
97053
|
+
module2.exports = {
|
|
97054
|
+
runEnforce,
|
|
97055
|
+
isMergeEnforcing,
|
|
97056
|
+
USAGE,
|
|
97057
|
+
AGENT_GUARD_GUIDANCE,
|
|
97058
|
+
DEPLOY_GUIDANCE,
|
|
97059
|
+
CONTENT_GUIDANCE
|
|
97060
|
+
};
|
|
97061
|
+
}
|
|
97062
|
+
});
|
|
97063
|
+
|
|
97064
|
+
// src/agent-host-files.embedded.js
|
|
97065
|
+
var require_agent_host_files_embedded = __commonJS({
|
|
97066
|
+
"src/agent-host-files.embedded.js"(exports2, module2) {
|
|
97067
|
+
"use strict";
|
|
97068
|
+
var AGENT_HOST_PATHS = Object.freeze([
|
|
97069
|
+
"AGENTS.md",
|
|
97070
|
+
"CLAUDE.md",
|
|
97071
|
+
".cursor/rules/coderifts.mdc",
|
|
97072
|
+
".github/copilot-instructions.md",
|
|
97073
|
+
"coderifts-langgraph-policy.js",
|
|
97074
|
+
"openai-agent-instructions.md"
|
|
97075
|
+
]);
|
|
97076
|
+
var AGENT_HOST_FILES = Object.freeze({
|
|
97077
|
+
"AGENTS.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts agent rules\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
97078
|
+
"CLAUDE.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
97079
|
+
".cursor/rules/coderifts.mdc": '---\ndescription: CodeRifts API governance \u2014 when to preflight and how to branch\nglobs:\nalwaysApply: true\n---\n\n<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
97080
|
+
".github/copilot-instructions.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts instructions for GitHub Copilot\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n',
|
|
97081
|
+
"coderifts-langgraph-policy.js": '// GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js\n// System policy string for a LangGraph (or similar) agent. Content is generated;\n// identical rule sentences to AGENTS.md / other formats.\n\'use strict\';\n\nmodule.exports = "Call `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\\n\\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\\n\\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\\n\\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\\n\\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not \\"proceed with caution\\" without monitoring.\\n\\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\\n\\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\\n\\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\\n\\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.";\n',
|
|
97082
|
+
"openai-agent-instructions.md": '<!-- GENERATED from CodeRifts agent-host-rule (single source). Do not edit rule sentences by hand; run: node scripts/generate-agent-host-files.js -->\n\n# CodeRifts agent instructions\n\nCall `preflight_change_set` before merge, deploy, publish, or tool registration when a contract artifact changed (OpenAPI/Swagger, GraphQL, gRPC/protobuf, AsyncAPI, MCP manifest, or agent tool schemas).\n\nSend the complete base-to-head change set: every changed contract artifact with full before and after content. Do not send a single-file subset when other contract files also change.\n\nBranch on `execution_action` only. Do not branch on `decision` and do not branch on `safe_for_agent` (not_for_control_flow_use_execution_action). Canonical `execution_action` values: CONTINUE, CONTINUE_WITH_MONITORING, REQUEST_APPROVAL, STOP.\n\nAn unrecognised `execution_action` is not permission: fail closed (halt or re-preflight). Well-known code: `not_permission_fail_closed`.\n\n`CONTINUE_WITH_MONITORING` requires a wired monitoring sink (`monitoringSinkWired`). It is not "proceed with caution" without monitoring.\n\nDo not call CodeRifts tools for a documentation-only change (README, guides, comments) with no contract artifact content change.\n\nIf you already hold a chain receipt and only need authenticity/lifecycle: `verify_receipt`. If you need a past decision by id: `get_decision_details`. Neither replaces preflight for a new change set.\n\nFor mutating tools, put only the guarded version in the agent\'s tool table; keep the raw handler host-only and unreachable from that table. How you name tools is yours \u2014 this is a reachability property, not a product rename of host tools. CodeRifts cannot see or stop a raw call the host makes outside the table it returns; adopt this as a host convention, not as a guarantee from the package.\n\nCodeRifts reports a governance decision and `execution_action`; it does not by itself block merges. Blocking requires separate repository configuration (required status checks, enforcement) that this rule file does not set.\n'
|
|
97083
|
+
});
|
|
97084
|
+
module2.exports = { AGENT_HOST_FILES, AGENT_HOST_PATHS };
|
|
97085
|
+
}
|
|
97086
|
+
});
|
|
97087
|
+
|
|
97088
|
+
// src/commands/agent-setup.js
|
|
97089
|
+
var require_agent_setup = __commonJS({
|
|
97090
|
+
"src/commands/agent-setup.js"(exports2, module2) {
|
|
97091
|
+
"use strict";
|
|
97092
|
+
var fs = require("fs");
|
|
97093
|
+
var path = require("path");
|
|
97094
|
+
var chalk = require_source();
|
|
97095
|
+
var { AGENT_HOST_FILES, AGENT_HOST_PATHS } = require_agent_host_files_embedded();
|
|
97096
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
97097
|
+
var USAGE = `Usage: coderifts agent-setup [--out <dir>] [--check] [--force]
|
|
97098
|
+
|
|
97099
|
+
--out <dir> Target directory (default: current working directory)
|
|
97100
|
+
--check Exit 0 if on-disk files match embedded content; exit 1 on drift
|
|
97101
|
+
--force Overwrite existing files (default: skip collisions)
|
|
97102
|
+
Unknown flags exit 1 (never silently ignored).
|
|
97103
|
+
`;
|
|
97104
|
+
function parseAgentSetupArgs(argv) {
|
|
97105
|
+
const args = argv.slice(2);
|
|
97106
|
+
let out = null;
|
|
97107
|
+
let check = false;
|
|
97108
|
+
let force = false;
|
|
97109
|
+
let i = 0;
|
|
97110
|
+
while (i < args.length && !String(args[i]).startsWith("-")) i += 1;
|
|
97111
|
+
while (i < args.length) {
|
|
97112
|
+
const a = args[i];
|
|
97113
|
+
if (a === "--out") {
|
|
97114
|
+
const v = args[i + 1];
|
|
97115
|
+
if (v == null || v.startsWith("-")) {
|
|
97116
|
+
return { out, check, force, error: `agent-setup: --out requires a path
|
|
97117
|
+
${USAGE}` };
|
|
97118
|
+
}
|
|
97119
|
+
out = path.resolve(v);
|
|
97120
|
+
i += 2;
|
|
97121
|
+
continue;
|
|
97122
|
+
}
|
|
97123
|
+
if (a === "--check") {
|
|
97124
|
+
check = true;
|
|
97125
|
+
i += 1;
|
|
97126
|
+
continue;
|
|
97127
|
+
}
|
|
97128
|
+
if (a === "--force") {
|
|
97129
|
+
force = true;
|
|
97130
|
+
i += 1;
|
|
97131
|
+
continue;
|
|
97132
|
+
}
|
|
97133
|
+
if (a.startsWith("-")) {
|
|
97134
|
+
return { out, check, force, error: `agent-setup: unrecognized argument: ${a}
|
|
97135
|
+
${USAGE}` };
|
|
97136
|
+
}
|
|
97137
|
+
return { out, check, force, error: `agent-setup: unrecognized argument: ${a}
|
|
97138
|
+
${USAGE}` };
|
|
97139
|
+
}
|
|
97140
|
+
return { out, check, force };
|
|
97141
|
+
}
|
|
97142
|
+
function runAgentSetup(options = {}, deps = {}) {
|
|
97143
|
+
const log = deps.log || console.log.bind(console);
|
|
97144
|
+
const logErr = deps.logErr || console.error.bind(console);
|
|
97145
|
+
const doExit = deps.exit !== false;
|
|
97146
|
+
const cwd = deps.cwd || process.cwd();
|
|
97147
|
+
const exists = deps.exists || fs.existsSync.bind(fs);
|
|
97148
|
+
const readFile = deps.readFile || ((p) => fs.readFileSync(p, "utf8"));
|
|
97149
|
+
const writeFile = deps.writeFile || ((p, c) => {
|
|
97150
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
97151
|
+
fs.writeFileSync(p, c, "utf8");
|
|
97152
|
+
});
|
|
97153
|
+
let outDir = options.out ? path.resolve(String(options.out)) : cwd;
|
|
97154
|
+
let check = !!options.check;
|
|
97155
|
+
let force = !!options.force;
|
|
97156
|
+
if (deps.argv) {
|
|
97157
|
+
const parsed = parseAgentSetupArgs(deps.argv);
|
|
97158
|
+
if (parsed.error) {
|
|
97159
|
+
logErr(parsed.error.trimEnd());
|
|
97160
|
+
if (doExit) process.exit(1);
|
|
97161
|
+
return { exitCode: 1, code: "USAGE", message: parsed.error };
|
|
97162
|
+
}
|
|
97163
|
+
if (parsed.out) outDir = parsed.out;
|
|
97164
|
+
check = parsed.check;
|
|
97165
|
+
force = parsed.force;
|
|
97166
|
+
}
|
|
97167
|
+
const files = deps.files || AGENT_HOST_FILES;
|
|
97168
|
+
const relPaths = deps.paths || AGENT_HOST_PATHS;
|
|
97169
|
+
if (check) {
|
|
97170
|
+
let stale = false;
|
|
97171
|
+
for (const rel of relPaths) {
|
|
97172
|
+
const fp = path.join(outDir, rel);
|
|
97173
|
+
const expected = files[rel];
|
|
97174
|
+
if (!exists(fp)) {
|
|
97175
|
+
logErr(chalk.red(`agent-setup --check: missing ${rel}`));
|
|
97176
|
+
stale = true;
|
|
97177
|
+
continue;
|
|
97178
|
+
}
|
|
97179
|
+
const onDisk = readFile(fp);
|
|
97180
|
+
if (onDisk !== expected) {
|
|
97181
|
+
logErr(chalk.red(`agent-setup --check: drift ${rel}`));
|
|
97182
|
+
stale = true;
|
|
97183
|
+
}
|
|
97184
|
+
}
|
|
97185
|
+
if (stale) {
|
|
97186
|
+
logErr("Run: coderifts agent-setup --out " + outDir + " --force");
|
|
97187
|
+
if (doExit) process.exit(1);
|
|
97188
|
+
return { exitCode: 1, code: "DRIFT", outDir };
|
|
97189
|
+
}
|
|
97190
|
+
log(chalk.green(`agent-setup: up to date (${outDir}, ${relPaths.length} files)`));
|
|
97191
|
+
if (doExit) process.exit(0);
|
|
97192
|
+
return { exitCode: 0, code: "UP_TO_DATE", outDir };
|
|
97193
|
+
}
|
|
97194
|
+
const summary = { written: [], skipped: [], forced: [] };
|
|
97195
|
+
for (const rel of relPaths) {
|
|
97196
|
+
const fp = path.join(outDir, rel);
|
|
97197
|
+
const content = files[rel];
|
|
97198
|
+
if (exists(fp) && !force) {
|
|
97199
|
+
summary.skipped.push(rel);
|
|
97200
|
+
continue;
|
|
97201
|
+
}
|
|
97202
|
+
if (exists(fp) && force) summary.forced.push(rel);
|
|
97203
|
+
writeFile(fp, content);
|
|
97204
|
+
summary.written.push(rel);
|
|
97205
|
+
}
|
|
97206
|
+
if (!options.json) {
|
|
97207
|
+
log(chalk.bold("CodeRifts agent-setup"));
|
|
97208
|
+
log(` target: ${outDir}`);
|
|
97209
|
+
for (const rel of summary.written) {
|
|
97210
|
+
const tag = summary.forced.includes(rel) ? "overwrote" : "wrote";
|
|
97211
|
+
log(chalk.green(` ${tag}: ${rel}`));
|
|
97212
|
+
}
|
|
97213
|
+
for (const rel of summary.skipped) {
|
|
97214
|
+
log(chalk.yellow(` skipped: ${rel} (exists; use --force to overwrite)`));
|
|
97215
|
+
}
|
|
97216
|
+
log("");
|
|
97217
|
+
log(chalk.dim(` ${summary.written.length} written, ${summary.skipped.length} skipped`));
|
|
97218
|
+
}
|
|
97219
|
+
if (doExit) process.exit(0);
|
|
97220
|
+
return { exitCode: 0, code: "OK", outDir, ...summary };
|
|
97221
|
+
}
|
|
97222
|
+
module2.exports = {
|
|
97223
|
+
runAgentSetup,
|
|
97224
|
+
parseAgentSetupArgs,
|
|
97225
|
+
AGENT_HOST_FILES,
|
|
97226
|
+
AGENT_HOST_PATHS,
|
|
97227
|
+
USAGE
|
|
97228
|
+
};
|
|
97229
|
+
}
|
|
97230
|
+
});
|
|
97231
|
+
|
|
94533
97232
|
// corpus/vectors-mcp-fpfn.json
|
|
94534
97233
|
var require_vectors_mcp_fpfn = __commonJS({
|
|
94535
97234
|
"corpus/vectors-mcp-fpfn.json"(exports2, module2) {
|
|
@@ -95843,6 +98542,7 @@ var require_mcp_poisoning_detector = __commonJS({
|
|
|
95843
98542
|
"../../src/mcp-poisoning-detector.js"(exports2, module2) {
|
|
95844
98543
|
"use strict";
|
|
95845
98544
|
var SENSITIVE_OBJECT = "(secrets?|credentials?|tokens?|passwords?|api[\\s_-]?keys?|private[\\s_-]?keys?|env|environment|conversation|chat\\s*history|context|user\\s*data|session)";
|
|
98545
|
+
var SENSITIVE_SCOPE = "(any|all|every|stored|user\\'?s?|the\\s+user\\'?s?|all\\s+environment)";
|
|
95846
98546
|
var SUSPICIOUS_PATTERNS = [
|
|
95847
98547
|
// Injection / "ignore your instructions" family. The trailing object
|
|
95848
98548
|
// (instructions|rules|prompts|context) is OPTIONAL so "ignore previous and ..."
|
|
@@ -95855,6 +98555,21 @@ var require_mcp_poisoning_detector = __commonJS({
|
|
|
95855
98555
|
/exfiltrate|steal|leak|send\s+to\s+external/i,
|
|
95856
98556
|
// Exfiltration-intent family: an action verb targeting a sensitive payload.
|
|
95857
98557
|
/\b(send|forward|copy|upload|post|transmit|exfiltrate|leak)\b[\s\S]{0,40}?\b(conversation|chat|history|context|messages?|credentials?|secrets?|tokens?|api\s*keys?|keys?|passwords?|data)\b/i,
|
|
98558
|
+
// Return/echo scoped sensitive material without a path ("return any stored credentials").
|
|
98559
|
+
// Scope word required — bare "return the new API token" must stay quiet.
|
|
98560
|
+
new RegExp(
|
|
98561
|
+
"\\b(return|echo|reveal|expose|send)\\b[\\s\\S]{0,30}?\\b" + SENSITIVE_SCOPE + "\\s+(secrets?|credentials?|tokens?|passwords?|private[\\s_-]?keys?|environment\\s+variables?|env)\\b",
|
|
98562
|
+
"i"
|
|
98563
|
+
),
|
|
98564
|
+
// Retrieve private-key material + echo/return (intent without a filesystem path).
|
|
98565
|
+
// Narrow: requires both a retrieve-class verb and an echo/return/reveal verb around key material.
|
|
98566
|
+
/\b(retrieve|fetch|extract|obtain|collect)\b[\s\S]{0,60}?\b(private[\s_-]?keys?(?:\s+material)?|secret[\s_-]?keys?|key\s+material)\b[\s\S]{0,50}?\b(echo|return|send|reveal|expose)\b/i,
|
|
98567
|
+
// Outbound destination without a named secret: exfil-shaped phrasing + http(s) URL.
|
|
98568
|
+
// Bare URL alone stays quiet (legit tools reference docs / configured webhook URLs).
|
|
98569
|
+
// "Sends the result to https://hooks…" (tool purpose) must NOT fire — requires "a copy"
|
|
98570
|
+
// or "also send" as the exfil marker, not mere "send … to <url>".
|
|
98571
|
+
/\b(send|forward|post|upload|transmit)\s+a\s+copy\b[\s\S]{0,50}?https?:\/\//i,
|
|
98572
|
+
/\balso\s+send\b[\s\S]{0,60}?https?:\/\//i,
|
|
95858
98573
|
/execute\s+(shell|command|code|system)/i,
|
|
95859
98574
|
/bypass\s+(auth|security|validation|filter)/i,
|
|
95860
98575
|
/override\s+(safety|policy|rules?)/i,
|
|
@@ -95866,8 +98581,8 @@ var require_mcp_poisoning_detector = __commonJS({
|
|
|
95866
98581
|
/return\s+secret|exfiltrat|leak\s+data/i
|
|
95867
98582
|
];
|
|
95868
98583
|
var URL_PATTERN = /https?:\/\/[^\s)'"]+/i;
|
|
95869
|
-
var SENSITIVE_PATH_PATTERN = /(~\/\.ssh|~\/\.cursor|~\/\.aws|\.aws\/|\.env\b|\/etc\/|mcp\.json|credentials|
|
|
95870
|
-
var CONTENT_MOVE_VERB_PATTERN = /\b(pass|send|return|forward|include|read|upload|post|transmit|copy|exfiltrate|leak)\b/i;
|
|
98584
|
+
var SENSITIVE_PATH_PATTERN = /(~\/\.ssh|~\/\.cursor|~\/\.aws|\.aws\/|\.env\b|\/etc\/|mcp\.json|id_rsa|\.ssh\/|\/credentials(?:\.|\/)|credentials\.(?:json|txt|pem|env)|\.credentials\b)/i;
|
|
98585
|
+
var CONTENT_MOVE_VERB_PATTERN = /\b(pass|send|return|forward|include|read|upload|post|transmit|copy|exfiltrate|leak|echo|reveal|expose)\b/i;
|
|
95871
98586
|
var BASE64_BLOB_PATTERN = /[A-Za-z0-9+/]{40,}={0,2}/;
|
|
95872
98587
|
function isLikelyBase64(s) {
|
|
95873
98588
|
if (/=$/.test(s)) return true;
|
|
@@ -96406,6 +99121,25 @@ program.command("deploy-gate").description("Gate a deploy on the current { envir
|
|
|
96406
99121
|
const { runDeployGate } = require_deploy_gate2();
|
|
96407
99122
|
await runDeployGate(options);
|
|
96408
99123
|
});
|
|
99124
|
+
program.command("publish-gate").description("Gate npm publish on contract-artifact preflight (before=git baseline, after=working tree)").option("--spec <path>", "Contract artifact path (default: git config coderifts.specPath or api/openapi.yaml)").option("--json", "Machine-readable JSON result").action(async (options) => {
|
|
99125
|
+
const { runPublishGate } = require_publish_gate();
|
|
99126
|
+
const result = await runPublishGate(options);
|
|
99127
|
+
const code = result && typeof result.exitCode === "number" ? result.exitCode : 1;
|
|
99128
|
+
process.exitCode = code;
|
|
99129
|
+
process.exit(code);
|
|
99130
|
+
});
|
|
99131
|
+
program.command("registry-gate [dir]").description("Admit a directory of OpenAPI specs via local registry validation (CI gate, no cloud)").option("--glob <pattern>", "Filter discovered paths with agent-guard matchGlob (relative to dir)").option("--errors-only", "Fail only on ERROR severity (warnings are printed, exit 0)").option("--warn-only", "Advisory: print all findings, always exit 0").action((dir, options) => {
|
|
99132
|
+
const { runRegistryGate } = require_registry_gate();
|
|
99133
|
+
const result = runRegistryGate({
|
|
99134
|
+
dir: dir || ".",
|
|
99135
|
+
glob: options.glob,
|
|
99136
|
+
errorsOnly: !!options.errorsOnly,
|
|
99137
|
+
warnOnly: !!options.warnOnly
|
|
99138
|
+
});
|
|
99139
|
+
const code = result && typeof result.exitCode === "number" ? result.exitCode : 1;
|
|
99140
|
+
process.exitCode = code;
|
|
99141
|
+
process.exit(code);
|
|
99142
|
+
});
|
|
96409
99143
|
program.command("init [template]").description("Generate a .coderifts.yml from a policy template (startup, growth, fintech, public-api, microservices)").action(async (template) => {
|
|
96410
99144
|
const { init } = require_init();
|
|
96411
99145
|
await init(template);
|
|
@@ -96414,6 +99148,38 @@ program.command("login").description("Save your API key for cloud features").act
|
|
|
96414
99148
|
const { login } = require_login();
|
|
96415
99149
|
await login();
|
|
96416
99150
|
});
|
|
99151
|
+
program.command("setup-required-check").description('Guide setup of required status check "CodeRifts / contract-gate" (uses gh, your credentials)').option("--branch <name>", "Branch to protect (default: repo default branch)").option("--repo <owner/repo>", "Override owner/repo (default: git remote origin)").option("--apply", "Apply the protection change (default: print the exact gh command only)").option("--json", "Machine-readable JSON result").action(async (options) => {
|
|
99152
|
+
const { runSetupRequiredCheck } = require_setup_required_check();
|
|
99153
|
+
const result = await runSetupRequiredCheck(options);
|
|
99154
|
+
if (result && typeof result.exitCode === "number") {
|
|
99155
|
+
process.exitCode = result.exitCode;
|
|
99156
|
+
}
|
|
99157
|
+
});
|
|
99158
|
+
program.command("status [repo]").description("Show cross-layer enforcement status (Runtime / Merge / Deploy) for a repo \u2014 read-only").option("--repo <owner/repo>", "Repository (owner/repo); also accepted as a positional argument").option("--json", "Machine-readable JSON (raw API body)").action(async (repoPositional, options) => {
|
|
99159
|
+
const { runStatus } = require_status();
|
|
99160
|
+
const result = await runStatus({
|
|
99161
|
+
...options,
|
|
99162
|
+
repo: options.repo || repoPositional || null
|
|
99163
|
+
});
|
|
99164
|
+
if (result && typeof result.exitCode === "number") {
|
|
99165
|
+
process.exitCode = result.exitCode;
|
|
99166
|
+
}
|
|
99167
|
+
});
|
|
99168
|
+
program.command("enforce [repo]").description("Close enforcement gaps by chaining setup-required-check / hook / deploy guidance (dry-run default; use --apply to mutate)").option("--repo <owner/repo>", "Repository (owner/repo); also accepted as a positional argument").option("--apply", "Actually run underlying setup commands (default: dry-run only)").option("--json", "Machine-readable JSON result").action(async (repoPositional, options) => {
|
|
99169
|
+
const { runEnforce } = require_enforce();
|
|
99170
|
+
const result = await runEnforce({
|
|
99171
|
+
...options,
|
|
99172
|
+
repo: options.repo || repoPositional || null,
|
|
99173
|
+
apply: !!options.apply
|
|
99174
|
+
});
|
|
99175
|
+
if (result && typeof result.exitCode === "number") {
|
|
99176
|
+
process.exitCode = result.exitCode;
|
|
99177
|
+
}
|
|
99178
|
+
});
|
|
99179
|
+
program.command("agent-setup").description("Write AGENTS.md / CLAUDE.md / Cursor / Copilot / LangGraph / OpenAI agent rule files").option("--out <dir>", "Target directory (default: current working directory)").option("--check", "Exit 0 if on-disk files match embedded content; exit 1 on drift").option("--force", "Overwrite existing files (default: skip collisions)").action((options) => {
|
|
99180
|
+
const { runAgentSetup } = require_agent_setup();
|
|
99181
|
+
runAgentSetup(options, { exit: true });
|
|
99182
|
+
});
|
|
96417
99183
|
var hookCmd = program.command("hook").description("Manage the CodeRifts pre-push Git hook");
|
|
96418
99184
|
hookCmd.command("install").description("Install the CodeRifts pre-push hook in the current Git repo").action(() => {
|
|
96419
99185
|
const { install } = require_hook();
|
|
@@ -96432,7 +99198,11 @@ corpusCmd.command("verify", { isDefault: true }).description("Evaluate every tru
|
|
|
96432
99198
|
const { corpusVerify } = require_corpus();
|
|
96433
99199
|
corpusVerify(options);
|
|
96434
99200
|
});
|
|
96435
|
-
program.
|
|
99201
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
99202
|
+
console.error(err);
|
|
99203
|
+
process.exitCode = typeof process.exitCode === "number" && process.exitCode !== 0 ? process.exitCode : 1;
|
|
99204
|
+
process.exit(process.exitCode);
|
|
99205
|
+
});
|
|
96436
99206
|
/*! Bundled license information:
|
|
96437
99207
|
|
|
96438
99208
|
safe-buffer/index.js:
|