claude-nomad 0.63.0 → 0.63.1
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/CHANGELOG.md +7 -0
- package/dist/nomad.mjs +115 -47
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.63.1](https://github.com/funkadelic/claude-nomad/compare/v0.63.0...v0.63.1) (2026-07-31)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Fixed
|
|
7
|
+
|
|
8
|
+
* **push:** correct gitleaks column offsets in recovery prompts ([#478](https://github.com/funkadelic/claude-nomad/issues/478)) ([e230993](https://github.com/funkadelic/claude-nomad/commit/e2309932456f5646694685dd0c3b6b6e1007059f))
|
|
9
|
+
|
|
3
10
|
## [0.63.0](https://github.com/funkadelic/claude-nomad/compare/v0.62.6...v0.63.0) (2026-07-31)
|
|
4
11
|
|
|
5
12
|
### What's new
|
package/dist/nomad.mjs
CHANGED
|
@@ -5658,6 +5658,93 @@ function dropSessionFromStaged(sid, map) {
|
|
|
5658
5658
|
init_push_gitleaks_scan();
|
|
5659
5659
|
init_utils();
|
|
5660
5660
|
|
|
5661
|
+
// src/commands.push.recovery.span-align.ts
|
|
5662
|
+
var REDACTED_TOKEN = "REDACTED";
|
|
5663
|
+
var SHIFT_WINDOW = [0, -1, 1, -2, 2];
|
|
5664
|
+
var ENTROPY_TOLERANCE = 1e-4;
|
|
5665
|
+
function byteAt(buf, at) {
|
|
5666
|
+
return at >= 0 && at < buf.length ? buf[at] : void 0;
|
|
5667
|
+
}
|
|
5668
|
+
function isWordByte(byte) {
|
|
5669
|
+
if (byte === void 0) return false;
|
|
5670
|
+
return byte >= 48 && byte <= 57 || byte >= 65 && byte <= 90 || byte >= 97 && byte <= 122 || byte === 95;
|
|
5671
|
+
}
|
|
5672
|
+
function splitsToken(buf, at) {
|
|
5673
|
+
return isWordByte(byteAt(buf, at - 1)) && isWordByte(byteAt(buf, at));
|
|
5674
|
+
}
|
|
5675
|
+
function shannonEntropy(text) {
|
|
5676
|
+
if (text.length === 0) return 0;
|
|
5677
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5678
|
+
for (const ch of text) counts.set(ch, (counts.get(ch) ?? 0) + 1);
|
|
5679
|
+
let bits = 0;
|
|
5680
|
+
for (const n of counts.values()) {
|
|
5681
|
+
const p = n / text.length;
|
|
5682
|
+
bits -= p * Math.log2(p);
|
|
5683
|
+
}
|
|
5684
|
+
return bits;
|
|
5685
|
+
}
|
|
5686
|
+
function matchTemplate(finding) {
|
|
5687
|
+
if (finding.Match.length === 0) return null;
|
|
5688
|
+
const idx = finding.Match.indexOf(REDACTED_TOKEN);
|
|
5689
|
+
if (idx < 0) return { pre: "", post: "", redacted: false };
|
|
5690
|
+
return {
|
|
5691
|
+
pre: finding.Match.slice(0, idx),
|
|
5692
|
+
post: finding.Match.slice(idx + REDACTED_TOKEN.length),
|
|
5693
|
+
redacted: true
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5696
|
+
function fitsTemplate(span, finding, template) {
|
|
5697
|
+
if (!template.redacted) return span === finding.Match;
|
|
5698
|
+
return span.startsWith(template.pre) && span.endsWith(template.post);
|
|
5699
|
+
}
|
|
5700
|
+
function candidateAt(finding, buf, template, shift) {
|
|
5701
|
+
const start = finding.StartColumn - 1 + shift;
|
|
5702
|
+
const end = start + (finding.EndColumn - finding.StartColumn + 1);
|
|
5703
|
+
if (start < 0 || end > buf.length) return null;
|
|
5704
|
+
const span = buf.subarray(start, end).toString("utf8");
|
|
5705
|
+
if (!fitsTemplate(span, finding, template)) return null;
|
|
5706
|
+
const valueStart = start + Buffer.byteLength(template.pre, "utf8");
|
|
5707
|
+
const valueEnd = end - Buffer.byteLength(template.post, "utf8");
|
|
5708
|
+
const value = buf.subarray(valueStart, valueEnd).toString("utf8");
|
|
5709
|
+
if (value.length === 0) return null;
|
|
5710
|
+
if (splitsToken(buf, start) || splitsToken(buf, end)) return null;
|
|
5711
|
+
return { start, end, valueStart, value, exact: template.redacted };
|
|
5712
|
+
}
|
|
5713
|
+
function locateSecretLiteral(finding, buf) {
|
|
5714
|
+
const secret = finding.Secret;
|
|
5715
|
+
if (typeof secret !== "string" || secret.length === 0 || secret === REDACTED_TOKEN) return null;
|
|
5716
|
+
const needle = Buffer.from(secret, "utf8");
|
|
5717
|
+
const hint = finding.StartColumn - 1;
|
|
5718
|
+
let best = null;
|
|
5719
|
+
for (let at = buf.indexOf(needle); at >= 0; at = buf.indexOf(needle, at + 1)) {
|
|
5720
|
+
if (best === null || Math.abs(at - hint) < Math.abs(best - hint)) best = at;
|
|
5721
|
+
}
|
|
5722
|
+
if (best === null) return null;
|
|
5723
|
+
return { start: best, end: best + needle.length, valueStart: best, value: secret, exact: true };
|
|
5724
|
+
}
|
|
5725
|
+
function breakTie(hits, finding) {
|
|
5726
|
+
const reported = finding.Entropy;
|
|
5727
|
+
if (typeof reported !== "number" || !Number.isFinite(reported) || reported <= 0) return null;
|
|
5728
|
+
const agreeing = hits.filter(
|
|
5729
|
+
(h2) => h2.exact && Math.abs(shannonEntropy(h2.value) - reported) <= ENTROPY_TOLERANCE
|
|
5730
|
+
);
|
|
5731
|
+
return agreeing.length === 1 ? agreeing[0] : null;
|
|
5732
|
+
}
|
|
5733
|
+
function alignFindingSpan(finding, buf) {
|
|
5734
|
+
const literal = locateSecretLiteral(finding, buf);
|
|
5735
|
+
if (literal !== null) return literal;
|
|
5736
|
+
if (typeof finding.EndLine === "number" && finding.EndLine > finding.StartLine) return null;
|
|
5737
|
+
const template = matchTemplate(finding);
|
|
5738
|
+
if (template === null) return null;
|
|
5739
|
+
const hits = [];
|
|
5740
|
+
for (const shift of SHIFT_WINDOW) {
|
|
5741
|
+
const candidate = candidateAt(finding, buf, template, shift);
|
|
5742
|
+
if (candidate !== null) hits.push(candidate);
|
|
5743
|
+
}
|
|
5744
|
+
if (hits.length === 1) return hits[0];
|
|
5745
|
+
return breakTie(hits, finding);
|
|
5746
|
+
}
|
|
5747
|
+
|
|
5661
5748
|
// src/commands.push.recovery.secret-shape.ts
|
|
5662
5749
|
var VALUE_HEAD = 4;
|
|
5663
5750
|
var VALUE_TAIL = 4;
|
|
@@ -5665,7 +5752,7 @@ var MIN_ELIDE_LEN = 16;
|
|
|
5665
5752
|
var FRAGMENT_SEP = "...";
|
|
5666
5753
|
var NEAR_WINDOW = 40;
|
|
5667
5754
|
var VALUE_PAD = 16;
|
|
5668
|
-
var
|
|
5755
|
+
var SIBLING_PAD = 2;
|
|
5669
5756
|
var CONTROL_CHARS = /[\x00-\x1f\x7f]/g;
|
|
5670
5757
|
function classifyCharset(value) {
|
|
5671
5758
|
if (/^[0-9a-fA-F]+$/.test(value)) return "hex";
|
|
@@ -5691,26 +5778,23 @@ function withoutValue(near, value) {
|
|
|
5691
5778
|
const idx = near.indexOf(value);
|
|
5692
5779
|
return idx < 0 ? near : emptyToNull(near.slice(0, idx));
|
|
5693
5780
|
}
|
|
5694
|
-
function
|
|
5781
|
+
function templateLead(finding) {
|
|
5695
5782
|
const idx = finding.Match.indexOf(REDACTED_TOKEN);
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
}
|
|
5706
|
-
return { value: null, near: emptyToNull(lead + pre), exact: false };
|
|
5783
|
+
return idx > 0 ? finding.Match.slice(0, idx) : null;
|
|
5784
|
+
}
|
|
5785
|
+
function siblingBounds(sibling, buf) {
|
|
5786
|
+
const aligned = alignFindingSpan(sibling, buf);
|
|
5787
|
+
if (aligned !== null) return { start: aligned.start, end: aligned.end };
|
|
5788
|
+
const reportedStart = sibling.StartColumn - 1;
|
|
5789
|
+
if (sibling.EndColumn <= reportedStart) return { start: 0, end: 0 };
|
|
5790
|
+
const start = Math.max(0, Math.min(reportedStart, buf.length) - SIBLING_PAD);
|
|
5791
|
+
return { start, end: Math.max(start, Math.min(sibling.EndColumn + SIBLING_PAD, buf.length)) };
|
|
5707
5792
|
}
|
|
5708
5793
|
function maskSiblingSpans(buf, siblings, ownStart, ownEnd) {
|
|
5709
5794
|
if (siblings.length === 0) return buf;
|
|
5710
5795
|
const copy = Buffer.from(buf);
|
|
5711
5796
|
for (const sibling of siblings) {
|
|
5712
|
-
const start =
|
|
5713
|
-
const end = Math.max(start, Math.min(sibling.EndColumn, copy.length));
|
|
5797
|
+
const { start, end } = siblingBounds(sibling, buf);
|
|
5714
5798
|
const headEnd = Math.min(end, ownStart);
|
|
5715
5799
|
if (start < headEnd) copy.fill(0, start, headEnd);
|
|
5716
5800
|
const tailStart = Math.max(start, ownEnd);
|
|
@@ -5720,43 +5804,27 @@ function maskSiblingSpans(buf, siblings, ownStart, ownEnd) {
|
|
|
5720
5804
|
}
|
|
5721
5805
|
function resolveSecretContext(finding, readLine, siblings = []) {
|
|
5722
5806
|
const resolved = resolveSpanContext(finding, readLine, siblings);
|
|
5723
|
-
const ownStripped = withoutValue(resolved.near, resolved.value);
|
|
5724
|
-
const multiLine = typeof finding.EndLine === "number" && finding.EndLine > finding.StartLine;
|
|
5725
5807
|
return {
|
|
5726
5808
|
value: resolved.value,
|
|
5727
|
-
near:
|
|
5728
|
-
exact: resolved.exact
|
|
5809
|
+
near: withoutValue(resolved.near, resolved.value),
|
|
5810
|
+
exact: resolved.exact
|
|
5729
5811
|
};
|
|
5730
5812
|
}
|
|
5813
|
+
function resolveWithoutLine(finding) {
|
|
5814
|
+
if (finding.Match.includes(REDACTED_TOKEN)) {
|
|
5815
|
+
return { value: null, near: templateLead(finding), exact: false };
|
|
5816
|
+
}
|
|
5817
|
+
return { value: emptyToNull(finding.Match), near: null, exact: false };
|
|
5818
|
+
}
|
|
5731
5819
|
function resolveSpanContext(finding, readLine, siblings = []) {
|
|
5732
5820
|
const raw = readLine(finding.File, finding.StartLine);
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
span = buf.subarray(startCol - 1, endCol).toString("utf8");
|
|
5741
|
-
lead = maskSiblingSpans(buf, siblings, startCol - 1, endCol).subarray(0, startCol - 1).toString("utf8");
|
|
5742
|
-
} else {
|
|
5743
|
-
span = finding.Match;
|
|
5744
|
-
lead = "";
|
|
5745
|
-
}
|
|
5746
|
-
const aligned = alignRedactedMatch(finding, span, lead);
|
|
5747
|
-
if (aligned !== null) return aligned;
|
|
5748
|
-
if (typeof finding.Secret === "string" && finding.Secret.length > 0 && span.includes(finding.Secret)) {
|
|
5749
|
-
return {
|
|
5750
|
-
value: finding.Secret,
|
|
5751
|
-
near: emptyToNull(lead + span.slice(0, span.indexOf(finding.Secret))),
|
|
5752
|
-
exact: true
|
|
5753
|
-
};
|
|
5754
|
-
}
|
|
5755
|
-
return {
|
|
5756
|
-
value: span.length > 0 ? span : null,
|
|
5757
|
-
near: emptyToNull(lead),
|
|
5758
|
-
exact: false
|
|
5759
|
-
};
|
|
5821
|
+
if (raw === null) return resolveWithoutLine(finding);
|
|
5822
|
+
const unresolved = { value: null, near: templateLead(finding), exact: false };
|
|
5823
|
+
const buf = Buffer.from(raw, "utf8");
|
|
5824
|
+
const aligned = alignFindingSpan(finding, buf);
|
|
5825
|
+
if (aligned === null) return unresolved;
|
|
5826
|
+
const near = maskSiblingSpans(buf, siblings, aligned.start, aligned.end).subarray(0, aligned.valueStart).toString("utf8");
|
|
5827
|
+
return { value: aligned.value, near: emptyToNull(near), exact: aligned.exact };
|
|
5760
5828
|
}
|
|
5761
5829
|
function formatNear(text) {
|
|
5762
5830
|
const stripped = elideTokenRuns(text.replace(CONTROL_CHARS, ""));
|
|
@@ -9439,7 +9507,7 @@ function parseSyncArgs(argv) {
|
|
|
9439
9507
|
// package.json
|
|
9440
9508
|
var package_default = {
|
|
9441
9509
|
name: "claude-nomad",
|
|
9442
|
-
version: "0.63.
|
|
9510
|
+
version: "0.63.1",
|
|
9443
9511
|
type: "module",
|
|
9444
9512
|
description: "Sync Claude Code config (~/.claude/) across machines via a private Git repo, with path remapping and per-host settings overrides.",
|
|
9445
9513
|
keywords: [
|
package/package.json
CHANGED