codebuff-cli 1.0.16 → 1.0.17
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 +202 -0
- package/README.md +202 -58
- package/README.zh-CN.md +251 -0
- package/cli/README.md +84 -0
- package/cli/bin/codebuff.cjs +165 -0
- package/cli/scripts/download-binary.cjs +164 -0
- package/package.json +78 -31
- package/http.js +0 -176
- package/index.js +0 -592
- package/postinstall.js +0 -34
package/index.js
DELETED
|
@@ -1,592 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { spawn } = require('child_process')
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const http = require('http')
|
|
6
|
-
const https = require('https')
|
|
7
|
-
const os = require('os')
|
|
8
|
-
const path = require('path')
|
|
9
|
-
const zlib = require('zlib')
|
|
10
|
-
|
|
11
|
-
const tar = require('tar')
|
|
12
|
-
const { createReleaseHttpClient } = require('./http')
|
|
13
|
-
|
|
14
|
-
const packageName = 'codebuff'
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Terminal escape sequences to reset terminal state after the child process exits.
|
|
18
|
-
* When the binary is SIGKILL'd, it can't clean up its own terminal state.
|
|
19
|
-
* The wrapper (this process) survives and must reset these modes.
|
|
20
|
-
*
|
|
21
|
-
* Keep in sync with TERMINAL_RESET_SEQUENCES in cli/src/utils/renderer-cleanup.ts
|
|
22
|
-
*/
|
|
23
|
-
const TERMINAL_RESET_SEQUENCES =
|
|
24
|
-
'\x1b[?1049l' + // Exit alternate screen buffer
|
|
25
|
-
'\x1b[?1000l' + // Disable X10 mouse mode
|
|
26
|
-
'\x1b[?1002l' + // Disable button event mouse mode
|
|
27
|
-
'\x1b[?1003l' + // Disable any-event mouse mode (all motion)
|
|
28
|
-
'\x1b[?1006l' + // Disable SGR extended mouse mode
|
|
29
|
-
'\x1b[?1004l' + // Disable focus reporting
|
|
30
|
-
'\x1b[?2004l' + // Disable bracketed paste mode
|
|
31
|
-
'\x1b[?25h' // Show cursor
|
|
32
|
-
|
|
33
|
-
function resetTerminal() {
|
|
34
|
-
try {
|
|
35
|
-
if (process.stdin.isTTY && process.stdin.setRawMode) {
|
|
36
|
-
process.stdin.setRawMode(false)
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
// stdin may be closed
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
if (process.stdout.isTTY) {
|
|
43
|
-
process.stdout.write(TERMINAL_RESET_SEQUENCES)
|
|
44
|
-
}
|
|
45
|
-
} catch {
|
|
46
|
-
// stdout may be closed
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function createConfig(packageName) {
|
|
51
|
-
const homeDir = os.homedir()
|
|
52
|
-
const configDir = path.join(homeDir, '.config', 'manicode')
|
|
53
|
-
const binaryName =
|
|
54
|
-
process.platform === 'win32' ? `${packageName}.exe` : packageName
|
|
55
|
-
|
|
56
|
-
return {
|
|
57
|
-
homeDir,
|
|
58
|
-
configDir,
|
|
59
|
-
binaryName,
|
|
60
|
-
binaryPath: path.join(configDir, binaryName),
|
|
61
|
-
metadataPath: path.join(configDir, 'codebuff-metadata.json'),
|
|
62
|
-
tempDownloadDir: path.join(configDir, '.download-temp'),
|
|
63
|
-
userAgent: `${packageName}-cli`,
|
|
64
|
-
requestTimeout: 20000,
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const CONFIG = createConfig(packageName)
|
|
69
|
-
const { getProxyUrl, httpGet } = createReleaseHttpClient({
|
|
70
|
-
env: process.env,
|
|
71
|
-
userAgent: CONFIG.userAgent,
|
|
72
|
-
requestTimeout: CONFIG.requestTimeout,
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
function getPostHogConfig() {
|
|
76
|
-
const apiKey =
|
|
77
|
-
process.env.CODEBUFF_POSTHOG_API_KEY ||
|
|
78
|
-
process.env.NEXT_PUBLIC_POSTHOG_API_KEY
|
|
79
|
-
const host =
|
|
80
|
-
process.env.CODEBUFF_POSTHOG_HOST ||
|
|
81
|
-
process.env.NEXT_PUBLIC_POSTHOG_HOST_URL
|
|
82
|
-
|
|
83
|
-
if (!apiKey || !host) {
|
|
84
|
-
return null
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return { apiKey, host }
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Track update failure event to PostHog.
|
|
92
|
-
* Fire-and-forget - errors are silently ignored.
|
|
93
|
-
*/
|
|
94
|
-
function trackUpdateFailed(errorMessage, version, context = {}) {
|
|
95
|
-
try {
|
|
96
|
-
const posthogConfig = getPostHogConfig()
|
|
97
|
-
if (!posthogConfig) {
|
|
98
|
-
return
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const payload = JSON.stringify({
|
|
102
|
-
api_key: posthogConfig.apiKey,
|
|
103
|
-
event: 'cli.update_codebuff_failed',
|
|
104
|
-
properties: {
|
|
105
|
-
distinct_id: `anonymous-${CONFIG.homeDir}`,
|
|
106
|
-
error: errorMessage,
|
|
107
|
-
version: version || 'unknown',
|
|
108
|
-
platform: process.platform,
|
|
109
|
-
arch: process.arch,
|
|
110
|
-
...context,
|
|
111
|
-
},
|
|
112
|
-
timestamp: new Date().toISOString(),
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
const parsedUrl = new URL(`${posthogConfig.host}/capture/`)
|
|
116
|
-
const isHttps = parsedUrl.protocol === 'https:'
|
|
117
|
-
const options = {
|
|
118
|
-
hostname: parsedUrl.hostname,
|
|
119
|
-
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
120
|
-
path: parsedUrl.pathname + parsedUrl.search,
|
|
121
|
-
method: 'POST',
|
|
122
|
-
headers: {
|
|
123
|
-
'Content-Type': 'application/json',
|
|
124
|
-
'Content-Length': Buffer.byteLength(payload),
|
|
125
|
-
},
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const transport = isHttps ? https : http
|
|
129
|
-
const req = transport.request(options)
|
|
130
|
-
req.on('error', () => {}) // Silently ignore errors
|
|
131
|
-
req.write(payload)
|
|
132
|
-
req.end()
|
|
133
|
-
} catch (e) {
|
|
134
|
-
// Silently ignore any tracking errors
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
const PLATFORM_TARGETS = {
|
|
139
|
-
'linux-x64': `${packageName}-linux-x64.tar.gz`,
|
|
140
|
-
'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
|
|
141
|
-
'darwin-x64': `${packageName}-darwin-x64.tar.gz`,
|
|
142
|
-
'darwin-arm64': `${packageName}-darwin-arm64.tar.gz`,
|
|
143
|
-
'win32-x64': `${packageName}-win32-x64.tar.gz`,
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const term = {
|
|
147
|
-
clearLine: () => {
|
|
148
|
-
if (process.stderr.isTTY) {
|
|
149
|
-
process.stderr.write('\r\x1b[K')
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
write: (text) => {
|
|
153
|
-
term.clearLine()
|
|
154
|
-
process.stderr.write(text)
|
|
155
|
-
},
|
|
156
|
-
writeLine: (text) => {
|
|
157
|
-
term.clearLine()
|
|
158
|
-
process.stderr.write(text + '\n')
|
|
159
|
-
},
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async function getLatestVersion() {
|
|
163
|
-
try {
|
|
164
|
-
const res = await httpGet(
|
|
165
|
-
`https://registry.npmjs.org/${packageName}/latest`,
|
|
166
|
-
)
|
|
167
|
-
|
|
168
|
-
if (res.statusCode !== 200) return null
|
|
169
|
-
|
|
170
|
-
const body = await streamToString(res)
|
|
171
|
-
const packageData = JSON.parse(body)
|
|
172
|
-
|
|
173
|
-
return packageData.version || null
|
|
174
|
-
} catch (error) {
|
|
175
|
-
return null
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function streamToString(stream) {
|
|
180
|
-
return new Promise((resolve, reject) => {
|
|
181
|
-
let data = ''
|
|
182
|
-
stream.on('data', (chunk) => (data += chunk))
|
|
183
|
-
stream.on('end', () => resolve(data))
|
|
184
|
-
stream.on('error', reject)
|
|
185
|
-
})
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
function getCurrentVersion() {
|
|
189
|
-
try {
|
|
190
|
-
if (!fs.existsSync(CONFIG.metadataPath)) {
|
|
191
|
-
return null
|
|
192
|
-
}
|
|
193
|
-
const metadata = JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
|
|
194
|
-
// Also verify the binary still exists
|
|
195
|
-
if (!fs.existsSync(CONFIG.binaryPath)) {
|
|
196
|
-
return null
|
|
197
|
-
}
|
|
198
|
-
return metadata.version || null
|
|
199
|
-
} catch (error) {
|
|
200
|
-
return null
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
function compareVersions(v1, v2) {
|
|
205
|
-
if (!v1 || !v2) return 0
|
|
206
|
-
|
|
207
|
-
// Always update if the current version is not a valid semver
|
|
208
|
-
// e.g. 1.0.420-beta.1
|
|
209
|
-
if (!v1.match(/^\d+(\.\d+)*$/)) {
|
|
210
|
-
return -1
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
const parseVersion = (version) => {
|
|
214
|
-
const parts = version.split('-')
|
|
215
|
-
const mainParts = parts[0].split('.').map(Number)
|
|
216
|
-
const prereleaseParts = parts[1] ? parts[1].split('.') : []
|
|
217
|
-
return { main: mainParts, prerelease: prereleaseParts }
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
const p1 = parseVersion(v1)
|
|
221
|
-
const p2 = parseVersion(v2)
|
|
222
|
-
|
|
223
|
-
for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
|
|
224
|
-
const n1 = p1.main[i] || 0
|
|
225
|
-
const n2 = p2.main[i] || 0
|
|
226
|
-
|
|
227
|
-
if (n1 < n2) return -1
|
|
228
|
-
if (n1 > n2) return 1
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
|
|
232
|
-
return 0
|
|
233
|
-
} else if (p1.prerelease.length === 0) {
|
|
234
|
-
return 1
|
|
235
|
-
} else if (p2.prerelease.length === 0) {
|
|
236
|
-
return -1
|
|
237
|
-
} else {
|
|
238
|
-
for (
|
|
239
|
-
let i = 0;
|
|
240
|
-
i < Math.max(p1.prerelease.length, p2.prerelease.length);
|
|
241
|
-
i++
|
|
242
|
-
) {
|
|
243
|
-
const pr1 = p1.prerelease[i] || ''
|
|
244
|
-
const pr2 = p2.prerelease[i] || ''
|
|
245
|
-
|
|
246
|
-
const isNum1 = !isNaN(parseInt(pr1))
|
|
247
|
-
const isNum2 = !isNaN(parseInt(pr2))
|
|
248
|
-
|
|
249
|
-
if (isNum1 && isNum2) {
|
|
250
|
-
const num1 = parseInt(pr1)
|
|
251
|
-
const num2 = parseInt(pr2)
|
|
252
|
-
if (num1 < num2) return -1
|
|
253
|
-
if (num1 > num2) return 1
|
|
254
|
-
} else if (isNum1 && !isNum2) {
|
|
255
|
-
return 1
|
|
256
|
-
} else if (!isNum1 && isNum2) {
|
|
257
|
-
return -1
|
|
258
|
-
} else if (pr1 < pr2) {
|
|
259
|
-
return -1
|
|
260
|
-
} else if (pr1 > pr2) {
|
|
261
|
-
return 1
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return 0
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
function formatBytes(bytes) {
|
|
269
|
-
if (bytes === 0) return '0 B'
|
|
270
|
-
const k = 1024
|
|
271
|
-
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
272
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
273
|
-
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function createProgressBar(percentage, width = 30) {
|
|
277
|
-
const filled = Math.round((width * percentage) / 100)
|
|
278
|
-
const empty = width - filled
|
|
279
|
-
return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']'
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async function downloadBinary(version) {
|
|
283
|
-
const platformKey = `${process.platform}-${process.arch}`
|
|
284
|
-
const fileName = PLATFORM_TARGETS[platformKey]
|
|
285
|
-
|
|
286
|
-
if (!fileName) {
|
|
287
|
-
const error = new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
|
|
288
|
-
trackUpdateFailed(error.message, version, { stage: 'platform_check' })
|
|
289
|
-
throw error
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const downloadUrl = `${
|
|
293
|
-
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
|
|
294
|
-
}/api/releases/download/${version}/${fileName}`
|
|
295
|
-
|
|
296
|
-
// Ensure config directory exists
|
|
297
|
-
fs.mkdirSync(CONFIG.configDir, { recursive: true })
|
|
298
|
-
|
|
299
|
-
// Clean up any previous temp download directory
|
|
300
|
-
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
301
|
-
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
302
|
-
}
|
|
303
|
-
fs.mkdirSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
304
|
-
|
|
305
|
-
term.write('Downloading...')
|
|
306
|
-
|
|
307
|
-
const res = await httpGet(downloadUrl)
|
|
308
|
-
|
|
309
|
-
if (res.statusCode !== 200) {
|
|
310
|
-
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
311
|
-
const error = new Error(`Download failed: HTTP ${res.statusCode}`)
|
|
312
|
-
trackUpdateFailed(error.message, version, { stage: 'http_download', statusCode: res.statusCode })
|
|
313
|
-
throw error
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
const totalSize = parseInt(res.headers['content-length'] || '0', 10)
|
|
317
|
-
let downloadedSize = 0
|
|
318
|
-
let lastProgressTime = Date.now()
|
|
319
|
-
|
|
320
|
-
res.on('data', (chunk) => {
|
|
321
|
-
downloadedSize += chunk.length
|
|
322
|
-
const now = Date.now()
|
|
323
|
-
if (now - lastProgressTime >= 100 || downloadedSize === totalSize) {
|
|
324
|
-
lastProgressTime = now
|
|
325
|
-
if (totalSize > 0) {
|
|
326
|
-
const pct = Math.round((downloadedSize / totalSize) * 100)
|
|
327
|
-
term.write(
|
|
328
|
-
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
|
|
329
|
-
totalSize,
|
|
330
|
-
)}`,
|
|
331
|
-
)
|
|
332
|
-
} else {
|
|
333
|
-
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
})
|
|
337
|
-
|
|
338
|
-
// Extract to temp directory
|
|
339
|
-
await new Promise((resolve, reject) => {
|
|
340
|
-
res
|
|
341
|
-
.pipe(zlib.createGunzip())
|
|
342
|
-
.pipe(tar.x({ cwd: CONFIG.tempDownloadDir }))
|
|
343
|
-
.on('finish', resolve)
|
|
344
|
-
.on('error', reject)
|
|
345
|
-
})
|
|
346
|
-
|
|
347
|
-
const tempBinaryPath = path.join(CONFIG.tempDownloadDir, CONFIG.binaryName)
|
|
348
|
-
|
|
349
|
-
// Verify the binary was extracted
|
|
350
|
-
if (!fs.existsSync(tempBinaryPath)) {
|
|
351
|
-
const files = fs.readdirSync(CONFIG.tempDownloadDir)
|
|
352
|
-
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
353
|
-
const error = new Error(
|
|
354
|
-
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
|
|
355
|
-
)
|
|
356
|
-
trackUpdateFailed(error.message, version, { stage: 'extraction' })
|
|
357
|
-
throw error
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Set executable permissions
|
|
361
|
-
if (process.platform !== 'win32') {
|
|
362
|
-
fs.chmodSync(tempBinaryPath, 0o755)
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Move binary to final location
|
|
366
|
-
try {
|
|
367
|
-
if (fs.existsSync(CONFIG.binaryPath)) {
|
|
368
|
-
try {
|
|
369
|
-
fs.unlinkSync(CONFIG.binaryPath)
|
|
370
|
-
} catch (err) {
|
|
371
|
-
// Fallback: try renaming the locked/undeletable binary (Windows)
|
|
372
|
-
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
373
|
-
try {
|
|
374
|
-
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
375
|
-
} catch (renameErr) {
|
|
376
|
-
throw new Error(
|
|
377
|
-
`Failed to replace existing binary. ` +
|
|
378
|
-
`unlink error: ${err.code || err.message}, ` +
|
|
379
|
-
`rename error: ${renameErr.code || renameErr.message}`,
|
|
380
|
-
)
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
fs.renameSync(tempBinaryPath, CONFIG.binaryPath)
|
|
385
|
-
|
|
386
|
-
// Move tree-sitter.wasm next to the binary if the tarball included
|
|
387
|
-
// it. The CLI binary loads this at startup; embedding it inside the
|
|
388
|
-
// binary itself was unreliable on Windows (bun --compile asset
|
|
389
|
-
// bundling silently dropped or unbound it across several attempts),
|
|
390
|
-
// so we ship it as a sibling file instead. Older artifacts that
|
|
391
|
-
// pre-date this change won't have the wasm and will still install —
|
|
392
|
-
// they'll just hit the same crash they had before, which is fine.
|
|
393
|
-
const tempWasmPath = path.join(CONFIG.tempDownloadDir, 'tree-sitter.wasm')
|
|
394
|
-
if (fs.existsSync(tempWasmPath)) {
|
|
395
|
-
const targetWasmPath = path.join(
|
|
396
|
-
path.dirname(CONFIG.binaryPath),
|
|
397
|
-
'tree-sitter.wasm',
|
|
398
|
-
)
|
|
399
|
-
try {
|
|
400
|
-
if (fs.existsSync(targetWasmPath)) fs.unlinkSync(targetWasmPath)
|
|
401
|
-
} catch {
|
|
402
|
-
// best effort; rename below will surface the real error if it matters
|
|
403
|
-
}
|
|
404
|
-
fs.renameSync(tempWasmPath, targetWasmPath)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Save version metadata for fast version checking
|
|
408
|
-
fs.writeFileSync(
|
|
409
|
-
CONFIG.metadataPath,
|
|
410
|
-
JSON.stringify({ version }, null, 2),
|
|
411
|
-
)
|
|
412
|
-
} finally {
|
|
413
|
-
// Clean up temp directory even if rename fails
|
|
414
|
-
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
415
|
-
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
term.clearLine()
|
|
420
|
-
console.log('Download complete! Starting Codebuff...')
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
async function ensureBinaryExists() {
|
|
424
|
-
const currentVersion = getCurrentVersion()
|
|
425
|
-
if (currentVersion !== null) {
|
|
426
|
-
return
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const version = await getLatestVersion()
|
|
430
|
-
if (!version) {
|
|
431
|
-
console.error('❌ Failed to determine latest version')
|
|
432
|
-
console.error('Please check your internet connection and try again')
|
|
433
|
-
if (!getProxyUrl()) {
|
|
434
|
-
console.error(
|
|
435
|
-
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
|
|
436
|
-
)
|
|
437
|
-
}
|
|
438
|
-
process.exit(1)
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
try {
|
|
442
|
-
await downloadBinary(version)
|
|
443
|
-
} catch (error) {
|
|
444
|
-
term.clearLine()
|
|
445
|
-
console.error('❌ Failed to download codebuff:', error.message)
|
|
446
|
-
console.error('Please check your internet connection and try again')
|
|
447
|
-
if (!getProxyUrl()) {
|
|
448
|
-
console.error(
|
|
449
|
-
'If you are behind a proxy, set the HTTPS_PROXY environment variable',
|
|
450
|
-
)
|
|
451
|
-
}
|
|
452
|
-
process.exit(1)
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
async function checkForUpdates(runningProcess, exitListener) {
|
|
457
|
-
try {
|
|
458
|
-
const currentVersion = getCurrentVersion()
|
|
459
|
-
|
|
460
|
-
const latestVersion = await getLatestVersion()
|
|
461
|
-
if (!latestVersion) return
|
|
462
|
-
|
|
463
|
-
if (
|
|
464
|
-
// Download new version if current version is unknown or outdated.
|
|
465
|
-
currentVersion === null ||
|
|
466
|
-
compareVersions(currentVersion, latestVersion) < 0
|
|
467
|
-
) {
|
|
468
|
-
term.clearLine()
|
|
469
|
-
|
|
470
|
-
runningProcess.removeListener('exit', exitListener)
|
|
471
|
-
|
|
472
|
-
await new Promise((resolve) => {
|
|
473
|
-
let exited = false
|
|
474
|
-
runningProcess.once('exit', () => {
|
|
475
|
-
exited = true
|
|
476
|
-
resolve()
|
|
477
|
-
})
|
|
478
|
-
runningProcess.kill('SIGTERM')
|
|
479
|
-
setTimeout(() => {
|
|
480
|
-
if (!exited) {
|
|
481
|
-
runningProcess.kill('SIGKILL')
|
|
482
|
-
// Safety: resolve after giving SIGKILL time to take effect
|
|
483
|
-
setTimeout(() => resolve(), 1000)
|
|
484
|
-
}
|
|
485
|
-
}, 5000)
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
resetTerminal()
|
|
489
|
-
console.log(`Update available: ${currentVersion} → ${latestVersion}`)
|
|
490
|
-
|
|
491
|
-
await downloadBinary(latestVersion)
|
|
492
|
-
|
|
493
|
-
const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
494
|
-
stdio: 'inherit',
|
|
495
|
-
detached: false,
|
|
496
|
-
})
|
|
497
|
-
|
|
498
|
-
newChild.on('exit', (code, signal) => {
|
|
499
|
-
resetTerminal()
|
|
500
|
-
printCrashDiagnostics(code, signal)
|
|
501
|
-
process.exit(signal ? 1 : (code || 0))
|
|
502
|
-
})
|
|
503
|
-
|
|
504
|
-
newChild.on('error', (err) => {
|
|
505
|
-
console.error('Failed to start codebuff:', err.message)
|
|
506
|
-
process.exit(1)
|
|
507
|
-
})
|
|
508
|
-
|
|
509
|
-
return new Promise(() => {})
|
|
510
|
-
}
|
|
511
|
-
} catch (error) {
|
|
512
|
-
// Ignore update failures
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
function printCrashDiagnostics(code, signal) {
|
|
517
|
-
// Windows NTSTATUS codes (unsigned DWORD)
|
|
518
|
-
const unsignedCode = code != null && code < 0 ? (code >>> 0) : code
|
|
519
|
-
const isIllegalInstruction =
|
|
520
|
-
signal === 'SIGILL' ||
|
|
521
|
-
(process.platform === 'win32' && unsignedCode === 0xC000001D)
|
|
522
|
-
const isAccessViolation =
|
|
523
|
-
signal === 'SIGSEGV' ||
|
|
524
|
-
(process.platform === 'win32' && unsignedCode === 0xC0000005)
|
|
525
|
-
const isBusError = signal === 'SIGBUS'
|
|
526
|
-
const isAbort =
|
|
527
|
-
signal === 'SIGABRT' ||
|
|
528
|
-
(process.platform === 'win32' && unsignedCode === 0xC0000409)
|
|
529
|
-
|
|
530
|
-
if (!isIllegalInstruction && !isAccessViolation && !isBusError && !isAbort) return
|
|
531
|
-
|
|
532
|
-
const exitInfo = signal ? `signal ${signal}` : `code ${code}`
|
|
533
|
-
console.error('')
|
|
534
|
-
console.error(`❌ ${packageName} exited immediately (${exitInfo})`)
|
|
535
|
-
console.error('')
|
|
536
|
-
|
|
537
|
-
if (isIllegalInstruction) {
|
|
538
|
-
console.error('Your CPU may not support the required instruction set (AVX2).')
|
|
539
|
-
console.error('This typically affects CPUs from before 2013.')
|
|
540
|
-
console.error('Unfortunately, this binary is not compatible with your system.')
|
|
541
|
-
console.error('')
|
|
542
|
-
} else if (isAccessViolation) {
|
|
543
|
-
console.error('The binary crashed with an access violation.')
|
|
544
|
-
console.error('')
|
|
545
|
-
} else if (isBusError) {
|
|
546
|
-
console.error('The binary crashed with a bus error.')
|
|
547
|
-
console.error('This may indicate a platform compatibility issue.')
|
|
548
|
-
console.error('')
|
|
549
|
-
} else if (isAbort) {
|
|
550
|
-
console.error('The binary crashed with an abort signal.')
|
|
551
|
-
console.error('')
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
console.error('System info:')
|
|
555
|
-
console.error(` Platform: ${process.platform} ${process.arch}`)
|
|
556
|
-
console.error(` Node: ${process.version}`)
|
|
557
|
-
console.error(` Binary: ${CONFIG.binaryPath}`)
|
|
558
|
-
console.error('')
|
|
559
|
-
console.error('Please report this issue at:')
|
|
560
|
-
console.error(' https://github.com/CodebuffAI/codebuff/issues')
|
|
561
|
-
console.error('')
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async function main() {
|
|
565
|
-
await ensureBinaryExists()
|
|
566
|
-
|
|
567
|
-
const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
568
|
-
stdio: 'inherit',
|
|
569
|
-
})
|
|
570
|
-
|
|
571
|
-
const exitListener = (code, signal) => {
|
|
572
|
-
resetTerminal()
|
|
573
|
-
printCrashDiagnostics(code, signal)
|
|
574
|
-
process.exit(signal ? 1 : (code || 0))
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
child.on('exit', exitListener)
|
|
578
|
-
|
|
579
|
-
child.on('error', (err) => {
|
|
580
|
-
console.error('Failed to start codebuff:', err.message)
|
|
581
|
-
process.exit(1)
|
|
582
|
-
})
|
|
583
|
-
|
|
584
|
-
setTimeout(() => {
|
|
585
|
-
checkForUpdates(child, exitListener)
|
|
586
|
-
}, 100)
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
main().catch((error) => {
|
|
590
|
-
console.error('❌ Unexpected error:', error.message)
|
|
591
|
-
process.exit(1)
|
|
592
|
-
})
|
package/postinstall.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const os = require('os');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
// Clean up old binary
|
|
8
|
-
const binaryPath = path.join(
|
|
9
|
-
os.homedir(),
|
|
10
|
-
'.config',
|
|
11
|
-
'manicode',
|
|
12
|
-
process.platform === 'win32' ? 'codebuff.exe' : 'codebuff'
|
|
13
|
-
);
|
|
14
|
-
|
|
15
|
-
try {
|
|
16
|
-
fs.unlinkSync(binaryPath);
|
|
17
|
-
} catch (e) {
|
|
18
|
-
/* ignore if file doesn't exist */
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// Print welcome message
|
|
22
|
-
console.log('\n');
|
|
23
|
-
console.log('🎉 Welcome to Codebuff!');
|
|
24
|
-
console.log('\n');
|
|
25
|
-
console.log('To get started:');
|
|
26
|
-
console.log(' 1. cd to your project directory');
|
|
27
|
-
console.log(' 2. Run: codebuff');
|
|
28
|
-
console.log('\n');
|
|
29
|
-
console.log('Example:');
|
|
30
|
-
console.log(' $ cd ~/my-project');
|
|
31
|
-
console.log(' $ codebuff');
|
|
32
|
-
console.log('\n');
|
|
33
|
-
console.log('For more information, visit: https://codebuff.com/docs');
|
|
34
|
-
console.log('\n');
|