codebuff-cli 1.0.14 → 1.0.16
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 +58 -202
- package/http.js +176 -0
- package/index.js +592 -0
- package/package.json +31 -78
- package/postinstall.js +34 -0
- package/LICENSE +0 -202
- package/README.zh-CN.md +0 -251
- package/cli/README.md +0 -84
- package/cli/bin/codebuff.cjs +0 -165
- package/cli/scripts/download-binary.cjs +0 -164
package/cli/README.md
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# @codebuff/cli
|
|
2
|
-
|
|
3
|
-
A Terminal User Interface (TUI) package built with OpenTUI and React.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun install
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Development
|
|
12
|
-
|
|
13
|
-
Run the TUI in development mode:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
bun run dev
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Testing
|
|
20
|
-
|
|
21
|
-
Run the test suite:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
bun test
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### Interactive E2E Testing
|
|
28
|
-
|
|
29
|
-
For testing interactive CLI features, install tmux:
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
# macOS
|
|
33
|
-
brew install tmux
|
|
34
|
-
|
|
35
|
-
# Ubuntu/Debian
|
|
36
|
-
sudo apt-get install tmux
|
|
37
|
-
|
|
38
|
-
# Windows (via WSL)
|
|
39
|
-
wsl --install
|
|
40
|
-
sudo apt-get install tmux
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Then run the proof-of-concept:
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
bun run test:tmux-poc
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
**Note:** When sending input to the CLI via tmux, you must use bracketed paste mode. Standard `send-keys` drops characters.
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
# ❌ Broken: tmux send-keys -t session "hello"
|
|
53
|
-
# ✅ Works: tmux send-keys -t session $'\e[200~hello\e[201~'
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
See [tmux.knowledge.md](tmux.knowledge.md) for comprehensive tmux documentation and [src/__tests__/README.md](src/__tests__/README.md) for testing documentation.
|
|
57
|
-
|
|
58
|
-
## Build
|
|
59
|
-
|
|
60
|
-
Build the package:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
bun run build
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Run
|
|
67
|
-
|
|
68
|
-
Run the built TUI:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
bun run start
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
Or use the binary directly:
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
codebuff-tui
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Features
|
|
81
|
-
|
|
82
|
-
- Built with OpenTUI for modern terminal interfaces
|
|
83
|
-
- Uses React for declarative component-based UI
|
|
84
|
-
- TypeScript support out of the box
|
package/cli/bin/codebuff.cjs
DELETED
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const { spawn } = require('child_process');
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
const os = require('os');
|
|
6
|
-
const https = require('https');
|
|
7
|
-
const http = require('http');
|
|
8
|
-
|
|
9
|
-
const BINARY_NAME = 'codebuff';
|
|
10
|
-
const REPO = 'Marcus-Mok-GH/codebuff-cli';
|
|
11
|
-
|
|
12
|
-
const moduleBinary = path.join(__dirname, process.platform === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME);
|
|
13
|
-
const localDir = path.join(os.homedir(), '.codebuff', 'bin');
|
|
14
|
-
const localBinary = path.join(localDir, process.platform === 'win32' ? `${BINARY_NAME}.exe` : BINARY_NAME);
|
|
15
|
-
|
|
16
|
-
function resolveBinaryPath() {
|
|
17
|
-
if (fs.existsSync(localBinary)) return localBinary;
|
|
18
|
-
if (fs.existsSync(moduleBinary)) return moduleBinary;
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getVersion() {
|
|
23
|
-
const candidates = [
|
|
24
|
-
path.join(__dirname, '..', '..', 'package.json'),
|
|
25
|
-
path.join(__dirname, '..', 'package.json'),
|
|
26
|
-
];
|
|
27
|
-
for (const p of candidates) {
|
|
28
|
-
try {
|
|
29
|
-
const pkg = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
30
|
-
if (pkg.version) return pkg.version;
|
|
31
|
-
} catch {}
|
|
32
|
-
}
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function download(url, dest) {
|
|
37
|
-
return new Promise((resolve, reject) => {
|
|
38
|
-
const client = url.startsWith('https:') ? https : http;
|
|
39
|
-
const file = fs.createWriteStream(dest);
|
|
40
|
-
|
|
41
|
-
const req = client.get(
|
|
42
|
-
url,
|
|
43
|
-
{ headers: { 'User-Agent': 'codebuff-cli-installer' } },
|
|
44
|
-
(res) => {
|
|
45
|
-
if (res.statusCode === 301 || res.statusCode === 302) {
|
|
46
|
-
file.close();
|
|
47
|
-
if (fs.existsSync(dest)) fs.unlinkSync(dest);
|
|
48
|
-
download(new URL(res.headers.location, url).href, dest)
|
|
49
|
-
.then(resolve)
|
|
50
|
-
.catch(reject);
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (res.statusCode !== 200) {
|
|
55
|
-
file.close();
|
|
56
|
-
if (fs.existsSync(dest)) fs.unlinkSync(dest);
|
|
57
|
-
reject(new Error(`HTTP ${res.statusCode}`));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
res.pipe(file);
|
|
62
|
-
file.on('finish', () => {
|
|
63
|
-
file.close();
|
|
64
|
-
resolve();
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
req.on('error', (err) => {
|
|
70
|
-
file.close();
|
|
71
|
-
if (fs.existsSync(dest)) fs.unlinkSync(dest);
|
|
72
|
-
reject(err);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
req.setTimeout(30000, () => {
|
|
76
|
-
req.destroy();
|
|
77
|
-
file.close();
|
|
78
|
-
if (fs.existsSync(dest)) fs.unlinkSync(dest);
|
|
79
|
-
reject(new Error('Request timeout'));
|
|
80
|
-
});
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function tryDownload(url, dest) {
|
|
85
|
-
try {
|
|
86
|
-
await download(url, dest);
|
|
87
|
-
return true;
|
|
88
|
-
} catch {
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
async function downloadBinary(destPath) {
|
|
94
|
-
const version = getVersion();
|
|
95
|
-
if (!version) {
|
|
96
|
-
throw new Error('Could not determine version from package.json');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const baseUrl = `https://github.com/${REPO}/releases/download/v${version}`;
|
|
100
|
-
const platformName = `${BINARY_NAME}-${process.platform}-${process.arch}${process.platform === 'win32' ? '.exe' : ''}`;
|
|
101
|
-
const platformUrl = `${baseUrl}/${platformName}`;
|
|
102
|
-
const genericUrl = `${baseUrl}/${BINARY_NAME}`;
|
|
103
|
-
|
|
104
|
-
fs.mkdirSync(path.dirname(destPath), { recursive: true });
|
|
105
|
-
|
|
106
|
-
if (await tryDownload(platformUrl, destPath)) {
|
|
107
|
-
console.log(`Downloaded platform-specific binary: ${platformName}`);
|
|
108
|
-
} else if (await tryDownload(genericUrl, destPath)) {
|
|
109
|
-
console.log(`Downloaded generic binary: ${BINARY_NAME}`);
|
|
110
|
-
} else {
|
|
111
|
-
throw new Error(
|
|
112
|
-
`Failed to download binary from:\n ${platformUrl}\n ${genericUrl}`
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (process.platform !== 'win32') {
|
|
117
|
-
fs.chmodSync(destPath, 0o755);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function runBinary(binaryPath) {
|
|
122
|
-
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
123
|
-
stdio: 'inherit',
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
child.on('exit', (code, signal) => {
|
|
127
|
-
process.exit(signal ? 1 : code || 0);
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
child.on('error', (err) => {
|
|
131
|
-
console.error('Failed to start codebuff:', err.message);
|
|
132
|
-
process.exit(1);
|
|
133
|
-
});
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async function main() {
|
|
137
|
-
let binaryPath = resolveBinaryPath();
|
|
138
|
-
|
|
139
|
-
if (binaryPath) {
|
|
140
|
-
runBinary(binaryPath);
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
console.log('Codebuff binary not found. Downloading...');
|
|
145
|
-
|
|
146
|
-
try {
|
|
147
|
-
await downloadBinary(localBinary);
|
|
148
|
-
binaryPath = localBinary;
|
|
149
|
-
} catch (err) {
|
|
150
|
-
console.error('Failed to download codebuff:', err.message);
|
|
151
|
-
process.exit(1);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
if (!fs.existsSync(binaryPath)) {
|
|
155
|
-
console.error('Binary still not found after download.');
|
|
156
|
-
process.exit(1);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
runBinary(binaryPath);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
main().catch((err) => {
|
|
163
|
-
console.error('Error:', err.message);
|
|
164
|
-
process.exit(1);
|
|
165
|
-
});
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
const fs = require('fs');
|
|
3
|
-
const path = require('path');
|
|
4
|
-
const https = require('https');
|
|
5
|
-
const http = require('http');
|
|
6
|
-
|
|
7
|
-
const REPO = 'Marcus-Mok-GH/codebuff-cli';
|
|
8
|
-
const BINARY_NAME = 'codebuff';
|
|
9
|
-
const MAX_RETRIES = 3;
|
|
10
|
-
const REQUEST_TIMEOUT_MS = 120000;
|
|
11
|
-
const MAX_REDIRECTS = 10;
|
|
12
|
-
|
|
13
|
-
function getVersion() {
|
|
14
|
-
const pkgPath = path.join(__dirname, '..', 'package.json');
|
|
15
|
-
try {
|
|
16
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
17
|
-
if (pkg.version) return pkg.version;
|
|
18
|
-
} catch {}
|
|
19
|
-
throw new Error('Could not determine version from ' + pkgPath);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function getPlatform() {
|
|
23
|
-
const platform = process.platform;
|
|
24
|
-
const arch = process.arch;
|
|
25
|
-
const mappings = {
|
|
26
|
-
darwin: 'darwin',
|
|
27
|
-
linux: 'linux',
|
|
28
|
-
win32: 'win32',
|
|
29
|
-
};
|
|
30
|
-
const osName = mappings[platform] || platform;
|
|
31
|
-
return osName + '-' + arch;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getBinaryPath() {
|
|
35
|
-
const binDir = path.join(__dirname, '..', 'bin');
|
|
36
|
-
const name = process.platform === 'win32' ? BINARY_NAME + '.exe' : BINARY_NAME;
|
|
37
|
-
return path.join(binDir, name);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function cleanup(dest) {
|
|
41
|
-
try {
|
|
42
|
-
if (fs.existsSync(dest)) {
|
|
43
|
-
fs.unlinkSync(dest);
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// ignore cleanup errors
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function download(url, dest, redirectCount = 0) {
|
|
51
|
-
return new Promise((resolve, reject) => {
|
|
52
|
-
if (redirectCount > MAX_REDIRECTS) {
|
|
53
|
-
reject(new Error('Too many redirects (max ' + MAX_REDIRECTS + ') while downloading from ' + url));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const client = url.startsWith('https:') ? https : http;
|
|
58
|
-
const file = fs.createWriteStream(dest);
|
|
59
|
-
|
|
60
|
-
const req = client.get(
|
|
61
|
-
url,
|
|
62
|
-
{ headers: { 'User-Agent': 'codebuff-cli-installer' } },
|
|
63
|
-
(res) => {
|
|
64
|
-
if (res.statusCode === 301 || res.statusCode === 302 || res.statusCode === 307 || res.statusCode === 308) {
|
|
65
|
-
file.close();
|
|
66
|
-
cleanup(dest);
|
|
67
|
-
download(new URL(res.headers.location, url).href, dest, redirectCount + 1)
|
|
68
|
-
.then(resolve)
|
|
69
|
-
.catch(reject);
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (res.statusCode !== 200) {
|
|
74
|
-
file.close();
|
|
75
|
-
cleanup(dest);
|
|
76
|
-
reject(new Error('Download failed: HTTP ' + res.statusCode + ' from ' + url));
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
res.pipe(file);
|
|
81
|
-
file.on('finish', () => {
|
|
82
|
-
file.close();
|
|
83
|
-
resolve();
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
|
|
88
|
-
req.on('error', (err) => {
|
|
89
|
-
file.close();
|
|
90
|
-
cleanup(dest);
|
|
91
|
-
reject(new Error('Network error downloading from ' + url + ': ' + err.message));
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
req.setTimeout(REQUEST_TIMEOUT_MS, () => {
|
|
95
|
-
req.destroy();
|
|
96
|
-
file.close();
|
|
97
|
-
cleanup(dest);
|
|
98
|
-
reject(new Error('Download timeout (' + REQUEST_TIMEOUT_MS + 'ms) for ' + url));
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
async function downloadWithRetry(url, dest) {
|
|
104
|
-
let lastError;
|
|
105
|
-
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
106
|
-
try {
|
|
107
|
-
console.log('Downloading binary (attempt ' + attempt + '/' + MAX_RETRIES + ')...');
|
|
108
|
-
await download(url, dest);
|
|
109
|
-
return true;
|
|
110
|
-
} catch (err) {
|
|
111
|
-
lastError = err;
|
|
112
|
-
console.error('Attempt ' + attempt + ' failed: ' + err.message);
|
|
113
|
-
if (attempt < MAX_RETRIES) {
|
|
114
|
-
const delay = Math.pow(2, attempt) * 1000;
|
|
115
|
-
console.log('Retrying in ' + delay + 'ms...');
|
|
116
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
console.error('Failed to download binary after ' + MAX_RETRIES + ' attempts.');
|
|
121
|
-
console.error('Last error: ' + lastError.message);
|
|
122
|
-
return false;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
async function main() {
|
|
126
|
-
const version = getVersion();
|
|
127
|
-
const platform = getPlatform();
|
|
128
|
-
const binaryPath = getBinaryPath();
|
|
129
|
-
|
|
130
|
-
const url = 'https://github.com/' + REPO + '/releases/download/v' + version + '/codebuff-' + platform;
|
|
131
|
-
|
|
132
|
-
console.log('Platform: ' + platform);
|
|
133
|
-
console.log('Version: ' + version);
|
|
134
|
-
console.log('Binary path: ' + binaryPath);
|
|
135
|
-
console.log('Download URL: ' + url);
|
|
136
|
-
|
|
137
|
-
fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
|
|
138
|
-
|
|
139
|
-
if (fs.existsSync(binaryPath)) {
|
|
140
|
-
console.log('Binary already exists. Skipping download.');
|
|
141
|
-
process.exit(0);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
if (await downloadWithRetry(url, binaryPath)) {
|
|
145
|
-
console.log('Download complete.');
|
|
146
|
-
} else {
|
|
147
|
-
console.error('\nUnable to download the codebuff binary.');
|
|
148
|
-
console.error('You can try installing manually from:');
|
|
149
|
-
console.error(' ' + url);
|
|
150
|
-
process.exit(1);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
if (process.platform !== 'win32') {
|
|
154
|
-
fs.chmodSync(binaryPath, 0o755);
|
|
155
|
-
console.log('Made binary executable.');
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
console.log('Binary installed at: ' + binaryPath);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
main().catch((err) => {
|
|
162
|
-
console.error('Fatal error:', err.message);
|
|
163
|
-
process.exit(1);
|
|
164
|
-
});
|