@qoder-ai/qodercli 0.0.9-preview
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 +36 -0
- package/README.md +42 -0
- package/bin/qodercli +37 -0
- package/package.json +77 -0
- package/scripts/install.js +293 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
MIT License (for npm installer scripts only)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Qoder AI
|
|
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.
|
|
22
|
+
|
|
23
|
+
IMPORTANT NOTICE REGARDING QODERCLI BINARY:
|
|
24
|
+
|
|
25
|
+
The qodercli binary software downloaded and installed by this package is
|
|
26
|
+
proprietary software owned by Qoder AI and is subject to separate commercial
|
|
27
|
+
licensing terms. The qodercli binary is NOT covered by the above MIT license.
|
|
28
|
+
|
|
29
|
+
By using this installer, you agree that:
|
|
30
|
+
|
|
31
|
+
1. The qodercli binary is proprietary commercial software
|
|
32
|
+
2. You may use the binary only under the terms of Qoder AI's End User License Agreement
|
|
33
|
+
3. Reverse engineering, decompilation, or disassembly of the binary is prohibited
|
|
34
|
+
4. The binary may collect usage data and require authentication
|
|
35
|
+
5. Commercial use may require a separate license from Qoder AI
|
|
36
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Qoder CLI
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@qoder-ai/qodercli)
|
|
4
|
+
|
|
5
|
+
Qoder CLI is a powerful terminal-based AI assistant that understands your codebase and helps you code faster by executing routine tasks, explaining complex code, and handling development workflows -- all through natural language commands.
|
|
6
|
+
|
|
7
|
+
## Get started
|
|
8
|
+
|
|
9
|
+
Install Qoder CLI using your preferred method:
|
|
10
|
+
|
|
11
|
+
### NPM (Recommended)
|
|
12
|
+
```sh
|
|
13
|
+
npm install -g @qoder-ai/qodercli
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### Homebrew (macOS & Linux)
|
|
17
|
+
```sh
|
|
18
|
+
brew install --cask QoderAI/qoder/qodercli
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Curl + Bash
|
|
22
|
+
```sh
|
|
23
|
+
curl -fsSL https://qoder.com/install | bash
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
1. Navigate to your project directory
|
|
29
|
+
2. Run `qodercli` to start the interactive mode
|
|
30
|
+
3. Or run single commands: `qodercli -p "your prompt here"`
|
|
31
|
+
|
|
32
|
+
## Reporting Bugs
|
|
33
|
+
|
|
34
|
+
We welcome feedback. Use the `/bug` command to report issues directly within Qoder CLI.
|
|
35
|
+
|
|
36
|
+
## Data collection, usage, and retention
|
|
37
|
+
|
|
38
|
+
please review our Commercial Terms of Service at https://qoder.com/product-service.
|
|
39
|
+
|
|
40
|
+
## Support
|
|
41
|
+
|
|
42
|
+
For support and documentation, visit [https://qoder.com](https://qoder.com)
|
package/bin/qodercli
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const { spawn } = require('child_process');
|
|
6
|
+
|
|
7
|
+
const BINARY_NAME = 'qodercli';
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
9
|
+
const BIN_DIR = path.join(PACKAGE_ROOT, 'bin');
|
|
10
|
+
const binPath = path.join(BIN_DIR, BINARY_NAME + (process.platform === 'win32' ? '.exe' : ''));
|
|
11
|
+
|
|
12
|
+
function showInstallationError() {
|
|
13
|
+
console.error('❌ qodercli binary not found');
|
|
14
|
+
console.error('please try to reinstall:');
|
|
15
|
+
console.error(' npm install --force');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// check if the binary file exists
|
|
19
|
+
if (!fs.existsSync(binPath)) {
|
|
20
|
+
showInstallationError();
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// execute the actual binary file
|
|
25
|
+
const child = spawn(binPath, process.argv.slice(2), {
|
|
26
|
+
stdio: 'inherit',
|
|
27
|
+
windowsHide: false
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
child.on('close', (code) => {
|
|
31
|
+
process.exit(code);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
child.on('error', (error) => {
|
|
35
|
+
console.error('execution failed:', error.message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@qoder-ai/qodercli",
|
|
3
|
+
"version": "0.0.9-preview",
|
|
4
|
+
"description": "qodercli - npm installer",
|
|
5
|
+
"private": false,
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"bin": {
|
|
10
|
+
"qodercli": "./bin/qodercli"
|
|
11
|
+
},
|
|
12
|
+
"scripts": {
|
|
13
|
+
"postinstall": "node scripts/install.js"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"qoder",
|
|
17
|
+
"ai",
|
|
18
|
+
"cli"
|
|
19
|
+
],
|
|
20
|
+
"author": "Qoder AI",
|
|
21
|
+
"license": "SEE LICENSE IN README.md",
|
|
22
|
+
"homepage": "https://qoder.com",
|
|
23
|
+
"files": [
|
|
24
|
+
"scripts/",
|
|
25
|
+
"bin/",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE"
|
|
28
|
+
],
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=14"
|
|
31
|
+
},
|
|
32
|
+
"os": [
|
|
33
|
+
"darwin",
|
|
34
|
+
"linux",
|
|
35
|
+
"win32"
|
|
36
|
+
],
|
|
37
|
+
"cpu": [
|
|
38
|
+
"x64",
|
|
39
|
+
"arm64"
|
|
40
|
+
],
|
|
41
|
+
"preferGlobal": true,
|
|
42
|
+
"binaries": {
|
|
43
|
+
"version": "0.0.9-preview",
|
|
44
|
+
"files": [
|
|
45
|
+
{
|
|
46
|
+
"os": "linux",
|
|
47
|
+
"arch": "amd64",
|
|
48
|
+
"url": "qoder-ide.oss-ap-southeast-1.aliyuncs.com/qodercli/releases/0.0.9-preview/qodercli_0.0.9-preview_linux_amd64.tar.gz",
|
|
49
|
+
"sha256": "7e9e33d234c9d819290872c11daef8cbcf5df92e9525533e8ad3932a0420c9d0"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"os": "linux",
|
|
53
|
+
"arch": "arm64",
|
|
54
|
+
"url": "qoder-ide.oss-ap-southeast-1.aliyuncs.com/qodercli/releases/0.0.9-preview/qodercli_0.0.9-preview_linux_arm64.tar.gz",
|
|
55
|
+
"sha256": "0371b085c472269f5e565f718957e79b8fad2135c8ab16b0592276ce6863f233"
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"os": "darwin",
|
|
59
|
+
"arch": "amd64",
|
|
60
|
+
"url": "qoder-ide.oss-ap-southeast-1.aliyuncs.com/qodercli/releases/0.0.9-preview/qodercli_0.0.9-preview_darwin_amd64.zip",
|
|
61
|
+
"sha256": "b15c7e3692a322c7f605a097aa24827f7465d4f4037e0548eae4e99abd0a1ba9"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"os": "darwin",
|
|
65
|
+
"arch": "arm64",
|
|
66
|
+
"url": "qoder-ide.oss-ap-southeast-1.aliyuncs.com/qodercli/releases/0.0.9-preview/qodercli_0.0.9-preview_darwin_arm64.zip",
|
|
67
|
+
"sha256": "bec7c81aabf5c42fcf94b78402508c0e742b6eb36bc7b739f49e54138f05c445"
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"os": "windows",
|
|
71
|
+
"arch": "amd64",
|
|
72
|
+
"url": "qoder-ide.oss-ap-southeast-1.aliyuncs.com/qodercli/releases/0.0.9-preview/qodercli_0.0.9-preview_windows_amd64.zip",
|
|
73
|
+
"sha256": "7504b4712d099b13bd2d29b3649cd7a654d60d0e659b852dc5e6f20c0d4557ce"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const https = require('https');
|
|
6
|
+
const http = require('http');
|
|
7
|
+
const { execSync } = require('child_process');
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
|
|
10
|
+
// Configuration
|
|
11
|
+
const BINARY_NAME = 'qodercli';
|
|
12
|
+
const PACKAGE_ROOT = path.resolve(__dirname, '..');
|
|
13
|
+
const BIN_DIR = path.join(PACKAGE_ROOT, 'bin');
|
|
14
|
+
const PACKAGE_JSON_PATH = path.join(PACKAGE_ROOT, 'package.json');
|
|
15
|
+
|
|
16
|
+
class QoderInstaller {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.platform = this.detectPlatform();
|
|
19
|
+
this.arch = this.detectArch();
|
|
20
|
+
this.binPath = path.join(BIN_DIR, BINARY_NAME + (process.platform === 'win32' ? '.exe' : ''));
|
|
21
|
+
this.packageInfo = this.loadPackageInfo();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
detectPlatform() {
|
|
25
|
+
switch (process.platform) {
|
|
26
|
+
case 'darwin': return 'darwin';
|
|
27
|
+
case 'linux': return 'linux';
|
|
28
|
+
case 'win32': return 'windows';
|
|
29
|
+
default:
|
|
30
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
detectArch() {
|
|
35
|
+
const arch = process.arch;
|
|
36
|
+
switch (arch) {
|
|
37
|
+
case 'x64': return 'amd64';
|
|
38
|
+
case 'arm64': return 'arm64';
|
|
39
|
+
default:
|
|
40
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
loadPackageInfo() {
|
|
45
|
+
try {
|
|
46
|
+
const packageJson = fs.readFileSync(PACKAGE_JSON_PATH, 'utf8');
|
|
47
|
+
const packageInfo = JSON.parse(packageJson);
|
|
48
|
+
|
|
49
|
+
if (!packageInfo.binaries || !packageInfo.binaries.files) {
|
|
50
|
+
throw new Error('Binary information missing in package configuration');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return packageInfo;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
throw new Error(`Unable to read package configuration: ${error.message}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
findBinaryInfo() {
|
|
60
|
+
const files = this.packageInfo.binaries.files;
|
|
61
|
+
const targetFile = files.find(file =>
|
|
62
|
+
file.os === this.platform && file.arch === this.arch
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!targetFile) {
|
|
66
|
+
throw new Error(`Unsupported platform: ${this.platform}/${this.arch}`);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return targetFile;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async downloadBinary(url, expectedSha256) {
|
|
73
|
+
console.log(`Downloading binary: ${url}`);
|
|
74
|
+
|
|
75
|
+
// Ensure directory exists
|
|
76
|
+
if (!fs.existsSync(BIN_DIR)) {
|
|
77
|
+
fs.mkdirSync(BIN_DIR, { recursive: true });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Download file
|
|
81
|
+
const filename = path.basename(url);
|
|
82
|
+
const archivePath = path.join(BIN_DIR, filename);
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
await this.downloadFile(url, archivePath);
|
|
86
|
+
|
|
87
|
+
// Verify checksum
|
|
88
|
+
console.log('Verifying file integrity...');
|
|
89
|
+
const actualSha256 = this.calculateSha256(archivePath);
|
|
90
|
+
if (actualSha256 !== expectedSha256) {
|
|
91
|
+
throw new Error(`Checksum mismatch. Expected: ${expectedSha256}, Got: ${actualSha256}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Extract file
|
|
95
|
+
console.log('Extracting binary...');
|
|
96
|
+
await this.extractArchive(archivePath, filename);
|
|
97
|
+
|
|
98
|
+
// Remove archive
|
|
99
|
+
fs.unlinkSync(archivePath);
|
|
100
|
+
|
|
101
|
+
// Set executable permission
|
|
102
|
+
if (process.platform !== 'win32') {
|
|
103
|
+
fs.chmodSync(this.binPath, 0o755);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Create installation source marker
|
|
107
|
+
const sourceFile = path.join(BIN_DIR, '.qodercli-install-resource');
|
|
108
|
+
fs.writeFileSync(sourceFile, 'npm', 'utf8');
|
|
109
|
+
|
|
110
|
+
// Verify installation
|
|
111
|
+
this.verifyInstallation();
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
// Cleanup failed download
|
|
115
|
+
if (fs.existsSync(archivePath)) {
|
|
116
|
+
fs.unlinkSync(archivePath);
|
|
117
|
+
}
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async extractArchive(archivePath, filename) {
|
|
123
|
+
if (filename.endsWith('.zip')) {
|
|
124
|
+
// Extract ZIP file
|
|
125
|
+
if (process.platform === 'win32') {
|
|
126
|
+
// Windows: Use PowerShell
|
|
127
|
+
try {
|
|
128
|
+
execSync(`powershell -command "Expand-Archive -Path '${archivePath}' -DestinationPath '${BIN_DIR}' -Force"`, {
|
|
129
|
+
stdio: 'pipe'
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Additional verification - check if binary file exists after extraction
|
|
133
|
+
if (!fs.existsSync(this.binPath)) {
|
|
134
|
+
// Maybe the binary is in a subdirectory, let's check
|
|
135
|
+
const extractedFiles = this.findExtractedBinary(BIN_DIR);
|
|
136
|
+
if (extractedFiles.length > 0) {
|
|
137
|
+
fs.renameSync(extractedFiles[0], this.binPath);
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error(`Binary file not found after extraction. Expected at: ${this.binPath}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
throw new Error(`ZIP extraction failed: ${error.message}. Please ensure PowerShell is available.`);
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
// Unix: Use unzip command
|
|
147
|
+
try {
|
|
148
|
+
execSync(`unzip -o "${archivePath}" -d "${BIN_DIR}"`, {
|
|
149
|
+
stdio: 'pipe'
|
|
150
|
+
});
|
|
151
|
+
} catch (error) {
|
|
152
|
+
throw new Error('ZIP extraction failed. Please ensure unzip command is installed.');
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
// Extract tar.gz file
|
|
157
|
+
try {
|
|
158
|
+
execSync(`tar -xzf "${archivePath}" -C "${BIN_DIR}"`, {
|
|
159
|
+
stdio: 'pipe'
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
throw new Error('tar.gz extraction failed. Please ensure tar command is installed.');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
calculateSha256(filePath) {
|
|
168
|
+
const fileBuffer = fs.readFileSync(filePath);
|
|
169
|
+
const hashSum = crypto.createHash('sha256');
|
|
170
|
+
hashSum.update(fileBuffer);
|
|
171
|
+
return hashSum.digest('hex');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
findExtractedBinary(searchDir) {
|
|
175
|
+
const results = [];
|
|
176
|
+
const expectedFilename = BINARY_NAME + (process.platform === 'win32' ? '.exe' : '');
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const items = fs.readdirSync(searchDir, { withFileTypes: true });
|
|
180
|
+
|
|
181
|
+
for (const item of items) {
|
|
182
|
+
const fullPath = path.join(searchDir, item.name);
|
|
183
|
+
|
|
184
|
+
if (item.isDirectory()) {
|
|
185
|
+
// Recursively search in subdirectories
|
|
186
|
+
results.push(...this.findExtractedBinary(fullPath));
|
|
187
|
+
} else if (item.name === expectedFilename) {
|
|
188
|
+
results.push(fullPath);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.warn(`Unable to search directory ${searchDir}:`, error.message);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return results;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
verifyInstallation() {
|
|
199
|
+
if (!fs.existsSync(this.binPath)) {
|
|
200
|
+
throw new Error('Binary installation failed');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
try {
|
|
204
|
+
// Try to run version command for verification
|
|
205
|
+
const output = execSync(`"${this.binPath}" --version`, {
|
|
206
|
+
encoding: 'utf8',
|
|
207
|
+
stdio: 'pipe'
|
|
208
|
+
});
|
|
209
|
+
console.log('Installation verified successfully');
|
|
210
|
+
console.log(`Version info: ${output.trim()}`);
|
|
211
|
+
} catch (error) {
|
|
212
|
+
console.warn('Warning: Unable to verify installation, but binary file exists');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async downloadFile(url, filePath, timeout = 60000) {
|
|
217
|
+
return new Promise((resolve, reject) => {
|
|
218
|
+
const file = fs.createWriteStream(filePath);
|
|
219
|
+
const client = url.startsWith('https:') ? https : http;
|
|
220
|
+
|
|
221
|
+
const request = client.get(url, (response) => {
|
|
222
|
+
if (response.statusCode === 302 || response.statusCode === 301) {
|
|
223
|
+
// Handle redirect
|
|
224
|
+
file.close();
|
|
225
|
+
fs.unlinkSync(filePath);
|
|
226
|
+
return this.downloadFile(response.headers.location, filePath, timeout)
|
|
227
|
+
.then(resolve).catch(reject);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (response.statusCode !== 200) {
|
|
231
|
+
file.close();
|
|
232
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
233
|
+
reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
response.pipe(file);
|
|
238
|
+
|
|
239
|
+
file.on('finish', () => {
|
|
240
|
+
file.close();
|
|
241
|
+
resolve();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
file.on('error', (error) => {
|
|
245
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
246
|
+
reject(error);
|
|
247
|
+
});
|
|
248
|
+
}).on('error', (error) => {
|
|
249
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
250
|
+
reject(error);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
// Set timeout
|
|
254
|
+
request.setTimeout(timeout, () => {
|
|
255
|
+
request.destroy();
|
|
256
|
+
file.close();
|
|
257
|
+
if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
|
|
258
|
+
reject(new Error(`Download timeout (${timeout}ms): ${url}`));
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async install() {
|
|
264
|
+
try {
|
|
265
|
+
console.log('Installing Qoder CLI...');
|
|
266
|
+
console.log(`Target platform: ${this.platform}/${this.arch}`);
|
|
267
|
+
console.log(`Version: ${this.packageInfo.binaries.version}`);
|
|
268
|
+
|
|
269
|
+
// If already installed, reinstall
|
|
270
|
+
if (fs.existsSync(this.binPath)) {
|
|
271
|
+
console.log('Existing version detected, will reinstall');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
const binaryInfo = this.findBinaryInfo();
|
|
275
|
+
await this.downloadBinary(binaryInfo.url, binaryInfo.sha256);
|
|
276
|
+
|
|
277
|
+
console.log('✅ Qoder CLI installed successfully!');
|
|
278
|
+
console.log(`Run 'npx qodercli --help' to get started`);
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
console.error('❌ Installation failed:', error.message);
|
|
282
|
+
process.exit(1);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Main program
|
|
288
|
+
if (require.main === module) {
|
|
289
|
+
const installer = new QoderInstaller();
|
|
290
|
+
installer.install();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
module.exports = QoderInstaller;
|