@remnic/plugin-openclaw 1.0.10 → 1.0.12
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/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
- package/dist/capsule-cli-GBM3WPAM.js +33 -0
- package/dist/capsule-crypto-K3IRTKRH.js +17 -0
- package/dist/capsule-export-IXVERCQG.js +17 -0
- package/dist/capsule-import-IA6VIOPQ.js +16 -0
- package/dist/capsule-merge-IWOQ34KL.js +189 -0
- package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
- package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-YI53C2AO.js} +16 -12
- package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
- package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
- package/dist/chunk-37NKFWSO.js +233 -0
- package/dist/chunk-3G7FAF6S.js +60 -0
- package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
- package/dist/chunk-4G2XCSD2.js +186 -0
- package/dist/chunk-4LYQ4ONL.js +185 -0
- package/dist/chunk-6F6EKSVP.js +453 -0
- package/dist/chunk-6IWEAUN6.js +148 -0
- package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
- package/dist/chunk-7OQEPGQF.js +529 -0
- package/dist/{chunk-JJSNPSCD.js → chunk-7UZNLMW5.js} +652 -174
- package/dist/chunk-B52XADV3.js +244 -0
- package/dist/chunk-BU5KJVWF.js +78 -0
- package/dist/chunk-CDAZGIGT.js +720 -0
- package/dist/chunk-CXM7EBAO.js +289 -0
- package/dist/{chunk-HCFFXBLV.js → chunk-EXDYWXMB.js} +6 -1861
- package/dist/chunk-FGTYFLL5.js +274 -0
- package/dist/chunk-FQRSVYY4.js +110 -0
- package/dist/chunk-HRGFO6AW.js +349 -0
- package/dist/chunk-I6B2W2IY.js +47 -0
- package/dist/chunk-JZBOXOUC.js +259 -0
- package/dist/chunk-L6I4MQKO.js +227 -0
- package/dist/chunk-LLUROTZJ.js +328 -0
- package/dist/chunk-MBIFE6SA.js +250 -0
- package/dist/chunk-NKVIN6RD.js +118 -0
- package/dist/chunk-OAE7AQ6R.js +1832 -0
- package/dist/chunk-OEI7GLV2.js +17 -0
- package/dist/chunk-RKR6PTPA.js +308 -0
- package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
- package/dist/chunk-SSFTU6LP.js +182 -0
- package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
- package/dist/{chunk-S2ISS4AH.js → chunk-TILAJIJR.js} +10 -10
- package/dist/chunk-TLVIQLB4.js +874 -0
- package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
- package/dist/chunk-YGGGUTG3.js +125 -0
- package/dist/cipher-VHAFCG7Z.js +27 -0
- package/dist/dreams-ledger-3I52ISYR.js +285 -0
- package/dist/{engine-65C2J63X.js → engine-BIYI3P4J.js} +7 -2
- package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
- package/dist/first-start-migration-I24M2JEE.js +258 -0
- package/dist/forget-NI4RBDPB.js +68 -0
- package/dist/fs-utils-PZRI2HDZ.js +29 -0
- package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
- package/dist/index.js +10654 -3067
- package/dist/kdf-H5B23ZM2.js +25 -0
- package/dist/memory-governance-SJ5DGRB3.js +25 -0
- package/dist/metadata-JAGIWHEA.js +20 -0
- package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
- package/dist/path-5LCUBAAZ.js +8 -0
- package/dist/peers-JF2I6RCR.js +43 -0
- package/dist/purge-XN2VSPZ2.js +204 -0
- package/dist/secure-store-A4NGCNXV.js +155 -0
- package/dist/state-PVISYXRH.js +7 -0
- package/dist/state-store-LP5BO6SF.js +15 -0
- package/dist/{storage-DM4ZGOCN.js → storage-PTQ2H2YJ.js} +3 -1
- package/dist/tier-stats-IZNW66NC.js +147 -0
- package/dist/trace-NJESSGH7.js +289 -0
- package/dist/tui-MGK2LYJY.js +12 -0
- package/dist/types-R4DO7AKM.js +30 -0
- package/openclaw.plugin.json +519 -4
- package/package.json +2 -2
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// ../remnic-core/src/console/state.ts
|
|
2
|
+
import { promises as fs, createReadStream } from "fs";
|
|
3
|
+
import { createInterface } from "readline";
|
|
4
|
+
import path from "path";
|
|
5
|
+
var MAX_LEDGER_TAIL = 50;
|
|
6
|
+
var MAX_VERDICT_TAIL = 25;
|
|
7
|
+
var MAX_DEDUP_TAIL = 10;
|
|
8
|
+
async function gatherConsoleState(orchestrator) {
|
|
9
|
+
const errors = [];
|
|
10
|
+
const capturedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
11
|
+
const bufferState = readBufferState(orchestrator, errors);
|
|
12
|
+
const extractionQueue = readExtractionQueue(orchestrator, errors);
|
|
13
|
+
const dedupRecent = readDedupRecent(orchestrator, errors);
|
|
14
|
+
const maintenanceLedgerTail = await readMaintenanceLedgerTail(
|
|
15
|
+
orchestrator,
|
|
16
|
+
errors
|
|
17
|
+
);
|
|
18
|
+
const qmdProbe = readQmdProbe(orchestrator, errors);
|
|
19
|
+
const daemon = readDaemonInfo(orchestrator, errors);
|
|
20
|
+
return {
|
|
21
|
+
capturedAt,
|
|
22
|
+
bufferState,
|
|
23
|
+
extractionQueue,
|
|
24
|
+
dedupRecent,
|
|
25
|
+
maintenanceLedgerTail,
|
|
26
|
+
qmdProbe,
|
|
27
|
+
daemon,
|
|
28
|
+
errors
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function readBufferState(orchestrator, errors) {
|
|
32
|
+
try {
|
|
33
|
+
const getTurns = orchestrator.buffer?.getTurns;
|
|
34
|
+
if (typeof getTurns !== "function") {
|
|
35
|
+
return { turnsCount: 0, byteCount: 0 };
|
|
36
|
+
}
|
|
37
|
+
const turns = getTurns.call(orchestrator.buffer) ?? [];
|
|
38
|
+
let byteCount = 0;
|
|
39
|
+
for (const turn of turns) {
|
|
40
|
+
const content = typeof turn?.content === "string" ? turn.content : "";
|
|
41
|
+
byteCount += Buffer.byteLength(content, "utf8");
|
|
42
|
+
}
|
|
43
|
+
return { turnsCount: turns.length, byteCount };
|
|
44
|
+
} catch (err) {
|
|
45
|
+
errors.push(`bufferState: ${describeError(err)}`);
|
|
46
|
+
return { turnsCount: 0, byteCount: 0 };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function readExtractionQueue(orchestrator, errors) {
|
|
50
|
+
try {
|
|
51
|
+
const depth = typeof orchestrator.getConsoleExtractionQueueDepth === "function" ? orchestrator.getConsoleExtractionQueueDepth() : 0;
|
|
52
|
+
const verdicts = typeof orchestrator.getConsoleExtractionRecentVerdicts === "function" ? orchestrator.getConsoleExtractionRecentVerdicts() : [];
|
|
53
|
+
const recentVerdicts = (verdicts ?? []).slice(-MAX_VERDICT_TAIL).map((v) => ({
|
|
54
|
+
ts: typeof v?.ts === "string" ? v.ts : "",
|
|
55
|
+
kind: typeof v?.kind === "string" ? v.kind : "unknown",
|
|
56
|
+
...typeof v?.reason === "string" ? { reason: v.reason } : {}
|
|
57
|
+
}));
|
|
58
|
+
return { depth: Number.isFinite(depth) ? depth : 0, recentVerdicts };
|
|
59
|
+
} catch (err) {
|
|
60
|
+
errors.push(`extractionQueue: ${describeError(err)}`);
|
|
61
|
+
return { depth: 0, recentVerdicts: [] };
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readDedupRecent(orchestrator, errors) {
|
|
65
|
+
try {
|
|
66
|
+
if (typeof orchestrator.getConsoleDedupRecentDecisions !== "function") return [];
|
|
67
|
+
const raw = orchestrator.getConsoleDedupRecentDecisions() ?? [];
|
|
68
|
+
return raw.slice(-MAX_DEDUP_TAIL).map((d) => ({
|
|
69
|
+
ts: typeof d?.ts === "string" ? d.ts : "",
|
|
70
|
+
decision: typeof d?.decision === "string" ? d.decision : "unknown",
|
|
71
|
+
...typeof d?.fingerprint === "string" ? { fingerprint: d.fingerprint } : {},
|
|
72
|
+
...typeof d?.similarity === "number" && Number.isFinite(d.similarity) ? { similarity: d.similarity } : {}
|
|
73
|
+
}));
|
|
74
|
+
} catch (err) {
|
|
75
|
+
errors.push(`dedupRecent: ${describeError(err)}`);
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function streamLedgerTopN(ledgerPath, topN, project) {
|
|
80
|
+
let stream;
|
|
81
|
+
try {
|
|
82
|
+
await fs.stat(ledgerPath);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
const code = err.code;
|
|
85
|
+
if (code === "ENOENT") return [];
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
stream = createReadStream(ledgerPath, { encoding: "utf-8" });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const code = err.code;
|
|
92
|
+
if (code === "ENOENT") return [];
|
|
93
|
+
throw err;
|
|
94
|
+
}
|
|
95
|
+
const rl = createInterface({ input: stream, crlfDelay: Infinity });
|
|
96
|
+
const cap = Math.max(1, topN) * 2;
|
|
97
|
+
const buf = [];
|
|
98
|
+
const truncate = () => {
|
|
99
|
+
buf.sort((a, b) => {
|
|
100
|
+
const aMs = Date.parse(a.ts);
|
|
101
|
+
const bMs = Date.parse(b.ts);
|
|
102
|
+
if (Number.isFinite(aMs) && Number.isFinite(bMs)) return bMs - aMs;
|
|
103
|
+
if (Number.isFinite(aMs)) return -1;
|
|
104
|
+
if (Number.isFinite(bMs)) return 1;
|
|
105
|
+
return 0;
|
|
106
|
+
});
|
|
107
|
+
if (buf.length > topN) buf.length = topN;
|
|
108
|
+
};
|
|
109
|
+
try {
|
|
110
|
+
for await (const line of rl) {
|
|
111
|
+
const trimmed = line.trim();
|
|
112
|
+
if (!trimmed) continue;
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = JSON.parse(trimmed);
|
|
116
|
+
} catch {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const event = project(parsed);
|
|
123
|
+
if (event === null) continue;
|
|
124
|
+
if (!event.ts || !Number.isFinite(Date.parse(event.ts))) continue;
|
|
125
|
+
buf.push(event);
|
|
126
|
+
if (buf.length >= cap) truncate();
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
rl.close();
|
|
130
|
+
stream.close();
|
|
131
|
+
}
|
|
132
|
+
truncate();
|
|
133
|
+
return buf.reverse();
|
|
134
|
+
}
|
|
135
|
+
function projectLedgerRow(p) {
|
|
136
|
+
const ts = typeof p.ts === "string" && p.ts.length > 0 ? p.ts : typeof p.hour === "string" && p.hour.length > 0 ? p.hour : typeof p.rebuiltAt === "string" && p.rebuiltAt.length > 0 ? p.rebuiltAt : "";
|
|
137
|
+
const category = typeof p.category === "string" && p.category.length > 0 ? p.category : typeof p.verdictKind === "string" ? "judge-verdict" : typeof p.turnCount === "number" ? "observation" : "unknown";
|
|
138
|
+
const summary = summarizeLedgerEvent(p);
|
|
139
|
+
return { ts, category, summary };
|
|
140
|
+
}
|
|
141
|
+
async function readMaintenanceLedgerTail(orchestrator, errors) {
|
|
142
|
+
try {
|
|
143
|
+
const memoryDir = orchestrator.config?.memoryDir;
|
|
144
|
+
if (!memoryDir || typeof memoryDir !== "string") return [];
|
|
145
|
+
const ledgerDir = path.join(memoryDir, "state", "observation-ledger");
|
|
146
|
+
const verdictsTopN = await streamLedgerTopN(
|
|
147
|
+
path.join(ledgerDir, "extraction-judge-verdicts.jsonl"),
|
|
148
|
+
MAX_LEDGER_TAIL,
|
|
149
|
+
projectLedgerRow
|
|
150
|
+
);
|
|
151
|
+
const rebuiltTopN = await streamLedgerTopN(
|
|
152
|
+
path.join(ledgerDir, "rebuilt-observations.jsonl"),
|
|
153
|
+
MAX_LEDGER_TAIL,
|
|
154
|
+
projectLedgerRow
|
|
155
|
+
);
|
|
156
|
+
const events = [...verdictsTopN, ...rebuiltTopN];
|
|
157
|
+
if (events.length === 0) return [];
|
|
158
|
+
events.sort((a, b) => {
|
|
159
|
+
const aMs = Date.parse(a.ts);
|
|
160
|
+
const bMs = Date.parse(b.ts);
|
|
161
|
+
if (Number.isFinite(aMs) && Number.isFinite(bMs)) return bMs - aMs;
|
|
162
|
+
if (Number.isFinite(aMs)) return -1;
|
|
163
|
+
if (Number.isFinite(bMs)) return 1;
|
|
164
|
+
return 0;
|
|
165
|
+
});
|
|
166
|
+
const tail = events.slice(0, MAX_LEDGER_TAIL);
|
|
167
|
+
return tail.reverse();
|
|
168
|
+
} catch (err) {
|
|
169
|
+
errors.push(`maintenanceLedgerTail: ${describeError(err)}`);
|
|
170
|
+
return [];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function summarizeLedgerEvent(p) {
|
|
174
|
+
const parts = [];
|
|
175
|
+
if (typeof p.verdictKind === "string") parts.push(`verdict=${p.verdictKind}`);
|
|
176
|
+
if (typeof p.reason === "string" && p.reason.length > 0) {
|
|
177
|
+
const trimmed = p.reason.length > 80 ? `${p.reason.slice(0, 80)}\u2026` : p.reason;
|
|
178
|
+
parts.push(`reason=${trimmed}`);
|
|
179
|
+
}
|
|
180
|
+
if (typeof p.candidateCategory === "string") {
|
|
181
|
+
parts.push(`cat=${p.candidateCategory}`);
|
|
182
|
+
}
|
|
183
|
+
if (typeof p.sessionKey === "string" && p.sessionKey.length > 0) {
|
|
184
|
+
parts.push(`session=${p.sessionKey}`);
|
|
185
|
+
}
|
|
186
|
+
if (typeof p.turnCount === "number" && Number.isFinite(p.turnCount)) {
|
|
187
|
+
parts.push(`turns=${p.turnCount}`);
|
|
188
|
+
}
|
|
189
|
+
if (typeof p.userTurns === "number" && Number.isFinite(p.userTurns)) {
|
|
190
|
+
parts.push(`u=${p.userTurns}`);
|
|
191
|
+
}
|
|
192
|
+
if (typeof p.assistantTurns === "number" && Number.isFinite(p.assistantTurns)) {
|
|
193
|
+
parts.push(`a=${p.assistantTurns}`);
|
|
194
|
+
}
|
|
195
|
+
if (parts.length === 0) {
|
|
196
|
+
return typeof p.category === "string" ? p.category : "event";
|
|
197
|
+
}
|
|
198
|
+
return parts.join(" ");
|
|
199
|
+
}
|
|
200
|
+
function readQmdProbe(orchestrator, errors) {
|
|
201
|
+
try {
|
|
202
|
+
const qmd = orchestrator.qmd;
|
|
203
|
+
if (!qmd) {
|
|
204
|
+
return { available: false, daemonMode: false, debug: "qmd unavailable" };
|
|
205
|
+
}
|
|
206
|
+
const available = typeof qmd.isAvailable === "function" ? qmd.isAvailable() : false;
|
|
207
|
+
const daemonMode = typeof qmd.isDaemonMode === "function" ? qmd.isDaemonMode() : false;
|
|
208
|
+
const debug = typeof qmd.debugStatus === "function" ? qmd.debugStatus() : "";
|
|
209
|
+
return { available, daemonMode, debug };
|
|
210
|
+
} catch (err) {
|
|
211
|
+
errors.push(`qmdProbe: ${describeError(err)}`);
|
|
212
|
+
return { available: false, daemonMode: false, debug: "" };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function readDaemonInfo(orchestrator, errors) {
|
|
216
|
+
try {
|
|
217
|
+
if (typeof orchestrator.getConsoleDaemonInfo === "function") {
|
|
218
|
+
const info = orchestrator.getConsoleDaemonInfo();
|
|
219
|
+
return {
|
|
220
|
+
uptimeMs: Number.isFinite(info?.uptimeMs) ? info.uptimeMs : 0,
|
|
221
|
+
version: typeof info?.version === "string" ? info.version : "unknown"
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const uptimeMs = Math.round((process.uptime?.() ?? 0) * 1e3);
|
|
225
|
+
return { uptimeMs, version: resolvePackageVersion() };
|
|
226
|
+
} catch (err) {
|
|
227
|
+
errors.push(`daemon: ${describeError(err)}`);
|
|
228
|
+
return { uptimeMs: 0, version: "unknown" };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function resolvePackageVersion() {
|
|
232
|
+
try {
|
|
233
|
+
const env = process.env?.REMNIC_VERSION;
|
|
234
|
+
if (typeof env === "string" && env.length > 0) return env;
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
return "unknown";
|
|
238
|
+
}
|
|
239
|
+
function describeError(err) {
|
|
240
|
+
if (err instanceof Error) return err.message;
|
|
241
|
+
try {
|
|
242
|
+
return String(err);
|
|
243
|
+
} catch {
|
|
244
|
+
return "unknown error";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
export {
|
|
249
|
+
gatherConsoleState
|
|
250
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
// ../remnic-core/src/transfer/fs-utils.ts
|
|
2
|
+
import { createHash } from "crypto";
|
|
3
|
+
import {
|
|
4
|
+
lstat,
|
|
5
|
+
mkdir,
|
|
6
|
+
readdir,
|
|
7
|
+
readFile,
|
|
8
|
+
realpath,
|
|
9
|
+
stat,
|
|
10
|
+
writeFile
|
|
11
|
+
} from "fs/promises";
|
|
12
|
+
import path from "path";
|
|
13
|
+
async function sha256File(filePath) {
|
|
14
|
+
const buf = await readFile(filePath);
|
|
15
|
+
const sha256 = createHash("sha256").update(buf).digest("hex");
|
|
16
|
+
return { sha256, bytes: buf.byteLength };
|
|
17
|
+
}
|
|
18
|
+
function sha256String(content) {
|
|
19
|
+
const buf = Buffer.from(content, "utf-8");
|
|
20
|
+
const sha256 = createHash("sha256").update(buf).digest("hex");
|
|
21
|
+
return { sha256, bytes: buf.byteLength };
|
|
22
|
+
}
|
|
23
|
+
async function writeJsonFile(filePath, value) {
|
|
24
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
25
|
+
await writeFile(filePath, JSON.stringify(value, null, 2) + "\n", "utf-8");
|
|
26
|
+
}
|
|
27
|
+
async function readJsonFile(filePath) {
|
|
28
|
+
const raw = await readFile(filePath, "utf-8");
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
async function listFilesRecursive(rootDir) {
|
|
32
|
+
const out = [];
|
|
33
|
+
async function walk(dir) {
|
|
34
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
35
|
+
for (const ent of entries) {
|
|
36
|
+
const fp = path.join(dir, ent.name);
|
|
37
|
+
if (ent.isDirectory()) {
|
|
38
|
+
await walk(fp);
|
|
39
|
+
} else if (ent.isFile()) {
|
|
40
|
+
out.push(fp);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
await walk(rootDir);
|
|
45
|
+
return out.sort();
|
|
46
|
+
}
|
|
47
|
+
async function ensureDirExists(dirPath) {
|
|
48
|
+
await mkdir(dirPath, { recursive: true });
|
|
49
|
+
}
|
|
50
|
+
async function fileExists(filePath) {
|
|
51
|
+
try {
|
|
52
|
+
await stat(filePath);
|
|
53
|
+
return true;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function toPosixRelPath(absPath, rootDir) {
|
|
59
|
+
const rel = path.relative(rootDir, absPath);
|
|
60
|
+
return rel.split(path.sep).join("/");
|
|
61
|
+
}
|
|
62
|
+
function fromPosixRelPath(relPath) {
|
|
63
|
+
return relPath.split("/").join(path.sep);
|
|
64
|
+
}
|
|
65
|
+
function isPathInsideRoot(rootReal, absPath) {
|
|
66
|
+
const rel = path.relative(rootReal, absPath);
|
|
67
|
+
if (rel === "") return true;
|
|
68
|
+
if (rel === "..") return false;
|
|
69
|
+
if (rel.startsWith(`..${path.sep}`)) return false;
|
|
70
|
+
if (path.isAbsolute(rel)) return false;
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
async function assertIsDirectoryNotSymlink(absPath, errorPrefix, argName) {
|
|
74
|
+
const st = await stat(absPath).catch(() => null);
|
|
75
|
+
if (!st || !st.isDirectory()) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`${errorPrefix}: '${argName}' must be an existing directory: ${absPath}`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const lst = await lstat(absPath).catch(() => null);
|
|
81
|
+
if (lst && lst.isSymbolicLink()) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`${errorPrefix}: '${argName}' must not be a symlink \u2014 resolve it to its real path first: ${absPath}`
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function assertRealpathInsideRoot(rootReal, targetAbs, sourcePath, errorPrefix) {
|
|
88
|
+
let existing = targetAbs;
|
|
89
|
+
const suffix = [];
|
|
90
|
+
while (existing !== path.dirname(existing)) {
|
|
91
|
+
const st = await lstat(existing).catch(() => null);
|
|
92
|
+
if (st !== null) break;
|
|
93
|
+
suffix.unshift(path.basename(existing));
|
|
94
|
+
existing = path.dirname(existing);
|
|
95
|
+
}
|
|
96
|
+
const existingReal = await realpath(existing).catch(() => existing);
|
|
97
|
+
const targetReal = suffix.length > 0 ? path.join(existingReal, ...suffix) : existingReal;
|
|
98
|
+
if (!isPathInsideRoot(rootReal, targetReal)) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${errorPrefix}: record path escapes target root via symlink: ${sourcePath}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
sha256File,
|
|
107
|
+
sha256String,
|
|
108
|
+
writeJsonFile,
|
|
109
|
+
readJsonFile,
|
|
110
|
+
listFilesRecursive,
|
|
111
|
+
ensureDirExists,
|
|
112
|
+
fileExists,
|
|
113
|
+
toPosixRelPath,
|
|
114
|
+
fromPosixRelPath,
|
|
115
|
+
isPathInsideRoot,
|
|
116
|
+
assertIsDirectoryNotSymlink,
|
|
117
|
+
assertRealpathInsideRoot
|
|
118
|
+
};
|