chain-insights 0.71.4 → 0.73.2
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 +38 -3
- package/dist/buy-97M6dtEu.cjs +195 -0
- package/dist/buy-_mpdRieG.mjs +195 -0
- package/dist/buy-_mpdRieG.mjs.map +1 -0
- package/dist/{capabilities-DGavTSoW.cjs → capabilities-Ds3320iS.cjs} +1 -1
- package/dist/{capabilities-BBVDIECk.mjs → capabilities-DxojbX4D.mjs} +2 -2
- package/dist/{capabilities-BBVDIECk.mjs.map → capabilities-DxojbX4D.mjs.map} +1 -1
- package/dist/{claude-desktop-CTaBCUIm.mjs → claude-desktop-6jwX0Lux.mjs} +18 -3
- package/dist/{claude-desktop-CTaBCUIm.mjs.map → claude-desktop-6jwX0Lux.mjs.map} +1 -1
- package/dist/{claude-desktop-BI_mGkZ6.cjs → claude-desktop-Jmm4PlBZ.cjs} +17 -2
- package/dist/cli.cjs +163 -31
- package/dist/cli.mjs +163 -31
- package/dist/cli.mjs.map +1 -1
- package/dist/{client-gsLi528K.mjs → client-7qOfAdpH.mjs} +31 -12
- package/dist/client-7qOfAdpH.mjs.map +1 -0
- package/dist/{client-Ypw9BNbn.cjs → client-C9uwHnBS.cjs} +33 -14
- package/dist/{dex-registry-KOTJhAQf.cjs → dex-registry-Chs79Ukc.cjs} +11 -8
- package/dist/{dex-registry-05wAhSa0.mjs → dex-registry-rziydXYq.mjs} +12 -9
- package/dist/{dex-registry-05wAhSa0.mjs.map → dex-registry-rziydXYq.mjs.map} +1 -1
- package/dist/{dex-tool-B4T8Czhm.cjs → dex-tool-DLWnff1n.cjs} +6 -4
- package/dist/{dex-tool-B5Qwz1Zd.mjs → dex-tool-DsaAIa-a.mjs} +7 -5
- package/dist/{dex-tool-B5Qwz1Zd.mjs.map → dex-tool-DsaAIa-a.mjs.map} +1 -1
- package/dist/{graph-schema-BoUMa03H.cjs → graph-schema-BGbJumCY.cjs} +63 -25
- package/dist/{graph-schema-DqZUTnP0.mjs → graph-schema-ClqjzgrM.mjs} +59 -27
- package/dist/{graph-schema-DqZUTnP0.mjs.map → graph-schema-ClqjzgrM.mjs.map} +1 -1
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +7 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +7 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +5 -5
- package/dist/known-keys-B87ZqOgE.mjs +78 -0
- package/dist/known-keys-B87ZqOgE.mjs.map +1 -0
- package/dist/known-keys-PKZF5GYd.cjs +99 -0
- package/dist/mcp-proxy.cjs +86 -39
- package/dist/mcp-proxy.d.cts.map +1 -1
- package/dist/mcp-proxy.d.mts.map +1 -1
- package/dist/mcp-proxy.mjs +86 -39
- package/dist/mcp-proxy.mjs.map +1 -1
- package/dist/{public-tools-Dj130EuJ.cjs → public-tools-Bl_l_rKK.cjs} +8 -5
- package/dist/{public-tools-B77ZT0jt.mjs → public-tools-CQjGt7_q.mjs} +9 -6
- package/dist/{public-tools-B77ZT0jt.mjs.map → public-tools-CQjGt7_q.mjs.map} +1 -1
- package/dist/quote-B9sYNOiv.cjs +70 -0
- package/dist/quote-DfTut0cx.mjs +68 -0
- package/dist/quote-DfTut0cx.mjs.map +1 -0
- package/dist/robinhood-CCTMagwM.mjs +226 -0
- package/dist/robinhood-CCTMagwM.mjs.map +1 -0
- package/dist/robinhood-ChiT69sc.cjs +313 -0
- package/dist/rules-BD-cNHMX.cjs +31 -0
- package/dist/rules-BWdbOtHi.mjs +10 -0
- package/dist/rules-BWdbOtHi.mjs.map +1 -0
- package/dist/{schema-cache-DwddPLcY.cjs → schema-cache-C7v9ml6S.cjs} +1 -1
- package/dist/{schema-cache-BT-kHy_r.mjs → schema-cache-DRObKUvS.mjs} +3 -3
- package/dist/{schema-cache-BT-kHy_r.mjs.map → schema-cache-DRObKUvS.mjs.map} +1 -1
- package/dist/{skill-refresh-FXhQpW94.cjs → skill-refresh-Be2D5FrX.cjs} +2 -2
- package/dist/{skill-refresh-I5MSULwP.mjs → skill-refresh-xYWZ_hua.mjs} +3 -3
- package/dist/{skill-refresh-I5MSULwP.mjs.map → skill-refresh-xYWZ_hua.mjs.map} +1 -1
- package/dist/subscription-status-9tMfD90J.cjs +168 -0
- package/dist/subscription-status-DIuH84fM.mjs +148 -0
- package/dist/subscription-status-DIuH84fM.mjs.map +1 -0
- package/dist/{tools-BHfphgy9.cjs → tools-Cmnq7yip.cjs} +21 -4
- package/dist/{tools-BnThuvE2.mjs → tools-D9ep9E7Q.mjs} +22 -5
- package/dist/tools-D9ep9E7Q.mjs.map +1 -0
- package/dist/topup--nWtDnWP.cjs +43 -0
- package/dist/topup-CfFe1bGS.mjs +45 -0
- package/dist/topup-CfFe1bGS.mjs.map +1 -0
- package/dist/{topup-server-DzeM_YAt.mjs → topup-server-B0VKHslr.mjs} +2 -2
- package/dist/{topup-server-DzeM_YAt.mjs.map → topup-server-B0VKHslr.mjs.map} +1 -1
- package/dist/{topup-server-BIbSfvX5.cjs → topup-server-BkGM8P99.cjs} +1 -1
- package/dist/{topup-server-Bh7MlzRt.cjs → topup-server-C58eWflv.cjs} +284 -69
- package/dist/{topup-server-BrkPhqJG.mjs → topup-server-RQ6-c92F.mjs} +274 -71
- package/dist/topup-server-RQ6-c92F.mjs.map +1 -0
- package/dist/{wallet-B3hAr70M.cjs → wallet-CgIAfdxL.cjs} +34 -0
- package/dist/{wallet-GNDl3FkR.mjs → wallet-QTo5YCFR.mjs} +35 -1
- package/dist/wallet-QTo5YCFR.mjs.map +1 -0
- package/docs/development.md +1 -1
- package/docs/graph-tools.md +2 -1
- package/docs/mcp-proxy.md +30 -4
- package/docs/stability.md +3 -2
- package/package.json +1 -1
- package/skills/chain-insights-cypher/SKILL.md +9 -0
- package/dist/client-gsLi528K.mjs.map +0 -1
- package/dist/subscription-status-DA9DCFrC.mjs +0 -32
- package/dist/subscription-status-DA9DCFrC.mjs.map +0 -1
- package/dist/subscription-status-ru99UCPZ.cjs +0 -40
- package/dist/tools-BnThuvE2.mjs.map +0 -1
- package/dist/topup-server-BrkPhqJG.mjs.map +0 -1
- package/dist/wallet-GNDl3FkR.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities-BBVDIECk.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\n /** The relationships a facts layer serves, when the server lists them. */\n relationships?: string[]\n}\n\n/**\n * A block the server publishes about one layer's limits. The mirror repeats it\n * exactly as it came: the members named below are the ones known today, and\n * any other member the server adds is kept. Read a limit from the block, never\n * from a number written elsewhere.\n */\nexport type NetworkAdmissionBlock = Record<string, unknown>\n\n/** `USE chain`: the lookups, the ceilings and the slots of the chain layer. */\nexport interface ChainAdmission extends NetworkAdmissionBlock {\n enabled: boolean\n /** ok, or the reason the layer is off or behind. */\n status?: string\n rules_version?: string\n grammar?: string\n /** The labels of `USE chain`, such as Transaction, Block, Head and Address. */\n lookups?: string[]\n ceiling_seconds?: Record<string, number>\n slots?: number\n past_slots?: number\n slots_per_caller?: number\n calls_per_second_per_caller?: number\n batch_max?: number\n call_gas?: number\n head_ttl_ms?: number\n max_head_age_seconds?: number\n /** How many blocks below the tip a past `Address` read (`at_block`) must be. Read it here, never write it down. */\n at_block_min_depth?: number\n}\n\n/** `USE topology`: the shape rules, when the server publishes them. */\nexport interface TopologyAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n max_hops_per_path?: number\n max_limit?: number\n slots?: number\n}\n\n/** `USE facts`: the read contract, when the server publishes it. */\nexport interface FactsAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n window_days?: number\n max_rows?: number\n max_hops?: number\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n /** The layers as the server sent them. Absent when the server sent none. */\n layers?: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n /** The three layer blocks below are repeated exactly as sent, and absent when not sent. */\n chain_admission?: ChainAdmission\n topology_admission?: TopologyAdmission\n facts_admission?: FactsAdmission\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n /** The chain node's own head block, and how old that reading is. */\n chain_head_block?: number\n chain_head_age_seconds?: number\n warehouse_blocks_behind_head?: number\n graph_blocks_behind_head?: number\n }\n lane_progress?: NetworkLaneProgress[]\n graph_progress?: NetworkGraphProgress\n}\n\n/** One writer lane's committed height. null: the lane has not started. */\nexport interface NetworkLaneProgress {\n lane: string\n height: number | null\n}\n\n/**\n * How far each block layer of the topology graph reaches, and how far the\n * money family reaches (`complete_through_block`). `families` is absent on a\n * server that does not group its layers into families.\n */\nexport interface NetworkGraphProgress {\n complete_through_block: number\n layers: { layer: string; position: number }[]\n families?: NetworkGraphFamily[]\n}\n\n/**\n * One layer family as the server reports it. A block family carries the block\n * it is complete through and its lag behind the head (null on a server that\n * knows no head). `annotations` follows runs, not blocks: both numbers are null\n * and `fresh` says whether its last run is current.\n */\nexport interface NetworkGraphFamily {\n name: string\n complete_through_block: number | null\n blocks_behind_head: number | null\n fresh?: boolean\n layers: string[]\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction advertisedTools(raw: unknown): Record<string, string> {\n if (!isRecord(raw)) return {}\n const tools: Record<string, string> = {}\n for (const [name, status] of Object.entries(raw)) {\n if (name.trim() !== '' && typeof status === 'string') tools[name] = status\n }\n return tools\n}\n\nfunction advertisedLaneProgress(raw: unknown): NetworkLaneProgress[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const lanes: NetworkLaneProgress[] = []\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.lane !== 'string' || entry.lane.trim() === '') continue\n if (typeof entry.height === 'number') lanes.push({ lane: entry.lane, height: entry.height })\n else if (entry.height === null) lanes.push({ lane: entry.lane, height: null })\n }\n return lanes\n}\n\nfunction isNumberOrNull(value: unknown): value is number | null {\n return value === null || typeof value === 'number'\n}\n\n/** One family entry, or null when any member has the wrong shape. */\nfunction advertisedGraphFamily(entry: unknown): NetworkGraphFamily | null {\n if (!isRecord(entry) || typeof entry.name !== 'string' || entry.name.trim() === '') return null\n if (!isNumberOrNull(entry.complete_through_block)) return null\n const behind = entry.blocks_behind_head === undefined ? null : entry.blocks_behind_head\n if (!isNumberOrNull(behind)) return null\n if (entry.fresh !== undefined && typeof entry.fresh !== 'boolean') return null\n if (!Array.isArray(entry.layers) || !entry.layers.every((layer) => typeof layer === 'string'))\n return null\n return {\n name: entry.name,\n complete_through_block: entry.complete_through_block,\n blocks_behind_head: behind,\n ...(typeof entry.fresh === 'boolean' ? { fresh: entry.fresh } : {}),\n layers: [...(entry.layers as string[])],\n }\n}\n\n/** The families list, whole or not at all: one entry with a bad shape drops it. */\nfunction advertisedGraphFamilies(raw: unknown): NetworkGraphFamily[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const families: NetworkGraphFamily[] = []\n for (const entry of raw) {\n const family = advertisedGraphFamily(entry)\n if (family === null) return undefined\n families.push(family)\n }\n return families\n}\n\nfunction advertisedGraphProgress(raw: unknown): NetworkGraphProgress | undefined {\n if (!isRecord(raw) || typeof raw.complete_through_block !== 'number') return undefined\n if (!Array.isArray(raw.layers)) return undefined\n const layers: NetworkGraphProgress['layers'] = []\n for (const entry of raw.layers) {\n if (!isRecord(entry) || typeof entry.layer !== 'string' || typeof entry.position !== 'number')\n continue\n layers.push({ layer: entry.layer, position: entry.position })\n }\n const progress: NetworkGraphProgress = {\n complete_through_block: raw.complete_through_block,\n layers,\n }\n const families = advertisedGraphFamilies(raw.families)\n if (families) progress.families = families\n return progress\n}\n\nfunction advertisedNetwork(raw: unknown): NetworkCapability | null {\n if (!isRecord(raw) || typeof raw.network !== 'string' || raw.network.trim() === '') {\n return null\n }\n const network = raw.network.trim()\n const capability: NetworkCapability = {\n network,\n display_name:\n typeof raw.display_name === 'string' && raw.display_name.trim() !== ''\n ? raw.display_name\n : network,\n status: typeof raw.status === 'string' && raw.status.trim() !== '' ? raw.status : 'live',\n tools: advertisedTools(raw.tools),\n }\n // The layer blocks are the server's own words about its limits. They pass\n // through exactly as sent: nothing is added, no default is invented and no\n // member is dropped. A block the server did not send stays absent.\n if (isRecord(raw.layers)) capability.layers = raw.layers as NetworkCapability['layers']\n if (isRecord(raw.chain_admission)) {\n capability.chain_admission = raw.chain_admission as unknown as ChainAdmission\n }\n if (isRecord(raw.topology_admission)) {\n capability.topology_admission = raw.topology_admission as TopologyAdmission\n }\n if (isRecord(raw.facts_admission)) {\n capability.facts_admission = raw.facts_admission as FactsAdmission\n }\n if (raw.default === true) capability.default = true\n if (raw.default === false) capability.default = false\n if (isRecord(raw.coverage)) capability.coverage = raw.coverage as NetworkCapability['coverage']\n if (isRecord(raw.freshness))\n capability.freshness = raw.freshness as NetworkCapability['freshness']\n const laneProgress = advertisedLaneProgress(raw.lane_progress)\n if (laneProgress) capability.lane_progress = laneProgress\n const graphProgress = advertisedGraphProgress(raw.graph_progress)\n if (graphProgress) capability.graph_progress = graphProgress\n return capability\n}\n\n/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */\nexport function mirrorGraphNetworkCapabilities(document: {\n networks: unknown[]\n}): NetworkCapabilitiesDocument {\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: document.networks\n .map((network) => advertisedNetwork(network))\n .filter((network): network is NetworkCapability => network !== null),\n }\n}\n\nfunction publicNetworkCapabilities(\n document: NetworkCapabilitiesDocument\n): NetworkCapabilitiesDocument {\n return mirrorGraphNetworkCapabilities(document)\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint'>\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim()\n if (token) {\n applyMcpAuthHeaders(headers, token)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = (await response.json()) as NetworkCapabilitiesDocument\n if (\n parsed.schema !== 'chain-insights.network-capabilities.v1' ||\n !Array.isArray(parsed.networks)\n ) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return publicNetworkCapabilities(parsed)\n}\n\nfunction availableTools(network: NetworkCapability): string[] {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange =\n coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange =\n coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\n return `${blockRange} / ${dateRange}`\n}\n\nfunction displayName(network: NetworkCapability): string {\n return network.display_name || network.network\n}\n\nfunction statusLabel(network: NetworkCapability): string {\n return network.default ? `${network.status} (default)` : network.status\n}\n\nfunction formatTable(headers: string[], rows: string[][], minimumWidths: number[]): string {\n const widths = headers.map((header, index) =>\n Math.max(\n minimumWidths[index] ?? 0,\n header.length,\n ...rows.map((row) => row[index]?.length ?? 0)\n )\n )\n const row = (values: string[]) =>\n values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [row(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(row)].join(\n '\\n'\n )\n}\n\nexport function formatNetworkOverview(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const rows = document.networks.map((network) => [\n displayName(network),\n statusLabel(network),\n datasetLabel(network),\n ])\n return formatTable(['Network', 'Status', 'Dataset'], rows, [14, 10, 38])\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => [\n index === 0 ? displayName(network) : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ])\n })\n return formatTable(['Network', 'Dataset', 'Chain Insights tools'], networkRows, [14, 38, 64])\n}\n\nexport function findNetworkCapability(\n document: NetworkCapabilitiesDocument,\n name: string\n): NetworkCapability | undefined {\n const normalizedName = name.trim().toLowerCase()\n if (!normalizedName) return undefined\n return document.networks.find((network) =>\n [network.network, network.display_name].some(\n (candidate) => candidate?.trim().toLowerCase() === normalizedName\n )\n )\n}\n\n/** The writer lanes a customer sees, in screen order, by their plain names. */\nconst KNOWN_LANES: readonly (readonly [string, string])[] = [\n ['core_transfers_source_head', 'receipts'],\n ['core_transfers_internal_follow', 'traces'],\n ['core_dex_follow', 'dex'],\n ['core_bridge_follow', 'bridge'],\n ['core_smart_account_follow', 'smart accounts'],\n]\n\n/** The evidence worker's lane. It has its own table and never joins Indexing. */\nconst EVIDENCE_LANE = 'evidence_sync'\n\n/** The plain name a lane prints as. An unknown lane prints as sent. */\nexport function laneDisplayName(lane: string): string {\n if (lane === EVIDENCE_LANE) return 'evidence'\n return KNOWN_LANES.find(([name]) => name === lane)?.[1] ?? lane\n}\n\n/** A graph layer prints as sent: it is the link kind a query names. */\nexport function layerDisplayName(layer: string): string {\n return layer\n}\n\n/** A family prints by its plain name: `smart_accounts` prints as \"smart accounts\". */\nexport function familyDisplayName(family: string): string {\n return family.replace(/_/g, ' ')\n}\n\n/** 87642448 -> \"87,642,448\", the same on every machine (no locale). */\nexport function groupThousands(value: number): string {\n const sign = value < 0 ? '-' : ''\n const digits = String(Math.trunc(Math.abs(value)))\n return sign + digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\n/** One row of a progress table. `behind` is absent with no head or no height. */\ninterface ProgressRow {\n label: string\n height: number | null\n /** Text printed in the height column instead of a number. */\n note?: string\n behind?: number\n}\n\nconst DETAIL_LABEL_WIDTH = 18\nconst TABLE_LABEL_WIDTH = 30\nconst HEIGHT_MIN_WIDTH = 10\nconst LAG_HEADER = 'behind head'\n\nfunction progressRows(\n entries: { label: string; height: number | null }[],\n head: number | undefined\n): ProgressRow[] {\n return entries.map(({ label, height }) => ({\n label,\n height,\n ...(height === null ? { note: 'not started' } : {}),\n ...(height !== null && head !== undefined ? { behind: Math.max(0, head - height) } : {}),\n }))\n}\n\nfunction notReportedRow(label: string): ProgressRow {\n return { label, height: null, note: 'not reported' }\n}\n\n/**\n * The family rows, in the order the server sends them. A block family's lag is\n * the server's `blocks_behind_head`, or head minus height when the server knows\n * no head. A family with no block (annotations) prints \"fresh\" or \"walking\" and\n * no lag.\n */\nfunction familyRows(families: NetworkGraphFamily[], head: number | undefined): ProgressRow[] {\n return families.map((family) => {\n const label = familyDisplayName(family.name)\n const height = family.complete_through_block\n if (height === null) {\n const note = family.fresh === undefined ? 'not started' : family.fresh ? 'fresh' : 'walking'\n return { label, height: null, note }\n }\n if (head === undefined) return { label, height }\n const behind = family.blocks_behind_head ?? Math.max(0, head - height)\n return { label, height, behind }\n })\n}\n\nfunction formatProgressTables(\n tables: { title: string; rows: ProgressRow[] }[],\n withLag: boolean\n): string[] {\n const allRows = tables.flatMap((table) => table.rows)\n const labelWidth = Math.max(\n TABLE_LABEL_WIDTH,\n ...allRows.map((row) => row.label.length + 4),\n ...tables.map((table) => table.title.length + 2)\n )\n const heightWidth = Math.max(\n HEIGHT_MIN_WIDTH,\n ...allRows.map((row) => (row.height === null ? 0 : groupThousands(row.height).length))\n )\n const lagWidth = Math.max(\n LAG_HEADER.length,\n ...allRows.map((row) => (row.behind === undefined ? 0 : groupThousands(row.behind).length))\n )\n const lines: string[] = []\n for (const table of tables) {\n if (lines.length > 0) lines.push('')\n const header = table.title.padEnd(labelWidth) + 'height'\n lines.push(withLag ? header.padEnd(labelWidth + heightWidth + 2) + LAG_HEADER : header)\n for (const row of table.rows) {\n let line = ` ${row.label}`.padEnd(labelWidth)\n if (row.height === null) {\n line += row.note ?? ''\n } else {\n line += groupThousands(row.height).padStart(heightWidth)\n if (withLag && row.behind !== undefined) {\n line += ' ' + groupThousands(row.behind).padStart(lagWidth)\n }\n }\n lines.push(line.trimEnd())\n }\n }\n return lines\n}\n\n/**\n * `cia network <name>`: how far this network is indexed. The chain node's head\n * comes first; every row reads against it. Known lanes print by plain name,\n * unknown lanes as sent after them, the layer families by plain name in the\n * server's order, graph layers as sent, and evidence in its own table. Heights\n * are right-aligned, so a shorter height lines up by its last digit.\n * `--json` never comes through here.\n */\nexport function formatNetworkCapability(network: NetworkCapability): string {\n const headBlock = network.freshness?.chain_head_block\n const head = typeof headBlock === 'number' ? headBlock : undefined\n const detail: [string, string][] = [\n ['Network', displayName(network)],\n ['Identifier', network.network],\n ['Status', statusLabel(network)],\n ]\n if (head !== undefined) {\n const age = network.freshness?.chain_head_age_seconds\n const ageText = typeof age === 'number' ? ` (${groupThousands(age)} s old)` : ''\n detail.push(['Blockchain head', `${groupThousands(head)}${ageText}`])\n }\n\n const lanes = network.lane_progress ?? []\n const known = KNOWN_LANES.flatMap(([name, label]) =>\n lanes.filter((lane) => lane.lane === name).map((lane) => ({ label, height: lane.height }))\n )\n const unknown = lanes\n .filter(\n (lane) => lane.lane !== EVIDENCE_LANE && !KNOWN_LANES.some(([name]) => name === lane.lane)\n )\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n const indexing = [...known, ...unknown]\n const layers = (network.graph_progress?.layers ?? []).map((layer) => ({\n label: layerDisplayName(layer.layer),\n height: layer.position,\n }))\n const evidence = lanes\n .filter((lane) => lane.lane === EVIDENCE_LANE)\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n\n const families = network.graph_progress?.families\n\n const tables = [\n {\n title: 'Indexing',\n rows: indexing.length > 0 ? progressRows(indexing, head) : [notReportedRow('indexing')],\n },\n // Absent on a server that sends no families: the table is left out, so the\n // screen of such a server stays as it was.\n ...(families ? [{ title: 'Families', rows: familyRows(families, head) }] : []),\n {\n title: 'Graph',\n rows: layers.length > 0 ? progressRows(layers, head) : [notReportedRow('graph')],\n },\n {\n title: 'Evidence',\n rows: evidence.length > 0 ? progressRows(evidence, head) : [notReportedRow('evidence')],\n },\n ]\n\n return [\n ...detail.map(([label, value]) => `${label.padEnd(DETAIL_LABEL_WIDTH)}${value}`),\n '',\n ...formatProgressTables(tables, head !== undefined),\n ].join('\\n')\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"capabilities-DxojbX4D.mjs","names":[],"sources":["../src/mcp/capabilities.ts"],"sourcesContent":["import type { InvestigatorConfig } from '../config/schema.js'\nimport { applyMcpAuthHeaders, resolveGraphMcpEndpoint } from './client.js'\n\nexport interface NetworkTopologyLayer {\n enabled: boolean\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n}\n\nexport interface NetworkLayerCapability {\n enabled: boolean\n live?: NetworkTopologyLayer\n archive?: NetworkTopologyLayer\n /** The relationships a facts layer serves, when the server lists them. */\n relationships?: string[]\n}\n\n/**\n * A block the server publishes about one layer's limits. The mirror repeats it\n * exactly as it came: the members named below are the ones known today, and\n * any other member the server adds is kept. Read a limit from the block, never\n * from a number written elsewhere.\n */\nexport type NetworkAdmissionBlock = Record<string, unknown>\n\n/** `USE chain`: the lookups, the ceilings and the slots of the chain layer. */\nexport interface ChainAdmission extends NetworkAdmissionBlock {\n enabled: boolean\n /** ok, or the reason the layer is off or behind. */\n status?: string\n rules_version?: string\n grammar?: string\n /** The labels of `USE chain`, such as Transaction, Block, Head and Address. */\n lookups?: string[]\n ceiling_seconds?: Record<string, number>\n slots?: number\n past_slots?: number\n slots_per_caller?: number\n calls_per_second_per_caller?: number\n batch_max?: number\n call_gas?: number\n head_ttl_ms?: number\n max_head_age_seconds?: number\n /** How many blocks below the tip a past `Address` read (`at_block`) must be. Read it here, never write it down. */\n at_block_min_depth?: number\n}\n\n/** `USE topology`: the shape rules, when the server publishes them. */\nexport interface TopologyAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n max_hops_per_path?: number\n max_limit?: number\n slots?: number\n}\n\n/** `USE facts`: the read contract, when the server publishes it. */\nexport interface FactsAdmission extends NetworkAdmissionBlock {\n rules_version?: string\n window_days?: number\n max_rows?: number\n max_hops?: number\n}\n\nexport interface NetworkCapability {\n network: string\n display_name?: string\n status: string\n default?: boolean\n /** The layers as the server sent them. Absent when the server sent none. */\n layers?: Record<string, NetworkLayerCapability>\n tools: Record<string, string>\n /** The three layer blocks below are repeated exactly as sent, and absent when not sent. */\n chain_admission?: ChainAdmission\n topology_admission?: TopologyAdmission\n facts_admission?: FactsAdmission\n coverage?: {\n from_block?: number\n to_block?: number\n from_timestamp?: string\n to_timestamp?: string\n chain_tip_block?: number\n blocks_behind_tip?: number\n /** Lowest height among the raw-data lanes that have started. Above it an empty answer may mean \"not indexed yet\". */\n complete_through_block?: number\n }\n freshness?: {\n last_processed_at?: string\n last_successful_sync_at?: string\n max_data_age_seconds?: number\n last_processing_duration_seconds?: number\n /** The chain node's own head block, and how old that reading is. */\n chain_head_block?: number\n chain_head_age_seconds?: number\n warehouse_blocks_behind_head?: number\n graph_blocks_behind_head?: number\n }\n lane_progress?: NetworkLaneProgress[]\n graph_progress?: NetworkGraphProgress\n}\n\n/** One writer lane's committed height. null: the lane has not started. */\nexport interface NetworkLaneProgress {\n lane: string\n height: number | null\n}\n\n/**\n * How far each block layer of the topology graph reaches, and how far the\n * money family reaches (`complete_through_block`). `families` is absent on a\n * server that does not group its layers into families.\n */\nexport interface NetworkGraphProgress {\n complete_through_block: number\n layers: { layer: string; position: number }[]\n families?: NetworkGraphFamily[]\n}\n\n/**\n * One layer family as the server reports it. A block family carries the block\n * it is complete through and its lag behind the head (null on a server that\n * knows no head). `annotations` follows runs, not blocks: both numbers are null\n * and `fresh` says whether its last run is current.\n */\nexport interface NetworkGraphFamily {\n name: string\n complete_through_block: number | null\n blocks_behind_head: number | null\n fresh?: boolean\n layers: string[]\n}\n\nexport interface NetworkCapabilitiesDocument {\n schema: 'chain-insights.network-capabilities.v1'\n networks: NetworkCapability[]\n}\n\nconst AVAILABLE_TOOLS_PER_LINE = 3\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n\nfunction advertisedTools(raw: unknown): Record<string, string> {\n if (!isRecord(raw)) return {}\n const tools: Record<string, string> = {}\n for (const [name, status] of Object.entries(raw)) {\n if (name.trim() !== '' && typeof status === 'string') tools[name] = status\n }\n return tools\n}\n\nfunction advertisedLaneProgress(raw: unknown): NetworkLaneProgress[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const lanes: NetworkLaneProgress[] = []\n for (const entry of raw) {\n if (!isRecord(entry) || typeof entry.lane !== 'string' || entry.lane.trim() === '') continue\n if (typeof entry.height === 'number') lanes.push({ lane: entry.lane, height: entry.height })\n else if (entry.height === null) lanes.push({ lane: entry.lane, height: null })\n }\n return lanes\n}\n\nfunction isNumberOrNull(value: unknown): value is number | null {\n return value === null || typeof value === 'number'\n}\n\n/** One family entry, or null when any member has the wrong shape. */\nfunction advertisedGraphFamily(entry: unknown): NetworkGraphFamily | null {\n if (!isRecord(entry) || typeof entry.name !== 'string' || entry.name.trim() === '') return null\n if (!isNumberOrNull(entry.complete_through_block)) return null\n const behind = entry.blocks_behind_head === undefined ? null : entry.blocks_behind_head\n if (!isNumberOrNull(behind)) return null\n if (entry.fresh !== undefined && typeof entry.fresh !== 'boolean') return null\n if (!Array.isArray(entry.layers) || !entry.layers.every((layer) => typeof layer === 'string'))\n return null\n return {\n name: entry.name,\n complete_through_block: entry.complete_through_block,\n blocks_behind_head: behind,\n ...(typeof entry.fresh === 'boolean' ? { fresh: entry.fresh } : {}),\n layers: [...(entry.layers as string[])],\n }\n}\n\n/** The families list, whole or not at all: one entry with a bad shape drops it. */\nfunction advertisedGraphFamilies(raw: unknown): NetworkGraphFamily[] | undefined {\n if (!Array.isArray(raw)) return undefined\n const families: NetworkGraphFamily[] = []\n for (const entry of raw) {\n const family = advertisedGraphFamily(entry)\n if (family === null) return undefined\n families.push(family)\n }\n return families\n}\n\nfunction advertisedGraphProgress(raw: unknown): NetworkGraphProgress | undefined {\n if (!isRecord(raw) || typeof raw.complete_through_block !== 'number') return undefined\n if (!Array.isArray(raw.layers)) return undefined\n const layers: NetworkGraphProgress['layers'] = []\n for (const entry of raw.layers) {\n if (!isRecord(entry) || typeof entry.layer !== 'string' || typeof entry.position !== 'number')\n continue\n layers.push({ layer: entry.layer, position: entry.position })\n }\n const progress: NetworkGraphProgress = {\n complete_through_block: raw.complete_through_block,\n layers,\n }\n const families = advertisedGraphFamilies(raw.families)\n if (families) progress.families = families\n return progress\n}\n\nfunction advertisedNetwork(raw: unknown): NetworkCapability | null {\n if (!isRecord(raw) || typeof raw.network !== 'string' || raw.network.trim() === '') {\n return null\n }\n const network = raw.network.trim()\n const capability: NetworkCapability = {\n network,\n display_name:\n typeof raw.display_name === 'string' && raw.display_name.trim() !== ''\n ? raw.display_name\n : network,\n status: typeof raw.status === 'string' && raw.status.trim() !== '' ? raw.status : 'live',\n tools: advertisedTools(raw.tools),\n }\n // The layer blocks are the server's own words about its limits. They pass\n // through exactly as sent: nothing is added, no default is invented and no\n // member is dropped. A block the server did not send stays absent.\n if (isRecord(raw.layers)) capability.layers = raw.layers as NetworkCapability['layers']\n if (isRecord(raw.chain_admission)) {\n capability.chain_admission = raw.chain_admission as unknown as ChainAdmission\n }\n if (isRecord(raw.topology_admission)) {\n capability.topology_admission = raw.topology_admission as TopologyAdmission\n }\n if (isRecord(raw.facts_admission)) {\n capability.facts_admission = raw.facts_admission as FactsAdmission\n }\n if (raw.default === true) capability.default = true\n if (raw.default === false) capability.default = false\n if (isRecord(raw.coverage)) capability.coverage = raw.coverage as NetworkCapability['coverage']\n if (isRecord(raw.freshness))\n capability.freshness = raw.freshness as NetworkCapability['freshness']\n const laneProgress = advertisedLaneProgress(raw.lane_progress)\n if (laneProgress) capability.lane_progress = laneProgress\n const graphProgress = advertisedGraphProgress(raw.graph_progress)\n if (graphProgress) capability.graph_progress = graphProgress\n return capability\n}\n\n/** Repeat GraphRAG's network list. CIA does not add, drop, or invent names. */\nexport function mirrorGraphNetworkCapabilities(document: {\n networks: unknown[]\n}): NetworkCapabilitiesDocument {\n return {\n schema: 'chain-insights.network-capabilities.v1',\n networks: document.networks\n .map((network) => advertisedNetwork(network))\n .filter((network): network is NetworkCapability => network !== null),\n }\n}\n\nfunction publicNetworkCapabilities(\n document: NetworkCapabilitiesDocument\n): NetworkCapabilitiesDocument {\n return mirrorGraphNetworkCapabilities(document)\n}\n\nfunction metadataNetworksUrl(endpoint: string): URL {\n const url = new URL(endpoint)\n url.pathname = '/metadata/networks'\n url.search = ''\n url.hash = ''\n return url\n}\n\nexport async function fetchNetworkCapabilities(\n config: Pick<InvestigatorConfig, 'graphMcpAuthToken' | 'graphMcpMode' | 'graphMcpEndpoint'>\n): Promise<NetworkCapabilitiesDocument> {\n const endpoint = resolveGraphMcpEndpoint(config)\n const request = metadataNetworksUrl(endpoint)\n const headers = new Headers()\n const token = config.graphMcpAuthToken?.trim()\n if (token) {\n applyMcpAuthHeaders(headers, token)\n }\n let response: Response\n try {\n response = await fetch(request, { headers })\n } catch (err) {\n throw new Error(`network capabilities unavailable at ${request}: ${(err as Error).message}`)\n }\n if (!response.ok) {\n throw new Error(`network capabilities unavailable at ${request}: HTTP ${response.status}`)\n }\n const parsed = (await response.json()) as NetworkCapabilitiesDocument\n if (\n parsed.schema !== 'chain-insights.network-capabilities.v1' ||\n !Array.isArray(parsed.networks)\n ) {\n throw new Error('network capabilities response has unsupported schema')\n }\n return publicNetworkCapabilities(parsed)\n}\n\nfunction availableTools(network: NetworkCapability): string[] {\n const tools = Object.entries(network.tools ?? {})\n .filter(([, status]) => status === 'available')\n .map(([name]) => name)\n return tools.sort()\n}\n\nfunction availableToolLines(network: NetworkCapability): string[] {\n const tools = availableTools(network)\n if (tools.length === 0) return ['none']\n const lines: string[] = []\n for (let index = 0; index < tools.length; index += AVAILABLE_TOOLS_PER_LINE) {\n lines.push(tools.slice(index, index + AVAILABLE_TOOLS_PER_LINE).join(', '))\n }\n return lines\n}\n\nfunction shortDate(value?: string): string {\n if (!value) return ''\n return value.slice(0, 10)\n}\n\nfunction datasetLabel(network: NetworkCapability): string {\n const coverage = network.coverage\n if (!coverage) return 'unknown'\n const blockRange =\n coverage.from_block !== undefined && coverage.to_block !== undefined\n ? `${coverage.from_block}..${coverage.to_block}`\n : 'blocks unknown'\n const dateRange =\n coverage.from_timestamp && coverage.to_timestamp\n ? `${shortDate(coverage.from_timestamp)}..${shortDate(coverage.to_timestamp)}`\n : ''\n if (blockRange === 'blocks unknown' && dateRange === '') return 'unknown'\n if (blockRange === 'blocks unknown') return dateRange\n if (dateRange === '') return blockRange\n return `${blockRange} / ${dateRange}`\n}\n\nfunction displayName(network: NetworkCapability): string {\n return network.display_name || network.network\n}\n\nfunction statusLabel(network: NetworkCapability): string {\n return network.default ? `${network.status} (default)` : network.status\n}\n\nfunction formatTable(headers: string[], rows: string[][], minimumWidths: number[]): string {\n const widths = headers.map((header, index) =>\n Math.max(\n minimumWidths[index] ?? 0,\n header.length,\n ...rows.map((row) => row[index]?.length ?? 0)\n )\n )\n const row = (values: string[]) =>\n values.map((value, index) => value.padEnd(widths[index]!)).join(' ')\n return [row(headers), widths.map((width) => '-'.repeat(width)).join(' '), ...rows.map(row)].join(\n '\\n'\n )\n}\n\nexport function formatNetworkOverview(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const rows = document.networks.map((network) => [\n displayName(network),\n statusLabel(network),\n datasetLabel(network),\n ])\n return formatTable(['Network', 'Status', 'Dataset'], rows, [14, 10, 38])\n}\n\nexport function formatNetworkCapabilities(document: NetworkCapabilitiesDocument): string {\n if (document.networks.length === 0) return 'No supported networks advertised.'\n const networkRows = document.networks.flatMap((network) => {\n const toolLines = availableToolLines(network)\n return toolLines.map((toolLine, index) => [\n index === 0 ? displayName(network) : '',\n index === 0 ? datasetLabel(network) : '',\n toolLine,\n ])\n })\n return formatTable(['Network', 'Dataset', 'Chain Insights tools'], networkRows, [14, 38, 64])\n}\n\nexport function findNetworkCapability(\n document: NetworkCapabilitiesDocument,\n name: string\n): NetworkCapability | undefined {\n const normalizedName = name.trim().toLowerCase()\n if (!normalizedName) return undefined\n return document.networks.find((network) =>\n [network.network, network.display_name].some(\n (candidate) => candidate?.trim().toLowerCase() === normalizedName\n )\n )\n}\n\n/** The writer lanes a customer sees, in screen order, by their plain names. */\nconst KNOWN_LANES: readonly (readonly [string, string])[] = [\n ['core_transfers_source_head', 'receipts'],\n ['core_transfers_internal_follow', 'traces'],\n ['core_dex_follow', 'dex'],\n ['core_bridge_follow', 'bridge'],\n ['core_smart_account_follow', 'smart accounts'],\n]\n\n/** The evidence worker's lane. It has its own table and never joins Indexing. */\nconst EVIDENCE_LANE = 'evidence_sync'\n\n/** The plain name a lane prints as. An unknown lane prints as sent. */\nexport function laneDisplayName(lane: string): string {\n if (lane === EVIDENCE_LANE) return 'evidence'\n return KNOWN_LANES.find(([name]) => name === lane)?.[1] ?? lane\n}\n\n/** A graph layer prints as sent: it is the link kind a query names. */\nexport function layerDisplayName(layer: string): string {\n return layer\n}\n\n/** A family prints by its plain name: `smart_accounts` prints as \"smart accounts\". */\nexport function familyDisplayName(family: string): string {\n return family.replace(/_/g, ' ')\n}\n\n/** 87642448 -> \"87,642,448\", the same on every machine (no locale). */\nexport function groupThousands(value: number): string {\n const sign = value < 0 ? '-' : ''\n const digits = String(Math.trunc(Math.abs(value)))\n return sign + digits.replace(/\\B(?=(\\d{3})+(?!\\d))/g, ',')\n}\n\n/** One row of a progress table. `behind` is absent with no head or no height. */\ninterface ProgressRow {\n label: string\n height: number | null\n /** Text printed in the height column instead of a number. */\n note?: string\n behind?: number\n}\n\nconst DETAIL_LABEL_WIDTH = 18\nconst TABLE_LABEL_WIDTH = 30\nconst HEIGHT_MIN_WIDTH = 10\nconst LAG_HEADER = 'behind head'\n\nfunction progressRows(\n entries: { label: string; height: number | null }[],\n head: number | undefined\n): ProgressRow[] {\n return entries.map(({ label, height }) => ({\n label,\n height,\n ...(height === null ? { note: 'not started' } : {}),\n ...(height !== null && head !== undefined ? { behind: Math.max(0, head - height) } : {}),\n }))\n}\n\nfunction notReportedRow(label: string): ProgressRow {\n return { label, height: null, note: 'not reported' }\n}\n\n/**\n * The family rows, in the order the server sends them. A block family's lag is\n * the server's `blocks_behind_head`, or head minus height when the server knows\n * no head. A family with no block (annotations) prints \"fresh\" or \"walking\" and\n * no lag.\n */\nfunction familyRows(families: NetworkGraphFamily[], head: number | undefined): ProgressRow[] {\n return families.map((family) => {\n const label = familyDisplayName(family.name)\n const height = family.complete_through_block\n if (height === null) {\n const note = family.fresh === undefined ? 'not started' : family.fresh ? 'fresh' : 'walking'\n return { label, height: null, note }\n }\n if (head === undefined) return { label, height }\n const behind = family.blocks_behind_head ?? Math.max(0, head - height)\n return { label, height, behind }\n })\n}\n\nfunction formatProgressTables(\n tables: { title: string; rows: ProgressRow[] }[],\n withLag: boolean\n): string[] {\n const allRows = tables.flatMap((table) => table.rows)\n const labelWidth = Math.max(\n TABLE_LABEL_WIDTH,\n ...allRows.map((row) => row.label.length + 4),\n ...tables.map((table) => table.title.length + 2)\n )\n const heightWidth = Math.max(\n HEIGHT_MIN_WIDTH,\n ...allRows.map((row) => (row.height === null ? 0 : groupThousands(row.height).length))\n )\n const lagWidth = Math.max(\n LAG_HEADER.length,\n ...allRows.map((row) => (row.behind === undefined ? 0 : groupThousands(row.behind).length))\n )\n const lines: string[] = []\n for (const table of tables) {\n if (lines.length > 0) lines.push('')\n const header = table.title.padEnd(labelWidth) + 'height'\n lines.push(withLag ? header.padEnd(labelWidth + heightWidth + 2) + LAG_HEADER : header)\n for (const row of table.rows) {\n let line = ` ${row.label}`.padEnd(labelWidth)\n if (row.height === null) {\n line += row.note ?? ''\n } else {\n line += groupThousands(row.height).padStart(heightWidth)\n if (withLag && row.behind !== undefined) {\n line += ' ' + groupThousands(row.behind).padStart(lagWidth)\n }\n }\n lines.push(line.trimEnd())\n }\n }\n return lines\n}\n\n/**\n * `cia network <name>`: how far this network is indexed. The chain node's head\n * comes first; every row reads against it. Known lanes print by plain name,\n * unknown lanes as sent after them, the layer families by plain name in the\n * server's order, graph layers as sent, and evidence in its own table. Heights\n * are right-aligned, so a shorter height lines up by its last digit.\n * `--json` never comes through here.\n */\nexport function formatNetworkCapability(network: NetworkCapability): string {\n const headBlock = network.freshness?.chain_head_block\n const head = typeof headBlock === 'number' ? headBlock : undefined\n const detail: [string, string][] = [\n ['Network', displayName(network)],\n ['Identifier', network.network],\n ['Status', statusLabel(network)],\n ]\n if (head !== undefined) {\n const age = network.freshness?.chain_head_age_seconds\n const ageText = typeof age === 'number' ? ` (${groupThousands(age)} s old)` : ''\n detail.push(['Blockchain head', `${groupThousands(head)}${ageText}`])\n }\n\n const lanes = network.lane_progress ?? []\n const known = KNOWN_LANES.flatMap(([name, label]) =>\n lanes.filter((lane) => lane.lane === name).map((lane) => ({ label, height: lane.height }))\n )\n const unknown = lanes\n .filter(\n (lane) => lane.lane !== EVIDENCE_LANE && !KNOWN_LANES.some(([name]) => name === lane.lane)\n )\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n const indexing = [...known, ...unknown]\n const layers = (network.graph_progress?.layers ?? []).map((layer) => ({\n label: layerDisplayName(layer.layer),\n height: layer.position,\n }))\n const evidence = lanes\n .filter((lane) => lane.lane === EVIDENCE_LANE)\n .map((lane) => ({ label: laneDisplayName(lane.lane), height: lane.height }))\n\n const families = network.graph_progress?.families\n\n const tables = [\n {\n title: 'Indexing',\n rows: indexing.length > 0 ? progressRows(indexing, head) : [notReportedRow('indexing')],\n },\n // Absent on a server that sends no families: the table is left out, so the\n // screen of such a server stays as it was.\n ...(families ? [{ title: 'Families', rows: familyRows(families, head) }] : []),\n {\n title: 'Graph',\n rows: layers.length > 0 ? progressRows(layers, head) : [notReportedRow('graph')],\n },\n {\n title: 'Evidence',\n rows: evidence.length > 0 ? progressRows(evidence, head) : [notReportedRow('evidence')],\n },\n ]\n\n return [\n ...detail.map(([label, value]) => `${label.padEnd(DETAIL_LABEL_WIDTH)}${value}`),\n '',\n ...formatProgressTables(tables, head !== undefined),\n ].join('\\n')\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
+
//#region src/mcp/claude-desktop.ts
|
|
5
|
+
/**
|
|
6
|
+
* Claude Desktop chat runs its conversations on the Claude Code engine. That
|
|
7
|
+
* engine starts every server in the user's Claude Code settings as well as the
|
|
8
|
+
* servers in Claude Desktop's own settings file. Claude Desktop draws our views
|
|
9
|
+
* only for a server from its own file (`chain-insights`, written by
|
|
10
|
+
* `cia setup claude-desktop`); the Claude Code copy answers in text. With both
|
|
11
|
+
* present the model sees every tool twice and may pick the copy that cannot
|
|
12
|
+
* draw. The Claude Code copy therefore steps aside when it runs inside Claude
|
|
13
|
+
* Desktop and the drawing copy is configured.
|
|
14
|
+
*/
|
|
15
|
+
/** The value Claude Code sets in CLAUDE_CODE_ENTRYPOINT when Claude Desktop runs it. */
|
|
16
|
+
const CLAUDE_DESKTOP_ENTRYPOINT = "claude-desktop";
|
|
17
|
+
/** The server name `cia setup claude-desktop` writes. */
|
|
18
|
+
const CLAUDE_DESKTOP_SERVER_NAME = "chain-insights";
|
|
4
19
|
const DEFERRED_SERVER_INSTRUCTIONS = [
|
|
5
20
|
"Chain Insights is served in Claude Desktop by its `chain-insights` connector,",
|
|
6
21
|
"which draws the money-flow graph and the time series. Use the tools of that",
|
|
@@ -21,7 +36,7 @@ function claudeDesktopConfigPath(env = process.env, platform = process.platform,
|
|
|
21
36
|
function claudeDesktopServesChainInsights(configFile) {
|
|
22
37
|
try {
|
|
23
38
|
const servers = JSON.parse(readFileSync(configFile, "utf8"))?.mcpServers;
|
|
24
|
-
return servers !== null && typeof servers === "object" && !Array.isArray(servers) &&
|
|
39
|
+
return servers !== null && typeof servers === "object" && !Array.isArray(servers) && CLAUDE_DESKTOP_SERVER_NAME in servers;
|
|
25
40
|
} catch {
|
|
26
41
|
return false;
|
|
27
42
|
}
|
|
@@ -32,10 +47,10 @@ function claudeDesktopServesChainInsights(configFile) {
|
|
|
32
47
|
* CLAUDE_CODE_ENTRYPOINT, so that copy never steps aside.
|
|
33
48
|
*/
|
|
34
49
|
function defersToClaudeDesktop(env = process.env, configFile = claudeDesktopConfigPath(env)) {
|
|
35
|
-
if (env["CLAUDE_CODE_ENTRYPOINT"] !==
|
|
50
|
+
if (env["CLAUDE_CODE_ENTRYPOINT"] !== CLAUDE_DESKTOP_ENTRYPOINT) return false;
|
|
36
51
|
return claudeDesktopServesChainInsights(configFile);
|
|
37
52
|
}
|
|
38
53
|
//#endregion
|
|
39
54
|
export { DEFERRED_SERVER_INSTRUCTIONS, defersToClaudeDesktop };
|
|
40
55
|
|
|
41
|
-
//# sourceMappingURL=claude-desktop-
|
|
56
|
+
//# sourceMappingURL=claude-desktop-6jwX0Lux.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claude-desktop-
|
|
1
|
+
{"version":3,"file":"claude-desktop-6jwX0Lux.mjs","names":[],"sources":["../src/mcp/claude-desktop.ts"],"sourcesContent":["import { readFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport path from 'node:path'\n\n/**\n * Claude Desktop chat runs its conversations on the Claude Code engine. That\n * engine starts every server in the user's Claude Code settings as well as the\n * servers in Claude Desktop's own settings file. Claude Desktop draws our views\n * only for a server from its own file (`chain-insights`, written by\n * `cia setup claude-desktop`); the Claude Code copy answers in text. With both\n * present the model sees every tool twice and may pick the copy that cannot\n * draw. The Claude Code copy therefore steps aside when it runs inside Claude\n * Desktop and the drawing copy is configured.\n */\n\n/** The value Claude Code sets in CLAUDE_CODE_ENTRYPOINT when Claude Desktop runs it. */\nexport const CLAUDE_DESKTOP_ENTRYPOINT = 'claude-desktop'\n\n/** The server name `cia setup claude-desktop` writes. */\nexport const CLAUDE_DESKTOP_SERVER_NAME = 'chain-insights'\n\nexport const DEFERRED_SERVER_INSTRUCTIONS = [\n 'Chain Insights is served in Claude Desktop by its `chain-insights` connector,',\n 'which draws the money-flow graph and the time series. Use the tools of that',\n 'connector. This copy, started from the Claude Code settings, lists no tools',\n 'here so that each tool appears once.',\n].join(' ')\n\n/** Claude Desktop's settings file, as bin/install.cjs writes it. */\nexport function claudeDesktopConfigPath(\n env: NodeJS.ProcessEnv = process.env,\n platform: NodeJS.Platform = process.platform,\n home: string = homedir()\n): string {\n if (platform === 'darwin') {\n return path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')\n }\n if (platform === 'win32') {\n const appData = env['APPDATA'] || path.join(home, 'AppData', 'Roaming')\n return path.join(appData, 'Claude', 'claude_desktop_config.json')\n }\n const configHome = env['XDG_CONFIG_HOME'] || path.join(home, '.config')\n return path.join(configHome, 'Claude', 'claude_desktop_config.json')\n}\n\n/** True when Claude Desktop's own settings start a `chain-insights` server. */\nexport function claudeDesktopServesChainInsights(configFile: string): boolean {\n try {\n const config = JSON.parse(readFileSync(configFile, 'utf8')) as {\n mcpServers?: Record<string, unknown>\n }\n const servers = config?.mcpServers\n return (\n servers !== null &&\n typeof servers === 'object' &&\n !Array.isArray(servers) &&\n CLAUDE_DESKTOP_SERVER_NAME in servers\n )\n } catch {\n return false\n }\n}\n\n/**\n * True when this process is the Claude Code copy inside Claude Desktop and the\n * drawing copy is configured. Claude Desktop starts its own copy without\n * CLAUDE_CODE_ENTRYPOINT, so that copy never steps aside.\n */\nexport function defersToClaudeDesktop(\n env: NodeJS.ProcessEnv = process.env,\n configFile: string = claudeDesktopConfigPath(env)\n): boolean {\n if (env['CLAUDE_CODE_ENTRYPOINT'] !== CLAUDE_DESKTOP_ENTRYPOINT) return false\n return claudeDesktopServesChainInsights(configFile)\n}\n"],"mappings":";;;;;;;;;;;;;;;AAgBA,MAAa,4BAA4B;;AAGzC,MAAa,6BAA6B;AAE1C,MAAa,+BAA+B;CAC1C;CACA;CACA;CACA;AACF,CAAC,CAAC,KAAK,GAAG;;AAGV,SAAgB,wBACd,MAAyB,QAAQ,KACjC,WAA4B,QAAQ,UACpC,OAAe,QAAQ,GACf;CACR,IAAI,aAAa,UACf,OAAO,KAAK,KAAK,MAAM,WAAW,uBAAuB,UAAU,4BAA4B;CAEjG,IAAI,aAAa,SAAS;EACxB,MAAM,UAAU,IAAI,cAAc,KAAK,KAAK,MAAM,WAAW,SAAS;EACtE,OAAO,KAAK,KAAK,SAAS,UAAU,4BAA4B;CAClE;CACA,MAAM,aAAa,IAAI,sBAAsB,KAAK,KAAK,MAAM,SAAS;CACtE,OAAO,KAAK,KAAK,YAAY,UAAU,4BAA4B;AACrE;;AAGA,SAAgB,iCAAiC,YAA6B;CAC5E,IAAI;EAIF,MAAM,UAHS,KAAK,MAAM,aAAa,YAAY,MAAM,CAGpC,CAAC,EAAE;EACxB,OACE,YAAY,QACZ,OAAO,YAAY,YACnB,CAAC,MAAM,QAAQ,OAAO,KACtB,8BAA8B;CAElC,QAAQ;EACN,OAAO;CACT;AACF;;;;;;AAOA,SAAgB,sBACd,MAAyB,QAAQ,KACjC,aAAqB,wBAAwB,GAAG,GACvC;CACT,IAAI,IAAI,8BAA8B,2BAA2B,OAAO;CACxE,OAAO,iCAAiC,UAAU;AACpD"}
|
|
@@ -3,6 +3,21 @@ let node_path = require("node:path");
|
|
|
3
3
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
let node_os = require("node:os");
|
|
6
|
+
//#region src/mcp/claude-desktop.ts
|
|
7
|
+
/**
|
|
8
|
+
* Claude Desktop chat runs its conversations on the Claude Code engine. That
|
|
9
|
+
* engine starts every server in the user's Claude Code settings as well as the
|
|
10
|
+
* servers in Claude Desktop's own settings file. Claude Desktop draws our views
|
|
11
|
+
* only for a server from its own file (`chain-insights`, written by
|
|
12
|
+
* `cia setup claude-desktop`); the Claude Code copy answers in text. With both
|
|
13
|
+
* present the model sees every tool twice and may pick the copy that cannot
|
|
14
|
+
* draw. The Claude Code copy therefore steps aside when it runs inside Claude
|
|
15
|
+
* Desktop and the drawing copy is configured.
|
|
16
|
+
*/
|
|
17
|
+
/** The value Claude Code sets in CLAUDE_CODE_ENTRYPOINT when Claude Desktop runs it. */
|
|
18
|
+
const CLAUDE_DESKTOP_ENTRYPOINT = "claude-desktop";
|
|
19
|
+
/** The server name `cia setup claude-desktop` writes. */
|
|
20
|
+
const CLAUDE_DESKTOP_SERVER_NAME = "chain-insights";
|
|
6
21
|
const DEFERRED_SERVER_INSTRUCTIONS = [
|
|
7
22
|
"Chain Insights is served in Claude Desktop by its `chain-insights` connector,",
|
|
8
23
|
"which draws the money-flow graph and the time series. Use the tools of that",
|
|
@@ -23,7 +38,7 @@ function claudeDesktopConfigPath(env = process.env, platform = process.platform,
|
|
|
23
38
|
function claudeDesktopServesChainInsights(configFile) {
|
|
24
39
|
try {
|
|
25
40
|
const servers = JSON.parse((0, node_fs.readFileSync)(configFile, "utf8"))?.mcpServers;
|
|
26
|
-
return servers !== null && typeof servers === "object" && !Array.isArray(servers) &&
|
|
41
|
+
return servers !== null && typeof servers === "object" && !Array.isArray(servers) && CLAUDE_DESKTOP_SERVER_NAME in servers;
|
|
27
42
|
} catch {
|
|
28
43
|
return false;
|
|
29
44
|
}
|
|
@@ -34,7 +49,7 @@ function claudeDesktopServesChainInsights(configFile) {
|
|
|
34
49
|
* CLAUDE_CODE_ENTRYPOINT, so that copy never steps aside.
|
|
35
50
|
*/
|
|
36
51
|
function defersToClaudeDesktop(env = process.env, configFile = claudeDesktopConfigPath(env)) {
|
|
37
|
-
if (env["CLAUDE_CODE_ENTRYPOINT"] !==
|
|
52
|
+
if (env["CLAUDE_CODE_ENTRYPOINT"] !== CLAUDE_DESKTOP_ENTRYPOINT) return false;
|
|
38
53
|
return claudeDesktopServesChainInsights(configFile);
|
|
39
54
|
}
|
|
40
55
|
//#endregion
|