-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconfig.js
More file actions
146 lines (127 loc) · 6.82 KB
/
Copy pathconfig.js
File metadata and controls
146 lines (127 loc) · 6.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Environment configuration with validation
// All configurable constants are centralized here
function parseIntEnv(name, defaultValue) {
const raw = process.env[name];
if (raw === undefined || raw === '') return defaultValue;
const parsed = Number.parseInt(raw, 10);
if (Number.isNaN(parsed)) {
console.error(`Invalid value for ${name}: "${raw}" (expected integer). Using default: ${defaultValue}`);
process.exit(1);
}
return parsed;
}
function parseStringEnv(name, defaultValue) {
return process.env[name] || defaultValue;
}
function parseBooleanEnv(name, defaultValue) {
const raw = process.env[name];
if (raw === undefined || raw === '') return defaultValue;
const normalized = raw.trim().toLowerCase();
if (['true', '1', 'yes', 'on'].includes(normalized)) return true;
if (['false', '0', 'no', 'off'].includes(normalized)) return false;
console.error(`Invalid value for ${name}: "${raw}" (expected boolean). Using default: ${defaultValue}`);
process.exit(1);
}
// Server
export const PORT = parseIntEnv('PORT', 3000);
export const HOST = parseStringEnv('HOST', '0.0.0.0');
export const MCP_PORT = parseIntEnv('MCP_PORT', 3001);
export const MCP_HOST = parseStringEnv('MCP_HOST', '0.0.0.0');
// Request limits
export const BODY_LIMIT = parseIntEnv('BODY_LIMIT', 1048576); // 1 MB
export const MAX_PARAM_LENGTH = parseIntEnv('MAX_PARAM_LENGTH', 200);
// Rate limiting
export const RATE_LIMIT_MAX = parseIntEnv('RATE_LIMIT_MAX', 100);
export const RATE_LIMIT_WINDOW_MS = parseIntEnv('RATE_LIMIT_WINDOW_MS', 60000); // 1 minute
export const RELOAD_RATE_LIMIT_MAX = parseIntEnv('RELOAD_RATE_LIMIT_MAX', 5);
export const SEARCH_RATE_LIMIT_MAX = parseIntEnv('SEARCH_RATE_LIMIT_MAX', 30);
// RPC health check
export const RPC_CHECK_TIMEOUT_MS = parseIntEnv('RPC_CHECK_TIMEOUT_MS', 8000);
/**
* @deprecated Unused since the unified rolling refresher (services/chainRefresher.js).
* The new loop processes one chain per tick; each chain's RPC endpoints are
* checked in parallel inside that chain's job. There is no global concurrency
* cap. Kept for backwards-compatible env parsing; safe to remove in v2.
*/
export const RPC_CHECK_CONCURRENCY = parseIntEnv('RPC_CHECK_CONCURRENCY', 8);
export const MAX_ENDPOINTS_PER_CHAIN = parseIntEnv('MAX_ENDPOINTS_PER_CHAIN', 5);
// Search
export const MAX_SEARCH_QUERY_LENGTH = parseIntEnv('MAX_SEARCH_QUERY_LENGTH', 200);
// Data source URLs
export const DATA_SOURCE_THE_GRAPH = parseStringEnv(
'DATA_SOURCE_THE_GRAPH',
'https://raw.githubusercontent.com/Johnaverse/networks-registry/refs/heads/main/public/TheGraphNetworksRegistry.json'
);
export const DATA_SOURCE_CHAINLIST = parseStringEnv(
'DATA_SOURCE_CHAINLIST',
'https://chainlist.org/rpcs.json'
);
export const DATA_SOURCE_CHAINS = parseStringEnv(
'DATA_SOURCE_CHAINS',
'https://chainid.network/chains.json'
);
export const DATA_SOURCE_SLIP44 = parseStringEnv(
'DATA_SOURCE_SLIP44',
'https://raw.githubusercontent.com/satoshilabs/slips/master/slip-0044.md'
);
export const DATA_SOURCE_L2BEAT_API = parseStringEnv(
'DATA_SOURCE_L2BEAT_API',
'https://l2beat.com/api/scaling/summary'
);
export const L2BEAT_FETCH_TIMEOUT_MS = parseIntEnv('L2BEAT_FETCH_TIMEOUT_MS', 10000);
// How long L2BEAT data may go without a successful refresh before /health
// flags it stale. L2BEAT refreshes once per full rolling sweep
// (CHAIN_REFRESHER_TICK_MS × chain count ≈ tens of minutes for ~3k chains),
// so this bound must be generous — a tight value (e.g. the old 2× refresh
// interval) reads as permanently "degraded" even though the refresher is
// healthy. Default 6h catches a genuinely stuck refresher without false alarms.
export const L2BEAT_STALE_AFTER_MS = parseIntEnv('L2BEAT_STALE_AFTER_MS', 6 * 60 * 60 * 1000);
// Source fetch resilience. A source fetch is retried with exponential backoff
// before being treated as failed, so a transient blip at startup doesn't leave
// a registry permanently empty until a manual reload.
export const SOURCE_FETCH_MAX_RETRIES = parseIntEnv('SOURCE_FETCH_MAX_RETRIES', 3);
export const SOURCE_FETCH_RETRY_BASE_MS = parseIntEnv('SOURCE_FETCH_RETRY_BASE_MS', 500);
// Background self-heal: every interval, if any core/supplementary source is
// currently missing (e.g. failed at boot), re-fetch all sources. Set to 0 to
// disable. Default 15 min.
export const SOURCE_REFRESH_INTERVAL_MS = parseIntEnv('SOURCE_REFRESH_INTERVAL_MS', 900000);
/**
* @deprecated Cadence is now driven by the unified rolling refresher
* (CHAIN_REFRESHER_TICK_MS × queue length). Kept so /scaling/status can keep
* exposing the value as a hint to consumers, but no longer used for
* scheduling. Safe to remove in v2 once consumers migrate to /refresher.
*/
export const L2BEAT_REFRESH_INTERVAL_MS = parseIntEnv('L2BEAT_REFRESH_INTERVAL_MS', 300000);
// Disk cache
export const DATA_CACHE_ENABLED = parseBooleanEnv('DATA_CACHE_ENABLED', true);
export const DATA_CACHE_FILE = parseStringEnv('DATA_CACHE_FILE', '.cache/chains-api-data.json');
// CORS
export const CORS_ORIGIN = parseStringEnv('CORS_ORIGIN', '*');
// Proxy (optional)
export const PROXY_URL = parseStringEnv('PROXY_URL', '');
export const PROXY_ENABLED = PROXY_URL !== '';
// Price cache
export const PRICE_CACHE_TTL_MS = parseIntEnv('PRICE_CACHE_TTL_MS', 3600000);
export const PRICE_NEGATIVE_CACHE_TTL_MS = parseIntEnv('PRICE_NEGATIVE_CACHE_TTL_MS', 300000);
export const PRICE_FETCH_TIMEOUT_MS = parseIntEnv('PRICE_FETCH_TIMEOUT_MS', 3000);
// Live incidents feed (chains-status-news). Used by the get_live_incidents
// tool so the assistant/MCP can answer "is X down" questions server-side.
export const LIVE_INCIDENTS_URL = parseStringEnv(
'LIVE_INCIDENTS_URL',
'https://chains-status-news.johnaverse.cc'
);
export const LIVE_INCIDENTS_CACHE_TTL_MS = parseIntEnv('LIVE_INCIDENTS_CACHE_TTL_MS', 60000);
export const LIVE_INCIDENTS_FETCH_TIMEOUT_MS = parseIntEnv('LIVE_INCIDENTS_FETCH_TIMEOUT_MS', 10000);
// Assistant (optional LLM chat over the registry + live incidents).
// Disabled unless ASSISTANT_LLM_URL points at an OpenAI-compatible server
// (e.g. Ollama: http://localhost:11434).
export const ASSISTANT_LLM_URL = parseStringEnv('ASSISTANT_LLM_URL', '');
export const ASSISTANT_ENABLED = ASSISTANT_LLM_URL !== '';
export const ASSISTANT_MODEL = parseStringEnv('ASSISTANT_MODEL', 'qwen3');
export const ASSISTANT_MAX_TOOL_ITERATIONS = parseIntEnv('ASSISTANT_MAX_TOOL_ITERATIONS', 6);
export const ASSISTANT_TIMEOUT_MS = parseIntEnv('ASSISTANT_TIMEOUT_MS', 60000);
export const ASSISTANT_MAX_TOKENS = parseIntEnv('ASSISTANT_MAX_TOKENS', 1024);
export const ASSISTANT_RATE_LIMIT_MAX = parseIntEnv('ASSISTANT_RATE_LIMIT_MAX', 10);
export const ASSISTANT_MAX_MESSAGES = parseIntEnv('ASSISTANT_MAX_MESSAGES', 20);
export const ASSISTANT_MAX_MESSAGE_LENGTH = parseIntEnv('ASSISTANT_MAX_MESSAGE_LENGTH', 4000);
export const ASSISTANT_TOOL_RESULT_MAX_CHARS = parseIntEnv('ASSISTANT_TOOL_RESULT_MAX_CHARS', 8000);