@cryptoquant_official/mcp 0.0.1
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 +88 -0
- package/dist/auth/server.d.ts +3 -0
- package/dist/auth/server.d.ts.map +1 -0
- package/dist/auth/server.js +405 -0
- package/dist/auth/server.js.map +1 -0
- package/dist/auth/storage.d.ts +11 -0
- package/dist/auth/storage.d.ts.map +1 -0
- package/dist/auth/storage.js +53 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/cache/storage.d.ts +47 -0
- package/dist/cache/storage.d.ts.map +1 -0
- package/dist/cache/storage.js +140 -0
- package/dist/cache/storage.js.map +1 -0
- package/dist/cache/summary.d.ts +16 -0
- package/dist/cache/summary.d.ts.map +1 -0
- package/dist/cache/summary.js +85 -0
- package/dist/cache/summary.js.map +1 -0
- package/dist/cache/types.d.ts +76 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +6 -0
- package/dist/cache/types.js.map +1 -0
- package/dist/config.d.ts +18 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/data/metrics.toon +58 -0
- package/dist/discovery.d.ts +70 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +159 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/permissions.d.ts +36 -0
- package/dist/permissions.d.ts.map +1 -0
- package/dist/permissions.js +135 -0
- package/dist/permissions.js.map +1 -0
- package/dist/plan-limits.d.ts +71 -0
- package/dist/plan-limits.d.ts.map +1 -0
- package/dist/plan-limits.js +400 -0
- package/dist/plan-limits.js.map +1 -0
- package/dist/tools/auth.d.ts +3 -0
- package/dist/tools/auth.d.ts.map +1 -0
- package/dist/tools/auth.js +157 -0
- package/dist/tools/auth.js.map +1 -0
- package/dist/tools/core.d.ts +3 -0
- package/dist/tools/core.d.ts.map +1 -0
- package/dist/tools/core.js +472 -0
- package/dist/tools/core.js.map +1 -0
- package/dist/utils.d.ts +32 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +42 -0
- package/dist/utils.js.map +1 -0
- package/package.json +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @cryptoquant_official/mcp
|
|
2
|
+
|
|
3
|
+
CryptoQuant MCP Server for on-chain analytics in Claude and AI coding agents.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
### Claude Code / Claude Desktop
|
|
8
|
+
|
|
9
|
+
Add to your MCP configuration:
|
|
10
|
+
|
|
11
|
+
**Claude Code** (`~/.claude/mcp.json` or project `.mcp.json`):
|
|
12
|
+
```json
|
|
13
|
+
{
|
|
14
|
+
"mcpServers": {
|
|
15
|
+
"cryptoquant": {
|
|
16
|
+
"command": "npx",
|
|
17
|
+
"args": ["-y", "@cryptoquant_official/mcp"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"cryptoquant": {
|
|
28
|
+
"command": "npx",
|
|
29
|
+
"args": ["-y", "@cryptoquant_official/mcp"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then restart your app.
|
|
36
|
+
|
|
37
|
+
### Other MCP-Compatible Apps (Cursor, etc.)
|
|
38
|
+
|
|
39
|
+
Add the same configuration to your app's MCP settings file.
|
|
40
|
+
|
|
41
|
+
## Authentication
|
|
42
|
+
|
|
43
|
+
On first use, call `initialize()` - a browser window opens automatically for API key setup.
|
|
44
|
+
|
|
45
|
+
Your API key is saved securely to `~/.cryptoquant/credentials`.
|
|
46
|
+
|
|
47
|
+
**Get your API key**: [cryptoquant.com](https://cryptoquant.com) → Dashboard → API
|
|
48
|
+
|
|
49
|
+
## Available Tools
|
|
50
|
+
|
|
51
|
+
| Tool | Description |
|
|
52
|
+
|------|-------------|
|
|
53
|
+
| `initialize` | Start session, auto-opens browser for API key |
|
|
54
|
+
| `discover_endpoints` | Browse 245+ available API endpoints |
|
|
55
|
+
| `get_endpoint_info` | Get endpoint parameter details |
|
|
56
|
+
| `query_data` | Query raw on-chain data |
|
|
57
|
+
| `describe_metric` | Get metric descriptions and thresholds |
|
|
58
|
+
| `list_assets` | List supported assets (BTC, ETH, etc.) |
|
|
59
|
+
| `reset_session` | Clear session / switch accounts |
|
|
60
|
+
|
|
61
|
+
## Supported Assets
|
|
62
|
+
|
|
63
|
+
BTC, ETH, ALT, Stablecoin, ERC20, TRX, XRP
|
|
64
|
+
|
|
65
|
+
## Example Usage
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
User: "Is Bitcoin overvalued?"
|
|
69
|
+
|
|
70
|
+
Claude: Let me check the MVRV ratio...
|
|
71
|
+
→ Calls query_data({ endpoint: "/v1/btc/market-data/mvrv" })
|
|
72
|
+
→ Returns analysis with current valuation status
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Requirements
|
|
76
|
+
|
|
77
|
+
- Node.js 18+
|
|
78
|
+
- CryptoQuant API key
|
|
79
|
+
|
|
80
|
+
## Links
|
|
81
|
+
|
|
82
|
+
- [Full Documentation](https://github.com/CryptoQuantOfficial/cryptoquant-skills)
|
|
83
|
+
- [CryptoQuant](https://cryptoquant.com)
|
|
84
|
+
- [API Docs](https://docs.cryptoquant.com)
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/auth/server.ts"],"names":[],"mappings":"AAoTA,wBAAgB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAgHjD;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC"}
|
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
import { exec } from "child_process";
|
|
3
|
+
import { platform } from "os";
|
|
4
|
+
import { saveApiKey } from "./storage.js";
|
|
5
|
+
import { getApiBaseUrl } from "../config.js";
|
|
6
|
+
const AUTH_PORT = 9876;
|
|
7
|
+
const AUTH_TIMEOUT_MS = 5 * 60 * 1000;
|
|
8
|
+
const AUTH_PAGE_HTML = `
|
|
9
|
+
<!DOCTYPE html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="UTF-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
14
|
+
<title>CryptoQuant - API Key Setup</title>
|
|
15
|
+
<style>
|
|
16
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
17
|
+
body {
|
|
18
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
19
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
20
|
+
min-height: 100vh;
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
padding: 20px;
|
|
25
|
+
}
|
|
26
|
+
.container {
|
|
27
|
+
background: #fff;
|
|
28
|
+
padding: 40px;
|
|
29
|
+
border-radius: 16px;
|
|
30
|
+
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
|
31
|
+
max-width: 460px;
|
|
32
|
+
width: 100%;
|
|
33
|
+
}
|
|
34
|
+
.logo {
|
|
35
|
+
font-size: 40px;
|
|
36
|
+
margin-bottom: 8px;
|
|
37
|
+
}
|
|
38
|
+
h1 {
|
|
39
|
+
color: #1a1a2e;
|
|
40
|
+
font-size: 24px;
|
|
41
|
+
margin-bottom: 8px;
|
|
42
|
+
}
|
|
43
|
+
.subtitle {
|
|
44
|
+
color: #666;
|
|
45
|
+
margin-bottom: 24px;
|
|
46
|
+
font-size: 14px;
|
|
47
|
+
}
|
|
48
|
+
.link-box {
|
|
49
|
+
background: #f5f7fa;
|
|
50
|
+
padding: 12px 16px;
|
|
51
|
+
border-radius: 8px;
|
|
52
|
+
margin-bottom: 24px;
|
|
53
|
+
}
|
|
54
|
+
.link-box p {
|
|
55
|
+
color: #666;
|
|
56
|
+
font-size: 13px;
|
|
57
|
+
margin-bottom: 6px;
|
|
58
|
+
}
|
|
59
|
+
.link-box a {
|
|
60
|
+
color: #0066cc;
|
|
61
|
+
text-decoration: none;
|
|
62
|
+
font-weight: 500;
|
|
63
|
+
word-break: break-all;
|
|
64
|
+
}
|
|
65
|
+
.link-box a:hover { text-decoration: underline; }
|
|
66
|
+
.input-group {
|
|
67
|
+
margin-bottom: 20px;
|
|
68
|
+
}
|
|
69
|
+
label {
|
|
70
|
+
display: block;
|
|
71
|
+
color: #333;
|
|
72
|
+
font-size: 14px;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
margin-bottom: 8px;
|
|
75
|
+
}
|
|
76
|
+
input[type="password"] {
|
|
77
|
+
width: 100%;
|
|
78
|
+
padding: 14px 16px;
|
|
79
|
+
border: 2px solid #e1e5eb;
|
|
80
|
+
border-radius: 8px;
|
|
81
|
+
font-size: 15px;
|
|
82
|
+
font-family: monospace;
|
|
83
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
84
|
+
}
|
|
85
|
+
input[type="password"]:focus {
|
|
86
|
+
outline: none;
|
|
87
|
+
border-color: #0066cc;
|
|
88
|
+
box-shadow: 0 0 0 3px rgba(0,102,204,0.1);
|
|
89
|
+
}
|
|
90
|
+
button {
|
|
91
|
+
width: 100%;
|
|
92
|
+
padding: 14px;
|
|
93
|
+
background: linear-gradient(135deg, #0066cc 0%, #0052a3 100%);
|
|
94
|
+
color: white;
|
|
95
|
+
border: none;
|
|
96
|
+
border-radius: 8px;
|
|
97
|
+
font-size: 16px;
|
|
98
|
+
font-weight: 600;
|
|
99
|
+
cursor: pointer;
|
|
100
|
+
transition: transform 0.1s, box-shadow 0.2s;
|
|
101
|
+
}
|
|
102
|
+
button:hover {
|
|
103
|
+
transform: translateY(-1px);
|
|
104
|
+
box-shadow: 0 4px 12px rgba(0,102,204,0.4);
|
|
105
|
+
}
|
|
106
|
+
button:active { transform: translateY(0); }
|
|
107
|
+
button:disabled {
|
|
108
|
+
background: #ccc;
|
|
109
|
+
cursor: not-allowed;
|
|
110
|
+
transform: none;
|
|
111
|
+
box-shadow: none;
|
|
112
|
+
}
|
|
113
|
+
.error {
|
|
114
|
+
background: #fff5f5;
|
|
115
|
+
color: #c53030;
|
|
116
|
+
padding: 12px 16px;
|
|
117
|
+
border-radius: 8px;
|
|
118
|
+
margin-top: 16px;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
display: none;
|
|
121
|
+
}
|
|
122
|
+
.error.show { display: block; }
|
|
123
|
+
.success-container {
|
|
124
|
+
text-align: center;
|
|
125
|
+
padding: 20px 0;
|
|
126
|
+
}
|
|
127
|
+
.success-icon {
|
|
128
|
+
font-size: 64px;
|
|
129
|
+
margin-bottom: 16px;
|
|
130
|
+
}
|
|
131
|
+
.success-container h2 {
|
|
132
|
+
color: #1a1a2e;
|
|
133
|
+
margin-bottom: 8px;
|
|
134
|
+
}
|
|
135
|
+
.success-container p {
|
|
136
|
+
color: #666;
|
|
137
|
+
font-size: 15px;
|
|
138
|
+
}
|
|
139
|
+
.spinner {
|
|
140
|
+
display: inline-block;
|
|
141
|
+
width: 16px;
|
|
142
|
+
height: 16px;
|
|
143
|
+
border: 2px solid #fff;
|
|
144
|
+
border-top-color: transparent;
|
|
145
|
+
border-radius: 50%;
|
|
146
|
+
animation: spin 0.8s linear infinite;
|
|
147
|
+
margin-right: 8px;
|
|
148
|
+
vertical-align: middle;
|
|
149
|
+
}
|
|
150
|
+
@keyframes spin { to { transform: rotate(360deg); } }
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
<div class="container" id="authForm">
|
|
155
|
+
<div class="logo">🔐</div>
|
|
156
|
+
<h1>CryptoQuant API Key</h1>
|
|
157
|
+
<p class="subtitle">Connect your CryptoQuant account to Claude</p>
|
|
158
|
+
|
|
159
|
+
<div class="link-box">
|
|
160
|
+
<p>Get your API key at:</p>
|
|
161
|
+
<a href="https://cryptoquant.com/settings/api" target="_blank" rel="noopener">
|
|
162
|
+
cryptoquant.com/settings/api
|
|
163
|
+
</a>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<form id="form">
|
|
167
|
+
<div class="input-group">
|
|
168
|
+
<label for="apiKey">API Key</label>
|
|
169
|
+
<input
|
|
170
|
+
type="password"
|
|
171
|
+
id="apiKey"
|
|
172
|
+
placeholder="cq_xxxxxxxxxxxxxxxxxx"
|
|
173
|
+
required
|
|
174
|
+
autocomplete="off"
|
|
175
|
+
spellcheck="false"
|
|
176
|
+
/>
|
|
177
|
+
</div>
|
|
178
|
+
<button type="submit" id="submitBtn">
|
|
179
|
+
<span id="btnText">Save & Connect</span>
|
|
180
|
+
</button>
|
|
181
|
+
</form>
|
|
182
|
+
<div id="error" class="error"></div>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
<div class="container success-container" id="successView" style="display:none;">
|
|
186
|
+
<div class="success-icon">✅</div>
|
|
187
|
+
<h2>Connected!</h2>
|
|
188
|
+
<p>Your API key has been saved securely.<br>You can close this tab and return to Claude.</p>
|
|
189
|
+
</div>
|
|
190
|
+
|
|
191
|
+
<script>
|
|
192
|
+
const form = document.getElementById('form');
|
|
193
|
+
const apiKeyInput = document.getElementById('apiKey');
|
|
194
|
+
const submitBtn = document.getElementById('submitBtn');
|
|
195
|
+
const btnText = document.getElementById('btnText');
|
|
196
|
+
const errorDiv = document.getElementById('error');
|
|
197
|
+
const authForm = document.getElementById('authForm');
|
|
198
|
+
const successView = document.getElementById('successView');
|
|
199
|
+
|
|
200
|
+
form.addEventListener('submit', async (e) => {
|
|
201
|
+
e.preventDefault();
|
|
202
|
+
|
|
203
|
+
const apiKey = apiKeyInput.value.trim();
|
|
204
|
+
if (!apiKey) return;
|
|
205
|
+
|
|
206
|
+
// Show loading state
|
|
207
|
+
submitBtn.disabled = true;
|
|
208
|
+
btnText.innerHTML = '<span class="spinner"></span>Validating...';
|
|
209
|
+
errorDiv.classList.remove('show');
|
|
210
|
+
|
|
211
|
+
try {
|
|
212
|
+
const res = await fetch('/auth/save', {
|
|
213
|
+
method: 'POST',
|
|
214
|
+
headers: { 'Content-Type': 'application/json' },
|
|
215
|
+
body: JSON.stringify({ api_key: apiKey })
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
const data = await res.json();
|
|
219
|
+
|
|
220
|
+
if (data.success) {
|
|
221
|
+
authForm.style.display = 'none';
|
|
222
|
+
successView.style.display = 'block';
|
|
223
|
+
} else {
|
|
224
|
+
errorDiv.textContent = data.error || 'Invalid API key. Please check and try again.';
|
|
225
|
+
errorDiv.classList.add('show');
|
|
226
|
+
submitBtn.disabled = false;
|
|
227
|
+
btnText.textContent = 'Save & Connect';
|
|
228
|
+
}
|
|
229
|
+
} catch (err) {
|
|
230
|
+
errorDiv.textContent = 'Connection error. Please try again.';
|
|
231
|
+
errorDiv.classList.add('show');
|
|
232
|
+
submitBtn.disabled = false;
|
|
233
|
+
btnText.textContent = 'Save & Connect';
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
// Focus input on load
|
|
238
|
+
apiKeyInput.focus();
|
|
239
|
+
</script>
|
|
240
|
+
</body>
|
|
241
|
+
</html>
|
|
242
|
+
`;
|
|
243
|
+
async function validateApiKey(apiKey) {
|
|
244
|
+
try {
|
|
245
|
+
const res = await fetch(`${getApiBaseUrl()}/v1/discovery/endpoints?source=mcp`, {
|
|
246
|
+
method: "GET",
|
|
247
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
248
|
+
});
|
|
249
|
+
return res.ok;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function getBrowserCommand(url) {
|
|
256
|
+
const plat = platform();
|
|
257
|
+
switch (plat) {
|
|
258
|
+
case "darwin":
|
|
259
|
+
return `open "${url}"`;
|
|
260
|
+
case "win32":
|
|
261
|
+
return `start "" "${url}"`;
|
|
262
|
+
default:
|
|
263
|
+
return `xdg-open "${url}"`;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
function openBrowser(url) {
|
|
267
|
+
return new Promise((resolve, reject) => {
|
|
268
|
+
const cmd = getBrowserCommand(url);
|
|
269
|
+
exec(cmd, (error) => {
|
|
270
|
+
if (error) {
|
|
271
|
+
reject(new Error(`Failed to open browser: ${error.message}\nPlease open manually: ${url}`));
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
resolve();
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
function sendJson(res, status, data) {
|
|
280
|
+
res.writeHead(status, { "Content-Type": "application/json" });
|
|
281
|
+
res.end(JSON.stringify(data));
|
|
282
|
+
}
|
|
283
|
+
const RATE_LIMIT_WINDOW_MS = 60 * 1000;
|
|
284
|
+
const RATE_LIMIT_MAX_ATTEMPTS = 5;
|
|
285
|
+
const rateLimitMap = new Map();
|
|
286
|
+
function checkRateLimit(ip) {
|
|
287
|
+
const now = Date.now();
|
|
288
|
+
const entry = rateLimitMap.get(ip);
|
|
289
|
+
if (!entry || now > entry.resetAt) {
|
|
290
|
+
rateLimitMap.set(ip, { count: 1, resetAt: now + RATE_LIMIT_WINDOW_MS });
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
if (entry.count >= RATE_LIMIT_MAX_ATTEMPTS) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
entry.count++;
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
export function startAuthServer() {
|
|
300
|
+
return new Promise((resolve, reject) => {
|
|
301
|
+
let server = null;
|
|
302
|
+
let timeoutId = null;
|
|
303
|
+
const cleanup = () => {
|
|
304
|
+
if (timeoutId) {
|
|
305
|
+
clearTimeout(timeoutId);
|
|
306
|
+
timeoutId = null;
|
|
307
|
+
}
|
|
308
|
+
if (server) {
|
|
309
|
+
server.close();
|
|
310
|
+
server = null;
|
|
311
|
+
}
|
|
312
|
+
rateLimitMap.clear();
|
|
313
|
+
};
|
|
314
|
+
server = createServer(async (req, res) => {
|
|
315
|
+
const origin = req.headers.origin || "";
|
|
316
|
+
const allowedOrigin = origin.startsWith("http://localhost:") || origin.startsWith("http://127.0.0.1:")
|
|
317
|
+
? origin
|
|
318
|
+
: `http://localhost:${AUTH_PORT}`;
|
|
319
|
+
res.setHeader("Access-Control-Allow-Origin", allowedOrigin);
|
|
320
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
|
|
321
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
322
|
+
if (req.method === "OPTIONS") {
|
|
323
|
+
res.writeHead(200);
|
|
324
|
+
res.end();
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
if (req.url === "/auth" && req.method === "GET") {
|
|
328
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
329
|
+
res.end(AUTH_PAGE_HTML);
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (req.url === "/auth/save" && req.method === "POST") {
|
|
333
|
+
const clientIp = req.socket.remoteAddress || "unknown";
|
|
334
|
+
if (!checkRateLimit(clientIp)) {
|
|
335
|
+
sendJson(res, 429, { success: false, error: "Too many attempts. Please wait and try again." });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
let body = "";
|
|
339
|
+
req.on("data", (chunk) => (body += chunk));
|
|
340
|
+
req.on("end", async () => {
|
|
341
|
+
try {
|
|
342
|
+
const { api_key } = JSON.parse(body);
|
|
343
|
+
if (!api_key || typeof api_key !== "string") {
|
|
344
|
+
sendJson(res, 400, { success: false, error: "API key required" });
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const isValid = await validateApiKey(api_key);
|
|
348
|
+
if (isValid) {
|
|
349
|
+
saveApiKey(api_key);
|
|
350
|
+
sendJson(res, 200, { success: true });
|
|
351
|
+
setTimeout(() => {
|
|
352
|
+
cleanup();
|
|
353
|
+
resolve(api_key);
|
|
354
|
+
}, 500);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
sendJson(res, 400, {
|
|
358
|
+
success: false,
|
|
359
|
+
error: "Invalid API key. Please check your key at cryptoquant.com/settings/api",
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
sendJson(res, 400, { success: false, error: "Invalid request" });
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (req.url === "/health" && req.method === "GET") {
|
|
370
|
+
sendJson(res, 200, { status: "ok" });
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
res.writeHead(404);
|
|
374
|
+
res.end("Not found");
|
|
375
|
+
});
|
|
376
|
+
server.on("error", (err) => {
|
|
377
|
+
cleanup();
|
|
378
|
+
if (err.code === "EADDRINUSE") {
|
|
379
|
+
reject(new Error(`Port ${AUTH_PORT} is already in use. Please close other applications using this port and try again.`));
|
|
380
|
+
}
|
|
381
|
+
else {
|
|
382
|
+
reject(err);
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
server.listen(AUTH_PORT, "127.0.0.1", async () => {
|
|
386
|
+
const url = `http://localhost:${AUTH_PORT}/auth`;
|
|
387
|
+
console.error(`Auth server started at ${url}`);
|
|
388
|
+
try {
|
|
389
|
+
await openBrowser(url);
|
|
390
|
+
}
|
|
391
|
+
catch (error) {
|
|
392
|
+
cleanup();
|
|
393
|
+
reject(error);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
timeoutId = setTimeout(() => {
|
|
397
|
+
cleanup();
|
|
398
|
+
reject(new Error("Authentication timeout. Please try again by calling initialize()."));
|
|
399
|
+
}, AUTH_TIMEOUT_MS);
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
export function getAuthUrl() {
|
|
403
|
+
return `http://localhost:${AUTH_PORT}/auth`;
|
|
404
|
+
}
|
|
405
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/auth/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface StoredCredentials {
|
|
2
|
+
api_key: string;
|
|
3
|
+
created_at: string;
|
|
4
|
+
validated_at: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function getStoredApiKey(): string | null;
|
|
7
|
+
export declare function saveApiKey(apiKey: string): void;
|
|
8
|
+
export declare function updateValidatedAt(): void;
|
|
9
|
+
export declare function clearCredentials(): void;
|
|
10
|
+
export declare function getCredentialsPath(): string;
|
|
11
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAQ/C;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAa/C;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CASxC;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAED,wBAAgB,kBAAkB,IAAI,MAAM,CAE3C"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { homedir } from "os";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, unlinkSync, chmodSync } from "fs";
|
|
4
|
+
const CREDENTIALS_DIR = join(homedir(), ".cryptoquant");
|
|
5
|
+
const CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials");
|
|
6
|
+
export function getStoredApiKey() {
|
|
7
|
+
try {
|
|
8
|
+
if (!existsSync(CREDENTIALS_FILE))
|
|
9
|
+
return null;
|
|
10
|
+
const data = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
11
|
+
return data.api_key || null;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function saveApiKey(apiKey) {
|
|
18
|
+
if (!existsSync(CREDENTIALS_DIR)) {
|
|
19
|
+
mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
|
|
20
|
+
}
|
|
21
|
+
const data = {
|
|
22
|
+
api_key: apiKey,
|
|
23
|
+
created_at: new Date().toISOString(),
|
|
24
|
+
validated_at: new Date().toISOString(),
|
|
25
|
+
};
|
|
26
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2), { encoding: "utf-8" });
|
|
27
|
+
chmodSync(CREDENTIALS_FILE, 0o600);
|
|
28
|
+
}
|
|
29
|
+
export function updateValidatedAt() {
|
|
30
|
+
try {
|
|
31
|
+
if (!existsSync(CREDENTIALS_FILE))
|
|
32
|
+
return;
|
|
33
|
+
const data = JSON.parse(readFileSync(CREDENTIALS_FILE, "utf-8"));
|
|
34
|
+
data.validated_at = new Date().toISOString();
|
|
35
|
+
writeFileSync(CREDENTIALS_FILE, JSON.stringify(data, null, 2), { encoding: "utf-8" });
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Validation timestamp update is non-critical
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function clearCredentials() {
|
|
42
|
+
try {
|
|
43
|
+
if (existsSync(CREDENTIALS_FILE))
|
|
44
|
+
unlinkSync(CREDENTIALS_FILE);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// Already deleted or inaccessible
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function getCredentialsPath() {
|
|
51
|
+
return CREDENTIALS_FILE;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/auth/storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAE/F,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,CAAC,CAAC;AACxD,MAAM,gBAAgB,GAAG,IAAI,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;AAQ9D,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAC,CAAC;QACjE,OAAO,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,SAAS,CAAC,eAAe,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,MAAM,IAAI,GAAsB;QAC9B,OAAO,EAAE,MAAM;QACf,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,YAAY,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACvC,CAAC;IAEF,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACtF,SAAS,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC;YAAE,OAAO;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,gBAAgB,EAAE,OAAO,CAAC,CAAsB,CAAC;QACtF,IAAI,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC7C,aAAa,CAAC,gBAAgB,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACxF,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,gBAAgB,CAAC;YAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache storage operations for discovery response caching
|
|
3
|
+
*/
|
|
4
|
+
import { type DiscoveryCacheSchema, type MyDiscoveryRawResponse, type DiscoverySummaryData } from "./types.js";
|
|
5
|
+
import type { UserPlan, PlanLimits, ApiRateLimit } from "../plan-limits.js";
|
|
6
|
+
/**
|
|
7
|
+
* Get cache file path.
|
|
8
|
+
*/
|
|
9
|
+
export declare function getCacheFilePath(_apiUrl: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Check if cache is still valid (not expired and version matches).
|
|
12
|
+
*/
|
|
13
|
+
export declare function isCacheValid(cache: DiscoveryCacheSchema, apiUrl: string, apiKeyPrefix: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Read cache from file.
|
|
16
|
+
* Returns null if cache doesn't exist or is invalid JSON.
|
|
17
|
+
*/
|
|
18
|
+
export declare function readCache(apiUrl: string): DiscoveryCacheSchema | null;
|
|
19
|
+
/**
|
|
20
|
+
* Write cache to file with secure permissions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function writeCache(apiUrl: string, apiKey: string, rawResponse: MyDiscoveryRawResponse, parsed: {
|
|
23
|
+
limits: PlanLimits | null;
|
|
24
|
+
statics: string[];
|
|
25
|
+
apiRateLimit: ApiRateLimit | null;
|
|
26
|
+
}, summary: DiscoverySummaryData, plan: UserPlan): void;
|
|
27
|
+
/**
|
|
28
|
+
* Delete cache file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function invalidateCache(apiUrl: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Delete the cache file.
|
|
33
|
+
*/
|
|
34
|
+
export declare function clearAllCaches(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Calculate cache age in days.
|
|
37
|
+
*/
|
|
38
|
+
export declare function getCacheAgeDays(cache: DiscoveryCacheSchema): number;
|
|
39
|
+
/**
|
|
40
|
+
* Get human-readable cache status string.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getCacheStatus(cache: DiscoveryCacheSchema | null, fromCache: boolean): string;
|
|
43
|
+
/**
|
|
44
|
+
* Get cache file path for user display.
|
|
45
|
+
*/
|
|
46
|
+
export declare function getCachePath(): string;
|
|
47
|
+
//# sourceMappingURL=storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/cache/storage.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EAG1B,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAK5E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,oBAAoB,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAuBvG;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAmBrE;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,sBAAsB,EACnC,MAAM,EAAE;IACN,MAAM,EAAE,UAAU,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,YAAY,GAAG,IAAI,CAAC;CACnC,EACD,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,QAAQ,GACb,IAAI,CA2BN;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAUpD;AAED;;GAEG;AACH,wBAAgB,cAAc,IAAI,IAAI,CAQrC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,oBAAoB,GAAG,MAAM,CAKnE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CAM7F;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,MAAM,CAErC"}
|