ccburn 0.1.4 → 0.1.5
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 -21
- package/README.md +51 -51
- package/bin/ccburn.js +36 -36
- package/package.json +50 -50
- package/scripts/postinstall.js +106 -106
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 JuanjoFuchs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JuanjoFuchs
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
# ccburn
|
|
2
|
-
|
|
3
|
-
Terminal-based Claude Code usage limit visualizer with real-time burn-up charts.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# Run directly with npx
|
|
9
|
-
npx ccburn
|
|
10
|
-
|
|
11
|
-
# Or install globally
|
|
12
|
-
npm install -g ccburn
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Usage
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
ccburn # Session limit TUI
|
|
19
|
-
ccburn weekly # Weekly limit
|
|
20
|
-
ccburn --compact # Single line for status bars
|
|
21
|
-
ccburn --json # JSON output for automation
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Features
|
|
25
|
-
|
|
26
|
-
- **Real-time burn-up charts** with budget pace line
|
|
27
|
-
- **Pace indicators**: 🧊 behind pace, 🔥 on pace, 🚨 burning too hot
|
|
28
|
-
- **Session, Weekly, and Weekly-Sonnet limits**
|
|
29
|
-
- **Compact mode** for tmux/status bars
|
|
30
|
-
- **JSON output** for scripting
|
|
31
|
-
|
|
32
|
-
## Requirements
|
|
33
|
-
|
|
34
|
-
- Claude Code installed with valid credentials
|
|
35
|
-
|
|
36
|
-
## Alternative Installation
|
|
37
|
-
|
|
38
|
-
If npm installation fails, you can install via pip:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
pip install ccburn
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Links
|
|
45
|
-
|
|
46
|
-
- [GitHub](https://github.com/JuanjoFuchs/ccburn)
|
|
47
|
-
- [PyPI](https://pypi.org/project/ccburn/)
|
|
48
|
-
|
|
49
|
-
## License
|
|
50
|
-
|
|
51
|
-
MIT
|
|
1
|
+
# ccburn
|
|
2
|
+
|
|
3
|
+
Terminal-based Claude Code usage limit visualizer with real-time burn-up charts.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Run directly with npx
|
|
9
|
+
npx ccburn
|
|
10
|
+
|
|
11
|
+
# Or install globally
|
|
12
|
+
npm install -g ccburn
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
ccburn # Session limit TUI
|
|
19
|
+
ccburn weekly # Weekly limit
|
|
20
|
+
ccburn --compact # Single line for status bars
|
|
21
|
+
ccburn --json # JSON output for automation
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
- **Real-time burn-up charts** with budget pace line
|
|
27
|
+
- **Pace indicators**: 🧊 behind pace, 🔥 on pace, 🚨 burning too hot
|
|
28
|
+
- **Session, Weekly, and Weekly-Sonnet limits**
|
|
29
|
+
- **Compact mode** for tmux/status bars
|
|
30
|
+
- **JSON output** for scripting
|
|
31
|
+
|
|
32
|
+
## Requirements
|
|
33
|
+
|
|
34
|
+
- Claude Code installed with valid credentials
|
|
35
|
+
|
|
36
|
+
## Alternative Installation
|
|
37
|
+
|
|
38
|
+
If npm installation fails, you can install via pip:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install ccburn
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Links
|
|
45
|
+
|
|
46
|
+
- [GitHub](https://github.com/JuanjoFuchs/ccburn)
|
|
47
|
+
- [PyPI](https://pypi.org/project/ccburn/)
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
MIT
|
package/bin/ccburn.js
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { spawn } = require('child_process');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const fs = require('fs');
|
|
6
|
-
|
|
7
|
-
const binDir = path.join(__dirname);
|
|
8
|
-
const platform = process.platform;
|
|
9
|
-
const ext = platform === 'win32' ? '.exe' : '';
|
|
10
|
-
|
|
11
|
-
// Find the binary
|
|
12
|
-
const binaryName = fs.readdirSync(binDir).find(f =>
|
|
13
|
-
f.startsWith('ccburn-') && f.endsWith(ext) && !f.endsWith('.js')
|
|
14
|
-
);
|
|
15
|
-
|
|
16
|
-
if (!binaryName) {
|
|
17
|
-
console.error('Error: ccburn binary not found. Try reinstalling: npm install -g ccburn');
|
|
18
|
-
process.exit(1);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const binaryPath = path.join(binDir, binaryName);
|
|
22
|
-
|
|
23
|
-
// Spawn the binary with all arguments
|
|
24
|
-
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
25
|
-
stdio: 'inherit',
|
|
26
|
-
windowsHide: true
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
child.on('error', (err) => {
|
|
30
|
-
console.error(`Error executing ccburn: ${err.message}`);
|
|
31
|
-
process.exit(1);
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
child.on('close', (code) => {
|
|
35
|
-
process.exit(code || 0);
|
|
36
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
|
|
7
|
+
const binDir = path.join(__dirname);
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
const ext = platform === 'win32' ? '.exe' : '';
|
|
10
|
+
|
|
11
|
+
// Find the binary
|
|
12
|
+
const binaryName = fs.readdirSync(binDir).find(f =>
|
|
13
|
+
f.startsWith('ccburn-') && f.endsWith(ext) && !f.endsWith('.js')
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
if (!binaryName) {
|
|
17
|
+
console.error('Error: ccburn binary not found. Try reinstalling: npm install -g ccburn');
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
22
|
+
|
|
23
|
+
// Spawn the binary with all arguments
|
|
24
|
+
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
25
|
+
stdio: 'inherit',
|
|
26
|
+
windowsHide: true
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
child.on('error', (err) => {
|
|
30
|
+
console.error(`Error executing ccburn: ${err.message}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
child.on('close', (code) => {
|
|
35
|
+
process.exit(code || 0);
|
|
36
|
+
});
|
package/package.json
CHANGED
|
@@ -1,50 +1,50 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ccburn",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Terminal-based Claude Code usage limit visualizer with real-time burn-up charts",
|
|
5
|
-
"author": "JuanjoFuchs",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/JuanjoFuchs/ccburn.git"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://github.com/JuanjoFuchs/ccburn#readme",
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/JuanjoFuchs/ccburn/issues"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"claude",
|
|
17
|
-
"anthropic",
|
|
18
|
-
"usage",
|
|
19
|
-
"monitoring",
|
|
20
|
-
"tui",
|
|
21
|
-
"cli",
|
|
22
|
-
"terminal",
|
|
23
|
-
"burn-up",
|
|
24
|
-
"chart"
|
|
25
|
-
],
|
|
26
|
-
"bin": {
|
|
27
|
-
"ccburn": "./bin/ccburn.js"
|
|
28
|
-
},
|
|
29
|
-
"scripts": {
|
|
30
|
-
"postinstall": "node scripts/postinstall.js"
|
|
31
|
-
},
|
|
32
|
-
"files": [
|
|
33
|
-
"bin/",
|
|
34
|
-
"scripts/",
|
|
35
|
-
"README.md",
|
|
36
|
-
"LICENSE"
|
|
37
|
-
],
|
|
38
|
-
"engines": {
|
|
39
|
-
"node": ">=16.0.0"
|
|
40
|
-
},
|
|
41
|
-
"os": [
|
|
42
|
-
"darwin",
|
|
43
|
-
"linux",
|
|
44
|
-
"win32"
|
|
45
|
-
],
|
|
46
|
-
"cpu": [
|
|
47
|
-
"x64",
|
|
48
|
-
"arm64"
|
|
49
|
-
]
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "ccburn",
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Terminal-based Claude Code usage limit visualizer with real-time burn-up charts",
|
|
5
|
+
"author": "JuanjoFuchs",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/JuanjoFuchs/ccburn.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/JuanjoFuchs/ccburn#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/JuanjoFuchs/ccburn/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"claude",
|
|
17
|
+
"anthropic",
|
|
18
|
+
"usage",
|
|
19
|
+
"monitoring",
|
|
20
|
+
"tui",
|
|
21
|
+
"cli",
|
|
22
|
+
"terminal",
|
|
23
|
+
"burn-up",
|
|
24
|
+
"chart"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"ccburn": "./bin/ccburn.js"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"postinstall": "node scripts/postinstall.js"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"bin/",
|
|
34
|
+
"scripts/",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE"
|
|
37
|
+
],
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=16.0.0"
|
|
40
|
+
},
|
|
41
|
+
"os": [
|
|
42
|
+
"darwin",
|
|
43
|
+
"linux",
|
|
44
|
+
"win32"
|
|
45
|
+
],
|
|
46
|
+
"cpu": [
|
|
47
|
+
"x64",
|
|
48
|
+
"arm64"
|
|
49
|
+
]
|
|
50
|
+
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,106 +1,106 @@
|
|
|
1
|
-
const https = require('https');
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
|
|
5
|
-
const REPO = 'JuanjoFuchs/ccburn';
|
|
6
|
-
const PACKAGE_VERSION = require('../package.json').version;
|
|
7
|
-
|
|
8
|
-
const PLATFORM_MAP = {
|
|
9
|
-
'win32-x64': 'windows-x64.exe',
|
|
10
|
-
'linux-x64': 'linux-x64',
|
|
11
|
-
'darwin-x64': 'darwin-x64',
|
|
12
|
-
'darwin-arm64': 'darwin-arm64'
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
async function main() {
|
|
16
|
-
const platform = process.platform;
|
|
17
|
-
const arch = process.arch;
|
|
18
|
-
const key = `${platform}-${arch}`;
|
|
19
|
-
|
|
20
|
-
const suffix = PLATFORM_MAP[key];
|
|
21
|
-
if (!suffix) {
|
|
22
|
-
console.error(`Unsupported platform: ${platform}-${arch}`);
|
|
23
|
-
console.error('Supported platforms: win32-x64, linux-x64, darwin-x64, darwin-arm64');
|
|
24
|
-
console.error('');
|
|
25
|
-
console.error('You can still use ccburn via pip: pip install ccburn');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const binaryName = `ccburn-${PACKAGE_VERSION}-${suffix}`;
|
|
30
|
-
const url = `https://github.com/${REPO}/releases/download/v${PACKAGE_VERSION}/${binaryName}`;
|
|
31
|
-
const binDir = path.join(__dirname, '..', 'bin');
|
|
32
|
-
const binaryPath = path.join(binDir, binaryName);
|
|
33
|
-
|
|
34
|
-
// Skip if binary already exists
|
|
35
|
-
if (fs.existsSync(binaryPath)) {
|
|
36
|
-
console.log(`ccburn binary already exists at ${binaryPath}`);
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
console.log(`Downloading ccburn ${PACKAGE_VERSION} for ${platform}-${arch}...`);
|
|
41
|
-
console.log(`URL: ${url}`);
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
await downloadFile(url, binaryPath);
|
|
45
|
-
|
|
46
|
-
// Make executable on Unix
|
|
47
|
-
if (platform !== 'win32') {
|
|
48
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
console.log(`Successfully installed ccburn to ${binaryPath}`);
|
|
52
|
-
} catch (err) {
|
|
53
|
-
console.error(`Failed to download ccburn: ${err.message}`);
|
|
54
|
-
console.error('');
|
|
55
|
-
console.error('You can still use ccburn via pip: pip install ccburn');
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function downloadFile(url, dest) {
|
|
61
|
-
return new Promise((resolve, reject) => {
|
|
62
|
-
const file = fs.createWriteStream(dest);
|
|
63
|
-
|
|
64
|
-
const request = (url) => {
|
|
65
|
-
https.get(url, (response) => {
|
|
66
|
-
// Handle redirects (GitHub releases use redirects)
|
|
67
|
-
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
68
|
-
request(response.headers.location);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (response.statusCode !== 200) {
|
|
73
|
-
fs.unlinkSync(dest);
|
|
74
|
-
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const totalBytes = parseInt(response.headers['content-length'], 10);
|
|
79
|
-
let downloadedBytes = 0;
|
|
80
|
-
|
|
81
|
-
response.on('data', (chunk) => {
|
|
82
|
-
downloadedBytes += chunk.length;
|
|
83
|
-
if (totalBytes) {
|
|
84
|
-
const percent = Math.round((downloadedBytes / totalBytes) * 100);
|
|
85
|
-
process.stdout.write(`\rDownloading: ${percent}%`);
|
|
86
|
-
}
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
response.pipe(file);
|
|
90
|
-
|
|
91
|
-
file.on('finish', () => {
|
|
92
|
-
file.close();
|
|
93
|
-
console.log(''); // New line after progress
|
|
94
|
-
resolve();
|
|
95
|
-
});
|
|
96
|
-
}).on('error', (err) => {
|
|
97
|
-
fs.unlinkSync(dest);
|
|
98
|
-
reject(err);
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
request(url);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
main().catch(console.error);
|
|
1
|
+
const https = require('https');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
const REPO = 'JuanjoFuchs/ccburn';
|
|
6
|
+
const PACKAGE_VERSION = require('../package.json').version;
|
|
7
|
+
|
|
8
|
+
const PLATFORM_MAP = {
|
|
9
|
+
'win32-x64': 'windows-x64.exe',
|
|
10
|
+
'linux-x64': 'linux-x64',
|
|
11
|
+
'darwin-x64': 'darwin-x64',
|
|
12
|
+
'darwin-arm64': 'darwin-arm64'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
async function main() {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
const arch = process.arch;
|
|
18
|
+
const key = `${platform}-${arch}`;
|
|
19
|
+
|
|
20
|
+
const suffix = PLATFORM_MAP[key];
|
|
21
|
+
if (!suffix) {
|
|
22
|
+
console.error(`Unsupported platform: ${platform}-${arch}`);
|
|
23
|
+
console.error('Supported platforms: win32-x64, linux-x64, darwin-x64, darwin-arm64');
|
|
24
|
+
console.error('');
|
|
25
|
+
console.error('You can still use ccburn via pip: pip install ccburn');
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const binaryName = `ccburn-${PACKAGE_VERSION}-${suffix}`;
|
|
30
|
+
const url = `https://github.com/${REPO}/releases/download/v${PACKAGE_VERSION}/${binaryName}`;
|
|
31
|
+
const binDir = path.join(__dirname, '..', 'bin');
|
|
32
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
33
|
+
|
|
34
|
+
// Skip if binary already exists
|
|
35
|
+
if (fs.existsSync(binaryPath)) {
|
|
36
|
+
console.log(`ccburn binary already exists at ${binaryPath}`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
console.log(`Downloading ccburn ${PACKAGE_VERSION} for ${platform}-${arch}...`);
|
|
41
|
+
console.log(`URL: ${url}`);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await downloadFile(url, binaryPath);
|
|
45
|
+
|
|
46
|
+
// Make executable on Unix
|
|
47
|
+
if (platform !== 'win32') {
|
|
48
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(`Successfully installed ccburn to ${binaryPath}`);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(`Failed to download ccburn: ${err.message}`);
|
|
54
|
+
console.error('');
|
|
55
|
+
console.error('You can still use ccburn via pip: pip install ccburn');
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function downloadFile(url, dest) {
|
|
61
|
+
return new Promise((resolve, reject) => {
|
|
62
|
+
const file = fs.createWriteStream(dest);
|
|
63
|
+
|
|
64
|
+
const request = (url) => {
|
|
65
|
+
https.get(url, (response) => {
|
|
66
|
+
// Handle redirects (GitHub releases use redirects)
|
|
67
|
+
if (response.statusCode === 301 || response.statusCode === 302) {
|
|
68
|
+
request(response.headers.location);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (response.statusCode !== 200) {
|
|
73
|
+
fs.unlinkSync(dest);
|
|
74
|
+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const totalBytes = parseInt(response.headers['content-length'], 10);
|
|
79
|
+
let downloadedBytes = 0;
|
|
80
|
+
|
|
81
|
+
response.on('data', (chunk) => {
|
|
82
|
+
downloadedBytes += chunk.length;
|
|
83
|
+
if (totalBytes) {
|
|
84
|
+
const percent = Math.round((downloadedBytes / totalBytes) * 100);
|
|
85
|
+
process.stdout.write(`\rDownloading: ${percent}%`);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
response.pipe(file);
|
|
90
|
+
|
|
91
|
+
file.on('finish', () => {
|
|
92
|
+
file.close();
|
|
93
|
+
console.log(''); // New line after progress
|
|
94
|
+
resolve();
|
|
95
|
+
});
|
|
96
|
+
}).on('error', (err) => {
|
|
97
|
+
fs.unlinkSync(dest);
|
|
98
|
+
reject(err);
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
request(url);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
main().catch(console.error);
|