@mu-crew/mu 3.12.0 → 3.13.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/chunk-3BARPJLY.js +44 -0
- package/dist/{chunk-R2SVBZAB.js → chunk-45KQLMEL.js} +193 -93
- package/dist/chunk-45KQLMEL.js.map +1 -0
- package/dist/{chunk-TEGZOXML.js → chunk-54WJCMJR.js} +12 -10
- package/dist/chunk-54WJCMJR.js.map +1 -0
- package/dist/{chunk-XV7J56GD.js → chunk-EPCEEDHG.js} +4 -3
- package/dist/{chunk-XV7J56GD.js.map → chunk-EPCEEDHG.js.map} +1 -1
- package/dist/{chunk-77GBZ2KR.js → chunk-QITLNZEM.js} +3 -1
- package/dist/{chunk-77GBZ2KR.js.map → chunk-QITLNZEM.js.map} +1 -1
- package/dist/{chunk-IRPTNVJC.js → chunk-TXV34VWT.js} +8614 -6029
- package/dist/chunk-TXV34VWT.js.map +1 -0
- package/dist/{chunk-L2ABBTYG.js → chunk-WI6AWCMK.js} +3 -1
- package/dist/{chunk-L2ABBTYG.js.map → chunk-WI6AWCMK.js.map} +1 -1
- package/dist/{cli-MDCJEU74.js → cli-GMJUOXPW.js} +3581 -107
- package/dist/cli-GMJUOXPW.js.map +1 -0
- package/dist/cli.js +4 -3
- package/dist/cli.js.map +1 -1
- package/dist/devtools-LS4ZEPWM.js +3772 -0
- package/dist/devtools-LS4ZEPWM.js.map +1 -0
- package/dist/extension/mu-pi.js +206 -61
- package/dist/extension/mu-pi.js.map +1 -1
- package/dist/index.d.ts +309 -228
- package/dist/index.js +12 -5
- package/dist/tui-WSDDTA4K.js +40956 -0
- package/dist/tui-WSDDTA4K.js.map +1 -0
- package/dist/tui-sync-worker.js +4 -3
- package/dist/tui-sync-worker.js.map +1 -1
- package/dist/yank-XDOONQSL.js +13 -0
- package/docs/ARCHITECTURE.md +4 -1
- package/docs/ROADMAP.md +43 -38
- package/docs/VISION.md +8 -0
- package/docs/VOCABULARY.md +3 -1
- package/docs/architecture/dag.md +4 -0
- package/docs/architecture/sync.md +8 -0
- package/docs/architecture/tui.md +11 -2
- package/docs/guide/delegate.md +6 -4
- package/docs/guide/recovery.md +4 -0
- package/docs/reference/env.md +2 -2
- package/package.json +2 -1
- package/skills/mu/SKILL.md +12 -2
- package/skills/mu/recipes/adversarial-review.md +3 -0
- package/skills/mu/recipes/backlog-triage.md +2 -0
- package/skills/mu/recipes/findings.md +7 -0
- package/skills/mu/recipes/long-run.md +3 -1
- package/skills/mu/recipes/loop-until-done.md +3 -1
- package/skills/mu/recipes/orchestrator-loop.md +4 -0
- package/skills/mu/recipes/ultrathink.md +3 -1
- package/skills/mu/recipes/watcher.md +6 -1
- package/dist/chunk-IRPTNVJC.js.map +0 -1
- package/dist/chunk-PZ5AY32C.js +0 -10
- package/dist/chunk-R2SVBZAB.js.map +0 -1
- package/dist/chunk-TEGZOXML.js.map +0 -1
- package/dist/cli-MDCJEU74.js.map +0 -1
- package/dist/tui-LO75XA3O.js +0 -6452
- package/dist/tui-LO75XA3O.js.map +0 -1
- package/dist/yank-T3AYQTVR.js +0 -12
- /package/dist/{chunk-PZ5AY32C.js.map → chunk-3BARPJLY.js.map} +0 -0
- /package/dist/{yank-T3AYQTVR.js.map → yank-XDOONQSL.js.map} +0 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { createRequire as __muCreateRequire } from "node:module"; const require = __muCreateRequire(import.meta.url);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
9
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
10
|
+
}) : x)(function(x) {
|
|
11
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
12
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
13
|
+
});
|
|
14
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
15
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
16
|
+
};
|
|
17
|
+
var __export = (target, all) => {
|
|
18
|
+
for (var name in all)
|
|
19
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (let key of __getOwnPropNames(from))
|
|
24
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
25
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
26
|
+
}
|
|
27
|
+
return to;
|
|
28
|
+
};
|
|
29
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
30
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
31
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
32
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
33
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
34
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
35
|
+
mod
|
|
36
|
+
));
|
|
37
|
+
|
|
38
|
+
export {
|
|
39
|
+
__require,
|
|
40
|
+
__commonJS,
|
|
41
|
+
__export,
|
|
42
|
+
__toESM
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=chunk-3BARPJLY.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { createRequire as __muCreateRequire } from "node:module"; const require = __muCreateRequire(import.meta.url);
|
|
1
2
|
import {
|
|
2
3
|
defaultStateDir,
|
|
3
4
|
xdgStateHome
|
|
4
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-WI6AWCMK.js";
|
|
5
6
|
|
|
6
7
|
// src/capture.ts
|
|
7
8
|
var UNRESOLVED = "<unresolved>";
|
|
@@ -768,6 +769,9 @@ CREATE TABLE IF NOT EXISTS sync_peers (
|
|
|
768
769
|
-- when ingest read that segment cleanly to its end (src/segments.ts
|
|
769
770
|
-- segmentCaughtUp). Keyed like sync_peers; a watermark write without a
|
|
770
771
|
-- clean read deletes the row. A cache: losing it costs one full read.
|
|
772
|
+
-- The value may carry tab-separated resume fields after the key
|
|
773
|
+
-- (src/segment-resume.ts): byte offset and sha256 state for ingest
|
|
774
|
+
-- to read only appended bytes.
|
|
771
775
|
-- Additive, so no schema bump: an older mu ignores it, and any
|
|
772
776
|
-- watermark it writes can no longer match a stale fingerprint.
|
|
773
777
|
CREATE TABLE IF NOT EXISTS sync_fingerprints (
|
|
@@ -1788,22 +1792,118 @@ function readManifest(segment) {
|
|
|
1788
1792
|
}
|
|
1789
1793
|
|
|
1790
1794
|
// src/segments.ts
|
|
1791
|
-
import { createHash as
|
|
1795
|
+
import { createHash as createHash3 } from "crypto";
|
|
1792
1796
|
import {
|
|
1793
1797
|
appendFileSync,
|
|
1794
|
-
closeSync,
|
|
1798
|
+
closeSync as closeSync2,
|
|
1795
1799
|
existsSync as existsSync2,
|
|
1796
1800
|
mkdirSync as mkdirSync2,
|
|
1797
|
-
openSync,
|
|
1801
|
+
openSync as openSync2,
|
|
1798
1802
|
readdirSync,
|
|
1799
|
-
readFileSync as
|
|
1800
|
-
readSync,
|
|
1801
|
-
statSync,
|
|
1803
|
+
readFileSync as readFileSync3,
|
|
1804
|
+
readSync as readSync2,
|
|
1805
|
+
statSync as statSync2,
|
|
1802
1806
|
writeFileSync as writeFileSync2
|
|
1803
1807
|
} from "fs";
|
|
1804
1808
|
import { tmpdir } from "os";
|
|
1805
1809
|
import { join as join3, resolve as resolve2, sep } from "path";
|
|
1806
1810
|
import { crc32 } from "zlib";
|
|
1811
|
+
|
|
1812
|
+
// src/compare.ts
|
|
1813
|
+
function compareStrings(a, b) {
|
|
1814
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// src/segment-resume.ts
|
|
1818
|
+
import { createHash as createHash2 } from "crypto";
|
|
1819
|
+
import { closeSync, fstatSync, openSync, readFileSync as readFileSync2, readSync, statSync } from "fs";
|
|
1820
|
+
function usableShaState(state, length) {
|
|
1821
|
+
if (typeof state !== "object" || state === null) return null;
|
|
1822
|
+
const { h, n, tail } = state;
|
|
1823
|
+
if (typeof h !== "string" || !/^[0-9a-f]{64}$/.test(h)) return null;
|
|
1824
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n < 0 || n !== length) return null;
|
|
1825
|
+
if (typeof tail !== "string") return null;
|
|
1826
|
+
const raw = Buffer.from(tail, "base64");
|
|
1827
|
+
if (raw.length !== n % 64 || raw.toString("base64") !== tail) return null;
|
|
1828
|
+
return { h, n, tail };
|
|
1829
|
+
}
|
|
1830
|
+
function encodeFingerprint(key, resume) {
|
|
1831
|
+
if (resume === null) return key;
|
|
1832
|
+
const { lines, size, lastHlc, state } = resume;
|
|
1833
|
+
return [key, lines, size, lastHlc, state.h, state.tail].join(" ");
|
|
1834
|
+
}
|
|
1835
|
+
function decodeFingerprint(raw) {
|
|
1836
|
+
const [key = "", lines, size, lastHlc, h, tail] = raw.split(" ");
|
|
1837
|
+
if (h === void 0 || tail === void 0 || lines === void 0 || size === void 0) {
|
|
1838
|
+
return { key, resume: null };
|
|
1839
|
+
}
|
|
1840
|
+
const n = Number(size);
|
|
1841
|
+
const count = Number(lines);
|
|
1842
|
+
if (!Number.isSafeInteger(n) || n <= 0 || !Number.isSafeInteger(count) || count <= 0) {
|
|
1843
|
+
return { key, resume: null };
|
|
1844
|
+
}
|
|
1845
|
+
if (!lastHlc || !/^[0-9a-f]{64}$/.test(h)) return { key, resume: null };
|
|
1846
|
+
return { key, resume: { lines: count, size: n, lastHlc, state: { h, n, tail } } };
|
|
1847
|
+
}
|
|
1848
|
+
function readAppended(path, resume, manifestSha) {
|
|
1849
|
+
let fd;
|
|
1850
|
+
try {
|
|
1851
|
+
fd = openSync(path, "r");
|
|
1852
|
+
} catch {
|
|
1853
|
+
return null;
|
|
1854
|
+
}
|
|
1855
|
+
try {
|
|
1856
|
+
const size = fstatSync(fd).size;
|
|
1857
|
+
if (size <= resume.size) return null;
|
|
1858
|
+
const bytes = Buffer.alloc(size - resume.size);
|
|
1859
|
+
for (let got = 0; got < bytes.length; ) {
|
|
1860
|
+
const n = readSync(fd, bytes, got, bytes.length - got, resume.size + got);
|
|
1861
|
+
if (n === 0) return null;
|
|
1862
|
+
got += n;
|
|
1863
|
+
}
|
|
1864
|
+
const state = sha256Update(resume.state, bytes);
|
|
1865
|
+
return sha256Digest(state) === manifestSha ? { bytes, state } : null;
|
|
1866
|
+
} finally {
|
|
1867
|
+
closeSync(fd);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
function segmentFingerprint(path) {
|
|
1871
|
+
try {
|
|
1872
|
+
const { size, mtimeMs } = statSync(path);
|
|
1873
|
+
const manifest = readFileSync2(manifestPath(path));
|
|
1874
|
+
const sha = createHash2("sha256").update(manifest).digest("hex");
|
|
1875
|
+
let count = null;
|
|
1876
|
+
let parsed = null;
|
|
1877
|
+
try {
|
|
1878
|
+
parsed = JSON.parse(manifest.toString("utf8"));
|
|
1879
|
+
count = parsed?.count;
|
|
1880
|
+
} catch {
|
|
1881
|
+
}
|
|
1882
|
+
return { key: `${size} ${mtimeMs} ${sha}`, count, manifest: parsed };
|
|
1883
|
+
} catch {
|
|
1884
|
+
return null;
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
function recordedFingerprint(db, watermarkKey) {
|
|
1888
|
+
const row = prepareCached(
|
|
1889
|
+
db,
|
|
1890
|
+
"SELECT fingerprint FROM sync_fingerprints WHERE machine_id = ?"
|
|
1891
|
+
).get(watermarkKey);
|
|
1892
|
+
return row?.fingerprint ?? null;
|
|
1893
|
+
}
|
|
1894
|
+
function segmentCaughtUp(db, watermarkKey, path, lines) {
|
|
1895
|
+
const recorded = recordedFingerprint(db, watermarkKey);
|
|
1896
|
+
if (recorded === null) return false;
|
|
1897
|
+
const now = segmentFingerprint(path);
|
|
1898
|
+
return now !== null && now.count === lines && now.key === decodeFingerprint(recorded).key;
|
|
1899
|
+
}
|
|
1900
|
+
function resumePoint(db, watermarkKey, lines) {
|
|
1901
|
+
const recorded = recordedFingerprint(db, watermarkKey);
|
|
1902
|
+
const resume = recorded === null ? null : decodeFingerprint(recorded).resume;
|
|
1903
|
+
return resume !== null && resume.lines === lines ? resume : null;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
// src/segments.ts
|
|
1807
1907
|
var SEGMENT_FORMAT_VERSION = 1;
|
|
1808
1908
|
var SEGMENT_EXT2 = ".jsonl";
|
|
1809
1909
|
function syncDir() {
|
|
@@ -1983,7 +2083,7 @@ async function flushSegment(db, dir = syncDir(), opts) {
|
|
|
1983
2083
|
};
|
|
1984
2084
|
}
|
|
1985
2085
|
}
|
|
1986
|
-
const lockName =
|
|
2086
|
+
const lockName = createHash3("sha256").update(`${dir}${machineId}`).digest("hex").slice(0, 16);
|
|
1987
2087
|
return withFileLock(
|
|
1988
2088
|
join3(locksDir(), `segment-${lockName}.lock`),
|
|
1989
2089
|
`segment:${machineId}`,
|
|
@@ -2105,7 +2205,7 @@ function trustedTail(path, machineId) {
|
|
|
2105
2205
|
const { count, lastHlc, size, mtimeMs, shaState } = m;
|
|
2106
2206
|
let stat2;
|
|
2107
2207
|
try {
|
|
2108
|
-
stat2 =
|
|
2208
|
+
stat2 = statSync2(path);
|
|
2109
2209
|
} catch {
|
|
2110
2210
|
return null;
|
|
2111
2211
|
}
|
|
@@ -2119,17 +2219,17 @@ function trustedTail(path, machineId) {
|
|
|
2119
2219
|
}
|
|
2120
2220
|
function readLastLine(path, size) {
|
|
2121
2221
|
if (size === 0) return null;
|
|
2122
|
-
const fd =
|
|
2222
|
+
const fd = openSync2(path, "r");
|
|
2123
2223
|
try {
|
|
2124
2224
|
const last = Buffer.alloc(1);
|
|
2125
|
-
|
|
2225
|
+
readSync2(fd, last, 0, 1, size - 1);
|
|
2126
2226
|
if (last[0] !== 10) return null;
|
|
2127
2227
|
const chunks = [];
|
|
2128
2228
|
let end = size - 1;
|
|
2129
2229
|
while (end > 0) {
|
|
2130
2230
|
const start = Math.max(0, end - 64 * 1024);
|
|
2131
2231
|
const buf = Buffer.alloc(end - start);
|
|
2132
|
-
|
|
2232
|
+
readSync2(fd, buf, 0, buf.length, start);
|
|
2133
2233
|
const nl = buf.lastIndexOf(10);
|
|
2134
2234
|
if (nl >= 0) {
|
|
2135
2235
|
chunks.unshift(buf.subarray(nl + 1));
|
|
@@ -2140,43 +2240,15 @@ function readLastLine(path, size) {
|
|
|
2140
2240
|
}
|
|
2141
2241
|
return Buffer.concat(chunks).toString("utf8");
|
|
2142
2242
|
} finally {
|
|
2143
|
-
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
function segmentFingerprint(path) {
|
|
2147
|
-
try {
|
|
2148
|
-
const { size, mtimeMs } = statSync(path);
|
|
2149
|
-
const manifest = readFileSync2(manifestPath(path));
|
|
2150
|
-
const sha = createHash2("sha256").update(manifest).digest("hex");
|
|
2151
|
-
let count = null;
|
|
2152
|
-
try {
|
|
2153
|
-
count = JSON.parse(manifest.toString("utf8")).count;
|
|
2154
|
-
} catch {
|
|
2155
|
-
}
|
|
2156
|
-
return { key: `${size} ${mtimeMs} ${sha}`, count };
|
|
2157
|
-
} catch {
|
|
2158
|
-
return null;
|
|
2243
|
+
closeSync2(fd);
|
|
2159
2244
|
}
|
|
2160
2245
|
}
|
|
2161
|
-
function recordedFingerprint(db, watermarkKey) {
|
|
2162
|
-
const row = prepareCached(
|
|
2163
|
-
db,
|
|
2164
|
-
"SELECT fingerprint FROM sync_fingerprints WHERE machine_id = ?"
|
|
2165
|
-
).get(watermarkKey);
|
|
2166
|
-
return row?.fingerprint ?? null;
|
|
2167
|
-
}
|
|
2168
|
-
function segmentCaughtUp(db, watermarkKey, path, lines) {
|
|
2169
|
-
const recorded = recordedFingerprint(db, watermarkKey);
|
|
2170
|
-
if (recorded === null) return false;
|
|
2171
|
-
const now = segmentFingerprint(path);
|
|
2172
|
-
return now !== null && now.count === lines && now.key === recorded;
|
|
2173
|
-
}
|
|
2174
2246
|
function segmentLineCount(path) {
|
|
2175
2247
|
return readSegmentTail(path).count;
|
|
2176
2248
|
}
|
|
2177
2249
|
function readSegmentTail(path) {
|
|
2178
2250
|
if (!existsSync2(path)) return { count: 0, lastHlc: null, goodLines: [], defect: null };
|
|
2179
|
-
const lines = segmentLines(
|
|
2251
|
+
const lines = segmentLines(readFileSync3(path, "utf8"));
|
|
2180
2252
|
let lastHlc = null;
|
|
2181
2253
|
let count = 0;
|
|
2182
2254
|
let defect = null;
|
|
@@ -2203,14 +2275,14 @@ function segmentLines(raw) {
|
|
|
2203
2275
|
return lines;
|
|
2204
2276
|
}
|
|
2205
2277
|
function writeManifest(path, machine, count, lastHlc, incremental) {
|
|
2206
|
-
let stat2 =
|
|
2278
|
+
let stat2 = statSync2(path);
|
|
2207
2279
|
let shaState;
|
|
2208
2280
|
if (incremental !== null && stat2.size === incremental.size + incremental.appended.length) {
|
|
2209
2281
|
shaState = sha256Update(incremental.state, incremental.appended);
|
|
2210
2282
|
} else {
|
|
2211
|
-
const bytes =
|
|
2283
|
+
const bytes = readFileSync3(path);
|
|
2212
2284
|
shaState = sha256Update(sha256Initial(), bytes);
|
|
2213
|
-
stat2 =
|
|
2285
|
+
stat2 = statSync2(path);
|
|
2214
2286
|
}
|
|
2215
2287
|
writeManifestFile(path, {
|
|
2216
2288
|
v: SEGMENT_FORMAT_VERSION,
|
|
@@ -2227,11 +2299,14 @@ function verifyAgainstManifest(segment) {
|
|
|
2227
2299
|
const manifest = readManifest(segment);
|
|
2228
2300
|
if (manifest === null) return { ok: true };
|
|
2229
2301
|
if (!existsSync2(segment)) return { ok: false, reason: "segment missing but manifest present" };
|
|
2230
|
-
|
|
2231
|
-
|
|
2302
|
+
return checkBytesAgainstManifest(segment, manifest, readFileSync3(segment));
|
|
2303
|
+
}
|
|
2304
|
+
function checkBytesAgainstManifest(segment, manifest, bytes) {
|
|
2305
|
+
const sha = createHash3("sha256").update(bytes).digest("hex");
|
|
2306
|
+
if (sha === manifest.sha256) return { ok: true, exact: true };
|
|
2232
2307
|
const tail = readSegmentTail(segment);
|
|
2233
2308
|
if (tail.count >= manifest.count) {
|
|
2234
|
-
return { ok: true };
|
|
2309
|
+
return { ok: true, exact: false };
|
|
2235
2310
|
}
|
|
2236
2311
|
return {
|
|
2237
2312
|
ok: false,
|
|
@@ -2250,13 +2325,13 @@ function discoverPeers(dir, selfMachineId) {
|
|
|
2250
2325
|
if (machineId === selfMachineId && !conflictCopy) continue;
|
|
2251
2326
|
const path = join3(dir, name);
|
|
2252
2327
|
try {
|
|
2253
|
-
if (!
|
|
2328
|
+
if (!statSync2(path).isFile()) continue;
|
|
2254
2329
|
} catch {
|
|
2255
2330
|
continue;
|
|
2256
2331
|
}
|
|
2257
2332
|
peers.push({ machineId, watermarkKey: conflictCopy ? stem : machineId, path, conflictCopy });
|
|
2258
2333
|
}
|
|
2259
|
-
return peers.sort((a, b) => a.path
|
|
2334
|
+
return peers.sort((a, b) => compareStrings(a.path, b.path));
|
|
2260
2335
|
}
|
|
2261
2336
|
function peerWatermarkKey(peer) {
|
|
2262
2337
|
return peer.watermarkKey ?? peer.machineId;
|
|
@@ -2304,52 +2379,68 @@ function ingestSegment(db, peer, opts) {
|
|
|
2304
2379
|
const defects = [];
|
|
2305
2380
|
const watermarkKey = peerWatermarkKey(peer);
|
|
2306
2381
|
const start = getWatermark(db, watermarkKey);
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
if (!existsSync2(peer.path)) {
|
|
2320
|
-
return {
|
|
2321
|
-
machineId: peer.machineId,
|
|
2322
|
-
path: peer.path,
|
|
2323
|
-
read: 0,
|
|
2324
|
-
applied: 0,
|
|
2325
|
-
changed: 0,
|
|
2326
|
-
watermark: start,
|
|
2327
|
-
defects,
|
|
2328
|
-
truncatedAt: null
|
|
2329
|
-
};
|
|
2330
|
-
}
|
|
2382
|
+
const nothing = {
|
|
2383
|
+
machineId: peer.machineId,
|
|
2384
|
+
path: peer.path,
|
|
2385
|
+
read: 0,
|
|
2386
|
+
applied: 0,
|
|
2387
|
+
changed: 0,
|
|
2388
|
+
watermark: start,
|
|
2389
|
+
defects,
|
|
2390
|
+
truncatedAt: null
|
|
2391
|
+
};
|
|
2392
|
+
if (opts?.verify !== true && segmentCaughtUp(db, watermarkKey, peer.path, start)) return nothing;
|
|
2393
|
+
if (!existsSync2(peer.path)) return nothing;
|
|
2331
2394
|
const fingerprint = segmentFingerprint(peer.path);
|
|
2332
|
-
const
|
|
2333
|
-
|
|
2334
|
-
|
|
2395
|
+
const resume = opts?.verify !== true && fingerprint?.manifest != null && start > 0 ? resumePoint(db, watermarkKey, start) : null;
|
|
2396
|
+
const appended = resume !== null && fingerprint?.manifest != null ? readAppended(peer.path, resume, fingerprint.manifest.sha256) : null;
|
|
2397
|
+
let lines;
|
|
2398
|
+
let base;
|
|
2399
|
+
let seedHlc = null;
|
|
2400
|
+
let nextResume = null;
|
|
2401
|
+
let resumeState = null;
|
|
2402
|
+
let resumeSize = 0;
|
|
2403
|
+
if (resume !== null && appended !== null) {
|
|
2404
|
+
lines = segmentLines(appended.bytes.toString("utf8"));
|
|
2405
|
+
base = resume.lines;
|
|
2406
|
+
seedHlc = resume.lastHlc;
|
|
2407
|
+
resumeState = appended.bytes[appended.bytes.length - 1] === 10 ? appended.state : null;
|
|
2408
|
+
resumeSize = appended.state.n;
|
|
2409
|
+
} else {
|
|
2410
|
+
const manifest = readManifest(peer.path);
|
|
2411
|
+
const bytes = readFileSync3(peer.path);
|
|
2412
|
+
if (manifest !== null) {
|
|
2413
|
+
const verified = checkBytesAgainstManifest(peer.path, manifest, bytes);
|
|
2414
|
+
if (!verified.ok) {
|
|
2415
|
+
defects.push({ kind: "manifest-mismatch", line: 0, detail: verified.reason });
|
|
2416
|
+
} else if (verified.exact) {
|
|
2417
|
+
const state = usableShaState(manifest.shaState, bytes.length);
|
|
2418
|
+
if (state !== null && bytes[bytes.length - 1] === 10 && sha256Digest(state) === manifest.sha256) {
|
|
2419
|
+
resumeState = state;
|
|
2420
|
+
resumeSize = bytes.length;
|
|
2421
|
+
}
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
lines = segmentLines(bytes.toString("utf8"));
|
|
2425
|
+
base = 0;
|
|
2426
|
+
if (start > 0 && start <= lines.length) {
|
|
2427
|
+
const prior = lines[start - 1];
|
|
2428
|
+
if (prior !== void 0) {
|
|
2429
|
+
const decoded = decodeLine(prior);
|
|
2430
|
+
if (decoded.ok) seedHlc = decoded.line.hlc;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2335
2433
|
}
|
|
2336
|
-
const
|
|
2434
|
+
const total = base + lines.length;
|
|
2337
2435
|
let applied = 0;
|
|
2338
2436
|
let changed = 0;
|
|
2339
2437
|
let watermark = start;
|
|
2340
2438
|
let truncatedAt = null;
|
|
2341
|
-
let previousHlc =
|
|
2342
|
-
if (start > 0 && start <= lines.length) {
|
|
2343
|
-
const prior = lines[start - 1];
|
|
2344
|
-
if (prior !== void 0) {
|
|
2345
|
-
const decoded = decodeLine(prior);
|
|
2346
|
-
if (decoded.ok) previousHlc = decoded.line.hlc;
|
|
2347
|
-
}
|
|
2348
|
-
}
|
|
2439
|
+
let previousHlc = seedHlc;
|
|
2349
2440
|
const run = db.transaction(() => {
|
|
2350
|
-
for (let index = start; index < lines.length; index++) {
|
|
2441
|
+
for (let index = start - base; index < lines.length; index++) {
|
|
2351
2442
|
const raw = lines[index];
|
|
2352
|
-
const lineNo = index + 1;
|
|
2443
|
+
const lineNo = base + index + 1;
|
|
2353
2444
|
if (raw === void 0 || raw.trim() === "") {
|
|
2354
2445
|
defects.push({ kind: "malformed-shape", line: lineNo, detail: "blank line" });
|
|
2355
2446
|
truncatedAt = lineNo;
|
|
@@ -2415,8 +2506,16 @@ function ingestSegment(db, peer, opts) {
|
|
|
2415
2506
|
previousHlc = op.hlc;
|
|
2416
2507
|
watermark = lineNo;
|
|
2417
2508
|
}
|
|
2418
|
-
const clean = defects.length === 0 && watermark ===
|
|
2419
|
-
|
|
2509
|
+
const clean = defects.length === 0 && watermark === total;
|
|
2510
|
+
if (clean && resumeState !== null && previousHlc !== null) {
|
|
2511
|
+
nextResume = { lines: total, size: resumeSize, lastHlc: previousHlc, state: resumeState };
|
|
2512
|
+
}
|
|
2513
|
+
setWatermark(
|
|
2514
|
+
db,
|
|
2515
|
+
watermarkKey,
|
|
2516
|
+
watermark,
|
|
2517
|
+
clean && fingerprint !== null ? encodeFingerprint(fingerprint.key, nextResume) : null
|
|
2518
|
+
);
|
|
2420
2519
|
});
|
|
2421
2520
|
run.immediate();
|
|
2422
2521
|
return {
|
|
@@ -2491,7 +2590,7 @@ async function syncPass(db, dir = syncDir()) {
|
|
|
2491
2590
|
}
|
|
2492
2591
|
|
|
2493
2592
|
// src/sync.ts
|
|
2494
|
-
import { existsSync as existsSync3, statSync as
|
|
2593
|
+
import { existsSync as existsSync3, statSync as statSync3 } from "fs";
|
|
2495
2594
|
import Database2 from "better-sqlite3";
|
|
2496
2595
|
var SyncPeerNotFoundError = class extends Error {
|
|
2497
2596
|
constructor(ref, known) {
|
|
@@ -2533,7 +2632,7 @@ function peerStatuses(db, dir) {
|
|
|
2533
2632
|
const rows = discoverPeers(dir, self).map((peer) => {
|
|
2534
2633
|
let lastSeenMs = null;
|
|
2535
2634
|
try {
|
|
2536
|
-
lastSeenMs =
|
|
2635
|
+
lastSeenMs = statSync3(peer.path).mtimeMs;
|
|
2537
2636
|
} catch {
|
|
2538
2637
|
lastSeenMs = null;
|
|
2539
2638
|
}
|
|
@@ -2775,6 +2874,7 @@ export {
|
|
|
2775
2874
|
repairTaskPair,
|
|
2776
2875
|
applyOp,
|
|
2777
2876
|
applyOps,
|
|
2877
|
+
compareStrings,
|
|
2778
2878
|
readManifest,
|
|
2779
2879
|
SEGMENT_FORMAT_VERSION,
|
|
2780
2880
|
syncDir,
|
|
@@ -2805,4 +2905,4 @@ export {
|
|
|
2805
2905
|
ingestFromDb,
|
|
2806
2906
|
transportNextSteps
|
|
2807
2907
|
};
|
|
2808
|
-
//# sourceMappingURL=chunk-
|
|
2908
|
+
//# sourceMappingURL=chunk-45KQLMEL.js.map
|