chain-insights 0.22.2 → 0.22.3

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.
Files changed (34) hide show
  1. package/README.md +8 -7
  2. package/dist/{app-l9JE6bt3.cjs → app-BHRl5oDo.cjs} +1 -1
  3. package/dist/{app-7NRCqx_Q.mjs → app-BeZyCVuz.mjs} +2 -2
  4. package/dist/{app-7NRCqx_Q.mjs.map → app-BeZyCVuz.mjs.map} +1 -1
  5. package/dist/{artifact-server-CH-asxF5.cjs → artifact-server-B8WKXtXr.cjs} +1 -1
  6. package/dist/{artifact-server-DoxJr0z8.mjs → artifact-server-C1fETWsc.mjs} +2 -2
  7. package/dist/{artifact-server-DoxJr0z8.mjs.map → artifact-server-C1fETWsc.mjs.map} +1 -1
  8. package/dist/cli.cjs +19 -19
  9. package/dist/cli.mjs +19 -19
  10. package/dist/{config-BVFf3v8-.cjs → config-DYiUgdzg.cjs} +2 -2
  11. package/dist/{config-CnNMdt4M.mjs → config-DiQs0rQX.mjs} +3 -3
  12. package/dist/{config-CnNMdt4M.mjs.map → config-DiQs0rQX.mjs.map} +1 -1
  13. package/dist/index.cjs +3 -3
  14. package/dist/index.mjs +3 -3
  15. package/dist/{init-DvrKxK15.cjs → init-C3AVZHFb.cjs} +2 -2
  16. package/dist/{init-Djhml4ih.mjs → init-CO-PRYhT.mjs} +3 -3
  17. package/dist/init-CO-PRYhT.mjs.map +1 -0
  18. package/dist/{mcp-endpoint-DbWtLcba.mjs → mcp-endpoint-Bd3SvCkg.mjs} +3 -3
  19. package/dist/mcp-endpoint-Bd3SvCkg.mjs.map +1 -0
  20. package/dist/{mcp-endpoint-BIWrUOFG.cjs → mcp-endpoint-DdIVQKW5.cjs} +3 -3
  21. package/dist/mcp-proxy.cjs +3 -3
  22. package/dist/mcp-proxy.mjs +3 -3
  23. package/dist/{schema-BpeV3xDV.cjs → schema-DepyuUEP.cjs} +2 -2
  24. package/dist/{schema-DOJA2nNi.mjs → schema-v5cwychf.mjs} +3 -3
  25. package/dist/schema-v5cwychf.mjs.map +1 -0
  26. package/dist/{server-mDSRgk1X.mjs → server-BDmHAF1w.mjs} +2 -2
  27. package/dist/{server-mDSRgk1X.mjs.map → server-BDmHAF1w.mjs.map} +1 -1
  28. package/dist/{server-CmvzYu7v.cjs → server-BjprY9Ef.cjs} +1 -1
  29. package/docs/architecture.md +3 -2
  30. package/docs/mcp-proxy.md +7 -9
  31. package/package.json +1 -1
  32. package/dist/init-Djhml4ih.mjs.map +0 -1
  33. package/dist/mcp-endpoint-DbWtLcba.mjs.map +0 -1
  34. package/dist/schema-DOJA2nNi.mjs.map +0 -1
package/README.md CHANGED
@@ -80,8 +80,8 @@ Never touches:
80
80
  Upstream:
81
81
 
82
82
  - **Chain Insights Graph MCP endpoint** — all graph queries and AML
83
- primitives. Configured via `graphMcpEndpoint`; defaults to a local
84
- endpoint.
83
+ primitives. Configured via `graphMcpEndpoint`; defaults to the public
84
+ production endpoint `https://mcp.chain-insights.ai/`.
85
85
  - **Base mainnet RPC** — wallet balance and payment only
86
86
  (`BASE_RPC_URL` override). Not a graph-support claim.
87
87
 
@@ -253,16 +253,17 @@ More query examples (manual fund-flow reads, pagination):
253
253
  ## Configure
254
254
 
255
255
  `cia` uses `graphMcpEndpoint` for all Chain Insights Graph calls. The npm
256
- package does not hardcode a hosted endpoint.
256
+ package uses the public production endpoint by default, so a fresh install can
257
+ run `cia networks` immediately.
257
258
 
258
- Local development endpoint (default):
259
+ For local development, override the default explicitly:
259
260
 
260
261
  ```bash
261
262
  cia config set graphMcpEndpoint http://127.0.0.1:8012/mcp
262
263
  ```
263
264
 
264
- Public production Graph (operator configuration, never a package default).
265
- Use the host root. Do not add `/mcp`.
265
+ The public production Graph is already the default. Use the host root. Do not
266
+ add `/mcp`.
266
267
 
267
268
  ```bash
268
269
  cia config set graphMcpEndpoint https://mcp.chain-insights.ai/
@@ -279,7 +280,7 @@ Configuration precedence:
279
280
  1. `CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT` env var (`GRAPH_MCP_ENDPOINT`
280
281
  legacy alias also supported).
281
282
  2. `cia config set graphMcpEndpoint ...` saved value.
282
- 3. Local default `http://127.0.0.1:8012/mcp`.
283
+ 3. Public production default `https://mcp.chain-insights.ai/`.
283
284
 
284
285
  Validation rules:
285
286
 
@@ -84,7 +84,7 @@ function createApp() {
84
84
  ts: Date.now()
85
85
  }));
