crosscheck-mcp 0.2.14 → 0.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-ext.cjs +154 -74
- package/dist/browser-ext.cjs.map +1 -1
- package/dist/browser-ext.js +154 -74
- package/dist/browser-ext.js.map +1 -1
- package/dist/node-stdio.cjs +573 -268
- package/dist/node-stdio.cjs.map +1 -1
- package/dist/node-stdio.d.cts +9 -0
- package/dist/node-stdio.d.ts +9 -0
- package/dist/node-stdio.js +569 -264
- package/dist/node-stdio.js.map +1 -1
- package/package.json +1 -1
package/dist/node-stdio.cjs
CHANGED
|
@@ -958,8 +958,8 @@ __export(node_stdio_exports, {
|
|
|
958
958
|
});
|
|
959
959
|
module.exports = __toCommonJS(node_stdio_exports);
|
|
960
960
|
init_cjs_shims();
|
|
961
|
-
var
|
|
962
|
-
var
|
|
961
|
+
var import_node_fs19 = require("fs");
|
|
962
|
+
var import_node_path18 = __toESM(require("path"), 1);
|
|
963
963
|
var import_node_url2 = require("url");
|
|
964
964
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
965
965
|
|
|
@@ -976,10 +976,10 @@ var StderrEmitter = class {
|
|
|
976
976
|
}
|
|
977
977
|
};
|
|
978
978
|
var FileEmitter = class {
|
|
979
|
-
constructor(
|
|
980
|
-
this.path =
|
|
979
|
+
constructor(path15) {
|
|
980
|
+
this.path = path15;
|
|
981
981
|
try {
|
|
982
|
-
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(
|
|
982
|
+
(0, import_node_fs.mkdirSync)((0, import_node_path.dirname)(path15), { recursive: true });
|
|
983
983
|
} catch {
|
|
984
984
|
}
|
|
985
985
|
}
|
|
@@ -1012,13 +1012,13 @@ var NullEmitter = class {
|
|
|
1012
1012
|
function buildDefaultEmitter(env) {
|
|
1013
1013
|
const mode = (env["CROSSCHECK_EVENTS"] ?? "stderr").toLowerCase();
|
|
1014
1014
|
if (mode === "off") return new NullEmitter();
|
|
1015
|
-
const
|
|
1015
|
+
const path15 = env["CROSSCHECK_EVENTS_PATH"];
|
|
1016
1016
|
if (mode === "file") {
|
|
1017
|
-
return
|
|
1017
|
+
return path15 ? new FileEmitter(path15) : new NullEmitter();
|
|
1018
1018
|
}
|
|
1019
1019
|
if (mode === "both") {
|
|
1020
1020
|
const ems = [new StderrEmitter()];
|
|
1021
|
-
if (
|
|
1021
|
+
if (path15) ems.push(new FileEmitter(path15));
|
|
1022
1022
|
return new TeeEmitter(ems);
|
|
1023
1023
|
}
|
|
1024
1024
|
return new StderrEmitter();
|
|
@@ -1067,10 +1067,10 @@ function envelopeBytes(v) {
|
|
|
1067
1067
|
// src/core/pricing.ts
|
|
1068
1068
|
init_cjs_shims();
|
|
1069
1069
|
var import_node_fs2 = require("fs");
|
|
1070
|
-
function loadPricing(
|
|
1070
|
+
function loadPricing(path15) {
|
|
1071
1071
|
let raw;
|
|
1072
1072
|
try {
|
|
1073
|
-
raw = (0, import_node_fs2.readFileSync)(
|
|
1073
|
+
raw = (0, import_node_fs2.readFileSync)(path15, "utf8");
|
|
1074
1074
|
} catch {
|
|
1075
1075
|
return {};
|
|
1076
1076
|
}
|
|
@@ -1242,11 +1242,18 @@ var ProviderError = class extends Error {
|
|
|
1242
1242
|
status;
|
|
1243
1243
|
transient;
|
|
1244
1244
|
retryAfterS;
|
|
1245
|
+
/** True specifically for "the key is fine but this model isn't
|
|
1246
|
+
* available to this account/project" (see http-errors.ts's
|
|
1247
|
+
* isModelAccessFailure) — the one failure a model fallback chain can
|
|
1248
|
+
* actually fix. Distinct from `transient`: this is never worth
|
|
1249
|
+
* retrying the SAME model, but IS worth trying a different one. */
|
|
1250
|
+
modelAccessFailure;
|
|
1245
1251
|
constructor(kind, message, opts) {
|
|
1246
1252
|
super(message);
|
|
1247
1253
|
this.kind = kind;
|
|
1248
1254
|
this.status = opts?.status;
|
|
1249
1255
|
this.transient = opts?.transient ?? defaultTransient(kind);
|
|
1256
|
+
this.modelAccessFailure = opts?.modelAccessFailure ?? false;
|
|
1250
1257
|
if (opts?.retryAfterS !== void 0) {
|
|
1251
1258
|
this.retryAfterS = opts.retryAfterS;
|
|
1252
1259
|
}
|
|
@@ -1278,7 +1285,7 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
1278
1285
|
return new ProviderError(
|
|
1279
1286
|
"client",
|
|
1280
1287
|
`${provider}: your account/project doesn't have access to ${modelRef} (HTTP ${status}). This is a model-access problem, not a key problem \u2014 your ${provider} API key is fine, ${modelRef} just isn't enabled for this account/project. Fix: run \`crosscheck models set ${provider} <a-model-you-have-access-to>\` (or set ${provider.toUpperCase()}_MODEL in your .env until that's available). Detail: ${detail}`,
|
|
1281
|
-
{ status }
|
|
1288
|
+
{ status, modelAccessFailure: true }
|
|
1282
1289
|
);
|
|
1283
1290
|
}
|
|
1284
1291
|
if (isCreditsFailure(status, bodyText)) {
|
|
@@ -1319,8 +1326,8 @@ function httpFailureToProviderError(provider, status, bodyText, retryAfterS, mod
|
|
|
1319
1326
|
// src/providers/retry.ts
|
|
1320
1327
|
init_cjs_shims();
|
|
1321
1328
|
var override = null;
|
|
1322
|
-
function setRetryConfig(
|
|
1323
|
-
override =
|
|
1329
|
+
function setRetryConfig(cfg2) {
|
|
1330
|
+
override = cfg2;
|
|
1324
1331
|
}
|
|
1325
1332
|
function resolveRetryConfig() {
|
|
1326
1333
|
const envInt = (k, d) => {
|
|
@@ -1382,21 +1389,21 @@ function defaultSleep(ms, signal) {
|
|
|
1382
1389
|
signal?.addEventListener?.("abort", onAbort);
|
|
1383
1390
|
});
|
|
1384
1391
|
}
|
|
1385
|
-
async function sendWithRetry(attemptOnce,
|
|
1392
|
+
async function sendWithRetry(attemptOnce, cfg2, signal, deps) {
|
|
1386
1393
|
const sleep = deps?.sleepImpl ?? ((ms) => defaultSleep(ms, signal));
|
|
1387
1394
|
const rand = deps?.random ?? Math.random;
|
|
1388
1395
|
let lastErr;
|
|
1389
|
-
for (let attempt = 1; attempt <=
|
|
1396
|
+
for (let attempt = 1; attempt <= cfg2.maxAttempts; attempt += 1) {
|
|
1390
1397
|
try {
|
|
1391
1398
|
const result = await attemptOnce();
|
|
1392
1399
|
return { ...result, attempts: attempt };
|
|
1393
1400
|
} catch (e) {
|
|
1394
1401
|
lastErr = e;
|
|
1395
1402
|
const pe = e instanceof ProviderError ? e : null;
|
|
1396
|
-
if (pe?.transient !== true || attempt >=
|
|
1397
|
-
let backoffMs = pe.retryAfterS !== void 0 ? pe.retryAfterS * 1e3 :
|
|
1398
|
-
backoffMs += rand() *
|
|
1399
|
-
backoffMs = Math.min(backoffMs,
|
|
1403
|
+
if (pe?.transient !== true || attempt >= cfg2.maxAttempts) throw e;
|
|
1404
|
+
let backoffMs = pe.retryAfterS !== void 0 ? pe.retryAfterS * 1e3 : cfg2.backoffBaseMs * 2 ** (attempt - 1);
|
|
1405
|
+
backoffMs += rand() * cfg2.backoffBaseMs;
|
|
1406
|
+
backoffMs = Math.min(backoffMs, cfg2.maxBackoffMs);
|
|
1400
1407
|
await sleep(backoffMs);
|
|
1401
1408
|
}
|
|
1402
1409
|
}
|
|
@@ -1408,8 +1415,8 @@ init_cjs_shims();
|
|
|
1408
1415
|
var DEFAULT_SPEC = { capacity: 5, refillPerSec: 5 };
|
|
1409
1416
|
var MAX_WAIT_SLICE_MS = 250;
|
|
1410
1417
|
var override2 = null;
|
|
1411
|
-
function setRateLimitConfig(
|
|
1412
|
-
override2 =
|
|
1418
|
+
function setRateLimitConfig(cfg2) {
|
|
1419
|
+
override2 = cfg2;
|
|
1413
1420
|
buckets.clear();
|
|
1414
1421
|
}
|
|
1415
1422
|
function disabled() {
|
|
@@ -1494,6 +1501,14 @@ function buildAnthropicRequest(opts) {
|
|
|
1494
1501
|
if (system !== void 0) {
|
|
1495
1502
|
body.system = system;
|
|
1496
1503
|
}
|
|
1504
|
+
if (isReasoningModel("anthropic", opts.model)) {
|
|
1505
|
+
if (opts.model.toLowerCase().startsWith("claude-fable-5")) {
|
|
1506
|
+
body.thinking = { type: "adaptive" };
|
|
1507
|
+
body.output_config = { effort: "low" };
|
|
1508
|
+
} else {
|
|
1509
|
+
body.thinking = { type: "disabled" };
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1497
1512
|
if (opts.jsonSchema) {
|
|
1498
1513
|
body.tools = [{
|
|
1499
1514
|
name: ANTHROPIC_STRUCTURED_TOOL_NAME,
|
|
@@ -1561,7 +1576,12 @@ function parseAnthropicResponse(opts) {
|
|
|
1561
1576
|
purpose: opts.purpose
|
|
1562
1577
|
};
|
|
1563
1578
|
usage.total_tokens = usage.prompt_tokens + usage.completion_tokens;
|
|
1564
|
-
|
|
1579
|
+
const stopReasonRaw = r["stop_reason"];
|
|
1580
|
+
const stopReason = typeof stopReasonRaw === "string" ? stopReasonRaw : void 0;
|
|
1581
|
+
return { text, usage, ...stopReason !== void 0 ? { stopReason } : {} };
|
|
1582
|
+
}
|
|
1583
|
+
function isEmptyDueToMaxTokens(text, stopReason) {
|
|
1584
|
+
return text.trim() === "" && stopReason === "max_tokens";
|
|
1565
1585
|
}
|
|
1566
1586
|
function applyPricing(usage, pricing) {
|
|
1567
1587
|
const { cost_usd, estimated } = calculateCost(
|
|
@@ -1579,49 +1599,64 @@ function applyPricing(usage, pricing) {
|
|
|
1579
1599
|
estimated: usage.estimated || estimated
|
|
1580
1600
|
};
|
|
1581
1601
|
}
|
|
1602
|
+
var MAX_TOKENS_ESCALATIONS = 2;
|
|
1603
|
+
var ESCALATION_MULTIPLIER = 2;
|
|
1582
1604
|
async function sendAnthropic(args) {
|
|
1583
|
-
const { url, headers, body } = buildAnthropicRequest({
|
|
1584
|
-
model: args.model,
|
|
1585
|
-
apiKey: args.apiKey,
|
|
1586
|
-
messages: args.messages,
|
|
1587
|
-
maxTokens: args.maxTokens,
|
|
1588
|
-
temperature: args.temperature,
|
|
1589
|
-
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1590
|
-
});
|
|
1591
1605
|
const doFetch = args.fetchImpl ?? globalThis.fetch;
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1606
|
+
let maxTokens = args.maxTokens;
|
|
1607
|
+
let result = null;
|
|
1608
|
+
let totalAttempts = 0;
|
|
1609
|
+
for (let escalation = 0; escalation <= MAX_TOKENS_ESCALATIONS; escalation += 1) {
|
|
1610
|
+
const { url, headers, body } = buildAnthropicRequest({
|
|
1611
|
+
model: args.model,
|
|
1612
|
+
apiKey: args.apiKey,
|
|
1613
|
+
messages: args.messages,
|
|
1614
|
+
maxTokens,
|
|
1615
|
+
temperature: args.temperature,
|
|
1616
|
+
...args.jsonSchema ? { jsonSchema: args.jsonSchema } : {}
|
|
1617
|
+
});
|
|
1618
|
+
const init = {
|
|
1619
|
+
method: "POST",
|
|
1620
|
+
headers,
|
|
1621
|
+
body: JSON.stringify(body)
|
|
1622
|
+
};
|
|
1623
|
+
if (args.signal) init.signal = args.signal;
|
|
1624
|
+
let stopReason;
|
|
1625
|
+
const attemptOnce = async () => {
|
|
1626
|
+
let respLike;
|
|
1608
1627
|
try {
|
|
1609
|
-
|
|
1628
|
+
respLike = await doFetch(url, init);
|
|
1610
1629
|
} catch (e) {
|
|
1611
|
-
throw new ProviderError("
|
|
1630
|
+
throw new ProviderError("network", `anthropic: fetch failed: ${e.message}`);
|
|
1612
1631
|
}
|
|
1613
|
-
const
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1632
|
+
const status = respLike.status;
|
|
1633
|
+
if (status >= 200 && status < 300) {
|
|
1634
|
+
let parsed;
|
|
1635
|
+
try {
|
|
1636
|
+
parsed = await respLike.json();
|
|
1637
|
+
} catch (e) {
|
|
1638
|
+
throw new ProviderError("parse", `anthropic: response body not JSON: ${e.message}`);
|
|
1639
|
+
}
|
|
1640
|
+
const { text, usage, stopReason: sr } = parseAnthropicResponse({
|
|
1641
|
+
resp: parsed,
|
|
1642
|
+
model: args.model,
|
|
1643
|
+
purpose: args.purpose ?? "worker"
|
|
1644
|
+
});
|
|
1645
|
+
stopReason = sr;
|
|
1646
|
+
return { text, attempts: 1, usage: applyPricing(usage, args.pricing) };
|
|
1647
|
+
}
|
|
1648
|
+
const bodyText = await respLike.text().catch(() => "");
|
|
1649
|
+
throw httpFailureToProviderError("anthropic", status, bodyText, parseRetryAfter(respLike), args.model);
|
|
1650
|
+
};
|
|
1651
|
+
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1652
|
+
result = await sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1653
|
+
totalAttempts += result.attempts;
|
|
1654
|
+
if (escalation >= MAX_TOKENS_ESCALATIONS || !isEmptyDueToMaxTokens(result.text, stopReason)) {
|
|
1655
|
+
break;
|
|
1619
1656
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
};
|
|
1623
|
-
await acquireRateLimit("anthropic", args.signal ? { signal: args.signal } : void 0);
|
|
1624
|
-
return sendWithRetry(attemptOnce, resolveRetryConfig(), args.signal);
|
|
1657
|
+
maxTokens *= ESCALATION_MULTIPLIER;
|
|
1658
|
+
}
|
|
1659
|
+
return { ...result, attempts: totalAttempts };
|
|
1625
1660
|
}
|
|
1626
1661
|
|
|
1627
1662
|
// src/providers/gemini.ts
|
|
@@ -2007,19 +2042,27 @@ function detectStalePins(env) {
|
|
|
2007
2042
|
const out = [];
|
|
2008
2043
|
for (const [provider, envVar] of Object.entries(MODEL_ENV_VARS)) {
|
|
2009
2044
|
const pinned = env[envVar];
|
|
2010
|
-
const
|
|
2011
|
-
if (pinned &&
|
|
2012
|
-
out.push({ provider, envVar, pinned, current });
|
|
2045
|
+
const current2 = DEFAULT_MODELS[provider];
|
|
2046
|
+
if (pinned && current2 && pinned !== current2) {
|
|
2047
|
+
out.push({ provider, envVar, pinned, current: current2 });
|
|
2013
2048
|
}
|
|
2014
2049
|
}
|
|
2015
2050
|
return out;
|
|
2016
2051
|
}
|
|
2052
|
+
function parseFallbackModels(raw) {
|
|
2053
|
+
if (!raw) return [];
|
|
2054
|
+
return raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2055
|
+
}
|
|
2056
|
+
function fallbackModelsFor(env, provider) {
|
|
2057
|
+
const envVar = MODEL_ENV_VARS[provider];
|
|
2058
|
+
return envVar ? parseFallbackModels(env[`${envVar}_FALLBACKS`]) : [];
|
|
2059
|
+
}
|
|
2017
2060
|
function buildProviders(opts) {
|
|
2018
2061
|
const out = {};
|
|
2019
2062
|
const anthropicKey = opts.env["ANTHROPIC_API_KEY"];
|
|
2020
2063
|
if (anthropicKey) {
|
|
2021
2064
|
const model = opts.env["ANTHROPIC_MODEL"] ?? DEFAULT_MODELS.anthropic;
|
|
2022
|
-
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts);
|
|
2065
|
+
out["anthropic"] = makeAnthropicProvider(model, anthropicKey, opts, fallbackModelsFor(opts.env, "anthropic"));
|
|
2023
2066
|
}
|
|
2024
2067
|
const openAiCompatSpec = [
|
|
2025
2068
|
{ name: "openai", keyEnv: "OPENAI_API_KEY", modelEnv: "OPENAI_MODEL", defaultModel: DEFAULT_MODELS["openai"] },
|
|
@@ -2034,19 +2077,20 @@ function buildProviders(opts) {
|
|
|
2034
2077
|
const apiKey = opts.env[s.keyEnv];
|
|
2035
2078
|
if (!apiKey) continue;
|
|
2036
2079
|
const model = opts.env[s.modelEnv] ?? s.defaultModel;
|
|
2037
|
-
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts);
|
|
2080
|
+
out[s.name] = makeOpenAICompatibleProvider(s.name, model, apiKey, opts, fallbackModelsFor(opts.env, s.name));
|
|
2038
2081
|
}
|
|
2039
2082
|
const geminiKey = opts.env["GEMINI_API_KEY"];
|
|
2040
2083
|
if (geminiKey) {
|
|
2041
2084
|
const model = opts.env["GEMINI_MODEL"] ?? DEFAULT_MODELS.gemini;
|
|
2042
|
-
out["gemini"] = makeGeminiProvider(model, geminiKey, opts);
|
|
2085
|
+
out["gemini"] = makeGeminiProvider(model, geminiKey, opts, fallbackModelsFor(opts.env, "gemini"));
|
|
2043
2086
|
}
|
|
2044
2087
|
return out;
|
|
2045
2088
|
}
|
|
2046
|
-
function makeAnthropicProvider(model, apiKey, opts) {
|
|
2089
|
+
function makeAnthropicProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2047
2090
|
return {
|
|
2048
2091
|
name: "anthropic",
|
|
2049
2092
|
model,
|
|
2093
|
+
fallbackModels,
|
|
2050
2094
|
send: async (args) => {
|
|
2051
2095
|
const sendOpts = {
|
|
2052
2096
|
...args,
|
|
@@ -2059,11 +2103,12 @@ function makeAnthropicProvider(model, apiKey, opts) {
|
|
|
2059
2103
|
}
|
|
2060
2104
|
};
|
|
2061
2105
|
}
|
|
2062
|
-
function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
2106
|
+
function makeOpenAICompatibleProvider(name, model, apiKey, opts, fallbackModels = []) {
|
|
2063
2107
|
const url = OPENAI_COMPAT_DEFAULT_URLS[name];
|
|
2064
2108
|
return {
|
|
2065
2109
|
name,
|
|
2066
2110
|
model,
|
|
2111
|
+
fallbackModels,
|
|
2067
2112
|
send: async (args) => {
|
|
2068
2113
|
const sendOpts = {
|
|
2069
2114
|
...args,
|
|
@@ -2078,10 +2123,11 @@ function makeOpenAICompatibleProvider(name, model, apiKey, opts) {
|
|
|
2078
2123
|
}
|
|
2079
2124
|
};
|
|
2080
2125
|
}
|
|
2081
|
-
function makeGeminiProvider(model, apiKey, opts) {
|
|
2126
|
+
function makeGeminiProvider(model, apiKey, opts, fallbackModels = []) {
|
|
2082
2127
|
return {
|
|
2083
2128
|
name: "gemini",
|
|
2084
2129
|
model,
|
|
2130
|
+
fallbackModels,
|
|
2085
2131
|
send: async (args) => {
|
|
2086
2132
|
const sendOpts = {
|
|
2087
2133
|
...args,
|
|
@@ -2098,7 +2144,7 @@ function makeGeminiProvider(model, apiKey, opts) {
|
|
|
2098
2144
|
// src/server.ts
|
|
2099
2145
|
init_cjs_shims();
|
|
2100
2146
|
var import_server = require("@modelcontextprotocol/sdk/server/index.js");
|
|
2101
|
-
var
|
|
2147
|
+
var import_types9 = require("@modelcontextprotocol/sdk/types.js");
|
|
2102
2148
|
|
|
2103
2149
|
// src/bridge/index.ts
|
|
2104
2150
|
init_cjs_shims();
|
|
@@ -2229,7 +2275,7 @@ var import_zod = require("zod");
|
|
|
2229
2275
|
// src/server-meta.ts
|
|
2230
2276
|
init_cjs_shims();
|
|
2231
2277
|
var SERVER_NAME = "crosscheck-agent";
|
|
2232
|
-
var SERVER_VERSION = true ? "0.2.
|
|
2278
|
+
var SERVER_VERSION = true ? "0.2.16" : "0.0.0-dev";
|
|
2233
2279
|
|
|
2234
2280
|
// src/tools/audit.ts
|
|
2235
2281
|
init_cjs_shims();
|
|
@@ -2296,27 +2342,27 @@ function extractJson(text) {
|
|
|
2296
2342
|
|
|
2297
2343
|
// src/core/json-schema.ts
|
|
2298
2344
|
init_cjs_shims();
|
|
2299
|
-
function validateSchema(value, schema,
|
|
2345
|
+
function validateSchema(value, schema, path15 = "") {
|
|
2300
2346
|
const errs = [];
|
|
2301
2347
|
if ("anyOf" in schema) {
|
|
2302
2348
|
const subs = schema["anyOf"].map(
|
|
2303
|
-
(s) => validateSchema(value, s,
|
|
2349
|
+
(s) => validateSchema(value, s, path15)
|
|
2304
2350
|
);
|
|
2305
2351
|
if (!subs.some((e) => e.length === 0)) {
|
|
2306
|
-
errs.push(`${
|
|
2352
|
+
errs.push(`${path15 || "<root>"}: did not match anyOf`);
|
|
2307
2353
|
}
|
|
2308
2354
|
return errs;
|
|
2309
2355
|
}
|
|
2310
2356
|
if ("oneOf" in schema) {
|
|
2311
|
-
const passed = schema["oneOf"].filter((s) => validateSchema(value, s,
|
|
2357
|
+
const passed = schema["oneOf"].filter((s) => validateSchema(value, s, path15).length === 0).length;
|
|
2312
2358
|
if (passed !== 1) {
|
|
2313
|
-
errs.push(`${
|
|
2359
|
+
errs.push(`${path15 || "<root>"}: matched ${passed} of oneOf, expected 1`);
|
|
2314
2360
|
}
|
|
2315
2361
|
return errs;
|
|
2316
2362
|
}
|
|
2317
2363
|
if ("const" in schema && !deepEqual(value, schema["const"])) {
|
|
2318
2364
|
errs.push(
|
|
2319
|
-
`${
|
|
2365
|
+
`${path15 || "<root>"}: expected const ${pyRepr(schema["const"])}, got ${pyRepr(value)}`
|
|
2320
2366
|
);
|
|
2321
2367
|
return errs;
|
|
2322
2368
|
}
|
|
@@ -2326,7 +2372,7 @@ function validateSchema(value, schema, path14 = "") {
|
|
|
2326
2372
|
const ok = types.some((tt) => matchesType(value, String(tt)));
|
|
2327
2373
|
if (!ok) {
|
|
2328
2374
|
errs.push(
|
|
2329
|
-
`${
|
|
2375
|
+
`${path15 || "<root>"}: expected type ${pyReprType(t)}, got ${pyTypeName(value)}`
|
|
2330
2376
|
);
|
|
2331
2377
|
return errs;
|
|
2332
2378
|
}
|
|
@@ -2335,29 +2381,29 @@ function validateSchema(value, schema, path14 = "") {
|
|
|
2335
2381
|
if ("enum" in schema) {
|
|
2336
2382
|
const en = schema["enum"];
|
|
2337
2383
|
if (!en.some((x) => deepEqual(x, value))) {
|
|
2338
|
-
errs.push(`${
|
|
2384
|
+
errs.push(`${path15 || "<root>"}: value ${pyRepr(value)} not in enum`);
|
|
2339
2385
|
}
|
|
2340
2386
|
}
|
|
2341
2387
|
if ("minLength" in schema && value.length < schema["minLength"]) {
|
|
2342
|
-
errs.push(`${
|
|
2388
|
+
errs.push(`${path15 || "<root>"}: shorter than minLength ${schema["minLength"]}`);
|
|
2343
2389
|
}
|
|
2344
2390
|
}
|
|
2345
2391
|
if (typeof value === "number" && !Number.isNaN(value)) {
|
|
2346
2392
|
if ("minimum" in schema && value < schema["minimum"]) {
|
|
2347
|
-
errs.push(`${
|
|
2393
|
+
errs.push(`${path15 || "<root>"}: ${value} < minimum ${schema["minimum"]}`);
|
|
2348
2394
|
}
|
|
2349
2395
|
if ("maximum" in schema && value > schema["maximum"]) {
|
|
2350
|
-
errs.push(`${
|
|
2396
|
+
errs.push(`${path15 || "<root>"}: ${value} > maximum ${schema["maximum"]}`);
|
|
2351
2397
|
}
|
|
2352
2398
|
}
|
|
2353
2399
|
if (Array.isArray(value)) {
|
|
2354
2400
|
if ("minItems" in schema && value.length < schema["minItems"]) {
|
|
2355
|
-
errs.push(`${
|
|
2401
|
+
errs.push(`${path15 || "<root>"}: fewer items than minItems ${schema["minItems"]}`);
|
|
2356
2402
|
}
|
|
2357
2403
|
const itemSchema = schema["items"];
|
|
2358
2404
|
if (isObj(itemSchema)) {
|
|
2359
2405
|
for (let i = 0; i < value.length; i++) {
|
|
2360
|
-
errs.push(...validateSchema(value[i], itemSchema, `${
|
|
2406
|
+
errs.push(...validateSchema(value[i], itemSchema, `${path15}[${i}]`));
|
|
2361
2407
|
}
|
|
2362
2408
|
}
|
|
2363
2409
|
}
|
|
@@ -2366,19 +2412,19 @@ function validateSchema(value, schema, path14 = "") {
|
|
|
2366
2412
|
const required = schema["required"] ?? [];
|
|
2367
2413
|
for (const r of required) {
|
|
2368
2414
|
if (!(r in value)) {
|
|
2369
|
-
errs.push(`${
|
|
2415
|
+
errs.push(`${path15 || "<root>"}: missing required key ${pyRepr(r)}`);
|
|
2370
2416
|
}
|
|
2371
2417
|
}
|
|
2372
2418
|
if (schema["additionalProperties"] === false) {
|
|
2373
2419
|
for (const k of Object.keys(value)) {
|
|
2374
2420
|
if (!(k in props)) {
|
|
2375
|
-
errs.push(`${
|
|
2421
|
+
errs.push(`${path15 || "<root>"}: unknown key ${pyRepr(k)}`);
|
|
2376
2422
|
}
|
|
2377
2423
|
}
|
|
2378
2424
|
}
|
|
2379
2425
|
for (const [k, v] of Object.entries(value)) {
|
|
2380
2426
|
const ps = props[k];
|
|
2381
|
-
if (ps) errs.push(...validateSchema(v, ps,
|
|
2427
|
+
if (ps) errs.push(...validateSchema(v, ps, path15 ? `${path15}.${k}` : k));
|
|
2382
2428
|
}
|
|
2383
2429
|
}
|
|
2384
2430
|
return errs;
|
|
@@ -2879,8 +2925,8 @@ async function recordSessionCall(storage, sessionId, answers, wallMs, cpuMs, now
|
|
|
2879
2925
|
// src/core/budgets.ts
|
|
2880
2926
|
init_cjs_shims();
|
|
2881
2927
|
var operatorBudgets = null;
|
|
2882
|
-
function setOperatorBudgets(
|
|
2883
|
-
operatorBudgets =
|
|
2928
|
+
function setOperatorBudgets(cfg2) {
|
|
2929
|
+
operatorBudgets = cfg2;
|
|
2884
2930
|
}
|
|
2885
2931
|
function operatorCeiling(purpose, provider) {
|
|
2886
2932
|
const direct = operatorBudgets?.token_budgets?.[purpose];
|
|
@@ -2891,6 +2937,55 @@ function operatorCeiling(purpose, provider) {
|
|
|
2891
2937
|
return null;
|
|
2892
2938
|
}
|
|
2893
2939
|
|
|
2940
|
+
// src/core/model-fallback.ts
|
|
2941
|
+
init_cjs_shims();
|
|
2942
|
+
|
|
2943
|
+
// src/core/retarget.ts
|
|
2944
|
+
init_cjs_shims();
|
|
2945
|
+
function retargetProvider(p, newModel) {
|
|
2946
|
+
if (p.model === newModel) return p;
|
|
2947
|
+
return {
|
|
2948
|
+
name: p.name,
|
|
2949
|
+
model: newModel,
|
|
2950
|
+
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
2951
|
+
};
|
|
2952
|
+
}
|
|
2953
|
+
async function loadProviderWeights(storage, names) {
|
|
2954
|
+
const out = {};
|
|
2955
|
+
for (const raw of names) {
|
|
2956
|
+
const name = raw.toLowerCase();
|
|
2957
|
+
if (name in out) continue;
|
|
2958
|
+
const row = await storage.getProviderStats(name);
|
|
2959
|
+
if (!row) {
|
|
2960
|
+
out[name] = 0.5;
|
|
2961
|
+
continue;
|
|
2962
|
+
}
|
|
2963
|
+
const total = row.wins + row.losses + row.abstains;
|
|
2964
|
+
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
2965
|
+
}
|
|
2966
|
+
return out;
|
|
2967
|
+
}
|
|
2968
|
+
|
|
2969
|
+
// src/core/model-fallback.ts
|
|
2970
|
+
async function sendWithModelFallback(provider, args) {
|
|
2971
|
+
const chain = [provider.model, ...provider.fallbackModels ?? []];
|
|
2972
|
+
let lastErr;
|
|
2973
|
+
for (let i = 0; i < chain.length; i += 1) {
|
|
2974
|
+
const candidate = chain[i];
|
|
2975
|
+
const p = i === 0 ? provider : retargetProvider(provider, candidate);
|
|
2976
|
+
try {
|
|
2977
|
+
const result = await p.send(args);
|
|
2978
|
+
return { result, modelUsed: candidate };
|
|
2979
|
+
} catch (e) {
|
|
2980
|
+
lastErr = e;
|
|
2981
|
+
const isModelAccessFailure2 = e instanceof ProviderError && e.modelAccessFailure;
|
|
2982
|
+
const hasMoreCandidates = i < chain.length - 1;
|
|
2983
|
+
if (!isModelAccessFailure2 || !hasMoreCandidates) throw e;
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
throw lastErr;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2894
2989
|
// src/core/structured.ts
|
|
2895
2990
|
async function requestStructured(provider, baseMessages, schema, opts) {
|
|
2896
2991
|
const maxRetries = opts.maxRetries ?? 1;
|
|
@@ -2969,7 +3064,7 @@ async function askOne(provider, messages, opts) {
|
|
|
2969
3064
|
const ceiling = operatorCeiling(opts.purpose, provider.name);
|
|
2970
3065
|
if (ceiling !== null && ceiling < maxTokens) maxTokens = ceiling;
|
|
2971
3066
|
try {
|
|
2972
|
-
const r = await provider
|
|
3067
|
+
const { result: r, modelUsed } = await sendWithModelFallback(provider, {
|
|
2973
3068
|
messages,
|
|
2974
3069
|
maxTokens,
|
|
2975
3070
|
temperature: opts.temperature,
|
|
@@ -2982,7 +3077,10 @@ async function askOne(provider, messages, opts) {
|
|
|
2982
3077
|
const cpuMs = Math.trunc((cpu.user + cpu.system) / 1e3);
|
|
2983
3078
|
return {
|
|
2984
3079
|
provider: provider.name,
|
|
2985
|
-
model
|
|
3080
|
+
// modelUsed reflects whichever model actually answered — the
|
|
3081
|
+
// primary, or a fallback if one was needed. Equivalent to the old
|
|
3082
|
+
// `provider.model` whenever no fallback occurred.
|
|
3083
|
+
model: modelUsed,
|
|
2986
3084
|
response: r.text,
|
|
2987
3085
|
attempts: r.attempts,
|
|
2988
3086
|
usage: r.usage,
|
|
@@ -3029,8 +3127,8 @@ function isPersonaId(s) {
|
|
|
3029
3127
|
return PERSONA_IDS.includes(s);
|
|
3030
3128
|
}
|
|
3031
3129
|
var personaConfig = null;
|
|
3032
|
-
function setPersonaConfig(
|
|
3033
|
-
personaConfig =
|
|
3130
|
+
function setPersonaConfig(cfg2) {
|
|
3131
|
+
personaConfig = cfg2;
|
|
3034
3132
|
}
|
|
3035
3133
|
var processFirstCallConsumed = false;
|
|
3036
3134
|
var sessionsSeen = /* @__PURE__ */ new Set();
|
|
@@ -3097,8 +3195,8 @@ function routePersona(prompt, toolName) {
|
|
|
3097
3195
|
return { id: "engineering", scores };
|
|
3098
3196
|
}
|
|
3099
3197
|
function resolvePersona(opts) {
|
|
3100
|
-
const
|
|
3101
|
-
if (
|
|
3198
|
+
const cfg2 = opts.config ?? personaConfig;
|
|
3199
|
+
if (cfg2?.enabled === false) return { id: null, source: "disabled", bypassed: true };
|
|
3102
3200
|
const explicit = opts.explicit?.trim().toLowerCase();
|
|
3103
3201
|
if (explicit === "none" || explicit === "off") {
|
|
3104
3202
|
return { id: null, source: "none", bypassed: true };
|
|
@@ -3144,12 +3242,12 @@ function readIfExists(p) {
|
|
|
3144
3242
|
}
|
|
3145
3243
|
}
|
|
3146
3244
|
function loadPersonaBody(id, opts) {
|
|
3147
|
-
const
|
|
3148
|
-
const cacheKey = `${id}|${opts?.repoRoot ?? ""}|${
|
|
3245
|
+
const cfg2 = opts?.config ?? personaConfig;
|
|
3246
|
+
const cacheKey = `${id}|${opts?.repoRoot ?? ""}|${cfg2?.dir ?? ""}`;
|
|
3149
3247
|
const cached = bodyCache.get(cacheKey);
|
|
3150
3248
|
if (cached !== void 0) return cached;
|
|
3151
3249
|
const candidates = [];
|
|
3152
|
-
if (
|
|
3250
|
+
if (cfg2?.dir) candidates.push(import_node_path2.default.join(cfg2.dir, `${id}.md`));
|
|
3153
3251
|
if (opts?.repoRoot) candidates.push(import_node_path2.default.join(opts.repoRoot, ".crosscheck", "personas", `${id}.md`));
|
|
3154
3252
|
if (id === "engineering" && opts?.repoRoot) candidates.push(import_node_path2.default.join(opts.repoRoot, "CLAUDE.md"));
|
|
3155
3253
|
const bundled = bundledPersonasDir();
|
|
@@ -3199,9 +3297,9 @@ function buildPersonaInjection(opts) {
|
|
|
3199
3297
|
|
|
3200
3298
|
// src/core/utils.ts
|
|
3201
3299
|
init_cjs_shims();
|
|
3202
|
-
function checkSessionBreakers(session,
|
|
3300
|
+
function checkSessionBreakers(session, cfg2) {
|
|
3203
3301
|
if (!session || typeof session !== "object") return null;
|
|
3204
|
-
const c =
|
|
3302
|
+
const c = cfg2 ?? {};
|
|
3205
3303
|
const costCap = Number(c.max_session_cost_usd ?? 0);
|
|
3206
3304
|
if (costCap > 0 && Number(session.total_cost_usd ?? 0) >= costCap) {
|
|
3207
3305
|
return {
|
|
@@ -3225,10 +3323,10 @@ function checkSessionBreakers(session, cfg) {
|
|
|
3225
3323
|
}
|
|
3226
3324
|
return null;
|
|
3227
3325
|
}
|
|
3228
|
-
function checkDagBreakers(dag,
|
|
3326
|
+
function checkDagBreakers(dag, cfg2) {
|
|
3229
3327
|
if (!dag || typeof dag !== "object") return null;
|
|
3230
3328
|
const nodes = Array.isArray(dag.nodes) ? dag.nodes : [];
|
|
3231
|
-
const c =
|
|
3329
|
+
const c = cfg2 ?? {};
|
|
3232
3330
|
const maxNodes = Math.trunc(Number(c.max_dag_nodes ?? 0));
|
|
3233
3331
|
if (maxNodes > 0 && nodes.length > maxNodes) {
|
|
3234
3332
|
return {
|
|
@@ -3313,8 +3411,8 @@ function breakerEnvelope(toolName, trip, sessionId) {
|
|
|
3313
3411
|
transient: false
|
|
3314
3412
|
};
|
|
3315
3413
|
}
|
|
3316
|
-
async function maybeBreakerEnvelope(storage, sessionId, toolName,
|
|
3317
|
-
if (!sessionId || !storage || !
|
|
3414
|
+
async function maybeBreakerEnvelope(storage, sessionId, toolName, cfg2, extras) {
|
|
3415
|
+
if (!sessionId || !storage || !cfg2) return null;
|
|
3318
3416
|
let session;
|
|
3319
3417
|
try {
|
|
3320
3418
|
session = await storage.getSession(sessionId) ?? null;
|
|
@@ -3322,13 +3420,13 @@ async function maybeBreakerEnvelope(storage, sessionId, toolName, cfg, extras) {
|
|
|
3322
3420
|
return null;
|
|
3323
3421
|
}
|
|
3324
3422
|
const target = extras && extras.length > 0 ? projectSessionWithAnswers(session, extras) : session;
|
|
3325
|
-
const trip = checkSessionBreakers(target,
|
|
3423
|
+
const trip = checkSessionBreakers(target, cfg2);
|
|
3326
3424
|
if (!trip) return null;
|
|
3327
3425
|
return breakerEnvelope(toolName, trip, sessionId);
|
|
3328
3426
|
}
|
|
3329
|
-
function maybeDagBreakerEnvelope(dag, toolName,
|
|
3330
|
-
if (!
|
|
3331
|
-
const trip = checkDagBreakers(dag ?? null,
|
|
3427
|
+
function maybeDagBreakerEnvelope(dag, toolName, cfg2, sessionId) {
|
|
3428
|
+
if (!cfg2) return null;
|
|
3429
|
+
const trip = checkDagBreakers(dag ?? null, cfg2);
|
|
3332
3430
|
if (!trip) return null;
|
|
3333
3431
|
return breakerEnvelope(toolName, trip, sessionId);
|
|
3334
3432
|
}
|
|
@@ -3351,9 +3449,9 @@ function builtinRedactionRules() {
|
|
|
3351
3449
|
{ pattern: /\b(?:\d{4}[ -]?){3}\d{4}\b/g, label: "CARD", prefix_group: false }
|
|
3352
3450
|
];
|
|
3353
3451
|
}
|
|
3354
|
-
function compileRules(
|
|
3452
|
+
function compileRules(cfg2) {
|
|
3355
3453
|
const rules = builtinRedactionRules();
|
|
3356
|
-
const extras =
|
|
3454
|
+
const extras = cfg2?.patterns_extra ?? [];
|
|
3357
3455
|
for (const src of extras) {
|
|
3358
3456
|
try {
|
|
3359
3457
|
rules.push({ pattern: new RegExp(src, "g"), label: "EXTRA", prefix_group: false });
|
|
@@ -3362,25 +3460,25 @@ function compileRules(cfg) {
|
|
|
3362
3460
|
}
|
|
3363
3461
|
return { rules };
|
|
3364
3462
|
}
|
|
3365
|
-
function hmacSuffix(
|
|
3366
|
-
const secret =
|
|
3367
|
-
const sid =
|
|
3463
|
+
function hmacSuffix(cfg2, label, value) {
|
|
3464
|
+
const secret = cfg2.hmac_secret ?? Buffer.alloc(0);
|
|
3465
|
+
const sid = cfg2.session_id ?? "";
|
|
3368
3466
|
const sidBytes = Buffer.from(sid, "utf8");
|
|
3369
3467
|
const key = Buffer.concat([Buffer.from(secret), sidBytes]);
|
|
3370
3468
|
const digest = (0, import_node_crypto2.createHmac)("sha256", key).update(`${label}:${value}`, "utf8").digest("hex");
|
|
3371
3469
|
return digest.slice(0, 8);
|
|
3372
3470
|
}
|
|
3373
|
-
function redactText(s,
|
|
3471
|
+
function redactText(s, cfg2) {
|
|
3374
3472
|
if (typeof s !== "string" || s.length === 0) return s;
|
|
3375
|
-
if (
|
|
3376
|
-
const { rules } = compileRules(
|
|
3377
|
-
const hmacMode = Boolean(
|
|
3473
|
+
if (cfg2 && cfg2.enabled === false) return s;
|
|
3474
|
+
const { rules } = compileRules(cfg2);
|
|
3475
|
+
const hmacMode = Boolean(cfg2?.hmac_tokens);
|
|
3378
3476
|
let out = s;
|
|
3379
3477
|
for (const { pattern, label, prefix_group } of rules) {
|
|
3380
3478
|
pattern.lastIndex = 0;
|
|
3381
3479
|
if (hmacMode) {
|
|
3382
3480
|
out = out.replace(pattern, (match, ...args) => {
|
|
3383
|
-
const suffix = hmacSuffix(
|
|
3481
|
+
const suffix = hmacSuffix(cfg2 ?? {}, label, match);
|
|
3384
3482
|
const token = `[REDACTED_${label}:${suffix}]`;
|
|
3385
3483
|
if (prefix_group) {
|
|
3386
3484
|
const g1 = args[0];
|
|
@@ -3400,23 +3498,23 @@ function redactText(s, cfg) {
|
|
|
3400
3498
|
}
|
|
3401
3499
|
return out;
|
|
3402
3500
|
}
|
|
3403
|
-
function redactObj(obj,
|
|
3404
|
-
if (typeof obj === "string") return redactText(obj,
|
|
3501
|
+
function redactObj(obj, cfg2) {
|
|
3502
|
+
if (typeof obj === "string") return redactText(obj, cfg2);
|
|
3405
3503
|
if (Array.isArray(obj)) {
|
|
3406
|
-
return obj.map((v) => redactObj(v,
|
|
3504
|
+
return obj.map((v) => redactObj(v, cfg2));
|
|
3407
3505
|
}
|
|
3408
3506
|
if (obj && typeof obj === "object") {
|
|
3409
3507
|
const out = {};
|
|
3410
3508
|
for (const [k, v] of Object.entries(obj)) {
|
|
3411
|
-
out[k] = redactObj(v,
|
|
3509
|
+
out[k] = redactObj(v, cfg2);
|
|
3412
3510
|
}
|
|
3413
3511
|
return out;
|
|
3414
3512
|
}
|
|
3415
3513
|
return obj;
|
|
3416
3514
|
}
|
|
3417
3515
|
var globalRedaction = null;
|
|
3418
|
-
function setRedactionConfig(
|
|
3419
|
-
globalRedaction =
|
|
3516
|
+
function setRedactionConfig(cfg2) {
|
|
3517
|
+
globalRedaction = cfg2;
|
|
3420
3518
|
}
|
|
3421
3519
|
function getRedactionConfig() {
|
|
3422
3520
|
return globalRedaction ?? void 0;
|
|
@@ -3703,32 +3801,6 @@ function numberOrNull(v) {
|
|
|
3703
3801
|
// src/tools/audit.ts
|
|
3704
3802
|
var import_node_perf_hooks = require("perf_hooks");
|
|
3705
3803
|
|
|
3706
|
-
// src/core/retarget.ts
|
|
3707
|
-
init_cjs_shims();
|
|
3708
|
-
function retargetProvider(p, newModel) {
|
|
3709
|
-
if (p.model === newModel) return p;
|
|
3710
|
-
return {
|
|
3711
|
-
name: p.name,
|
|
3712
|
-
model: newModel,
|
|
3713
|
-
send: (args) => p.send({ ...args, modelOverride: newModel })
|
|
3714
|
-
};
|
|
3715
|
-
}
|
|
3716
|
-
async function loadProviderWeights(storage, names) {
|
|
3717
|
-
const out = {};
|
|
3718
|
-
for (const raw of names) {
|
|
3719
|
-
const name = raw.toLowerCase();
|
|
3720
|
-
if (name in out) continue;
|
|
3721
|
-
const row = await storage.getProviderStats(name);
|
|
3722
|
-
if (!row) {
|
|
3723
|
-
out[name] = 0.5;
|
|
3724
|
-
continue;
|
|
3725
|
-
}
|
|
3726
|
-
const total = row.wins + row.losses + row.abstains;
|
|
3727
|
-
out[name] = total <= 0 ? 0.5 : (row.wins + 0.5 * row.abstains) / total;
|
|
3728
|
-
}
|
|
3729
|
-
return out;
|
|
3730
|
-
}
|
|
3731
|
-
|
|
3732
3804
|
// src/core/co-reason.ts
|
|
3733
3805
|
init_cjs_shims();
|
|
3734
3806
|
var CO_REASON_MODEL = "gpt-5.6";
|
|
@@ -4692,8 +4764,8 @@ function neutralizeInjection(s) {
|
|
|
4692
4764
|
|
|
4693
4765
|
// src/core/worker.ts
|
|
4694
4766
|
var WORKER_TOOL_COST_CAP_MODES = ["warn", "enforce", "off"];
|
|
4695
|
-
function workerToolCostCapDefaults(callerCapUsd, callerMode,
|
|
4696
|
-
const cfgObj =
|
|
4767
|
+
function workerToolCostCapDefaults(callerCapUsd, callerMode, cfg2) {
|
|
4768
|
+
const cfgObj = cfg2 ?? {};
|
|
4697
4769
|
let capRaw = callerCapUsd !== void 0 && callerCapUsd !== null ? callerCapUsd : cfgObj.cost_cap_usd;
|
|
4698
4770
|
let cap;
|
|
4699
4771
|
if (capRaw === void 0 || capRaw === null) {
|
|
@@ -6304,7 +6376,7 @@ function tryKill(child, signal, isUnix) {
|
|
|
6304
6376
|
init_cjs_shims();
|
|
6305
6377
|
function shlexSplit(s) {
|
|
6306
6378
|
const out = [];
|
|
6307
|
-
let
|
|
6379
|
+
let current2 = "";
|
|
6308
6380
|
let hasCurrent = false;
|
|
6309
6381
|
let mode = "normal";
|
|
6310
6382
|
for (let i = 0; i < s.length; i++) {
|
|
@@ -6322,7 +6394,7 @@ function shlexSplit(s) {
|
|
|
6322
6394
|
}
|
|
6323
6395
|
if (ch === "\\") {
|
|
6324
6396
|
if (i + 1 < s.length) {
|
|
6325
|
-
|
|
6397
|
+
current2 += s[i + 1];
|
|
6326
6398
|
i++;
|
|
6327
6399
|
hasCurrent = true;
|
|
6328
6400
|
}
|
|
@@ -6330,13 +6402,13 @@ function shlexSplit(s) {
|
|
|
6330
6402
|
}
|
|
6331
6403
|
if (ch === " " || ch === " " || ch === "\n" || ch === "\r") {
|
|
6332
6404
|
if (hasCurrent) {
|
|
6333
|
-
out.push(
|
|
6334
|
-
|
|
6405
|
+
out.push(current2);
|
|
6406
|
+
current2 = "";
|
|
6335
6407
|
hasCurrent = false;
|
|
6336
6408
|
}
|
|
6337
6409
|
continue;
|
|
6338
6410
|
}
|
|
6339
|
-
|
|
6411
|
+
current2 += ch;
|
|
6340
6412
|
hasCurrent = true;
|
|
6341
6413
|
continue;
|
|
6342
6414
|
}
|
|
@@ -6345,7 +6417,7 @@ function shlexSplit(s) {
|
|
|
6345
6417
|
mode = "normal";
|
|
6346
6418
|
continue;
|
|
6347
6419
|
}
|
|
6348
|
-
|
|
6420
|
+
current2 += ch;
|
|
6349
6421
|
continue;
|
|
6350
6422
|
}
|
|
6351
6423
|
if (mode === "double") {
|
|
@@ -6356,21 +6428,21 @@ function shlexSplit(s) {
|
|
|
6356
6428
|
if (ch === "\\") {
|
|
6357
6429
|
const nxt = s[i + 1];
|
|
6358
6430
|
if (nxt === "$" || nxt === "`" || nxt === '"' || nxt === "\\" || nxt === "\n") {
|
|
6359
|
-
|
|
6431
|
+
current2 += nxt;
|
|
6360
6432
|
i++;
|
|
6361
6433
|
continue;
|
|
6362
6434
|
}
|
|
6363
|
-
|
|
6435
|
+
current2 += "\\";
|
|
6364
6436
|
continue;
|
|
6365
6437
|
}
|
|
6366
|
-
|
|
6438
|
+
current2 += ch;
|
|
6367
6439
|
continue;
|
|
6368
6440
|
}
|
|
6369
6441
|
}
|
|
6370
6442
|
if (mode !== "normal") {
|
|
6371
6443
|
throw new Error("unmatched quote");
|
|
6372
6444
|
}
|
|
6373
|
-
if (hasCurrent) out.push(
|
|
6445
|
+
if (hasCurrent) out.push(current2);
|
|
6374
6446
|
return out;
|
|
6375
6447
|
}
|
|
6376
6448
|
|
|
@@ -7078,16 +7150,16 @@ function computeDrift(opts) {
|
|
|
7078
7150
|
const absPath = resolvePinPath(opts);
|
|
7079
7151
|
const doc = loadDoc(absPath);
|
|
7080
7152
|
const pinned = doc && typeof doc === "object" && doc["pins"] && typeof doc["pins"] === "object" ? doc["pins"] : {};
|
|
7081
|
-
const
|
|
7153
|
+
const current2 = computeCurrent(opts);
|
|
7082
7154
|
const drift = [];
|
|
7083
7155
|
for (const [p, h] of Object.entries(pinned)) {
|
|
7084
|
-
if (
|
|
7156
|
+
if (current2[p] && current2[p] !== h) drift.push(p);
|
|
7085
7157
|
}
|
|
7086
|
-
const missingFromPin = Object.keys(
|
|
7087
|
-
const missingFiles = Object.keys(pinned).filter((p) => !(p in
|
|
7158
|
+
const missingFromPin = Object.keys(current2).filter((p) => !(p in pinned));
|
|
7159
|
+
const missingFiles = Object.keys(pinned).filter((p) => !(p in current2));
|
|
7088
7160
|
return {
|
|
7089
7161
|
pinned,
|
|
7090
|
-
current,
|
|
7162
|
+
current: current2,
|
|
7091
7163
|
drift: drift.sort(),
|
|
7092
7164
|
missing_from_pin: missingFromPin.sort(),
|
|
7093
7165
|
missing_files: missingFiles.sort(),
|
|
@@ -7372,19 +7444,19 @@ function sortKeyJsonifyTopLevel(json) {
|
|
|
7372
7444
|
for (const k of Object.keys(obj).sort()) sorted[k] = obj[k];
|
|
7373
7445
|
return JSON.stringify(sorted);
|
|
7374
7446
|
}
|
|
7375
|
-
function resolveCacheDir(
|
|
7376
|
-
const raw =
|
|
7447
|
+
function resolveCacheDir(cfg2, repoRoot) {
|
|
7448
|
+
const raw = cfg2.dir ?? ".crosscheck/node_cache";
|
|
7377
7449
|
if ((0, import_node_path8.isAbsolute)(raw)) return raw;
|
|
7378
7450
|
return (0, import_node_path8.resolve)(repoRoot ?? process.cwd(), raw);
|
|
7379
7451
|
}
|
|
7380
|
-
function cachePath(
|
|
7381
|
-
return (0, import_node_path8.join)(resolveCacheDir(
|
|
7452
|
+
function cachePath(cfg2, repoRoot, key) {
|
|
7453
|
+
return (0, import_node_path8.join)(resolveCacheDir(cfg2, repoRoot), key.slice(0, 2), `${key}.json`);
|
|
7382
7454
|
}
|
|
7383
|
-
function getNodeCache(
|
|
7384
|
-
if (!
|
|
7385
|
-
const p = cachePath(
|
|
7455
|
+
function getNodeCache(cfg2, repoRoot, key, nowMs) {
|
|
7456
|
+
if (!cfg2 || cfg2.enabled === false) return null;
|
|
7457
|
+
const p = cachePath(cfg2, repoRoot, key);
|
|
7386
7458
|
if (!(0, import_node_fs8.existsSync)(p)) return null;
|
|
7387
|
-
const ttlS =
|
|
7459
|
+
const ttlS = cfg2.ttl_seconds ?? 604800;
|
|
7388
7460
|
try {
|
|
7389
7461
|
const st = (0, import_node_fs8.statSync)(p);
|
|
7390
7462
|
if (ttlS > 0) {
|
|
@@ -7402,21 +7474,21 @@ function getNodeCache(cfg, repoRoot, key, nowMs) {
|
|
|
7402
7474
|
return null;
|
|
7403
7475
|
}
|
|
7404
7476
|
}
|
|
7405
|
-
function atomicWriteJson(
|
|
7406
|
-
(0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(
|
|
7407
|
-
const tmp = `${
|
|
7477
|
+
function atomicWriteJson(path15, payload) {
|
|
7478
|
+
(0, import_node_fs8.mkdirSync)((0, import_node_path8.dirname)(path15), { recursive: true });
|
|
7479
|
+
const tmp = `${path15}.${process.pid}.${Date.now()}.tmp`;
|
|
7408
7480
|
(0, import_node_fs8.writeFileSync)(tmp, JSON.stringify(payload), "utf8");
|
|
7409
|
-
(0, import_node_fs8.renameSync)(tmp,
|
|
7481
|
+
(0, import_node_fs8.renameSync)(tmp, path15);
|
|
7410
7482
|
}
|
|
7411
|
-
function putNodeCache(
|
|
7412
|
-
if (!
|
|
7483
|
+
function putNodeCache(cfg2, repoRoot, key, value) {
|
|
7484
|
+
if (!cfg2 || cfg2.enabled === false) return;
|
|
7413
7485
|
try {
|
|
7414
|
-
const p = cachePath(
|
|
7486
|
+
const p = cachePath(cfg2, repoRoot, key);
|
|
7415
7487
|
atomicWriteJson(p, value);
|
|
7416
|
-
const maxEntries =
|
|
7417
|
-
const maxBytes =
|
|
7488
|
+
const maxEntries = cfg2.max_entries ?? 2e3;
|
|
7489
|
+
const maxBytes = cfg2.max_cache_bytes ?? 100 * 1024 * 1024;
|
|
7418
7490
|
if (maxEntries <= 0 && maxBytes <= 0) return;
|
|
7419
|
-
const base = resolveCacheDir(
|
|
7491
|
+
const base = resolveCacheDir(cfg2, repoRoot);
|
|
7420
7492
|
if (!(0, import_node_fs8.existsSync)(base)) return;
|
|
7421
7493
|
const entries = [];
|
|
7422
7494
|
let totalBytes = 0;
|
|
@@ -8534,31 +8606,31 @@ async function runFetch(args, opts) {
|
|
|
8534
8606
|
const url = String(args["url"] ?? "");
|
|
8535
8607
|
const force = Boolean(args["force_refresh"]);
|
|
8536
8608
|
const sessionId = typeof args["session_id"] === "string" ? args["session_id"] : null;
|
|
8537
|
-
const
|
|
8609
|
+
const cfg2 = { ...DEFAULT_CONFIG, ...opts.config ?? {} };
|
|
8538
8610
|
const base = { tool: "fetch", url };
|
|
8539
|
-
if (!
|
|
8611
|
+
if (!cfg2.enabled) {
|
|
8540
8612
|
return { ...base, accepted: false, reason: "fetch is disabled" };
|
|
8541
8613
|
}
|
|
8542
8614
|
if (!(url.startsWith("https://") || url.startsWith("http://"))) {
|
|
8543
8615
|
return { ...base, accepted: false, reason: "only http/https schemes are supported" };
|
|
8544
8616
|
}
|
|
8545
|
-
if (
|
|
8617
|
+
if (cfg2.url_allowlist.length === 0) {
|
|
8546
8618
|
return {
|
|
8547
8619
|
...base,
|
|
8548
8620
|
accepted: false,
|
|
8549
8621
|
reason: "fetch.url_allowlist is empty; no URLs may be fetched"
|
|
8550
8622
|
};
|
|
8551
8623
|
}
|
|
8552
|
-
if (!isUrlAllowed(url,
|
|
8624
|
+
if (!isUrlAllowed(url, cfg2.url_allowlist)) {
|
|
8553
8625
|
return {
|
|
8554
8626
|
...base,
|
|
8555
8627
|
accepted: false,
|
|
8556
8628
|
reason: "url is not covered by fetch.url_allowlist",
|
|
8557
|
-
allowlist:
|
|
8629
|
+
allowlist: cfg2.url_allowlist
|
|
8558
8630
|
};
|
|
8559
8631
|
}
|
|
8560
|
-
const maxBytesSession = Math.max(0,
|
|
8561
|
-
const maxUniqueHosts = Math.max(0,
|
|
8632
|
+
const maxBytesSession = Math.max(0, cfg2.max_bytes_per_session);
|
|
8633
|
+
const maxUniqueHosts = Math.max(0, cfg2.max_unique_hosts_per_session);
|
|
8562
8634
|
const host = extractHost(url);
|
|
8563
8635
|
if (sessionId && opts.storage && (maxBytesSession > 0 || maxUniqueHosts > 0)) {
|
|
8564
8636
|
const { total_bytes, unique_hosts } = await opts.storage.getFetchEgressTotals(sessionId);
|
|
@@ -8598,7 +8670,7 @@ async function runFetch(args, opts) {
|
|
|
8598
8670
|
}
|
|
8599
8671
|
}
|
|
8600
8672
|
}
|
|
8601
|
-
const evDir = resolveEvidenceDir(
|
|
8673
|
+
const evDir = resolveEvidenceDir(cfg2.evidence_dir, opts.repoRoot);
|
|
8602
8674
|
const urlHash16 = sha256Hex2(url).slice(0, 16);
|
|
8603
8675
|
const metaPath = import_node_path9.default.join(evDir, `by-url-${urlHash16}.json`);
|
|
8604
8676
|
if ((0, import_node_fs9.existsSync)(metaPath) && !force) {
|
|
@@ -8620,7 +8692,7 @@ async function runFetch(args, opts) {
|
|
|
8620
8692
|
try {
|
|
8621
8693
|
response = await withTimeout(
|
|
8622
8694
|
fetchFn(url, { headers: { "User-Agent": "crosscheck-agent/0.1" } }),
|
|
8623
|
-
|
|
8695
|
+
cfg2.timeout_s * 1e3
|
|
8624
8696
|
);
|
|
8625
8697
|
} catch (e) {
|
|
8626
8698
|
return {
|
|
@@ -8646,8 +8718,8 @@ async function runFetch(args, opts) {
|
|
|
8646
8718
|
const reader = response.body?.getReader?.();
|
|
8647
8719
|
if (!reader) {
|
|
8648
8720
|
const buf = await response.arrayBuffer();
|
|
8649
|
-
if (buf.byteLength >
|
|
8650
|
-
return { ...base, accepted: false, reason: `response exceeds max_bytes=${
|
|
8721
|
+
if (buf.byteLength > cfg2.max_bytes) {
|
|
8722
|
+
return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg2.max_bytes}` };
|
|
8651
8723
|
}
|
|
8652
8724
|
data = new Uint8Array(buf);
|
|
8653
8725
|
} else {
|
|
@@ -8659,7 +8731,7 @@ async function runFetch(args, opts) {
|
|
|
8659
8731
|
if (done) break;
|
|
8660
8732
|
if (value) {
|
|
8661
8733
|
total += value.byteLength;
|
|
8662
|
-
if (total >
|
|
8734
|
+
if (total > cfg2.max_bytes) {
|
|
8663
8735
|
exceeded = true;
|
|
8664
8736
|
await reader.cancel().catch(() => {
|
|
8665
8737
|
});
|
|
@@ -8669,7 +8741,7 @@ async function runFetch(args, opts) {
|
|
|
8669
8741
|
}
|
|
8670
8742
|
}
|
|
8671
8743
|
if (exceeded) {
|
|
8672
|
-
return { ...base, accepted: false, reason: `response exceeds max_bytes=${
|
|
8744
|
+
return { ...base, accepted: false, reason: `response exceeds max_bytes=${cfg2.max_bytes}` };
|
|
8673
8745
|
}
|
|
8674
8746
|
data = new Uint8Array(total);
|
|
8675
8747
|
let offset = 0;
|
|
@@ -12523,15 +12595,15 @@ var CHECK_INTERVAL_SECONDS = 3 * 24 * 60 * 60;
|
|
|
12523
12595
|
var DEFAULT_PACKAGE = "crosscheck-cli";
|
|
12524
12596
|
var FETCH_TIMEOUT_MS = 3e3;
|
|
12525
12597
|
function engineVersion() {
|
|
12526
|
-
return true ? "0.2.
|
|
12598
|
+
return true ? "0.2.16" : "0.0.0-dev";
|
|
12527
12599
|
}
|
|
12528
12600
|
function defaultUpdateCachePath() {
|
|
12529
12601
|
const base = process.env["CROSSCHECK_DATA_DIR"] || import_node_path13.default.join(import_node_os2.default.homedir() || import_node_os2.default.tmpdir(), ".crosscheck");
|
|
12530
12602
|
return import_node_path13.default.join(base, "update_check.json");
|
|
12531
12603
|
}
|
|
12532
|
-
function isNewer(latest,
|
|
12604
|
+
function isNewer(latest, current2) {
|
|
12533
12605
|
const a = parseTriple(latest);
|
|
12534
|
-
const b = parseTriple(
|
|
12606
|
+
const b = parseTriple(current2);
|
|
12535
12607
|
for (let i = 0; i < 3; i += 1) {
|
|
12536
12608
|
if (a[i] !== b[i]) return a[i] > b[i];
|
|
12537
12609
|
}
|
|
@@ -12541,13 +12613,13 @@ function parseTriple(v) {
|
|
|
12541
12613
|
const parts = (v.split("-")[0] ?? "").split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
12542
12614
|
return [parts[0] ?? 0, parts[1] ?? 0, parts[2] ?? 0];
|
|
12543
12615
|
}
|
|
12544
|
-
function buildNotice(pkg,
|
|
12616
|
+
function buildNotice(pkg, current2, latest) {
|
|
12545
12617
|
return {
|
|
12546
12618
|
update_available: true,
|
|
12547
12619
|
package: pkg,
|
|
12548
|
-
current_version:
|
|
12620
|
+
current_version: current2,
|
|
12549
12621
|
latest_version: latest,
|
|
12550
|
-
message: `crosscheck ${latest} is available \u2014 you're on ${
|
|
12622
|
+
message: `crosscheck ${latest} is available \u2014 you're on ${current2}. To upgrade, run:
|
|
12551
12623
|
|
|
12552
12624
|
npm install -g crosscheck-cli@latest
|
|
12553
12625
|
|
|
@@ -14234,13 +14306,214 @@ function attachUpdateNotice(result, toolName, opts) {
|
|
|
14234
14306
|
|
|
14235
14307
|
// src/core/telemetry.ts
|
|
14236
14308
|
init_cjs_shims();
|
|
14309
|
+
var import_node_crypto10 = __toESM(require("crypto"), 1);
|
|
14310
|
+
|
|
14311
|
+
// src/core/segments.ts
|
|
14312
|
+
init_cjs_shims();
|
|
14313
|
+
var import_node_crypto9 = __toESM(require("crypto"), 1);
|
|
14314
|
+
var import_node_fs17 = require("fs");
|
|
14315
|
+
var import_node_path16 = __toESM(require("path"), 1);
|
|
14316
|
+
|
|
14317
|
+
// src/core/git-context.ts
|
|
14318
|
+
init_cjs_shims();
|
|
14319
|
+
var import_node_child_process3 = require("child_process");
|
|
14237
14320
|
var import_node_crypto8 = __toESM(require("crypto"), 1);
|
|
14321
|
+
var TRUNK_BRANCHES = /* @__PURE__ */ new Set([
|
|
14322
|
+
"main",
|
|
14323
|
+
"master",
|
|
14324
|
+
"dev",
|
|
14325
|
+
"develop",
|
|
14326
|
+
"trunk"
|
|
14327
|
+
]);
|
|
14328
|
+
function git(args, cwd) {
|
|
14329
|
+
try {
|
|
14330
|
+
return (0, import_node_child_process3.execFileSync)("git", args, {
|
|
14331
|
+
cwd,
|
|
14332
|
+
encoding: "utf8",
|
|
14333
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
14334
|
+
timeout: 2e3
|
|
14335
|
+
}).trim();
|
|
14336
|
+
} catch {
|
|
14337
|
+
return null;
|
|
14338
|
+
}
|
|
14339
|
+
}
|
|
14340
|
+
function repoIdentity(cwd) {
|
|
14341
|
+
const root = git(["rev-list", "--max-parents=0", "HEAD"], cwd);
|
|
14342
|
+
if (root) return `root:${root.split("\n").pop().trim()}`;
|
|
14343
|
+
const remote = git(["config", "--get", "remote.origin.url"], cwd);
|
|
14344
|
+
if (remote) return `remote:${remote}`;
|
|
14345
|
+
const top = git(["rev-parse", "--show-toplevel"], cwd);
|
|
14346
|
+
return top ? `path:${top}` : null;
|
|
14347
|
+
}
|
|
14348
|
+
function fingerprintOf(seatKey, parts) {
|
|
14349
|
+
return import_node_crypto8.default.createHmac("sha256", seatKey).update(parts.join("\0")).digest("hex");
|
|
14350
|
+
}
|
|
14351
|
+
function readGitContext(seatKey, cwd) {
|
|
14352
|
+
try {
|
|
14353
|
+
const inside = git(["rev-parse", "--is-inside-work-tree"], cwd);
|
|
14354
|
+
if (inside !== "true") return { signal: "no_repo" };
|
|
14355
|
+
const repo = repoIdentity(cwd);
|
|
14356
|
+
if (!repo) return { signal: "no_repo" };
|
|
14357
|
+
const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], cwd);
|
|
14358
|
+
if (!branch || branch === "HEAD") {
|
|
14359
|
+
const wt = git(["rev-parse", "--show-toplevel"], cwd) ?? cwd;
|
|
14360
|
+
return {
|
|
14361
|
+
fingerprint: fingerprintOf(seatKey, [repo, "worktree", wt]),
|
|
14362
|
+
signal: "worktree",
|
|
14363
|
+
localLabel: `worktree:${wt}`
|
|
14364
|
+
};
|
|
14365
|
+
}
|
|
14366
|
+
if (TRUNK_BRANCHES.has(branch)) {
|
|
14367
|
+
return {
|
|
14368
|
+
fingerprint: fingerprintOf(seatKey, [repo, "trunk"]),
|
|
14369
|
+
signal: "trunk",
|
|
14370
|
+
localLabel: `${branch} (maintenance)`
|
|
14371
|
+
};
|
|
14372
|
+
}
|
|
14373
|
+
return {
|
|
14374
|
+
fingerprint: fingerprintOf(seatKey, [repo, "branch", branch]),
|
|
14375
|
+
signal: "branch",
|
|
14376
|
+
localLabel: branch
|
|
14377
|
+
};
|
|
14378
|
+
} catch {
|
|
14379
|
+
return { signal: "no_repo" };
|
|
14380
|
+
}
|
|
14381
|
+
}
|
|
14382
|
+
function sameContext(a, b) {
|
|
14383
|
+
return a.fingerprint === b.fingerprint && a.signal === b.signal;
|
|
14384
|
+
}
|
|
14385
|
+
|
|
14386
|
+
// src/core/segments.ts
|
|
14387
|
+
var POST_TIMEOUT_MS = 5e3;
|
|
14388
|
+
function canonicalSegmentBody(s) {
|
|
14389
|
+
return [
|
|
14390
|
+
s.segmentId,
|
|
14391
|
+
s.seatId,
|
|
14392
|
+
s.orgId,
|
|
14393
|
+
s.sessionId,
|
|
14394
|
+
String(s.seq),
|
|
14395
|
+
s.fingerprint ?? "",
|
|
14396
|
+
s.signal,
|
|
14397
|
+
s.startedAt,
|
|
14398
|
+
s.endedAt ?? ""
|
|
14399
|
+
].join("|");
|
|
14400
|
+
}
|
|
14401
|
+
function signSegment(seatKey, s) {
|
|
14402
|
+
return import_node_crypto9.default.createHmac("sha256", seatKey).update(canonicalSegmentBody(s)).digest("hex");
|
|
14403
|
+
}
|
|
14404
|
+
var cfgResolved = false;
|
|
14405
|
+
var cfg = null;
|
|
14238
14406
|
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14407
|
+
function getConfig() {
|
|
14408
|
+
if (cfgResolved) return cfg;
|
|
14409
|
+
cfgResolved = true;
|
|
14410
|
+
if (process.env["CROSSCHECK_TELEMETRY"] === "off") return cfg = null;
|
|
14411
|
+
const base = process.env["CROSSCHECK_TELEMETRY_URL"];
|
|
14412
|
+
const seatId = process.env["CROSSCHECK_SEAT_ID"];
|
|
14413
|
+
const orgId = process.env["CROSSCHECK_ORG_ID"];
|
|
14414
|
+
const seatKey = process.env["CROSSCHECK_SEAT_HMAC_KEY"];
|
|
14415
|
+
if (!base || !seatId || !orgId || !seatKey) return cfg = null;
|
|
14416
|
+
if (!UUID_RE.test(seatId) || !UUID_RE.test(orgId)) return cfg = null;
|
|
14417
|
+
const sessionId = process.env["CLAUDE_SESSION_ID"] || process.env["CLAUDE_CODE_SESSION_ID"] || `engine-${import_node_crypto9.default.randomUUID()}`;
|
|
14418
|
+
cfg = {
|
|
14419
|
+
url: base.replace(/\/telemetry\/?$/, "/segments"),
|
|
14420
|
+
seatId,
|
|
14421
|
+
orgId,
|
|
14422
|
+
seatKey,
|
|
14423
|
+
sessionId
|
|
14424
|
+
};
|
|
14425
|
+
return cfg;
|
|
14426
|
+
}
|
|
14427
|
+
var current = null;
|
|
14428
|
+
var nextSeq = 0;
|
|
14429
|
+
function currentFingerprint() {
|
|
14430
|
+
return current?.context.fingerprint;
|
|
14431
|
+
}
|
|
14432
|
+
function rememberLabel(context) {
|
|
14433
|
+
if (!context.fingerprint || !context.localLabel) return;
|
|
14434
|
+
try {
|
|
14435
|
+
const dir = process.env["CROSSCHECK_DATA_DIR"] || import_node_path16.default.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? ".", ".crosscheck");
|
|
14436
|
+
const file = import_node_path16.default.join(dir, "features.json");
|
|
14437
|
+
let map = {};
|
|
14438
|
+
if ((0, import_node_fs17.existsSync)(file)) {
|
|
14439
|
+
try {
|
|
14440
|
+
const parsed = JSON.parse((0, import_node_fs17.readFileSync)(file, "utf8"));
|
|
14441
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) map = parsed;
|
|
14442
|
+
} catch {
|
|
14443
|
+
}
|
|
14444
|
+
}
|
|
14445
|
+
map[context.fingerprint] = {
|
|
14446
|
+
label: context.localLabel,
|
|
14447
|
+
lastSeen: (/* @__PURE__ */ new Date()).toISOString()
|
|
14448
|
+
};
|
|
14449
|
+
(0, import_node_fs17.mkdirSync)(dir, { recursive: true });
|
|
14450
|
+
(0, import_node_fs17.writeFileSync)(file, `${JSON.stringify(map, null, 2)}
|
|
14451
|
+
`, "utf8");
|
|
14452
|
+
} catch {
|
|
14453
|
+
}
|
|
14454
|
+
}
|
|
14455
|
+
async function post(url, segments) {
|
|
14456
|
+
try {
|
|
14457
|
+
await fetch(url, {
|
|
14458
|
+
method: "POST",
|
|
14459
|
+
headers: { "content-type": "application/json" },
|
|
14460
|
+
body: JSON.stringify({ segments }),
|
|
14461
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS)
|
|
14462
|
+
});
|
|
14463
|
+
} catch {
|
|
14464
|
+
}
|
|
14465
|
+
}
|
|
14466
|
+
function emit(c, seg, endedAt) {
|
|
14467
|
+
const payload = {
|
|
14468
|
+
segmentId: seg.segmentId,
|
|
14469
|
+
seatId: c.seatId,
|
|
14470
|
+
orgId: c.orgId,
|
|
14471
|
+
sessionId: c.sessionId,
|
|
14472
|
+
seq: seg.seq,
|
|
14473
|
+
...seg.context.fingerprint ? { fingerprint: seg.context.fingerprint } : {},
|
|
14474
|
+
signal: seg.context.signal,
|
|
14475
|
+
startedAt: seg.startedAt,
|
|
14476
|
+
...endedAt ? { endedAt } : {}
|
|
14477
|
+
};
|
|
14478
|
+
const signatureHmac = signSegment(c.seatKey, payload);
|
|
14479
|
+
void post(c.url, [{ ...payload, signatureHmac }]);
|
|
14480
|
+
}
|
|
14481
|
+
function syncSegment(cwd) {
|
|
14482
|
+
try {
|
|
14483
|
+
const c = getConfig();
|
|
14484
|
+
if (!c) return;
|
|
14485
|
+
const context = readGitContext(c.seatKey, cwd ?? process.cwd());
|
|
14486
|
+
if (current && sameContext(current.context, context)) return;
|
|
14487
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
14488
|
+
if (current) emit(c, current, now);
|
|
14489
|
+
current = {
|
|
14490
|
+
segmentId: import_node_crypto9.default.randomUUID(),
|
|
14491
|
+
seq: nextSeq++,
|
|
14492
|
+
context,
|
|
14493
|
+
startedAt: now
|
|
14494
|
+
};
|
|
14495
|
+
rememberLabel(context);
|
|
14496
|
+
emit(c, current);
|
|
14497
|
+
} catch {
|
|
14498
|
+
}
|
|
14499
|
+
}
|
|
14500
|
+
function closeSegment() {
|
|
14501
|
+
try {
|
|
14502
|
+
const c = getConfig();
|
|
14503
|
+
if (!c || !current) return;
|
|
14504
|
+
emit(c, current, (/* @__PURE__ */ new Date()).toISOString());
|
|
14505
|
+
current = null;
|
|
14506
|
+
} catch {
|
|
14507
|
+
}
|
|
14508
|
+
}
|
|
14509
|
+
|
|
14510
|
+
// src/core/telemetry.ts
|
|
14511
|
+
var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14239
14512
|
var BATCH_MAX = 100;
|
|
14240
14513
|
var FLUSH_BATCH_AT = 25;
|
|
14241
14514
|
var FLUSH_INTERVAL_MS = 15e3;
|
|
14242
14515
|
var MAX_QUEUE = 500;
|
|
14243
|
-
var
|
|
14516
|
+
var POST_TIMEOUT_MS2 = 5e3;
|
|
14244
14517
|
var PROVIDER_TO_SCHEMA = {
|
|
14245
14518
|
anthropic: "anthropic",
|
|
14246
14519
|
openai: "openai",
|
|
@@ -14270,7 +14543,7 @@ function toolToPattern(toolName) {
|
|
|
14270
14543
|
return TOOL_TO_PATTERN[String(toolName).toLowerCase()] ?? "other";
|
|
14271
14544
|
}
|
|
14272
14545
|
function canonicalEventBody(e) {
|
|
14273
|
-
|
|
14546
|
+
const base = [
|
|
14274
14547
|
e.eventId,
|
|
14275
14548
|
e.seatId,
|
|
14276
14549
|
e.orgId,
|
|
@@ -14285,13 +14558,22 @@ function canonicalEventBody(e) {
|
|
|
14285
14558
|
e.status,
|
|
14286
14559
|
e.errorClass ?? ""
|
|
14287
14560
|
].join("|");
|
|
14561
|
+
if (e.runId === void 0 && e.purpose === void 0 && e.fingerprint === void 0) {
|
|
14562
|
+
return base;
|
|
14563
|
+
}
|
|
14564
|
+
return [base, e.runId ?? "", e.purpose ?? "", e.fingerprint ?? ""].join("|");
|
|
14565
|
+
}
|
|
14566
|
+
function normalizePurpose(v) {
|
|
14567
|
+
if (typeof v !== "string") return void 0;
|
|
14568
|
+
const slug = v.trim().toLowerCase().replace(/[^a-z0-9_-]/g, "-").slice(0, 32);
|
|
14569
|
+
return /^[a-z0-9][a-z0-9_-]*$/.test(slug) ? slug : void 0;
|
|
14288
14570
|
}
|
|
14289
14571
|
function signEvent(seatSigningKey, e) {
|
|
14290
|
-
return
|
|
14572
|
+
return import_node_crypto10.default.createHmac("sha256", seatSigningKey).update(canonicalEventBody(e)).digest("hex");
|
|
14291
14573
|
}
|
|
14292
14574
|
var configResolved = false;
|
|
14293
14575
|
var config = null;
|
|
14294
|
-
function
|
|
14576
|
+
function getConfig2() {
|
|
14295
14577
|
if (configResolved) return config;
|
|
14296
14578
|
configResolved = true;
|
|
14297
14579
|
if (process.env["CROSSCHECK_TELEMETRY"] === "off") return config = null;
|
|
@@ -14300,7 +14582,7 @@ function getConfig() {
|
|
|
14300
14582
|
const orgId = process.env["CROSSCHECK_ORG_ID"];
|
|
14301
14583
|
const seatKey = process.env["CROSSCHECK_SEAT_HMAC_KEY"];
|
|
14302
14584
|
if (!url || !seatId || !orgId || !seatKey) return config = null;
|
|
14303
|
-
if (!
|
|
14585
|
+
if (!UUID_RE2.test(seatId) || !UUID_RE2.test(orgId)) return config = null;
|
|
14304
14586
|
config = { url, seatId, orgId, seatKey };
|
|
14305
14587
|
return config;
|
|
14306
14588
|
}
|
|
@@ -14335,7 +14617,10 @@ function usageEventsFromEnvelope(out, toolName) {
|
|
|
14335
14617
|
completionTokens: intNonNeg(u.completion_tokens),
|
|
14336
14618
|
costUsdEstimate: cost,
|
|
14337
14619
|
latencyMs,
|
|
14338
|
-
status: "ok"
|
|
14620
|
+
status: "ok",
|
|
14621
|
+
// Envelopes have carried `purpose` per call all along (debate /
|
|
14622
|
+
// debate-coreason / synth / worker); it just was never forwarded.
|
|
14623
|
+
purpose: normalizePurpose(u.purpose)
|
|
14339
14624
|
});
|
|
14340
14625
|
}
|
|
14341
14626
|
return events;
|
|
@@ -14350,11 +14635,11 @@ function scheduleFlush() {
|
|
|
14350
14635
|
}, FLUSH_INTERVAL_MS);
|
|
14351
14636
|
if (typeof flushTimer.unref === "function") flushTimer.unref();
|
|
14352
14637
|
}
|
|
14353
|
-
function enqueue(
|
|
14638
|
+
function enqueue(cfg2, input, runId, fingerprint) {
|
|
14354
14639
|
const base = {
|
|
14355
|
-
eventId:
|
|
14356
|
-
seatId:
|
|
14357
|
-
orgId:
|
|
14640
|
+
eventId: import_node_crypto10.default.randomUUID(),
|
|
14641
|
+
seatId: cfg2.seatId,
|
|
14642
|
+
orgId: cfg2.orgId,
|
|
14358
14643
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
14359
14644
|
provider: input.provider,
|
|
14360
14645
|
model: input.model,
|
|
@@ -14363,9 +14648,17 @@ function enqueue(cfg, input) {
|
|
|
14363
14648
|
completionTokens: input.completionTokens,
|
|
14364
14649
|
costUsdEstimate: input.costUsdEstimate,
|
|
14365
14650
|
latencyMs: input.latencyMs,
|
|
14366
|
-
status: input.status
|
|
14367
|
-
|
|
14368
|
-
|
|
14651
|
+
status: input.status,
|
|
14652
|
+
runId,
|
|
14653
|
+
// Omit entirely (not "") when unknown, so the canonical body stays
|
|
14654
|
+
// consistent with what the server reconstructs from the JSON.
|
|
14655
|
+
...input.purpose === void 0 ? {} : { purpose: input.purpose },
|
|
14656
|
+
// Panel attribution is EXACT: we know the active git segment at emit
|
|
14657
|
+
// time, so there is no time-join and no ambiguity window — unlike host
|
|
14658
|
+
// tokens, which can only be matched by session and timestamp.
|
|
14659
|
+
...fingerprint === void 0 ? {} : { fingerprint }
|
|
14660
|
+
};
|
|
14661
|
+
const signatureHmac = signEvent(cfg2.seatKey, base);
|
|
14369
14662
|
queue.push({ ...base, signatureHmac });
|
|
14370
14663
|
if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
|
|
14371
14664
|
if (queue.length >= FLUSH_BATCH_AT) void flush();
|
|
@@ -14373,10 +14666,14 @@ function enqueue(cfg, input) {
|
|
|
14373
14666
|
}
|
|
14374
14667
|
function recordUsage(out, toolName) {
|
|
14375
14668
|
try {
|
|
14376
|
-
const
|
|
14377
|
-
if (!
|
|
14669
|
+
const cfg2 = getConfig2();
|
|
14670
|
+
if (!cfg2) return;
|
|
14378
14671
|
if (toolName === "update_crosscheck") return;
|
|
14379
|
-
|
|
14672
|
+
const runId = import_node_crypto10.default.randomUUID();
|
|
14673
|
+
const fingerprint = currentFingerprint();
|
|
14674
|
+
for (const ev of usageEventsFromEnvelope(out, toolName)) {
|
|
14675
|
+
enqueue(cfg2, ev, runId, fingerprint);
|
|
14676
|
+
}
|
|
14380
14677
|
} catch {
|
|
14381
14678
|
}
|
|
14382
14679
|
}
|
|
@@ -14387,7 +14684,7 @@ async function postBatch(url, batch) {
|
|
|
14387
14684
|
method: "POST",
|
|
14388
14685
|
headers: { "content-type": "application/json" },
|
|
14389
14686
|
body: JSON.stringify({ events: batch }),
|
|
14390
|
-
signal: AbortSignal.timeout(
|
|
14687
|
+
signal: AbortSignal.timeout(POST_TIMEOUT_MS2)
|
|
14391
14688
|
});
|
|
14392
14689
|
} catch {
|
|
14393
14690
|
queue.unshift(...batch);
|
|
@@ -14409,15 +14706,15 @@ async function postBatch(url, batch) {
|
|
|
14409
14706
|
if (queue.length > MAX_QUEUE) queue.splice(0, queue.length - MAX_QUEUE);
|
|
14410
14707
|
}
|
|
14411
14708
|
async function flush() {
|
|
14412
|
-
const
|
|
14413
|
-
if (!
|
|
14709
|
+
const cfg2 = getConfig2();
|
|
14710
|
+
if (!cfg2) return;
|
|
14414
14711
|
if (flushTimer) {
|
|
14415
14712
|
clearTimeout(flushTimer);
|
|
14416
14713
|
flushTimer = null;
|
|
14417
14714
|
}
|
|
14418
14715
|
while (queue.length > 0) {
|
|
14419
14716
|
const batch = queue.splice(0, BATCH_MAX);
|
|
14420
|
-
await postBatch(
|
|
14717
|
+
await postBatch(cfg2.url, batch);
|
|
14421
14718
|
if (queue.length > 0 && queue[0] === batch[0]) break;
|
|
14422
14719
|
}
|
|
14423
14720
|
}
|
|
@@ -14457,14 +14754,14 @@ function createServer(opts = {}) {
|
|
|
14457
14754
|
});
|
|
14458
14755
|
};
|
|
14459
14756
|
const tools = buildToolRegistry(opts);
|
|
14460
|
-
server.setRequestHandler(
|
|
14757
|
+
server.setRequestHandler(import_types9.ListToolsRequestSchema, async () => ({
|
|
14461
14758
|
tools: Array.from(tools.values()).map((t) => ({
|
|
14462
14759
|
name: t.name,
|
|
14463
14760
|
description: t.description,
|
|
14464
14761
|
inputSchema: t.inputSchema
|
|
14465
14762
|
}))
|
|
14466
14763
|
}));
|
|
14467
|
-
server.setRequestHandler(
|
|
14764
|
+
server.setRequestHandler(import_types9.CallToolRequestSchema, async (req) => {
|
|
14468
14765
|
const name = req.params.name;
|
|
14469
14766
|
const tool = tools.get(name);
|
|
14470
14767
|
if (!tool) {
|
|
@@ -14485,6 +14782,7 @@ function createServer(opts = {}) {
|
|
|
14485
14782
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
14486
14783
|
const startedHr = process.hrtime.bigint();
|
|
14487
14784
|
const id = nextEventId();
|
|
14785
|
+
syncSegment(opts.repoRoot);
|
|
14488
14786
|
const gateBlock = configPinGate(name, {
|
|
14489
14787
|
...opts.repoRoot ? { repoRoot: opts.repoRoot } : {},
|
|
14490
14788
|
...opts.configPinning ? { config: opts.configPinning } : {},
|
|
@@ -14566,6 +14864,13 @@ function createServer(opts = {}) {
|
|
|
14566
14864
|
async function connectAndServe(transport, opts = {}) {
|
|
14567
14865
|
const server = createServer(opts);
|
|
14568
14866
|
await server.connect(transport);
|
|
14867
|
+
for (const sig of ["SIGINT", "SIGTERM"]) {
|
|
14868
|
+
process.once(sig, () => {
|
|
14869
|
+
closeSegment();
|
|
14870
|
+
process.exit(0);
|
|
14871
|
+
});
|
|
14872
|
+
}
|
|
14873
|
+
process.once("beforeExit", () => closeSegment());
|
|
14569
14874
|
const cliVersion = process.env["CROSSCHECK_CLI_VERSION"];
|
|
14570
14875
|
void maybeCheckForUpdate({
|
|
14571
14876
|
currentVersion: cliVersion ?? SERVER_VERSION,
|
|
@@ -14603,9 +14908,9 @@ function buildToolRegistry(opts) {
|
|
|
14603
14908
|
|
|
14604
14909
|
// src/core/config-file.ts
|
|
14605
14910
|
init_cjs_shims();
|
|
14606
|
-
var
|
|
14607
|
-
var
|
|
14608
|
-
var
|
|
14911
|
+
var import_node_fs18 = require("fs");
|
|
14912
|
+
var import_node_path17 = __toESM(require("path"), 1);
|
|
14913
|
+
var import_node_crypto11 = require("crypto");
|
|
14609
14914
|
function asObj(v) {
|
|
14610
14915
|
return v && typeof v === "object" && !Array.isArray(v) ? v : void 0;
|
|
14611
14916
|
}
|
|
@@ -14632,18 +14937,18 @@ function pickNums(src, keys) {
|
|
|
14632
14937
|
}
|
|
14633
14938
|
function findConfigFile(opts) {
|
|
14634
14939
|
const direct = opts.explicitPath ?? process.env["CROSSCHECK_CONFIG_PATH"];
|
|
14635
|
-
if (direct) return (0,
|
|
14940
|
+
if (direct) return (0, import_node_fs18.existsSync)(direct) ? direct : void 0;
|
|
14636
14941
|
const candidates = [];
|
|
14637
14942
|
let dir = opts.startDir;
|
|
14638
14943
|
for (let i = 0; dir && i < 8; i += 1) {
|
|
14639
|
-
candidates.push(
|
|
14640
|
-
const parent =
|
|
14944
|
+
candidates.push(import_node_path17.default.join(dir, "crosscheck.config.json"));
|
|
14945
|
+
const parent = import_node_path17.default.dirname(dir);
|
|
14641
14946
|
if (parent === dir) break;
|
|
14642
14947
|
dir = parent;
|
|
14643
14948
|
}
|
|
14644
|
-
if (opts.repoRoot) candidates.push(
|
|
14645
|
-
if (opts.dataDir) candidates.push(
|
|
14646
|
-
return candidates.find((p) => (0,
|
|
14949
|
+
if (opts.repoRoot) candidates.push(import_node_path17.default.join(opts.repoRoot, "crosscheck.config.json"));
|
|
14950
|
+
if (opts.dataDir) candidates.push(import_node_path17.default.join(opts.dataDir, "crosscheck.config.json"));
|
|
14951
|
+
return candidates.find((p) => (0, import_node_fs18.existsSync)(p));
|
|
14647
14952
|
}
|
|
14648
14953
|
function mapConfig(raw, sourcePath) {
|
|
14649
14954
|
const result = { sourcePath };
|
|
@@ -14660,16 +14965,16 @@ function mapConfig(raw, sourcePath) {
|
|
|
14660
14965
|
}
|
|
14661
14966
|
const nc = asObj(raw["node_cache"]);
|
|
14662
14967
|
if (nc) {
|
|
14663
|
-
const
|
|
14968
|
+
const cfg2 = {
|
|
14664
14969
|
...pickNums(nc, ["ttl_seconds", "max_entries", "max_cache_bytes"])
|
|
14665
14970
|
};
|
|
14666
|
-
if (bool(nc["enabled"]) !== void 0)
|
|
14667
|
-
if (str(nc["dir"]) !== void 0)
|
|
14668
|
-
result.nodeCache =
|
|
14971
|
+
if (bool(nc["enabled"]) !== void 0) cfg2.enabled = bool(nc["enabled"]);
|
|
14972
|
+
if (str(nc["dir"]) !== void 0) cfg2.dir = str(nc["dir"]);
|
|
14973
|
+
result.nodeCache = cfg2;
|
|
14669
14974
|
}
|
|
14670
14975
|
const f = asObj(raw["fetch"]);
|
|
14671
14976
|
if (f) {
|
|
14672
|
-
const
|
|
14977
|
+
const cfg2 = {
|
|
14673
14978
|
...pickNums(f, [
|
|
14674
14979
|
"max_bytes",
|
|
14675
14980
|
"max_bytes_per_session",
|
|
@@ -14677,10 +14982,10 @@ function mapConfig(raw, sourcePath) {
|
|
|
14677
14982
|
"timeout_s"
|
|
14678
14983
|
])
|
|
14679
14984
|
};
|
|
14680
|
-
if (bool(f["enabled"]) !== void 0)
|
|
14681
|
-
if (strArr(f["url_allowlist"]) !== void 0)
|
|
14682
|
-
if (str(f["evidence_dir"]) !== void 0)
|
|
14683
|
-
result.fetchConfig =
|
|
14985
|
+
if (bool(f["enabled"]) !== void 0) cfg2.enabled = bool(f["enabled"]);
|
|
14986
|
+
if (strArr(f["url_allowlist"]) !== void 0) cfg2.url_allowlist = strArr(f["url_allowlist"]);
|
|
14987
|
+
if (str(f["evidence_dir"]) !== void 0) cfg2.evidence_dir = str(f["evidence_dir"]);
|
|
14988
|
+
result.fetchConfig = cfg2;
|
|
14684
14989
|
}
|
|
14685
14990
|
if ("provider_allowlist" in raw) {
|
|
14686
14991
|
const pa = raw["provider_allowlist"];
|
|
@@ -14704,10 +15009,10 @@ function mapConfig(raw, sourcePath) {
|
|
|
14704
15009
|
if (retries) {
|
|
14705
15010
|
const maxAttempts = num(retries["max_attempts"]);
|
|
14706
15011
|
const baseS = num(retries["backoff_base_s"]);
|
|
14707
|
-
const
|
|
14708
|
-
if (maxAttempts !== void 0)
|
|
14709
|
-
if (baseS !== void 0)
|
|
14710
|
-
if (Object.keys(
|
|
15012
|
+
const cfg2 = {};
|
|
15013
|
+
if (maxAttempts !== void 0) cfg2.maxAttempts = Math.max(1, Math.trunc(maxAttempts));
|
|
15014
|
+
if (baseS !== void 0) cfg2.backoffBaseMs = Math.round(baseS * 1e3);
|
|
15015
|
+
if (Object.keys(cfg2).length > 0) setRetryConfig(cfg2);
|
|
14711
15016
|
}
|
|
14712
15017
|
const rl = asObj(raw["rate_limits"]);
|
|
14713
15018
|
if (rl) {
|
|
@@ -14733,22 +15038,22 @@ function mapConfig(raw, sourcePath) {
|
|
|
14733
15038
|
}
|
|
14734
15039
|
const red = asObj(raw["redaction"]);
|
|
14735
15040
|
if (red) {
|
|
14736
|
-
const
|
|
14737
|
-
if (bool(red["enabled"]) !== void 0)
|
|
15041
|
+
const cfg2 = {};
|
|
15042
|
+
if (bool(red["enabled"]) !== void 0) cfg2.enabled = bool(red["enabled"]);
|
|
14738
15043
|
const extras = strArr(red["patterns_extra"]);
|
|
14739
|
-
if (extras && extras.length > 0)
|
|
15044
|
+
if (extras && extras.length > 0) cfg2.patterns_extra = extras;
|
|
14740
15045
|
if (bool(red["hmac_tokens"]) === true) {
|
|
14741
|
-
|
|
14742
|
-
|
|
15046
|
+
cfg2.hmac_tokens = true;
|
|
15047
|
+
cfg2.hmac_secret = (0, import_node_crypto11.randomBytes)(32);
|
|
14743
15048
|
}
|
|
14744
|
-
if (Object.keys(
|
|
15049
|
+
if (Object.keys(cfg2).length > 0) setRedactionConfig(cfg2);
|
|
14745
15050
|
}
|
|
14746
15051
|
const personas = asObj(raw["personas"]);
|
|
14747
15052
|
if (personas) {
|
|
14748
|
-
const
|
|
14749
|
-
if (bool(personas["enabled"]) !== void 0)
|
|
14750
|
-
if (str(personas["dir"]) !== void 0)
|
|
14751
|
-
if (Object.keys(
|
|
15053
|
+
const cfg2 = {};
|
|
15054
|
+
if (bool(personas["enabled"]) !== void 0) cfg2.enabled = bool(personas["enabled"]);
|
|
15055
|
+
if (str(personas["dir"]) !== void 0) cfg2.dir = str(personas["dir"]);
|
|
15056
|
+
if (Object.keys(cfg2).length > 0) setPersonaConfig(cfg2);
|
|
14752
15057
|
}
|
|
14753
15058
|
return result;
|
|
14754
15059
|
}
|
|
@@ -14757,7 +15062,7 @@ function loadConfigFile(opts) {
|
|
|
14757
15062
|
if (!file) return null;
|
|
14758
15063
|
let raw;
|
|
14759
15064
|
try {
|
|
14760
|
-
raw = JSON.parse((0,
|
|
15065
|
+
raw = JSON.parse((0, import_node_fs18.readFileSync)(file, "utf8"));
|
|
14761
15066
|
} catch (e) {
|
|
14762
15067
|
try {
|
|
14763
15068
|
process.stderr.write(
|
|
@@ -14777,7 +15082,7 @@ function loadConfigFile(opts) {
|
|
|
14777
15082
|
var SHUTDOWN_TIMEOUT_MS = 2e3;
|
|
14778
15083
|
async function main() {
|
|
14779
15084
|
enforceLicenseOrExit();
|
|
14780
|
-
const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] ||
|
|
15085
|
+
const cfgDataDir = process.env["CROSSCHECK_DATA_DIR"] || import_node_path18.default.join(process.env["HOME"] ?? process.env["USERPROFILE"] ?? "", ".crosscheck");
|
|
14781
15086
|
const configFile = loadConfigFile({
|
|
14782
15087
|
startDir: process.cwd(),
|
|
14783
15088
|
repoRoot: findGitRoot(__dirname),
|
|
@@ -14802,7 +15107,7 @@ async function main() {
|
|
|
14802
15107
|
installShutdownHandlers();
|
|
14803
15108
|
const bundledPricing = (() => {
|
|
14804
15109
|
try {
|
|
14805
|
-
return
|
|
15110
|
+
return import_node_path18.default.join(import_node_path18.default.dirname((0, import_node_url2.fileURLToPath)(importMetaUrl)), "pricing.json");
|
|
14806
15111
|
} catch {
|
|
14807
15112
|
return void 0;
|
|
14808
15113
|
}
|
|
@@ -14811,7 +15116,7 @@ async function main() {
|
|
|
14811
15116
|
process.env["CROSSCHECK_PRICING_PATH"],
|
|
14812
15117
|
resolveRepoFile("config/pricing.json"),
|
|
14813
15118
|
bundledPricing
|
|
14814
|
-
].find((p) => p && (0,
|
|
15119
|
+
].find((p) => p && (0, import_node_fs19.existsSync)(p));
|
|
14815
15120
|
const pricing = pricingPath ? loadPricing(pricingPath) : {};
|
|
14816
15121
|
const providers = buildProviders({ env: process.env, pricing });
|
|
14817
15122
|
if (Object.keys(providers).length > 0) {
|
|
@@ -14829,10 +15134,10 @@ async function main() {
|
|
|
14829
15134
|
}
|
|
14830
15135
|
let storage;
|
|
14831
15136
|
if (process.env["CROSSCHECK_DB_DISABLED"] !== "1") {
|
|
14832
|
-
const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ??
|
|
15137
|
+
const dbPath = process.env["CROSSCHECK_DB_PATH"] ?? resolveRepoFile(".crosscheck/db.sqlite") ?? import_node_path18.default.join(cfgDataDir, "db.sqlite");
|
|
14833
15138
|
if (dbPath) {
|
|
14834
15139
|
try {
|
|
14835
|
-
(0,
|
|
15140
|
+
(0, import_node_fs19.mkdirSync)(import_node_path18.default.dirname(dbPath), { recursive: true });
|
|
14836
15141
|
const { openBetterSqliteStorage: openBetterSqliteStorage2 } = await Promise.resolve().then(() => (init_better_sqlite3(), better_sqlite3_exports));
|
|
14837
15142
|
storage = openBetterSqliteStorage2({ path: dbPath });
|
|
14838
15143
|
await storage.migrate();
|
|
@@ -14900,9 +15205,9 @@ function installShutdownHandlers() {
|
|
|
14900
15205
|
function resolveRepoFile(rel) {
|
|
14901
15206
|
let dir = __dirname;
|
|
14902
15207
|
for (let i = 0; i < 8; i++) {
|
|
14903
|
-
const candidate =
|
|
14904
|
-
if ((0,
|
|
14905
|
-
const parent =
|
|
15208
|
+
const candidate = import_node_path18.default.join(dir, rel);
|
|
15209
|
+
if ((0, import_node_fs19.existsSync)(candidate)) return candidate;
|
|
15210
|
+
const parent = import_node_path18.default.dirname(dir);
|
|
14906
15211
|
if (parent === dir) break;
|
|
14907
15212
|
dir = parent;
|
|
14908
15213
|
}
|
|
@@ -14911,8 +15216,8 @@ function resolveRepoFile(rel) {
|
|
|
14911
15216
|
function findGitRoot(startDir) {
|
|
14912
15217
|
let dir = startDir;
|
|
14913
15218
|
for (let i = 0; i < 12; i++) {
|
|
14914
|
-
if ((0,
|
|
14915
|
-
const parent =
|
|
15219
|
+
if ((0, import_node_fs19.existsSync)(import_node_path18.default.join(dir, ".git"))) return dir;
|
|
15220
|
+
const parent = import_node_path18.default.dirname(dir);
|
|
14916
15221
|
if (parent === dir) break;
|
|
14917
15222
|
dir = parent;
|
|
14918
15223
|
}
|