hillclimb 0.5.4 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +379 -217
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -9,22 +9,6 @@ import * as p6 from "@clack/prompts";
|
|
|
9
9
|
import path7 from "path";
|
|
10
10
|
import * as p3 from "@clack/prompts";
|
|
11
11
|
|
|
12
|
-
// src/color.ts
|
|
13
|
-
var IS_TTY = Boolean(process.stdout.isTTY);
|
|
14
|
-
var NO_COLOR = process.env.FORCE_COLOR !== void 0 ? false : process.env.NO_COLOR !== void 0 || process.env.TERM === "dumb" || !IS_TTY;
|
|
15
|
-
function wrap(open, close) {
|
|
16
|
-
if (NO_COLOR) return (s) => s;
|
|
17
|
-
const o = `\x1B[${open}m`;
|
|
18
|
-
const c = `\x1B[${close}m`;
|
|
19
|
-
return (s) => `${o}${s}${c}`;
|
|
20
|
-
}
|
|
21
|
-
var bold = wrap(1, 22);
|
|
22
|
-
var dim = wrap(2, 22);
|
|
23
|
-
var red = wrap(31, 39);
|
|
24
|
-
var green = wrap(32, 39);
|
|
25
|
-
var yellow = wrap(33, 39);
|
|
26
|
-
var cyan = wrap(36, 39);
|
|
27
|
-
|
|
28
12
|
// src/config.ts
|
|
29
13
|
import fs from "fs";
|
|
30
14
|
import os from "os";
|
|
@@ -369,18 +353,18 @@ var PlatformClient = class {
|
|
|
369
353
|
let code;
|
|
370
354
|
let message = `${method} ${relative} failed: HTTP ${res.status}`;
|
|
371
355
|
try {
|
|
372
|
-
const
|
|
373
|
-
if (
|
|
356
|
+
const text2 = await res.text();
|
|
357
|
+
if (text2) {
|
|
374
358
|
try {
|
|
375
|
-
const parsed = JSON.parse(
|
|
359
|
+
const parsed = JSON.parse(text2);
|
|
376
360
|
if (parsed.error?.code) code = parsed.error.code;
|
|
377
361
|
if (parsed.error?.message) {
|
|
378
362
|
message = `${method} ${relative} failed: ${parsed.error.message}`;
|
|
379
363
|
} else {
|
|
380
|
-
message = `${method} ${relative} failed: ${
|
|
364
|
+
message = `${method} ${relative} failed: ${text2}`;
|
|
381
365
|
}
|
|
382
366
|
} catch {
|
|
383
|
-
message = `${method} ${relative} failed: ${
|
|
367
|
+
message = `${method} ${relative} failed: ${text2}`;
|
|
384
368
|
}
|
|
385
369
|
}
|
|
386
370
|
} catch {
|
|
@@ -397,17 +381,6 @@ var PlatformClient = class {
|
|
|
397
381
|
}
|
|
398
382
|
return void 0;
|
|
399
383
|
}
|
|
400
|
-
async signInWithEmail(email, password2) {
|
|
401
|
-
await this.request("POST", "/api/auth/sign-in/email", {
|
|
402
|
-
email,
|
|
403
|
-
password: password2
|
|
404
|
-
});
|
|
405
|
-
if (!this.sessionCookie) {
|
|
406
|
-
throw new PlatformError(
|
|
407
|
-
"Sign-in succeeded but no session cookie was returned"
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
}
|
|
411
384
|
async getBootstrap() {
|
|
412
385
|
const data = await this.request("GET", "/api/v1/app/bootstrap");
|
|
413
386
|
if (!data?.session?.user || !data.session.session) return null;
|
|
@@ -1483,22 +1456,6 @@ var CLAUDE_DEF = TOOLS[0];
|
|
|
1483
1456
|
|
|
1484
1457
|
// src/commands/_shared.ts
|
|
1485
1458
|
import * as p from "@clack/prompts";
|
|
1486
|
-
async function requireText(label, opts = {}) {
|
|
1487
|
-
const result = opts.password ? await p.password({
|
|
1488
|
-
message: label,
|
|
1489
|
-
validate: (v) => v && v.length > 0 ? void 0 : "Required"
|
|
1490
|
-
}) : await p.text({
|
|
1491
|
-
message: label,
|
|
1492
|
-
placeholder: opts.placeholder,
|
|
1493
|
-
initialValue: opts.initialValue,
|
|
1494
|
-
validate: (v) => v && v.trim().length > 0 ? void 0 : "Required"
|
|
1495
|
-
});
|
|
1496
|
-
if (p.isCancel(result)) {
|
|
1497
|
-
p.cancel("Cancelled.");
|
|
1498
|
-
process.exit(0);
|
|
1499
|
-
}
|
|
1500
|
-
return typeof result === "string" ? result.trim() : "";
|
|
1501
|
-
}
|
|
1502
1459
|
async function withSpinner(startLabel, onSuccess, onFailureLabel, fn) {
|
|
1503
1460
|
const spinner5 = p.spinner();
|
|
1504
1461
|
spinner5.start(startLabel);
|
|
@@ -1517,6 +1474,24 @@ async function withSpinner(startLabel, onSuccess, onFailureLabel, fn) {
|
|
|
1517
1474
|
// src/commands/web-auth.ts
|
|
1518
1475
|
import { spawn } from "child_process";
|
|
1519
1476
|
import * as p2 from "@clack/prompts";
|
|
1477
|
+
|
|
1478
|
+
// src/color.ts
|
|
1479
|
+
var IS_TTY = Boolean(process.stdout.isTTY);
|
|
1480
|
+
var NO_COLOR = process.env.FORCE_COLOR !== void 0 ? false : process.env.NO_COLOR !== void 0 || process.env.TERM === "dumb" || !IS_TTY;
|
|
1481
|
+
function wrap(open, close) {
|
|
1482
|
+
if (NO_COLOR) return (s) => s;
|
|
1483
|
+
const o = `\x1B[${open}m`;
|
|
1484
|
+
const c = `\x1B[${close}m`;
|
|
1485
|
+
return (s) => `${o}${s}${c}`;
|
|
1486
|
+
}
|
|
1487
|
+
var bold = wrap(1, 22);
|
|
1488
|
+
var dim = wrap(2, 22);
|
|
1489
|
+
var red = wrap(31, 39);
|
|
1490
|
+
var green = wrap(32, 39);
|
|
1491
|
+
var yellow = wrap(33, 39);
|
|
1492
|
+
var cyan = wrap(36, 39);
|
|
1493
|
+
|
|
1494
|
+
// src/commands/web-auth.ts
|
|
1520
1495
|
function sleep(ms) {
|
|
1521
1496
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1522
1497
|
}
|
|
@@ -1531,10 +1506,6 @@ function openBrowser(url) {
|
|
|
1531
1506
|
}
|
|
1532
1507
|
}
|
|
1533
1508
|
async function webAuth(apiBaseUrl) {
|
|
1534
|
-
if (!IS_TTY) {
|
|
1535
|
-
appendLog("info", "web-auth: skipped (not a TTY)");
|
|
1536
|
-
return null;
|
|
1537
|
-
}
|
|
1538
1509
|
appendLog("info", `web-auth: started (apiBaseUrl=${apiBaseUrl})`);
|
|
1539
1510
|
const client = new PlatformClient(apiBaseUrl);
|
|
1540
1511
|
const codeResponse = await withSpinner(
|
|
@@ -1548,9 +1519,16 @@ async function webAuth(apiBaseUrl) {
|
|
|
1548
1519
|
`web-auth: code prepared (expiresIn=${codeResponse.expiresIn}s, pollInterval=${codeResponse.interval}s)`
|
|
1549
1520
|
);
|
|
1550
1521
|
const url = codeResponse.verificationUrl;
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1522
|
+
if (IS_TTY) {
|
|
1523
|
+
openBrowser(url);
|
|
1524
|
+
p2.log.info(`Opening browser to: ${cyan(url)}`);
|
|
1525
|
+
p2.log.info(dim("If the browser did not open, copy the URL above."));
|
|
1526
|
+
} else {
|
|
1527
|
+
p2.log.info(
|
|
1528
|
+
"To sign in, open this URL in a browser and approve the request:"
|
|
1529
|
+
);
|
|
1530
|
+
p2.log.info(cyan(url));
|
|
1531
|
+
}
|
|
1554
1532
|
const deadline = Date.now() + codeResponse.expiresIn * 1e3;
|
|
1555
1533
|
const interval = codeResponse.interval * 1e3;
|
|
1556
1534
|
const spinner5 = p2.spinner();
|
|
@@ -1674,66 +1652,24 @@ async function runInit(args = []) {
|
|
|
1674
1652
|
bootstrap = await tryReuseIdentity(apiBaseUrl, client, saved);
|
|
1675
1653
|
}
|
|
1676
1654
|
if (!bootstrap) {
|
|
1677
|
-
|
|
1678
|
-
if (
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
value: "web",
|
|
1684
|
-
label: "Web authentication (recommended)",
|
|
1685
|
-
hint: "opens browser"
|
|
1686
|
-
},
|
|
1687
|
-
{ value: "email", label: "Email and password" }
|
|
1688
|
-
],
|
|
1689
|
-
initialValue: "web"
|
|
1690
|
-
});
|
|
1691
|
-
if (p3.isCancel(selected)) {
|
|
1692
|
-
p3.cancel("Cancelled.");
|
|
1693
|
-
process.exit(0);
|
|
1694
|
-
}
|
|
1695
|
-
method = selected;
|
|
1655
|
+
const result = await webAuth(apiBaseUrl);
|
|
1656
|
+
if (!result) {
|
|
1657
|
+
appendLog("error", "init: web authentication failed");
|
|
1658
|
+
p3.log.error("Web authentication failed.");
|
|
1659
|
+
p3.outro("Aborted.");
|
|
1660
|
+
process.exit(1);
|
|
1696
1661
|
}
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1662
|
+
client = result.client;
|
|
1663
|
+
bootstrap = await withSpinner(
|
|
1664
|
+
"Loading account...",
|
|
1665
|
+
(b) => `Signed in as ${b.session.email}${b.session.role === "admin" ? " (admin)" : ""}.`,
|
|
1666
|
+
"Could not load account.",
|
|
1667
|
+
async () => {
|
|
1668
|
+
const data = await client.getBootstrap();
|
|
1669
|
+
if (!data) throw new Error("Bootstrap returned no session.");
|
|
1670
|
+
return data;
|
|
1705
1671
|
}
|
|
1706
|
-
|
|
1707
|
-
bootstrap = await withSpinner(
|
|
1708
|
-
"Loading account...",
|
|
1709
|
-
(b) => `Signed in as ${b.session.email}${b.session.role === "admin" ? " (admin)" : ""}.`,
|
|
1710
|
-
"Could not load account.",
|
|
1711
|
-
async () => {
|
|
1712
|
-
const data = await client.getBootstrap();
|
|
1713
|
-
if (!data) throw new Error("Bootstrap returned no session.");
|
|
1714
|
-
return data;
|
|
1715
|
-
}
|
|
1716
|
-
);
|
|
1717
|
-
} else {
|
|
1718
|
-
const email = await requireText("Email");
|
|
1719
|
-
const password2 = await requireText("Password", { password: true });
|
|
1720
|
-
await withSpinner(
|
|
1721
|
-
"Signing in...",
|
|
1722
|
-
() => "Signed in.",
|
|
1723
|
-
"Sign-in failed.",
|
|
1724
|
-
() => client.signInWithEmail(email, password2)
|
|
1725
|
-
);
|
|
1726
|
-
bootstrap = await withSpinner(
|
|
1727
|
-
"Loading account...",
|
|
1728
|
-
(b) => `Signed in as ${b.session.email}${b.session.role === "admin" ? " (admin)" : ""}.`,
|
|
1729
|
-
"Could not load account.",
|
|
1730
|
-
async () => {
|
|
1731
|
-
const data = await client.getBootstrap();
|
|
1732
|
-
if (!data) throw new Error("Bootstrap returned no session.");
|
|
1733
|
-
return data;
|
|
1734
|
-
}
|
|
1735
|
-
);
|
|
1736
|
-
}
|
|
1672
|
+
);
|
|
1737
1673
|
}
|
|
1738
1674
|
let project;
|
|
1739
1675
|
if (projectSlugFlag) {
|
|
@@ -2070,87 +2006,23 @@ async function runLogin(_args = []) {
|
|
|
2070
2006
|
println();
|
|
2071
2007
|
}
|
|
2072
2008
|
}
|
|
2073
|
-
|
|
2074
|
-
if (
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
options: [
|
|
2078
|
-
{
|
|
2079
|
-
value: "web",
|
|
2080
|
-
label: "Web authentication (recommended)",
|
|
2081
|
-
hint: "opens browser"
|
|
2082
|
-
},
|
|
2083
|
-
{ value: "email", label: "Email and password" }
|
|
2084
|
-
],
|
|
2085
|
-
initialValue: "web"
|
|
2086
|
-
});
|
|
2087
|
-
if (p4.isCancel(selected)) {
|
|
2088
|
-
p4.cancel("Cancelled.");
|
|
2089
|
-
process.exit(0);
|
|
2090
|
-
}
|
|
2091
|
-
method = selected;
|
|
2092
|
-
}
|
|
2093
|
-
appendLog("info", `login: auth method selected (${method})`);
|
|
2094
|
-
if (method === "web") {
|
|
2095
|
-
const result = await webAuth(apiBaseUrl);
|
|
2096
|
-
if (!result) {
|
|
2097
|
-
appendLog("error", "login: web authentication failed");
|
|
2098
|
-
p4.log.error("Web authentication failed.");
|
|
2099
|
-
p4.outro("Aborted.");
|
|
2100
|
-
process.exit(1);
|
|
2101
|
-
}
|
|
2102
|
-
await saveIdentity({
|
|
2103
|
-
apiBaseUrl,
|
|
2104
|
-
sessionCookie: result.sessionCookie,
|
|
2105
|
-
email: result.email,
|
|
2106
|
-
userId: result.userId,
|
|
2107
|
-
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2108
|
-
});
|
|
2109
|
-
appendLog("info", `login: completed via web auth (email=${result.email})`);
|
|
2110
|
-
println();
|
|
2111
|
-
row(CHECK, "Signed in", cyan(result.email));
|
|
2112
|
-
footer("Run `npx hillclimb` in a repo to wire up auto-upload.");
|
|
2113
|
-
return;
|
|
2114
|
-
}
|
|
2115
|
-
const email = await requireText("Email");
|
|
2116
|
-
const password2 = await requireText("Password", { password: true });
|
|
2117
|
-
const client = new PlatformClient(apiBaseUrl);
|
|
2118
|
-
await withSpinner(
|
|
2119
|
-
"Signing in...",
|
|
2120
|
-
() => "Signed in.",
|
|
2121
|
-
"Sign-in failed.",
|
|
2122
|
-
() => client.signInWithEmail(email, password2)
|
|
2123
|
-
);
|
|
2124
|
-
const bootstrap = await withSpinner(
|
|
2125
|
-
"Loading account...",
|
|
2126
|
-
(b) => `Signed in as ${b.session.email}.`,
|
|
2127
|
-
"Could not load account.",
|
|
2128
|
-
async () => {
|
|
2129
|
-
const data = await client.getBootstrap();
|
|
2130
|
-
if (!data) throw new Error("Bootstrap returned no session.");
|
|
2131
|
-
return data;
|
|
2132
|
-
}
|
|
2133
|
-
);
|
|
2134
|
-
const cookie = client.getSessionCookie();
|
|
2135
|
-
if (!cookie) {
|
|
2136
|
-
appendLog("error", "login: session cookie missing after sign-in");
|
|
2137
|
-
p4.log.error("Session cookie was lost during sign-in.");
|
|
2009
|
+
const result = await webAuth(apiBaseUrl);
|
|
2010
|
+
if (!result) {
|
|
2011
|
+
appendLog("error", "login: web authentication failed");
|
|
2012
|
+
p4.log.error("Web authentication failed.");
|
|
2138
2013
|
p4.outro("Aborted.");
|
|
2139
2014
|
process.exit(1);
|
|
2140
2015
|
}
|
|
2141
2016
|
await saveIdentity({
|
|
2142
2017
|
apiBaseUrl,
|
|
2143
|
-
sessionCookie:
|
|
2144
|
-
email:
|
|
2145
|
-
userId:
|
|
2018
|
+
sessionCookie: result.sessionCookie,
|
|
2019
|
+
email: result.email,
|
|
2020
|
+
userId: result.userId,
|
|
2146
2021
|
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
2147
2022
|
});
|
|
2148
|
-
appendLog(
|
|
2149
|
-
"info",
|
|
2150
|
-
`login: completed via email/password (email=${bootstrap.session.email})`
|
|
2151
|
-
);
|
|
2023
|
+
appendLog("info", `login: completed via web auth (email=${result.email})`);
|
|
2152
2024
|
println();
|
|
2153
|
-
row(CHECK, "Signed in", cyan(
|
|
2025
|
+
row(CHECK, "Signed in", cyan(result.email));
|
|
2154
2026
|
footer("Run `npx hillclimb` in a repo to wire up auto-upload.");
|
|
2155
2027
|
}
|
|
2156
2028
|
|
|
@@ -2419,6 +2291,48 @@ async function readFileContent(file) {
|
|
|
2419
2291
|
}
|
|
2420
2292
|
}
|
|
2421
2293
|
|
|
2294
|
+
// src/middleware/large-file.ts
|
|
2295
|
+
import { constants as bufferConstants } from "buffer";
|
|
2296
|
+
var MAX_STRINGIFIABLE_BYTES = bufferConstants.MAX_STRING_LENGTH;
|
|
2297
|
+
var largeFileThreshold = MAX_STRINGIFIABLE_BYTES;
|
|
2298
|
+
function getLargeFileThreshold() {
|
|
2299
|
+
return largeFileThreshold;
|
|
2300
|
+
}
|
|
2301
|
+
var NEWLINE = 10;
|
|
2302
|
+
var OVERSIZED_LINE_PLACEHOLDER = '{"_hillclimb_omitted":"line exceeded max string length; dropped to allow redaction"}';
|
|
2303
|
+
function redactBufferByLine(buffer, redactLine, options = {}) {
|
|
2304
|
+
const maxLineBytes = options.maxLineBytes ?? MAX_STRINGIFIABLE_BYTES;
|
|
2305
|
+
const oversizedPlaceholder = options.oversizedPlaceholder ?? OVERSIZED_LINE_PLACEHOLDER;
|
|
2306
|
+
const pieces = [];
|
|
2307
|
+
const newlineBuf = Buffer.from([NEWLINE]);
|
|
2308
|
+
let totalCount = 0;
|
|
2309
|
+
let oversizedLines = 0;
|
|
2310
|
+
let start = 0;
|
|
2311
|
+
let first = true;
|
|
2312
|
+
for (; ; ) {
|
|
2313
|
+
const nl = buffer.indexOf(NEWLINE, start);
|
|
2314
|
+
const end = nl === -1 ? buffer.length : nl;
|
|
2315
|
+
const slice = buffer.subarray(start, end);
|
|
2316
|
+
if (!first) pieces.push(newlineBuf);
|
|
2317
|
+
first = false;
|
|
2318
|
+
if (slice.length > maxLineBytes) {
|
|
2319
|
+
oversizedLines++;
|
|
2320
|
+
pieces.push(Buffer.from(oversizedPlaceholder, "utf-8"));
|
|
2321
|
+
} else {
|
|
2322
|
+
const { value, count } = redactLine(slice.toString("utf-8"));
|
|
2323
|
+
totalCount += count;
|
|
2324
|
+
pieces.push(Buffer.from(value, "utf-8"));
|
|
2325
|
+
}
|
|
2326
|
+
if (nl === -1) break;
|
|
2327
|
+
start = nl + 1;
|
|
2328
|
+
}
|
|
2329
|
+
return {
|
|
2330
|
+
content: Buffer.concat(pieces),
|
|
2331
|
+
count: totalCount,
|
|
2332
|
+
oversizedLines
|
|
2333
|
+
};
|
|
2334
|
+
}
|
|
2335
|
+
|
|
2422
2336
|
// src/middleware/patterns.json
|
|
2423
2337
|
var patterns_default = {
|
|
2424
2338
|
patterns: [
|
|
@@ -10658,6 +10572,19 @@ function processFiles(files) {
|
|
|
10658
10572
|
}
|
|
10659
10573
|
|
|
10660
10574
|
// src/middleware/pattern-redact.ts
|
|
10575
|
+
function redactPatternLine(line, patterns, memo, isJsonl) {
|
|
10576
|
+
if (isJsonl) {
|
|
10577
|
+
if (!line.trim()) return { value: line, count: 0 };
|
|
10578
|
+
try {
|
|
10579
|
+
const parsed = JSON.parse(line);
|
|
10580
|
+
const walked = walkAndRedactAll(parsed, patterns, memo);
|
|
10581
|
+
return { value: JSON.stringify(walked.value), count: walked.count };
|
|
10582
|
+
} catch {
|
|
10583
|
+
return redactString(line, patterns);
|
|
10584
|
+
}
|
|
10585
|
+
}
|
|
10586
|
+
return redactString(line, patterns);
|
|
10587
|
+
}
|
|
10661
10588
|
var WORKER_COUNT = Math.min(
|
|
10662
10589
|
os3.availableParallelism?.() ?? os3.cpus().length,
|
|
10663
10590
|
4
|
|
@@ -10686,9 +10613,14 @@ var PatternRedactMiddleware = class {
|
|
|
10686
10613
|
const tasks = [];
|
|
10687
10614
|
const fileMap = /* @__PURE__ */ new Map();
|
|
10688
10615
|
const passThrough = [];
|
|
10616
|
+
const largeResults = /* @__PURE__ */ new Map();
|
|
10689
10617
|
for (let i = 0; i < group.files.length; i++) {
|
|
10690
10618
|
const file = group.files[i];
|
|
10691
10619
|
this.stats.filesScanned++;
|
|
10620
|
+
if (file.content && !file.isBinary && file.content.length > getLargeFileThreshold()) {
|
|
10621
|
+
largeResults.set(i, this.redactLargeFile(file));
|
|
10622
|
+
continue;
|
|
10623
|
+
}
|
|
10692
10624
|
const read = await readFileContent(file);
|
|
10693
10625
|
if (read.kind === "binary") {
|
|
10694
10626
|
this.stats.binaryFiles++;
|
|
@@ -10718,6 +10650,11 @@ var PatternRedactMiddleware = class {
|
|
|
10718
10650
|
}
|
|
10719
10651
|
const newFiles = [];
|
|
10720
10652
|
for (let i = 0; i < group.files.length; i++) {
|
|
10653
|
+
const large = largeResults.get(i);
|
|
10654
|
+
if (large) {
|
|
10655
|
+
newFiles.push(large);
|
|
10656
|
+
continue;
|
|
10657
|
+
}
|
|
10721
10658
|
const pt = passThrough.find((p7) => p7.index === i);
|
|
10722
10659
|
if (pt) {
|
|
10723
10660
|
newFiles.push(pt.file);
|
|
@@ -10739,6 +10676,33 @@ var PatternRedactMiddleware = class {
|
|
|
10739
10676
|
}
|
|
10740
10677
|
return { ...group, files: newFiles };
|
|
10741
10678
|
}
|
|
10679
|
+
// Stream-redact an oversized file straight from its buffer, one line at a time,
|
|
10680
|
+
// so we never build a string larger than a single line. Single-threaded (no
|
|
10681
|
+
// worker pool) — it trades parallelism for bounded memory on the one giant file.
|
|
10682
|
+
redactLargeFile(file) {
|
|
10683
|
+
const buffer = file.content;
|
|
10684
|
+
const isJsonl = file.absolutePath.endsWith(".jsonl");
|
|
10685
|
+
const patterns = compilePatterns();
|
|
10686
|
+
const memo = /* @__PURE__ */ new Map();
|
|
10687
|
+
const { content, count, oversizedLines } = redactBufferByLine(
|
|
10688
|
+
buffer,
|
|
10689
|
+
(line) => redactPatternLine(line, patterns, memo, isJsonl)
|
|
10690
|
+
);
|
|
10691
|
+
if (oversizedLines > 0) {
|
|
10692
|
+
appendLog(
|
|
10693
|
+
"warn",
|
|
10694
|
+
`pattern-redact: dropped ${oversizedLines} oversized line(s) in ${file.absolutePath} (exceeded max string length)`
|
|
10695
|
+
);
|
|
10696
|
+
}
|
|
10697
|
+
if (count > 0) {
|
|
10698
|
+
this.stats.filesRedacted++;
|
|
10699
|
+
this.stats.totalRedactions += count;
|
|
10700
|
+
}
|
|
10701
|
+
if (count > 0 || oversizedLines > 0) {
|
|
10702
|
+
return { ...file, content };
|
|
10703
|
+
}
|
|
10704
|
+
return file;
|
|
10705
|
+
}
|
|
10742
10706
|
async processWithWorkers(tasks) {
|
|
10743
10707
|
const workerPath = resolveWorkerPath();
|
|
10744
10708
|
const sorted = [...tasks].sort(
|
|
@@ -10814,6 +10778,10 @@ var RedactMiddleware = class {
|
|
|
10814
10778
|
for (const file of group.files) {
|
|
10815
10779
|
this.stats.filesScanned++;
|
|
10816
10780
|
regex.lastIndex = 0;
|
|
10781
|
+
if (file.content && !file.isBinary && file.content.length > getLargeFileThreshold()) {
|
|
10782
|
+
newFiles.push(this.redactLargeFile(file, regex));
|
|
10783
|
+
continue;
|
|
10784
|
+
}
|
|
10817
10785
|
const read = await readFileContent(file);
|
|
10818
10786
|
if (read.kind === "binary") {
|
|
10819
10787
|
this.stats.binaryFiles++;
|
|
@@ -10849,6 +10817,47 @@ var RedactMiddleware = class {
|
|
|
10849
10817
|
}
|
|
10850
10818
|
return { ...group, files: newFiles };
|
|
10851
10819
|
}
|
|
10820
|
+
// Stream-redact an oversized file straight from its buffer. Each line is
|
|
10821
|
+
// redacted with the same JSON-aware (or raw) logic as redactJsonl, so the
|
|
10822
|
+
// output matches the in-memory path exactly for files whose lines all fit in a
|
|
10823
|
+
// string — which holds for real transcripts (largest line is a few MiB).
|
|
10824
|
+
redactLargeFile(file, regex) {
|
|
10825
|
+
const buffer = file.content;
|
|
10826
|
+
const isJsonl = file.absolutePath.endsWith(".jsonl");
|
|
10827
|
+
const { content, count, oversizedLines } = redactBufferByLine(
|
|
10828
|
+
buffer,
|
|
10829
|
+
(line) => this.redactLine(line, regex, isJsonl)
|
|
10830
|
+
);
|
|
10831
|
+
if (oversizedLines > 0) {
|
|
10832
|
+
appendLog(
|
|
10833
|
+
"warn",
|
|
10834
|
+
`redact: dropped ${oversizedLines} oversized line(s) in ${file.absolutePath} (exceeded max string length)`
|
|
10835
|
+
);
|
|
10836
|
+
}
|
|
10837
|
+
if (count > 0) {
|
|
10838
|
+
this.stats.filesRedacted++;
|
|
10839
|
+
this.stats.totalRedactions += count;
|
|
10840
|
+
}
|
|
10841
|
+
if (count > 0 || oversizedLines > 0) {
|
|
10842
|
+
return { ...file, content };
|
|
10843
|
+
}
|
|
10844
|
+
return file;
|
|
10845
|
+
}
|
|
10846
|
+
redactLine(line, regex, isJsonl) {
|
|
10847
|
+
if (isJsonl) {
|
|
10848
|
+
if (!line.trim()) return { value: line, count: 0 };
|
|
10849
|
+
try {
|
|
10850
|
+
const parsed = JSON.parse(line);
|
|
10851
|
+
const walked = walkAndRedact(parsed, regex);
|
|
10852
|
+
return { value: JSON.stringify(walked.value), count: walked.count };
|
|
10853
|
+
} catch {
|
|
10854
|
+
const { result: result2, count: count2 } = this.redactString(line, regex);
|
|
10855
|
+
return { value: result2, count: count2 };
|
|
10856
|
+
}
|
|
10857
|
+
}
|
|
10858
|
+
const { result, count } = this.redactString(line, regex);
|
|
10859
|
+
return { value: result, count };
|
|
10860
|
+
}
|
|
10852
10861
|
redactJsonl(content, regex) {
|
|
10853
10862
|
let totalCount = 0;
|
|
10854
10863
|
const lines = content.split("\n");
|
|
@@ -11708,9 +11717,9 @@ function extractTextReasoningToolUses(content) {
|
|
|
11708
11717
|
} else if (content !== void 0 && content !== null) {
|
|
11709
11718
|
textParts.push(stringify(content));
|
|
11710
11719
|
}
|
|
11711
|
-
const
|
|
11720
|
+
const text2 = textParts.filter((p7) => p7 && p7.trim()).map((p7) => p7.trim()).join("\n\n");
|
|
11712
11721
|
const reasoning = reasoningParts.filter((p7) => p7 && p7.trim()).map((p7) => p7.trim()).join("\n\n");
|
|
11713
|
-
return [
|
|
11722
|
+
return [text2, reasoning || void 0, toolBlocks];
|
|
11714
11723
|
}
|
|
11715
11724
|
function buildMetrics(usage) {
|
|
11716
11725
|
if (typeof usage !== "object" || usage === null) return void 0;
|
|
@@ -11739,8 +11748,8 @@ function formatToolResult(block, toolUseResult) {
|
|
|
11739
11748
|
if (content.trim()) parts.push(content.trim());
|
|
11740
11749
|
} else if (Array.isArray(content)) {
|
|
11741
11750
|
for (const item of content) {
|
|
11742
|
-
const
|
|
11743
|
-
if (
|
|
11751
|
+
const text2 = stringify(item);
|
|
11752
|
+
if (text2.trim()) parts.push(text2.trim());
|
|
11744
11753
|
}
|
|
11745
11754
|
} else if (content !== void 0 && content !== null && content !== "") {
|
|
11746
11755
|
parts.push(stringify(content));
|
|
@@ -11868,7 +11877,7 @@ function convertClaudeToTrajectory(jsonlContent, sessionId) {
|
|
|
11868
11877
|
const eventType = event.type;
|
|
11869
11878
|
const timestamp = event.timestamp;
|
|
11870
11879
|
if (eventType === "assistant") {
|
|
11871
|
-
const [
|
|
11880
|
+
const [text2, reasoning, toolBlocks] = extractTextReasoningToolUses(
|
|
11872
11881
|
msg.content
|
|
11873
11882
|
);
|
|
11874
11883
|
const msgId = msg.id;
|
|
@@ -11892,12 +11901,12 @@ function convertClaudeToTrajectory(jsonlContent, sessionId) {
|
|
|
11892
11901
|
extra.user_type = event.userType;
|
|
11893
11902
|
extra.is_sidechain = event.isSidechain ?? false;
|
|
11894
11903
|
const modelName = msg.model || defaultModelName;
|
|
11895
|
-
if (
|
|
11904
|
+
if (text2 || reasoning || toolBlocks.length === 0) {
|
|
11896
11905
|
normalizedEvents.push({
|
|
11897
11906
|
kind: "message",
|
|
11898
11907
|
timestamp,
|
|
11899
11908
|
role: msg.role ?? "assistant",
|
|
11900
|
-
text:
|
|
11909
|
+
text: text2 || "",
|
|
11901
11910
|
reasoning: msg.role === "assistant" ? reasoning : void 0,
|
|
11902
11911
|
metrics,
|
|
11903
11912
|
extra: Object.keys(extra).length > 0 ? extra : void 0,
|
|
@@ -11937,13 +11946,13 @@ function convertClaudeToTrajectory(jsonlContent, sessionId) {
|
|
|
11937
11946
|
if (eventType === "user") {
|
|
11938
11947
|
const content = msg.content;
|
|
11939
11948
|
if (typeof content === "string") {
|
|
11940
|
-
const
|
|
11941
|
-
if (
|
|
11949
|
+
const text2 = content.trim();
|
|
11950
|
+
if (text2) {
|
|
11942
11951
|
normalizedEvents.push({
|
|
11943
11952
|
kind: "message",
|
|
11944
11953
|
timestamp,
|
|
11945
11954
|
role: "user",
|
|
11946
|
-
text:
|
|
11955
|
+
text: text2,
|
|
11947
11956
|
extra: { is_sidechain: event.isSidechain ?? false }
|
|
11948
11957
|
});
|
|
11949
11958
|
}
|
|
@@ -12005,13 +12014,13 @@ function convertClaudeToTrajectory(jsonlContent, sessionId) {
|
|
|
12005
12014
|
continue;
|
|
12006
12015
|
}
|
|
12007
12016
|
if (content !== void 0 && content !== null && content !== "") {
|
|
12008
|
-
const
|
|
12009
|
-
if (
|
|
12017
|
+
const text2 = stringify(content).trim();
|
|
12018
|
+
if (text2) {
|
|
12010
12019
|
normalizedEvents.push({
|
|
12011
12020
|
kind: "message",
|
|
12012
12021
|
timestamp,
|
|
12013
12022
|
role: "user",
|
|
12014
|
-
text:
|
|
12023
|
+
text: text2
|
|
12015
12024
|
});
|
|
12016
12025
|
}
|
|
12017
12026
|
}
|
|
@@ -12147,8 +12156,8 @@ function extractMessageText(content) {
|
|
|
12147
12156
|
const parts = [];
|
|
12148
12157
|
for (const block of content) {
|
|
12149
12158
|
if (typeof block === "object" && block !== null) {
|
|
12150
|
-
const
|
|
12151
|
-
if (typeof
|
|
12159
|
+
const text2 = block.text;
|
|
12160
|
+
if (typeof text2 === "string") parts.push(text2);
|
|
12152
12161
|
}
|
|
12153
12162
|
}
|
|
12154
12163
|
return parts.join("");
|
|
@@ -12280,12 +12289,12 @@ function convertCodexToTrajectory(jsonlContent, sessionId) {
|
|
|
12280
12289
|
}
|
|
12281
12290
|
if (payloadType === "message") {
|
|
12282
12291
|
const content = payload.content;
|
|
12283
|
-
const
|
|
12292
|
+
const text2 = Array.isArray(content) ? extractMessageText(content) : "";
|
|
12284
12293
|
normalizedEvents.push({
|
|
12285
12294
|
kind: "message",
|
|
12286
12295
|
timestamp,
|
|
12287
12296
|
role: payload.role ?? "user",
|
|
12288
|
-
text:
|
|
12297
|
+
text: text2,
|
|
12289
12298
|
reasoning: payload.role === "assistant" ? pendingReasoning : void 0
|
|
12290
12299
|
});
|
|
12291
12300
|
pendingReasoning = void 0;
|
|
@@ -12791,7 +12800,7 @@ function convertCursorToTrajectory(jsonlContent, sessionId) {
|
|
|
12791
12800
|
else if (role === "user") source = "user";
|
|
12792
12801
|
else source = "system";
|
|
12793
12802
|
const contentParts = entry.message?.content;
|
|
12794
|
-
let
|
|
12803
|
+
let text2 = "";
|
|
12795
12804
|
if (Array.isArray(contentParts)) {
|
|
12796
12805
|
const textParts = [];
|
|
12797
12806
|
for (const part of contentParts) {
|
|
@@ -12799,13 +12808,13 @@ function convertCursorToTrajectory(jsonlContent, sessionId) {
|
|
|
12799
12808
|
textParts.push(part.text);
|
|
12800
12809
|
}
|
|
12801
12810
|
}
|
|
12802
|
-
|
|
12811
|
+
text2 = textParts.join("\n\n").trim();
|
|
12803
12812
|
}
|
|
12804
|
-
if (!
|
|
12813
|
+
if (!text2) continue;
|
|
12805
12814
|
const step = {
|
|
12806
12815
|
step_id: stepId,
|
|
12807
12816
|
source,
|
|
12808
|
-
message:
|
|
12817
|
+
message: text2
|
|
12809
12818
|
};
|
|
12810
12819
|
steps.push(step);
|
|
12811
12820
|
stepId++;
|
|
@@ -13351,10 +13360,10 @@ var NormalizeMiddleware = class {
|
|
|
13351
13360
|
file.sourceName
|
|
13352
13361
|
))
|
|
13353
13362
|
continue;
|
|
13354
|
-
const content = file.content ? file.content.toString("utf-8") : null;
|
|
13355
|
-
if (!content) continue;
|
|
13356
13363
|
const sessionId = file.metadata?.sessionId ?? (file.sourceName === "codex" ? void 0 : path13.basename(file.absolutePath, ".jsonl"));
|
|
13357
13364
|
try {
|
|
13365
|
+
const content = file.content ? file.content.toString("utf-8") : null;
|
|
13366
|
+
if (!content) continue;
|
|
13358
13367
|
const trajectory = normalizeContent(
|
|
13359
13368
|
file.sourceName,
|
|
13360
13369
|
content,
|
|
@@ -13383,7 +13392,7 @@ var NormalizeMiddleware = class {
|
|
|
13383
13392
|
} catch (err) {
|
|
13384
13393
|
appendLog(
|
|
13385
13394
|
"warn",
|
|
13386
|
-
`normalize:
|
|
13395
|
+
`normalize: skipped ATIF for source=${file.sourceName} session=${sessionId ?? "<unknown>"} file=${file.absolutePath}: ${err instanceof Error ? err.message : String(err)}`
|
|
13387
13396
|
);
|
|
13388
13397
|
}
|
|
13389
13398
|
}
|
|
@@ -14085,7 +14094,7 @@ import { execFileSync as execFileSync3 } from "child_process";
|
|
|
14085
14094
|
import path18 from "path";
|
|
14086
14095
|
|
|
14087
14096
|
// src/git-traces/git-ops.ts
|
|
14088
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
14097
|
+
import { execFileSync as execFileSync2, spawnSync } from "child_process";
|
|
14089
14098
|
import fs12 from "fs";
|
|
14090
14099
|
import os7 from "os";
|
|
14091
14100
|
import path16 from "path";
|
|
@@ -14097,11 +14106,155 @@ var EXEC_OPTS = {
|
|
|
14097
14106
|
};
|
|
14098
14107
|
var MAX_ERROR_OUTPUT_CHARS = 2e3;
|
|
14099
14108
|
var MAX_SNAPSHOT_FILE_BYTES = 10 * 1024 * 1024;
|
|
14100
|
-
var
|
|
14109
|
+
var MAX_BINARY_SNAPSHOT_FILE_BYTES = 1024 * 1024;
|
|
14110
|
+
var BINARY_SNIFF_BYTES = 8e3;
|
|
14111
|
+
var EXCLUDED_SNAPSHOT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
14112
|
+
// Documents
|
|
14113
|
+
".pdf",
|
|
14114
|
+
// Raster images
|
|
14115
|
+
".png",
|
|
14116
|
+
".jpg",
|
|
14117
|
+
".jpeg",
|
|
14118
|
+
".gif",
|
|
14119
|
+
".bmp",
|
|
14120
|
+
".tiff",
|
|
14121
|
+
".tif",
|
|
14122
|
+
".webp",
|
|
14123
|
+
".ico",
|
|
14124
|
+
".heic",
|
|
14125
|
+
".heif",
|
|
14126
|
+
".avif",
|
|
14127
|
+
// Video
|
|
14128
|
+
".mp4",
|
|
14129
|
+
".mov",
|
|
14130
|
+
".avi",
|
|
14131
|
+
".mkv",
|
|
14132
|
+
".webm",
|
|
14133
|
+
".m4v",
|
|
14134
|
+
".mpg",
|
|
14135
|
+
".mpeg",
|
|
14136
|
+
".wmv",
|
|
14137
|
+
".flv",
|
|
14138
|
+
// Audio
|
|
14139
|
+
".mp3",
|
|
14140
|
+
".wav",
|
|
14141
|
+
".flac",
|
|
14142
|
+
".ogg",
|
|
14143
|
+
".m4a",
|
|
14144
|
+
".aac",
|
|
14145
|
+
".aiff",
|
|
14146
|
+
// Archives / compressed
|
|
14147
|
+
".zip",
|
|
14148
|
+
".tar",
|
|
14149
|
+
".gz",
|
|
14150
|
+
".tgz",
|
|
14151
|
+
".bz2",
|
|
14152
|
+
".tbz2",
|
|
14153
|
+
".xz",
|
|
14154
|
+
".7z",
|
|
14155
|
+
".rar",
|
|
14156
|
+
".zst",
|
|
14157
|
+
".lz4",
|
|
14158
|
+
// Fonts
|
|
14159
|
+
".woff",
|
|
14160
|
+
".woff2",
|
|
14161
|
+
".ttf",
|
|
14162
|
+
".otf",
|
|
14163
|
+
".eot",
|
|
14164
|
+
// Compiled / native artifacts
|
|
14165
|
+
".so",
|
|
14166
|
+
".dylib",
|
|
14167
|
+
".dll",
|
|
14168
|
+
".a",
|
|
14169
|
+
".o",
|
|
14170
|
+
".lib",
|
|
14171
|
+
".class",
|
|
14172
|
+
".jar",
|
|
14173
|
+
".wasm",
|
|
14174
|
+
".exe",
|
|
14175
|
+
".pyc",
|
|
14176
|
+
".pyd",
|
|
14177
|
+
".node",
|
|
14178
|
+
// Databases
|
|
14179
|
+
".sqlite",
|
|
14180
|
+
".sqlite3",
|
|
14181
|
+
".db",
|
|
14182
|
+
".mdb",
|
|
14183
|
+
// Office / design binaries
|
|
14184
|
+
".doc",
|
|
14185
|
+
".docx",
|
|
14186
|
+
".xls",
|
|
14187
|
+
".xlsx",
|
|
14188
|
+
".ppt",
|
|
14189
|
+
".pptx",
|
|
14190
|
+
".odt",
|
|
14191
|
+
".ods",
|
|
14192
|
+
".odp",
|
|
14193
|
+
".psd",
|
|
14194
|
+
".ai",
|
|
14195
|
+
".sketch",
|
|
14196
|
+
".fig",
|
|
14197
|
+
".blend"
|
|
14198
|
+
]);
|
|
14199
|
+
var EXCLUDED_SNAPSHOT_BASENAMES = /* @__PURE__ */ new Set([
|
|
14200
|
+
".DS_Store",
|
|
14201
|
+
"Thumbs.db",
|
|
14202
|
+
"desktop.ini"
|
|
14203
|
+
]);
|
|
14101
14204
|
var EMPTY_TREE_SHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904";
|
|
14102
|
-
function
|
|
14205
|
+
function isExcludedSnapshotPath(filePath) {
|
|
14206
|
+
if (EXCLUDED_SNAPSHOT_BASENAMES.has(path16.basename(filePath))) return true;
|
|
14103
14207
|
return EXCLUDED_SNAPSHOT_EXTENSIONS.has(path16.extname(filePath).toLowerCase());
|
|
14104
14208
|
}
|
|
14209
|
+
function isBinaryBuffer(buffer) {
|
|
14210
|
+
return buffer.includes(0);
|
|
14211
|
+
}
|
|
14212
|
+
function readTreeBlobHead(repoRoot, sha) {
|
|
14213
|
+
try {
|
|
14214
|
+
const { stdout } = spawnSync("git", ["cat-file", "blob", sha], {
|
|
14215
|
+
cwd: repoRoot,
|
|
14216
|
+
timeout: GIT_COMMAND_TIMEOUT_MS,
|
|
14217
|
+
maxBuffer: BINARY_SNIFF_BYTES
|
|
14218
|
+
});
|
|
14219
|
+
return stdout?.length ? stdout.subarray(0, BINARY_SNIFF_BYTES) : null;
|
|
14220
|
+
} catch {
|
|
14221
|
+
return null;
|
|
14222
|
+
}
|
|
14223
|
+
}
|
|
14224
|
+
function readWorkingFileHead(absPath) {
|
|
14225
|
+
let fd = null;
|
|
14226
|
+
try {
|
|
14227
|
+
fd = fs12.openSync(absPath, "r");
|
|
14228
|
+
const buffer = Buffer.alloc(BINARY_SNIFF_BYTES);
|
|
14229
|
+
const bytesRead = fs12.readSync(fd, buffer, 0, BINARY_SNIFF_BYTES, 0);
|
|
14230
|
+
return buffer.subarray(0, bytesRead);
|
|
14231
|
+
} catch {
|
|
14232
|
+
return null;
|
|
14233
|
+
} finally {
|
|
14234
|
+
if (fd !== null) {
|
|
14235
|
+
try {
|
|
14236
|
+
fs12.closeSync(fd);
|
|
14237
|
+
} catch {
|
|
14238
|
+
}
|
|
14239
|
+
}
|
|
14240
|
+
}
|
|
14241
|
+
}
|
|
14242
|
+
function classifyOmission(filePath, sizeBytes, readHead) {
|
|
14243
|
+
if (isExcludedSnapshotPath(filePath)) return "excluded-extension";
|
|
14244
|
+
if (sizeBytes > MAX_SNAPSHOT_FILE_BYTES) return "file-over-limit";
|
|
14245
|
+
if (sizeBytes > MAX_BINARY_SNAPSHOT_FILE_BYTES) {
|
|
14246
|
+
const head = readHead();
|
|
14247
|
+
if (!head) {
|
|
14248
|
+
appendLog(
|
|
14249
|
+
"warn",
|
|
14250
|
+
`git-traces: binary sniff failed for ${filePath} (${sizeBytes} bytes); keeping it in the snapshot`
|
|
14251
|
+
);
|
|
14252
|
+
return null;
|
|
14253
|
+
}
|
|
14254
|
+
if (isBinaryBuffer(head)) return "binary-over-limit";
|
|
14255
|
+
}
|
|
14256
|
+
return null;
|
|
14257
|
+
}
|
|
14105
14258
|
function quoteGitArg(arg) {
|
|
14106
14259
|
if (/^[A-Za-z0-9_./:=@%+,-]+$/.test(arg)) return arg;
|
|
14107
14260
|
return JSON.stringify(arg);
|
|
@@ -14205,7 +14358,7 @@ function recordOmittedSnapshotFile(omittedFiles, file, options = {}) {
|
|
|
14205
14358
|
omittedFiles?.push(file);
|
|
14206
14359
|
if (options.log === false) return;
|
|
14207
14360
|
const action = file.tracked ? "omitting tracked" : "skipping untracked";
|
|
14208
|
-
const reason = file.reason === "file-over-limit" ? `${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit` : "excluded extension";
|
|
14361
|
+
const reason = file.reason === "file-over-limit" ? `${file.sizeBytes} bytes > ${MAX_SNAPSHOT_FILE_BYTES} limit` : file.reason === "binary-over-limit" ? `binary ${file.sizeBytes} bytes > ${MAX_BINARY_SNAPSHOT_FILE_BYTES} binary limit` : "excluded extension";
|
|
14209
14362
|
appendLog("warn", `git-traces: ${action} ${file.path} (${reason})`);
|
|
14210
14363
|
}
|
|
14211
14364
|
function parseLsTreeLongZ(output) {
|
|
@@ -14228,7 +14381,11 @@ function listOmittedTreeFiles(repoRoot, treeSha, options = {}) {
|
|
|
14228
14381
|
const output = gitBuffer(repoRoot, ["ls-tree", "-r", "-l", "-z", treeSha]);
|
|
14229
14382
|
const omitted = [];
|
|
14230
14383
|
for (const entry of parseLsTreeLongZ(output)) {
|
|
14231
|
-
const reason =
|
|
14384
|
+
const reason = classifyOmission(
|
|
14385
|
+
entry.path,
|
|
14386
|
+
entry.sizeBytes,
|
|
14387
|
+
() => readTreeBlobHead(repoRoot, entry.sha)
|
|
14388
|
+
);
|
|
14232
14389
|
if (!reason) continue;
|
|
14233
14390
|
const file = {
|
|
14234
14391
|
path: entry.path,
|
|
@@ -14286,8 +14443,13 @@ function buildUntrackedTree(repoRoot, omittedFiles) {
|
|
|
14286
14443
|
for (const relPath of list.split("\0")) {
|
|
14287
14444
|
if (!relPath) continue;
|
|
14288
14445
|
try {
|
|
14289
|
-
const
|
|
14290
|
-
const
|
|
14446
|
+
const absPath = path16.join(repoRoot, relPath);
|
|
14447
|
+
const stat = fs12.lstatSync(absPath);
|
|
14448
|
+
const reason = classifyOmission(
|
|
14449
|
+
relPath,
|
|
14450
|
+
stat.size,
|
|
14451
|
+
() => readWorkingFileHead(absPath)
|
|
14452
|
+
);
|
|
14291
14453
|
if (reason) {
|
|
14292
14454
|
recordOmittedSnapshotFile(omittedFiles, {
|
|
14293
14455
|
path: relPath,
|