86
86
  app.get("/status", async (c) => {
87
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
87
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
88
88
  const config = await loadConfig();
89
89
  return c.json({
90
90
  dataDir: config.dataDir,
@@ -82,7 +82,7 @@ function createApp() {
82
82
  ts: Date.now()
83
83
  }));
84
84
  app.get("/status", async (c) => {
85
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
85
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
86
86
  const config = await loadConfig();
87
87
  return c.json({
88
88
  dataDir: config.dataDir,
@@ -137,4 +137,4 @@ function createApp() {
137
137
  //#endregion
138
138
  export { createApp as t };
139
139
 
140
- //# sourceMappingURL=app-7NRCqx_Q.mjs.map
140
+ //# sourceMappingURL=app-BeZyCVuz.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-7NRCqx_Q.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 { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = [\n 'artifacts',\n 'entities',\n 'reports',\n 'sessions',\n '.chain-insights/schema',\n]\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()\n ? 'symlink'\n : info.isDirectory()\n ? 'directory'\n : info.isFile()\n ? 'file'\n : 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 filename = `${vizId}.html`\n const paths = workspaceOutputPaths()\n const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n try {\n return await readFile(vizPath, 'utf-8')\n } catch {\n return null\n }\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-BeZyCVuz.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 { workspaceOutputPaths } from '../workspace/output-root.js'\n\nconst WORKSPACE_TREE_ROOTS = [\n 'artifacts',\n 'entities',\n 'reports',\n 'sessions',\n '.chain-insights/schema',\n]\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()\n ? 'symlink'\n : info.isDirectory()\n ? 'directory'\n : info.isFile()\n ? 'file'\n : 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 filename = `${vizId}.html`\n const paths = workspaceOutputPaths()\n const vizPath = path.join(paths.publishedRoot, 'viz', filename)\n try {\n return await readFile(vizPath, 'utf-8')\n } catch {\n return null\n }\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
- const require_app = require("./app-l9JE6bt3.cjs");
1
+ const require_app = require("./app-BHRl5oDo.cjs");
2
2
  let _hono_node_server = require("@hono/node-server");
3
3
  let node_timers_promises = require("node:timers/promises");
4
4
  //#region src/mcp/artifact-server.ts
@@ -1,4 +1,4 @@
1
- import { t as createApp } from "./app-7NRCqx_Q.mjs";
1
+ import { t as createApp } from "./app-BeZyCVuz.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
@@ -46,4 +46,4 @@ async function ensureArtifactServer(port) {
46
46
  //#endregion
47
47
  export { ensureArtifactServer };
48
48
 
49
- //# sourceMappingURL=artifact-server-DoxJr0z8.mjs.map
49
+ //# sourceMappingURL=artifact-server-C1fETWsc.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-server-DoxJr0z8.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(\n `Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`\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,EAAA,CACe;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;CAE3B,MAAM,MAAM,UAAU;CACtB,MAAM,SAAS,MAAM;EACnB,OAAO,IAAI;EACX,UAAU;EACV;CACF,CAAC;CACD,QAAQ,IAAI,MAAM,MAAM;CACxB,OAAO,GAAG,UAAU,QAAQ;EAC1B,QAAQ,OAAO,IAAI;EACnB,QAAQ,OAAO,MACb,0DAA0D,KAAK,IAAK,IAAc,QAAQ,GAC5F;CACF,CAAC;CAED,IAAI;EACF,MAAM,iBAAiB,IAAI;CAC7B,SAAS,KAAK;EACZ,QAAQ,OAAO,IAAI;EACnB,OAAO,MAAM;EACb,MAAM;CACR;AACF"}
1
+ {"version":3,"file":"artifact-server-C1fETWsc.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(\n `Chain Insights graph report server failed on 127.0.0.1:${port}: ${(err as Error).message}\\n`\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,EAAA,CACe;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;CAE3B,MAAM,MAAM,UAAU;CACtB,MAAM,SAAS,MAAM;EACnB,OAAO,IAAI;EACX,UAAU;EACV;CACF,CAAC;CACD,QAAQ,IAAI,MAAM,MAAM;CACxB,OAAO,GAAG,UAAU,QAAQ;EAC1B,QAAQ,OAAO,IAAI;EACnB,QAAQ,OAAO,MACb,0DAA0D,KAAK,IAAK,IAAc,QAAQ,GAC5F;CACF,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
@@ -50,7 +50,7 @@ function runInstaller(flag) {
50
50
  }
51
51
  }
52
52
  async function withGraphMcpClient(name, fn) {
53
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
53
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
54
54
  const config = await loadConfig();
55
55
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-CqAzD519.cjs")).then((n) => n.client_exports);
56
56
  const paymentFetch = await createConfiguredGraphMcpFetch(config);
@@ -70,7 +70,7 @@ async function withGraphMcpClient(name, fn) {
70
70
  }
71
71
  }
72
72
  async function printNetworkCapabilities(opts) {
73
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
73
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
74
74
  const { fetchNetworkCapabilities, formatNetworkCapabilities } = await Promise.resolve().then(() => require("./capabilities-CPcrxodx.cjs")).then((n) => n.capabilities_exports);
75
75
  const document = await fetchNetworkCapabilities(await loadConfig());
76
76
  if (opts.json) console.log(JSON.stringify(document, null, 2));
@@ -88,11 +88,11 @@ program.command("serve").description("Start local visualization server").option(
88
88
  try {
89
89
  const { requireWorkspaceRoot } = await Promise.resolve().then(() => require("./output-root-BewoQaTx.cjs")).then((n) => n.output_root_exports);
90
90
  const workspaceRoot = requireWorkspaceRoot();
91
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
91
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
92
92
  const { resolveServerPort } = await Promise.resolve().then(() => require("./resolve-port-CbaEoODz.cjs"));
93
93
  const config = await loadConfig();
94
94
  const port = resolveServerPort(opts.port, config.serverPort);
95
- const { startServer } = await Promise.resolve().then(() => require("./server-CmvzYu7v.cjs")).then((n) => n.server_exports);
95
+ const { startServer } = await Promise.resolve().then(() => require("./server-BjprY9Ef.cjs")).then((n) => n.server_exports);
96
96
  console.log(`Workspace: ${workspaceRoot}`);
97
97
  startServer(port);
98
98
  } catch (err) {
@@ -101,7 +101,7 @@ program.command("serve").description("Start local visualization server").option(
101
101
  }
102
102
  });
103
103
  program.command("status").description("Show toolkit status and configuration").action(async () => {
104
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
104
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
105
105
  const { findActiveWorkspace, activeDataDir } = await Promise.resolve().then(() => require("./active-Bc13hJIm.cjs")).then((n) => n.active_exports);
106
106
  const config = await loadConfig();
107
107
  const workspace = findActiveWorkspace();
@@ -139,7 +139,7 @@ program.command("update").description("Check npmjs for a newer Chain Insights re
139
139
  });
140
140
  program.command("debug").description("Configure Chain Insights Graph debug mode").addCommand(new commander.Command("on").description("Enable Chain Insights Graph debug mode without x402 payments").requiredOption("--token <token>", "Debug bearer token").option("--endpoint <url>", "Chain Insights Graph endpoint").action(async (opts) => {
141
141
  try {
142
- const { saveConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
142
+ const { saveConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
143
143
  await saveConfig({
144
144
  graphMcpMode: "debug",
145
145
  graphMcpAuthToken: opts.token,
@@ -154,7 +154,7 @@ program.command("debug").description("Configure Chain Insights Graph debug mode"
154
154
  }
155
155
  })).addCommand(new commander.Command("off").description("Disable Chain Insights Graph debug mode and use paid x402 calls").action(async () => {
156
156
  try {
157
- const { saveConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
157
+ const { saveConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
158
158
  await saveConfig({
159
159
  graphMcpMode: "paid",
160
160
  graphMcpAuthToken: ""
@@ -167,7 +167,7 @@ program.command("debug").description("Configure Chain Insights Graph debug mode"
167
167
  }
168
168
  })).addCommand(new commander.Command("status").description("Show Chain Insights Graph payment/debug mode").action(async () => {
169
169
  try {
170
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
170
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
171
171
  const config = await loadConfig();
172
172
  console.log(`Chain Insights Graph mode: ${config.graphMcpMode}`);
173
173
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
@@ -182,7 +182,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
182
182
  try {
183
183
  const normalizedKey = key.trim();
184
184
  if (!normalizedKey) throw new Error("Test access key is required");
185
- const { saveConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
185
+ const { saveConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
186
186
  await saveConfig({
187
187
  graphMcpMode: "debug",
188
188
  graphMcpAuthToken: normalizedKey,
@@ -197,7 +197,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
197
197
  }
198
198
  })).addCommand(new commander.Command("clear").description("Remove the Chain Insights Graph test access key and use paid x402 calls").action(async () => {
199
199
  try {
200
- const { saveConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
200
+ const { saveConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
201
201
  await saveConfig({
202
202
  graphMcpMode: "paid",
203
203
  graphMcpAuthToken: ""
@@ -210,7 +210,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
210
210
  }
211
211
  })).addCommand(new commander.Command("status").description("Show Chain Insights Graph test access key status").action(async () => {
212
212
  try {
213
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
213
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
214
214
  const config = await loadConfig();
215
215
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
216
216
  console.log(`Access key: ${config.graphMcpAuthToken?.trim() ? "configured" : "not configured"}`);
@@ -222,7 +222,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
222
222
  }));
223
223
  program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
224
224
  try {
225
- const { initWorkspace } = await Promise.resolve().then(() => require("./init-DvrKxK15.cjs"));
225
+ const { initWorkspace } = await Promise.resolve().then(() => require("./init-C3AVZHFb.cjs"));
226
226
  const result = await initWorkspace({
227
227
  targetDir: dir,
228
228
  force: opts.force
@@ -244,8 +244,8 @@ program.command("setup").description("Configure external MCP clients").addComman
244
244
  runInstaller("--hermes");
245
245
  }));
246
246
  program.command("config").description("Read or write configuration values").addCommand(new commander.Command("get").argument("<key>", "Config key to read").action(async (key) => {
247
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
248
- const { CONFIG_KEYS } = await Promise.resolve().then(() => require("./schema-BpeV3xDV.cjs")).then((n) => n.schema_exports);
247
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
248
+ const { CONFIG_KEYS } = await Promise.resolve().then(() => require("./schema-DepyuUEP.cjs")).then((n) => n.schema_exports);
249
249
  if (!CONFIG_KEYS.includes(key)) {
250
250
  console.error(`Unknown config key: ${key}`);
251
251
  process.exit(1);
@@ -265,8 +265,8 @@ program.command("config").description("Read or write configuration values").addC
265
265
  }
266
266
  return;
267
267
  }
268
- const { loadConfig, saveConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
269
- const { CONFIG_KEYS, DEFAULT_CONFIG } = await Promise.resolve().then(() => require("./schema-BpeV3xDV.cjs")).then((n) => n.schema_exports);
268
+ const { loadConfig, saveConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
269
+ const { CONFIG_KEYS, DEFAULT_CONFIG } = await Promise.resolve().then(() => require("./schema-DepyuUEP.cjs")).then((n) => n.schema_exports);
270
270
  const current = await loadConfig();
271
271
  if (!CONFIG_KEYS.includes(key)) {
272
272
  console.error(`Unknown config key: ${key}`);
@@ -360,7 +360,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
360
360
  const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-n7Gbhvsj.cjs"));
361
361
  const { formatToolsTable } = await Promise.resolve().then(() => require("./format-9NLBykEL.cjs"));
362
362
  const { visibleRemoteTools } = await Promise.resolve().then(() => require("./tool-visibility-DZBAP62b.cjs")).then((n) => n.tool_visibility_exports);
363
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
363
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
364
364
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-CqAzD519.cjs")).then((n) => n.client_exports);
365
365
  const config = await loadConfig();
366
366
  const graphMcpEndpoint = resolveGraphMcpEndpoint(config);
@@ -462,7 +462,7 @@ program.command("viz").description("Generate a workspace visualization").argumen
462
462
  console.error("Provide either a visualization source ID or --data <file.json>");
463
463
  process.exit(1);
464
464
  }
465
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
465
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
466
466
  const { resolveServerPort } = await Promise.resolve().then(() => require("./resolve-port-CbaEoODz.cjs"));
467
467
  const config = await loadConfig();
468
468
  const port = resolveServerPort(opts.port, config.serverPort);
@@ -471,7 +471,7 @@ program.command("viz").description("Generate a workspace visualization").argumen
471
471
  sourceId,
472
472
  dataFile: opts.data
473
473
  });
474
- const { startServer } = await Promise.resolve().then(() => require("./server-CmvzYu7v.cjs")).then((n) => n.server_exports);
474
+ const { startServer } = await Promise.resolve().then(() => require("./server-BjprY9Ef.cjs")).then((n) => n.server_exports);
475
475
  startServer(port);
476
476
  const url = `http://127.0.0.1:${port}/viz/${result.vizId}`;
477
477
  console.log(`Visualization: ${url}`);
package/dist/cli.mjs CHANGED
@@ -48,7 +48,7 @@ function runInstaller(flag) {
48
48
  }
49
49
  }
50
50
  async function withGraphMcpClient(name, fn) {
51
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
51
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
52
52
  const config = await loadConfig();
53
53
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4aM4bzP.mjs").then((n) => n.r);
54
54
  const paymentFetch = await createConfiguredGraphMcpFetch(config);
@@ -68,7 +68,7 @@ async function withGraphMcpClient(name, fn) {
68
68
  }
69
69
  }
70
70
  async function printNetworkCapabilities(opts) {
71
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
71
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
72
72
  const { fetchNetworkCapabilities, formatNetworkCapabilities } = await import("./capabilities-BZI6qROg.mjs").then((n) => n.t);
73
73
  const document = await fetchNetworkCapabilities(await loadConfig());
74
74
  if (opts.json) console.log(JSON.stringify(document, null, 2));
@@ -86,11 +86,11 @@ program.command("serve").description("Start local visualization server").option(
86
86
  try {
87
87
  const { requireWorkspaceRoot } = await import("./output-root-BK4pdjyz.mjs").then((n) => n.t);
88
88
  const workspaceRoot = requireWorkspaceRoot();
89
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
89
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
90
90
  const { resolveServerPort } = await import("./resolve-port-DxPK7Ipx.mjs");
91
91
  const config = await loadConfig();
92
92
  const port = resolveServerPort(opts.port, config.serverPort);
93
- const { startServer } = await import("./server-mDSRgk1X.mjs").then((n) => n.t);
93
+ const { startServer } = await import("./server-BDmHAF1w.mjs").then((n) => n.t);
94
94
  console.log(`Workspace: ${workspaceRoot}`);
95
95
  startServer(port);
96
96
  } catch (err) {
@@ -99,7 +99,7 @@ program.command("serve").description("Start local visualization server").option(
99
99
  }
100
100
  });
101
101
  program.command("status").description("Show toolkit status and configuration").action(async () => {
102
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
102
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
103
103
  const { findActiveWorkspace, activeDataDir } = await import("./active-BQopLul8.mjs").then((n) => n.t);
104
104
  const config = await loadConfig();
105
105
  const workspace = findActiveWorkspace();
@@ -137,7 +137,7 @@ program.command("update").description("Check npmjs for a newer Chain Insights re
137
137
  });
138
138
  program.command("debug").description("Configure Chain Insights Graph debug mode").addCommand(new Command("on").description("Enable Chain Insights Graph debug mode without x402 payments").requiredOption("--token <token>", "Debug bearer token").option("--endpoint <url>", "Chain Insights Graph endpoint").action(async (opts) => {
139
139
  try {
140
- const { saveConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
140
+ const { saveConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
141
141
  await saveConfig({
142
142
  graphMcpMode: "debug",
143
143
  graphMcpAuthToken: opts.token,
@@ -152,7 +152,7 @@ program.command("debug").description("Configure Chain Insights Graph debug mode"
152
152
  }
153
153
  })).addCommand(new Command("off").description("Disable Chain Insights Graph debug mode and use paid x402 calls").action(async () => {
154
154
  try {
155
- const { saveConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
155
+ const { saveConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
156
156
  await saveConfig({
157
157
  graphMcpMode: "paid",
158
158
  graphMcpAuthToken: ""
@@ -165,7 +165,7 @@ program.command("debug").description("Configure Chain Insights Graph debug mode"
165
165
  }
166
166
  })).addCommand(new Command("status").description("Show Chain Insights Graph payment/debug mode").action(async () => {
167
167
  try {
168
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
168
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
169
169
  const config = await loadConfig();
170
170
  console.log(`Chain Insights Graph mode: ${config.graphMcpMode}`);
171
171
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
@@ -180,7 +180,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
180
180
  try {
181
181
  const normalizedKey = key.trim();
182
182
  if (!normalizedKey) throw new Error("Test access key is required");
183
- const { saveConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
183
+ const { saveConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
184
184
  await saveConfig({
185
185
  graphMcpMode: "debug",
186
186
  graphMcpAuthToken: normalizedKey,
@@ -195,7 +195,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
195
195
  }
196
196
  })).addCommand(new Command("clear").description("Remove the Chain Insights Graph test access key and use paid x402 calls").action(async () => {
197
197
  try {
198
- const { saveConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
198
+ const { saveConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
199
199
  await saveConfig({
200
200
  graphMcpMode: "paid",
201
201
  graphMcpAuthToken: ""
@@ -208,7 +208,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
208
208
  }
209
209
  })).addCommand(new Command("status").description("Show Chain Insights Graph test access key status").action(async () => {
210
210
  try {
211
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
211
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
212
212
  const config = await loadConfig();
213
213
  console.log(`Graph endpoint: ${config.graphMcpEndpoint}`);
214
214
  console.log(`Access key: ${config.graphMcpAuthToken?.trim() ? "configured" : "not configured"}`);
@@ -220,7 +220,7 @@ program.command("access-key").description("Configure Chain Insights Graph test a
220
220
  }));
221
221
  program.command("init").description("Initialize an investigation workspace").argument("[dir]", "Workspace directory to initialize", ".").option("--force", "Overwrite existing workspace files").action(async (dir, opts) => {
222
222
  try {
223
- const { initWorkspace } = await import("./init-Djhml4ih.mjs");
223
+ const { initWorkspace } = await import("./init-CO-PRYhT.mjs");
224
224
  const result = await initWorkspace({
225
225
  targetDir: dir,
226
226
  force: opts.force
@@ -242,8 +242,8 @@ program.command("setup").description("Configure external MCP clients").addComman
242
242
  runInstaller("--hermes");
243
243
  }));
244
244
  program.command("config").description("Read or write configuration values").addCommand(new Command("get").argument("<key>", "Config key to read").action(async (key) => {
245
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
246
- const { CONFIG_KEYS } = await import("./schema-DOJA2nNi.mjs").then((n) => n.r);
245
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
246
+ const { CONFIG_KEYS } = await import("./schema-v5cwychf.mjs").then((n) => n.r);
247
247
  if (!CONFIG_KEYS.includes(key)) {
248
248
  console.error(`Unknown config key: ${key}`);
249
249
  process.exit(1);
@@ -263,8 +263,8 @@ program.command("config").description("Read or write configuration values").addC
263
263
  }
264
264
  return;
265
265
  }
266
- const { loadConfig, saveConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
267
- const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-DOJA2nNi.mjs").then((n) => n.r);
266
+ const { loadConfig, saveConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
267
+ const { CONFIG_KEYS, DEFAULT_CONFIG } = await import("./schema-v5cwychf.mjs").then((n) => n.r);
268
268
  const current = await loadConfig();
269
269
  if (!CONFIG_KEYS.includes(key)) {
270
270
  console.error(`Unknown config key: ${key}`);
@@ -358,7 +358,7 @@ program.command("mcp").description("Interact with the Chain Insights MCP endpoin
358
358
  const { loadSchema, saveSchema } = await import("./schema-cache-DJdU4MIP.mjs");
359
359
  const { formatToolsTable } = await import("./format-Bq94jSyw.mjs");
360
360
  const { visibleRemoteTools } = await import("./tool-visibility-C0ALejXI.mjs").then((n) => n.i);
361
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
361
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
362
362
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4aM4bzP.mjs").then((n) => n.r);
363
363
  const config = await loadConfig();
364
364
  const graphMcpEndpoint = resolveGraphMcpEndpoint(config);
@@ -460,7 +460,7 @@ program.command("viz").description("Generate a workspace visualization").argumen
460
460
  console.error("Provide either a visualization source ID or --data <file.json>");
461
461
  process.exit(1);
462
462
  }
463
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
463
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
464
464
  const { resolveServerPort } = await import("./resolve-port-DxPK7Ipx.mjs");
465
465
  const config = await loadConfig();
466
466
  const port = resolveServerPort(opts.port, config.serverPort);
@@ -469,7 +469,7 @@ program.command("viz").description("Generate a workspace visualization").argumen
469
469
  sourceId,
470
470
  dataFile: opts.data
471
471
  });
472
- const { startServer } = await import("./server-mDSRgk1X.mjs").then((n) => n.t);
472
+ const { startServer } = await import("./server-BDmHAF1w.mjs").then((n) => n.t);
473
473
  startServer(port);
474
474
  const url = `http://127.0.0.1:${port}/viz/${result.vizId}`;
475
475
  console.log(`Visualization: ${url}`);
@@ -1,6 +1,6 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_mcp_endpoint = require("./mcp-endpoint-BIWrUOFG.cjs");
3
- const require_schema = require("./schema-BpeV3xDV.cjs");
2
+ const require_mcp_endpoint = require("./mcp-endpoint-DdIVQKW5.cjs");
3
+ const require_schema = require("./schema-DepyuUEP.cjs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
6
6
  let node_fs_promises = require("node:fs/promises");
@@ -1,6 +1,6 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { n as graphMcpEndpointEnvOverride } from "./mcp-endpoint-DbWtLcba.mjs";
3
- import { n as parseInvestigatorConfig, t as DEFAULT_CONFIG } from "./schema-DOJA2nNi.mjs";
2
+ import { n as graphMcpEndpointEnvOverride } from "./mcp-endpoint-Bd3SvCkg.mjs";
3
+ import { n as parseInvestigatorConfig, t as DEFAULT_CONFIG } from "./schema-v5cwychf.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-CnNMdt4M.mjs.map
77
+ //# sourceMappingURL=config-DiQs0rQX.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-CnNMdt4M.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 ? parseInvestigatorConfig({ ...config, graphMcpEndpoint }) : 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,mBAAmB,wBAAwB;EAAE,GAAG;EAAQ;CAAiB,CAAC,IAAI;AACvF;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
+ {"version":3,"file":"config-DiQs0rQX.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 ? parseInvestigatorConfig({ ...config, graphMcpEndpoint }) : 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,mBAAmB,wBAAwB;EAAE,GAAG;EAAQ;CAAiB,CAAC,IAAI;AACvF;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"}
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_config = require("./config-BVFf3v8-.cjs");
3
- const require_app = require("./app-l9JE6bt3.cjs");
4
- const require_server = require("./server-CmvzYu7v.cjs");
2
+ const require_config = require("./config-DYiUgdzg.cjs");
3
+ const require_app = require("./app-BHRl5oDo.cjs");
4
+ const require_server = require("./server-BjprY9Ef.cjs");
5
5
  const require_wallet = require("./wallet-BEvHrHi3.cjs");
6
6
  const require_tools = require("./tools-lPY6k-vJ.cjs");
7
7
  const require_topup_server = require("./topup-server-DAqmAuu1.cjs");
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
- import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-CnNMdt4M.mjs";
2
- import { t as createApp } from "./app-7NRCqx_Q.mjs";
3
- import { n as startServer } from "./server-mDSRgk1X.mjs";
1
+ import { i as saveConfig, n as loadConfig, r as resetConfigCache } from "./config-DiQs0rQX.mjs";
2
+ import { t as createApp } from "./app-BeZyCVuz.mjs";
3
+ import { n as startServer } from "./server-BDmHAF1w.mjs";
4
4
  import { a as setWalletPrivateKey, i as normalizeWalletPrivateKey, n as encryptKey, o as walletAddressFromPrivateKey, r as isWalletConfigured, t as decryptKey } from "./wallet-fMbhvKXT.mjs";
5
5
  import { a as formatWalletBalanceResult, c as getWalletAccount, i as formatWalletBalance, l as getWalletBalanceResult, o as getBalanceEth, r as buildTopupInfo, s as getBalanceUsdc, u as getWalletBalanceText } from "./tools-BKLnlBG1.mjs";
6
6
  import { i as generateArtifactHtml, n as startTopupServer, t as getTopupUrl } from "./topup-server-rqI7bqem.mjs";
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_mcp_endpoint = require("./mcp-endpoint-BIWrUOFG.cjs");
2
+ const require_mcp_endpoint = require("./mcp-endpoint-DdIVQKW5.cjs");
3
3
  let node_path = require("node:path");
4
4
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
5
5
  let node_fs_promises = require("node:fs/promises");
@@ -30,7 +30,7 @@ function workspaceJson(workspaceRoot) {
30
30
  name: "Chain Insights Workspace",
31
31
  workspace_root: workspaceRoot,
32
32
  default_network: "robinhood",
33
- graph_mcp_endpoint: require_mcp_endpoint.LOCAL_GRAPH_MCP_ENDPOINT,
33
+ graph_mcp_endpoint: require_mcp_endpoint.DEFAULT_GRAPH_MCP_ENDPOINT,
34
34
  artifacts_dir: "artifacts",
35
35
  imports_dir: "imports",
36
36
  reports_dir: "reports",
@@ -1,4 +1,4 @@
1
- import { t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-DbWtLcba.mjs";
1
+ import { t as DEFAULT_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-Bd3SvCkg.mjs";
2
2
  import path from "node:path";
3
3
  import { access, mkdir, writeFile } from "node:fs/promises";
4
4
  //#region src/workspace/init.ts
@@ -28,7 +28,7 @@ function workspaceJson(workspaceRoot) {
28
28
  name: "Chain Insights Workspace",
29
29
  workspace_root: workspaceRoot,
30
30
  default_network: "robinhood",
31
- graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,
31
+ graph_mcp_endpoint: DEFAULT_GRAPH_MCP_ENDPOINT,
32
32
  artifacts_dir: "artifacts",
33
33
  imports_dir: "imports",
34
34
  reports_dir: "reports",
@@ -316,4 +316,4 @@ async function initWorkspace(options) {
316
316
  //#endregion
317
317
  export { initWorkspace };
318
318
 
319
- //# sourceMappingURL=init-Djhml4ih.mjs.map
319
+ //# sourceMappingURL=init-CO-PRYhT.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-CO-PRYhT.mjs","names":[],"sources":["../src/workspace/init.ts"],"sourcesContent":["import { access, mkdir, writeFile } from 'node:fs/promises'\nimport path from 'node:path'\nimport { DEFAULT_GRAPH_MCP_ENDPOINT } from '../config/mcp-endpoint.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 'artifacts',\n 'entities',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'sessions',\n 'templates',\n 'published',\n]\n\nconst DEFAULT_DOMAIN_HINTS = ['aml']\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return (\n JSON.stringify(\n {\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Workspace',\n workspace_root: workspaceRoot,\n default_network: 'robinhood',\n graph_mcp_endpoint: DEFAULT_GRAPH_MCP_ENDPOINT,\n artifacts_dir: 'artifacts',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n domain_hints: DEFAULT_DOMAIN_HINTS,\n created_at: todayIso(),\n },\n null,\n 2\n ) + '\\n'\n )\n}\n\nconst README = `# Chain Insights Workspace\n\nThis is a generic Chain Insights workspace.\nUse any editor or agent tooling you want to review reports, artifacts, entity\nnotes, graphs, and published bundles alongside the Chain Insights runtime\nmetadata.\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\nartifacts/ Tool-generated artifacts and durable workspace records\nentities/ Entity notes and indexes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\nsessions/ Optional session notes\ntemplates/ Reusable workspace templates\npublished/ Published bundles and handoff-ready exports\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\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before persistence-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 graph workflow.\n- \\`domain_hints\\` in \\`.chain-insights/workspace.json\\` are optional advisory workflow\n preferences (for example \\`[\"aml\"]\\`) and should guide, not constrain,\n tool selection.\n- Save compact artifacts with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Markdown reports should summarize and point to graph/table outputs; do not paste large raw JSON blobs into reports.\n- Workspace output must stay in this initialized workspace.\n- Never write artifacts, 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 = `# Workspace 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 material here before turning it into durable\nworkspace outputs.\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 a workspace report or artifact note and\nreference the original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, prompt, artifact, and note templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as durable workspace state until copied into an artifact, report,\nentity note, or session note.\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 topology reads with\n\\`USE topology\\` (address/FLOWS_TO/LINKED graph, unified recent+historical)\nand fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"flow_sample\",\"query\":\"USE topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.amount_usd_sum AS amount_usd_sum, f.tx_count AS tx_count LIMIT 20\"},{\"id\":\"linked_sample\",\"query\":\"USE topology MATCH (a:Address)-[l:LINKED]-(b:Address) RETURN a.address AS address, b.address AS linked_address, l.basis AS basis, l.confidence AS confidence LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nThe address-grain graph schema:\n\n- Call \\`meta_network_capabilities\\` first. Pass \\`network=\\` exactly as\n GraphRAG advertised it. CIA does not pick a default network.\n Address format follows that network. Every topology node is\n \\`(:Address {address, network})\\`, keyed by the raw chain-native\n \\`address\\`. There is no separate identity key and no member-address\n satellite: the address IS the graph node.\n- \\`(:Address)-[:LINKED]-(:Address)\\` is an **undirected** ownership-overlay\n edge (\\`basis\\` \\`derived\\`/\\`associated\\`, plus \\`confidence\\`,\n \\`source_event\\`, \\`declared_owner\\`) asserting the two addresses are\n controlled by the same actor. \\`LINKED\\` is the ownership edge within the\n advertised network — a same-network query traces\n (\\`LINKED\\` or \\`FLOWS_TO\\`) with no network switch. Walk\n one visible \\`LINKED\\` hop to surface actor-level exposure; never treat\n linked addresses\n as a single collapsed node. \\`LINKED\\` is served on the topology graph\n only.\n- Other Address properties: \\`labels\\` (array) and \\`is_exchange\\`\n (sparse true/null traversal hint). Labels and per-label risk live on the\n address node: \\`label_risk\\` is a list of \\`{label, risk_level,\n updated_timestamp}\\` maps, one per current label row.\n- Address nodes carry a risk verdict for quick triage\n (\\`risk_score\\` float, \\`risk_level\\` string) plus base activity rollups:\n \\`degree_in\\`/\\`degree_out\\`/\\`degree_total\\` (distinct counterparty\n addresses), \\`tx_in_count\\`/\\`tx_out_count\\`/\\`tx_total_count\\`,\n \\`total_in_usd\\`/\\`total_out_usd\\`/\\`total_volume_usd\\`, \\`net_flow_usd\\`\n (in minus out; positive = net receiver) — all computed from external\n flows only — and \\`first_activity_timestamp\\`/\n \\`last_activity_timestamp\\`/\\`activity_span_days\\`, which include all\n flows (self-loops included). FLOWS_TO edges carry \\`tx_count\\`,\n \\`amount_usd_sum\\`, \\`avg_tx_size_usd\\` (understates when\n \\`price_coverage_ratio\\` < 1), \\`first_seen_timestamp\\`/\n \\`last_seen_timestamp\\`, \\`first_tx_id\\`/\\`last_tx_id\\`, and\n \\`price_coverage_ratio\\`.\n Lifetime aggregates are the only serving window.\n- Money flow is \\`(:Address)-[:FLOWS_TO]->(:Address)\\`. Public AML tools\n accept the raw blockchain address directly — there is no resolution step.\n- The risk verdict lives on topology nodes (\\`risk_score\\`/\\`risk_level\\`),\n and labels and per-label risk live on the address node (\\`labels\\` array\n + \\`label_risk\\` entries). \\`USE facts\\` serves bounded individual\n transfer rows and, until P3, address features. Facts address keys match\n topology \\`address\\` values exactly. Do not read \\`ml_*\\`,\n \\`confluence_score\\`, or \\`pattern_flags\\` off topology nodes — those\n properties do not exist.\n- \\`(from:Address)-[t:TRANSFER]->(to:Address)\\` on \\`USE facts\\` returns\n individual transfer rows (not aggregates) from \\`facts_transfers_view\\`,\n with edge properties \\`amount\\`, \\`amount_usd\\`, \\`asset_symbol\\`,\n \\`asset_contract\\`, \\`tx_id\\`, \\`block_height\\`, \\`block_timestamp\\`,\n \\`event_index\\`, \\`edge_index\\`, \\`price_usd\\`, and \\`price_missing\\`.\n Every TRANSFER query (row-select or a \\`count()\\`/\\`sum()\\` aggregate)\n requires an indexed predicate — address equality on either endpoint or\n \\`WHERE t.tx_id = \"...\"\\` — a bare \\`LIMIT\\` alone is rejected. Lifetime\n address metrics (degrees, totals, activity window) are node properties on\n \\`USE topology\\`.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- The graph choice stays inside the query via \\`USE ...\\`; there is no\n tool argument to select a graph.\n- Use \\`USE topology\\` for topology (the address/FLOWS_TO/LINKED graph,\n covering unified recent and full historical activity in one graph, plus\n the node \\`risk_score\\`/\\`risk_level\\` verdict, and labels + per-label\n risk) and \\`USE facts\\` for features, assets, and enrichment.\n The \\`LINKED\\` ownership overlay is served on the topology graph only.\n- Preserve source schema field names in 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 persisted outputs compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs 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 summary notes: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Markdown reports should be short provenance records with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in reports.\n\nAML tool guidance:\n\n1. Use \\`aml_address_risk\\` for single-address enrichment and optional\n comparison with another address.\n2. Use \\`graph_query_batch\\` only when the high-level tools do not answer the\n exact question, and \\`graph_query\\` for single read-only queries.\n\n\\`aml_address_risk\\` takes a raw blockchain address as input directly — there\nis no identity-resolution step — and returns \\`chain-insights.result.v1\\`. Preserve\nfull blockchain addresses in the summary and all workspace artifacts.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nrobinhood.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first graph workflow in a fresh\nworkspace, then referenced by artifacts, 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/workspace-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(\n `Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`\n )\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 }\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(\n `Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`\n )\n }\n throw err\n }\n }\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;;AAcA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,uBAAuB,CAAC,KAAK;AAEnC,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OACE,KAAK,UACH;EACE,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,eAAe;EACf,aAAa;EACb,aAAa;EACb,eAAe;EACf,cAAc;EACd,YAAY,SAAS;CACvB,GACA,MACA,CACF,IAAI;AAER;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCf,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;AAsBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwHtB,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,gCAAgC,UAAU;EAC3C,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,MACR,yBAAyB,SAAS,kDACpC;EACF,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,OACX,MAAM,uBAAuB,aAAa;CAG5C,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,MACR,yBAAyB,SAAS,kDACpC;GAEF,MAAM;EACR;CACF;CAEA,OAAO;EAAE;EAAe;CAAa;AACvC"}
@@ -6,7 +6,7 @@ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
6
6
  "::1",
7
7
  "[::1]"
8
8
  ]);
9
- const LOCAL_GRAPH_MCP_ENDPOINT = "http://127.0.0.1:8012/mcp";
9
+ const DEFAULT_GRAPH_MCP_ENDPOINT = "https://mcp.chain-insights.ai/";
10
10
  function isLoopbackHost(hostname) {
11
11
  const normalized = hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
12
12
  if (LOOPBACK_HOSTS.has(normalized)) return true;
@@ -36,6 +36,6 @@ function validateMcpEndpoint(value, key) {
36
36
  return trimmed;
37
37
  }
38
38
  //#endregion
39
- export { graphMcpEndpointEnvOverride as n, validateMcpEndpoint as r, LOCAL_GRAPH_MCP_ENDPOINT as t };
39
+ export { graphMcpEndpointEnvOverride as n, validateMcpEndpoint as r, DEFAULT_GRAPH_MCP_ENDPOINT as t };
40
40
 
41
- //# sourceMappingURL=mcp-endpoint-DbWtLcba.mjs.map
41
+ //# sourceMappingURL=mcp-endpoint-Bd3SvCkg.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-endpoint-Bd3SvCkg.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 DEFAULT_GRAPH_MCP_ENDPOINT = 'https://mcp.chain-insights.ai/'\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 =\n process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || 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 (\n parsed.protocol === 'http:' &&\n !isLoopbackHost(parsed.hostname) &&\n !isKubernetesServiceHost(parsed.hostname)\n ) {\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,iCAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;AAAO,CAAC;AAGzE,MAAa,6BAA6B;AAE1C,SAAS,eAAe,UAA2B;CACjD,MAAM,aAAa,SAAS,YAAY,CAAC,CAAC,QAAQ,cAAc,IAAI;CACpE,IAAI,eAAe,IAAI,UAAU,GAAG,OAAO;CAC3C,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO;CACnC,OAAO,WAAW,MAAM,GAAG,CAAC,CAAC,OAAO;AACtC;AAEA,SAAS,wBAAwB,UAA2B;CAC1D,OAAO,SAAS,YAAY,CAAC,CAAC,SAAS,oBAAoB;AAC7D;AAEA,SAAgB,8BAAkD;CAChE,MAAM,cACJ,QAAQ,IAAI,mCAAmC,KAAK,KAAK,QAAQ,IAAI,oBAAoB,KAAK;CAChG,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,IACE,OAAO,aAAa,WACpB,CAAC,eAAe,OAAO,QAAQ,KAC/B,CAAC,wBAAwB,OAAO,QAAQ,GAExC,MAAM,IAAI,MACR,GAAG,IAAI,+IACT;CAGF,OAAO;AACT"}
@@ -6,7 +6,7 @@ const LOOPBACK_HOSTS = /* @__PURE__ */ new Set([
6
6
  "::1",
7
7
  "[::1]"
8
8
  ]);
9
- const LOCAL_GRAPH_MCP_ENDPOINT = "http://127.0.0.1:8012/mcp";
9
+ const DEFAULT_GRAPH_MCP_ENDPOINT = "https://mcp.chain-insights.ai/";
10
10
  function isLoopbackHost(hostname) {
11
11
  const normalized = hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
12
12
  if (LOOPBACK_HOSTS.has(normalized)) return true;
@@ -36,10 +36,10 @@ function validateMcpEndpoint(value, key) {
36
36
  return trimmed;
37
37
  }
38
38
  //#endregion
39
- Object.defineProperty(exports, "LOCAL_GRAPH_MCP_ENDPOINT", {
39
+ Object.defineProperty(exports, "DEFAULT_GRAPH_MCP_ENDPOINT", {
40
40
  enumerable: true,
41
41
  get: function() {
42
- return LOCAL_GRAPH_MCP_ENDPOINT;
42
+ return DEFAULT_GRAPH_MCP_ENDPOINT;
43
43
  }
44
44
  });
45
45
  Object.defineProperty(exports, "graphMcpEndpointEnvOverride", {
@@ -482,7 +482,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
482
482
  const meta = { ...result._meta ?? {} };
483
483
  if (graphPayload && includeAttachments) {
484
484
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-_gqzxvvs.cjs"));
485
- const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-CH-asxF5.cjs"));
485
+ const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-B8WKXtXr.cjs"));
486
486
  const report = await writeGraphReport(graphPayload, {
487
487
  serverPort: config.serverPort,
488
488
  slug: toolName || "remote-graph"
@@ -509,7 +509,7 @@ function shouldIncludeAttachments(args, workspaceArtifactsEnabled) {
509
509
  async function writeLocalGraphMeta(graphData, config, slug, includeAttachments) {
510
510
  if (!includeAttachments) return void 0;
511
511
  const { writeGraphReport } = await Promise.resolve().then(() => require("./graph-reports-_gqzxvvs.cjs"));
512
- const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-CH-asxF5.cjs"));
512
+ const { ensureArtifactServer } = await Promise.resolve().then(() => require("./artifact-server-B8WKXtXr.cjs"));
513
513
  const report = await writeGraphReport(graphData, {
514
514
  serverPort: config.serverPort,
515
515
  slug
@@ -553,7 +553,7 @@ function jsonTextResult(structuredContent) {
553
553
  * All diagnostic output goes to console.error() or process.stderr.write().
554
554
  */
555
555
  async function createProxy() {
556
- const { loadConfig } = await Promise.resolve().then(() => require("./config-BVFf3v8-.cjs")).then((n) => n.config_exports);
556
+ const { loadConfig } = await Promise.resolve().then(() => require("./config-DYiUgdzg.cjs")).then((n) => n.config_exports);
557
557
  const { activeDataDir, findActiveWorkspace } = await Promise.resolve().then(() => require("./active-Bc13hJIm.cjs")).then((n) => n.active_exports);
558
558
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await Promise.resolve().then(() => require("./client-CqAzD519.cjs")).then((n) => n.client_exports);
559
559
  const { loadSchema, saveSchema } = await Promise.resolve().then(() => require("./schema-cache-n7Gbhvsj.cjs"));
@@ -478,7 +478,7 @@ async function normalizeRemoteToolResult(result, config, toolName = "remote-grap
478
478
  const meta = { ...result._meta ?? {} };
479
479
  if (graphPayload && includeAttachments) {
480
480
  const { writeGraphReport } = await import("./graph-reports-B6yoYbPq.mjs");
481
- const { ensureArtifactServer } = await import("./artifact-server-DoxJr0z8.mjs");
481
+ const { ensureArtifactServer } = await import("./artifact-server-C1fETWsc.mjs");
482
482
  const report = await writeGraphReport(graphPayload, {
483
483
  serverPort: config.serverPort,
484
484
  slug: toolName || "remote-graph"
@@ -505,7 +505,7 @@ function shouldIncludeAttachments(args, workspaceArtifactsEnabled) {
505
505
  async function writeLocalGraphMeta(graphData, config, slug, includeAttachments) {
506
506
  if (!includeAttachments) return void 0;
507
507
  const { writeGraphReport } = await import("./graph-reports-B6yoYbPq.mjs");
508
- const { ensureArtifactServer } = await import("./artifact-server-DoxJr0z8.mjs");
508
+ const { ensureArtifactServer } = await import("./artifact-server-C1fETWsc.mjs");
509
509
  const report = await writeGraphReport(graphData, {
510
510
  serverPort: config.serverPort,
511
511
  slug
@@ -549,7 +549,7 @@ function jsonTextResult(structuredContent) {
549
549
  * All diagnostic output goes to console.error() or process.stderr.write().
550
550
  */
551
551
  async function createProxy() {
552
- const { loadConfig } = await import("./config-CnNMdt4M.mjs").then((n) => n.t);
552
+ const { loadConfig } = await import("./config-DiQs0rQX.mjs").then((n) => n.t);
553
553
  const { activeDataDir, findActiveWorkspace } = await import("./active-BQopLul8.mjs").then((n) => n.t);
554
554
  const { createConfiguredGraphMcpFetch, resolveGraphMcpEndpoint } = await import("./client-D4aM4bzP.mjs").then((n) => n.r);
555
555
  const { loadSchema, saveSchema } = await import("./schema-cache-DJdU4MIP.mjs");
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_mcp_endpoint = require("./mcp-endpoint-BIWrUOFG.cjs");
2
+ const require_mcp_endpoint = require("./mcp-endpoint-DdIVQKW5.cjs");
3
3
  const require_limits = require("./limits-km3pmmL3.cjs");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
@@ -61,7 +61,7 @@ function endpointSchema(key) {
61
61
  });
62
62
  }
63
63
  const ConfigSchema = zod.object({
64
- graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(require_mcp_endpoint.LOCAL_GRAPH_MCP_ENDPOINT),
64
+ graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(require_mcp_endpoint.DEFAULT_GRAPH_MCP_ENDPOINT),
65
65
  graphMcpAuthToken: zod.string().optional(),
66
66
  graphMcpMode: zod.enum(["paid", "debug"]).default("paid"),
67
67
  walletAddress: zod.string().optional(),
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { r as validateMcpEndpoint, t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-DbWtLcba.mjs";
2
+ import { r as validateMcpEndpoint, t as DEFAULT_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-Bd3SvCkg.mjs";
3
3
  import { n as isLimitKey, r as limitCeiling, t as LIMIT_SPECS } from "./limits-D3Mn-gig.mjs";
4
4
  import path from "node:path";
5
5
  import os from "node:os";
@@ -58,7 +58,7 @@ function endpointSchema(key) {
58
58
  });
59
59
  }
60
60
  const ConfigSchema = z.object({
61
- graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(LOCAL_GRAPH_MCP_ENDPOINT),
61
+ graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(DEFAULT_GRAPH_MCP_ENDPOINT),
62
62
  graphMcpAuthToken: z.string().optional(),
63
63
  graphMcpMode: z.enum(["paid", "debug"]).default("paid"),
64
64
  walletAddress: z.string().optional(),
@@ -92,4 +92,4 @@ const CONFIG_KEYS = [
92
92
  //#endregion
93
93
  export { parseInvestigatorConfig as n, schema_exports as r, DEFAULT_CONFIG as t };
94
94
 
95
- //# sourceMappingURL=schema-DOJA2nNi.mjs.map
95
+ //# sourceMappingURL=schema-v5cwychf.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-v5cwychf.mjs","names":[],"sources":["../src/config/schema.ts"],"sourcesContent":["import * as z from 'zod'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { DEFAULT_GRAPH_MCP_ENDPOINT, validateMcpEndpoint } from './mcp-endpoint.js'\nimport { LIMIT_SPECS, isLimitKey, limitCeiling, type LimitKey } from './limits.js'\n\n// Config-file layer for the tunable search bounds (see config/limits.ts).\n// `limits` applies to every network; `networkLimits.<network>` is more\n// specific and wins over it. Both are validated here so a bad value fails at\n// config load rather than mid-investigation, and an unknown key is rejected\n// outright — a silently ignored knob is indistinguishable from one that had\n// no effect.\nfunction limitBagSchema(network?: string) {\n return z.record(z.string(), z.number()).superRefine((bag, ctx) => {\n for (const [key, value] of Object.entries(bag)) {\n const where = network ? `networkLimits.${network}.${key}` : `limits.${key}`\n if (!isLimitKey(key)) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: `${where}: unknown limit key` })\n continue\n }\n const spec = LIMIT_SPECS[key as LimitKey]\n const ceiling = limitCeiling(key as LimitKey, network)\n if (!Number.isInteger(value) || value < spec.min || value > ceiling) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: must be an integer between ${spec.min} and ${ceiling} (got ${value})`,\n })\n }\n }\n })\n}\n\nexport const LimitsSchema = limitBagSchema()\n\nexport const NetworkLimitsSchema = z\n .record(z.string(), z.record(z.string(), z.number()))\n .superRefine((byNetwork, ctx) => {\n for (const [network, bag] of Object.entries(byNetwork)) {\n const parsed = limitBagSchema(network).safeParse(bag)\n if (parsed.success) continue\n for (const issue of parsed.error.issues) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: issue.message })\n }\n }\n })\n\nfunction endpointSchema(key: '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 graphMcpEndpoint: endpointSchema('graphMcpEndpoint').default(DEFAULT_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 limits: LimitsSchema.optional(),\n networkLimits: NetworkLimitsSchema.optional(),\n // Paths only. Never store or log certificate or key bytes.\n truthIngressCertPath: z.string().min(1).optional(),\n truthIngressKeyPath: z.string().min(1).optional(),\n truthIngressCaPath: z.string().min(1).optional(),\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 'graphMcpEndpoint',\n 'graphMcpAuthToken',\n 'graphMcpMode',\n 'walletAddress',\n 'serverPort',\n 'dataDir',\n 'version',\n // `limits` / `networkLimits` are nested objects, not scalar `cia config set`\n // targets, so they are deliberately absent from CONFIG_KEYS. Edit them in\n // ~/.chain-insights/config.json; they are validated on load.\n] as const\n\nexport type ConfigKey = (typeof CONFIG_KEYS)[number]\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
2
- import { t as createApp } from "./app-7NRCqx_Q.mjs";
2
+ import { t as createApp } from "./app-BeZyCVuz.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 });
@@ -43,4 +43,4 @@ function startServer(port = 4321) {
43
43
  //#endregion
44
44
  export { startServer as n, server_exports as t };
45
45
 
46
- //# sourceMappingURL=server-mDSRgk1X.mjs.map
46
+ //# sourceMappingURL=server-BDmHAF1w.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-mDSRgk1X.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 = () => {\n stop()\n process.exit(0)\n }\n const onSigterm = () => {\n stop(() => process.exit(0))\n }\n\n process.on('SIGINT', onSigint)\n process.on('SIGTERM', onSigterm)\n\n return stop\n}\n"],"mappings":";;;;;AAGA,SAAgB,YAAY,OAAO,MAAkB;CACnD,MAAM,MAAM,UAAU;CACtB,MAAM,SAAS,MAAM;EACnB,OAAO,IAAI;EACX,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;EACrB,KAAK;EACL,QAAQ,KAAK,CAAC;CAChB;CACA,MAAM,kBAAkB;EACtB,WAAW,QAAQ,KAAK,CAAC,CAAC;CAC5B;CAEA,QAAQ,GAAG,UAAU,QAAQ;CAC7B,QAAQ,GAAG,WAAW,SAAS;CAE/B,OAAO;AACT"}
1
+ {"version":3,"file":"server-BDmHAF1w.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 = () => {\n stop()\n process.exit(0)\n }\n const onSigterm = () => {\n stop(() => process.exit(0))\n }\n\n process.on('SIGINT', onSigint)\n process.on('SIGTERM', onSigterm)\n\n return stop\n}\n"],"mappings":";;;;;AAGA,SAAgB,YAAY,OAAO,MAAkB;CACnD,MAAM,MAAM,UAAU;CACtB,MAAM,SAAS,MAAM;EACnB,OAAO,IAAI;EACX,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;EACrB,KAAK;EACL,QAAQ,KAAK,CAAC;CAChB;CACA,MAAM,kBAAkB;EACtB,WAAW,QAAQ,KAAK,CAAC,CAAC;CAC5B;CAEA,QAAQ,GAAG,UAAU,QAAQ;CAC7B,QAAQ,GAAG,WAAW,SAAS;CAE/B,OAAO;AACT"}
@@ -1,5 +1,5 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_app = require("./app-l9JE6bt3.cjs");
2
+ const require_app = require("./app-BHRl5oDo.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_rolldown_runtime.__exportAll({ startServer: () => startServer });
@@ -60,8 +60,9 @@ chain-insights config set graphMcpEndpoint https://mcp.chain-insights.ai/
60
60
  export CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT=https://mcp.chain-insights.ai/
61
61
  ```
62
62
 
63
- The runtime default is local loopback: `http://127.0.0.1:8012/mcp`. Hosted
64
- endpoints are operator configuration, not hardcoded package defaults.
63
+ The runtime default is the hosted production endpoint
64
+ `https://mcp.chain-insights.ai/`. Local loopback
65
+ `http://127.0.0.1:8012/mcp` remains available through explicit configuration.
65
66
 
66
67
  Supported config keys:
67
68
 
package/docs/mcp-proxy.md CHANGED
@@ -27,11 +27,10 @@ The proxy reads the same local Chain Insights config as the CLI.
27
27
  ## Chain Insights Graph Endpoint Configuration
28
28
 
29
29
  The endpoint lives in Chain Insights config, not in the MCP client registration.
30
- The npm package default is the local development endpoint
31
- `http://127.0.0.1:8012/mcp`. Public production is
32
- `https://mcp.chain-insights.ai/` (host root, no `/mcp` path). Set it
33
- explicitly. Do not bake that URL into MCP client JSON, source, or
34
- workspace templates.
30
+ The npm package defaults to public production:
31
+ `https://mcp.chain-insights.ai/` (host root, no `/mcp` path). A fresh install
32
+ can use `cia networks` without endpoint setup. MCP client JSON does not carry
33
+ the endpoint; use Chain Insights config for the default or an override.
35
34
 
36
35
  Set local development:
37
36
 
@@ -56,7 +55,7 @@ Configuration precedence:
56
55
  1. `CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT`
57
56
  2. `GRAPH_MCP_ENDPOINT` legacy alias
58
57
  3. saved `graphMcpEndpoint`
59
- 4. local default `http://127.0.0.1:8012/mcp`
58
+ 4. hosted production default `https://mcp.chain-insights.ai/`
60
59
 
61
60
  Validation rules:
62
61
 
@@ -64,9 +63,8 @@ Validation rules:
64
63
  - remote endpoints must use `https://`
65
64
  - endpoint URLs with credentials, query strings, or fragments are rejected
66
65
 
67
- Keep hosted endpoint values in operator config or environment variables. Do not
68
- bake hosted endpoint URLs into MCP client JSON, source code, or workspace
69
- templates.
66
+ Keep endpoint overrides in operator config or environment variables. Do not
67
+ put endpoint configuration in MCP client JSON.
70
68
 
71
69
  ## Behavior
72
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chain-insights",
3
- "version": "0.22.2",
3
+ "version": "0.22.3",
4
4
  "description": "AML investigation CLI and MCP proxy for blockchain risk screening, case tracking, workspace reports, and Chain Insights Graph access",
5
5
  "homepage": "https://chain-insights.ai",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- {"version":3,"file":"init-Djhml4ih.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'\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 'artifacts',\n 'entities',\n 'imports',\n 'reports',\n 'reports/graphs',\n 'reports/tables',\n 'sessions',\n 'templates',\n 'published',\n]\n\nconst DEFAULT_DOMAIN_HINTS = ['aml']\n\nfunction todayIso(): string {\n return new Date().toISOString().slice(0, 10)\n}\n\nfunction workspaceJson(workspaceRoot: string): string {\n return (\n JSON.stringify(\n {\n schema: 'chain-insights.workspace.v1',\n name: 'Chain Insights Workspace',\n workspace_root: workspaceRoot,\n default_network: 'robinhood',\n graph_mcp_endpoint: LOCAL_GRAPH_MCP_ENDPOINT,\n artifacts_dir: 'artifacts',\n imports_dir: 'imports',\n reports_dir: 'reports',\n templates_dir: 'templates',\n domain_hints: DEFAULT_DOMAIN_HINTS,\n created_at: todayIso(),\n },\n null,\n 2\n ) + '\\n'\n )\n}\n\nconst README = `# Chain Insights Workspace\n\nThis is a generic Chain Insights workspace.\nUse any editor or agent tooling you want to review reports, artifacts, entity\nnotes, graphs, and published bundles alongside the Chain Insights runtime\nmetadata.\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\nartifacts/ Tool-generated artifacts and durable workspace records\nentities/ Entity notes and indexes\nimports/ External reports, CSVs, screenshots, raw notes\nreports/ Final or interim analyst reports\nreports/graphs/ Graph JSON for visualization\nreports/tables/ Compact tabular extracts\nsessions/ Optional session notes\ntemplates/ Reusable workspace templates\npublished/ Published bundles and handoff-ready exports\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\\`\\`\\`\n`\n\nconst AGENTS = `# Agent Instructions\n\nYou are operating inside a Chain Insights workspace.\n\n- Read README.md first.\n- If this directory is not initialized, run \\`cia init .\\` before persistence-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 graph workflow.\n- \\`domain_hints\\` in \\`.chain-insights/workspace.json\\` are optional advisory workflow\n preferences (for example \\`[\"aml\"]\\`) and should guide, not constrain,\n tool selection.\n- Save compact artifacts with original graph field names.\n- Put canonical graph JSON in reports/graphs/ and analyst tables in reports/tables/.\n- Markdown reports should summarize and point to graph/table outputs; do not paste large raw JSON blobs into reports.\n- Workspace output must stay in this initialized workspace.\n- Never write artifacts, 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 = `# Workspace 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 material here before turning it into durable\nworkspace outputs.\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 a workspace report or artifact note and\nreference the original file path.\n`\n\nconst TEMPLATES_README = `# Reusable Workspace Templates\n\nStore local report, prompt, artifact, and note templates here.\n\nTemplates are optional workspace helpers. They are not evidence and should not\nbe treated as durable workspace state until copied into an artifact, report,\nentity note, or session note.\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 topology reads with\n\\`USE topology\\` (address/FLOWS_TO/LINKED graph, unified recent+historical)\nand fact reads with \\`USE facts\\`, for example:\n\n\\`\\`\\`bash\ncia mcp call graph_query_batch network=<network> 'queries=[{\"id\":\"node_labels\",\"query\":\"USE topology MATCH (n:Address) RETURN \\\"Address\\\" AS node_label, count(n) AS sample_count LIMIT 1\"},{\"id\":\"flow_sample\",\"query\":\"USE topology MATCH (:Address)-[f:FLOWS_TO]->(:Address) RETURN f.amount_usd_sum AS amount_usd_sum, f.tx_count AS tx_count LIMIT 20\"},{\"id\":\"linked_sample\",\"query\":\"USE topology MATCH (a:Address)-[l:LINKED]-(b:Address) RETURN a.address AS address, b.address AS linked_address, l.basis AS basis, l.confidence AS confidence LIMIT 20\"}]'\n\\`\\`\\`\n\nThen update this file with observed labels, relationship types, and allowed\nproperty names for the active network.\n\nThe address-grain graph schema:\n\n- Call \\`meta_network_capabilities\\` first. Pass \\`network=\\` exactly as\n GraphRAG advertised it. CIA does not pick a default network.\n Address format follows that network. Every topology node is\n \\`(:Address {address, network})\\`, keyed by the raw chain-native\n \\`address\\`. There is no separate identity key and no member-address\n satellite: the address IS the graph node.\n- \\`(:Address)-[:LINKED]-(:Address)\\` is an **undirected** ownership-overlay\n edge (\\`basis\\` \\`derived\\`/\\`associated\\`, plus \\`confidence\\`,\n \\`source_event\\`, \\`declared_owner\\`) asserting the two addresses are\n controlled by the same actor. \\`LINKED\\` is the ownership edge within the\n advertised network — a same-network query traces\n (\\`LINKED\\` or \\`FLOWS_TO\\`) with no network switch. Walk\n one visible \\`LINKED\\` hop to surface actor-level exposure; never treat\n linked addresses\n as a single collapsed node. \\`LINKED\\` is served on the topology graph\n only.\n- Other Address properties: \\`labels\\` (array) and \\`is_exchange\\`\n (sparse true/null traversal hint). Labels and per-label risk live on the\n address node: \\`label_risk\\` is a list of \\`{label, risk_level,\n updated_timestamp}\\` maps, one per current label row.\n- Address nodes carry a risk verdict for quick triage\n (\\`risk_score\\` float, \\`risk_level\\` string) plus base activity rollups:\n \\`degree_in\\`/\\`degree_out\\`/\\`degree_total\\` (distinct counterparty\n addresses), \\`tx_in_count\\`/\\`tx_out_count\\`/\\`tx_total_count\\`,\n \\`total_in_usd\\`/\\`total_out_usd\\`/\\`total_volume_usd\\`, \\`net_flow_usd\\`\n (in minus out; positive = net receiver) — all computed from external\n flows only — and \\`first_activity_timestamp\\`/\n \\`last_activity_timestamp\\`/\\`activity_span_days\\`, which include all\n flows (self-loops included). FLOWS_TO edges carry \\`tx_count\\`,\n \\`amount_usd_sum\\`, \\`avg_tx_size_usd\\` (understates when\n \\`price_coverage_ratio\\` < 1), \\`first_seen_timestamp\\`/\n \\`last_seen_timestamp\\`, \\`first_tx_id\\`/\\`last_tx_id\\`, and\n \\`price_coverage_ratio\\`.\n Lifetime aggregates are the only serving window.\n- Money flow is \\`(:Address)-[:FLOWS_TO]->(:Address)\\`. Public AML tools\n accept the raw blockchain address directly — there is no resolution step.\n- The risk verdict lives on topology nodes (\\`risk_score\\`/\\`risk_level\\`),\n and labels and per-label risk live on the address node (\\`labels\\` array\n + \\`label_risk\\` entries). \\`USE facts\\` serves bounded individual\n transfer rows and, until P3, address features. Facts address keys match\n topology \\`address\\` values exactly. Do not read \\`ml_*\\`,\n \\`confluence_score\\`, or \\`pattern_flags\\` off topology nodes — those\n properties do not exist.\n- \\`(from:Address)-[t:TRANSFER]->(to:Address)\\` on \\`USE facts\\` returns\n individual transfer rows (not aggregates) from \\`facts_transfers_view\\`,\n with edge properties \\`amount\\`, \\`amount_usd\\`, \\`asset_symbol\\`,\n \\`asset_contract\\`, \\`tx_id\\`, \\`block_height\\`, \\`block_timestamp\\`,\n \\`event_index\\`, \\`edge_index\\`, \\`price_usd\\`, and \\`price_missing\\`.\n Every TRANSFER query (row-select or a \\`count()\\`/\\`sum()\\` aggregate)\n requires an indexed predicate — address equality on either endpoint or\n \\`WHERE t.tx_id = \"...\"\\` — a bare \\`LIMIT\\` alone is rejected. Lifetime\n address metrics (degrees, totals, activity window) are node properties on\n \\`USE topology\\`.\n\nRules:\n\n- Prefer \\`graph_query\\` and \\`graph_query_batch\\` for graph-language reads.\n- The graph choice stays inside the query via \\`USE ...\\`; there is no\n tool argument to select a graph.\n- Use \\`USE topology\\` for topology (the address/FLOWS_TO/LINKED graph,\n covering unified recent and full historical activity in one graph, plus\n the node \\`risk_score\\`/\\`risk_level\\` verdict, and labels + per-label\n risk) and \\`USE facts\\` for features, assets, and enrichment.\n The \\`LINKED\\` ownership overlay is served on the topology graph only.\n- Preserve source schema field names in 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 persisted outputs compact: select only the fields needed to support the claim.\n Avoid storing whole node or relationship property blobs 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 summary notes: graph JSON belongs under\n \\`reports/graphs/\\`, tabular extracts under \\`reports/tables/\\`, and analyst\n narrative under \\`reports/\\`.\n- Markdown reports should be short provenance records with key facts and\n pointers. Large JSON belongs in \\`reports/tables/\\`, not inline in reports.\n\nAML tool guidance:\n\n1. Use \\`aml_address_risk\\` for single-address enrichment and optional\n comparison with another address.\n2. Use \\`graph_query_batch\\` only when the high-level tools do not answer the\n exact question, and \\`graph_query\\` for single read-only queries.\n\n\\`aml_address_risk\\` takes a raw blockchain address as input directly — there\nis no identity-resolution step — and returns \\`chain-insights.result.v1\\`. Preserve\nfull blockchain addresses in the summary and all workspace artifacts.\n`\n\nconst SCHEMA_README = `# Runtime Schema Captures\n\nStore graph schema captures here, for example:\n\n\\`\\`\\`text\nrobinhood.graph-schema.json\n\\`\\`\\`\n\nSchema captures should be generated before the first graph workflow in a fresh\nworkspace, then referenced by artifacts, 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/workspace-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(\n `Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`\n )\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 }\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(\n `Refusing to overwrite ${filePath}. Re-run with --force to replace workspace files.`\n )\n }\n throw err\n }\n }\n\n return { workspaceRoot, filesWritten }\n}\n"],"mappings":";;;;AAcA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,MAAM,uBAAuB,CAAC,KAAK;AAEnC,SAAS,WAAmB;CAC1B,wBAAO,IAAI,KAAK,EAAA,CAAE,YAAY,CAAC,CAAC,MAAM,GAAG,EAAE;AAC7C;AAEA,SAAS,cAAc,eAA+B;CACpD,OACE,KAAK,UACH;EACE,QAAQ;EACR,MAAM;EACN,gBAAgB;EAChB,iBAAiB;EACjB,oBAAoB;EACpB,eAAe;EACf,aAAa;EACb,aAAa;EACb,eAAe;EACf,cAAc;EACd,YAAY,SAAS;CACvB,GACA,MACA,CACF,IAAI;AAER;AAEA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCf,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;AAsBf,MAAM,SAAS;AAEf,MAAM,aAAa;;;;;;;;;;;;;;;;AAiBnB,MAAM,iBAAiB;;;;;;;;;;;;;;;;;AAkBvB,MAAM,mBAAmB;;;;;;;;AASzB,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAwHtB,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,gCAAgC,UAAU;EAC3C,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,MACR,yBAAyB,SAAS,kDACpC;EACF,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,OACX,MAAM,uBAAuB,aAAa;CAG5C,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,MACR,yBAAyB,SAAS,kDACpC;GAEF,MAAM;EACR;CACF;CAEA,OAAO;EAAE;EAAe;CAAa;AACvC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"mcp-endpoint-DbWtLcba.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'\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 =\n process.env.CHAIN_INSIGHTS_GRAPH_MCP_ENDPOINT?.trim() || 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 (\n parsed.protocol === 'http:' &&\n !isLoopbackHost(parsed.hostname) &&\n !isKubernetesServiceHost(parsed.hostname)\n ) {\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,iCAAiB,IAAI,IAAI;CAAC;CAAa;CAAa;CAAO;AAAO,CAAC;AAEzE,MAAa,2BAA2B;AAExC,SAAS,eAAe,UAA2B;CACjD,MAAM,aAAa,SAAS,YAAY,CAAC,CAAC,QAAQ,cAAc,IAAI;CACpE,IAAI,eAAe,IAAI,UAAU,GAAG,OAAO;CAC3C,IAAI,KAAK,UAAU,MAAM,GAAG,OAAO;CACnC,OAAO,WAAW,MAAM,GAAG,CAAC,CAAC,OAAO;AACtC;AAEA,SAAS,wBAAwB,UAA2B;CAC1D,OAAO,SAAS,YAAY,CAAC,CAAC,SAAS,oBAAoB;AAC7D;AAEA,SAAgB,8BAAkD;CAChE,MAAM,cACJ,QAAQ,IAAI,mCAAmC,KAAK,KAAK,QAAQ,IAAI,oBAAoB,KAAK;CAChG,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,IACE,OAAO,aAAa,WACpB,CAAC,eAAe,OAAO,QAAQ,KAC/B,CAAC,wBAAwB,OAAO,QAAQ,GAExC,MAAM,IAAI,MACR,GAAG,IAAI,+IACT;CAGF,OAAO;AACT"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"schema-DOJA2nNi.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, validateMcpEndpoint } from './mcp-endpoint.js'\nimport { LIMIT_SPECS, isLimitKey, limitCeiling, type LimitKey } from './limits.js'\n\n// Config-file layer for the tunable search bounds (see config/limits.ts).\n// `limits` applies to every network; `networkLimits.<network>` is more\n// specific and wins over it. Both are validated here so a bad value fails at\n// config load rather than mid-investigation, and an unknown key is rejected\n// outright — a silently ignored knob is indistinguishable from one that had\n// no effect.\nfunction limitBagSchema(network?: string) {\n return z.record(z.string(), z.number()).superRefine((bag, ctx) => {\n for (const [key, value] of Object.entries(bag)) {\n const where = network ? `networkLimits.${network}.${key}` : `limits.${key}`\n if (!isLimitKey(key)) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: `${where}: unknown limit key` })\n continue\n }\n const spec = LIMIT_SPECS[key as LimitKey]\n const ceiling = limitCeiling(key as LimitKey, network)\n if (!Number.isInteger(value) || value < spec.min || value > ceiling) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `${where}: must be an integer between ${spec.min} and ${ceiling} (got ${value})`,\n })\n }\n }\n })\n}\n\nexport const LimitsSchema = limitBagSchema()\n\nexport const NetworkLimitsSchema = z\n .record(z.string(), z.record(z.string(), z.number()))\n .superRefine((byNetwork, ctx) => {\n for (const [network, bag] of Object.entries(byNetwork)) {\n const parsed = limitBagSchema(network).safeParse(bag)\n if (parsed.success) continue\n for (const issue of parsed.error.issues) {\n ctx.addIssue({ code: z.ZodIssueCode.custom, message: issue.message })\n }\n }\n })\n\nfunction endpointSchema(key: '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 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 limits: LimitsSchema.optional(),\n networkLimits: NetworkLimitsSchema.optional(),\n // Paths only. Never store or log certificate or key bytes.\n truthIngressCertPath: z.string().min(1).optional(),\n truthIngressKeyPath: z.string().min(1).optional(),\n truthIngressCaPath: z.string().min(1).optional(),\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 'graphMcpEndpoint',\n 'graphMcpAuthToken',\n 'graphMcpMode',\n 'walletAddress',\n 'serverPort',\n 'dataDir',\n 'version',\n // `limits` / `networkLimits` are nested objects, not scalar `cia config set`\n // targets, so they are deliberately absent from CONFIG_KEYS. Edit them in\n // ~/.chain-insights/config.json; they are validated on load.\n] as const\n\nexport type ConfigKey = (typeof CONFIG_KEYS)[number]\n"],"mappings":"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"}