sendbird-ai-league 1.0.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/dist/index.js +220 -0
- package/package.json +22 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const readline = __importStar(require("readline"));
|
|
41
|
+
const https = __importStar(require("https"));
|
|
42
|
+
const http = __importStar(require("http"));
|
|
43
|
+
// Claude pricing per million tokens
|
|
44
|
+
const PRICING = {
|
|
45
|
+
'claude-opus': { input: 15, output: 75, cacheRead: 1.5, cacheWrite: 18.75 },
|
|
46
|
+
'claude-sonnet': { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
47
|
+
'claude-haiku': { input: 0.8, output: 4, cacheRead: 0.08, cacheWrite: 1 },
|
|
48
|
+
};
|
|
49
|
+
function getModelPricing(model) {
|
|
50
|
+
if (model?.includes('opus'))
|
|
51
|
+
return PRICING['claude-opus'];
|
|
52
|
+
if (model?.includes('haiku'))
|
|
53
|
+
return PRICING['claude-haiku'];
|
|
54
|
+
return PRICING['claude-sonnet'];
|
|
55
|
+
}
|
|
56
|
+
function readClaudeUsage() {
|
|
57
|
+
const claudeDir = path.join(os.homedir(), '.claude', 'projects');
|
|
58
|
+
const totals = {
|
|
59
|
+
input_tokens: 0,
|
|
60
|
+
output_tokens: 0,
|
|
61
|
+
cache_read_tokens: 0,
|
|
62
|
+
cache_creation_tokens: 0,
|
|
63
|
+
cost_usd: 0,
|
|
64
|
+
};
|
|
65
|
+
if (!fs.existsSync(claudeDir)) {
|
|
66
|
+
return totals;
|
|
67
|
+
}
|
|
68
|
+
const projectDirs = fs.readdirSync(claudeDir);
|
|
69
|
+
for (const projectDir of projectDirs) {
|
|
70
|
+
const projectPath = path.join(claudeDir, projectDir);
|
|
71
|
+
try {
|
|
72
|
+
if (!fs.statSync(projectPath).isDirectory())
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
let files;
|
|
79
|
+
try {
|
|
80
|
+
files = fs.readdirSync(projectPath).filter((f) => f.endsWith('.jsonl'));
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
for (const file of files) {
|
|
86
|
+
const filePath = path.join(projectPath, file);
|
|
87
|
+
let content;
|
|
88
|
+
try {
|
|
89
|
+
content = fs.readFileSync(filePath, 'utf-8');
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
const lines = content.split('\n').filter(Boolean);
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
try {
|
|
97
|
+
const data = JSON.parse(line);
|
|
98
|
+
const usage = data?.message?.usage ?? data?.usage;
|
|
99
|
+
const model = data?.message?.model ?? data?.model ?? '';
|
|
100
|
+
if (!usage)
|
|
101
|
+
continue;
|
|
102
|
+
const pricing = getModelPricing(model);
|
|
103
|
+
const input = Number(usage.input_tokens ?? 0);
|
|
104
|
+
const output = Number(usage.output_tokens ?? 0);
|
|
105
|
+
const cacheRead = Number(usage.cache_read_input_tokens ?? 0);
|
|
106
|
+
const cacheWrite = Number(usage.cache_creation_input_tokens ?? 0);
|
|
107
|
+
totals.input_tokens += input;
|
|
108
|
+
totals.output_tokens += output;
|
|
109
|
+
totals.cache_read_tokens += cacheRead;
|
|
110
|
+
totals.cache_creation_tokens += cacheWrite;
|
|
111
|
+
totals.cost_usd +=
|
|
112
|
+
(input * pricing.input +
|
|
113
|
+
output * pricing.output +
|
|
114
|
+
cacheRead * pricing.cacheRead +
|
|
115
|
+
cacheWrite * pricing.cacheWrite) /
|
|
116
|
+
1000000;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
// skip malformed lines
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return totals;
|
|
125
|
+
}
|
|
126
|
+
function prompt(question) {
|
|
127
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
128
|
+
return new Promise((resolve) => {
|
|
129
|
+
rl.question(question, (answer) => {
|
|
130
|
+
rl.close();
|
|
131
|
+
resolve(answer.trim());
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function postJson(url, body) {
|
|
136
|
+
return new Promise((resolve, reject) => {
|
|
137
|
+
const payload = JSON.stringify(body);
|
|
138
|
+
const urlObj = new URL(url);
|
|
139
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
140
|
+
const transport = isHttps ? https : http;
|
|
141
|
+
const options = {
|
|
142
|
+
hostname: urlObj.hostname,
|
|
143
|
+
port: urlObj.port || (isHttps ? 443 : 80),
|
|
144
|
+
path: urlObj.pathname + urlObj.search,
|
|
145
|
+
method: 'POST',
|
|
146
|
+
headers: {
|
|
147
|
+
'Content-Type': 'application/json',
|
|
148
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
const req = transport.request(options, (res) => {
|
|
152
|
+
let data = '';
|
|
153
|
+
res.on('data', (chunk) => { data += chunk; });
|
|
154
|
+
res.on('end', () => {
|
|
155
|
+
try {
|
|
156
|
+
resolve({ ok: (res.statusCode ?? 0) < 400, status: res.statusCode ?? 0, data: JSON.parse(data) });
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
resolve({ ok: false, status: res.statusCode ?? 0, data: {} });
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
req.on('error', reject);
|
|
164
|
+
req.write(payload);
|
|
165
|
+
req.end();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
const API_URL = process.env.LEADERBOARD_API_URL ?? 'https://sendbird-ai-native-leaderboard.vercel.app/api/submit';
|
|
169
|
+
const LEADERBOARD_URL = process.env.LEADERBOARD_URL ?? 'https://sendbird-ai-native-leaderboard.vercel.app/';
|
|
170
|
+
async function main() {
|
|
171
|
+
console.log('\nSendbird AI Native Leaderboard\n');
|
|
172
|
+
console.log('Reading your Claude Code usage data...');
|
|
173
|
+
const usage = readClaudeUsage();
|
|
174
|
+
const total = usage.input_tokens + usage.output_tokens + usage.cache_read_tokens + usage.cache_creation_tokens;
|
|
175
|
+
if (total === 0) {
|
|
176
|
+
console.log('No Claude Code usage found. Use Claude Code first, then run this command again.');
|
|
177
|
+
process.exit(0);
|
|
178
|
+
}
|
|
179
|
+
console.log('\nFound usage data:');
|
|
180
|
+
console.log(` Input tokens: ${usage.input_tokens.toLocaleString()}`);
|
|
181
|
+
console.log(` Output tokens: ${usage.output_tokens.toLocaleString()}`);
|
|
182
|
+
console.log(` Cache read tokens: ${usage.cache_read_tokens.toLocaleString()}`);
|
|
183
|
+
console.log(` Cache creation tokens: ${usage.cache_creation_tokens.toLocaleString()}`);
|
|
184
|
+
console.log(` Total tokens: ${total.toLocaleString()}`);
|
|
185
|
+
console.log(` Estimated cost: $${usage.cost_usd.toFixed(4)}\n`);
|
|
186
|
+
const name = await prompt('Enter your display name (English): ');
|
|
187
|
+
if (!name) {
|
|
188
|
+
console.log('Name is required.');
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
const email = await prompt('Enter your @sendbird.com email: ');
|
|
192
|
+
if (!email.endsWith('@sendbird.com')) {
|
|
193
|
+
console.log('Only @sendbird.com email addresses are allowed.');
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
console.log('\nSubmitting to leaderboard...');
|
|
197
|
+
try {
|
|
198
|
+
const result = await postJson(API_URL, {
|
|
199
|
+
name,
|
|
200
|
+
email,
|
|
201
|
+
input_tokens: usage.input_tokens,
|
|
202
|
+
output_tokens: usage.output_tokens,
|
|
203
|
+
cache_read_tokens: usage.cache_read_tokens,
|
|
204
|
+
cache_creation_tokens: usage.cache_creation_tokens,
|
|
205
|
+
cost_usd: usage.cost_usd,
|
|
206
|
+
});
|
|
207
|
+
if (!result.ok) {
|
|
208
|
+
console.log(`Error: ${result.data?.error ?? 'Unknown error'}`);
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
console.log('\nSubmitted successfully!');
|
|
212
|
+
console.log(` You are ranked #${result.data.rank} today!`);
|
|
213
|
+
console.log(`\nView the leaderboard: ${LEADERBOARD_URL}\n`);
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
console.log('Failed to connect to the leaderboard server. Please try again later.');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
main();
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sendbird-ai-league",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Submit your Claude Code token usage to the Sendbird AI Native Leaderboard",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"sendbird-ai-league": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"dev": "ts-node src/index.ts"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "^20.0.0",
|
|
19
|
+
"ts-node": "^10.9.2",
|
|
20
|
+
"typescript": "^5.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|