@tokenomist-ai/tokenomist-cli 0.1.0
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/LICENSE +21 -0
- package/README.md +176 -0
- package/dist/index.js +1237 -0
- package/dist/index.js.map +1 -0
- package/dist/update-check.js +113 -0
- package/dist/update-check.js.map +1 -0
- package/package.json +76 -0
- package/scripts/postinstall.js +162 -0
- package/skills/tokenomist-allocation-breakdown/SKILL.md +76 -0
- package/skills/tokenomist-burn-buyback/SKILL.md +92 -0
- package/skills/tokenomist-emission-report/SKILL.md +70 -0
- package/skills/tokenomist-fundraising-lookup/SKILL.md +89 -0
- package/skills/tokenomist-token-overview/SKILL.md +80 -0
- package/skills/tokenomist-unlock-analysis/SKILL.md +87 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { Command as Command11 } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/commands/token.ts
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
|
|
9
|
+
// src/api/errors.ts
|
|
10
|
+
function classifyStatus(status) {
|
|
11
|
+
if (status === 401) return "UNAUTHORIZED" /* UNAUTHORIZED */;
|
|
12
|
+
if (status === 403) return "FORBIDDEN" /* FORBIDDEN */;
|
|
13
|
+
if (status === 404) return "NOT_FOUND" /* NOT_FOUND */;
|
|
14
|
+
if (status === 429) return "RATE_LIMITED" /* RATE_LIMITED */;
|
|
15
|
+
if (status >= 500 && status < 600) return "SERVER_ERROR" /* SERVER_ERROR */;
|
|
16
|
+
return "UNKNOWN" /* UNKNOWN */;
|
|
17
|
+
}
|
|
18
|
+
function parseRetryAfter(value) {
|
|
19
|
+
if (!value) return void 0;
|
|
20
|
+
const seconds = Number(value);
|
|
21
|
+
return Number.isFinite(seconds) && seconds > 0 ? seconds : void 0;
|
|
22
|
+
}
|
|
23
|
+
var ApiError = class _ApiError extends Error {
|
|
24
|
+
constructor(message, code, detail, statusCode, retryAfter) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = code;
|
|
27
|
+
this.detail = detail;
|
|
28
|
+
this.statusCode = statusCode;
|
|
29
|
+
this.retryAfter = retryAfter;
|
|
30
|
+
this.name = "ApiError";
|
|
31
|
+
}
|
|
32
|
+
retriesExhausted = false;
|
|
33
|
+
get retryable() {
|
|
34
|
+
return this.code === "RATE_LIMITED" /* RATE_LIMITED */ || this.code === "SERVER_ERROR" /* SERVER_ERROR */ || this.code === "NETWORK_ERROR" /* NETWORK_ERROR */;
|
|
35
|
+
}
|
|
36
|
+
static fromResponse(status, body, retryAfterHeader) {
|
|
37
|
+
const b = body && typeof body === "object" ? body : {};
|
|
38
|
+
const errorMessage = typeof b.errorMessage === "string" ? b.errorMessage : void 0;
|
|
39
|
+
const errorDetail = typeof b.errorDetail === "string" ? b.errorDetail : void 0;
|
|
40
|
+
if (status === 200 && b.status === false) {
|
|
41
|
+
const bodyStatus = typeof b.statusCode === "number" ? b.statusCode : 0;
|
|
42
|
+
const code2 = bodyStatus > 0 ? classifyStatus(bodyStatus) : "UNKNOWN" /* UNKNOWN */;
|
|
43
|
+
return new _ApiError(
|
|
44
|
+
errorMessage || "API request failed",
|
|
45
|
+
code2,
|
|
46
|
+
errorDetail,
|
|
47
|
+
bodyStatus || void 0
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const code = classifyStatus(status);
|
|
51
|
+
return new _ApiError(
|
|
52
|
+
errorMessage || `HTTP ${status}`,
|
|
53
|
+
code,
|
|
54
|
+
errorDetail,
|
|
55
|
+
status,
|
|
56
|
+
parseRetryAfter(retryAfterHeader)
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
static fromNetworkError(error) {
|
|
60
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
+
return new _ApiError(message, "NETWORK_ERROR" /* NETWORK_ERROR */);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// src/api/retry.ts
|
|
66
|
+
var DEFAULTS = {
|
|
67
|
+
maxAttempts: 3,
|
|
68
|
+
baseDelay: 1e3,
|
|
69
|
+
maxDelay: 3e4
|
|
70
|
+
};
|
|
71
|
+
function sleep(ms) {
|
|
72
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
73
|
+
}
|
|
74
|
+
async function withRetry(fn, options) {
|
|
75
|
+
const maxAttempts = options?.maxAttempts ?? DEFAULTS.maxAttempts;
|
|
76
|
+
const baseDelay = options?.baseDelay ?? DEFAULTS.baseDelay;
|
|
77
|
+
const maxDelay = options?.maxDelay ?? DEFAULTS.maxDelay;
|
|
78
|
+
let lastError;
|
|
79
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
80
|
+
try {
|
|
81
|
+
return await fn();
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (!(err instanceof ApiError) || !err.retryable) {
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
lastError = err;
|
|
87
|
+
if (attempt === maxAttempts) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
const jitter = Math.random() * 500;
|
|
91
|
+
const calculated = baseDelay * Math.pow(2, attempt - 1) + jitter;
|
|
92
|
+
const delay = err.retryAfter ? Math.min(err.retryAfter * 1e3, maxDelay) : Math.min(calculated, maxDelay);
|
|
93
|
+
process.stderr.write(
|
|
94
|
+
`${err.code}: ${err.message}. Retrying in ${Math.round(delay / 1e3)}s... (attempt ${attempt + 1}/${maxAttempts})
|
|
95
|
+
`
|
|
96
|
+
);
|
|
97
|
+
await sleep(delay);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
lastError.retriesExhausted = true;
|
|
101
|
+
throw lastError;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// src/api/cache.ts
|
|
105
|
+
import { createHash } from "crypto";
|
|
106
|
+
import { readFileSync, writeFileSync, mkdirSync, unlinkSync, readdirSync } from "fs";
|
|
107
|
+
import { join } from "path";
|
|
108
|
+
import { homedir } from "os";
|
|
109
|
+
var DEFAULT_CACHE_DIR = join(homedir(), ".tokenomist", "cache");
|
|
110
|
+
var MAX_CACHE_FILES = 500;
|
|
111
|
+
var ResponseCache = class {
|
|
112
|
+
constructor(ttl = 6e5, cacheDir = DEFAULT_CACHE_DIR) {
|
|
113
|
+
this.ttl = ttl;
|
|
114
|
+
this.cacheDir = cacheDir;
|
|
115
|
+
}
|
|
116
|
+
writeCount = 0;
|
|
117
|
+
buildKey(url, apiKey) {
|
|
118
|
+
const parsed = new URL(url);
|
|
119
|
+
parsed.searchParams.sort();
|
|
120
|
+
const prefix = apiKey ? createHash("md5").update(apiKey).digest("hex").slice(0, 8) : "";
|
|
121
|
+
const normalized = `${prefix}:GET:${parsed.toString()}`;
|
|
122
|
+
return createHash("md5").update(normalized).digest("hex");
|
|
123
|
+
}
|
|
124
|
+
get(key) {
|
|
125
|
+
try {
|
|
126
|
+
const filePath = join(this.cacheDir, `${key}.json`);
|
|
127
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
128
|
+
const entry = JSON.parse(raw);
|
|
129
|
+
if (Date.now() - entry.timestamp > entry.ttl) {
|
|
130
|
+
try {
|
|
131
|
+
unlinkSync(filePath);
|
|
132
|
+
} catch {
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return entry.data;
|
|
137
|
+
} catch {
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
set(key, url, data) {
|
|
142
|
+
try {
|
|
143
|
+
mkdirSync(this.cacheDir, { recursive: true, mode: 448 });
|
|
144
|
+
const entry = {
|
|
145
|
+
url,
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
ttl: this.ttl,
|
|
148
|
+
data
|
|
149
|
+
};
|
|
150
|
+
const filePath = join(this.cacheDir, `${key}.json`);
|
|
151
|
+
writeFileSync(filePath, JSON.stringify(entry), { encoding: "utf-8", mode: 384 });
|
|
152
|
+
this.writeCount++;
|
|
153
|
+
if (this.writeCount % 20 === 0) this.sweep();
|
|
154
|
+
} catch {
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
sweep() {
|
|
158
|
+
try {
|
|
159
|
+
const files = readdirSync(this.cacheDir).filter((f) => f.endsWith(".json"));
|
|
160
|
+
const now = Date.now();
|
|
161
|
+
for (const file of files) {
|
|
162
|
+
try {
|
|
163
|
+
const filePath = join(this.cacheDir, file);
|
|
164
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
165
|
+
const entry = JSON.parse(raw);
|
|
166
|
+
if (now - entry.timestamp > entry.ttl) {
|
|
167
|
+
unlinkSync(filePath);
|
|
168
|
+
}
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const remaining = readdirSync(this.cacheDir).filter((f) => f.endsWith(".json"));
|
|
173
|
+
if (remaining.length > MAX_CACHE_FILES) {
|
|
174
|
+
const entries = remaining.map((file) => {
|
|
175
|
+
try {
|
|
176
|
+
const filePath = join(this.cacheDir, file);
|
|
177
|
+
const raw = readFileSync(filePath, "utf-8");
|
|
178
|
+
const entry = JSON.parse(raw);
|
|
179
|
+
return { file, timestamp: entry.timestamp };
|
|
180
|
+
} catch {
|
|
181
|
+
return { file, timestamp: 0 };
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
entries.sort((a, b) => a.timestamp - b.timestamp);
|
|
185
|
+
const toRemove = entries.slice(0, entries.length - MAX_CACHE_FILES);
|
|
186
|
+
for (const { file } of toRemove) {
|
|
187
|
+
try {
|
|
188
|
+
unlinkSync(join(this.cacheDir, file));
|
|
189
|
+
} catch {
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} catch {
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// src/api/client.ts
|
|
199
|
+
var HEADER_API_KEY = "x-api-key";
|
|
200
|
+
var HEADER_CLIENT_SOURCE = "x-client-source";
|
|
201
|
+
var CLIENT_SOURCE_CLI = "cli";
|
|
202
|
+
var TokenomistClient = class {
|
|
203
|
+
constructor(apiKey, baseUrl, options) {
|
|
204
|
+
this.apiKey = apiKey;
|
|
205
|
+
this.baseUrl = baseUrl;
|
|
206
|
+
this.options = options;
|
|
207
|
+
this.cache = new ResponseCache();
|
|
208
|
+
}
|
|
209
|
+
cache;
|
|
210
|
+
async get(path, params) {
|
|
211
|
+
const url = this.buildUrl(path, params);
|
|
212
|
+
const urlStr = url.toString();
|
|
213
|
+
const cacheKey = this.cache.buildKey(urlStr, this.apiKey);
|
|
214
|
+
const useCache = this.options?.noCache !== true;
|
|
215
|
+
if (useCache) {
|
|
216
|
+
const cached = this.cache.get(cacheKey);
|
|
217
|
+
if (cached) return cached;
|
|
218
|
+
}
|
|
219
|
+
const data = await withRetry(() => this.fetchFromApi(url));
|
|
220
|
+
if (useCache) {
|
|
221
|
+
this.cache.set(cacheKey, urlStr, data);
|
|
222
|
+
}
|
|
223
|
+
return data;
|
|
224
|
+
}
|
|
225
|
+
buildUrl(path, params) {
|
|
226
|
+
const url = new URL(path, this.baseUrl);
|
|
227
|
+
if (params) {
|
|
228
|
+
for (const [key, value] of Object.entries(params)) {
|
|
229
|
+
if (value !== void 0 && value !== "") {
|
|
230
|
+
url.searchParams.set(key, value);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return url;
|
|
235
|
+
}
|
|
236
|
+
async fetchFromApi(url) {
|
|
237
|
+
let res;
|
|
238
|
+
try {
|
|
239
|
+
res = await fetch(url.toString(), {
|
|
240
|
+
headers: {
|
|
241
|
+
[HEADER_API_KEY]: this.apiKey,
|
|
242
|
+
[HEADER_CLIENT_SOURCE]: CLIENT_SOURCE_CLI
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
} catch (err) {
|
|
246
|
+
throw ApiError.fromNetworkError(err);
|
|
247
|
+
}
|
|
248
|
+
if (!res.ok) {
|
|
249
|
+
let body2 = {};
|
|
250
|
+
try {
|
|
251
|
+
body2 = await res.json();
|
|
252
|
+
} catch {
|
|
253
|
+
}
|
|
254
|
+
const retryAfter = res.headers.get("retry-after") ?? void 0;
|
|
255
|
+
throw ApiError.fromResponse(res.status, body2, retryAfter);
|
|
256
|
+
}
|
|
257
|
+
const body = await res.json();
|
|
258
|
+
if (!body.status) {
|
|
259
|
+
throw ApiError.fromResponse(200, body);
|
|
260
|
+
}
|
|
261
|
+
return body.data;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
// src/api/token.ts
|
|
266
|
+
async function listTokens(client, params) {
|
|
267
|
+
return client.get("/v5/token/list", params);
|
|
268
|
+
}
|
|
269
|
+
async function searchTokens(client, query) {
|
|
270
|
+
return client.get("/v5/search-token", { query });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// src/output/table.ts
|
|
274
|
+
import Table from "cli-table3";
|
|
275
|
+
function renderTable(data, columns5) {
|
|
276
|
+
const table = new Table({
|
|
277
|
+
head: columns5.map((c) => c.header),
|
|
278
|
+
style: { head: ["cyan"] },
|
|
279
|
+
colAligns: columns5.map((c) => c.align || "left")
|
|
280
|
+
});
|
|
281
|
+
for (const row of data) {
|
|
282
|
+
table.push(columns5.map((c) => formatValue(row[c.key])));
|
|
283
|
+
}
|
|
284
|
+
return table.toString();
|
|
285
|
+
}
|
|
286
|
+
function formatValue(value) {
|
|
287
|
+
if (value === null || value === void 0) return "\u2014";
|
|
288
|
+
if (typeof value === "number") {
|
|
289
|
+
if (Number.isInteger(value)) return value.toLocaleString();
|
|
290
|
+
return value.toLocaleString(void 0, { maximumFractionDigits: 4 });
|
|
291
|
+
}
|
|
292
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
293
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
294
|
+
return String(value);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/output/csv.ts
|
|
298
|
+
function renderCsv(data, columns5) {
|
|
299
|
+
const header = columns5.map((c) => escapeCsv(c.header)).join(",");
|
|
300
|
+
const rows = data.map(
|
|
301
|
+
(row) => columns5.map((c) => escapeCsv(formatCsvValue(row[c.key]))).join(",")
|
|
302
|
+
);
|
|
303
|
+
return [header, ...rows].join("\n");
|
|
304
|
+
}
|
|
305
|
+
function escapeCsv(value) {
|
|
306
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
307
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
308
|
+
}
|
|
309
|
+
return value;
|
|
310
|
+
}
|
|
311
|
+
function formatCsvValue(value) {
|
|
312
|
+
if (value === null || value === void 0) return "";
|
|
313
|
+
if (Array.isArray(value)) return value.join("; ");
|
|
314
|
+
return String(value);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/output/formatter.ts
|
|
318
|
+
function format(data, columns5, mode) {
|
|
319
|
+
switch (mode) {
|
|
320
|
+
case "json":
|
|
321
|
+
return JSON.stringify(data, null, 2);
|
|
322
|
+
case "csv":
|
|
323
|
+
return renderCsv(data, columns5);
|
|
324
|
+
case "table":
|
|
325
|
+
default:
|
|
326
|
+
return renderTable(data, columns5);
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
function output(data, columns5, mode) {
|
|
330
|
+
console.log(format(data, columns5, mode));
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/config/config.ts
|
|
334
|
+
import { config as loadDotenv } from "dotenv";
|
|
335
|
+
|
|
336
|
+
// src/config/store.ts
|
|
337
|
+
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
338
|
+
import { join as join2 } from "path";
|
|
339
|
+
import { homedir as homedir2 } from "os";
|
|
340
|
+
var CONFIG_DIR = join2(homedir2(), ".tokenomist");
|
|
341
|
+
var CONFIG_PATH = join2(CONFIG_DIR, "config.json");
|
|
342
|
+
function getConfigPath() {
|
|
343
|
+
return CONFIG_PATH;
|
|
344
|
+
}
|
|
345
|
+
function loadConfigFile() {
|
|
346
|
+
try {
|
|
347
|
+
const raw = readFileSync2(CONFIG_PATH, "utf-8");
|
|
348
|
+
return JSON.parse(raw);
|
|
349
|
+
} catch {
|
|
350
|
+
return {};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
function saveConfigFile(config) {
|
|
354
|
+
mkdirSync2(CONFIG_DIR, { recursive: true, mode: 448 });
|
|
355
|
+
writeFileSync2(CONFIG_PATH, JSON.stringify(config, null, 2) + "\n", {
|
|
356
|
+
encoding: "utf-8",
|
|
357
|
+
mode: 384
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
function deleteConfigFile() {
|
|
361
|
+
try {
|
|
362
|
+
unlinkSync2(CONFIG_PATH);
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function maskApiKey(key) {
|
|
367
|
+
if (key.length <= 8) return "****";
|
|
368
|
+
return key.slice(0, 4) + "****" + key.slice(-4);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// src/config/config.ts
|
|
372
|
+
loadDotenv();
|
|
373
|
+
function resolveConfig(opts) {
|
|
374
|
+
const file = loadConfigFile();
|
|
375
|
+
const apiKey = opts.apiKey || process.env.TOKENOMIST_API_KEY || file.apiKey || "";
|
|
376
|
+
const baseUrl = opts.baseUrl || process.env.TOKENOMIST_BASE_URL || file.baseUrl || "https://api.tokenomist.ai";
|
|
377
|
+
if (!apiKey) {
|
|
378
|
+
console.error(
|
|
379
|
+
'Error: API key is required. Run "tokenomist auth login" or provide via --api-key / TOKENOMIST_API_KEY env var.'
|
|
380
|
+
);
|
|
381
|
+
process.exit(1);
|
|
382
|
+
}
|
|
383
|
+
return { apiKey, baseUrl, noCache: opts.cache === false };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/commands/token.ts
|
|
387
|
+
var columns = [
|
|
388
|
+
{ key: "id", header: "ID" },
|
|
389
|
+
{ key: "name", header: "Name" },
|
|
390
|
+
{ key: "symbol", header: "Symbol" },
|
|
391
|
+
{ key: "marketCap", header: "Market Cap", align: "right" },
|
|
392
|
+
{ key: "circulatingSupply", header: "Circ. Supply", align: "right" },
|
|
393
|
+
{ key: "totalLockedAmount", header: "Locked", align: "right" },
|
|
394
|
+
{ key: "unlockedAmount", header: "Unlocked", align: "right" },
|
|
395
|
+
{ key: "lastUpdatedDate", header: "Updated" }
|
|
396
|
+
];
|
|
397
|
+
var searchColumns = [
|
|
398
|
+
{ key: "tokenId", header: "ID" },
|
|
399
|
+
{ key: "tokenName", header: "Name" },
|
|
400
|
+
{ key: "tokenSymbol", header: "Symbol" }
|
|
401
|
+
];
|
|
402
|
+
var tokenCommand = new Command("token").description("Token commands");
|
|
403
|
+
tokenCommand.command("list").description("List all tokens with summary data").option("--token-id <ids>", "Filter by token IDs (comma-separated)").action(async (opts, cmd) => {
|
|
404
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
405
|
+
const config = resolveConfig(globalOpts);
|
|
406
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
407
|
+
noCache: config.noCache
|
|
408
|
+
});
|
|
409
|
+
const data = await listTokens(client, {
|
|
410
|
+
tokenId: opts.tokenId
|
|
411
|
+
});
|
|
412
|
+
output(data, columns, globalOpts.output || "table");
|
|
413
|
+
});
|
|
414
|
+
tokenCommand.command("search <query>").description("Search for tokens by name, symbol, or slug").action(async (query, _opts, cmd) => {
|
|
415
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
416
|
+
const config = resolveConfig(globalOpts);
|
|
417
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
418
|
+
noCache: config.noCache
|
|
419
|
+
});
|
|
420
|
+
const data = await searchTokens(client, query);
|
|
421
|
+
output(data, searchColumns, globalOpts.output || "table");
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
// src/commands/unlock.ts
|
|
425
|
+
import { Command as Command2 } from "commander";
|
|
426
|
+
|
|
427
|
+
// src/api/unlock.ts
|
|
428
|
+
async function listUnlockEvents(client, params) {
|
|
429
|
+
return client.get("/v5/unlock/events/list", params);
|
|
430
|
+
}
|
|
431
|
+
async function listUpcomingUnlockEvents(client, params) {
|
|
432
|
+
return client.get(
|
|
433
|
+
"/v5/unlock/events/upcoming",
|
|
434
|
+
params
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
async function getUnlockEvents(client, tokenSlug, params) {
|
|
438
|
+
return client.get(
|
|
439
|
+
`/v5/unlock/events/${encodeURIComponent(tokenSlug)}`,
|
|
440
|
+
params
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// src/commands/unlock.ts
|
|
445
|
+
var listColumns = [
|
|
446
|
+
{ key: "tokenName", header: "Token" },
|
|
447
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
448
|
+
{ key: "marketCap", header: "Market Cap", align: "right" },
|
|
449
|
+
{ key: "releasedPercentage", header: "Released %", align: "right" },
|
|
450
|
+
{ key: "eventCount", header: "Events", align: "right" }
|
|
451
|
+
];
|
|
452
|
+
var upcomingColumns = [
|
|
453
|
+
{ key: "tokenName", header: "Token" },
|
|
454
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
455
|
+
{ key: "marketCap", header: "Market Cap", align: "right" },
|
|
456
|
+
{ key: "unlockDate", header: "Unlock Date" },
|
|
457
|
+
{ key: "totalCliffAmount", header: "Cliff Amount", align: "right" },
|
|
458
|
+
{ key: "totalCliffValue", header: "Cliff Value", align: "right" }
|
|
459
|
+
];
|
|
460
|
+
var eventsColumns = [
|
|
461
|
+
{ key: "unlockDate", header: "Date" },
|
|
462
|
+
{ key: "tokenName", header: "Token" },
|
|
463
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
464
|
+
{ key: "cliffAmount", header: "Amount", align: "right" },
|
|
465
|
+
{ key: "cliffValue", header: "Value", align: "right" },
|
|
466
|
+
{ key: "valueToMarketCap", header: "Val/MCap", align: "right" }
|
|
467
|
+
];
|
|
468
|
+
function addPaginationOptions(cmd) {
|
|
469
|
+
return cmd.option("--page <number>", "Page number").option("--page-size <number>", "Items per page");
|
|
470
|
+
}
|
|
471
|
+
function addListFilterOptions(cmd) {
|
|
472
|
+
return addPaginationOptions(
|
|
473
|
+
cmd.option("--min-market-cap <value>", "Minimum market cap").option("--min-released-percentage <value>", "Minimum released percentage").option("--min-unlock-date <date>", "Min unlock date (YYYY-MM-DD)").option("--max-unlock-date <date>", "Max unlock date (YYYY-MM-DD)").option("--min-total-unlock-amount <value>", "Minimum total unlock amount").option("--min-value-to-market-cap <value>", "Min value-to-market-cap ratio").option("--standard-allocation <names>", "Filter by allocation (comma-separated)")
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
function addUpcomingFilterOptions(cmd) {
|
|
477
|
+
return addPaginationOptions(
|
|
478
|
+
cmd.option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").option("--standard-allocation <names>", "Filter by allocation (comma-separated)").option("--min-market-cap <value>", "Minimum market cap").option("--min-released-percentage <value>", "Minimum released percentage").option("--min-total-unlock-amount <value>", "Minimum total unlock amount").option("--min-value-to-market-cap <value>", "Min value-to-market-cap ratio")
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
var unlockCommand = new Command2("unlock").description("Token unlock event commands");
|
|
482
|
+
addListFilterOptions(
|
|
483
|
+
unlockCommand.command("list").description("List unlock events grouped by token")
|
|
484
|
+
).action(async (opts, cmd) => {
|
|
485
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
486
|
+
const config = resolveConfig(globalOpts);
|
|
487
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
488
|
+
noCache: config.noCache
|
|
489
|
+
});
|
|
490
|
+
const data = await listUnlockEvents(client, {
|
|
491
|
+
minMarketCap: opts.minMarketCap,
|
|
492
|
+
minReleasedPercentage: opts.minReleasedPercentage,
|
|
493
|
+
minUnlockDate: opts.minUnlockDate,
|
|
494
|
+
maxUnlockDate: opts.maxUnlockDate,
|
|
495
|
+
minTotalUnlockAmount: opts.minTotalUnlockAmount,
|
|
496
|
+
minValueToMarketCap: opts.minValueToMarketCap,
|
|
497
|
+
standardAllocation: opts.standardAllocation,
|
|
498
|
+
page: opts.page,
|
|
499
|
+
pageSize: opts.pageSize
|
|
500
|
+
});
|
|
501
|
+
const rows = data.map((d) => ({
|
|
502
|
+
...d,
|
|
503
|
+
eventCount: d.events.length
|
|
504
|
+
}));
|
|
505
|
+
output(rows, listColumns, globalOpts.output || "table");
|
|
506
|
+
});
|
|
507
|
+
addUpcomingFilterOptions(
|
|
508
|
+
unlockCommand.command("upcoming").description("List next upcoming unlock event per token")
|
|
509
|
+
).action(async (opts, cmd) => {
|
|
510
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
511
|
+
const config = resolveConfig(globalOpts);
|
|
512
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
513
|
+
noCache: config.noCache
|
|
514
|
+
});
|
|
515
|
+
const data = await listUpcomingUnlockEvents(client, {
|
|
516
|
+
start: opts.start,
|
|
517
|
+
end: opts.end,
|
|
518
|
+
standardAllocation: opts.standardAllocation,
|
|
519
|
+
minMarketCap: opts.minMarketCap,
|
|
520
|
+
minReleasedPercentage: opts.minReleasedPercentage,
|
|
521
|
+
minTotalUnlockAmount: opts.minTotalUnlockAmount,
|
|
522
|
+
minValueToMarketCap: opts.minValueToMarketCap,
|
|
523
|
+
page: opts.page,
|
|
524
|
+
pageSize: opts.pageSize
|
|
525
|
+
});
|
|
526
|
+
const rows = data.map((d) => ({
|
|
527
|
+
...d,
|
|
528
|
+
unlockDate: d.upcomingEvent.unlockDate,
|
|
529
|
+
totalCliffAmount: d.upcomingEvent.cliffUnlocks.totalCliffAmount,
|
|
530
|
+
totalCliffValue: d.upcomingEvent.cliffUnlocks.totalCliffValue
|
|
531
|
+
}));
|
|
532
|
+
output(
|
|
533
|
+
rows,
|
|
534
|
+
upcomingColumns,
|
|
535
|
+
globalOpts.output || "table"
|
|
536
|
+
);
|
|
537
|
+
});
|
|
538
|
+
addPaginationOptions(
|
|
539
|
+
unlockCommand.command("events <tokenSlug>").description("Get unlock events for a specific token").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").option("--standard-allocation <names>", "Filter by allocation (comma-separated)")
|
|
540
|
+
).action(async (tokenSlug, opts, cmd) => {
|
|
541
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
542
|
+
const config = resolveConfig(globalOpts);
|
|
543
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
544
|
+
noCache: config.noCache
|
|
545
|
+
});
|
|
546
|
+
const data = await getUnlockEvents(client, tokenSlug, {
|
|
547
|
+
start: opts.start,
|
|
548
|
+
end: opts.end,
|
|
549
|
+
standardAllocation: opts.standardAllocation,
|
|
550
|
+
page: opts.page,
|
|
551
|
+
pageSize: opts.pageSize
|
|
552
|
+
});
|
|
553
|
+
const rows = data.map((d) => ({
|
|
554
|
+
...d,
|
|
555
|
+
cliffAmount: d.cliffUnlocks.cliffAmount,
|
|
556
|
+
cliffValue: d.cliffUnlocks.cliffValue,
|
|
557
|
+
valueToMarketCap: d.cliffUnlocks.valueToMarketCap
|
|
558
|
+
}));
|
|
559
|
+
output(
|
|
560
|
+
rows,
|
|
561
|
+
eventsColumns,
|
|
562
|
+
globalOpts.output || "table"
|
|
563
|
+
);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
// src/commands/investor.ts
|
|
567
|
+
import { Command as Command3 } from "commander";
|
|
568
|
+
|
|
569
|
+
// src/api/investor.ts
|
|
570
|
+
async function listInvestors(client, params) {
|
|
571
|
+
return client.get(
|
|
572
|
+
"/v5/fundraising/investors/list",
|
|
573
|
+
params
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
async function getInvestorDetail(client, investorId, params) {
|
|
577
|
+
return client.get(
|
|
578
|
+
`/v5/fundraising/investors/${encodeURIComponent(investorId)}`,
|
|
579
|
+
params
|
|
580
|
+
);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// src/commands/investor.ts
|
|
584
|
+
var listColumns2 = [
|
|
585
|
+
{ key: "investorId", header: "ID" },
|
|
586
|
+
{ key: "investorName", header: "Name" },
|
|
587
|
+
{ key: "website", header: "Website" },
|
|
588
|
+
{ key: "latestFundingDate", header: "Latest Funding" },
|
|
589
|
+
{ key: "latestFundingRound", header: "Latest Round" },
|
|
590
|
+
{ key: "roundCount", header: "Rounds", align: "right" }
|
|
591
|
+
];
|
|
592
|
+
var detailColumns = [
|
|
593
|
+
{ key: "tokenId", header: "Token ID" },
|
|
594
|
+
{ key: "roundName", header: "Round" },
|
|
595
|
+
{ key: "isLeadInvestor", header: "Lead?" },
|
|
596
|
+
{ key: "fundingDate", header: "Date" },
|
|
597
|
+
{ key: "totalAmountRaisedinThatRound", header: "Raised", align: "right" },
|
|
598
|
+
{ key: "valuation", header: "Valuation", align: "right" },
|
|
599
|
+
{ key: "pricePerToken", header: "Price/Token", align: "right" }
|
|
600
|
+
];
|
|
601
|
+
var investorCommand = new Command3("investor").description("Investor commands");
|
|
602
|
+
investorCommand.command("list").description("List all investors with their funding rounds").option("--start-latest-funding-date <date>", "Start date filter for latest funding (YYYY-MM-DD)").option("--end-latest-funding-date <date>", "End date filter for latest funding (YYYY-MM-DD)").option("--page <number>", "Page number").option("--page-size <number>", "Items per page").action(async (opts, cmd) => {
|
|
603
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
604
|
+
const config = resolveConfig(globalOpts);
|
|
605
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
606
|
+
noCache: config.noCache
|
|
607
|
+
});
|
|
608
|
+
const data = await listInvestors(client, {
|
|
609
|
+
startLatestFundingDate: opts.startLatestFundingDate,
|
|
610
|
+
endLatestFundingDate: opts.endLatestFundingDate,
|
|
611
|
+
page: opts.page,
|
|
612
|
+
pageSize: opts.pageSize
|
|
613
|
+
});
|
|
614
|
+
const rows = data.map((d) => ({
|
|
615
|
+
...d,
|
|
616
|
+
roundCount: d.fundingRounds?.length || 0
|
|
617
|
+
}));
|
|
618
|
+
output(rows, listColumns2, globalOpts.output || "table");
|
|
619
|
+
});
|
|
620
|
+
investorCommand.command("detail <investorId>").description("Get investor detail with funding rounds").option("--is-lead-investor", "Filter by lead investor status").option("--funding-date-min <date>", "Min funding date (YYYY-MM-DD)").option("--funding-date-max <date>", "Max funding date (YYYY-MM-DD)").option("--raised-amount-min <value>", "Min raised amount").option("--raised-amount-max <value>", "Max raised amount").action(async (investorId, opts, cmd) => {
|
|
621
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
622
|
+
const config = resolveConfig(globalOpts);
|
|
623
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
624
|
+
noCache: config.noCache
|
|
625
|
+
});
|
|
626
|
+
const data = await getInvestorDetail(client, investorId, {
|
|
627
|
+
isLeadInvestor: opts.isLeadInvestor ? "true" : void 0,
|
|
628
|
+
fundingDateMin: opts.fundingDateMin,
|
|
629
|
+
fundingDateMax: opts.fundingDateMax,
|
|
630
|
+
raisedAmountMin: opts.raisedAmountMin,
|
|
631
|
+
raisedAmountMax: opts.raisedAmountMax
|
|
632
|
+
});
|
|
633
|
+
const outputMode = globalOpts.output || "table";
|
|
634
|
+
if (outputMode === "json") {
|
|
635
|
+
console.log(JSON.stringify(data, null, 2));
|
|
636
|
+
} else {
|
|
637
|
+
console.log(`Investor: ${data.investorName}`);
|
|
638
|
+
console.log(`Website: ${data.website}`);
|
|
639
|
+
console.log(`Latest Funding: ${data.latestFundingDate}`);
|
|
640
|
+
console.log();
|
|
641
|
+
output(data.fundingRounds, detailColumns, outputMode);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
|
|
645
|
+
// src/commands/fundraising.ts
|
|
646
|
+
import { Command as Command4 } from "commander";
|
|
647
|
+
|
|
648
|
+
// src/api/fundraising.ts
|
|
649
|
+
async function getFundraising(client, tokenSlug, params) {
|
|
650
|
+
return client.get(
|
|
651
|
+
`/v5/fundraising/token/${encodeURIComponent(tokenSlug)}`,
|
|
652
|
+
params
|
|
653
|
+
);
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// src/commands/fundraising.ts
|
|
657
|
+
var columns2 = [
|
|
658
|
+
{ key: "fundingRound", header: "Round" },
|
|
659
|
+
{ key: "fundingDate", header: "Date" },
|
|
660
|
+
{ key: "raiseType", header: "Type" },
|
|
661
|
+
{ key: "amountRaised", header: "Raised", align: "right" },
|
|
662
|
+
{ key: "valuation", header: "Valuation", align: "right" },
|
|
663
|
+
{ key: "pricePerToken", header: "Price/Token", align: "right" },
|
|
664
|
+
{ key: "numberOfInvestors", header: "Investors", align: "right" }
|
|
665
|
+
];
|
|
666
|
+
var fundraisingCommand = new Command4("fundraising").description("Get fundraising data for a token").argument("<tokenSlug>", "Token slug").option("--funding-date-min <date>", "Min funding date (YYYY-MM-DD)").option("--funding-date-max <date>", "Max funding date (YYYY-MM-DD)").option("--raised-amount-min <value>", "Min raised amount").option("--raised-amount-max <value>", "Max raised amount").action(async (tokenSlug, opts, cmd) => {
|
|
667
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
668
|
+
const config = resolveConfig(globalOpts);
|
|
669
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
670
|
+
noCache: config.noCache
|
|
671
|
+
});
|
|
672
|
+
const data = await getFundraising(client, tokenSlug, {
|
|
673
|
+
fundingDateMin: opts.fundingDateMin,
|
|
674
|
+
fundingDateMax: opts.fundingDateMax,
|
|
675
|
+
raisedAmountMin: opts.raisedAmountMin,
|
|
676
|
+
raisedAmountMax: opts.raisedAmountMax
|
|
677
|
+
});
|
|
678
|
+
const outputMode = globalOpts.output || "table";
|
|
679
|
+
if (outputMode === "json") {
|
|
680
|
+
console.log(JSON.stringify(data, null, 2));
|
|
681
|
+
} else {
|
|
682
|
+
console.log(`Token: ${data.tokenName} (${data.tokenSymbol})`);
|
|
683
|
+
console.log(`Total Raised: ${data.totalAmountRaised ?? "\u2014"}`);
|
|
684
|
+
console.log(`Investor Released: ${data.investorReleasedPercentage ?? "\u2014"}%`);
|
|
685
|
+
console.log();
|
|
686
|
+
output(data.fundingRounds, columns2, outputMode);
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
// src/commands/burn.ts
|
|
691
|
+
import { Command as Command5 } from "commander";
|
|
692
|
+
|
|
693
|
+
// src/api/burn.ts
|
|
694
|
+
async function listBurns(client, params) {
|
|
695
|
+
return client.get("/v5/burn/list", params);
|
|
696
|
+
}
|
|
697
|
+
async function getBurnDetail(client, tokenSlug, params) {
|
|
698
|
+
return client.get(
|
|
699
|
+
`/v5/burn/${encodeURIComponent(tokenSlug)}`,
|
|
700
|
+
params
|
|
701
|
+
);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// src/commands/burn.ts
|
|
705
|
+
var listColumns3 = [
|
|
706
|
+
{ key: "tokenId", header: "ID" },
|
|
707
|
+
{ key: "tokenName", header: "Name" },
|
|
708
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
709
|
+
{ key: "totalBurnAmount", header: "Total Burned", align: "right" }
|
|
710
|
+
];
|
|
711
|
+
var detailColumns2 = [
|
|
712
|
+
{ key: "burnEventLabel", header: "Label" },
|
|
713
|
+
{ key: "burnType", header: "Type" },
|
|
714
|
+
{ key: "burnDate", header: "Date" },
|
|
715
|
+
{ key: "amount", header: "Amount", align: "right" }
|
|
716
|
+
];
|
|
717
|
+
var burnCommand = new Command5("burn").description("Token burn commands");
|
|
718
|
+
burnCommand.command("list").description("List all tokens with burn events").option("--token-id <ids>", "Filter by token IDs (comma-separated)").option("--page <number>", "Page number").option("--page-size <number>", "Items per page").action(async (opts, cmd) => {
|
|
719
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
720
|
+
const config = resolveConfig(globalOpts);
|
|
721
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
722
|
+
noCache: config.noCache
|
|
723
|
+
});
|
|
724
|
+
const data = await listBurns(client, {
|
|
725
|
+
tokenId: opts.tokenId,
|
|
726
|
+
page: opts.page,
|
|
727
|
+
pageSize: opts.pageSize
|
|
728
|
+
});
|
|
729
|
+
output(data, listColumns3, globalOpts.output || "table");
|
|
730
|
+
});
|
|
731
|
+
burnCommand.command("detail <tokenSlug>").description("Get burn events for a specific token").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").action(async (tokenSlug, opts, cmd) => {
|
|
732
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
733
|
+
const config = resolveConfig(globalOpts);
|
|
734
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
735
|
+
noCache: config.noCache
|
|
736
|
+
});
|
|
737
|
+
const data = await getBurnDetail(client, tokenSlug, {
|
|
738
|
+
start: opts.start,
|
|
739
|
+
end: opts.end
|
|
740
|
+
});
|
|
741
|
+
const outputMode = globalOpts.output || "table";
|
|
742
|
+
if (outputMode === "json") {
|
|
743
|
+
console.log(JSON.stringify(data, null, 2));
|
|
744
|
+
} else {
|
|
745
|
+
console.log(`Token: ${data.tokenName} (${data.tokenSymbol})`);
|
|
746
|
+
console.log(`Total Burned: ${data.totalBurnAmount.toLocaleString()}`);
|
|
747
|
+
console.log();
|
|
748
|
+
output(data.burns, detailColumns2, outputMode);
|
|
749
|
+
}
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
// src/commands/buyback.ts
|
|
753
|
+
import { Command as Command6 } from "commander";
|
|
754
|
+
|
|
755
|
+
// src/api/buyback.ts
|
|
756
|
+
async function listBuybacks(client, params) {
|
|
757
|
+
return client.get("/v5/buyback/list", params);
|
|
758
|
+
}
|
|
759
|
+
async function getBuybackDetail(client, tokenSlug, params) {
|
|
760
|
+
return client.get(
|
|
761
|
+
`/v5/buyback/${encodeURIComponent(tokenSlug)}`,
|
|
762
|
+
params
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
// src/commands/buyback.ts
|
|
767
|
+
var listColumns4 = [
|
|
768
|
+
{ key: "tokenId", header: "ID" },
|
|
769
|
+
{ key: "tokenName", header: "Name" },
|
|
770
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
771
|
+
{ key: "totalBuybackAmount", header: "Total Buyback", align: "right" }
|
|
772
|
+
];
|
|
773
|
+
var detailColumns3 = [
|
|
774
|
+
{ key: "buybackEventLabel", header: "Label" },
|
|
775
|
+
{ key: "buybackType", header: "Type" },
|
|
776
|
+
{ key: "buybackDate", header: "Date" },
|
|
777
|
+
{ key: "tokenAmount", header: "Amount", align: "right" },
|
|
778
|
+
{ key: "value", header: "Value", align: "right" },
|
|
779
|
+
{ key: "spentAmount", header: "Spent", align: "right" },
|
|
780
|
+
{ key: "spentUnit", header: "Unit" }
|
|
781
|
+
];
|
|
782
|
+
var buybackCommand = new Command6("buyback").description("Token buyback commands");
|
|
783
|
+
buybackCommand.command("list").description("List all tokens with buyback events").option("--token-id <ids>", "Filter by token IDs (comma-separated)").option("--page <number>", "Page number").option("--page-size <number>", "Items per page").action(async (opts, cmd) => {
|
|
784
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
785
|
+
const config = resolveConfig(globalOpts);
|
|
786
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
787
|
+
noCache: config.noCache
|
|
788
|
+
});
|
|
789
|
+
const data = await listBuybacks(client, {
|
|
790
|
+
tokenId: opts.tokenId,
|
|
791
|
+
page: opts.page,
|
|
792
|
+
pageSize: opts.pageSize
|
|
793
|
+
});
|
|
794
|
+
output(data, listColumns4, globalOpts.output || "table");
|
|
795
|
+
});
|
|
796
|
+
buybackCommand.command("detail <tokenSlug>").description("Get buyback events for a specific token").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").action(async (tokenSlug, opts, cmd) => {
|
|
797
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
798
|
+
const config = resolveConfig(globalOpts);
|
|
799
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
800
|
+
noCache: config.noCache
|
|
801
|
+
});
|
|
802
|
+
const data = await getBuybackDetail(client, tokenSlug, {
|
|
803
|
+
start: opts.start,
|
|
804
|
+
end: opts.end
|
|
805
|
+
});
|
|
806
|
+
const outputMode = globalOpts.output || "table";
|
|
807
|
+
if (outputMode === "json") {
|
|
808
|
+
console.log(JSON.stringify(data, null, 2));
|
|
809
|
+
} else {
|
|
810
|
+
console.log(`Token: ${data.tokenName} (${data.tokenSymbol})`);
|
|
811
|
+
console.log(`Total Buyback: ${data.totalBuybackAmount.toLocaleString()}`);
|
|
812
|
+
console.log();
|
|
813
|
+
output(data.buybacks, detailColumns3, outputMode);
|
|
814
|
+
}
|
|
815
|
+
});
|
|
816
|
+
|
|
817
|
+
// src/commands/emission.ts
|
|
818
|
+
import { Command as Command7 } from "commander";
|
|
819
|
+
|
|
820
|
+
// src/api/emission.ts
|
|
821
|
+
async function getDailyEmission(client, tokenSlug, params) {
|
|
822
|
+
return client.get(
|
|
823
|
+
`/v5/daily-emission/${encodeURIComponent(tokenSlug)}`,
|
|
824
|
+
params
|
|
825
|
+
);
|
|
826
|
+
}
|
|
827
|
+
async function getWeeklyEmission(client, tokenSlug, params) {
|
|
828
|
+
return client.get(
|
|
829
|
+
`/v5/emission/${encodeURIComponent(tokenSlug)}`,
|
|
830
|
+
params
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// src/commands/emission.ts
|
|
835
|
+
var columns3 = [
|
|
836
|
+
{ key: "startDate", header: "Start" },
|
|
837
|
+
{ key: "endDate", header: "End" },
|
|
838
|
+
{ key: "tokenName", header: "Token" },
|
|
839
|
+
{ key: "tokenSymbol", header: "Symbol" },
|
|
840
|
+
{ key: "unlockAmount", header: "Unlock Amt", align: "right" },
|
|
841
|
+
{ key: "unlockValue", header: "Unlock Val", align: "right" },
|
|
842
|
+
{ key: "referencePrice", header: "Ref Price", align: "right" },
|
|
843
|
+
{ key: "totalCumulativeUnlockedAmount", header: "Cumulative", align: "right" }
|
|
844
|
+
];
|
|
845
|
+
var emissionCommand = new Command7("emission").description("Token emission commands");
|
|
846
|
+
emissionCommand.command("daily <tokenSlug>").description("Get daily emission breakdown for a token").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").option("--standard-allocation <names>", "Filter by allocation (comma-separated)").option("--page <number>", "Page number").option("--page-size <number>", "Items per page").action(async (tokenSlug, opts, cmd) => {
|
|
847
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
848
|
+
const config = resolveConfig(globalOpts);
|
|
849
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
850
|
+
noCache: config.noCache
|
|
851
|
+
});
|
|
852
|
+
const data = await getDailyEmission(client, tokenSlug, {
|
|
853
|
+
start: opts.start,
|
|
854
|
+
end: opts.end,
|
|
855
|
+
standardAllocation: opts.standardAllocation,
|
|
856
|
+
page: opts.page,
|
|
857
|
+
pageSize: opts.pageSize
|
|
858
|
+
});
|
|
859
|
+
output(data, columns3, globalOpts.output || "table");
|
|
860
|
+
});
|
|
861
|
+
emissionCommand.command("weekly <tokenSlug>").description("Get weekly emission breakdown for a token").option("--start <date>", "Start date (YYYY-MM-DD)").option("--end <date>", "End date (YYYY-MM-DD)").option("--standard-allocation <names>", "Filter by allocation (comma-separated)").option("--page <number>", "Page number").option("--page-size <number>", "Items per page").action(async (tokenSlug, opts, cmd) => {
|
|
862
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
863
|
+
const config = resolveConfig(globalOpts);
|
|
864
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
865
|
+
noCache: config.noCache
|
|
866
|
+
});
|
|
867
|
+
const data = await getWeeklyEmission(client, tokenSlug, {
|
|
868
|
+
start: opts.start,
|
|
869
|
+
end: opts.end,
|
|
870
|
+
standardAllocation: opts.standardAllocation,
|
|
871
|
+
page: opts.page,
|
|
872
|
+
pageSize: opts.pageSize
|
|
873
|
+
});
|
|
874
|
+
output(data, columns3, globalOpts.output || "table");
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
// src/commands/allocation.ts
|
|
878
|
+
import { Command as Command8 } from "commander";
|
|
879
|
+
|
|
880
|
+
// src/api/allocation.ts
|
|
881
|
+
async function getAllocation(client, tokenSlug) {
|
|
882
|
+
return client.get(
|
|
883
|
+
`/v5/allocations/${encodeURIComponent(tokenSlug)}`
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
// src/commands/allocation.ts
|
|
888
|
+
var columns4 = [
|
|
889
|
+
{ key: "allocationName", header: "Allocation" },
|
|
890
|
+
{ key: "allocationType", header: "Type" },
|
|
891
|
+
{ key: "standardAllocationName", header: "Std. Allocation" },
|
|
892
|
+
{ key: "allocationAmount", header: "Amount", align: "right" },
|
|
893
|
+
{ key: "allocationUnlockedAmount", header: "Unlocked", align: "right" },
|
|
894
|
+
{ key: "allocationLockedAmount", header: "Locked", align: "right" },
|
|
895
|
+
{ key: "trackedAllocationPercentage", header: "Tracked %", align: "right" }
|
|
896
|
+
];
|
|
897
|
+
var allocationCommand = new Command8("allocation").description("Get standard allocation data for a token").argument("<tokenSlug>", "Token slug").action(async (tokenSlug, _opts, cmd) => {
|
|
898
|
+
const globalOpts = cmd.optsWithGlobals();
|
|
899
|
+
const config = resolveConfig(globalOpts);
|
|
900
|
+
const client = new TokenomistClient(config.apiKey, config.baseUrl, {
|
|
901
|
+
noCache: config.noCache
|
|
902
|
+
});
|
|
903
|
+
const data = await getAllocation(client, tokenSlug);
|
|
904
|
+
const outputMode = globalOpts.output || "table";
|
|
905
|
+
if (outputMode === "json") {
|
|
906
|
+
console.log(JSON.stringify(data, null, 2));
|
|
907
|
+
} else {
|
|
908
|
+
console.log(`Token: ${data.name} (${data.symbol})`);
|
|
909
|
+
console.log(`Max Supply: ${data.maxSupply?.toLocaleString() ?? "\u2014"}`);
|
|
910
|
+
console.log(`Total Unlocked: ${data.totalUnlockedAmount.toLocaleString()}`);
|
|
911
|
+
console.log(`Total Locked: ${data.totalLockedAmount.toLocaleString()}`);
|
|
912
|
+
console.log(`Last Updated: ${data.lastUpdatedDate}`);
|
|
913
|
+
console.log();
|
|
914
|
+
output(data.allocations, columns4, outputMode);
|
|
915
|
+
}
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
// src/commands/auth.ts
|
|
919
|
+
import { Command as Command9 } from "commander";
|
|
920
|
+
import { createInterface } from "readline";
|
|
921
|
+
function readInput(prompt) {
|
|
922
|
+
const rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
923
|
+
return new Promise((resolve) => {
|
|
924
|
+
rl.question(prompt, (answer) => {
|
|
925
|
+
rl.close();
|
|
926
|
+
resolve(answer.trim());
|
|
927
|
+
});
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
var authCommand = new Command9("auth").description("Manage API key authentication");
|
|
931
|
+
authCommand.command("login").description("Store your API key securely in ~/.tokenomist/config.json").option("--api-key <key>", "API key (will prompt if not provided)").action(async (opts) => {
|
|
932
|
+
let apiKey = opts.apiKey;
|
|
933
|
+
if (!apiKey) {
|
|
934
|
+
apiKey = await readInput("Enter your Tokenomist API key: ");
|
|
935
|
+
}
|
|
936
|
+
if (!apiKey) {
|
|
937
|
+
console.error("Error: API key cannot be empty.");
|
|
938
|
+
process.exit(1);
|
|
939
|
+
}
|
|
940
|
+
const config = loadConfigFile();
|
|
941
|
+
config.apiKey = apiKey;
|
|
942
|
+
saveConfigFile(config);
|
|
943
|
+
console.log(`API key saved to ${getConfigPath()}`);
|
|
944
|
+
console.log(`Key: ${maskApiKey(apiKey)}`);
|
|
945
|
+
});
|
|
946
|
+
authCommand.command("logout").description("Remove stored API key").action(() => {
|
|
947
|
+
const config = loadConfigFile();
|
|
948
|
+
if (!config.apiKey) {
|
|
949
|
+
console.log("No API key stored.");
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
delete config.apiKey;
|
|
953
|
+
if (Object.keys(config).length === 0) {
|
|
954
|
+
deleteConfigFile();
|
|
955
|
+
console.log(`Removed ${getConfigPath()}`);
|
|
956
|
+
} else {
|
|
957
|
+
saveConfigFile(config);
|
|
958
|
+
console.log("API key removed from config.");
|
|
959
|
+
}
|
|
960
|
+
});
|
|
961
|
+
authCommand.command("status").description("Show current authentication status").action(() => {
|
|
962
|
+
const envKey = process.env.TOKENOMIST_API_KEY;
|
|
963
|
+
const config = loadConfigFile();
|
|
964
|
+
const fileKey = config.apiKey;
|
|
965
|
+
if (envKey) {
|
|
966
|
+
console.log(`Source: TOKENOMIST_API_KEY env var`);
|
|
967
|
+
console.log(`Key: ${maskApiKey(envKey)}`);
|
|
968
|
+
} else if (fileKey) {
|
|
969
|
+
console.log(`Source: ${getConfigPath()}`);
|
|
970
|
+
console.log(`Key: ${maskApiKey(fileKey)}`);
|
|
971
|
+
} else {
|
|
972
|
+
console.log("Not authenticated.");
|
|
973
|
+
console.log(`Run "tokenomist auth login" to store your API key.`);
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
// src/commands/schema.ts
|
|
978
|
+
import { Command as Command10 } from "commander";
|
|
979
|
+
|
|
980
|
+
// src/schema.json
|
|
981
|
+
var schema_default = {
|
|
982
|
+
name: "tokenomist",
|
|
983
|
+
version: "0.1.0",
|
|
984
|
+
description: "CLI tool for the Tokenomist API",
|
|
985
|
+
globalOptions: {
|
|
986
|
+
"api-key": { flags: "-k, --api-key <key>", description: "API key (overrides env/config)" },
|
|
987
|
+
output: { flags: "-o, --output <format>", description: "Output format: table, json, csv", default: "table" },
|
|
988
|
+
"base-url": { flags: "--base-url <url>", description: "API base URL" },
|
|
989
|
+
"no-cache": { flags: "--no-cache", description: "Bypass response cache" }
|
|
990
|
+
},
|
|
991
|
+
commands: {
|
|
992
|
+
"token list": {
|
|
993
|
+
description: "List all tracked tokens with market data",
|
|
994
|
+
options: {
|
|
995
|
+
"token-id": { flags: "--token-id <ids>", description: "Filter by token IDs (comma-separated)" }
|
|
996
|
+
},
|
|
997
|
+
example: "tokenomist token list --output json"
|
|
998
|
+
},
|
|
999
|
+
"token search <query>": {
|
|
1000
|
+
description: "Search for tokens by name, symbol, or slug",
|
|
1001
|
+
arguments: { query: { required: true, description: "Search query string" } },
|
|
1002
|
+
example: "tokenomist token search bitcoin --output json"
|
|
1003
|
+
},
|
|
1004
|
+
"unlock list": {
|
|
1005
|
+
description: "List unlock events grouped by token",
|
|
1006
|
+
options: {
|
|
1007
|
+
"min-market-cap": { flags: "--min-market-cap <value>", description: "Minimum market cap" },
|
|
1008
|
+
"min-released-percentage": { flags: "--min-released-percentage <value>", description: "Minimum released percentage" },
|
|
1009
|
+
"min-unlock-date": { flags: "--min-unlock-date <date>", description: "Min unlock date (YYYY-MM-DD)" },
|
|
1010
|
+
"max-unlock-date": { flags: "--max-unlock-date <date>", description: "Max unlock date (YYYY-MM-DD)" },
|
|
1011
|
+
"min-total-unlock-amount": { flags: "--min-total-unlock-amount <value>", description: "Minimum total unlock amount" },
|
|
1012
|
+
"min-value-to-market-cap": { flags: "--min-value-to-market-cap <value>", description: "Min value-to-market-cap ratio" },
|
|
1013
|
+
"standard-allocation": { flags: "--standard-allocation <names>", description: "Filter by allocation (comma-separated)" },
|
|
1014
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1015
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1016
|
+
},
|
|
1017
|
+
example: "tokenomist unlock list --min-market-cap 1000000"
|
|
1018
|
+
},
|
|
1019
|
+
"unlock upcoming": {
|
|
1020
|
+
description: "List next upcoming unlock event per token",
|
|
1021
|
+
options: {
|
|
1022
|
+
start: { flags: "--start <date>", description: "Start date (YYYY-MM-DD)" },
|
|
1023
|
+
end: { flags: "--end <date>", description: "End date (YYYY-MM-DD)" },
|
|
1024
|
+
"standard-allocation": { flags: "--standard-allocation <names>", description: "Filter by allocation (comma-separated)" },
|
|
1025
|
+
"min-market-cap": { flags: "--min-market-cap <value>", description: "Minimum market cap" },
|
|
1026
|
+
"min-released-percentage": { flags: "--min-released-percentage <value>", description: "Minimum released percentage" },
|
|
1027
|
+
"min-total-unlock-amount": { flags: "--min-total-unlock-amount <value>", description: "Minimum total unlock amount" },
|
|
1028
|
+
"min-value-to-market-cap": { flags: "--min-value-to-market-cap <value>", description: "Min value-to-market-cap ratio" },
|
|
1029
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1030
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1031
|
+
},
|
|
1032
|
+
example: "tokenomist unlock upcoming --output json"
|
|
1033
|
+
},
|
|
1034
|
+
"unlock events <tokenSlug>": {
|
|
1035
|
+
description: "Get unlock events for a specific token",
|
|
1036
|
+
arguments: { tokenSlug: { required: true, description: "Token slug (e.g. solana)" } },
|
|
1037
|
+
options: {
|
|
1038
|
+
start: { flags: "--start <date>", description: "Start date (YYYY-MM-DD)" },
|
|
1039
|
+
end: { flags: "--end <date>", description: "End date (YYYY-MM-DD)" },
|
|
1040
|
+
"standard-allocation": { flags: "--standard-allocation <names>", description: "Filter by allocation (comma-separated)" },
|
|
1041
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1042
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1043
|
+
},
|
|
1044
|
+
example: "tokenomist unlock events solana --start 2024-01-01 --end 2024-12-31"
|
|
1045
|
+
},
|
|
1046
|
+
"investor list": {
|
|
1047
|
+
description: "List all investors",
|
|
1048
|
+
options: {
|
|
1049
|
+
"start-latest-funding-date": { flags: "--start-latest-funding-date <date>", description: "Start date filter for latest funding (YYYY-MM-DD)" },
|
|
1050
|
+
"end-latest-funding-date": { flags: "--end-latest-funding-date <date>", description: "End date filter for latest funding (YYYY-MM-DD)" },
|
|
1051
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1052
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1053
|
+
},
|
|
1054
|
+
example: "tokenomist investor list --output json"
|
|
1055
|
+
},
|
|
1056
|
+
"investor detail <investorId>": {
|
|
1057
|
+
description: "Get investor portfolio and funding rounds",
|
|
1058
|
+
arguments: { investorId: { required: true, description: "Investor ID" } },
|
|
1059
|
+
options: {
|
|
1060
|
+
"is-lead-investor": { flags: "--is-lead-investor", description: "Filter to rounds where investor was lead" },
|
|
1061
|
+
"funding-date-min": { flags: "--funding-date-min <date>", description: "Minimum funding date" },
|
|
1062
|
+
"funding-date-max": { flags: "--funding-date-max <date>", description: "Maximum funding date" },
|
|
1063
|
+
"raised-amount-min": { flags: "--raised-amount-min <amount>", description: "Minimum raised amount" },
|
|
1064
|
+
"raised-amount-max": { flags: "--raised-amount-max <amount>", description: "Maximum raised amount" }
|
|
1065
|
+
},
|
|
1066
|
+
example: "tokenomist investor detail 123 --is-lead-investor"
|
|
1067
|
+
},
|
|
1068
|
+
"fundraising <tokenSlug>": {
|
|
1069
|
+
description: "Get fundraising data for a token",
|
|
1070
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1071
|
+
options: {
|
|
1072
|
+
"funding-date-min": { flags: "--funding-date-min <date>", description: "Minimum funding date" },
|
|
1073
|
+
"funding-date-max": { flags: "--funding-date-max <date>", description: "Maximum funding date" },
|
|
1074
|
+
"raised-amount-min": { flags: "--raised-amount-min <amount>", description: "Minimum raised amount" },
|
|
1075
|
+
"raised-amount-max": { flags: "--raised-amount-max <amount>", description: "Maximum raised amount" }
|
|
1076
|
+
},
|
|
1077
|
+
example: "tokenomist fundraising uniswap --output json"
|
|
1078
|
+
},
|
|
1079
|
+
"burn list": {
|
|
1080
|
+
description: "List all tokens with burn data",
|
|
1081
|
+
options: {
|
|
1082
|
+
"token-id": { flags: "--token-id <ids>", description: "Filter by token IDs (comma-separated)" },
|
|
1083
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1084
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1085
|
+
},
|
|
1086
|
+
example: "tokenomist burn list --output json"
|
|
1087
|
+
},
|
|
1088
|
+
"burn detail <tokenSlug>": {
|
|
1089
|
+
description: "Get burn events for a specific token",
|
|
1090
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1091
|
+
options: {
|
|
1092
|
+
start: { flags: "--start <date>", description: "Start date" },
|
|
1093
|
+
end: { flags: "--end <date>", description: "End date" }
|
|
1094
|
+
},
|
|
1095
|
+
example: "tokenomist burn detail solana --start 2024-01-01"
|
|
1096
|
+
},
|
|
1097
|
+
"buyback list": {
|
|
1098
|
+
description: "List all tokens with buyback data",
|
|
1099
|
+
options: {
|
|
1100
|
+
"token-id": { flags: "--token-id <ids>", description: "Filter by token IDs (comma-separated)" },
|
|
1101
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1102
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1103
|
+
},
|
|
1104
|
+
example: "tokenomist buyback list --output json"
|
|
1105
|
+
},
|
|
1106
|
+
"buyback detail <tokenSlug>": {
|
|
1107
|
+
description: "Get buyback events for a specific token",
|
|
1108
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1109
|
+
options: {
|
|
1110
|
+
start: { flags: "--start <date>", description: "Start date" },
|
|
1111
|
+
end: { flags: "--end <date>", description: "End date" }
|
|
1112
|
+
},
|
|
1113
|
+
example: "tokenomist buyback detail solana --output json"
|
|
1114
|
+
},
|
|
1115
|
+
"emission daily <tokenSlug>": {
|
|
1116
|
+
description: "Get daily emission data for a token",
|
|
1117
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1118
|
+
options: {
|
|
1119
|
+
start: { flags: "--start <date>", description: "Start date" },
|
|
1120
|
+
end: { flags: "--end <date>", description: "End date" },
|
|
1121
|
+
"standard-allocation": { flags: "--standard-allocation <names>", description: "Filter by allocation (comma-separated)" },
|
|
1122
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1123
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1124
|
+
},
|
|
1125
|
+
example: "tokenomist emission daily arbitrum --start 2024-08-01 --end 2024-08-31"
|
|
1126
|
+
},
|
|
1127
|
+
"emission weekly <tokenSlug>": {
|
|
1128
|
+
description: "Get weekly emission data for a token",
|
|
1129
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1130
|
+
options: {
|
|
1131
|
+
start: { flags: "--start <date>", description: "Start date" },
|
|
1132
|
+
end: { flags: "--end <date>", description: "End date" },
|
|
1133
|
+
"standard-allocation": { flags: "--standard-allocation <names>", description: "Filter by allocation (comma-separated)" },
|
|
1134
|
+
page: { flags: "--page <number>", description: "Page number" },
|
|
1135
|
+
"page-size": { flags: "--page-size <number>", description: "Items per page" }
|
|
1136
|
+
},
|
|
1137
|
+
example: "tokenomist emission weekly arbitrum --start 2024-01-01 --end 2024-06-30"
|
|
1138
|
+
},
|
|
1139
|
+
"allocation <tokenSlug>": {
|
|
1140
|
+
description: "Get standard allocation distribution for a token",
|
|
1141
|
+
arguments: { tokenSlug: { required: true, description: "Token slug" } },
|
|
1142
|
+
example: "tokenomist allocation solana --output json"
|
|
1143
|
+
},
|
|
1144
|
+
"auth login": {
|
|
1145
|
+
description: "Store API key",
|
|
1146
|
+
options: {
|
|
1147
|
+
"api-key": { flags: "--api-key <key>", description: "API key (prompts if not provided)" }
|
|
1148
|
+
},
|
|
1149
|
+
example: "tokenomist auth login"
|
|
1150
|
+
},
|
|
1151
|
+
"auth logout": {
|
|
1152
|
+
description: "Remove stored API key",
|
|
1153
|
+
example: "tokenomist auth logout"
|
|
1154
|
+
},
|
|
1155
|
+
"auth status": {
|
|
1156
|
+
description: "Show current auth status",
|
|
1157
|
+
example: "tokenomist auth status"
|
|
1158
|
+
},
|
|
1159
|
+
schema: {
|
|
1160
|
+
description: "Print full command schema as JSON",
|
|
1161
|
+
options: {
|
|
1162
|
+
pretty: { flags: "--pretty", description: "Pretty-print with 2-space indent" }
|
|
1163
|
+
},
|
|
1164
|
+
example: "tokenomist schema --pretty"
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
};
|
|
1168
|
+
|
|
1169
|
+
// src/commands/schema.ts
|
|
1170
|
+
var schemaCommand = new Command10("schema").description("Print full command schema as JSON").option("--pretty", "Pretty-print with 2-space indent").action((opts) => {
|
|
1171
|
+
const indent = opts.pretty ? 2 : void 0;
|
|
1172
|
+
console.log(JSON.stringify(schema_default, null, indent));
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
// src/index.ts
|
|
1176
|
+
import { spawn } from "child_process";
|
|
1177
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1178
|
+
import { join as join3 } from "path";
|
|
1179
|
+
import { homedir as homedir3 } from "os";
|
|
1180
|
+
import { fileURLToPath } from "url";
|
|
1181
|
+
var ONE_DAY = 24 * 60 * 60 * 1e3;
|
|
1182
|
+
function showUpdateNotification() {
|
|
1183
|
+
if (process.env.NO_UPDATE_NOTIFIER === "1" || process.env.CI === "1") return;
|
|
1184
|
+
try {
|
|
1185
|
+
const raw = readFileSync3(join3(homedir3(), ".tokenomist", "update-check.json"), "utf-8");
|
|
1186
|
+
const data = JSON.parse(raw);
|
|
1187
|
+
if (data.latest !== data.current && Date.now() - data.checkedAt < ONE_DAY) {
|
|
1188
|
+
process.stderr.write(
|
|
1189
|
+
`
|
|
1190
|
+
Update available: ${data.current} \u2192 ${data.latest} (npm i -g @tokenomist-ai/tokenomist-cli)
|
|
1191
|
+
|
|
1192
|
+
`
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
} catch {
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
function spawnUpdateCheck() {
|
|
1199
|
+
if (process.env.NO_UPDATE_NOTIFIER === "1" || process.env.CI === "1") return;
|
|
1200
|
+
try {
|
|
1201
|
+
const script = fileURLToPath(new URL("./update-check.js", import.meta.url));
|
|
1202
|
+
spawn("node", [script], { detached: true, stdio: "ignore" }).unref();
|
|
1203
|
+
} catch {
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
var program = new Command11();
|
|
1207
|
+
program.name("tokenomist").description(
|
|
1208
|
+
"CLI tool for the Tokenomist API \u2014 query token unlocks, emissions, fundraising, burns, and buybacks"
|
|
1209
|
+
).version("0.1.0").option("-k, --api-key <key>", "API key (overrides env/config)").option("-o, --output <format>", "Output format: table, json, csv", "table").option("--base-url <url>", "API base URL").option("--no-cache", "Bypass response cache");
|
|
1210
|
+
program.addCommand(tokenCommand);
|
|
1211
|
+
program.addCommand(unlockCommand);
|
|
1212
|
+
program.addCommand(investorCommand);
|
|
1213
|
+
program.addCommand(fundraisingCommand);
|
|
1214
|
+
program.addCommand(burnCommand);
|
|
1215
|
+
program.addCommand(buybackCommand);
|
|
1216
|
+
program.addCommand(emissionCommand);
|
|
1217
|
+
program.addCommand(allocationCommand);
|
|
1218
|
+
program.addCommand(authCommand);
|
|
1219
|
+
program.addCommand(schemaCommand);
|
|
1220
|
+
showUpdateNotification();
|
|
1221
|
+
program.parseAsync(process.argv).then(() => {
|
|
1222
|
+
spawnUpdateCheck();
|
|
1223
|
+
}).catch((err) => {
|
|
1224
|
+
if (err instanceof ApiError) {
|
|
1225
|
+
if (err.retriesExhausted) {
|
|
1226
|
+
if (err.detail) console.error(`Detail: ${err.detail}`);
|
|
1227
|
+
} else {
|
|
1228
|
+
console.error(`${err.code}: ${err.message}`);
|
|
1229
|
+
if (err.detail) console.error(`Detail: ${err.detail}`);
|
|
1230
|
+
}
|
|
1231
|
+
} else {
|
|
1232
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
1233
|
+
}
|
|
1234
|
+
spawnUpdateCheck();
|
|
1235
|
+
process.exit(1);
|
|
1236
|
+
});
|
|
1237
|
+
//# sourceMappingURL=index.js.map
|