chain-insights 0.3.9 → 0.3.11
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/{app-BxojXjtB.cjs → app-DjJn3irw.cjs} +1 -1
- package/dist/{app-CRd39JJ8.mjs → app-norpwdou.mjs} +2 -2
- package/dist/{app-CRd39JJ8.mjs.map → app-norpwdou.mjs.map} +1 -1
- package/dist/{artifact-server-XbN16DwU.cjs → artifact-server-C6_gtIql.cjs} +1 -1
- package/dist/{artifact-server-CP6LXQ9d.mjs → artifact-server-DHPM0lxS.mjs} +2 -2
- package/dist/{artifact-server-CP6LXQ9d.mjs.map → artifact-server-DHPM0lxS.mjs.map} +1 -1
- package/dist/cli.cjs +18 -18
- package/dist/cli.mjs +18 -18
- package/dist/{config-Drgc2HuF.mjs → config-C6zM8Xir.mjs} +3 -3
- package/dist/{config-Drgc2HuF.mjs.map → config-C6zM8Xir.mjs.map} +1 -1
- package/dist/{config-BwVx19Og.cjs → config-CkW404Cs.cjs} +2 -2
- package/dist/index.cjs +3 -3
- package/dist/index.mjs +3 -3
- package/dist/{init-CKQ6F07J.mjs → init-uAmPfio2.mjs} +2 -2
- package/dist/{init-CKQ6F07J.mjs.map → init-uAmPfio2.mjs.map} +1 -1
- package/dist/{init-Dhw8F23z.cjs → init-vj2v5PMP.cjs} +1 -1
- package/dist/{mcp-endpoint-DHs1cRFH.mjs → mcp-endpoint-QQ5Lbqc2.mjs} +5 -2
- package/dist/mcp-endpoint-QQ5Lbqc2.mjs.map +1 -0
- package/dist/{mcp-endpoint-BaV8h_lq.cjs → mcp-endpoint-cQIZSjkK.cjs} +4 -1
- package/dist/mcp-proxy.cjs +3 -3
- package/dist/mcp-proxy.mjs +3 -3
- package/dist/{runner-CVnjpqc-.mjs → runner-B9fXAP0t.mjs} +2 -2
- package/dist/{runner-CVnjpqc-.mjs.map → runner-B9fXAP0t.mjs.map} +1 -1
- package/dist/{runner-bLy0pTr_.cjs → runner-CcZCrrkn.cjs} +1 -1
- package/dist/{schema-BFEWhzg7.mjs → schema-D_qwaQA5.mjs} +2 -2
- package/dist/{schema-BFEWhzg7.mjs.map → schema-D_qwaQA5.mjs.map} +1 -1
- package/dist/{schema-Vl9yuOFO.cjs → schema-Dr6JXSOF.cjs} +1 -1
- package/dist/{server-BXLX2j_A.mjs → server-86dyCsJO.mjs} +2 -2
- package/dist/{server-BXLX2j_A.mjs.map → server-86dyCsJO.mjs.map} +1 -1
- package/dist/{server-BqVdWath.cjs → server-B2NFmnCM.cjs} +1 -1
- package/package.json +1 -1
- package/dist/mcp-endpoint-DHs1cRFH.mjs.map +0 -1
|
@@ -100,7 +100,7 @@ function createApp() {
|
|
|
100
100
|
ts: Date.now()
|
|
101
101
|
}));
|
|
102
102
|
app.get("/status", async (c) => {
|
|
103
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
103
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
104
104
|
const config = await loadConfig();
|
|
105
105
|
return c.json({
|
|
106
106
|
dataDir: config.dataDir,
|
|
@@ -97,7 +97,7 @@ function createApp() {
|
|
|
97
97
|
ts: Date.now()
|
|
98
98
|
}));
|
|
99
99
|
app.get("/status", async (c) => {
|
|
100
|
-
const { loadConfig } = await import("./config-
|
|
100
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
101
101
|
const config = await loadConfig();
|
|
102
102
|
return c.json({
|
|
103
103
|
dataDir: config.dataDir,
|
|
@@ -152,4 +152,4 @@ function createApp() {
|
|
|
152
152
|
//#endregion
|
|
153
153
|
export { createApp as t };
|
|
154
154
|
|
|
155
|
-
//# sourceMappingURL=app-
|
|
155
|
+
//# sourceMappingURL=app-norpwdou.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-CRd39JJ8.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\n\nconst WORKSPACE_TREE_ROOTS = ['cases', 'reports', '.chain-insights/schema']\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n path: string\n type: 'file' | 'directory' | 'symlink'\n size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n const relative = path.relative(path.resolve(root), path.resolve(target))\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n try {\n const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n return withinRoot(realRoot, realTarget)\n } catch {\n return false\n }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n workspaceRoot: string,\n roots = WORKSPACE_TREE_ROOTS,\n maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n const entries: WorkspaceTreeEntry[] = []\n const root = path.resolve(workspaceRoot)\n\n async function visit(target: string, depth: number): Promise<void> {\n const resolved = path.resolve(target)\n if (!withinRoot(root, resolved)) return\n\n let info: Awaited<ReturnType<typeof lstat>>\n try {\n info = await lstat(resolved)\n } catch {\n return\n }\n\n const type = info.isSymbolicLink() ? 'symlink' : info.isDirectory() ? 'directory' : info.isFile() ? 'file' : null\n if (!type) return\n\n const entry: WorkspaceTreeEntry = {\n path: toWorkspaceRelative(root, resolved),\n type,\n }\n if (type === 'file') entry.size = info.size\n entries.push(entry)\n\n if (type !== 'directory' || depth >= maxDepth) return\n if (!await realPathWithinRoot(root, resolved)) return\n\n let children: string[]\n try {\n children = await readdir(resolved)\n } catch {\n return\n }\n\n for (const child of children.sort()) {\n await visit(path.join(resolved, child), depth + 1)\n }\n }\n\n for (const rootName of roots) {\n const target = path.resolve(root, rootName)\n if (withinRoot(root, target)) await visit(target, 0)\n }\n\n return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n const home = os.homedir()\n const filename = `${vizId}.html`\n\n // 1. Check central standalone directory first (fast, single path)\n const centralPath = path.join(home, '.chain-insights', 'viz', filename)\n try {\n return await readFile(centralPath, 'utf-8')\n } catch { /* not found here, continue */ }\n\n // 2. Check per-case directory using vizId prefix (case-based vizs use <caseId>_<timestamp>)\n // The vizId for case-based vizs is formatted as <case-id>_<timestamp>,\n // so extract the case-id prefix to check its directory first.\n const underscoreIdx = vizId.lastIndexOf('_')\n if (underscoreIdx > 0) {\n const possibleCaseId = vizId.substring(0, underscoreIdx)\n const casePath = path.join(home, '.chain-insights', 'cases', possibleCaseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not found here, continue */ }\n }\n\n // 3. Fallback: scan all case directories (CONTEXT.md: ~/.chain-insights/cases/<case-id>/viz/)\n const casesDir = path.join(home, '.chain-insights', 'cases')\n try {\n const cases = await readdir(casesDir)\n for (const caseId of cases) {\n const casePath = path.join(casesDir, caseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not in this case dir */ }\n }\n } catch { /* cases dir doesn't exist */ }\n\n return null\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n return (\n filename.endsWith('.graph.json') &&\n /^[A-Za-z0-9._-]+$/.test(filename) &&\n !filename.includes('..') &&\n !filename.includes('/') &&\n !filename.includes('\\\\')\n )\n}\n\nexport function createApp(): Hono {\n const app = new Hono()\n\n app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n app.get('/status', async (c) => {\n const { loadConfig } = await import('../config/index.js')\n const config = await loadConfig()\n return c.json({\n dataDir: config.dataDir,\n graphMcpMode: config.graphMcpMode,\n server: 'running',\n })\n })\n\n app.get('/viz/:id', async (c) => {\n const id = c.req.param('id')\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n return c.json({ error: 'Invalid visualization ID' }, 400)\n }\n const html = await findVizHtml(id)\n if (!html) {\n return c.json({ error: 'Visualization not found' }, 404)\n }\n return c.html(html)\n })\n\n app.get('/graph-reports/:filename', async (c) => {\n const filename = c.req.param('filename')\n if (!isSafeGraphReportFilename(filename)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n if (!await realPathWithinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n\n try {\n const graph = await readFile(graphPath, 'utf-8')\n return c.body(graph, 200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n } catch {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n })\n\n app.get('/graph-reports/*', (c) => {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n })\n\n app.get('/workspace/tree', async (c) => {\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n return c.json({\n schema: 'chain-insights.workspace-tree.v1',\n root: paths.root,\n entries,\n })\n })\n\n app.onError((err, c) => {\n console.error(err)\n return c.json({ error: 'Internal server error' }, 500)\n })\n\n return app\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"app-norpwdou.mjs","names":[],"sources":["../src/server/app.ts"],"sourcesContent":["import { Hono } from 'hono'\nimport { lstat, readFile, readdir, realpath } from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\n\nconst WORKSPACE_TREE_ROOTS = ['cases', 'reports', '.chain-insights/schema']\nconst WORKSPACE_TREE_MAX_DEPTH = 4\n\ninterface WorkspaceTreeEntry {\n path: string\n type: 'file' | 'directory' | 'symlink'\n size?: number\n}\n\nfunction withinRoot(root: string, target: string): boolean {\n const relative = path.relative(path.resolve(root), path.resolve(target))\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))\n}\n\nasync function realPathWithinRoot(root: string, target: string): Promise<boolean> {\n try {\n const [realRoot, realTarget] = await Promise.all([realpath(root), realpath(target)])\n return withinRoot(realRoot, realTarget)\n } catch {\n return false\n }\n}\n\nfunction toWorkspaceRelative(root: string, target: string): string {\n return path.relative(root, target).split(path.sep).join('/')\n}\n\nasync function listWorkspaceEntries(\n workspaceRoot: string,\n roots = WORKSPACE_TREE_ROOTS,\n maxDepth = WORKSPACE_TREE_MAX_DEPTH\n): Promise<WorkspaceTreeEntry[]> {\n const entries: WorkspaceTreeEntry[] = []\n const root = path.resolve(workspaceRoot)\n\n async function visit(target: string, depth: number): Promise<void> {\n const resolved = path.resolve(target)\n if (!withinRoot(root, resolved)) return\n\n let info: Awaited<ReturnType<typeof lstat>>\n try {\n info = await lstat(resolved)\n } catch {\n return\n }\n\n const type = info.isSymbolicLink() ? 'symlink' : info.isDirectory() ? 'directory' : info.isFile() ? 'file' : null\n if (!type) return\n\n const entry: WorkspaceTreeEntry = {\n path: toWorkspaceRelative(root, resolved),\n type,\n }\n if (type === 'file') entry.size = info.size\n entries.push(entry)\n\n if (type !== 'directory' || depth >= maxDepth) return\n if (!await realPathWithinRoot(root, resolved)) return\n\n let children: string[]\n try {\n children = await readdir(resolved)\n } catch {\n return\n }\n\n for (const child of children.sort()) {\n await visit(path.join(resolved, child), depth + 1)\n }\n }\n\n for (const rootName of roots) {\n const target = path.resolve(root, rootName)\n if (withinRoot(root, target)) await visit(target, 0)\n }\n\n return entries\n}\n\nasync function findVizHtml(vizId: string): Promise<string | null> {\n const home = os.homedir()\n const filename = `${vizId}.html`\n\n // 1. Check central standalone directory first (fast, single path)\n const centralPath = path.join(home, '.chain-insights', 'viz', filename)\n try {\n return await readFile(centralPath, 'utf-8')\n } catch { /* not found here, continue */ }\n\n // 2. Check per-case directory using vizId prefix (case-based vizs use <caseId>_<timestamp>)\n // The vizId for case-based vizs is formatted as <case-id>_<timestamp>,\n // so extract the case-id prefix to check its directory first.\n const underscoreIdx = vizId.lastIndexOf('_')\n if (underscoreIdx > 0) {\n const possibleCaseId = vizId.substring(0, underscoreIdx)\n const casePath = path.join(home, '.chain-insights', 'cases', possibleCaseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not found here, continue */ }\n }\n\n // 3. Fallback: scan all case directories (CONTEXT.md: ~/.chain-insights/cases/<case-id>/viz/)\n const casesDir = path.join(home, '.chain-insights', 'cases')\n try {\n const cases = await readdir(casesDir)\n for (const caseId of cases) {\n const casePath = path.join(casesDir, caseId, 'viz', filename)\n try {\n return await readFile(casePath, 'utf-8')\n } catch { /* not in this case dir */ }\n }\n } catch { /* cases dir doesn't exist */ }\n\n return null\n}\n\nfunction isSafeGraphReportFilename(filename: string): boolean {\n return (\n filename.endsWith('.graph.json') &&\n /^[A-Za-z0-9._-]+$/.test(filename) &&\n !filename.includes('..') &&\n !filename.includes('/') &&\n !filename.includes('\\\\')\n )\n}\n\nexport function createApp(): Hono {\n const app = new Hono()\n\n app.get('/health', (c) => c.json({ ok: true, ts: Date.now() }))\n\n app.get('/status', async (c) => {\n const { loadConfig } = await import('../config/index.js')\n const config = await loadConfig()\n return c.json({\n dataDir: config.dataDir,\n graphMcpMode: config.graphMcpMode,\n server: 'running',\n })\n })\n\n app.get('/viz/:id', async (c) => {\n const id = c.req.param('id')\n if (!/^[a-zA-Z0-9_-]+$/.test(id)) {\n return c.json({ error: 'Invalid visualization ID' }, 400)\n }\n const html = await findVizHtml(id)\n if (!html) {\n return c.json({ error: 'Visualization not found' }, 404)\n }\n return c.html(html)\n })\n\n app.get('/graph-reports/:filename', async (c) => {\n const filename = c.req.param('filename')\n if (!isSafeGraphReportFilename(filename)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const graphPath = path.resolve(paths.reportGraphsRoot, filename)\n if (!withinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n }\n if (!await realPathWithinRoot(paths.reportGraphsRoot, graphPath)) {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n\n try {\n const graph = await readFile(graphPath, 'utf-8')\n return c.body(graph, 200, {\n 'Content-Type': 'application/json',\n 'Access-Control-Allow-Origin': '*',\n })\n } catch {\n return c.json({ error: 'Graph report not found' }, 404)\n }\n })\n\n app.get('/graph-reports/*', (c) => {\n return c.json({ error: 'Invalid graph report filename' }, 400)\n })\n\n app.get('/workspace/tree', async (c) => {\n const { workspaceOutputPaths } = await import('../workspace/output-root.js')\n const paths = workspaceOutputPaths()\n const entries = await listWorkspaceEntries(paths.root, WORKSPACE_TREE_ROOTS)\n return c.json({\n schema: 'chain-insights.workspace-tree.v1',\n root: paths.root,\n entries,\n })\n })\n\n app.onError((err, c) => {\n console.error(err)\n return c.json({ error: 'Internal server error' }, 500)\n })\n\n return app\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as createApp } from "./app-
|
|
1
|
+
import { t as createApp } from "./app-norpwdou.mjs";
|
|
2
2
|
import { serve } from "@hono/node-server";
|
|
3
3
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
4
4
|
//#region src/mcp/artifact-server.ts
|
|
@@ -45,4 +45,4 @@ async function ensureArtifactServer(port) {
|
|
|
45
45
|
//#endregion
|
|
46
46
|
export { ensureArtifactServer };
|
|
47
47
|
|
|
48
|
-
//# sourceMappingURL=artifact-server-
|
|
48
|
+
//# sourceMappingURL=artifact-server-DHPM0lxS.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"artifact-server-
|
|
1
|
+
{"version":3,"file":"artifact-server-DHPM0lxS.mjs","names":["delay"],"sources":["../src/mcp/artifact-server.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { setTimeout as delay } from 'node:timers/promises'\nimport { createApp } from '../server/app.js'\n\ntype ArtifactServer = ReturnType<typeof serve>\n\nconst servers = new Map<number, ArtifactServer>()\n\nasync function isHealthy(port: number): Promise<boolean> {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(), 500)\n try {\n const response = await fetch(`http://127.0.0.1:${port}/health`, {\n signal: controller.signal,\n })\n return response.ok\n } catch {\n return false\n } finally {\n clearTimeout(timeout)\n }\n}\n\nasync function waitUntilHealthy(port: number): Promise<void> {\n for (let attempt = 0; attempt < 20; attempt += 1) {\n if (await isHealthy(port)) return\n await delay(50)\n }\n throw new Error(`Graph report server did not become healthy on 127.0.0.1:${port}`)\n}\n\nexport async function ensureArtifactServer(port: number): Promise<void> {\n if (servers.has(port)) return\n if (await isHealthy(port)) return\n\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1',\n port,\n })\n servers.set(port, server)\n server.on('error', (err) => {\n servers.delete(port)\n process.stderr.write(`Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`)\n })\n\n try {\n await waitUntilHealthy(port)\n } catch (err) {\n servers.delete(port)\n server.close()\n throw err\n }\n}\n\nexport function closeArtifactServers(): void {\n for (const [port, server] of servers.entries()) {\n server.close()\n servers.delete(port)\n }\n}\n"],"mappings":";;;;AAMA,MAAM,0BAAU,IAAI,IAA4B;AAEhD,eAAe,UAAU,MAAgC;CACvD,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,GAAG,GAAG;CACxD,IAAI;EAIF,QAAO,MAHgB,MAAM,oBAAoB,KAAK,UAAU,EAC9D,QAAQ,WAAW,OACrB,CAAC,GACe;CAClB,QAAQ;EACN,OAAO;CACT,UAAU;EACR,aAAa,OAAO;CACtB;AACF;AAEA,eAAe,iBAAiB,MAA6B;CAC3D,KAAK,IAAI,UAAU,GAAG,UAAU,IAAI,WAAW,GAAG;EAChD,IAAI,MAAM,UAAU,IAAI,GAAG;EAC3B,MAAMA,aAAM,EAAE;CAChB;CACA,MAAM,IAAI,MAAM,2DAA2D,MAAM;AACnF;AAEA,eAAsB,qBAAqB,MAA6B;CACtE,IAAI,QAAQ,IAAI,IAAI,GAAG;CACvB,IAAI,MAAM,UAAU,IAAI,GAAG;CAG3B,MAAM,SAAS,MAAM;EACnB,OAFU,UAED,EAAE;EACX,UAAU;EACV;CACF,CAAC;CACD,QAAQ,IAAI,MAAM,MAAM;CACxB,OAAO,GAAG,UAAU,QAAQ;EAC1B,QAAQ,OAAO,IAAI;EACnB,QAAQ,OAAO,MAAM,0DAA0D,KAAK,IAAK,IAAc,QAAQ,GAAG;CACpH,CAAC;CAED,IAAI;EACF,MAAM,iBAAiB,IAAI;CAC7B,SAAS,KAAK;EACZ,QAAQ,OAAO,IAAI;EACnB,OAAO,MAAM;EACb,MAAM;CACR;AACF"}
|
package/dist/cli.cjs
CHANGED
|
@@ -70,7 +70,7 @@ function optionalNumberArg(value, name) {
|
|
|
70
70
|
throw new Error(`Invalid number for ${name}: ${String(value)}`);
|
|
71
71
|
}
|
|
72
72
|
async function withGraphMcpClient(name, fn) {
|
|
73
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
73
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
74
74
|
const config = await loadConfig();
|
|
75
75
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-Y_zqKqJT.cjs")).then((n) => n.client_exports);
|
|
76
76
|
const paymentFetch = await createConfiguredGraphMcpFetch(config);
|
|
@@ -91,7 +91,7 @@ function printMcpTextContent(result) {
|
|
|
91
91
|
for (const item of result.content ?? []) if (item.type === "text") console.log(item.text);
|
|
92
92
|
}
|
|
93
93
|
async function printNetworkCapabilities(opts) {
|
|
94
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
94
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
95
95
|
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-DOa6EFO-.cjs"));
|
|
96
96
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
97
97
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
@@ -109,7 +109,7 @@ program.command("serve").description("Start local visualization server").option(
|
|
|
109
109
|
try {
|
|
110
110
|
const { requireWorkspaceRoot } = await Promise.resolve().then(() => require("./output-root-YIbl6PwF.cjs")).then((n) => n.output_root_exports);
|
|
111
111
|
const workspaceRoot = requireWorkspaceRoot();
|
|
112
|
-
const { startServer } = await Promise.resolve().then(() => require("./server-
|
|
112
|
+
const { startServer } = await Promise.resolve().then(() => require("./server-B2NFmnCM.cjs")).then((n) => n.server_exports);
|
|
113
113
|
console.log(`Workspace: ${workspaceRoot}`);
|
|
114
114
|
startServer(parseInt(opts.port, 10));
|
|
115
115
|
} catch (err) {
|
|
@@ -118,7 +118,7 @@ program.command("serve").description("Start local visualization server").option(
|
|
|
118
118
|
}
|
|
119
119
|
});
|
|
120
120
|
program.command("status").description("Show toolkit status and configuration").action(async () => {
|
|
121
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
121
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
122
122
|
const { findActiveWorkspace, activeDataDir } = await Promise.resolve().then(() => require("./active-BVr55kvW.cjs")).then((n) => n.active_exports);
|
|
123
123
|
const config = await loadConfig();
|
|
124
124
|
const workspace = findActiveWorkspace();
|
|
@@ -175,7 +175,7 @@ program.command("obsidian").description("Manage the local Obsidian investigation
|
|
|
175
175
|
}));
|
|
176
176
|
program.command("debug").description("Configure Graph MCP debug mode").addCommand(new commander.Command("on").description("Enable Graph MCP debug mode without x402 payments").requiredOption("--token <token>", "Debug bearer token").option("--endpoint <url>", "Graph MCP endpoint").action(async (opts) => {
|
|
177
177
|
try {
|
|
178
|
-
const { saveConfig } = await Promise.resolve().then(() => require("./config-
|
|
178
|
+
const { saveConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
179
179
|
await saveConfig({
|
|
180
180
|
graphMcpMode: "debug",
|
|
181
181
|
graphMcpAuthToken: opts.token,
|
|
@@ -190,7 +190,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
|
|
|
190
190
|
}
|
|
191
191
|
})).addCommand(new commander.Command("off").description("Disable Graph MCP debug mode and use paid x402 calls").action(async () => {
|
|
192
192
|
try {
|
|
193
|
-
const { saveConfig } = await Promise.resolve().then(() => require("./config-
|
|
193
|
+
const { saveConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
194
194
|
await saveConfig({
|
|
195
195
|
graphMcpMode: "paid",
|
|
196
196
|
graphMcpAuthToken: ""
|
|
@@ -203,7 +203,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
|
|
|
203
203
|
}
|
|
204
204
|
})).addCommand(new commander.Command("status").description("Show Graph MCP payment/debug mode").action(async () => {
|
|
205
205
|
try {
|
|
206
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
206
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
207
207
|
const config = await loadConfig();
|
|
208
208
|
console.log(`Graph MCP mode: ${config.graphMcpMode}`);
|
|
209
209
|
console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
|
|
@@ -218,7 +218,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
218
218
|
try {
|
|
219
219
|
const normalizedKey = key.trim();
|
|
220
220
|
if (!normalizedKey) throw new Error("Test access key is required");
|
|
221
|
-
const { saveConfig } = await Promise.resolve().then(() => require("./config-
|
|
221
|
+
const { saveConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
222
222
|
await saveConfig({
|
|
223
223
|
graphMcpMode: "debug",
|
|
224
224
|
graphMcpAuthToken: normalizedKey,
|
|
@@ -233,7 +233,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
233
233
|
}
|
|
234
234
|
})).addCommand(new commander.Command("clear").description("Remove the Graph MCP test access key and use paid x402 calls").action(async () => {
|
|
235
235
|
try {
|
|
236
|
-
const { saveConfig } = await Promise.resolve().then(() => require("./config-
|
|
236
|
+
const { saveConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
237
237
|
await saveConfig({
|
|
238
238
|
graphMcpMode: "paid",
|
|
239
239
|
graphMcpAuthToken: ""
|
|
@@ -246,7 +246,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
246
246
|
}
|
|
247
247
|
})).addCommand(new commander.Command("status").description("Show Graph MCP test access key status").action(async () => {
|
|
248
248
|
try {
|
|
249
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
249
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
250
250
|
const config = await loadConfig();
|
|
251
251
|
console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
|
|
252
252
|
console.log(`Access key: ${config.graphMcpAuthToken?.trim() ? "configured" : "not configured"}`);
|
|
@@ -258,7 +258,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
258
258
|
}));
|
|
259
259
|
program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
|
|
260
260
|
try {
|
|
261
|
-
const { initWorkspace } = await Promise.resolve().then(() => require("./init-
|
|
261
|
+
const { initWorkspace } = await Promise.resolve().then(() => require("./init-vj2v5PMP.cjs"));
|
|
262
262
|
const result = await initWorkspace({
|
|
263
263
|
targetDir: dir,
|
|
264
264
|
force: opts.force
|
|
@@ -295,8 +295,8 @@ program.command("setup").description("Configure external MCP clients").addComman
|
|
|
295
295
|
}
|
|
296
296
|
}));
|
|
297
297
|
program.command("config").description("Read or write configuration values").addCommand(new commander.Command("get").argument("<key>", "Config key to read").action(async (key) => {
|
|
298
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
299
|
-
const { CONFIG_KEYS } = await Promise.resolve().then(() => require("./schema-
|
|
298
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
299
|
+
const { CONFIG_KEYS } = await Promise.resolve().then(() => require("./schema-Dr6JXSOF.cjs")).then((n) => n.schema_exports);
|
|
300
300
|
if (!CONFIG_KEYS.includes(key)) {
|
|
301
301
|
console.error(`Unknown config key: ${key}`);
|
|
302
302
|
process.exit(1);
|
|
@@ -316,8 +316,8 @@ program.command("config").description("Read or write configuration values").addC
|
|
|
316
316
|
}
|
|
317
317
|
return;
|
|
318
318
|
}
|
|
319
|
-
const { loadConfig, saveConfig } = await Promise.resolve().then(() => require("./config-
|
|
320
|
-
const { CONFIG_KEYS, DEFAULT_CONFIG } = await Promise.resolve().then(() => require("./schema-
|
|
319
|
+
const { loadConfig, saveConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
320
|
+
const { CONFIG_KEYS, DEFAULT_CONFIG } = await Promise.resolve().then(() => require("./schema-Dr6JXSOF.cjs")).then((n) => n.schema_exports);
|
|
321
321
|
const current = await loadConfig();
|
|
322
322
|
if (!CONFIG_KEYS.includes(key)) {
|
|
323
323
|
console.error(`Unknown config key: ${key}`);
|
|
@@ -409,7 +409,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
|
|
|
409
409
|
const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-CJk1EL3L.cjs"));
|
|
410
410
|
const { formatToolsTable } = await Promise.resolve().then(() => require("./format-9NLBykEL.cjs"));
|
|
411
411
|
const { visibleRemoteTools } = await Promise.resolve().then(() => require("./tool-visibility-Buq7YdUZ.cjs")).then((n) => n.tool_visibility_exports);
|
|
412
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
412
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
413
413
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-Y_zqKqJT.cjs")).then((n) => n.client_exports);
|
|
414
414
|
const config = await loadConfig();
|
|
415
415
|
const graphMcpEndpoint = resolveGraphMcpEndpoint(config);
|
|
@@ -877,7 +877,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
|
|
|
877
877
|
console.error(`Invalid --from value: "${opts.from}". Must be a positive integer.`);
|
|
878
878
|
process.exit(1);
|
|
879
879
|
}
|
|
880
|
-
const { PlaybookRunner } = await Promise.resolve().then(() => require("./runner-
|
|
880
|
+
const { PlaybookRunner } = await Promise.resolve().then(() => require("./runner-CcZCrrkn.cjs"));
|
|
881
881
|
await PlaybookRunner.run(definition, {
|
|
882
882
|
caseId: opts.case,
|
|
883
883
|
from: fromN,
|
|
@@ -932,7 +932,7 @@ program.command("viz").description("Generate money flow visualization").argument
|
|
|
932
932
|
caseId,
|
|
933
933
|
dataFile: opts.data
|
|
934
934
|
});
|
|
935
|
-
const { startServer } = await Promise.resolve().then(() => require("./server-
|
|
935
|
+
const { startServer } = await Promise.resolve().then(() => require("./server-B2NFmnCM.cjs")).then((n) => n.server_exports);
|
|
936
936
|
const port = parseInt(opts.port, 10);
|
|
937
937
|
startServer(port);
|
|
938
938
|
const url = `http://127.0.0.1:${port}/viz/${result.vizId}`;
|
package/dist/cli.mjs
CHANGED
|
@@ -68,7 +68,7 @@ function optionalNumberArg(value, name) {
|
|
|
68
68
|
throw new Error(`Invalid number for ${name}: ${String(value)}`);
|
|
69
69
|
}
|
|
70
70
|
async function withGraphMcpClient(name, fn) {
|
|
71
|
-
const { loadConfig } = await import("./config-
|
|
71
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
72
72
|
const config = await loadConfig();
|
|
73
73
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
|
|
74
74
|
const paymentFetch = await createConfiguredGraphMcpFetch(config);
|
|
@@ -89,7 +89,7 @@ function printMcpTextContent(result) {
|
|
|
89
89
|
for (const item of result.content ?? []) if (item.type === "text") console.log(item.text);
|
|
90
90
|
}
|
|
91
91
|
async function printNetworkCapabilities(opts) {
|
|
92
|
-
const { loadConfig } = await import("./config-
|
|
92
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
93
93
|
const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-BCvkTkIu.mjs");
|
|
94
94
|
const document = await fetchNetworkCapabilities(await loadConfig());
|
|
95
95
|
if (opts.json) console.log(JSON.stringify(document, null, 2));
|
|
@@ -107,7 +107,7 @@ program.command("serve").description("Start local visualization server").option(
|
|
|
107
107
|
try {
|
|
108
108
|
const { requireWorkspaceRoot } = await import("./output-root-BRhzhhXZ.mjs").then((n) => n.t);
|
|
109
109
|
const workspaceRoot = requireWorkspaceRoot();
|
|
110
|
-
const { startServer } = await import("./server-
|
|
110
|
+
const { startServer } = await import("./server-86dyCsJO.mjs").then((n) => n.t);
|
|
111
111
|
console.log(`Workspace: ${workspaceRoot}`);
|
|
112
112
|
startServer(parseInt(opts.port, 10));
|
|
113
113
|
} catch (err) {
|
|
@@ -116,7 +116,7 @@ program.command("serve").description("Start local visualization server").option(
|
|
|
116
116
|
}
|
|
117
117
|
});
|
|
118
118
|
program.command("status").description("Show toolkit status and configuration").action(async () => {
|
|
119
|
-
const { loadConfig } = await import("./config-
|
|
119
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
120
120
|
const { findActiveWorkspace, activeDataDir } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
|
|
121
121
|
const config = await loadConfig();
|
|
122
122
|
const workspace = findActiveWorkspace();
|
|
@@ -173,7 +173,7 @@ program.command("obsidian").description("Manage the local Obsidian investigation
|
|
|
173
173
|
}));
|
|
174
174
|
program.command("debug").description("Configure Graph MCP debug mode").addCommand(new Command("on").description("Enable Graph MCP debug mode without x402 payments").requiredOption("--token <token>", "Debug bearer token").option("--endpoint <url>", "Graph MCP endpoint").action(async (opts) => {
|
|
175
175
|
try {
|
|
176
|
-
const { saveConfig } = await import("./config-
|
|
176
|
+
const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
177
177
|
await saveConfig({
|
|
178
178
|
graphMcpMode: "debug",
|
|
179
179
|
graphMcpAuthToken: opts.token,
|
|
@@ -188,7 +188,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
|
|
|
188
188
|
}
|
|
189
189
|
})).addCommand(new Command("off").description("Disable Graph MCP debug mode and use paid x402 calls").action(async () => {
|
|
190
190
|
try {
|
|
191
|
-
const { saveConfig } = await import("./config-
|
|
191
|
+
const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
192
192
|
await saveConfig({
|
|
193
193
|
graphMcpMode: "paid",
|
|
194
194
|
graphMcpAuthToken: ""
|
|
@@ -201,7 +201,7 @@ program.command("debug").description("Configure Graph MCP debug mode").addComman
|
|
|
201
201
|
}
|
|
202
202
|
})).addCommand(new Command("status").description("Show Graph MCP payment/debug mode").action(async () => {
|
|
203
203
|
try {
|
|
204
|
-
const { loadConfig } = await import("./config-
|
|
204
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
205
205
|
const config = await loadConfig();
|
|
206
206
|
console.log(`Graph MCP mode: ${config.graphMcpMode}`);
|
|
207
207
|
console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
|
|
@@ -216,7 +216,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
216
216
|
try {
|
|
217
217
|
const normalizedKey = key.trim();
|
|
218
218
|
if (!normalizedKey) throw new Error("Test access key is required");
|
|
219
|
-
const { saveConfig } = await import("./config-
|
|
219
|
+
const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
220
220
|
await saveConfig({
|
|
221
221
|
graphMcpMode: "debug",
|
|
222
222
|
graphMcpAuthToken: normalizedKey,
|
|
@@ -231,7 +231,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
231
231
|
}
|
|
232
232
|
})).addCommand(new Command("clear").description("Remove the Graph MCP test access key and use paid x402 calls").action(async () => {
|
|
233
233
|
try {
|
|
234
|
-
const { saveConfig } = await import("./config-
|
|
234
|
+
const { saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
235
235
|
await saveConfig({
|
|
236
236
|
graphMcpMode: "paid",
|
|
237
237
|
graphMcpAuthToken: ""
|
|
@@ -244,7 +244,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
244
244
|
}
|
|
245
245
|
})).addCommand(new Command("status").description("Show Graph MCP test access key status").action(async () => {
|
|
246
246
|
try {
|
|
247
|
-
const { loadConfig } = await import("./config-
|
|
247
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
248
248
|
const config = await loadConfig();
|
|
249
249
|
console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
|
|
250
250
|
console.log(`Access key: ${config.graphMcpAuthToken?.trim() ? "configured" : "not configured"}`);
|
|
@@ -256,7 +256,7 @@ program.command("access-key").description("Configure Graph MCP test access key m
|
|
|
256
256
|
}));
|
|
257
257
|
program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
|
|
258
258
|
try {
|
|
259
|
-
const { initWorkspace } = await import("./init-
|
|
259
|
+
const { initWorkspace } = await import("./init-uAmPfio2.mjs");
|
|
260
260
|
const result = await initWorkspace({
|
|
261
261
|
targetDir: dir,
|
|
262
262
|
force: opts.force
|
|
@@ -293,8 +293,8 @@ program.command("setup").description("Configure external MCP clients").addComman
|
|
|
293
293
|
}
|
|
294
294
|
}));
|
|
295
295
|
program.command("config").description("Read or write configuration values").addCommand(new Command("get").argument("<key>", "Config key to read").action(async (key) => {
|
|
296
|
-
const { loadConfig } = await import("./config-
|
|
297
|
-
const { CONFIG_KEYS } = await import("./schema-
|
|
296
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
297
|
+
const { CONFIG_KEYS } = await import("./schema-D_qwaQA5.mjs").then((n) => n.r);
|
|
298
298
|
if (!CONFIG_KEYS.includes(key)) {
|
|
299
299
|
console.error(`Unknown config key: ${key}`);
|
|
300
300
|
process.exit(1);
|
|
@@ -314,8 +314,8 @@ program.command("config").description("Read or write configuration values").addC
|
|
|
314
314
|
}
|
|
315
315
|
return;
|
|
316
316
|
}
|
|
317
|
-
const { loadConfig, saveConfig } = await import("./config-
|
|
318
|
-
const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-
|
|
317
|
+
const { loadConfig, saveConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
318
|
+
const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-D_qwaQA5.mjs").then((n) => n.r);
|
|
319
319
|
const current = await loadConfig();
|
|
320
320
|
if (!CONFIG_KEYS.includes(key)) {
|
|
321
321
|
console.error(`Unknown config key: ${key}`);
|
|
@@ -407,7 +407,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
|
|
|
407
407
|
const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
|
|
408
408
|
const { formatToolsTable } = await import("./format-Bq94jSyw.mjs");
|
|
409
409
|
const { visibleRemoteTools } = await import("./tool-visibility-BpyZHRBi.mjs").then((n) => n.n);
|
|
410
|
-
const { loadConfig } = await import("./config-
|
|
410
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
411
411
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
|
|
412
412
|
const config = await loadConfig();
|
|
413
413
|
const graphMcpEndpoint = resolveGraphMcpEndpoint(config);
|
|
@@ -875,7 +875,7 @@ program.command("playbook").description("Run and manage investigation playbooks"
|
|
|
875
875
|
console.error(`Invalid --from value: "${opts.from}". Must be a positive integer.`);
|
|
876
876
|
process.exit(1);
|
|
877
877
|
}
|
|
878
|
-
const { PlaybookRunner } = await import("./runner-
|
|
878
|
+
const { PlaybookRunner } = await import("./runner-B9fXAP0t.mjs");
|
|
879
879
|
await PlaybookRunner.run(definition, {
|
|
880
880
|
caseId: opts.case,
|
|
881
881
|
from: fromN,
|
|
@@ -930,7 +930,7 @@ program.command("viz").description("Generate money flow visualization").argument
|
|
|
930
930
|
caseId,
|
|
931
931
|
dataFile: opts.data
|
|
932
932
|
});
|
|
933
|
-
const { startServer } = await import("./server-
|
|
933
|
+
const { startServer } = await import("./server-86dyCsJO.mjs").then((n) => n.t);
|
|
934
934
|
const port = parseInt(opts.port, 10);
|
|
935
935
|
startServer(port);
|
|
936
936
|
const url = `http://127.0.0.1:${port}/viz/${result.vizId}`;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { r as graphMcpEndpointEnvOverride } from "./mcp-endpoint-
|
|
3
|
-
import { n as parseInvestigatorConfig, t as DEFAULT_CONFIG } from "./schema-
|
|
2
|
+
import { r as graphMcpEndpointEnvOverride } from "./mcp-endpoint-QQ5Lbqc2.mjs";
|
|
3
|
+
import { n as parseInvestigatorConfig, t as DEFAULT_CONFIG } from "./schema-D_qwaQA5.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
6
6
|
import os from "node:os";
|
|
@@ -74,4 +74,4 @@ async function resetConfigCache() {
|
|
|
74
74
|
//#endregion
|
|
75
75
|
export { saveConfig as i, loadConfig as n, resetConfigCache as r, config_exports as t };
|
|
76
76
|
|
|
77
|
-
//# sourceMappingURL=config-
|
|
77
|
+
//# sourceMappingURL=config-C6zM8Xir.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config-
|
|
1
|
+
{"version":3,"file":"config-C6zM8Xir.mjs","names":[],"sources":["../src/config/index.ts"],"sourcesContent":["import { readFile, writeFile, mkdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\nimport { DEFAULT_CONFIG, parseInvestigatorConfig, type InvestigatorConfig } from './schema.js'\nimport { graphMcpEndpointEnvOverride } from './mcp-endpoint.js'\n\n// Config path derived from HOME at call time so tests can override HOME.\nfunction configPath(): string {\n return path.join(os.homedir(), '.chain-insights', 'config.json')\n}\n\nlet _cached: InvestigatorConfig | null = null\n\nfunction applyRuntimeEnvOverrides(config: InvestigatorConfig): InvestigatorConfig {\n let graphMcpEndpoint: string | undefined\n try {\n graphMcpEndpoint = graphMcpEndpointEnvOverride()\n } catch (err) {\n throw new Error(`Invalid configuration in environment: ${(err as Error).message}`)\n }\n return graphMcpEndpoint\n ? parseInvestigatorConfig({ ...config, graphMcpEndpoint })\n : config\n}\n\nasync function loadStoredConfig(): Promise<InvestigatorConfig> {\n const cfgPath = configPath()\n let raw: string\n try {\n raw = await readFile(cfgPath, 'utf8')\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n return DEFAULT_CONFIG\n }\n throw new Error(`Unable to read config ${cfgPath}: ${(err as Error).message}`)\n }\n\n let parsedJson: unknown\n try {\n parsedJson = JSON.parse(raw) as unknown\n } catch (err) {\n throw new Error(`Invalid JSON in ${cfgPath}: ${(err as Error).message}`)\n }\n\n try {\n return parseInvestigatorConfig(parsedJson)\n } catch (err) {\n throw new Error(`Invalid configuration in ${cfgPath}: ${(err as Error).message}`)\n }\n}\n\nexport async function loadConfig(): Promise<InvestigatorConfig> {\n if (_cached) return _cached\n _cached = applyRuntimeEnvOverrides(await loadStoredConfig())\n return _cached\n}\n\nexport async function saveConfig(updates: Partial<InvestigatorConfig>): Promise<void> {\n const current = await loadStoredConfig()\n let next: InvestigatorConfig\n try {\n next = parseInvestigatorConfig({ ...current, ...updates })\n } catch (err) {\n throw new Error(`Invalid configuration update: ${(err as Error).message}`)\n }\n const p = configPath()\n await mkdir(path.dirname(p), { recursive: true })\n await writeFile(p, JSON.stringify(next, null, 2) + '\\n', { mode: 0o600 })\n _cached = applyRuntimeEnvOverrides(next)\n}\n\nexport async function resetConfigCache(): Promise<void> {\n _cached = null\n}\n"],"mappings":";;;;;;;;;;;;AAOA,SAAS,aAAqB;CAC5B,OAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,aAAa;AACjE;AAEA,IAAI,UAAqC;AAEzC,SAAS,yBAAyB,QAAgD;CAChF,IAAI;CACJ,IAAI;EACF,mBAAmB,4BAA4B;CACjD,SAAS,KAAK;EACZ,MAAM,IAAI,MAAM,yCAA0C,IAAc,SAAS;CACnF;CACA,OAAO,mBACH,wBAAwB;EAAE,GAAG;EAAQ;CAAiB,CAAC,IACvD;AACN;AAEA,eAAe,mBAAgD;CAC7D,MAAM,UAAU,WAAW;CAC3B,IAAI;CACJ,IAAI;EACF,MAAM,MAAM,SAAS,SAAS,MAAM;CACtC,SAAS,KAAK;EACZ,IAAK,IAA8B,SAAS,UAC1C,OAAO;EAET,MAAM,IAAI,MAAM,yBAAyB,QAAQ,IAAK,IAAc,SAAS;CAC/E;CAEA,IAAI;CACJ,IAAI;EACF,aAAa,KAAK,MAAM,GAAG;CAC7B,SAAS,KAAK;EACZ,MAAM,IAAI,MAAM,mBAAmB,QAAQ,IAAK,IAAc,SAAS;CACzE;CAEA,IAAI;EACF,OAAO,wBAAwB,UAAU;CAC3C,SAAS,KAAK;EACZ,MAAM,IAAI,MAAM,4BAA4B,QAAQ,IAAK,IAAc,SAAS;CAClF;AACF;AAEA,eAAsB,aAA0C;CAC9D,IAAI,SAAS,OAAO;CACpB,UAAU,yBAAyB,MAAM,iBAAiB,CAAC;CAC3D,OAAO;AACT;AAEA,eAAsB,WAAW,SAAqD;CACpF,MAAM,UAAU,MAAM,iBAAiB;CACvC,IAAI;CACJ,IAAI;EACF,OAAO,wBAAwB;GAAE,GAAG;GAAS,GAAG;EAAQ,CAAC;CAC3D,SAAS,KAAK;EACZ,MAAM,IAAI,MAAM,iCAAkC,IAAc,SAAS;CAC3E;CACA,MAAM,IAAI,WAAW;CACrB,MAAM,MAAM,KAAK,QAAQ,CAAC,GAAG,EAAE,WAAW,KAAK,CAAC;CAChD,MAAM,UAAU,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,IAAI,MAAM,EAAE,MAAM,IAAM,CAAC;CACxE,UAAU,yBAAyB,IAAI;AACzC;AAEA,eAAsB,mBAAkC;CACtD,UAAU;AACZ"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_mcp_endpoint = require("./mcp-endpoint-
|
|
3
|
-
const require_schema = require("./schema-
|
|
2
|
+
const require_mcp_endpoint = require("./mcp-endpoint-cQIZSjkK.cjs");
|
|
3
|
+
const require_schema = require("./schema-Dr6JXSOF.cjs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_config = require("./config-
|
|
3
|
-
const require_app = require("./app-
|
|
4
|
-
const require_server = require("./server-
|
|
2
|
+
const require_config = require("./config-CkW404Cs.cjs");
|
|
3
|
+
const require_app = require("./app-DjJn3irw.cjs");
|
|
4
|
+
const require_server = require("./server-B2NFmnCM.cjs");
|
|
5
5
|
const require_wallet = require("./wallet-gC2jxh7j.cjs");
|
|
6
6
|
const require_tools = require("./tools-BhTI3Lmg.cjs");
|
|
7
7
|
const require_topup_server = require("./topup-server-DhYlOOBM.cjs");
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-
|
|
2
|
-
import { t as createApp } from "./app-
|
|
3
|
-
import { n as startServer } from "./server-
|
|
1
|
+
import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-C6zM8Xir.mjs";
|
|
2
|
+
import { t as createApp } from "./app-norpwdou.mjs";
|
|
3
|
+
import { n as startServer } from "./server-86dyCsJO.mjs";
|
|
4
4
|
import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-BL0fJC29.mjs";
|
|
5
5
|
import { a as getWalletAccount, i as getBalanceUsdc, n as formatWalletBalance, o as getWalletBalanceText, r as getBalanceEth, t as buildTopupInfo } from "./tools-v6kcdojg.mjs";
|
|
6
6
|
import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-R3dNp-p8.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-
|
|
1
|
+
import { t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-QQ5Lbqc2.mjs";
|
|
2
2
|
import { assertNoVaultFileCollisions, scaffoldVault } from "./vault-z35Dohdq.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { access, mkdir, writeFile } from "node:fs/promises";
|
|
@@ -272,4 +272,4 @@ async function initWorkspace(options) {
|
|
|
272
272
|
//#endregion
|
|
273
273
|
export { initWorkspace };
|
|
274
274
|
|
|
275
|
-
//# sourceMappingURL=init-
|
|
275
|
+
//# sourceMappingURL=init-uAmPfio2.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init-CKQ6F07J.mjs","names":[],"sources":["../src/workspace/init.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { LOCAL_GRAPH_MCP_ENDPOINT } from '../config/mcp-endpoint.js'\nimport { assertNoVaultFileCollisions, scaffoldVault } from '../vault/index.js'\n\nexport interface InitWorkspaceOptions {\n targetDir: string\n force?: boolean\n}\n\nexport interface InitWorkspaceResult {\n workspaceRoot: string\n filesWritten: string[]\n}\n\nconst WORKSPACE_DIRS = [\n '.chain-insights',\n '.chain-insights/schema',\n '.chain-insights/runtime',\n '.chain-insights/runtime/logs',\n '.chain-insights/runtime-skill',\n 'cases',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'templates',\n 'published',\n]\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return JSON.stringify({\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Investigations',\n workspace_root: workspaceRoot,\n default_network: 'bittensor',\n graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,\n cases_dir: 'cases',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n created_at: todayIso(),\n }, null, 2) + '\\n'\n}\n\nconst README = `# Chain Insights Investigation Vault\n\nThis is a Chain Insights AML investigation workspace and Obsidian-compatible vault.\nOpen this directory in Obsidian to review cases, entities, evidence, graphs, and\npublished investigation bundles alongside the Chain Insights runtime metadata.\n\n## Start\n\n\\`\\`\\`bash\nchain-insights mcp tools --refresh\nchain-insights wallet ready --check-only\n\\`\\`\\`\n\n## Layout\n\n\\`\\`\\`text\n.chain-insights/ Workspace metadata\ncases/ Case exports and notes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\ntemplates/ Reusable case/report templates\npublished/ Obsidian-ready case exports and published bundles\n.chain-insights/schema/ Runtime graph schema captures\n.chain-insights/runtime/ Workspace-local runtime process state and debug logs\n.chain-insights/runtime-skill/ Workspace-specific agent schema notes\n.obsidian/ Obsidian vault configuration\nCanvases/ Obsidian canvases for graph review\nEntities/ Entity notes and indexes\nEvidence/ Evidence notes and indexes\n\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights investigation workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before investigation-producing commands.\n- Do not rerun init in an existing workspace unless replacing scaffolding with \\`--force\\`.\n- Read .chain-insights/runtime-skill/SKILL.md before graph queries.\n- Preserve full blockchain addresses exactly.\n- Do not guess the network for graph queries.\n- Capture or refresh graph schema before the first case query.\n- Save compact evidence with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Evidence files should summarize and point to graph/table outputs; do not paste large raw JSON blobs into evidence Markdown.\n- Investigation output must stay in this initialized workspace.\n- Never write cases, evidence, reports, graph JSON, HTML, schema captures, or logs to ~/.chain-insights.\n- Keep theories lightweight until evidence supports them.\n`\n\nconst CLAUDE = AGENTS\n\nconst CASE_BRIEF = `# Case Brief\n\n## Summary\n\nStatus:\nNetwork:\nCurrent Assessment:\n\n## Known Addresses\n\n## Claims To Validate\n\n## Evidence\n\n## Next Steps\n`\n\nconst IMPORTS_README = `# External Investigation Inputs\n\nPut user-provided or third-party investigation material here before turning it\ninto case evidence.\n\nExamples:\n\n- Exchange support exports\n- CSV extracts\n- Screenshots\n- Raw notes\n- Partner reports\n\nFiles in this directory are inputs, not verified evidence. When an import\nsupports a claim, summarize it into the case evidence manifest and reference\nthe original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, case, prompt, and evidence templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as case state until copied into a case, evidence file, dossier, or\nreport.\n`\n\nconst RUNTIME_SKILL = `---\nname: chain-insights-runtime-schema\ndescription: Workspace-local Chain Insights runtime schema notes. Refresh this after connecting to a graph MCP endpoint.\n---\n\n# Runtime Graph Schema\n\nBefore the first investigation query, capture the live graph schema into:\n\n\\`\\`\\`text\n.chain-insights/schema/<network>.graph-schema.json\n\\`\\`\\`\n\nUse \\`graph_query_batch\\` for schema capture. Prefix current topology reads\nwith \\`USE live_topology\\`, historical topology reads with\n\\`USE archive_topology\\`, and fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE live_topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"archive_flow_sample\",\"query\":\"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- Use \\`USE live_topology\\` for recent topology, \\`USE archive_topology\\`\n for historical topology, and \\`USE facts\\` for labels, features,\n risk scores, assets, and enrichment. Address facts can be reached through\n relationships such as \\`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\\`.\n Archived money-flow topology is exposed as\n \\`(:Address)-[:FLOWS_TO]->(:Address)\\` with \\`period_granularity\\`,\n \\`period_start_date\\`, and \\`period_end_date\\` on the relationship.\n- Preserve source schema field names in evidence and generated data files.\n- Do not rename, reinterpret, or add unit labels to graph fields unless the\n schema or query result explicitly supports that interpretation.\n- Keep evidence compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs in evidence unless\n the purpose of the query is schema discovery or debugging.\n- When using BFS, fixed-depth traversal fallbacks, or any manual \\`FLOWS_TO\\`\n traversal, treat exchange hot wallets as terminal endpoints only. Do not\n expand from, through, or classify exchange nodes as deposit, suspect, or\n intermediate candidates. In Cypher, require every non-terminal traversal node\n to satisfy \\`is_exchange IS NULL\\`; only the final exchange endpoint should\n satisfy \\`is_exchange IS NOT NULL\\`.\n- Keep analysis products separate from evidence: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Evidence Markdown should be a short provenance record with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in evidence.\n\nTrace tool chaining:\n\n1. Use \\`trace_victim_funds\\` when the user gives victim/source addresses.\n2. Pass returned \\`continuation.candidate_deposit_addresses\\` to\n \\`trace_deposit_sources\\`; do not make victim tracing run deposit traceback\n internally.\n3. Pass high-confidence \\`continuation.candidate_suspect_addresses\\` from\n deposit traceback to \\`trace_suspect_funds\\`.\n4. Use \\`trace_suspect_funds\\` when the user gives suspected scammer, mule,\n operator, or laundering-ring addresses. \\`incident_timestamp_ms\\` is\n optional.\n5. Use \\`address_risk\\` for single-address enrichment, and\n \\`graph_query_batch\\` only when the role-specific tools do not answer the\n exact question.\n\nAll trace tools return \\`chain-insights.trace.v1\\`. Preserve full addresses in\n\\`input.addresses\\`, \\`addresses[].address\\`, \\`edges[].from_address\\`,\n\\`edges[].to_address\\`, \\`paths[].addresses\\`, \\`candidate_labels[].address\\`,\nand \\`continuation\\` address lists.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nbittensor.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first case query in a fresh\nworkspace, then referenced by evidence, reports, and runtime skill notes.\n`\n\nfunction workspaceFiles(workspaceRoot: string): Array<[string, string]> {\n return [\n ['.chain-insights/workspace.json', workspaceJson(workspaceRoot)],\n ['README.md', README],\n ['AGENTS.md', AGENTS],\n ['CLAUDE.md', CLAUDE],\n ['imports/README.md', IMPORTS_README],\n ['templates/README.md', TEMPLATES_README],\n ['templates/case-brief.md', CASE_BRIEF],\n ['.chain-insights/runtime-skill/SKILL.md', RUNTIME_SKILL],\n ['.chain-insights/schema/README.md', SCHEMA_README],\n ['.chain-insights/runtime/.keep', ''],\n ['.chain-insights/runtime/logs/.keep', ''],\n ]\n}\n\nasync function assertNoFileCollisions(workspaceRoot: string): Promise<void> {\n for (const [relativePath] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await access(filePath)\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue\n }\n throw err\n }\n }\n}\n\nexport async function initWorkspace(options: InitWorkspaceOptions): Promise<InitWorkspaceResult> {\n const workspaceRoot = path.resolve(options.targetDir)\n if (!options.force) {\n await assertNoFileCollisions(workspaceRoot)\n await assertNoVaultFileCollisions(workspaceRoot)\n }\n\n for (const dir of WORKSPACE_DIRS) {\n await mkdir(path.join(workspaceRoot, dir), { recursive: true })\n }\n\n const filesWritten: string[] = []\n const flag = options.force ? 'w' : 'wx'\n for (const [relativePath, content] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await writeFile(filePath, content, { mode: 0o600, flag })\n filesWritten.push(relativePath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n }\n throw err\n }\n }\n\n const vault = await scaffoldVault({ workspaceRoot, force: options.force })\n filesWritten.push(...vault.filesWritten)\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;;;AAeA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OAAO,KAAK,UAAU;EACpB,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,WAAW;EACX,aAAa;EACb,aAAa;EACb,eAAe;EACf,YAAY,SAAS;CACvB,GAAG,MAAM,CAAC,IAAI;AAChB;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCf,MAAM,SAAS;;;;;;;;;;;;;;;;;;AAmBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEtB,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,eAAe,eAAgD;CACtE,OAAO;EACL,CAAC,kCAAkC,cAAc,aAAa,CAAC;EAC/D,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,qBAAqB,cAAc;EACpC,CAAC,uBAAuB,gBAAgB;EACxC,CAAC,2BAA2B,UAAU;EACtC,CAAC,0CAA0C,aAAa;EACxD,CAAC,oCAAoC,aAAa;EAClD,CAAC,iCAAiC,EAAE;EACpC,CAAC,sCAAsC,EAAE;CAC3C;AACF;AAEA,eAAe,uBAAuB,eAAsC;CAC1E,KAAK,MAAM,CAAC,iBAAiB,eAAe,aAAa,GAAG;EAC1D,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,OAAO,QAAQ;GACrB,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;EACtG,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C;GAEF,MAAM;EACR;CACF;AACF;AAEA,eAAsB,cAAc,SAA6D;CAC/F,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,SAAS;CACpD,IAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,uBAAuB,aAAa;EAC1C,MAAM,4BAA4B,aAAa;CACjD;CAEA,KAAK,MAAM,OAAO,gBAChB,MAAM,MAAM,KAAK,KAAK,eAAe,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;CAGhE,MAAM,eAAyB,CAAC;CAChC,MAAM,OAAO,QAAQ,QAAQ,MAAM;CACnC,KAAK,MAAM,CAAC,cAAc,YAAY,eAAe,aAAa,GAAG;EACnE,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,UAAU,UAAU,SAAS;IAAE,MAAM;IAAO;GAAK,CAAC;GACxD,aAAa,KAAK,YAAY;EAChC,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;GAEtG,MAAM;EACR;CACF;CAEA,MAAM,QAAQ,MAAM,cAAc;EAAE;EAAe,OAAO,QAAQ;CAAM,CAAC;CACzE,aAAa,KAAK,GAAG,MAAM,YAAY;CAEvC,OAAO;EAAE;EAAe;CAAa;AACvC"}
|
|
1
|
+
{"version":3,"file":"init-uAmPfio2.mjs","names":[],"sources":["../src/workspace/init.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { LOCAL_GRAPH_MCP_ENDPOINT } from '../config/mcp-endpoint.js'\nimport { assertNoVaultFileCollisions, scaffoldVault } from '../vault/index.js'\n\nexport interface InitWorkspaceOptions {\n targetDir: string\n force?: boolean\n}\n\nexport interface InitWorkspaceResult {\n workspaceRoot: string\n filesWritten: string[]\n}\n\nconst WORKSPACE_DIRS = [\n '.chain-insights',\n '.chain-insights/schema',\n '.chain-insights/runtime',\n '.chain-insights/runtime/logs',\n '.chain-insights/runtime-skill',\n 'cases',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'templates',\n 'published',\n]\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return JSON.stringify({\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Investigations',\n workspace_root: workspaceRoot,\n default_network: 'bittensor',\n graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,\n cases_dir: 'cases',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n created_at: todayIso(),\n }, null, 2) + '\\n'\n}\n\nconst README = `# Chain Insights Investigation Vault\n\nThis is a Chain Insights AML investigation workspace and Obsidian-compatible vault.\nOpen this directory in Obsidian to review cases, entities, evidence, graphs, and\npublished investigation bundles alongside the Chain Insights runtime metadata.\n\n## Start\n\n\\`\\`\\`bash\nchain-insights mcp tools --refresh\nchain-insights wallet ready --check-only\n\\`\\`\\`\n\n## Layout\n\n\\`\\`\\`text\n.chain-insights/ Workspace metadata\ncases/ Case exports and notes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\ntemplates/ Reusable case/report templates\npublished/ Obsidian-ready case exports and published bundles\n.chain-insights/schema/ Runtime graph schema captures\n.chain-insights/runtime/ Workspace-local runtime process state and debug logs\n.chain-insights/runtime-skill/ Workspace-specific agent schema notes\n.obsidian/ Obsidian vault configuration\nCanvases/ Obsidian canvases for graph review\nEntities/ Entity notes and indexes\nEvidence/ Evidence notes and indexes\n\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights investigation workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before investigation-producing commands.\n- Do not rerun init in an existing workspace unless replacing scaffolding with \\`--force\\`.\n- Read .chain-insights/runtime-skill/SKILL.md before graph queries.\n- Preserve full blockchain addresses exactly.\n- Do not guess the network for graph queries.\n- Capture or refresh graph schema before the first case query.\n- Save compact evidence with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Evidence files should summarize and point to graph/table outputs; do not paste large raw JSON blobs into evidence Markdown.\n- Investigation output must stay in this initialized workspace.\n- Never write cases, evidence, reports, graph JSON, HTML, schema captures, or logs to ~/.chain-insights.\n- Keep theories lightweight until evidence supports them.\n`\n\nconst CLAUDE = AGENTS\n\nconst CASE_BRIEF = `# Case Brief\n\n## Summary\n\nStatus:\nNetwork:\nCurrent Assessment:\n\n## Known Addresses\n\n## Claims To Validate\n\n## Evidence\n\n## Next Steps\n`\n\nconst IMPORTS_README = `# External Investigation Inputs\n\nPut user-provided or third-party investigation material here before turning it\ninto case evidence.\n\nExamples:\n\n- Exchange support exports\n- CSV extracts\n- Screenshots\n- Raw notes\n- Partner reports\n\nFiles in this directory are inputs, not verified evidence. When an import\nsupports a claim, summarize it into the case evidence manifest and reference\nthe original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, case, prompt, and evidence templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as case state until copied into a case, evidence file, dossier, or\nreport.\n`\n\nconst RUNTIME_SKILL = `---\nname: chain-insights-runtime-schema\ndescription: Workspace-local Chain Insights runtime schema notes. Refresh this after connecting to a graph MCP endpoint.\n---\n\n# Runtime Graph Schema\n\nBefore the first investigation query, capture the live graph schema into:\n\n\\`\\`\\`text\n.chain-insights/schema/<network>.graph-schema.json\n\\`\\`\\`\n\nUse \\`graph_query_batch\\` for schema capture. Prefix current topology reads\nwith \\`USE live_topology\\`, historical topology reads with\n\\`USE archive_topology\\`, and fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE live_topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"archive_flow_sample\",\"query\":\"USE archive_topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.period_granularity AS granularity, f.amount_sum AS amount_sum LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- Use \\`USE live_topology\\` for recent topology, \\`USE archive_topology\\`\n for historical topology, and \\`USE facts\\` for labels, features,\n risk scores, assets, and enrichment. Address facts can be reached through\n relationships such as \\`(:Address)-[:HAS_FEATURE]->(:AddressFeature)\\`.\n Archived money-flow topology is exposed as\n \\`(:Address)-[:FLOWS_TO]->(:Address)\\` with \\`period_granularity\\`,\n \\`period_start_date\\`, and \\`period_end_date\\` on the relationship.\n- Preserve source schema field names in evidence and generated data files.\n- Do not rename, reinterpret, or add unit labels to graph fields unless the\n schema or query result explicitly supports that interpretation.\n- Keep evidence compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs in evidence unless\n the purpose of the query is schema discovery or debugging.\n- When using BFS, fixed-depth traversal fallbacks, or any manual \\`FLOWS_TO\\`\n traversal, treat exchange hot wallets as terminal endpoints only. Do not\n expand from, through, or classify exchange nodes as deposit, suspect, or\n intermediate candidates. In Cypher, require every non-terminal traversal node\n to satisfy \\`is_exchange IS NULL\\`; only the final exchange endpoint should\n satisfy \\`is_exchange IS NOT NULL\\`.\n- Keep analysis products separate from evidence: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Evidence Markdown should be a short provenance record with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in evidence.\n\nTrace tool chaining:\n\n1. Use \\`trace_victim_funds\\` when the user gives victim/source addresses.\n2. Pass returned \\`continuation.candidate_deposit_addresses\\` to\n \\`trace_deposit_sources\\`; do not make victim tracing run deposit traceback\n internally.\n3. Pass high-confidence \\`continuation.candidate_suspect_addresses\\` from\n deposit traceback to \\`trace_suspect_funds\\`.\n4. Use \\`trace_suspect_funds\\` when the user gives suspected scammer, mule,\n operator, or laundering-ring addresses. \\`incident_timestamp_ms\\` is\n optional.\n5. Use \\`address_risk\\` for single-address enrichment, and\n \\`graph_query_batch\\` only when the role-specific tools do not answer the\n exact question.\n\nAll trace tools return \\`chain-insights.trace.v1\\`. Preserve full addresses in\n\\`input.addresses\\`, \\`addresses[].address\\`, \\`edges[].from_address\\`,\n\\`edges[].to_address\\`, \\`paths[].addresses\\`, \\`candidate_labels[].address\\`,\nand \\`continuation\\` address lists.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nbittensor.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first case query in a fresh\nworkspace, then referenced by evidence, reports, and runtime skill notes.\n`\n\nfunction workspaceFiles(workspaceRoot: string): Array<[string, string]> {\n return [\n ['.chain-insights/workspace.json', workspaceJson(workspaceRoot)],\n ['README.md', README],\n ['AGENTS.md', AGENTS],\n ['CLAUDE.md', CLAUDE],\n ['imports/README.md', IMPORTS_README],\n ['templates/README.md', TEMPLATES_README],\n ['templates/case-brief.md', CASE_BRIEF],\n ['.chain-insights/runtime-skill/SKILL.md', RUNTIME_SKILL],\n ['.chain-insights/schema/README.md', SCHEMA_README],\n ['.chain-insights/runtime/.keep', ''],\n ['.chain-insights/runtime/logs/.keep', ''],\n ]\n}\n\nasync function assertNoFileCollisions(workspaceRoot: string): Promise<void> {\n for (const [relativePath] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await access(filePath)\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue\n }\n throw err\n }\n }\n}\n\nexport async function initWorkspace(options: InitWorkspaceOptions): Promise<InitWorkspaceResult> {\n const workspaceRoot = path.resolve(options.targetDir)\n if (!options.force) {\n await assertNoFileCollisions(workspaceRoot)\n await assertNoVaultFileCollisions(workspaceRoot)\n }\n\n for (const dir of WORKSPACE_DIRS) {\n await mkdir(path.join(workspaceRoot, dir), { recursive: true })\n }\n\n const filesWritten: string[] = []\n const flag = options.force ? 'w' : 'wx'\n for (const [relativePath, content] of workspaceFiles(workspaceRoot)) {\n const filePath = path.join(workspaceRoot, relativePath)\n try {\n await writeFile(filePath, content, { mode: 0o600, flag })\n filesWritten.push(relativePath)\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'EEXIST') {\n throw new Error(`Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`)\n }\n throw err\n }\n }\n\n const vault = await scaffoldVault({ workspaceRoot, force: options.force })\n filesWritten.push(...vault.filesWritten)\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;;;AAeA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OAAO,KAAK,UAAU;EACpB,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,WAAW;EACX,aAAa;EACb,aAAa;EACb,eAAe;EACf,YAAY,SAAS;CACvB,GAAG,MAAM,CAAC,IAAI;AAChB;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCf,MAAM,SAAS;;;;;;;;;;;;;;;;;;AAmBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyEtB,MAAM,gBAAgB;;;;;;;;;;;AAYtB,SAAS,eAAe,eAAgD;CACtE,OAAO;EACL,CAAC,kCAAkC,cAAc,aAAa,CAAC;EAC/D,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,aAAa,MAAM;EACpB,CAAC,qBAAqB,cAAc;EACpC,CAAC,uBAAuB,gBAAgB;EACxC,CAAC,2BAA2B,UAAU;EACtC,CAAC,0CAA0C,aAAa;EACxD,CAAC,oCAAoC,aAAa;EAClD,CAAC,iCAAiC,EAAE;EACpC,CAAC,sCAAsC,EAAE;CAC3C;AACF;AAEA,eAAe,uBAAuB,eAAsC;CAC1E,KAAK,MAAM,CAAC,iBAAiB,eAAe,aAAa,GAAG;EAC1D,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,OAAO,QAAQ;GACrB,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;EACtG,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C;GAEF,MAAM;EACR;CACF;AACF;AAEA,eAAsB,cAAc,SAA6D;CAC/F,MAAM,gBAAgB,KAAK,QAAQ,QAAQ,SAAS;CACpD,IAAI,CAAC,QAAQ,OAAO;EAClB,MAAM,uBAAuB,aAAa;EAC1C,MAAM,4BAA4B,aAAa;CACjD;CAEA,KAAK,MAAM,OAAO,gBAChB,MAAM,MAAM,KAAK,KAAK,eAAe,GAAG,GAAG,EAAE,WAAW,KAAK,CAAC;CAGhE,MAAM,eAAyB,CAAC;CAChC,MAAM,OAAO,QAAQ,QAAQ,MAAM;CACnC,KAAK,MAAM,CAAC,cAAc,YAAY,eAAe,aAAa,GAAG;EACnE,MAAM,WAAW,KAAK,KAAK,eAAe,YAAY;EACtD,IAAI;GACF,MAAM,UAAU,UAAU,SAAS;IAAE,MAAM;IAAO;GAAK,CAAC;GACxD,aAAa,KAAK,YAAY;EAChC,SAAS,KAAK;GACZ,IAAK,IAA8B,SAAS,UAC1C,MAAM,IAAI,MAAM,yBAAyB,SAAS,kDAAkD;GAEtG,MAAM;EACR;CACF;CAEA,MAAM,QAAQ,MAAM,cAAc;EAAE;EAAe,OAAO,QAAQ;CAAM,CAAC;CACzE,aAAa,KAAK,GAAG,MAAM,YAAY;CAEvC,OAAO;EAAE;EAAe;CAAa;AACvC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_mcp_endpoint = require("./mcp-endpoint-
|
|
2
|
+
const require_mcp_endpoint = require("./mcp-endpoint-cQIZSjkK.cjs");
|
|
3
3
|
const require_vault = require("./vault-B2y78Ypu.cjs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
@@ -14,6 +14,9 @@ function isLoopbackHost(hostname) {
|
|
|
14
14
|
if (isIP(normalized) !== 4) return false;
|
|
15
15
|
return normalized.split(".")[0] === "127";
|
|
16
16
|
}
|
|
17
|
+
function isKubernetesServiceHost(hostname) {
|
|
18
|
+
return hostname.toLowerCase().endsWith(".svc.cluster.local");
|
|
19
|
+
}
|
|
17
20
|
function graphMcpEndpointEnvOverride() {
|
|
18
21
|
const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || process.env.GRAPH_MCP_ENDPOINT?.trim();
|
|
19
22
|
return envEndpoint ? validateMcpEndpoint(envEndpoint, "graphMcpEndpoint") : void 0;
|
|
@@ -30,10 +33,10 @@ function validateMcpEndpoint(value, key) {
|
|
|
30
33
|
if (parsed.username || parsed.password) throw new Error(`${key} must not include URL credentials.`);
|
|
31
34
|
if (parsed.search || parsed.hash) throw new Error(`${key} must not include query parameters or URL fragments.`);
|
|
32
35
|
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`${key} must use either https:// or http://.`);
|
|
33
|
-
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost or
|
|
36
|
+
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname) && !isKubernetesServiceHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost, loopback addresses, or Kubernetes *.svc.cluster.local service DNS.`);
|
|
34
37
|
return trimmed;
|
|
35
38
|
}
|
|
36
39
|
//#endregion
|
|
37
40
|
export { validateMcpEndpoint as i, LOCAL_LEGACY_MCP_ENDPOINT as n, graphMcpEndpointEnvOverride as r, LOCAL_GRAPH_MCP_ENDPOINT as t };
|
|
38
41
|
|
|
39
|
-
//# sourceMappingURL=mcp-endpoint-
|
|
42
|
+
//# sourceMappingURL=mcp-endpoint-QQ5Lbqc2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-endpoint-QQ5Lbqc2.mjs","names":[],"sources":["../src/config/mcp-endpoint.ts"],"sourcesContent":["import { isIP } from 'node:net'\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]'])\n\nexport const LOCAL_GRAPH_MCP_ENDPOINT = 'http://127.0.0.1:8012/mcp'\nexport const LOCAL_LEGACY_MCP_ENDPOINT = 'http://127.0.0.1:4000'\n\nfunction isLoopbackHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase().replace(/^\\[(.*)\\]$/, '$1')\n if (LOOPBACK_HOSTS.has(normalized)) return true\n if (isIP(normalized) !== 4) return false\n return normalized.split('.')[0] === '127'\n}\n\nfunction isKubernetesServiceHost(hostname: string): boolean {\n return hostname.toLowerCase().endsWith('.svc.cluster.local')\n}\n\nexport function graphMcpEndpointEnvOverride(): string | undefined {\n const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim()\n || process.env.GRAPH_MCP_ENDPOINT?.trim()\n return envEndpoint ? validateMcpEndpoint(envEndpoint, 'graphMcpEndpoint') : undefined\n}\n\nexport function validateMcpEndpoint(value: string, key: string): string {\n const trimmed = value.trim()\n if (!trimmed) {\n throw new Error(`${key} must be a non-empty absolute URL.`)\n }\n\n let parsed: URL\n try {\n parsed = new URL(trimmed)\n } catch {\n throw new Error(\n `${key} must be an absolute URL (example: https://mcp.example.com/mcp or http://127.0.0.1:8012/mcp).`,\n )\n }\n\n if (parsed.username || parsed.password) {\n throw new Error(`${key} must not include URL credentials.`)\n }\n if (parsed.search || parsed.hash) {\n throw new Error(`${key} must not include query parameters or URL fragments.`)\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`${key} must use either https:// or http://.`)\n }\n if (parsed.protocol === 'http:' && !isLoopbackHost(parsed.hostname) && !isKubernetesServiceHost(parsed.hostname)) {\n throw new Error(\n `${key} must use https:// for remote hosts. http:// is allowed only for localhost, loopback addresses, or Kubernetes *.svc.cluster.local service DNS.`,\n )\n }\n\n return trimmed\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;AAAO,CAAC;AAEzE,MAAa,2BAA2B;AACxC,MAAa,4BAA4B;AAEzC,SAAS,eAAe,UAA2B;CACjD,MAAM,aAAa,SAAS,YAAY,EAAE,QAAQ,cAAc,IAAI;CACpE,IAAI,eAAe,IAAI,UAAU,GAAG,OAAO;CAC3C,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO;CACnC,OAAO,WAAW,MAAM,GAAG,EAAE,OAAO;AACtC;AAEA,SAAS,wBAAwB,UAA2B;CAC1D,OAAO,SAAS,YAAY,EAAE,SAAS,oBAAoB;AAC7D;AAEA,SAAgB,8BAAkD;CAChE,MAAM,cAAc,QAAQ,IAAI,mCAAmC,KAAK,KACnE,QAAQ,IAAI,oBAAoB,KAAK;CAC1C,OAAO,cAAc,oBAAoB,aAAa,kBAAkB,IAAI,KAAA;AAC9E;AAEA,SAAgB,oBAAoB,OAAe,KAAqB;CACtE,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAG5D,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,OAAO;CAC1B,QAAQ;EACN,MAAM,IAAI,MACR,GAAG,IAAI,8FACT;CACF;CAEA,IAAI,OAAO,YAAY,OAAO,UAC5B,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAE5D,IAAI,OAAO,UAAU,OAAO,MAC1B,MAAM,IAAI,MAAM,GAAG,IAAI,qDAAqD;CAE9E,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,MAAM,IAAI,MAAM,GAAG,IAAI,sCAAsC;CAE/D,IAAI,OAAO,aAAa,WAAW,CAAC,eAAe,OAAO,QAAQ,KAAK,CAAC,wBAAwB,OAAO,QAAQ,GAC7G,MAAM,IAAI,MACR,GAAG,IAAI,+IACT;CAGF,OAAO;AACT"}
|
|
@@ -14,6 +14,9 @@ function isLoopbackHost(hostname) {
|
|
|
14
14
|
if ((0, node_net.isIP)(normalized) !== 4) return false;
|
|
15
15
|
return normalized.split(".")[0] === "127";
|
|
16
16
|
}
|
|
17
|
+
function isKubernetesServiceHost(hostname) {
|
|
18
|
+
return hostname.toLowerCase().endsWith(".svc.cluster.local");
|
|
19
|
+
}
|
|
17
20
|
function graphMcpEndpointEnvOverride() {
|
|
18
21
|
const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || process.env.GRAPH_MCP_ENDPOINT?.trim();
|
|
19
22
|
return envEndpoint ? validateMcpEndpoint(envEndpoint, "graphMcpEndpoint") : void 0;
|
|
@@ -30,7 +33,7 @@ function validateMcpEndpoint(value, key) {
|
|
|
30
33
|
if (parsed.username || parsed.password) throw new Error(`${key} must not include URL credentials.`);
|
|
31
34
|
if (parsed.search || parsed.hash) throw new Error(`${key} must not include query parameters or URL fragments.`);
|
|
32
35
|
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error(`${key} must use either https:// or http://.`);
|
|
33
|
-
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost or
|
|
36
|
+
if (parsed.protocol === "http:" && !isLoopbackHost(parsed.hostname) && !isKubernetesServiceHost(parsed.hostname)) throw new Error(`${key} must use https:// for remote hosts. http:// is allowed only for localhost, loopback addresses, or Kubernetes *.svc.cluster.local service DNS.`);
|
|
34
37
|
return trimmed;
|
|
35
38
|
}
|
|
36
39
|
//#endregion
|
package/dist/mcp-proxy.cjs
CHANGED
|
@@ -588,7 +588,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
|
|
|
588
588
|
const meta = { ...result._meta ?? {} };
|
|
589
589
|
if (graphPayload && includeAttachments) {
|
|
590
590
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
591
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
591
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-C6_gtIql.cjs"));
|
|
592
592
|
const report = await writeGraphReport(graphPayload, {
|
|
593
593
|
serverPort: config.serverPort,
|
|
594
594
|
slug: toolName || "remote-graph"
|
|
@@ -615,7 +615,7 @@ function shouldIncludeAttachments(args, workspaceArtifactsEnabled) {
|
|
|
615
615
|
async function writeLocalGraphMeta(graphData, config, slug, includeAttachments) {
|
|
616
616
|
if (!includeAttachments) return void 0;
|
|
617
617
|
const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-B3mkLP8Z.cjs"));
|
|
618
|
-
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-
|
|
618
|
+
const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-C6_gtIql.cjs"));
|
|
619
619
|
const report = await writeGraphReport(graphData, {
|
|
620
620
|
serverPort: config.serverPort,
|
|
621
621
|
slug
|
|
@@ -637,7 +637,7 @@ function graphMetaResult(graph) {
|
|
|
637
637
|
* All diagnostic output goes to console.error() or process.stderr.write().
|
|
638
638
|
*/
|
|
639
639
|
async function createProxy() {
|
|
640
|
-
const { loadConfig } = await Promise.resolve().then(() => require("./config-
|
|
640
|
+
const { loadConfig } = await Promise.resolve().then(() => require("./config-CkW404Cs.cjs")).then((n) => n.config_exports);
|
|
641
641
|
const { activeDataDir, findActiveWorkspace } = await Promise.resolve().then(() => require("./active-BVr55kvW.cjs")).then((n) => n.active_exports);
|
|
642
642
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-Y_zqKqJT.cjs")).then((n) => n.client_exports);
|
|
643
643
|
const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-CJk1EL3L.cjs"));
|
package/dist/mcp-proxy.mjs
CHANGED
|
@@ -584,7 +584,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
|
|
|
584
584
|
const meta = { ...result._meta ?? {} };
|
|
585
585
|
if (graphPayload && includeAttachments) {
|
|
586
586
|
const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
|
|
587
|
-
const { ensureArtifactServer } = await import("./artifact-server-
|
|
587
|
+
const { ensureArtifactServer } = await import("./artifact-server-DHPM0lxS.mjs");
|
|
588
588
|
const report = await writeGraphReport(graphPayload, {
|
|
589
589
|
serverPort: config.serverPort,
|
|
590
590
|
slug: toolName || "remote-graph"
|
|
@@ -611,7 +611,7 @@ function shouldIncludeAttachments(args, workspaceArtifactsEnabled) {
|
|
|
611
611
|
async function writeLocalGraphMeta(graphData, config, slug, includeAttachments) {
|
|
612
612
|
if (!includeAttachments) return void 0;
|
|
613
613
|
const { writeGraphReport } = await import("./graph-reports-BDELxmpi.mjs");
|
|
614
|
-
const { ensureArtifactServer } = await import("./artifact-server-
|
|
614
|
+
const { ensureArtifactServer } = await import("./artifact-server-DHPM0lxS.mjs");
|
|
615
615
|
const report = await writeGraphReport(graphData, {
|
|
616
616
|
serverPort: config.serverPort,
|
|
617
617
|
slug
|
|
@@ -633,7 +633,7 @@ function graphMetaResult(graph) {
|
|
|
633
633
|
* All diagnostic output goes to console.error() or process.stderr.write().
|
|
634
634
|
*/
|
|
635
635
|
async function createProxy() {
|
|
636
|
-
const { loadConfig } = await import("./config-
|
|
636
|
+
const { loadConfig } = await import("./config-C6zM8Xir.mjs").then((n) => n.t);
|
|
637
637
|
const { activeDataDir, findActiveWorkspace } = await import("./active-ByNgjuAg.mjs").then((n) => n.n);
|
|
638
638
|
const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-BgmHjBHQ.mjs").then((n) => n.r);
|
|
639
639
|
const { loadSchema, saveSchema } = await import("./schema-cache-DwDvPy4e.mjs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as PACKAGE_VERSION } from "./version-BA3J8hu4.mjs";
|
|
2
|
-
import { n as loadConfig } from "./config-
|
|
2
|
+
import { n as loadConfig } from "./config-C6zM8Xir.mjs";
|
|
3
3
|
import { i as createConfiguredMcpFetch } from "./client-BgmHjBHQ.mjs";
|
|
4
4
|
import { t as generateVisualization } from "./viz-DB5XFG1z.mjs";
|
|
5
5
|
import { t as CaseStore } from "./store-C2B_AssI.mjs";
|
|
@@ -146,4 +146,4 @@ async run(playbook, opts) {
|
|
|
146
146
|
//#endregion
|
|
147
147
|
export { PlaybookRunner };
|
|
148
148
|
|
|
149
|
-
//# sourceMappingURL=runner-
|
|
149
|
+
//# sourceMappingURL=runner-B9fXAP0t.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-CVnjpqc-.mjs","names":[],"sources":["../src/playbooks/runner.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { CaseStore } from '../cases/store.js'\nimport { EvidenceStore } from '../cases/evidence.js'\nimport { loadConfig } from '../config/index.js'\nimport { createConfiguredMcpFetch } from '../mcp/client.js'\nimport { generateVisualization } from '../viz/index.js'\nimport { PACKAGE_VERSION } from '../version.js'\nimport type { PlaybookDefinition } from './schema.js'\n\nexport interface RunnerOptions {\n caseId?: string // attach to existing case; omit for quick-case auto-creation\n from?: number // 1-based step to resume from (default: 1)\n dryRun?: boolean // print steps, no MCP calls\n params?: Record<string, string>\n}\n\n/** Sleep for ms milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/** Check if an error is a timeout/abort error. */\nfunction isTimeoutError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return err.name === 'AbortError' || (err as NodeJS.ErrnoException).code === 'ECONNRESET'\n}\n\n/** Check if an error is a payment failure. */\nfunction isPaymentError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message.toLowerCase()\n // Match HTTP 402 status more precisely, or x402-specific error signals\n return msg.includes('http 402') ||\n msg.includes('status 402') ||\n msg.includes('payment required') ||\n msg.includes('x402')\n}\n\n/**\n * Call an MCP tool with retry logic on timeout (up to 3 total attempts).\n * Returns the text result or throws on non-retryable error.\n */\nasync function callWithRetry(\n client: Client,\n toolName: string,\n params: Record<string, string>\n): Promise<string> {\n const MAX_ATTEMPTS = 3\n let lastErr: unknown\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n try {\n const result = await client.callTool({ name: toolName, arguments: params })\n const content = result.content as Array<{ type: string; text?: string }>\n return content.filter(c => c.type === 'text').map(c => c.text ?? '').join('\\n')\n } catch (err) {\n if (isTimeoutError(err) && attempt < MAX_ATTEMPTS) {\n lastErr = err\n await sleep(1000)\n continue\n }\n throw err\n }\n }\n\n throw lastErr\n}\n\nasync function validateStepTools(client: Client, steps: PlaybookDefinition['steps']): Promise<void> {\n const result = await client.listTools()\n const available = new Set(result.tools.map(tool => tool.name))\n const missing = [...new Set(steps.map(step => step.tool).filter(tool => !available.has(tool)))]\n if (missing.length === 0) return\n\n const availableList = [...available].sort().join(', ') || 'none'\n throw new Error(\n `Unknown MCP tool(s) in playbook: ${missing.join(', ')}. ` +\n `Available tools: ${availableList}. Run \\`chain-insights mcp tools --refresh\\` to inspect the live MCP schema.`\n )\n}\n\nexport const PlaybookRunner = {\n /**\n * Execute a playbook definition step-by-step against the live MCP.\n *\n * @param playbook - Parsed and validated PlaybookDefinition\n * @param opts - Runner options (caseId, from, dryRun, params)\n */\n async run(playbook: PlaybookDefinition, opts: RunnerOptions): Promise<void> {\n const startIndex = (opts.from ?? 1) - 1 // convert 1-based to 0-based\n const stepsToRun = playbook.steps.slice(startIndex)\n const totalSteps = playbook.steps.length\n\n // --- DRY RUN ---\n if (opts.dryRun) {\n console.log(`Playbook: ${playbook.name} (dry run — no MCP calls)`)\n console.log(`Steps: ${totalSteps} total, starting from ${startIndex + 1}`)\n console.log('')\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.tool} (params: ${JSON.stringify(step.params)})`)\n }\n console.log('')\n console.log('Cost: unknown (MCP pricing not available without live connection)')\n return\n }\n\n // --- MCP AUTH CHECK (before case creation to avoid orphan cases) ---\n const config = await loadConfig()\n const mcpFetch = await createConfiguredMcpFetch(config)\n\n // --- CASE RESOLUTION ---\n let caseId: string\n if (opts.caseId) {\n const existingCase = await CaseStore.get(opts.caseId)\n caseId = existingCase.id\n } else {\n const newCase = await CaseStore.create({\n name: `quick-${playbook.name}-${Date.now()}`,\n tags: ['quick', 'playbook', playbook.name],\n description: `Auto-created for one-off playbook run: ${playbook.name}`,\n })\n caseId = newCase.id\n console.log(`Created quick case: ${caseId}`)\n }\n\n // --- MCP CONNECTION ---\n const client = new Client({ name: 'chain-insights-playbook', version: PACKAGE_VERSION })\n await client.connect(\n new StreamableHTTPClientTransport(new URL(config.mcpEndpoint), { fetch: mcpFetch })\n )\n\n let evidenceCount = 0\n\n try {\n await validateStepTools(client, stepsToRun)\n\n // --- STEP LOOP ---\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.label}...`)\n\n let result: string\n try {\n result = await callWithRetry(client, step.tool, step.params)\n } catch (err) {\n if (isPaymentError(err)) {\n if (process.stdin.isTTY) {\n // Interactive: prompt user\n const { createInterface } = await import('node:readline')\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const answer = await new Promise<string>(resolve => {\n rl.question(`Payment required for step ${step.index}. (retry/skip/abort): `, resolve)\n })\n rl.close()\n\n if (answer.trim().toLowerCase() === 'retry') {\n result = await callWithRetry(client, step.tool, step.params)\n } else if (answer.trim().toLowerCase() === 'skip') {\n console.log(`Step ${step.index} skipped.`)\n continue\n } else {\n throw new Error(`Aborted at step ${step.index} due to payment failure.`)\n }\n } else {\n // Non-TTY: abort\n throw new Error(\n `Payment required for step ${step.index} but no interactive terminal available. ` +\n `Configure wallet with \\`chain-insights wallet import <private-key>\\`, then run \\`chain-insights wallet ready\\`. Aborting.`\n )\n }\n } else {\n // Non-payment, non-timeout MCP error — stop and report\n const completedSteps = step.index - 1 - startIndex\n const completedMsg = completedSteps > 0\n ? `Completed: steps ${startIndex + 1}..${step.index - 1}.`\n : 'No steps completed before failure.'\n console.error(\n `Step ${step.index} failed: ${(err as Error).message}. ` +\n `${completedMsg} Run with --from ${step.index} to resume.`\n )\n throw err\n }\n }\n\n // --- STORE EVIDENCE ---\n await EvidenceStore.append(caseId, {\n source: step.tool,\n content: result,\n queryParams: JSON.stringify(step.params),\n })\n evidenceCount++\n console.log(` (${result.length} chars stored)`)\n }\n\n // --- AUTO-VIZ for trace-funds ---\n if (playbook.name === 'trace-funds') {\n try {\n const viz = await generateVisualization({ caseId })\n console.log(`Visualization generated: ${viz.htmlPath}`)\n } catch {\n console.log('No transaction data to visualize.')\n }\n }\n\n // --- FINAL SUMMARY ---\n console.log(`Playbook complete. Case: ${caseId}. Evidence: ${evidenceCount} entries.`)\n } finally {\n await client.close()\n }\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runner-B9fXAP0t.mjs","names":[],"sources":["../src/playbooks/runner.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { CaseStore } from '../cases/store.js'\nimport { EvidenceStore } from '../cases/evidence.js'\nimport { loadConfig } from '../config/index.js'\nimport { createConfiguredMcpFetch } from '../mcp/client.js'\nimport { generateVisualization } from '../viz/index.js'\nimport { PACKAGE_VERSION } from '../version.js'\nimport type { PlaybookDefinition } from './schema.js'\n\nexport interface RunnerOptions {\n caseId?: string // attach to existing case; omit for quick-case auto-creation\n from?: number // 1-based step to resume from (default: 1)\n dryRun?: boolean // print steps, no MCP calls\n params?: Record<string, string>\n}\n\n/** Sleep for ms milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/** Check if an error is a timeout/abort error. */\nfunction isTimeoutError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return err.name === 'AbortError' || (err as NodeJS.ErrnoException).code === 'ECONNRESET'\n}\n\n/** Check if an error is a payment failure. */\nfunction isPaymentError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message.toLowerCase()\n // Match HTTP 402 status more precisely, or x402-specific error signals\n return msg.includes('http 402') ||\n msg.includes('status 402') ||\n msg.includes('payment required') ||\n msg.includes('x402')\n}\n\n/**\n * Call an MCP tool with retry logic on timeout (up to 3 total attempts).\n * Returns the text result or throws on non-retryable error.\n */\nasync function callWithRetry(\n client: Client,\n toolName: string,\n params: Record<string, string>\n): Promise<string> {\n const MAX_ATTEMPTS = 3\n let lastErr: unknown\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n try {\n const result = await client.callTool({ name: toolName, arguments: params })\n const content = result.content as Array<{ type: string; text?: string }>\n return content.filter(c => c.type === 'text').map(c => c.text ?? '').join('\\n')\n } catch (err) {\n if (isTimeoutError(err) && attempt < MAX_ATTEMPTS) {\n lastErr = err\n await sleep(1000)\n continue\n }\n throw err\n }\n }\n\n throw lastErr\n}\n\nasync function validateStepTools(client: Client, steps: PlaybookDefinition['steps']): Promise<void> {\n const result = await client.listTools()\n const available = new Set(result.tools.map(tool => tool.name))\n const missing = [...new Set(steps.map(step => step.tool).filter(tool => !available.has(tool)))]\n if (missing.length === 0) return\n\n const availableList = [...available].sort().join(', ') || 'none'\n throw new Error(\n `Unknown MCP tool(s) in playbook: ${missing.join(', ')}. ` +\n `Available tools: ${availableList}. Run \\`chain-insights mcp tools --refresh\\` to inspect the live MCP schema.`\n )\n}\n\nexport const PlaybookRunner = {\n /**\n * Execute a playbook definition step-by-step against the live MCP.\n *\n * @param playbook - Parsed and validated PlaybookDefinition\n * @param opts - Runner options (caseId, from, dryRun, params)\n */\n async run(playbook: PlaybookDefinition, opts: RunnerOptions): Promise<void> {\n const startIndex = (opts.from ?? 1) - 1 // convert 1-based to 0-based\n const stepsToRun = playbook.steps.slice(startIndex)\n const totalSteps = playbook.steps.length\n\n // --- DRY RUN ---\n if (opts.dryRun) {\n console.log(`Playbook: ${playbook.name} (dry run — no MCP calls)`)\n console.log(`Steps: ${totalSteps} total, starting from ${startIndex + 1}`)\n console.log('')\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.tool} (params: ${JSON.stringify(step.params)})`)\n }\n console.log('')\n console.log('Cost: unknown (MCP pricing not available without live connection)')\n return\n }\n\n // --- MCP AUTH CHECK (before case creation to avoid orphan cases) ---\n const config = await loadConfig()\n const mcpFetch = await createConfiguredMcpFetch(config)\n\n // --- CASE RESOLUTION ---\n let caseId: string\n if (opts.caseId) {\n const existingCase = await CaseStore.get(opts.caseId)\n caseId = existingCase.id\n } else {\n const newCase = await CaseStore.create({\n name: `quick-${playbook.name}-${Date.now()}`,\n tags: ['quick', 'playbook', playbook.name],\n description: `Auto-created for one-off playbook run: ${playbook.name}`,\n })\n caseId = newCase.id\n console.log(`Created quick case: ${caseId}`)\n }\n\n // --- MCP CONNECTION ---\n const client = new Client({ name: 'chain-insights-playbook', version: PACKAGE_VERSION })\n await client.connect(\n new StreamableHTTPClientTransport(new URL(config.mcpEndpoint), { fetch: mcpFetch })\n )\n\n let evidenceCount = 0\n\n try {\n await validateStepTools(client, stepsToRun)\n\n // --- STEP LOOP ---\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.label}...`)\n\n let result: string\n try {\n result = await callWithRetry(client, step.tool, step.params)\n } catch (err) {\n if (isPaymentError(err)) {\n if (process.stdin.isTTY) {\n // Interactive: prompt user\n const { createInterface } = await import('node:readline')\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const answer = await new Promise<string>(resolve => {\n rl.question(`Payment required for step ${step.index}. (retry/skip/abort): `, resolve)\n })\n rl.close()\n\n if (answer.trim().toLowerCase() === 'retry') {\n result = await callWithRetry(client, step.tool, step.params)\n } else if (answer.trim().toLowerCase() === 'skip') {\n console.log(`Step ${step.index} skipped.`)\n continue\n } else {\n throw new Error(`Aborted at step ${step.index} due to payment failure.`)\n }\n } else {\n // Non-TTY: abort\n throw new Error(\n `Payment required for step ${step.index} but no interactive terminal available. ` +\n `Configure wallet with \\`chain-insights wallet import <private-key>\\`, then run \\`chain-insights wallet ready\\`. Aborting.`\n )\n }\n } else {\n // Non-payment, non-timeout MCP error — stop and report\n const completedSteps = step.index - 1 - startIndex\n const completedMsg = completedSteps > 0\n ? `Completed: steps ${startIndex + 1}..${step.index - 1}.`\n : 'No steps completed before failure.'\n console.error(\n `Step ${step.index} failed: ${(err as Error).message}. ` +\n `${completedMsg} Run with --from ${step.index} to resume.`\n )\n throw err\n }\n }\n\n // --- STORE EVIDENCE ---\n await EvidenceStore.append(caseId, {\n source: step.tool,\n content: result,\n queryParams: JSON.stringify(step.params),\n })\n evidenceCount++\n console.log(` (${result.length} chars stored)`)\n }\n\n // --- AUTO-VIZ for trace-funds ---\n if (playbook.name === 'trace-funds') {\n try {\n const viz = await generateVisualization({ caseId })\n console.log(`Visualization generated: ${viz.htmlPath}`)\n } catch {\n console.log('No transaction data to visualize.')\n }\n }\n\n // --- FINAL SUMMARY ---\n console.log(`Playbook complete. Case: ${caseId}. Evidence: ${evidenceCount} entries.`)\n } finally {\n await client.close()\n }\n },\n}\n"],"mappings":";;;;;;;;;;AAkBA,SAAS,MAAM,IAA2B;CACxC,OAAO,IAAI,SAAQ,YAAW,WAAW,SAAS,EAAE,CAAC;AACvD;;AAGA,SAAS,eAAe,KAAuB;CAC7C,IAAI,EAAE,eAAe,QAAQ,OAAO;CACpC,OAAO,IAAI,SAAS,gBAAiB,IAA8B,SAAS;AAC9E;;AAGA,SAAS,eAAe,KAAuB;CAC7C,IAAI,EAAE,eAAe,QAAQ,OAAO;CACpC,MAAM,MAAM,IAAI,QAAQ,YAAY;CAEpC,OAAO,IAAI,SAAS,UAAU,KACvB,IAAI,SAAS,YAAY,KACzB,IAAI,SAAS,kBAAkB,KAC/B,IAAI,SAAS,MAAM;AAC5B;;;;;AAMA,eAAe,cACb,QACA,UACA,QACiB;CACjB,MAAM,eAAe;CACrB,IAAI;CAEJ,KAAK,IAAI,UAAU,GAAG,WAAW,cAAc,WAC7C,IAAI;EAGF,QADgB,MADK,OAAO,SAAS;GAAE,MAAM;GAAU,WAAW;EAAO,CAAC,GACnD,QACR,QAAO,MAAK,EAAE,SAAS,MAAM,EAAE,KAAI,MAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,IAAI;CAChF,SAAS,KAAK;EACZ,IAAI,eAAe,GAAG,KAAK,UAAU,cAAc;GACjD,UAAU;GACV,MAAM,MAAM,GAAI;GAChB;EACF;EACA,MAAM;CACR;CAGF,MAAM;AACR;AAEA,eAAe,kBAAkB,QAAgB,OAAmD;CAClG,MAAM,SAAS,MAAM,OAAO,UAAU;CACtC,MAAM,YAAY,IAAI,IAAI,OAAO,MAAM,KAAI,SAAQ,KAAK,IAAI,CAAC;CAC7D,MAAM,UAAU,CAAC,GAAG,IAAI,IAAI,MAAM,KAAI,SAAQ,KAAK,IAAI,EAAE,QAAO,SAAQ,CAAC,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC;CAC9F,IAAI,QAAQ,WAAW,GAAG;CAE1B,MAAM,gBAAgB,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,IAAI,KAAK;CAC1D,MAAM,IAAI,MACR,oCAAoC,QAAQ,KAAK,IAAI,EAAE,qBACnC,cAAc,6EACpC;AACF;AAEA,MAAa,iBAAiB;;;;;;;AAO5B,MAAM,IAAI,UAA8B,MAAoC;CAC1E,MAAM,cAAc,KAAK,QAAQ,KAAK;CACtC,MAAM,aAAa,SAAS,MAAM,MAAM,UAAU;CAClD,MAAM,aAAa,SAAS,MAAM;CAGlC,IAAI,KAAK,QAAQ;EACf,QAAQ,IAAI,aAAa,SAAS,KAAK,0BAA0B;EACjE,QAAQ,IAAI,UAAU,WAAW,wBAAwB,aAAa,GAAG;EACzE,QAAQ,IAAI,EAAE;EACd,KAAK,MAAM,QAAQ,YACjB,QAAQ,IAAI,QAAQ,KAAK,MAAM,GAAG,WAAW,IAAI,KAAK,KAAK,YAAY,KAAK,UAAU,KAAK,MAAM,EAAE,EAAE;EAEvG,QAAQ,IAAI,EAAE;EACd,QAAQ,IAAI,mEAAmE;EAC/E;CACF;CAGA,MAAM,SAAS,MAAM,WAAW;CAChC,MAAM,WAAW,MAAM,yBAAyB,MAAM;CAGtD,IAAI;CACJ,IAAI,KAAK,QAEP,UAAS,MADkB,UAAU,IAAI,KAAK,MAAM,GAC9B;MACjB;EAML,UAAS,MALa,UAAU,OAAO;GACrC,MAAM,SAAS,SAAS,KAAK,GAAG,KAAK,IAAI;GACzC,MAAM;IAAC;IAAS;IAAY,SAAS;GAAI;GACzC,aAAa,0CAA0C,SAAS;EAClE,CAAC,GACgB;EACjB,QAAQ,IAAI,uBAAuB,QAAQ;CAC7C;CAGA,MAAM,SAAS,IAAI,OAAO;EAAE,MAAM;EAA2B,SAAS;CAAgB,CAAC;CACvF,MAAM,OAAO,QACX,IAAI,8BAA8B,IAAI,IAAI,OAAO,WAAW,GAAG,EAAE,OAAO,SAAS,CAAC,CACpF;CAEA,IAAI,gBAAgB;CAEpB,IAAI;EACF,MAAM,kBAAkB,QAAQ,UAAU;EAG1C,KAAK,MAAM,QAAQ,YAAY;GAC7B,QAAQ,IAAI,QAAQ,KAAK,MAAM,GAAG,WAAW,IAAI,KAAK,MAAM,IAAI;GAEhE,IAAI;GACJ,IAAI;IACF,SAAS,MAAM,cAAc,QAAQ,KAAK,MAAM,KAAK,MAAM;GAC7D,SAAS,KAAK;IACZ,IAAI,eAAe,GAAG,GACpB,IAAI,QAAQ,MAAM,OAAO;KAEvB,MAAM,EAAE,oBAAoB,MAAM,OAAO;KACzC,MAAM,KAAK,gBAAgB;MAAE,OAAO,QAAQ;MAAO,QAAQ,QAAQ;KAAO,CAAC;KAC3E,MAAM,SAAS,MAAM,IAAI,SAAgB,YAAW;MAClD,GAAG,SAAS,6BAA6B,KAAK,MAAM,yBAAyB,OAAO;KACtF,CAAC;KACD,GAAG,MAAM;KAET,IAAI,OAAO,KAAK,EAAE,YAAY,MAAM,SAClC,SAAS,MAAM,cAAc,QAAQ,KAAK,MAAM,KAAK,MAAM;UACtD,IAAI,OAAO,KAAK,EAAE,YAAY,MAAM,QAAQ;MACjD,QAAQ,IAAI,QAAQ,KAAK,MAAM,UAAU;MACzC;KACF,OACE,MAAM,IAAI,MAAM,mBAAmB,KAAK,MAAM,yBAAyB;IAE3E,OAEE,MAAM,IAAI,MACR,6BAA6B,KAAK,MAAM,kKAE1C;SAEG;KAGL,MAAM,eADiB,KAAK,QAAQ,IAAI,aACF,IAClC,oBAAoB,aAAa,EAAE,IAAI,KAAK,QAAQ,EAAE,KACtD;KACJ,QAAQ,MACN,QAAQ,KAAK,MAAM,WAAY,IAAc,QAAQ,IAClD,aAAa,mBAAmB,KAAK,MAAM,YAChD;KACA,MAAM;IACR;GACF;GAGA,MAAM,cAAc,OAAO,QAAQ;IACjC,QAAQ,KAAK;IACb,SAAS;IACT,aAAa,KAAK,UAAU,KAAK,MAAM;GACzC,CAAC;GACD;GACA,QAAQ,IAAI,MAAM,OAAO,OAAO,eAAe;EACjD;EAGA,IAAI,SAAS,SAAS,eACpB,IAAI;GACF,MAAM,MAAM,MAAM,sBAAsB,EAAE,OAAO,CAAC;GAClD,QAAQ,IAAI,4BAA4B,IAAI,UAAU;EACxD,QAAQ;GACN,QAAQ,IAAI,mCAAmC;EACjD;EAIF,QAAQ,IAAI,4BAA4B,OAAO,cAAc,cAAc,UAAU;CACvF,UAAU;EACR,MAAM,OAAO,MAAM;CACrB;AACF,EACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_version = require("./version-CO9Or_YV.cjs");
|
|
2
|
-
const require_config = require("./config-
|
|
2
|
+
const require_config = require("./config-CkW404Cs.cjs");
|
|
3
3
|
const require_client = require("./client-Y_zqKqJT.cjs");
|
|
4
4
|
const require_viz = require("./viz-D1620cBX.cjs");
|
|
5
5
|
const require_store = require("./store-CQhU8dz8.cjs");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { i as validateMcpEndpoint, n as LOCAL_LEGACY_MCP_ENDPOINT, t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-
|
|
2
|
+
import { i as validateMcpEndpoint, n as LOCAL_LEGACY_MCP_ENDPOINT, t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-QQ5Lbqc2.mjs";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import * as z from "zod";
|
|
@@ -57,4 +57,4 @@ const CONFIG_KEYS = [
|
|
|
57
57
|
//#endregion
|
|
58
58
|
export { parseInvestigatorConfig as n, schema_exports as r, DEFAULT_CONFIG as t };
|
|
59
59
|
|
|
60
|
-
//# sourceMappingURL=schema-
|
|
60
|
+
//# sourceMappingURL=schema-D_qwaQA5.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-
|
|
1
|
+
{"version":3,"file":"schema-D_qwaQA5.mjs","names":[],"sources":["../src/config/schema.ts"],"sourcesContent":["import * as z from 'zod'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { LOCAL_GRAPH_MCP_ENDPOINT, LOCAL_LEGACY_MCP_ENDPOINT, validateMcpEndpoint } from './mcp-endpoint.js'\n\nfunction endpointSchema(key: 'mcpEndpoint' | 'graphMcpEndpoint') {\n return z.string().transform((value, ctx) => {\n try {\n return validateMcpEndpoint(value, key)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: (err as Error).message,\n })\n return z.NEVER\n }\n })\n}\n\nexport const ConfigSchema = z.object({\n mcpEndpoint: endpointSchema('mcpEndpoint').default(LOCAL_LEGACY_MCP_ENDPOINT),\n mcpAuthToken: z.string().optional(),\n graphMcpEndpoint: endpointSchema('graphMcpEndpoint').default(LOCAL_GRAPH_MCP_ENDPOINT),\n graphMcpAuthToken: z.string().optional(),\n graphMcpMode: z.enum(['paid', 'debug']).default('paid'),\n walletAddress: z.string().optional(),\n serverPort: z.number().int().min(1024).max(65535).default(4321),\n dataDir: z.string().default(path.join(os.homedir(), '.chain-insights')),\n version: z.string().default('1'),\n})\n\nexport type InvestigatorConfig = z.infer<typeof ConfigSchema>\n\nfunction formatConfigValidationError(error: z.ZodError): string {\n return error.issues.map((issue) => issue.message).join('\\n')\n}\n\nexport function parseInvestigatorConfig(input: unknown): InvestigatorConfig {\n const parsed = ConfigSchema.safeParse(input)\n if (parsed.success) return parsed.data\n throw new Error(formatConfigValidationError(parsed.error))\n}\n\nexport const DEFAULT_CONFIG: InvestigatorConfig = parseInvestigatorConfig({})\n\nexport const CONFIG_KEYS = [\n 'mcpEndpoint',\n 'mcpAuthToken',\n 'graphMcpEndpoint',\n 'graphMcpAuthToken',\n 'graphMcpMode',\n 'walletAddress',\n 'serverPort',\n 'dataDir',\n 'version',\n] as const\n\nexport type ConfigKey = typeof CONFIG_KEYS[number]\n"],"mappings":";;;;;;;;;;;;AAKA,SAAS,eAAe,KAAyC;CAC/D,OAAO,EAAE,OAAO,EAAE,WAAW,OAAO,QAAQ;EAC1C,IAAI;GACF,OAAO,oBAAoB,OAAO,GAAG;EACvC,SAAS,KAAK;GACZ,IAAI,SAAS;IACX,MAAM,EAAE,aAAa;IACrB,SAAU,IAAc;GAC1B,CAAC;GACD,OAAO,EAAE;EACX;CACF,CAAC;AACH;AAEA,MAAa,eAAe,EAAE,OAAO;CACnC,aAAmB,eAAe,aAAa,EAAE,QAAQ,yBAAyB;CAClF,cAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,kBAAmB,eAAe,kBAAkB,EAAE,QAAQ,wBAAwB;CACtF,mBAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,cAAmB,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM;CAC3D,eAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,YAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,EAAE,QAAQ,IAAI;CACrE,SAAmB,EAAE,OAAO,EAAE,QAAQ,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB,CAAC;CAChF,SAAmB,EAAE,OAAO,EAAE,QAAQ,GAAG;AAC3C,CAAC;AAID,SAAS,4BAA4B,OAA2B;CAC9D,OAAO,MAAM,OAAO,KAAK,UAAU,MAAM,OAAO,EAAE,KAAK,IAAI;AAC7D;AAEA,SAAgB,wBAAwB,OAAoC;CAC1E,MAAM,SAAS,aAAa,UAAU,KAAK;CAC3C,IAAI,OAAO,SAAS,OAAO,OAAO;CAClC,MAAM,IAAI,MAAM,4BAA4B,OAAO,KAAK,CAAC;AAC3D;AAEA,MAAa,iBAAqC,wBAAwB,CAAC,CAAC;AAE5E,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_mcp_endpoint = require("./mcp-endpoint-
|
|
2
|
+
const require_mcp_endpoint = require("./mcp-endpoint-cQIZSjkK.cjs");
|
|
3
3
|
let node_path = require("node:path");
|
|
4
4
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
5
5
|
let node_os = require("node:os");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { t as createApp } from "./app-
|
|
2
|
+
import { t as createApp } from "./app-norpwdou.mjs";
|
|
3
3
|
import { serve } from "@hono/node-server";
|
|
4
4
|
//#region src/server/index.ts
|
|
5
5
|
var server_exports = /* @__PURE__ */ __exportAll({ startServer: () => startServer });
|
|
@@ -42,4 +42,4 @@ function startServer(port = 4321) {
|
|
|
42
42
|
//#endregion
|
|
43
43
|
export { startServer as n, server_exports as t };
|
|
44
44
|
|
|
45
|
-
//# sourceMappingURL=server-
|
|
45
|
+
//# sourceMappingURL=server-86dyCsJO.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-
|
|
1
|
+
{"version":3,"file":"server-86dyCsJO.mjs","names":[],"sources":["../src/server/index.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { createApp } from './app.js'\n\nexport function startServer(port = 4321): () => void {\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1', // localhost-only — REQUIRED (default 0.0.0.0 is insecure)\n port,\n })\n\n server.on('listening', () => {\n console.log(`Chain Insights server running on http://127.0.0.1:${port}`)\n })\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n process.stderr.write(`Port already in use: 127.0.0.1:${port}\\n`)\n } else {\n process.stderr.write(`Chain Insights server failed: ${err.message}\\n`)\n }\n process.exitCode = 1\n })\n\n let stopped = false\n const stop = (callback?: () => void): void => {\n if (stopped) {\n callback?.()\n return\n }\n stopped = true\n process.off('SIGINT', onSigint)\n process.off('SIGTERM', onSigterm)\n server.close(callback)\n }\n const onSigint = () => { stop(); process.exit(0) }\n const onSigterm = () => { stop(() => process.exit(0)) }\n\n process.on('SIGINT', onSigint)\n process.on('SIGTERM', onSigterm)\n\n return stop\n}\n"],"mappings":";;;;;AAGA,SAAgB,YAAY,OAAO,MAAkB;CAEnD,MAAM,SAAS,MAAM;EACnB,OAFa,UAED,EAAE;EACd,UAAU;EACV;CACF,CAAC;CAED,OAAO,GAAG,mBAAmB;EAC3B,QAAQ,IAAI,qDAAqD,MAAM;CACzE,CAAC;CACD,OAAO,GAAG,UAAU,QAA+B;EACjD,IAAI,IAAI,SAAS,cACf,QAAQ,OAAO,MAAM,kCAAkC,KAAK,GAAG;OAE/D,QAAQ,OAAO,MAAM,iCAAiC,IAAI,QAAQ,GAAG;EAEvE,QAAQ,WAAW;CACrB,CAAC;CAED,IAAI,UAAU;CACd,MAAM,QAAQ,aAAgC;EAC5C,IAAI,SAAS;GACX,WAAW;GACX;EACF;EACA,UAAU;EACV,QAAQ,IAAI,UAAU,QAAQ;EAC9B,QAAQ,IAAI,WAAW,SAAS;EAChC,OAAO,MAAM,QAAQ;CACvB;CACA,MAAM,iBAAiB;EAAE,KAAK;EAAG,QAAQ,KAAK,CAAC;CAAE;CACjD,MAAM,kBAAkB;EAAE,WAAW,QAAQ,KAAK,CAAC,CAAC;CAAE;CAEtD,QAAQ,GAAG,UAAU,QAAQ;CAC7B,QAAQ,GAAG,WAAW,SAAS;CAE/B,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_app = require("./app-
|
|
2
|
+
const require_app = require("./app-DjJn3irw.cjs");
|
|
3
3
|
let _hono_node_server = require("@hono/node-server");
|
|
4
4
|
//#region src/server/index.ts
|
|
5
5
|
var server_exports = /* @__PURE__ */ require_chunk.__exportAll({ startServer: () => startServer });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chain-insights",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.11",
|
|
4
4
|
"description": "AML investigation CLI and MCP proxy for blockchain risk, fund-flow tracing, case reports, and GraphRAG MCP access",
|
|
5
5
|
"homepage": "https://chain-insights.ai",
|
|
6
6
|
"repository": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-endpoint-DHs1cRFH.mjs","names":[],"sources":["../src/config/mcp-endpoint.ts"],"sourcesContent":["import { isIP } from 'node:net'\n\nconst LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '::1', '[::1]'])\n\nexport const LOCAL_GRAPH_MCP_ENDPOINT = 'http://127.0.0.1:8012/mcp'\nexport const LOCAL_LEGACY_MCP_ENDPOINT = 'http://127.0.0.1:4000'\n\nfunction isLoopbackHost(hostname: string): boolean {\n const normalized = hostname.toLowerCase().replace(/^\\[(.*)\\]$/, '$1')\n if (LOOPBACK_HOSTS.has(normalized)) return true\n if (isIP(normalized) !== 4) return false\n return normalized.split('.')[0] === '127'\n}\n\nexport function graphMcpEndpointEnvOverride(): string | undefined {\n const envEndpoint = process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim()\n || process.env.GRAPH_MCP_ENDPOINT?.trim()\n return envEndpoint ? validateMcpEndpoint(envEndpoint, 'graphMcpEndpoint') : undefined\n}\n\nexport function validateMcpEndpoint(value: string, key: string): string {\n const trimmed = value.trim()\n if (!trimmed) {\n throw new Error(`${key} must be a non-empty absolute URL.`)\n }\n\n let parsed: URL\n try {\n parsed = new URL(trimmed)\n } catch {\n throw new Error(\n `${key} must be an absolute URL (example: https://mcp.example.com/mcp or http://127.0.0.1:8012/mcp).`,\n )\n }\n\n if (parsed.username || parsed.password) {\n throw new Error(`${key} must not include URL credentials.`)\n }\n if (parsed.search || parsed.hash) {\n throw new Error(`${key} must not include query parameters or URL fragments.`)\n }\n if (parsed.protocol !== 'https:' && parsed.protocol !== 'http:') {\n throw new Error(`${key} must use either https:// or http://.`)\n }\n if (parsed.protocol === 'http:' && !isLoopbackHost(parsed.hostname)) {\n throw new Error(\n `${key} must use https:// for remote hosts. http:// is allowed only for localhost or loopback addresses.`,\n )\n }\n\n return trimmed\n}\n"],"mappings":";;AAEA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;AAAO,CAAC;AAEzE,MAAa,2BAA2B;AACxC,MAAa,4BAA4B;AAEzC,SAAS,eAAe,UAA2B;CACjD,MAAM,aAAa,SAAS,YAAY,EAAE,QAAQ,cAAc,IAAI;CACpE,IAAI,eAAe,IAAI,UAAU,GAAG,OAAO;CAC3C,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO;CACnC,OAAO,WAAW,MAAM,GAAG,EAAE,OAAO;AACtC;AAEA,SAAgB,8BAAkD;CAChE,MAAM,cAAc,QAAQ,IAAI,mCAAmC,KAAK,KACnE,QAAQ,IAAI,oBAAoB,KAAK;CAC1C,OAAO,cAAc,oBAAoB,aAAa,kBAAkB,IAAI,KAAA;AAC9E;AAEA,SAAgB,oBAAoB,OAAe,KAAqB;CACtE,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,CAAC,SACH,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAG5D,IAAI;CACJ,IAAI;EACF,SAAS,IAAI,IAAI,OAAO;CAC1B,QAAQ;EACN,MAAM,IAAI,MACR,GAAG,IAAI,8FACT;CACF;CAEA,IAAI,OAAO,YAAY,OAAO,UAC5B,MAAM,IAAI,MAAM,GAAG,IAAI,mCAAmC;CAE5D,IAAI,OAAO,UAAU,OAAO,MAC1B,MAAM,IAAI,MAAM,GAAG,IAAI,qDAAqD;CAE9E,IAAI,OAAO,aAAa,YAAY,OAAO,aAAa,SACtD,MAAM,IAAI,MAAM,GAAG,IAAI,sCAAsC;CAE/D,IAAI,OAAO,aAAa,WAAW,CAAC,eAAe,OAAO,QAAQ,GAChE,MAAM,IAAI,MACR,GAAG,IAAI,kGACT;CAGF,OAAO;AACT"}
|