tokalytics 2.0.0 → 2.0.6
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 +12 -0
- package/package.json +4 -3
- package/scripts/postinstall.js +67 -8
package/README.md
CHANGED
|
@@ -41,6 +41,18 @@ npm run start # compila e em seguida executa ./tokalytics
|
|
|
41
41
|
|
|
42
42
|
Na primeira execução o app sobe o **servidor HTTP na porta `3456`** e o ícone na barra de menus. Use **«Abrir Dashboard»** no menu ou acesse [http://127.0.0.1:3456](http://127.0.0.1:3456).
|
|
43
43
|
|
|
44
|
+
### Instalação global pelo npm
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install -g tokalytics
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Instalação a partir do repositório (sempre o `postinstall` da branch atual):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install -g "github:kaicmurilo/Tokalytics"
|
|
54
|
+
```
|
|
55
|
+
|
|
44
56
|
## Dashboard
|
|
45
57
|
|
|
46
58
|
Interface web em abas:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tokalytics",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.6",
|
|
4
4
|
"description": "Veja para onde vão seus tokens do Claude Code, Gemini CLI, Cursor e Codex. Reescrito em Go como Tokalytics.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"tokalytics": "bin/run.js"
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"postinstall": "node scripts/postinstall.js",
|
|
14
14
|
"start": "go build -o tokalytics main.go && ./tokalytics",
|
|
15
15
|
"dev": "go run main.go",
|
|
16
|
-
"build": "go build -o tokalytics main.go"
|
|
16
|
+
"build": "go build -o tokalytics main.go",
|
|
17
|
+
"release:npm": "npm publish --access public"
|
|
17
18
|
},
|
|
18
19
|
"keywords": [
|
|
19
20
|
"claude",
|
|
@@ -31,6 +32,6 @@
|
|
|
31
32
|
"license": "MIT",
|
|
32
33
|
"repository": {
|
|
33
34
|
"type": "git",
|
|
34
|
-
"url": "git+https://github.com/kaicmurilo/
|
|
35
|
+
"url": "git+https://github.com/kaicmurilo/Tokalytics.git"
|
|
35
36
|
}
|
|
36
37
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -4,9 +4,18 @@
|
|
|
4
4
|
const https = require('https');
|
|
5
5
|
const fs = require('fs');
|
|
6
6
|
const path = require('path');
|
|
7
|
-
const { execSync } = require('child_process');
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
9
|
+
const pkgVersion = (() => {
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version || '?';
|
|
12
|
+
} catch {
|
|
13
|
+
return '?';
|
|
14
|
+
}
|
|
15
|
+
})();
|
|
16
|
+
|
|
17
|
+
// Repositório público no GitHub (nome real: Tokalytics)
|
|
18
|
+
const REPO = 'kaicmurilo/Tokalytics';
|
|
10
19
|
const BIN_DIR = path.join(__dirname, '..', 'bin');
|
|
11
20
|
const BIN_PATH = path.join(BIN_DIR, process.platform === 'win32' ? 'tokalytics.exe' : 'tokalytics');
|
|
12
21
|
|
|
@@ -30,9 +39,17 @@ function getPlatformBinary() {
|
|
|
30
39
|
return name;
|
|
31
40
|
}
|
|
32
41
|
|
|
33
|
-
function fetchJson(url) {
|
|
42
|
+
function fetchJson(url, extraHeaders = {}) {
|
|
34
43
|
return new Promise((resolve, reject) => {
|
|
35
|
-
const
|
|
44
|
+
const headers = {
|
|
45
|
+
'User-Agent': 'tokalytics-installer',
|
|
46
|
+
Accept: 'application/vnd.github+json',
|
|
47
|
+
...extraHeaders,
|
|
48
|
+
};
|
|
49
|
+
if (process.env.GITHUB_TOKEN) {
|
|
50
|
+
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
51
|
+
}
|
|
52
|
+
const options = { headers };
|
|
36
53
|
https.get(url, options, (res) => {
|
|
37
54
|
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
38
55
|
return fetchJson(res.headers.location).then(resolve).catch(reject);
|
|
@@ -40,8 +57,24 @@ function fetchJson(url) {
|
|
|
40
57
|
let data = '';
|
|
41
58
|
res.on('data', (chunk) => (data += chunk));
|
|
42
59
|
res.on('end', () => {
|
|
43
|
-
|
|
44
|
-
|
|
60
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
61
|
+
try {
|
|
62
|
+
const j = JSON.parse(data);
|
|
63
|
+
if (j && j.message) {
|
|
64
|
+
return reject(
|
|
65
|
+
new Error(`GitHub HTTP ${res.statusCode}: ${j.message}`)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
} catch (_) {
|
|
69
|
+
/* fallthrough */
|
|
70
|
+
}
|
|
71
|
+
return reject(new Error(`GitHub HTTP ${res.statusCode}`));
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
resolve(JSON.parse(data));
|
|
75
|
+
} catch {
|
|
76
|
+
reject(new Error('Resposta inválida da API do GitHub'));
|
|
77
|
+
}
|
|
45
78
|
});
|
|
46
79
|
}).on('error', reject);
|
|
47
80
|
});
|
|
@@ -70,14 +103,40 @@ function downloadFile(url, dest) {
|
|
|
70
103
|
});
|
|
71
104
|
}
|
|
72
105
|
|
|
106
|
+
function assertReleasePayload(release) {
|
|
107
|
+
if (!release || typeof release !== 'object') {
|
|
108
|
+
throw new Error('Resposta inválida da API do GitHub (corpo vazio).');
|
|
109
|
+
}
|
|
110
|
+
if (release.message && !release.tag_name) {
|
|
111
|
+
let hint = '';
|
|
112
|
+
if (/rate limit/i.test(release.message) && !process.env.GITHUB_TOKEN) {
|
|
113
|
+
hint = ' Defina GITHUB_TOKEN no ambiente para aumentar o limite.';
|
|
114
|
+
} else if (/not found/i.test(release.message)) {
|
|
115
|
+
hint =
|
|
116
|
+
` Confirme que https://github.com/${REPO} existe, é público e tem pelo menos uma release com binários anexados.`;
|
|
117
|
+
}
|
|
118
|
+
throw new Error(`GitHub: ${release.message}${hint}`);
|
|
119
|
+
}
|
|
120
|
+
if (!release.tag_name) {
|
|
121
|
+
throw new Error('Release sem tag_name; verifique se existe release no repositório.');
|
|
122
|
+
}
|
|
123
|
+
if (!Array.isArray(release.assets)) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
'Resposta da API sem lista de assets. Possível rate limit ou repositório/release inexistente.'
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
73
130
|
async function install() {
|
|
74
|
-
console.log(
|
|
131
|
+
console.log(`Tokalytics installer v${pkgVersion}: buscando última versão...`);
|
|
75
132
|
|
|
76
133
|
const release = await fetchJson(`https://api.github.com/repos/${REPO}/releases/latest`);
|
|
134
|
+
assertReleasePayload(release);
|
|
77
135
|
const version = release.tag_name;
|
|
78
136
|
const binaryName = getPlatformBinary();
|
|
79
137
|
|
|
80
|
-
const
|
|
138
|
+
const assets = Array.isArray(release.assets) ? release.assets : [];
|
|
139
|
+
const asset = assets.find((a) => a && a.name === binaryName);
|
|
81
140
|
if (!asset) {
|
|
82
141
|
throw new Error(`Binário "${binaryName}" não encontrado na release ${version}`);
|
|
83
142
|
}
|