chain-insights 0.2.18 → 0.2.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -12
- package/bin/cli.js +2 -3
- package/bin/install.cjs +0 -1
- package/dist/{app-DxlQE_P5.cjs → app-BxojXjtB.cjs} +1 -1
- package/dist/{app-DdWQF_zb.mjs → app-CRd39JJ8.mjs} +2 -2
- package/dist/{app-DdWQF_zb.mjs.map → app-CRd39JJ8.mjs.map} +1 -1
- package/dist/{artifact-server-4DiMvwhC.mjs → artifact-server-CP6LXQ9d.mjs} +2 -2
- package/dist/{artifact-server-4DiMvwhC.mjs.map → artifact-server-CP6LXQ9d.mjs.map} +1 -1
- package/dist/{artifact-server-B-3ho4bk.cjs → artifact-server-XbN16DwU.cjs} +1 -1
- package/dist/cli.cjs +66 -25
- package/dist/cli.mjs +66 -25
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-BhYbhLDI.cjs → config-BwVx19Og.cjs} +48 -15
- package/dist/config-Drgc2HuF.mjs +77 -0
- package/dist/config-Drgc2HuF.mjs.map +1 -0
- package/dist/frontmatter-D0ccQnUM.mjs.map +1 -1
- package/dist/index.cjs +4 -4
- package/dist/index.d.cts +3 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +3 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +4 -4
- package/dist/{init-CZbZegIW.mjs → init-4tn7jfhN.mjs} +3 -2
- package/dist/init-4tn7jfhN.mjs.map +1 -0
- package/dist/{init-BvpZtFiT.cjs → init-TCQY5RDJ.cjs} +2 -1
- package/dist/mcp-endpoint-BaV8h_lq.cjs +60 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs +39 -0
- package/dist/mcp-endpoint-DHs1cRFH.mjs.map +1 -0
- package/dist/mcp-proxy.cjs +108 -9
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +108 -9
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-D6Q5MTcO.mjs → public-tools-B13J0MJZ.mjs} +465 -70
- package/dist/public-tools-B13J0MJZ.mjs.map +1 -0
- package/dist/{public-tools-V7ON7goq.cjs → public-tools-BC1fi0DV.cjs} +464 -68
- package/dist/resolver-D7VBb0uB.mjs.map +1 -1
- package/dist/{runner-BatyCxv7.mjs → runner-DIs04IhN.mjs} +2 -2
- package/dist/{runner-BatyCxv7.mjs.map → runner-DIs04IhN.mjs.map} +1 -1
- package/dist/{runner-CCA7SJ7X.cjs → runner-ZYowxCVl.cjs} +1 -1
- package/dist/schema-BFEWhzg7.mjs +60 -0
- package/dist/schema-BFEWhzg7.mjs.map +1 -0
- package/dist/{schema-DN-KLkYN.cjs → schema-Vl9yuOFO.cjs} +31 -8
- package/dist/{server-BDlbmGbL.mjs → server-BXLX2j_A.mjs} +2 -2
- package/dist/{server-BDlbmGbL.mjs.map → server-BXLX2j_A.mjs.map} +1 -1
- package/dist/{server-C3y1gQmZ.cjs → server-BqVdWath.cjs} +1 -1
- package/dist/{topup-server-6MH7q73X.mjs → topup-server-BJgVw6Jt.mjs} +100 -42
- package/dist/topup-server-BJgVw6Jt.mjs.map +1 -0
- package/dist/{topup-server-DjUjhNjv.cjs → topup-server-yAaXYkJP.cjs} +98 -40
- package/docs/architecture.md +4 -0
- package/docs/contributing.md +1 -0
- package/docs/debugging.md +10 -14
- package/docs/graph-tools.md +60 -2
- package/docs/mcp-proxy.md +44 -0
- package/package.json +2 -2
- package/skills/chain-insights-developer-experience/SKILL.md +4 -2
- package/skills/chain-insights-investigation/SKILL.md +1 -1
- package/skills/test-chain-insights-graphrag-mcp/SKILL.md +4 -5
- package/skills/test-chain-insights-graphrag-mcp/scripts/run-uat.sh +5 -24
- package/dist/config-9KYXaAv-.mjs +0 -44
- package/dist/config-9KYXaAv-.mjs.map +0 -1
- package/dist/init-CZbZegIW.mjs.map +0 -1
- package/dist/public-tools-D6Q5MTcO.mjs.map +0 -1
- package/dist/schema-BbQVXp36.mjs +0 -37
- package/dist/schema-BbQVXp36.mjs.map +0 -1
- package/dist/topup-server-6MH7q73X.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver-D7VBb0uB.mjs","names":[],"sources":["../src/playbooks/builtins.ts","../src/playbooks/resolver.ts"],"sourcesContent":["// Built-in playbook definitions tied to the current GraphRAG public MCP tools.\n// Source of truth inspected in rbmk/repos/ml/graphrag/src/mcp_server/tools.\n\nexport const KNOWN_GRAPHRAG_PUBLIC_TOOLS = [\n 'address_risk',\n 'scam_topology',\n 'track_funds',\n 'graph_query',\n 'graph_query_batch',\n] as const\n\nexport const TRACE_FUNDS_PLAYBOOK = `---\nname: trace-funds\ndescription: Trace stolen funds from a victim address to exchange deposits using Chain Insights GraphRAG\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Screen Victim Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Trace Funds To Exchanges\n\n\\`\\`\\`tool\ntrack_funds\n\\`\\`\\`\n\n\\`\\`\\`params\ntrusted_addresses: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const RISK_CHECK_PLAYBOOK = `---\nname: risk-check\ndescription: Screen an address for Chain Insights risk, behavior, counterparties, exchange connections, and AML patterns\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Address Risk\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const ENTITY_PROFILE_PLAYBOOK = `---\nname: entity-profile\ndescription: Build an entity profile from Chain Insights address identity, metrics, risk, counterparties, and labels\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Entity Profile\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const SCAM_TOPOLOGY_PLAYBOOK = `---\nname: scam-topology\ndescription: Build laundering topology and scam labels from a victim incident\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: incident_timestamp_ms\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n - name: activity_policy\n type: string\n required: false\n default: node_relative_only\n---\n\n## Step 1: Screen Known Scam Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Build Scam Topology\n\nThe victim-only traversal is outward from victim/source funds. The\nprimary traversal is a node-relative novelty wave: each new node expands only\nonce, repeated targets remain as non-expanding convergence context, and\ndownstream edges must be active at or after the current node's wave-arrival\ntimestamp. Set activity_policy to global_incident_only to filter every wave\nagainst incident_timestamp_ms instead. Exchange terminal safety stops exchange\nendpoints, and label candidates are reviewable, not automatic writes.\nContract summary: victim-only traversal is outward from victim/source funds;\nnode-relative novelty wave by default; global_incident_only is available;\nexchange terminal safety; scam_labels are ML-ready flags.\n\n\\`\\`\\`tool\nscam_topology\n\\`\\`\\`\n\n\\`\\`\\`params\nvictim_address: {{address}}\nincident_timestamp_ms: {{incident_timestamp_ms}}\nnetwork: {{network}}\nactivity_policy: {{activity_policy}}\n\\`\\`\\`\n`\n\nexport const BUILTIN_PLAYBOOKS: Record<string, string> = {\n 'trace-funds': TRACE_FUNDS_PLAYBOOK,\n 'scam-topology': SCAM_TOPOLOGY_PLAYBOOK,\n 'risk-check': RISK_CHECK_PLAYBOOK,\n 'entity-profile': ENTITY_PROFILE_PLAYBOOK,\n}\n","import path from 'node:path'\nimport { access, readFile, readdir } from 'node:fs/promises'\nimport os from 'node:os'\nimport { BUILTIN_PLAYBOOKS } from './builtins.js'\n\nfunction userDir(): string {\n return path.join(os.homedir(), '.chain-insights', 'playbooks')\n}\n\n/**\n * Resolve a playbook name to its absolute file path.\n * Checks user directory (~/.chain-insights/playbooks/) first, then built-in directory.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n *\n * @deprecated Prefer resolvePlaybookContent() which returns markdown directly.\n */\nexport async function resolvePlaybook(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n const userPath = path.join(userDir(), `${safeName}.md`)\n\n try {\n await access(userPath)\n return userPath\n } catch {\n // Fall through to built-in check\n }\n\n // Check built-in playbooks map (T-05-01: no filesystem path exposure for built-ins)\n if (safeName in BUILTIN_PLAYBOOKS) {\n // Return a sentinel path for legacy callers — content comes from BUILTIN_PLAYBOOKS\n return `builtin:${safeName}`\n }\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * Resolve a playbook name to its markdown content.\n * Checks user directory (~/.chain-insights/playbooks/<name>.md) first,\n * then falls back to the built-in BUILTIN_PLAYBOOKS map.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n */\nexport async function resolvePlaybookContent(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n // 1. Check user directory first (user playbooks override built-ins)\n const userPath = path.join(userDir(), `${safeName}.md`)\n try {\n return await readFile(userPath, 'utf8')\n } catch {\n // Not in user dir — fall through\n }\n\n // 2. Fall back to built-in map\n const builtin = BUILTIN_PLAYBOOKS[safeName]\n if (builtin !== undefined) return builtin\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * List all available playbooks — user dir first (overrides), then built-ins.\n * Returns array of { name, source } objects.\n */\nexport async function listPlaybooks(): Promise<Array<{ name: string; source: 'builtin' | 'user' }>> {\n const result: Array<{ name: string; source: 'builtin' | 'user' }> = []\n const seen = new Set<string>()\n\n // User playbooks first\n try {\n const userFiles = await readdir(userDir())\n for (const file of userFiles) {\n if (!file.endsWith('.md')) continue\n const name = file.slice(0, -3) // remove .md\n seen.add(name)\n result.push({ name, source: 'user' })\n }\n } catch {\n // User dir doesn't exist — that's fine\n }\n\n // Built-in playbooks from the embedded map (not filesystem)\n for (const name of Object.keys(BUILTIN_PLAYBOOKS)) {\n if (seen.has(name)) continue // user override takes precedence\n result.push({ name, source: 'builtin' })\n }\n\n return result\n}\n"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"resolver-D7VBb0uB.mjs","names":[],"sources":["../src/playbooks/builtins.ts","../src/playbooks/resolver.ts"],"sourcesContent":["// Built-in playbook definitions tied to the current GraphRAG public MCP tools.\n// Source of truth inspected in rbmk/repos/ml/graphrag/src/mcp_server/tools.\n\nexport const KNOWN_GRAPHRAG_PUBLIC_TOOLS = [\n 'address_risk',\n 'scam_topology',\n 'stake_insights',\n 'track_funds',\n 'graph_query',\n 'graph_query_batch',\n] as const\n\nexport const TRACE_FUNDS_PLAYBOOK = `---\nname: trace-funds\ndescription: Trace stolen funds from a victim address to exchange deposits using Chain Insights GraphRAG\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Screen Victim Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Trace Funds To Exchanges\n\n\\`\\`\\`tool\ntrack_funds\n\\`\\`\\`\n\n\\`\\`\\`params\ntrusted_addresses: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const RISK_CHECK_PLAYBOOK = `---\nname: risk-check\ndescription: Screen an address for Chain Insights risk, behavior, counterparties, exchange connections, and AML patterns\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Address Risk\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const ENTITY_PROFILE_PLAYBOOK = `---\nname: entity-profile\ndescription: Build an entity profile from Chain Insights address identity, metrics, risk, counterparties, and labels\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n---\n\n## Step 1: Entity Profile\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n`\n\nexport const SCAM_TOPOLOGY_PLAYBOOK = `---\nname: scam-topology\ndescription: Build laundering topology and scam labels from a victim incident\nversion: 1.0.0\nparams:\n - name: address\n type: string\n required: true\n - name: incident_timestamp_ms\n type: string\n required: true\n - name: network\n type: string\n required: false\n default: bittensor\n - name: activity_policy\n type: string\n required: false\n default: node_relative_only\n---\n\n## Step 1: Screen Known Scam Address\n\n\\`\\`\\`tool\naddress_risk\n\\`\\`\\`\n\n\\`\\`\\`params\naddress: {{address}}\nnetwork: {{network}}\n\\`\\`\\`\n\n## Step 2: Build Scam Topology\n\nThe victim-only traversal is outward from victim/source funds. The\nprimary traversal is a node-relative novelty wave: each new node expands only\nonce, repeated targets remain as non-expanding convergence context, and\ndownstream edges must be active at or after the current node's wave-arrival\ntimestamp. Set activity_policy to global_incident_only to filter every wave\nagainst incident_timestamp_ms instead. Exchange terminal safety stops exchange\nendpoints, and label candidates are reviewable, not automatic writes.\nContract summary: victim-only traversal is outward from victim/source funds;\nnode-relative novelty wave by default; global_incident_only is available;\nexchange terminal safety; scam_labels are ML-ready flags.\n\n\\`\\`\\`tool\nscam_topology\n\\`\\`\\`\n\n\\`\\`\\`params\nvictim_address: {{address}}\nincident_timestamp_ms: {{incident_timestamp_ms}}\nnetwork: {{network}}\nactivity_policy: {{activity_policy}}\n\\`\\`\\`\n`\n\nexport const BUILTIN_PLAYBOOKS: Record<string, string> = {\n 'trace-funds': TRACE_FUNDS_PLAYBOOK,\n 'scam-topology': SCAM_TOPOLOGY_PLAYBOOK,\n 'risk-check': RISK_CHECK_PLAYBOOK,\n 'entity-profile': ENTITY_PROFILE_PLAYBOOK,\n}\n","import path from 'node:path'\nimport { access, readFile, readdir } from 'node:fs/promises'\nimport os from 'node:os'\nimport { BUILTIN_PLAYBOOKS } from './builtins.js'\n\nfunction userDir(): string {\n return path.join(os.homedir(), '.chain-insights', 'playbooks')\n}\n\n/**\n * Resolve a playbook name to its absolute file path.\n * Checks user directory (~/.chain-insights/playbooks/) first, then built-in directory.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n *\n * @deprecated Prefer resolvePlaybookContent() which returns markdown directly.\n */\nexport async function resolvePlaybook(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n const userPath = path.join(userDir(), `${safeName}.md`)\n\n try {\n await access(userPath)\n return userPath\n } catch {\n // Fall through to built-in check\n }\n\n // Check built-in playbooks map (T-05-01: no filesystem path exposure for built-ins)\n if (safeName in BUILTIN_PLAYBOOKS) {\n // Return a sentinel path for legacy callers — content comes from BUILTIN_PLAYBOOKS\n return `builtin:${safeName}`\n }\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * Resolve a playbook name to its markdown content.\n * Checks user directory (~/.chain-insights/playbooks/<name>.md) first,\n * then falls back to the built-in BUILTIN_PLAYBOOKS map.\n * Security: sanitizes name to prevent path traversal (T-05-01).\n */\nexport async function resolvePlaybookContent(name: string): Promise<string> {\n // Security: sanitize name to prevent path traversal (per security threat T-05-01)\n const safeName = name.replace(/[^a-z0-9_-]/gi, '')\n if (!safeName) throw new Error(`Invalid playbook name: ${name}`)\n\n // 1. Check user directory first (user playbooks override built-ins)\n const userPath = path.join(userDir(), `${safeName}.md`)\n try {\n return await readFile(userPath, 'utf8')\n } catch {\n // Not in user dir — fall through\n }\n\n // 2. Fall back to built-in map\n const builtin = BUILTIN_PLAYBOOKS[safeName]\n if (builtin !== undefined) return builtin\n\n throw new Error(\n `Playbook not found: \"${safeName}\". Run \\`chain-insights playbook list\\` to see available playbooks.`\n )\n}\n\n/**\n * List all available playbooks — user dir first (overrides), then built-ins.\n * Returns array of { name, source } objects.\n */\nexport async function listPlaybooks(): Promise<Array<{ name: string; source: 'builtin' | 'user' }>> {\n const result: Array<{ name: string; source: 'builtin' | 'user' }> = []\n const seen = new Set<string>()\n\n // User playbooks first\n try {\n const userFiles = await readdir(userDir())\n for (const file of userFiles) {\n if (!file.endsWith('.md')) continue\n const name = file.slice(0, -3) // remove .md\n seen.add(name)\n result.push({ name, source: 'user' })\n }\n } catch {\n // User dir doesn't exist — that's fine\n }\n\n // Built-in playbooks from the embedded map (not filesystem)\n for (const name of Object.keys(BUILTIN_PLAYBOOKS)) {\n if (seen.has(name)) continue // user override takes precedence\n result.push({ name, source: 'builtin' })\n }\n\n return result\n}\n"],"mappings":";;;AA8JA,MAAa,oBAA4C;CACvD,eAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,iBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,cAAkB;;;;;;;;;;;;;;;;;;;;;;;;;CAClB,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;AACpB;;;AC9JA,SAAS,UAAkB;CACzB,OAAO,KAAK,KAAK,GAAG,QAAQ,GAAG,mBAAmB,WAAW;AAC/D;;;;;;;AAwCA,eAAsB,uBAAuB,MAA+B;CAE1E,MAAM,WAAW,KAAK,QAAQ,iBAAiB,EAAE;CACjD,IAAI,CAAC,UAAU,MAAM,IAAI,MAAM,0BAA0B,MAAM;CAG/D,MAAM,WAAW,KAAK,KAAK,QAAQ,GAAG,GAAG,SAAS,IAAI;CACtD,IAAI;EACF,OAAO,MAAM,SAAS,UAAU,MAAM;CACxC,QAAQ,CAER;CAGA,MAAM,UAAU,kBAAkB;CAClC,IAAI,YAAY,KAAA,GAAW,OAAO;CAElC,MAAM,IAAI,MACR,wBAAwB,SAAS,oEACnC;AACF;;;;;AAMA,eAAsB,gBAA8E;CAClG,MAAM,SAA8D,CAAC;CACrE,MAAM,uBAAO,IAAI,IAAY;CAG7B,IAAI;EACF,MAAM,YAAY,MAAM,QAAQ,QAAQ,CAAC;EACzC,KAAK,MAAM,QAAQ,WAAW;GAC5B,IAAI,CAAC,KAAK,SAAS,KAAK,GAAG;GAC3B,MAAM,OAAO,KAAK,MAAM,GAAG,EAAE;GAC7B,KAAK,IAAI,IAAI;GACb,OAAO,KAAK;IAAE;IAAM,QAAQ;GAAO,CAAC;EACtC;CACF,QAAQ,CAER;CAGA,KAAK,MAAM,QAAQ,OAAO,KAAK,iBAAiB,GAAG;EACjD,IAAI,KAAK,IAAI,IAAI,GAAG;EACpB,OAAO,KAAK;GAAE;GAAM,QAAQ;EAAU,CAAC;CACzC;CAEA,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as PACKAGE_VERSION } from "./version-BA3J8hu4.mjs";
|
|
2
|
-
import { n as loadConfig } from "./config-
|
|
2
|
+
import { n as loadConfig } from "./config-Drgc2HuF.mjs";
|
|
3
3
|
import { r as createConfiguredMcpFetch } from "./client-D4_hd4AP.mjs";
|
|
4
4
|
import { t as generateVisualization } from "./viz-DkJyqlUu.mjs";
|
|
5
5
|
import { CaseStore } from "./store-BT2SCcQr.mjs";
|
|
@@ -146,4 +146,4 @@ async run(playbook, opts) {
|
|
|
146
146
|
//#endregion
|
|
147
147
|
export { PlaybookRunner };
|
|
148
148
|
|
|
149
|
-
//# sourceMappingURL=runner-
|
|
149
|
+
//# sourceMappingURL=runner-DIs04IhN.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner-BatyCxv7.mjs","names":[],"sources":["../src/playbooks/runner.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { CaseStore } from '../cases/store.js'\nimport { EvidenceStore } from '../cases/evidence.js'\nimport { loadConfig } from '../config/index.js'\nimport { createConfiguredMcpFetch } from '../mcp/client.js'\nimport { generateVisualization } from '../viz/index.js'\nimport { PACKAGE_VERSION } from '../version.js'\nimport type { PlaybookDefinition } from './schema.js'\n\nexport interface RunnerOptions {\n caseId?: string // attach to existing case; omit for quick-case auto-creation\n from?: number // 1-based step to resume from (default: 1)\n dryRun?: boolean // print steps, no MCP calls\n params?: Record<string, string>\n}\n\n/** Sleep for ms milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/** Check if an error is a timeout/abort error. */\nfunction isTimeoutError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return err.name === 'AbortError' || (err as NodeJS.ErrnoException).code === 'ECONNRESET'\n}\n\n/** Check if an error is a payment failure. */\nfunction isPaymentError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message.toLowerCase()\n // Match HTTP 402 status more precisely, or x402-specific error signals\n return msg.includes('http 402') ||\n msg.includes('status 402') ||\n msg.includes('payment required') ||\n msg.includes('x402')\n}\n\n/**\n * Call an MCP tool with retry logic on timeout (up to 3 total attempts).\n * Returns the text result or throws on non-retryable error.\n */\nasync function callWithRetry(\n client: Client,\n toolName: string,\n params: Record<string, string>\n): Promise<string> {\n const MAX_ATTEMPTS = 3\n let lastErr: unknown\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n try {\n const result = await client.callTool({ name: toolName, arguments: params })\n const content = result.content as Array<{ type: string; text?: string }>\n return content.filter(c => c.type === 'text').map(c => c.text ?? '').join('\\n')\n } catch (err) {\n if (isTimeoutError(err) && attempt < MAX_ATTEMPTS) {\n lastErr = err\n await sleep(1000)\n continue\n }\n throw err\n }\n }\n\n throw lastErr\n}\n\nasync function validateStepTools(client: Client, steps: PlaybookDefinition['steps']): Promise<void> {\n const result = await client.listTools()\n const available = new Set(result.tools.map(tool => tool.name))\n const missing = [...new Set(steps.map(step => step.tool).filter(tool => !available.has(tool)))]\n if (missing.length === 0) return\n\n const availableList = [...available].sort().join(', ') || 'none'\n throw new Error(\n `Unknown MCP tool(s) in playbook: ${missing.join(', ')}. ` +\n `Available tools: ${availableList}. Run \\`chain-insights mcp tools --refresh\\` to inspect the live MCP schema.`\n )\n}\n\nexport const PlaybookRunner = {\n /**\n * Execute a playbook definition step-by-step against the live MCP.\n *\n * @param playbook - Parsed and validated PlaybookDefinition\n * @param opts - Runner options (caseId, from, dryRun, params)\n */\n async run(playbook: PlaybookDefinition, opts: RunnerOptions): Promise<void> {\n const startIndex = (opts.from ?? 1) - 1 // convert 1-based to 0-based\n const stepsToRun = playbook.steps.slice(startIndex)\n const totalSteps = playbook.steps.length\n\n // --- DRY RUN ---\n if (opts.dryRun) {\n console.log(`Playbook: ${playbook.name} (dry run — no MCP calls)`)\n console.log(`Steps: ${totalSteps} total, starting from ${startIndex + 1}`)\n console.log('')\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.tool} (params: ${JSON.stringify(step.params)})`)\n }\n console.log('')\n console.log('Cost: unknown (MCP pricing not available without live connection)')\n return\n }\n\n // --- MCP AUTH CHECK (before case creation to avoid orphan cases) ---\n const config = await loadConfig()\n const mcpFetch = await createConfiguredMcpFetch(config)\n\n // --- CASE RESOLUTION ---\n let caseId: string\n if (opts.caseId) {\n const existingCase = await CaseStore.get(opts.caseId)\n caseId = existingCase.id\n } else {\n const newCase = await CaseStore.create({\n name: `quick-${playbook.name}-${Date.now()}`,\n tags: ['quick', 'playbook', playbook.name],\n description: `Auto-created for one-off playbook run: ${playbook.name}`,\n })\n caseId = newCase.id\n console.log(`Created quick case: ${caseId}`)\n }\n\n // --- MCP CONNECTION ---\n const client = new Client({ name: 'chain-insights-playbook', version: PACKAGE_VERSION })\n await client.connect(\n new StreamableHTTPClientTransport(new URL(config.mcpEndpoint), { fetch: mcpFetch })\n )\n\n let evidenceCount = 0\n\n try {\n await validateStepTools(client, stepsToRun)\n\n // --- STEP LOOP ---\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.label}...`)\n\n let result: string\n try {\n result = await callWithRetry(client, step.tool, step.params)\n } catch (err) {\n if (isPaymentError(err)) {\n if (process.stdin.isTTY) {\n // Interactive: prompt user\n const { createInterface } = await import('node:readline')\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const answer = await new Promise<string>(resolve => {\n rl.question(`Payment required for step ${step.index}. (retry/skip/abort): `, resolve)\n })\n rl.close()\n\n if (answer.trim().toLowerCase() === 'retry') {\n result = await callWithRetry(client, step.tool, step.params)\n } else if (answer.trim().toLowerCase() === 'skip') {\n console.log(`Step ${step.index} skipped.`)\n continue\n } else {\n throw new Error(`Aborted at step ${step.index} due to payment failure.`)\n }\n } else {\n // Non-TTY: abort\n throw new Error(\n `Payment required for step ${step.index} but no interactive terminal available. ` +\n `Configure wallet with \\`chain-insights config set walletPrivateKey <key>\\`. Aborting.`\n )\n }\n } else {\n // Non-payment, non-timeout MCP error — stop and report\n const completedSteps = step.index - 1 - startIndex\n const completedMsg = completedSteps > 0\n ? `Completed: steps ${startIndex + 1}..${step.index - 1}.`\n : 'No steps completed before failure.'\n console.error(\n `Step ${step.index} failed: ${(err as Error).message}. ` +\n `${completedMsg} Run with --from ${step.index} to resume.`\n )\n throw err\n }\n }\n\n // --- STORE EVIDENCE ---\n await EvidenceStore.append(caseId, {\n source: step.tool,\n content: result,\n queryParams: JSON.stringify(step.params),\n })\n evidenceCount++\n console.log(` (${result.length} chars stored)`)\n }\n\n // --- AUTO-VIZ for trace-funds ---\n if (playbook.name === 'trace-funds') {\n try {\n const viz = await generateVisualization({ caseId })\n console.log(`Visualization generated: ${viz.htmlPath}`)\n } catch {\n console.log('No transaction data to visualize.')\n }\n }\n\n // --- FINAL SUMMARY ---\n console.log(`Playbook complete. Case: ${caseId}. Evidence: ${evidenceCount} entries.`)\n } finally {\n await client.close()\n }\n },\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"runner-DIs04IhN.mjs","names":[],"sources":["../src/playbooks/runner.ts"],"sourcesContent":["import { Client } from '@modelcontextprotocol/sdk/client/index.js'\nimport { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'\nimport { CaseStore } from '../cases/store.js'\nimport { EvidenceStore } from '../cases/evidence.js'\nimport { loadConfig } from '../config/index.js'\nimport { createConfiguredMcpFetch } from '../mcp/client.js'\nimport { generateVisualization } from '../viz/index.js'\nimport { PACKAGE_VERSION } from '../version.js'\nimport type { PlaybookDefinition } from './schema.js'\n\nexport interface RunnerOptions {\n caseId?: string // attach to existing case; omit for quick-case auto-creation\n from?: number // 1-based step to resume from (default: 1)\n dryRun?: boolean // print steps, no MCP calls\n params?: Record<string, string>\n}\n\n/** Sleep for ms milliseconds. */\nfunction sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms))\n}\n\n/** Check if an error is a timeout/abort error. */\nfunction isTimeoutError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n return err.name === 'AbortError' || (err as NodeJS.ErrnoException).code === 'ECONNRESET'\n}\n\n/** Check if an error is a payment failure. */\nfunction isPaymentError(err: unknown): boolean {\n if (!(err instanceof Error)) return false\n const msg = err.message.toLowerCase()\n // Match HTTP 402 status more precisely, or x402-specific error signals\n return msg.includes('http 402') ||\n msg.includes('status 402') ||\n msg.includes('payment required') ||\n msg.includes('x402')\n}\n\n/**\n * Call an MCP tool with retry logic on timeout (up to 3 total attempts).\n * Returns the text result or throws on non-retryable error.\n */\nasync function callWithRetry(\n client: Client,\n toolName: string,\n params: Record<string, string>\n): Promise<string> {\n const MAX_ATTEMPTS = 3\n let lastErr: unknown\n\n for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n try {\n const result = await client.callTool({ name: toolName, arguments: params })\n const content = result.content as Array<{ type: string; text?: string }>\n return content.filter(c => c.type === 'text').map(c => c.text ?? '').join('\\n')\n } catch (err) {\n if (isTimeoutError(err) && attempt < MAX_ATTEMPTS) {\n lastErr = err\n await sleep(1000)\n continue\n }\n throw err\n }\n }\n\n throw lastErr\n}\n\nasync function validateStepTools(client: Client, steps: PlaybookDefinition['steps']): Promise<void> {\n const result = await client.listTools()\n const available = new Set(result.tools.map(tool => tool.name))\n const missing = [...new Set(steps.map(step => step.tool).filter(tool => !available.has(tool)))]\n if (missing.length === 0) return\n\n const availableList = [...available].sort().join(', ') || 'none'\n throw new Error(\n `Unknown MCP tool(s) in playbook: ${missing.join(', ')}. ` +\n `Available tools: ${availableList}. Run \\`chain-insights mcp tools --refresh\\` to inspect the live MCP schema.`\n )\n}\n\nexport const PlaybookRunner = {\n /**\n * Execute a playbook definition step-by-step against the live MCP.\n *\n * @param playbook - Parsed and validated PlaybookDefinition\n * @param opts - Runner options (caseId, from, dryRun, params)\n */\n async run(playbook: PlaybookDefinition, opts: RunnerOptions): Promise<void> {\n const startIndex = (opts.from ?? 1) - 1 // convert 1-based to 0-based\n const stepsToRun = playbook.steps.slice(startIndex)\n const totalSteps = playbook.steps.length\n\n // --- DRY RUN ---\n if (opts.dryRun) {\n console.log(`Playbook: ${playbook.name} (dry run — no MCP calls)`)\n console.log(`Steps: ${totalSteps} total, starting from ${startIndex + 1}`)\n console.log('')\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.tool} (params: ${JSON.stringify(step.params)})`)\n }\n console.log('')\n console.log('Cost: unknown (MCP pricing not available without live connection)')\n return\n }\n\n // --- MCP AUTH CHECK (before case creation to avoid orphan cases) ---\n const config = await loadConfig()\n const mcpFetch = await createConfiguredMcpFetch(config)\n\n // --- CASE RESOLUTION ---\n let caseId: string\n if (opts.caseId) {\n const existingCase = await CaseStore.get(opts.caseId)\n caseId = existingCase.id\n } else {\n const newCase = await CaseStore.create({\n name: `quick-${playbook.name}-${Date.now()}`,\n tags: ['quick', 'playbook', playbook.name],\n description: `Auto-created for one-off playbook run: ${playbook.name}`,\n })\n caseId = newCase.id\n console.log(`Created quick case: ${caseId}`)\n }\n\n // --- MCP CONNECTION ---\n const client = new Client({ name: 'chain-insights-playbook', version: PACKAGE_VERSION })\n await client.connect(\n new StreamableHTTPClientTransport(new URL(config.mcpEndpoint), { fetch: mcpFetch })\n )\n\n let evidenceCount = 0\n\n try {\n await validateStepTools(client, stepsToRun)\n\n // --- STEP LOOP ---\n for (const step of stepsToRun) {\n console.log(`Step ${step.index}/${totalSteps}: ${step.label}...`)\n\n let result: string\n try {\n result = await callWithRetry(client, step.tool, step.params)\n } catch (err) {\n if (isPaymentError(err)) {\n if (process.stdin.isTTY) {\n // Interactive: prompt user\n const { createInterface } = await import('node:readline')\n const rl = createInterface({ input: process.stdin, output: process.stdout })\n const answer = await new Promise<string>(resolve => {\n rl.question(`Payment required for step ${step.index}. (retry/skip/abort): `, resolve)\n })\n rl.close()\n\n if (answer.trim().toLowerCase() === 'retry') {\n result = await callWithRetry(client, step.tool, step.params)\n } else if (answer.trim().toLowerCase() === 'skip') {\n console.log(`Step ${step.index} skipped.`)\n continue\n } else {\n throw new Error(`Aborted at step ${step.index} due to payment failure.`)\n }\n } else {\n // Non-TTY: abort\n throw new Error(\n `Payment required for step ${step.index} but no interactive terminal available. ` +\n `Configure wallet with \\`chain-insights config set walletPrivateKey <key>\\`. Aborting.`\n )\n }\n } else {\n // Non-payment, non-timeout MCP error — stop and report\n const completedSteps = step.index - 1 - startIndex\n const completedMsg = completedSteps > 0\n ? `Completed: steps ${startIndex + 1}..${step.index - 1}.`\n : 'No steps completed before failure.'\n console.error(\n `Step ${step.index} failed: ${(err as Error).message}. ` +\n `${completedMsg} Run with --from ${step.index} to resume.`\n )\n throw err\n }\n }\n\n // --- STORE EVIDENCE ---\n await EvidenceStore.append(caseId, {\n source: step.tool,\n content: result,\n queryParams: JSON.stringify(step.params),\n })\n evidenceCount++\n console.log(` (${result.length} chars stored)`)\n }\n\n // --- AUTO-VIZ for trace-funds ---\n if (playbook.name === 'trace-funds') {\n try {\n const viz = await generateVisualization({ caseId })\n console.log(`Visualization generated: ${viz.htmlPath}`)\n } catch {\n console.log('No transaction data to visualize.')\n }\n }\n\n // --- FINAL SUMMARY ---\n console.log(`Playbook complete. Case: ${caseId}. Evidence: ${evidenceCount} entries.`)\n } finally {\n await client.close()\n }\n },\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_version = require("./version-CO9Or_YV.cjs");
|
|
2
|
-
const require_config = require("./config-
|
|
2
|
+
const require_config = require("./config-BwVx19Og.cjs");
|
|
3
3
|
const require_client = require("./client-DPc2eyVN.cjs");
|
|
4
4
|
const require_viz = require("./viz-Da9YWN_I.cjs");
|
|
5
5
|
const require_store = require("./store-DogLawSj.cjs");
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
+
import { i as validateMcpEndpoint, n as LOCAL_LEGACY_MCP_ENDPOINT, t as LOCAL_GRAPH_MCP_ENDPOINT } from "./mcp-endpoint-DHs1cRFH.mjs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import * as z from "zod";
|
|
6
|
+
//#region src/config/schema.ts
|
|
7
|
+
var schema_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
CONFIG_KEYS: () => CONFIG_KEYS,
|
|
9
|
+
ConfigSchema: () => ConfigSchema,
|
|
10
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
11
|
+
parseInvestigatorConfig: () => parseInvestigatorConfig
|
|
12
|
+
});
|
|
13
|
+
function endpointSchema(key) {
|
|
14
|
+
return z.string().transform((value, ctx) => {
|
|
15
|
+
try {
|
|
16
|
+
return validateMcpEndpoint(value, key);
|
|
17
|
+
} catch (err) {
|
|
18
|
+
ctx.addIssue({
|
|
19
|
+
code: z.ZodIssueCode.custom,
|
|
20
|
+
message: err.message
|
|
21
|
+
});
|
|
22
|
+
return z.NEVER;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
const ConfigSchema = z.object({
|
|
27
|
+
mcpEndpoint: endpointSchema("mcpEndpoint").default(LOCAL_LEGACY_MCP_ENDPOINT),
|
|
28
|
+
mcpAuthToken: z.string().optional(),
|
|
29
|
+
graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(LOCAL_GRAPH_MCP_ENDPOINT),
|
|
30
|
+
graphMcpAuthToken: z.string().optional(),
|
|
31
|
+
graphMcpMode: z.enum(["paid", "debug"]).default("paid"),
|
|
32
|
+
walletAddress: z.string().optional(),
|
|
33
|
+
serverPort: z.number().int().min(1024).max(65535).default(4321),
|
|
34
|
+
dataDir: z.string().default(path.join(os.homedir(), ".chain-insights")),
|
|
35
|
+
version: z.string().default("1")
|
|
36
|
+
});
|
|
37
|
+
function formatConfigValidationError(error) {
|
|
38
|
+
return error.issues.map((issue) => issue.message).join("\n");
|
|
39
|
+
}
|
|
40
|
+
function parseInvestigatorConfig(input) {
|
|
41
|
+
const parsed = ConfigSchema.safeParse(input);
|
|
42
|
+
if (parsed.success) return parsed.data;
|
|
43
|
+
throw new Error(formatConfigValidationError(parsed.error));
|
|
44
|
+
}
|
|
45
|
+
const DEFAULT_CONFIG = parseInvestigatorConfig({});
|
|
46
|
+
const CONFIG_KEYS = [
|
|
47
|
+
"mcpEndpoint",
|
|
48
|
+
"mcpAuthToken",
|
|
49
|
+
"graphMcpEndpoint",
|
|
50
|
+
"graphMcpAuthToken",
|
|
51
|
+
"graphMcpMode",
|
|
52
|
+
"walletAddress",
|
|
53
|
+
"serverPort",
|
|
54
|
+
"dataDir",
|
|
55
|
+
"version"
|
|
56
|
+
];
|
|
57
|
+
//#endregion
|
|
58
|
+
export { parseInvestigatorConfig as n, schema_exports as r, DEFAULT_CONFIG as t };
|
|
59
|
+
|
|
60
|
+
//# sourceMappingURL=schema-BFEWhzg7.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-BFEWhzg7.mjs","names":[],"sources":["../src/config/schema.ts"],"sourcesContent":["import * as z from 'zod'\nimport os from 'node:os'\nimport path from 'node:path'\nimport { LOCAL_GRAPH_MCP_ENDPOINT, LOCAL_LEGACY_MCP_ENDPOINT, validateMcpEndpoint } from './mcp-endpoint.js'\n\nfunction endpointSchema(key: 'mcpEndpoint' | 'graphMcpEndpoint') {\n return z.string().transform((value, ctx) => {\n try {\n return validateMcpEndpoint(value, key)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: (err as Error).message,\n })\n return z.NEVER\n }\n })\n}\n\nexport const ConfigSchema = z.object({\n mcpEndpoint: endpointSchema('mcpEndpoint').default(LOCAL_LEGACY_MCP_ENDPOINT),\n mcpAuthToken: z.string().optional(),\n graphMcpEndpoint: endpointSchema('graphMcpEndpoint').default(LOCAL_GRAPH_MCP_ENDPOINT),\n graphMcpAuthToken: z.string().optional(),\n graphMcpMode: z.enum(['paid', 'debug']).default('paid'),\n walletAddress: z.string().optional(),\n serverPort: z.number().int().min(1024).max(65535).default(4321),\n dataDir: z.string().default(path.join(os.homedir(), '.chain-insights')),\n version: z.string().default('1'),\n})\n\nexport type InvestigatorConfig = z.infer<typeof ConfigSchema>\n\nfunction formatConfigValidationError(error: z.ZodError): string {\n return error.issues.map((issue) => issue.message).join('\\n')\n}\n\nexport function parseInvestigatorConfig(input: unknown): InvestigatorConfig {\n const parsed = ConfigSchema.safeParse(input)\n if (parsed.success) return parsed.data\n throw new Error(formatConfigValidationError(parsed.error))\n}\n\nexport const DEFAULT_CONFIG: InvestigatorConfig = parseInvestigatorConfig({})\n\nexport const CONFIG_KEYS = [\n 'mcpEndpoint',\n 'mcpAuthToken',\n 'graphMcpEndpoint',\n 'graphMcpAuthToken',\n 'graphMcpMode',\n 'walletAddress',\n 'serverPort',\n 'dataDir',\n 'version',\n] as const\n\nexport type ConfigKey = typeof CONFIG_KEYS[number]\n"],"mappings":";;;;;;;;;;;;AAKA,SAAS,eAAe,KAAyC;CAC/D,OAAO,EAAE,OAAO,EAAE,WAAW,OAAO,QAAQ;EAC1C,IAAI;GACF,OAAO,oBAAoB,OAAO,GAAG;EACvC,SAAS,KAAK;GACZ,IAAI,SAAS;IACX,MAAM,EAAE,aAAa;IACrB,SAAU,IAAc;GAC1B,CAAC;GACD,OAAO,EAAE;EACX;CACF,CAAC;AACH;AAEA,MAAa,eAAe,EAAE,OAAO;CACnC,aAAmB,eAAe,aAAa,EAAE,QAAQ,yBAAyB;CAClF,cAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,kBAAmB,eAAe,kBAAkB,EAAE,QAAQ,wBAAwB;CACtF,mBAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,cAAmB,EAAE,KAAK,CAAC,QAAQ,OAAO,CAAC,EAAE,QAAQ,MAAM;CAC3D,eAAmB,EAAE,OAAO,EAAE,SAAS;CACvC,YAAmB,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,IAAI,EAAE,IAAI,KAAK,EAAE,QAAQ,IAAI;CACrE,SAAmB,EAAE,OAAO,EAAE,QAAQ,KAAK,KAAK,GAAG,QAAQ,GAAG,iBAAiB,CAAC;CAChF,SAAmB,EAAE,OAAO,EAAE,QAAQ,GAAG;AAC3C,CAAC;AAID,SAAS,4BAA4B,OAA2B;CAC9D,OAAO,MAAM,OAAO,KAAK,UAAU,MAAM,OAAO,EAAE,KAAK,IAAI;AAC7D;AAEA,SAAgB,wBAAwB,OAAoC;CAC1E,MAAM,SAAS,aAAa,UAAU,KAAK;CAC3C,IAAI,OAAO,SAAS,OAAO,OAAO;CAClC,MAAM,IAAI,MAAM,4BAA4B,OAAO,KAAK,CAAC;AAC3D;AAEA,MAAa,iBAAqC,wBAAwB,CAAC,CAAC;AAE5E,MAAa,cAAc;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
+
const require_mcp_endpoint = require("./mcp-endpoint-BaV8h_lq.cjs");
|
|
2
3
|
let node_path = require("node:path");
|
|
3
4
|
node_path = require_chunk.__toESM(node_path, 1);
|
|
4
5
|
let node_os = require("node:os");
|
|
@@ -9,12 +10,26 @@ zod = require_chunk.__toESM(zod, 1);
|
|
|
9
10
|
var schema_exports = /* @__PURE__ */ require_chunk.__exportAll({
|
|
10
11
|
CONFIG_KEYS: () => CONFIG_KEYS,
|
|
11
12
|
ConfigSchema: () => ConfigSchema,
|
|
12
|
-
DEFAULT_CONFIG: () => DEFAULT_CONFIG
|
|
13
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
14
|
+
parseInvestigatorConfig: () => parseInvestigatorConfig
|
|
13
15
|
});
|
|
16
|
+
function endpointSchema(key) {
|
|
17
|
+
return zod.string().transform((value, ctx) => {
|
|
18
|
+
try {
|
|
19
|
+
return require_mcp_endpoint.validateMcpEndpoint(value, key);
|
|
20
|
+
} catch (err) {
|
|
21
|
+
ctx.addIssue({
|
|
22
|
+
code: zod.ZodIssueCode.custom,
|
|
23
|
+
message: err.message
|
|
24
|
+
});
|
|
25
|
+
return zod.NEVER;
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
14
29
|
const ConfigSchema = zod.object({
|
|
15
|
-
mcpEndpoint:
|
|
30
|
+
mcpEndpoint: endpointSchema("mcpEndpoint").default(require_mcp_endpoint.LOCAL_LEGACY_MCP_ENDPOINT),
|
|
16
31
|
mcpAuthToken: zod.string().optional(),
|
|
17
|
-
graphMcpEndpoint:
|
|
32
|
+
graphMcpEndpoint: endpointSchema("graphMcpEndpoint").default(require_mcp_endpoint.LOCAL_GRAPH_MCP_ENDPOINT),
|
|
18
33
|
graphMcpAuthToken: zod.string().optional(),
|
|
19
34
|
graphMcpMode: zod.enum(["paid", "debug"]).default("paid"),
|
|
20
35
|
walletAddress: zod.string().optional(),
|
|
@@ -22,7 +37,15 @@ const ConfigSchema = zod.object({
|
|
|
22
37
|
dataDir: zod.string().default(node_path.default.join(node_os.default.homedir(), ".chain-insights")),
|
|
23
38
|
version: zod.string().default("1")
|
|
24
39
|
});
|
|
25
|
-
|
|
40
|
+
function formatConfigValidationError(error) {
|
|
41
|
+
return error.issues.map((issue) => issue.message).join("\n");
|
|
42
|
+
}
|
|
43
|
+
function parseInvestigatorConfig(input) {
|
|
44
|
+
const parsed = ConfigSchema.safeParse(input);
|
|
45
|
+
if (parsed.success) return parsed.data;
|
|
46
|
+
throw new Error(formatConfigValidationError(parsed.error));
|
|
47
|
+
}
|
|
48
|
+
const DEFAULT_CONFIG = parseInvestigatorConfig({});
|
|
26
49
|
const CONFIG_KEYS = [
|
|
27
50
|
"mcpEndpoint",
|
|
28
51
|
"mcpAuthToken",
|
|
@@ -35,16 +58,16 @@ const CONFIG_KEYS = [
|
|
|
35
58
|
"version"
|
|
36
59
|
];
|
|
37
60
|
//#endregion
|
|
38
|
-
Object.defineProperty(exports, "
|
|
61
|
+
Object.defineProperty(exports, "DEFAULT_CONFIG", {
|
|
39
62
|
enumerable: true,
|
|
40
63
|
get: function() {
|
|
41
|
-
return
|
|
64
|
+
return DEFAULT_CONFIG;
|
|
42
65
|
}
|
|
43
66
|
});
|
|
44
|
-
Object.defineProperty(exports, "
|
|
67
|
+
Object.defineProperty(exports, "parseInvestigatorConfig", {
|
|
45
68
|
enumerable: true,
|
|
46
69
|
get: function() {
|
|
47
|
-
return
|
|
70
|
+
return parseInvestigatorConfig;
|
|
48
71
|
}
|
|
49
72
|
});
|
|
50
73
|
Object.defineProperty(exports, "schema_exports", {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
2
|
-
import { t as createApp } from "./app-
|
|
2
|
+
import { t as createApp } from "./app-CRd39JJ8.mjs";
|
|
3
3
|
import { serve } from "@hono/node-server";
|
|
4
4
|
//#region src/server/index.ts
|
|
5
5
|
var server_exports = /* @__PURE__ */ __exportAll({ startServer: () => startServer });
|
|
@@ -42,4 +42,4 @@ function startServer(port = 4321) {
|
|
|
42
42
|
//#endregion
|
|
43
43
|
export { startServer as n, server_exports as t };
|
|
44
44
|
|
|
45
|
-
//# sourceMappingURL=server-
|
|
45
|
+
//# sourceMappingURL=server-BXLX2j_A.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-
|
|
1
|
+
{"version":3,"file":"server-BXLX2j_A.mjs","names":[],"sources":["../src/server/index.ts"],"sourcesContent":["import { serve } from '@hono/node-server'\nimport { createApp } from './app.js'\n\nexport function startServer(port = 4321): () => void {\n const app = createApp()\n const server = serve({\n fetch: app.fetch,\n hostname: '127.0.0.1', // localhost-only — REQUIRED (default 0.0.0.0 is insecure)\n port,\n })\n\n server.on('listening', () => {\n console.log(`Chain Insights server running on http://127.0.0.1:${port}`)\n })\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n process.stderr.write(`Port already in use: 127.0.0.1:${port}\\n`)\n } else {\n process.stderr.write(`Chain Insights server failed: ${err.message}\\n`)\n }\n process.exitCode = 1\n })\n\n let stopped = false\n const stop = (callback?: () => void): void => {\n if (stopped) {\n callback?.()\n return\n }\n stopped = true\n process.off('SIGINT', onSigint)\n process.off('SIGTERM', onSigterm)\n server.close(callback)\n }\n const onSigint = () => { stop(); process.exit(0) }\n const onSigterm = () => { stop(() => process.exit(0)) }\n\n process.on('SIGINT', onSigint)\n process.on('SIGTERM', onSigterm)\n\n return stop\n}\n"],"mappings":";;;;;AAGA,SAAgB,YAAY,OAAO,MAAkB;CAEnD,MAAM,SAAS,MAAM;EACnB,OAFa,UAED,EAAE;EACd,UAAU;EACV;CACF,CAAC;CAED,OAAO,GAAG,mBAAmB;EAC3B,QAAQ,IAAI,qDAAqD,MAAM;CACzE,CAAC;CACD,OAAO,GAAG,UAAU,QAA+B;EACjD,IAAI,IAAI,SAAS,cACf,QAAQ,OAAO,MAAM,kCAAkC,KAAK,GAAG;OAE/D,QAAQ,OAAO,MAAM,iCAAiC,IAAI,QAAQ,GAAG;EAEvE,QAAQ,WAAW;CACrB,CAAC;CAED,IAAI,UAAU;CACd,MAAM,QAAQ,aAAgC;EAC5C,IAAI,SAAS;GACX,WAAW;GACX;EACF;EACA,UAAU;EACV,QAAQ,IAAI,UAAU,QAAQ;EAC9B,QAAQ,IAAI,WAAW,SAAS;EAChC,OAAO,MAAM,QAAQ;CACvB;CACA,MAAM,iBAAiB;EAAE,KAAK;EAAG,QAAQ,KAAK,CAAC;CAAE;CACjD,MAAM,kBAAkB;EAAE,WAAW,QAAQ,KAAK,CAAC,CAAC;CAAE;CAEtD,QAAQ,GAAG,UAAU,QAAQ;CAC7B,QAAQ,GAAG,WAAW,SAAS;CAE/B,OAAO;AACT"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-DakpK96I.cjs");
|
|
2
|
-
const require_app = require("./app-
|
|
2
|
+
const require_app = require("./app-BxojXjtB.cjs");
|
|
3
3
|
let _hono_node_server = require("@hono/node-server");
|
|
4
4
|
//#region src/server/index.ts
|
|
5
5
|
var server_exports = /* @__PURE__ */ require_chunk.__exportAll({ startServer: () => startServer });
|
|
@@ -2,7 +2,7 @@ import { t as __exportAll } from "./rolldown-runtime-D7D4PA-g.mjs";
|
|
|
2
2
|
import { fileURLToPath } from "node:url";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { readFileSync } from "node:fs";
|
|
5
|
-
import { createPublicClient, formatEther, formatUnits, http } from "viem";
|
|
5
|
+
import { createPublicClient, formatEther, formatUnits, http, isAddress } from "viem";
|
|
6
6
|
import { base } from "viem/chains";
|
|
7
7
|
import { createServer } from "node:http";
|
|
8
8
|
//#region src/wallet/mcp-proxy/qr.ts
|
|
@@ -197,6 +197,9 @@ const USDC_ABI = [{
|
|
|
197
197
|
type: "uint256"
|
|
198
198
|
}]
|
|
199
199
|
}];
|
|
200
|
+
function walletAddress(wallet) {
|
|
201
|
+
return typeof wallet === "string" ? wallet : wallet.address;
|
|
202
|
+
}
|
|
200
203
|
async function getBalanceUsdc(wallet) {
|
|
201
204
|
const envRpcUrl = process.env.BASE_RPC_URL;
|
|
202
205
|
const rpcUrls = [...envRpcUrl ? [envRpcUrl] : [], ...PUBLIC_BASE_RPC_URLS.filter((url) => url !== envRpcUrl)];
|
|
@@ -208,7 +211,7 @@ async function getBalanceUsdc(wallet) {
|
|
|
208
211
|
address: USDC_ADDRESS$1,
|
|
209
212
|
abi: USDC_ABI,
|
|
210
213
|
functionName: "balanceOf",
|
|
211
|
-
args: [wallet
|
|
214
|
+
args: [walletAddress(wallet)]
|
|
212
215
|
}), 6);
|
|
213
216
|
} catch {}
|
|
214
217
|
return "unknown";
|
|
@@ -220,7 +223,7 @@ async function getBalanceEth(wallet) {
|
|
|
220
223
|
return formatEther(await createPublicClient({
|
|
221
224
|
chain: base,
|
|
222
225
|
transport: http(rpcUrl)
|
|
223
|
-
}).getBalance({ address: wallet
|
|
226
|
+
}).getBalance({ address: walletAddress(wallet) }));
|
|
224
227
|
} catch {}
|
|
225
228
|
return "unknown";
|
|
226
229
|
}
|
|
@@ -241,10 +244,22 @@ const logoPng = loadAsset("logo.png");
|
|
|
241
244
|
const bgPatternPng = loadAsset("bg-pattern.png");
|
|
242
245
|
let server = null;
|
|
243
246
|
let serverPort = null;
|
|
247
|
+
function assertWalletAddress(wallet) {
|
|
248
|
+
const walletAddress = typeof wallet === "string" ? wallet : wallet.address;
|
|
249
|
+
if (!isAddress(walletAddress)) throw new Error("Wallet address must be a valid 0x-prefixed 20-byte EVM address");
|
|
250
|
+
return walletAddress;
|
|
251
|
+
}
|
|
252
|
+
function escapeHtml(value) {
|
|
253
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
254
|
+
}
|
|
255
|
+
function jsonForScript(value) {
|
|
256
|
+
return JSON.stringify(value).replaceAll("<", "\\u003c").replaceAll(">", "\\u003e").replaceAll("&", "\\u0026");
|
|
257
|
+
}
|
|
244
258
|
function getTopupUrl$1() {
|
|
245
259
|
return serverPort ? `http://localhost:${serverPort}` : null;
|
|
246
260
|
}
|
|
247
261
|
async function startTopupServer$1(wallet) {
|
|
262
|
+
const walletAddress = assertWalletAddress(wallet);
|
|
248
263
|
if (server && serverPort) return `http://localhost:${serverPort}`;
|
|
249
264
|
return new Promise((resolve, reject) => {
|
|
250
265
|
server = createServer((req, res) => {
|
|
@@ -253,11 +268,11 @@ async function startTopupServer$1(wallet) {
|
|
|
253
268
|
"Content-Type": "application/json",
|
|
254
269
|
"Access-Control-Allow-Origin": "*"
|
|
255
270
|
});
|
|
256
|
-
res.end(JSON.stringify({ address:
|
|
271
|
+
res.end(JSON.stringify({ address: walletAddress }));
|
|
257
272
|
return;
|
|
258
273
|
}
|
|
259
274
|
if (req.url === "/api/balance") {
|
|
260
|
-
Promise.all([getBalanceUsdc(
|
|
275
|
+
Promise.all([getBalanceUsdc(walletAddress), getBalanceEth(walletAddress)]).then(([balanceUsdc, balanceEth]) => {
|
|
261
276
|
res.writeHead(200, {
|
|
262
277
|
"Content-Type": "application/json",
|
|
263
278
|
"Access-Control-Allow-Origin": "*"
|
|
@@ -297,7 +312,7 @@ async function startTopupServer$1(wallet) {
|
|
|
297
312
|
return;
|
|
298
313
|
}
|
|
299
314
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
300
|
-
res.end(generatePage(
|
|
315
|
+
res.end(generatePage(walletAddress));
|
|
301
316
|
});
|
|
302
317
|
server.listen(0, "127.0.0.1", () => {
|
|
303
318
|
const addr = server.address();
|
|
@@ -311,7 +326,11 @@ async function startTopupServer$1(wallet) {
|
|
|
311
326
|
server.on("error", reject);
|
|
312
327
|
});
|
|
313
328
|
}
|
|
314
|
-
function generateArtifactHtml(
|
|
329
|
+
function generateArtifactHtml(walletAddressInput, topupUrl) {
|
|
330
|
+
const walletAddress = assertWalletAddress(walletAddressInput);
|
|
331
|
+
const safeWalletAddress = escapeHtml(walletAddress);
|
|
332
|
+
const safeTopupUrl = escapeHtml(topupUrl);
|
|
333
|
+
const topupUrlJson = jsonForScript(topupUrl);
|
|
315
334
|
return `<!DOCTYPE html>
|
|
316
335
|
<html lang="en">
|
|
317
336
|
<head>
|
|
@@ -321,7 +340,7 @@ function generateArtifactHtml(walletAddress, topupUrl) {
|
|
|
321
340
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
322
341
|
body {
|
|
323
342
|
font-family: Inter, -apple-system, BlinkMacSystemFont, sans-serif;
|
|
324
|
-
background: rgba(10, 12, 17, 1) url('${
|
|
343
|
+
background: rgba(10, 12, 17, 1) url('${safeTopupUrl}/assets/bg-pattern.png') top left / contain no-repeat;
|
|
325
344
|
color: rgba(255, 255, 255, 0.9);
|
|
326
345
|
display: flex;
|
|
327
346
|
justify-content: center;
|
|
@@ -397,11 +416,11 @@ function generateArtifactHtml(walletAddress, topupUrl) {
|
|
|
397
416
|
</head>
|
|
398
417
|
<body>
|
|
399
418
|
<div class="card">
|
|
400
|
-
<div class="logo"><img src="${
|
|
419
|
+
<div class="logo"><img src="${safeTopupUrl}/assets/logo.png" alt="Chain Insights"></div>
|
|
401
420
|
<p class="subtitle">Fund your wallet with USDC on Base</p>
|
|
402
421
|
<div class="qr">${generateQrSvg(walletAddress, { cellSize: 8 })}</div>
|
|
403
422
|
<div class="address-wrap">
|
|
404
|
-
<div class="address" id="addr" onclick="selectAndCopy()">${
|
|
423
|
+
<div class="address" id="addr" onclick="selectAndCopy()">${safeWalletAddress}</div>
|
|
405
424
|
<div class="address-hint" id="addrHint">Click to copy address</div>
|
|
406
425
|
</div>
|
|
407
426
|
<div class="badge"><span class="dot"></span>Base Network · USDC</div>
|
|
@@ -460,8 +479,9 @@ function generateArtifactHtml(walletAddress, topupUrl) {
|
|
|
460
479
|
|
|
461
480
|
// Live balance polling
|
|
462
481
|
var lastBal = null;
|
|
482
|
+
var TOPUP_URL = ${topupUrlJson};
|
|
463
483
|
function fetchBal() {
|
|
464
|
-
fetch('
|
|
484
|
+
fetch(TOPUP_URL + '/api/balance')
|
|
465
485
|
.then(function(r) { return r.json(); })
|
|
466
486
|
.then(function(d) {
|
|
467
487
|
var el = document.getElementById('bal');
|
|
@@ -514,7 +534,8 @@ function selectAndCopy() {
|
|
|
514
534
|
</body>
|
|
515
535
|
</html>`;
|
|
516
536
|
}
|
|
517
|
-
function generatePage(
|
|
537
|
+
function generatePage(walletAddressInput) {
|
|
538
|
+
const walletAddress = assertWalletAddress(walletAddressInput);
|
|
518
539
|
return `<!DOCTYPE html>
|
|
519
540
|
<html lang="en">
|
|
520
541
|
<head>
|
|
@@ -741,7 +762,7 @@ function generatePage(walletAddress) {
|
|
|
741
762
|
<div class="qr-container" id="qr"></div>
|
|
742
763
|
|
|
743
764
|
<div class="address-box">
|
|
744
|
-
<code id="address">${walletAddress}</code>
|
|
765
|
+
<code id="address">${escapeHtml(walletAddress)}</code>
|
|
745
766
|
<button class="copy-btn" onclick="copyAddress()" id="copyBtn" title="Copy address">⎘</button>
|
|
746
767
|
</div>
|
|
747
768
|
|
|
@@ -767,7 +788,7 @@ function generatePage(walletAddress) {
|
|
|
767
788
|
</div>
|
|
768
789
|
|
|
769
790
|
<script>
|
|
770
|
-
const WALLET =
|
|
791
|
+
const WALLET = ${jsonForScript(walletAddress)};
|
|
771
792
|
const USDC = '${USDC_ADDRESS}';
|
|
772
793
|
const CHAIN_ID = '${BASE_CHAIN_ID}';
|
|
773
794
|
|
|
@@ -833,21 +854,23 @@ var topup_server_exports = /* @__PURE__ */ __exportAll({
|
|
|
833
854
|
generateArtifactHtml: () => generateArtifactHtml,
|
|
834
855
|
getTopupArtifactUrl: () => getTopupArtifactUrl,
|
|
835
856
|
getTopupUrl: () => getTopupUrl,
|
|
836
|
-
startTopupServer: () => startTopupServer
|
|
857
|
+
startTopupServer: () => startTopupServer,
|
|
858
|
+
stopTopupServer: () => stopTopupServer
|
|
837
859
|
});
|
|
838
|
-
const FALLBACK_PRIVATE_KEY = `0x${"0".repeat(63)}1`;
|
|
839
860
|
let artifactServerState = null;
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
861
|
+
const REQUEST_TARGET_BASE = "http://localhost";
|
|
862
|
+
var ProxyRequestError = class extends Error {
|
|
863
|
+
status;
|
|
864
|
+
constructor(status, message) {
|
|
865
|
+
super(message);
|
|
866
|
+
this.name = "ProxyRequestError";
|
|
867
|
+
this.status = status;
|
|
868
|
+
}
|
|
869
|
+
};
|
|
870
|
+
function toWalletAddress(account) {
|
|
871
|
+
const address = typeof account === "string" ? account : account.address;
|
|
872
|
+
if (!isAddress(address)) throw new Error("Wallet address must be a valid 0x-prefixed 20-byte EVM address");
|
|
873
|
+
return address;
|
|
851
874
|
}
|
|
852
875
|
function send(res, status, body, contentType) {
|
|
853
876
|
res.writeHead(status, {
|
|
@@ -857,8 +880,26 @@ function send(res, status, body, contentType) {
|
|
|
857
880
|
});
|
|
858
881
|
res.end(body);
|
|
859
882
|
}
|
|
860
|
-
|
|
861
|
-
|
|
883
|
+
function normalizeProxyTarget(reqUrl) {
|
|
884
|
+
if (!reqUrl.startsWith("/") || reqUrl.startsWith("//")) throw new ProxyRequestError(400, "Proxy request target must be an origin-form path");
|
|
885
|
+
const parsed = new URL(reqUrl, REQUEST_TARGET_BASE);
|
|
886
|
+
if (parsed.origin !== REQUEST_TARGET_BASE) throw new ProxyRequestError(400, "Absolute and protocol-relative proxy targets are not allowed");
|
|
887
|
+
let decodedPathname;
|
|
888
|
+
try {
|
|
889
|
+
decodedPathname = decodeURIComponent(parsed.pathname);
|
|
890
|
+
} catch {
|
|
891
|
+
throw new ProxyRequestError(400, "Proxy request target contains invalid encoding");
|
|
892
|
+
}
|
|
893
|
+
if (decodedPathname.startsWith("//") || /^\/[a-z][a-z0-9+.-]*:\/\//i.test(decodedPathname)) throw new ProxyRequestError(400, "Encoded host override targets are not allowed");
|
|
894
|
+
return {
|
|
895
|
+
pathAndSearch: `${parsed.pathname}${parsed.search}`,
|
|
896
|
+
pathname: parsed.pathname
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
async function proxyToCopiedServer(proxyTarget, res, assetServerUrl) {
|
|
900
|
+
const allowedOrigin = new URL(assetServerUrl).origin;
|
|
901
|
+
const upstreamUrl = new URL(proxyTarget.pathAndSearch, assetServerUrl);
|
|
902
|
+
if (upstreamUrl.origin !== allowedOrigin) throw new ProxyRequestError(403, "Upstream origin is not allowed");
|
|
862
903
|
const upstream = await fetch(upstreamUrl);
|
|
863
904
|
const contentType = upstream.headers.get("content-type") ?? "application/octet-stream";
|
|
864
905
|
const body = Buffer.from(await upstream.arrayBuffer());
|
|
@@ -870,24 +911,41 @@ function getTopupArtifactUrl() {
|
|
|
870
911
|
function getTopupUrl() {
|
|
871
912
|
return getTopupArtifactUrl() ?? getTopupUrl$1();
|
|
872
913
|
}
|
|
914
|
+
async function stopTopupServer() {
|
|
915
|
+
if (!artifactServerState) return;
|
|
916
|
+
const { server } = artifactServerState;
|
|
917
|
+
artifactServerState = null;
|
|
918
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
919
|
+
}
|
|
873
920
|
async function startTopupServer(account) {
|
|
874
|
-
const
|
|
875
|
-
if (artifactServerState && artifactServerState.address.toLowerCase() ===
|
|
876
|
-
const assetServerUrl = await startTopupServer$1(
|
|
877
|
-
if (artifactServerState)
|
|
878
|
-
await new Promise((resolve) => artifactServerState?.server.close(() => resolve()));
|
|
879
|
-
artifactServerState = null;
|
|
880
|
-
}
|
|
921
|
+
const walletAddress = toWalletAddress(account);
|
|
922
|
+
if (artifactServerState && artifactServerState.address.toLowerCase() === walletAddress.toLowerCase()) return artifactServerState.url;
|
|
923
|
+
const assetServerUrl = await startTopupServer$1(walletAddress);
|
|
924
|
+
if (artifactServerState) await stopTopupServer();
|
|
881
925
|
const server = createServer((req, res) => {
|
|
882
926
|
const reqUrl = req.url ?? "/";
|
|
883
|
-
|
|
927
|
+
let proxyTarget;
|
|
928
|
+
try {
|
|
929
|
+
proxyTarget = normalizeProxyTarget(reqUrl);
|
|
930
|
+
} catch (err) {
|
|
931
|
+
if (err instanceof ProxyRequestError) {
|
|
932
|
+
send(res, err.status, JSON.stringify({ error: err.message }) + "\n", "application/json; charset=utf-8");
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
send(res, 400, JSON.stringify({ error: "Invalid request target" }) + "\n", "application/json; charset=utf-8");
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
const { pathname } = proxyTarget;
|
|
884
939
|
if (pathname === "/" || pathname === "/index.html") {
|
|
885
|
-
|
|
886
|
-
send(res, 200, generateArtifactHtml(wallet.address, artifactUrl), "text/html; charset=utf-8");
|
|
940
|
+
send(res, 200, generateArtifactHtml(walletAddress, artifactServerState?.url ?? assetServerUrl), "text/html; charset=utf-8");
|
|
887
941
|
return;
|
|
888
942
|
}
|
|
889
943
|
if (pathname.startsWith("/assets/") || pathname.startsWith("/api/")) {
|
|
890
|
-
proxyToCopiedServer(
|
|
944
|
+
proxyToCopiedServer(proxyTarget, res, assetServerUrl).catch((err) => {
|
|
945
|
+
if (err instanceof ProxyRequestError) {
|
|
946
|
+
send(res, err.status, JSON.stringify({ error: err.message }) + "\n", "application/json; charset=utf-8");
|
|
947
|
+
return;
|
|
948
|
+
}
|
|
891
949
|
send(res, 502, JSON.stringify({ error: err.message }) + "\n", "application/json; charset=utf-8");
|
|
892
950
|
});
|
|
893
951
|
return;
|
|
@@ -906,7 +964,7 @@ async function startTopupServer(account) {
|
|
|
906
964
|
});
|
|
907
965
|
});
|
|
908
966
|
artifactServerState = {
|
|
909
|
-
address:
|
|
967
|
+
address: walletAddress,
|
|
910
968
|
assetServerUrl,
|
|
911
969
|
server,
|
|
912
970
|
url
|
|
@@ -916,4 +974,4 @@ async function startTopupServer(account) {
|
|
|
916
974
|
//#endregion
|
|
917
975
|
export { generateArtifactHtml as i, startTopupServer as n, topup_server_exports as r, getTopupUrl as t };
|
|
918
976
|
|
|
919
|
-
//# sourceMappingURL=topup-server-
|
|
977
|
+
//# sourceMappingURL=topup-server-BJgVw6Jt.mjs.map
|