chain-insights 0.3.7 → 0.3.9
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/README.md +5 -0
- package/dist/{capabilities-BC3Y5EOi.mjs → capabilities-BCvkTkIu.mjs} +3 -6
- package/dist/capabilities-BCvkTkIu.mjs.map +1 -0
- package/dist/{capabilities-D5PSx9Hj.cjs → capabilities-DOa6EFO-.cjs} +2 -5
- package/dist/cli.cjs +41 -14
- package/dist/cli.mjs +41 -14
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-D4JE7fFF.mjs → client-BgmHjBHQ.mjs} +15 -7
- package/dist/client-BgmHjBHQ.mjs.map +1 -0
- package/dist/{client-Db6IV1tv.cjs → client-Y_zqKqJT.cjs} +19 -5
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/mcp-proxy.cjs +442 -408
- package/dist/mcp-proxy.d.cts +3 -1
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts +3 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +442 -409
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-xfVNz9NE.cjs → public-tools-BY3PTw6x.cjs} +59 -31
- package/dist/{public-tools-CyUZEz9B.mjs → public-tools-CvlZcysd.mjs} +60 -32
- package/dist/public-tools-CvlZcysd.mjs.map +1 -0
- package/dist/{runner-DWuSy1Se.mjs → runner-CVnjpqc-.mjs} +2 -2
- package/dist/{runner-DWuSy1Se.mjs.map → runner-CVnjpqc-.mjs.map} +1 -1
- package/dist/{runner-CVo41fjz.cjs → runner-bLy0pTr_.cjs} +1 -1
- package/dist/update-BJoXYucO.cjs +145 -0
- package/dist/update-CJUfGCxs.mjs +145 -0
- package/dist/update-CJUfGCxs.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/capabilities-BC3Y5EOi.mjs.map +0 -1
- package/dist/client-D4JE7fFF.mjs.map +0 -1
- package/dist/public-tools-CyUZEz9B.mjs.map +0 -1
|
@@ -45,7 +45,7 @@ var AliasTracker = class {
|
|
|
45
45
|
return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b, void 0, { numeric: true })));
|
|
46
46
|
}
|
|
47
47
|
};
|
|
48
|
-
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 =
|
|
48
|
+
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 10;
|
|
49
49
|
const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS$1 = 300 * 1e3;
|
|
50
50
|
const SCHEMA_QUERY_SET = [
|
|
51
51
|
{
|
|
@@ -822,6 +822,7 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
|
|
|
822
822
|
for (const flow of flows) byHop.set(flow.hop, (byHop.get(flow.hop) ?? 0) + 1);
|
|
823
823
|
const depositCount = continuation.depositAddresses.length;
|
|
824
824
|
const exchangeCount = continuation.exchangeAddresses.length;
|
|
825
|
+
const hasFiles = Object.values(files).some((value) => value.length > 0);
|
|
825
826
|
return [
|
|
826
827
|
`Trace complete for ${network}:${seedAddress}`,
|
|
827
828
|
"",
|
|
@@ -830,14 +831,16 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
|
|
|
830
831
|
`Exchange endpoints reached: ${exchangeCount}. Deposit candidate address(es): ${depositCount}.`,
|
|
831
832
|
`Traceback source path(s): ${sourceMatches.length}. Reverse 1-hop lead(s): ${reverseLeads.length}.`,
|
|
832
833
|
"",
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
834
|
+
hasFiles ? [
|
|
835
|
+
"Files written:",
|
|
836
|
+
`- schema: ${files.schema}`,
|
|
837
|
+
`- compact evidence JSON: ${files.compactEvidence}`,
|
|
838
|
+
`- graph JSON: ${files.graph}`,
|
|
839
|
+
`- graph HTML: ${files.graphHtml}`,
|
|
840
|
+
`- table CSV: ${files.table}`,
|
|
841
|
+
`- table HTML: ${files.tableHtml}`,
|
|
842
|
+
`- report: ${files.report}`
|
|
843
|
+
].join("\n") : "Files written: disabled by stateless proxy mode.",
|
|
841
844
|
"",
|
|
842
845
|
`Continuation hint: ${continuation.hint}`,
|
|
843
846
|
continuation.depositAddresses.length > 0 ? `Deposit candidates: ${continuation.depositAddresses.map((address) => aliases.alias(address) ?? address).join(", ")}` : "Deposit candidates: none reached in this bounded trace.",
|
|
@@ -853,9 +856,18 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
853
856
|
const perAddressLimit = clampInt$2(options.perAddressLimit, 5, 1, 10);
|
|
854
857
|
const minAmountSum = Math.max(0, options.minAmountSum ?? 0);
|
|
855
858
|
const evidenceSource = options.evidenceSource ?? "track_funds";
|
|
856
|
-
const
|
|
857
|
-
|
|
858
|
-
const
|
|
859
|
+
const writeArtifacts = options.writeArtifacts !== false;
|
|
860
|
+
if (!writeArtifacts && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
|
|
861
|
+
const paths = writeArtifacts ? require_output_root.workspaceOutputPaths() : void 0;
|
|
862
|
+
if (paths) await ensureDirs(paths);
|
|
863
|
+
const schemaResult = paths ? await loadOrCaptureTopologySchema(remoteClient, paths, network) : {
|
|
864
|
+
schema: {
|
|
865
|
+
schema: "chain-insights.runtime_graph_schema.v1",
|
|
866
|
+
network,
|
|
867
|
+
source: "stateless_proxy_mode"
|
|
868
|
+
},
|
|
869
|
+
filePath: "stateless://runtime-schema-not-written"
|
|
870
|
+
};
|
|
859
871
|
const { flows, deposits, sourceMatches, reverseLeads } = await collectProbeTrace(remoteClient, {
|
|
860
872
|
seedAddress,
|
|
861
873
|
network,
|
|
@@ -868,19 +880,21 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
868
880
|
const slug = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}_${sanitizeSegment(seedAddress.slice(0, 16))}`;
|
|
869
881
|
const compact = probeEvidence(seedAddress, network, schemaResult.filePath, aliases, flows, deposits, sourceMatches, reverseLeads, evidenceSource);
|
|
870
882
|
const graph = buildGraph(seedAddress, network, flows, deposits, sourceMatches, reverseLeads);
|
|
871
|
-
const compactPath = node_path.default.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`);
|
|
872
|
-
const graphPath = node_path.default.join(paths.reportGraphsRoot, `${slug}.graph.json`);
|
|
873
|
-
const graphHtmlPath = node_path.default.join(paths.reportsRoot, `${slug}.graph.html`);
|
|
874
|
-
const tablePath = node_path.default.join(paths.reportTablesRoot, `${slug}.flows.csv`);
|
|
875
|
-
const tableHtmlPath = node_path.default.join(paths.reportsRoot, `${slug}.table.html`);
|
|
876
|
-
const reportPath = node_path.default.join(paths.reportsRoot, `${slug}.trace-report.md`);
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
883
|
+
const compactPath = paths ? node_path.default.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`) : "";
|
|
884
|
+
const graphPath = paths ? node_path.default.join(paths.reportGraphsRoot, `${slug}.graph.json`) : "";
|
|
885
|
+
const graphHtmlPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.graph.html`) : "";
|
|
886
|
+
const tablePath = paths ? node_path.default.join(paths.reportTablesRoot, `${slug}.flows.csv`) : "";
|
|
887
|
+
const tableHtmlPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.table.html`) : "";
|
|
888
|
+
const reportPath = paths ? node_path.default.join(paths.reportsRoot, `${slug}.trace-report.md`) : "";
|
|
889
|
+
if (paths) {
|
|
890
|
+
const { generateInlineGraphHtml } = await Promise.resolve().then(() => require("./html-generator-Bx3UcLTB.cjs")).then((n) => n.html_generator_exports);
|
|
891
|
+
await (0, node_fs_promises.writeFile)(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
|
|
892
|
+
await (0, node_fs_promises.writeFile)(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
|
|
893
|
+
await (0, node_fs_promises.writeFile)(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
|
|
894
|
+
await (0, node_fs_promises.writeFile)(tablePath, tableCsv(flows), { mode: 384 });
|
|
895
|
+
await (0, node_fs_promises.writeFile)(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
|
|
896
|
+
await (0, node_fs_promises.writeFile)(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
|
|
897
|
+
}
|
|
884
898
|
if (options.caseId) {
|
|
885
899
|
const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-Bz_9XKEw.cjs")).then((n) => n.cases_exports);
|
|
886
900
|
await EvidenceStore.append(options.caseId, {
|
|
@@ -939,7 +953,7 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
939
953
|
}
|
|
940
954
|
//#endregion
|
|
941
955
|
//#region src/investigation/stake-insights.ts
|
|
942
|
-
const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS =
|
|
956
|
+
const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 10;
|
|
943
957
|
const STAKE_INSIGHTS_REQUEST_TIMEOUT_MS = 300 * 1e3;
|
|
944
958
|
function escapeCypherString$1(value) {
|
|
945
959
|
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
@@ -1299,7 +1313,8 @@ function summaryLines(network, subject, rows, totals, failures) {
|
|
|
1299
1313
|
async function stakeInsights(remoteClient, options) {
|
|
1300
1314
|
const { network, subject, depth } = validateOptions(options);
|
|
1301
1315
|
const { live, archive, failures, evidence } = collectRelationships(await callGraphBatch$1(remoteClient, network, [stakeRelationshipQuery("live_topology", subject, options, depth), stakeRelationshipQuery("archive_topology", subject, options, depth)]));
|
|
1302
|
-
|
|
1316
|
+
const successfulQueryCount = evidence.filter((entry) => entry["ok"] === true).length;
|
|
1317
|
+
if (live.length === 0 && archive.length === 0 && failures.length > 0 && successfulQueryCount === 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
|
|
1303
1318
|
const rows = live.length > 0 ? live : archive;
|
|
1304
1319
|
const totals = stakeTotals(rows);
|
|
1305
1320
|
const facts = {
|
|
@@ -1334,7 +1349,7 @@ async function stakeInsights(remoteClient, options) {
|
|
|
1334
1349
|
}
|
|
1335
1350
|
//#endregion
|
|
1336
1351
|
//#region src/investigation/public-tools.ts
|
|
1337
|
-
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS =
|
|
1352
|
+
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 10;
|
|
1338
1353
|
const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 300 * 1e3;
|
|
1339
1354
|
function escapeCypherString(value) {
|
|
1340
1355
|
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
@@ -1815,6 +1830,10 @@ function normalizeTraceGraphData(runs, network) {
|
|
|
1815
1830
|
}
|
|
1816
1831
|
function traceArtifactPointersFromRun(run) {
|
|
1817
1832
|
if (!run) return {};
|
|
1833
|
+
if (!Object.values(run.files).some((value) => value.length > 0)) return {
|
|
1834
|
+
artifacts_written: false,
|
|
1835
|
+
artifact_mode: "stateless"
|
|
1836
|
+
};
|
|
1818
1837
|
return {
|
|
1819
1838
|
graph_json: run.files.graph,
|
|
1820
1839
|
graph_html: run.files.graphHtml,
|
|
@@ -1824,6 +1843,12 @@ function traceArtifactPointersFromRun(run) {
|
|
|
1824
1843
|
report_md: run.files.report
|
|
1825
1844
|
};
|
|
1826
1845
|
}
|
|
1846
|
+
function statelessArtifacts() {
|
|
1847
|
+
return {
|
|
1848
|
+
artifacts_written: false,
|
|
1849
|
+
artifact_mode: "stateless"
|
|
1850
|
+
};
|
|
1851
|
+
}
|
|
1827
1852
|
function artifactEvidence(artifacts) {
|
|
1828
1853
|
return Object.entries(artifacts).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0).map(([kind, filePath]) => ({
|
|
1829
1854
|
evidence_type: "artifact_pointer",
|
|
@@ -2028,7 +2053,8 @@ async function traceVictimFunds(remoteClient, config, options) {
|
|
|
2028
2053
|
perAddressLimit: options.perAddressLimit,
|
|
2029
2054
|
minAmountSum: options.minAmountSum,
|
|
2030
2055
|
includeDepositTraceback: false,
|
|
2031
|
-
evidenceSource: "trace_victim_funds"
|
|
2056
|
+
evidenceSource: "trace_victim_funds",
|
|
2057
|
+
writeArtifacts: options.writeArtifacts
|
|
2032
2058
|
})
|
|
2033
2059
|
});
|
|
2034
2060
|
return traceResultFromFundRuns("trace_victim_funds", "victim", network, runs, {
|
|
@@ -2056,7 +2082,8 @@ async function traceSuspectFunds(remoteClient, config, options) {
|
|
|
2056
2082
|
perAddressLimit: options.perAddressLimit,
|
|
2057
2083
|
minAmountSum: options.minAmountSum,
|
|
2058
2084
|
includeDepositTraceback: false,
|
|
2059
|
-
evidenceSource: "trace_suspect_funds"
|
|
2085
|
+
evidenceSource: "trace_suspect_funds",
|
|
2086
|
+
writeArtifacts: options.writeArtifacts
|
|
2060
2087
|
})
|
|
2061
2088
|
});
|
|
2062
2089
|
return traceResultFromFundRuns("trace_suspect_funds", "suspect", network, runs, {
|
|
@@ -2201,6 +2228,7 @@ async function traceDepositSources(remoteClient, _config, options) {
|
|
|
2201
2228
|
if (!network) throw new Error("network is required");
|
|
2202
2229
|
if (deposits.length < 1) throw new Error("deposit_addresses must contain at least 1 address");
|
|
2203
2230
|
if (deposits.length > 5) throw new Error("deposit_addresses cannot exceed 5 addresses");
|
|
2231
|
+
if (options.writeArtifacts === false && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
|
|
2204
2232
|
const maxHops = clampInt(options.maxHops, 2, 1, 5);
|
|
2205
2233
|
const batch = await callGraphBatch(remoteClient, network, Array.from({ length: maxHops }, (_, index) => reverseDepositSourceQueryAtDepth(deposits, index + 1)));
|
|
2206
2234
|
const failures = [];
|
|
@@ -2318,7 +2346,7 @@ async function traceDepositSources(remoteClient, _config, options) {
|
|
|
2318
2346
|
`Reverse path(s): ${paths.length}`,
|
|
2319
2347
|
`Shared upstream convergence: ${convergence.length}`
|
|
2320
2348
|
].join("\n");
|
|
2321
|
-
const artifacts = await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
|
|
2349
|
+
const artifacts = options.writeArtifacts === false ? statelessArtifacts() : await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
|
|
2322
2350
|
const evidence = artifactEvidence(artifacts);
|
|
2323
2351
|
if (options.caseId) {
|
|
2324
2352
|
const { EvidenceStore } = await Promise.resolve().then(() => require("./cases-Bz_9XKEw.cjs")).then((n) => n.cases_exports);
|
|
@@ -43,7 +43,7 @@ var AliasTracker = class {
|
|
|
43
43
|
return Object.fromEntries(entries.sort(([a], [b]) => a.localeCompare(b, void 0, { numeric: true })));
|
|
44
44
|
}
|
|
45
45
|
};
|
|
46
|
-
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 =
|
|
46
|
+
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS$1 = 10;
|
|
47
47
|
const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS$1 = 300 * 1e3;
|
|
48
48
|
const SCHEMA_QUERY_SET = [
|
|
49
49
|
{
|
|
@@ -820,6 +820,7 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
|
|
|
820
820
|
for (const flow of flows) byHop.set(flow.hop, (byHop.get(flow.hop) ?? 0) + 1);
|
|
821
821
|
const depositCount = continuation.depositAddresses.length;
|
|
822
822
|
const exchangeCount = continuation.exchangeAddresses.length;
|
|
823
|
+
const hasFiles = Object.values(files).some((value) => value.length > 0);
|
|
823
824
|
return [
|
|
824
825
|
`Trace complete for ${network}:${seedAddress}`,
|
|
825
826
|
"",
|
|
@@ -828,14 +829,16 @@ function summarize(seedAddress, network, flows, sourceMatches, reverseLeads, ali
|
|
|
828
829
|
`Exchange endpoints reached: ${exchangeCount}. Deposit candidate address(es): ${depositCount}.`,
|
|
829
830
|
`Traceback source path(s): ${sourceMatches.length}. Reverse 1-hop lead(s): ${reverseLeads.length}.`,
|
|
830
831
|
"",
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
832
|
+
hasFiles ? [
|
|
833
|
+
"Files written:",
|
|
834
|
+
`- schema: ${files.schema}`,
|
|
835
|
+
`- compact evidence JSON: ${files.compactEvidence}`,
|
|
836
|
+
`- graph JSON: ${files.graph}`,
|
|
837
|
+
`- graph HTML: ${files.graphHtml}`,
|
|
838
|
+
`- table CSV: ${files.table}`,
|
|
839
|
+
`- table HTML: ${files.tableHtml}`,
|
|
840
|
+
`- report: ${files.report}`
|
|
841
|
+
].join("\n") : "Files written: disabled by stateless proxy mode.",
|
|
839
842
|
"",
|
|
840
843
|
`Continuation hint: ${continuation.hint}`,
|
|
841
844
|
continuation.depositAddresses.length > 0 ? `Deposit candidates: ${continuation.depositAddresses.map((address) => aliases.alias(address) ?? address).join(", ")}` : "Deposit candidates: none reached in this bounded trace.",
|
|
@@ -851,9 +854,18 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
851
854
|
const perAddressLimit = clampInt$2(options.perAddressLimit, 5, 1, 10);
|
|
852
855
|
const minAmountSum = Math.max(0, options.minAmountSum ?? 0);
|
|
853
856
|
const evidenceSource = options.evidenceSource ?? "track_funds";
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
const
|
|
857
|
+
const writeArtifacts = options.writeArtifacts !== false;
|
|
858
|
+
if (!writeArtifacts && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
|
|
859
|
+
const paths = writeArtifacts ? workspaceOutputPaths() : void 0;
|
|
860
|
+
if (paths) await ensureDirs(paths);
|
|
861
|
+
const schemaResult = paths ? await loadOrCaptureTopologySchema(remoteClient, paths, network) : {
|
|
862
|
+
schema: {
|
|
863
|
+
schema: "chain-insights.runtime_graph_schema.v1",
|
|
864
|
+
network,
|
|
865
|
+
source: "stateless_proxy_mode"
|
|
866
|
+
},
|
|
867
|
+
filePath: "stateless://runtime-schema-not-written"
|
|
868
|
+
};
|
|
857
869
|
const { flows, deposits, sourceMatches, reverseLeads } = await collectProbeTrace(remoteClient, {
|
|
858
870
|
seedAddress,
|
|
859
871
|
network,
|
|
@@ -866,19 +878,21 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
866
878
|
const slug = `${(/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d{3}Z$/, "Z")}_${sanitizeSegment(seedAddress.slice(0, 16))}`;
|
|
867
879
|
const compact = probeEvidence(seedAddress, network, schemaResult.filePath, aliases, flows, deposits, sourceMatches, reverseLeads, evidenceSource);
|
|
868
880
|
const graph = buildGraph(seedAddress, network, flows, deposits, sourceMatches, reverseLeads);
|
|
869
|
-
const compactPath = path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`);
|
|
870
|
-
const graphPath = path.join(paths.reportGraphsRoot, `${slug}.graph.json`);
|
|
871
|
-
const graphHtmlPath = path.join(paths.reportsRoot, `${slug}.graph.html`);
|
|
872
|
-
const tablePath = path.join(paths.reportTablesRoot, `${slug}.flows.csv`);
|
|
873
|
-
const tableHtmlPath = path.join(paths.reportsRoot, `${slug}.table.html`);
|
|
874
|
-
const reportPath = path.join(paths.reportsRoot, `${slug}.trace-report.md`);
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
881
|
+
const compactPath = paths ? path.join(paths.reportTablesRoot, `${slug}.compact-evidence.json`) : "";
|
|
882
|
+
const graphPath = paths ? path.join(paths.reportGraphsRoot, `${slug}.graph.json`) : "";
|
|
883
|
+
const graphHtmlPath = paths ? path.join(paths.reportsRoot, `${slug}.graph.html`) : "";
|
|
884
|
+
const tablePath = paths ? path.join(paths.reportTablesRoot, `${slug}.flows.csv`) : "";
|
|
885
|
+
const tableHtmlPath = paths ? path.join(paths.reportsRoot, `${slug}.table.html`) : "";
|
|
886
|
+
const reportPath = paths ? path.join(paths.reportsRoot, `${slug}.trace-report.md`) : "";
|
|
887
|
+
if (paths) {
|
|
888
|
+
const { generateInlineGraphHtml } = await import("./html-generator-AowOmzyi.mjs").then((n) => n.n);
|
|
889
|
+
await writeFile(compactPath, JSON.stringify(compact, null, 2) + "\n", { mode: 384 });
|
|
890
|
+
await writeFile(graphPath, JSON.stringify(graph, null, 2) + "\n", { mode: 384 });
|
|
891
|
+
await writeFile(graphHtmlPath, generateInlineGraphHtml(graph), { mode: 384 });
|
|
892
|
+
await writeFile(tablePath, tableCsv(flows), { mode: 384 });
|
|
893
|
+
await writeFile(tableHtmlPath, buildTableHtml(seedAddress, network, flows, deposits, sourceMatches, reverseLeads), { mode: 384 });
|
|
894
|
+
await writeFile(reportPath, buildMarkdownReport(seedAddress, network, flows, deposits, sourceMatches, reverseLeads, aliases, graphPath, schemaResult.filePath), { mode: 384 });
|
|
895
|
+
}
|
|
882
896
|
if (options.caseId) {
|
|
883
897
|
const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
|
|
884
898
|
await EvidenceStore.append(options.caseId, {
|
|
@@ -937,7 +951,7 @@ async function runFundFlowProbe(remoteClient, _config, options) {
|
|
|
937
951
|
}
|
|
938
952
|
//#endregion
|
|
939
953
|
//#region src/investigation/stake-insights.ts
|
|
940
|
-
const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS =
|
|
954
|
+
const STAKE_INSIGHTS_QUERY_TIMEOUT_SECONDS = 10;
|
|
941
955
|
const STAKE_INSIGHTS_REQUEST_TIMEOUT_MS = 300 * 1e3;
|
|
942
956
|
function escapeCypherString$1(value) {
|
|
943
957
|
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
@@ -1297,7 +1311,8 @@ function summaryLines(network, subject, rows, totals, failures) {
|
|
|
1297
1311
|
async function stakeInsights(remoteClient, options) {
|
|
1298
1312
|
const { network, subject, depth } = validateOptions(options);
|
|
1299
1313
|
const { live, archive, failures, evidence } = collectRelationships(await callGraphBatch$1(remoteClient, network, [stakeRelationshipQuery("live_topology", subject, options, depth), stakeRelationshipQuery("archive_topology", subject, options, depth)]));
|
|
1300
|
-
|
|
1314
|
+
const successfulQueryCount = evidence.filter((entry) => entry["ok"] === true).length;
|
|
1315
|
+
if (live.length === 0 && archive.length === 0 && failures.length > 0 && successfulQueryCount === 0) throw new Error(`Stake insights unavailable: ${failures.map((failure) => `${failure.id}: ${failure.error}`).join("; ")}`);
|
|
1301
1316
|
const rows = live.length > 0 ? live : archive;
|
|
1302
1317
|
const totals = stakeTotals(rows);
|
|
1303
1318
|
const facts = {
|
|
@@ -1332,7 +1347,7 @@ async function stakeInsights(remoteClient, options) {
|
|
|
1332
1347
|
}
|
|
1333
1348
|
//#endregion
|
|
1334
1349
|
//#region src/investigation/public-tools.ts
|
|
1335
|
-
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS =
|
|
1350
|
+
const GRAPH_QUERY_BATCH_TIMEOUT_SECONDS = 10;
|
|
1336
1351
|
const GRAPH_QUERY_BATCH_REQUEST_TIMEOUT_MS = 300 * 1e3;
|
|
1337
1352
|
function escapeCypherString(value) {
|
|
1338
1353
|
return value.replaceAll("\\", "\\\\").replaceAll("\"", "\\\"");
|
|
@@ -1813,6 +1828,10 @@ function normalizeTraceGraphData(runs, network) {
|
|
|
1813
1828
|
}
|
|
1814
1829
|
function traceArtifactPointersFromRun(run) {
|
|
1815
1830
|
if (!run) return {};
|
|
1831
|
+
if (!Object.values(run.files).some((value) => value.length > 0)) return {
|
|
1832
|
+
artifacts_written: false,
|
|
1833
|
+
artifact_mode: "stateless"
|
|
1834
|
+
};
|
|
1816
1835
|
return {
|
|
1817
1836
|
graph_json: run.files.graph,
|
|
1818
1837
|
graph_html: run.files.graphHtml,
|
|
@@ -1822,6 +1841,12 @@ function traceArtifactPointersFromRun(run) {
|
|
|
1822
1841
|
report_md: run.files.report
|
|
1823
1842
|
};
|
|
1824
1843
|
}
|
|
1844
|
+
function statelessArtifacts() {
|
|
1845
|
+
return {
|
|
1846
|
+
artifacts_written: false,
|
|
1847
|
+
artifact_mode: "stateless"
|
|
1848
|
+
};
|
|
1849
|
+
}
|
|
1825
1850
|
function artifactEvidence(artifacts) {
|
|
1826
1851
|
return Object.entries(artifacts).filter((entry) => typeof entry[1] === "string" && entry[1].length > 0).map(([kind, filePath]) => ({
|
|
1827
1852
|
evidence_type: "artifact_pointer",
|
|
@@ -2026,7 +2051,8 @@ async function traceVictimFunds(remoteClient, config, options) {
|
|
|
2026
2051
|
perAddressLimit: options.perAddressLimit,
|
|
2027
2052
|
minAmountSum: options.minAmountSum,
|
|
2028
2053
|
includeDepositTraceback: false,
|
|
2029
|
-
evidenceSource: "trace_victim_funds"
|
|
2054
|
+
evidenceSource: "trace_victim_funds",
|
|
2055
|
+
writeArtifacts: options.writeArtifacts
|
|
2030
2056
|
})
|
|
2031
2057
|
});
|
|
2032
2058
|
return traceResultFromFundRuns("trace_victim_funds", "victim", network, runs, {
|
|
@@ -2054,7 +2080,8 @@ async function traceSuspectFunds(remoteClient, config, options) {
|
|
|
2054
2080
|
perAddressLimit: options.perAddressLimit,
|
|
2055
2081
|
minAmountSum: options.minAmountSum,
|
|
2056
2082
|
includeDepositTraceback: false,
|
|
2057
|
-
evidenceSource: "trace_suspect_funds"
|
|
2083
|
+
evidenceSource: "trace_suspect_funds",
|
|
2084
|
+
writeArtifacts: options.writeArtifacts
|
|
2058
2085
|
})
|
|
2059
2086
|
});
|
|
2060
2087
|
return traceResultFromFundRuns("trace_suspect_funds", "suspect", network, runs, {
|
|
@@ -2199,6 +2226,7 @@ async function traceDepositSources(remoteClient, _config, options) {
|
|
|
2199
2226
|
if (!network) throw new Error("network is required");
|
|
2200
2227
|
if (deposits.length < 1) throw new Error("deposit_addresses must contain at least 1 address");
|
|
2201
2228
|
if (deposits.length > 5) throw new Error("deposit_addresses cannot exceed 5 addresses");
|
|
2229
|
+
if (options.writeArtifacts === false && options.caseId) throw new Error("case_id requires workspace artifacts; omit case_id when CHAIN_INSIGHTS_MCP_PROXY_MODE=stateless");
|
|
2202
2230
|
const maxHops = clampInt(options.maxHops, 2, 1, 5);
|
|
2203
2231
|
const batch = await callGraphBatch(remoteClient, network, Array.from({ length: maxHops }, (_, index) => reverseDepositSourceQueryAtDepth(deposits, index + 1)));
|
|
2204
2232
|
const failures = [];
|
|
@@ -2316,7 +2344,7 @@ async function traceDepositSources(remoteClient, _config, options) {
|
|
|
2316
2344
|
`Reverse path(s): ${paths.length}`,
|
|
2317
2345
|
`Shared upstream convergence: ${convergence.length}`
|
|
2318
2346
|
].join("\n");
|
|
2319
|
-
const artifacts = await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
|
|
2347
|
+
const artifacts = options.writeArtifacts === false ? statelessArtifacts() : await writeTraceSourceArtifacts("trace_deposit_sources", network, graphData, rows, summaryText);
|
|
2320
2348
|
const evidence = artifactEvidence(artifacts);
|
|
2321
2349
|
if (options.caseId) {
|
|
2322
2350
|
const { EvidenceStore } = await import("./cases-TVcAifxu.mjs").then((n) => n.t);
|
|
@@ -2391,4 +2419,4 @@ async function traceDepositSources(remoteClient, _config, options) {
|
|
|
2391
2419
|
//#endregion
|
|
2392
2420
|
export { addressRisk, stakeInsights, traceDepositSources, traceSuspectFunds, traceVictimFunds };
|
|
2393
2421
|
|
|
2394
|
-
//# sourceMappingURL=public-tools-
|
|
2422
|
+
//# sourceMappingURL=public-tools-CvlZcysd.mjs.map
|