freebuff 0.0.1 → 0.0.2
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/index.js +469 -7
- package/package.json +6 -2
- package/postinstall.js +28 -2
package/index.js
CHANGED
|
@@ -1,9 +1,471 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
+
|
|
13
|
+
const packageName = 'freebuff'
|
|
14
|
+
|
|
15
|
+
function createConfig(packageName) {
|
|
16
|
+
const homeDir = os.homedir()
|
|
17
|
+
const configDir = path.join(homeDir, '.config', 'manicode')
|
|
18
|
+
const binaryName =
|
|
19
|
+
process.platform === 'win32' ? `${packageName}.exe` : packageName
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
homeDir,
|
|
23
|
+
configDir,
|
|
24
|
+
binaryName,
|
|
25
|
+
binaryPath: path.join(configDir, binaryName),
|
|
26
|
+
metadataPath: path.join(configDir, 'freebuff-metadata.json'),
|
|
27
|
+
tempDownloadDir: path.join(configDir, '.freebuff-download-temp'),
|
|
28
|
+
userAgent: `${packageName}-cli`,
|
|
29
|
+
requestTimeout: 20000,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const CONFIG = createConfig(packageName)
|
|
34
|
+
|
|
35
|
+
function getPostHogConfig() {
|
|
36
|
+
const apiKey =
|
|
37
|
+
process.env.CODEBUFF_POSTHOG_API_KEY ||
|
|
38
|
+
process.env.NEXT_PUBLIC_POSTHOG_API_KEY
|
|
39
|
+
const host =
|
|
40
|
+
process.env.CODEBUFF_POSTHOG_HOST ||
|
|
41
|
+
process.env.NEXT_PUBLIC_POSTHOG_HOST_URL
|
|
42
|
+
|
|
43
|
+
if (!apiKey || !host) {
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { apiKey, host }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Track update failure event to PostHog.
|
|
52
|
+
* Fire-and-forget - errors are silently ignored.
|
|
53
|
+
*/
|
|
54
|
+
function trackUpdateFailed(errorMessage, version, context = {}) {
|
|
55
|
+
try {
|
|
56
|
+
const posthogConfig = getPostHogConfig()
|
|
57
|
+
if (!posthogConfig) {
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const payload = JSON.stringify({
|
|
62
|
+
api_key: posthogConfig.apiKey,
|
|
63
|
+
event: 'cli.update_freebuff_failed',
|
|
64
|
+
properties: {
|
|
65
|
+
distinct_id: `anonymous-${CONFIG.homeDir}`,
|
|
66
|
+
error: errorMessage,
|
|
67
|
+
version: version || 'unknown',
|
|
68
|
+
platform: process.platform,
|
|
69
|
+
arch: process.arch,
|
|
70
|
+
...context,
|
|
71
|
+
},
|
|
72
|
+
timestamp: new Date().toISOString(),
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
const parsedUrl = new URL(`${posthogConfig.host}/capture/`)
|
|
76
|
+
const isHttps = parsedUrl.protocol === 'https:'
|
|
77
|
+
const options = {
|
|
78
|
+
hostname: parsedUrl.hostname,
|
|
79
|
+
port: parsedUrl.port || (isHttps ? 443 : 80),
|
|
80
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
'Content-Length': Buffer.byteLength(payload),
|
|
85
|
+
},
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const transport = isHttps ? https : http
|
|
89
|
+
const req = transport.request(options)
|
|
90
|
+
req.on('error', () => {})
|
|
91
|
+
req.write(payload)
|
|
92
|
+
req.end()
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// Silently ignore any tracking errors
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const PLATFORM_TARGETS = {
|
|
99
|
+
'linux-x64': `${packageName}-linux-x64.tar.gz`,
|
|
100
|
+
'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
|
|
101
|
+
'darwin-x64': `${packageName}-darwin-x64.tar.gz`,
|
|
102
|
+
'darwin-arm64': `${packageName}-darwin-arm64.tar.gz`,
|
|
103
|
+
'win32-x64': `${packageName}-win32-x64.tar.gz`,
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const term = {
|
|
107
|
+
clearLine: () => {
|
|
108
|
+
if (process.stderr.isTTY) {
|
|
109
|
+
process.stderr.write('\r\x1b[K')
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
write: (text) => {
|
|
113
|
+
term.clearLine()
|
|
114
|
+
process.stderr.write(text)
|
|
115
|
+
},
|
|
116
|
+
writeLine: (text) => {
|
|
117
|
+
term.clearLine()
|
|
118
|
+
process.stderr.write(text + '\n')
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function httpGet(url, options = {}) {
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
const parsedUrl = new URL(url)
|
|
125
|
+
const reqOptions = {
|
|
126
|
+
hostname: parsedUrl.hostname,
|
|
127
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
128
|
+
headers: {
|
|
129
|
+
'User-Agent': CONFIG.userAgent,
|
|
130
|
+
...options.headers,
|
|
131
|
+
},
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const req = https.get(reqOptions, (res) => {
|
|
135
|
+
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
136
|
+
return httpGet(new URL(res.headers.location, url).href, options)
|
|
137
|
+
.then(resolve)
|
|
138
|
+
.catch(reject)
|
|
139
|
+
}
|
|
140
|
+
resolve(res)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
req.on('error', reject)
|
|
144
|
+
|
|
145
|
+
const timeout = options.timeout || CONFIG.requestTimeout
|
|
146
|
+
req.setTimeout(timeout, () => {
|
|
147
|
+
req.destroy()
|
|
148
|
+
reject(new Error('Request timeout.'))
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function getLatestVersion() {
|
|
154
|
+
try {
|
|
155
|
+
const res = await httpGet(
|
|
156
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
if (res.statusCode !== 200) return null
|
|
160
|
+
|
|
161
|
+
const body = await streamToString(res)
|
|
162
|
+
const packageData = JSON.parse(body)
|
|
163
|
+
|
|
164
|
+
return packageData.version || null
|
|
165
|
+
} catch (error) {
|
|
166
|
+
return null
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function streamToString(stream) {
|
|
171
|
+
return new Promise((resolve, reject) => {
|
|
172
|
+
let data = ''
|
|
173
|
+
stream.on('data', (chunk) => (data += chunk))
|
|
174
|
+
stream.on('end', () => resolve(data))
|
|
175
|
+
stream.on('error', reject)
|
|
176
|
+
})
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function getCurrentVersion() {
|
|
180
|
+
try {
|
|
181
|
+
if (!fs.existsSync(CONFIG.metadataPath)) {
|
|
182
|
+
return null
|
|
183
|
+
}
|
|
184
|
+
const metadata = JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
|
|
185
|
+
if (!fs.existsSync(CONFIG.binaryPath)) {
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
return metadata.version || null
|
|
189
|
+
} catch (error) {
|
|
190
|
+
return null
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function compareVersions(v1, v2) {
|
|
195
|
+
if (!v1 || !v2) return 0
|
|
196
|
+
|
|
197
|
+
if (!v1.match(/^\d+(\.\d+)*$/)) {
|
|
198
|
+
return -1
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const parseVersion = (version) => {
|
|
202
|
+
const parts = version.split('-')
|
|
203
|
+
const mainParts = parts[0].split('.').map(Number)
|
|
204
|
+
const prereleaseParts = parts[1] ? parts[1].split('.') : []
|
|
205
|
+
return { main: mainParts, prerelease: prereleaseParts }
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const p1 = parseVersion(v1)
|
|
209
|
+
const p2 = parseVersion(v2)
|
|
210
|
+
|
|
211
|
+
for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
|
|
212
|
+
const n1 = p1.main[i] || 0
|
|
213
|
+
const n2 = p2.main[i] || 0
|
|
214
|
+
|
|
215
|
+
if (n1 < n2) return -1
|
|
216
|
+
if (n1 > n2) return 1
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
|
|
220
|
+
return 0
|
|
221
|
+
} else if (p1.prerelease.length === 0) {
|
|
222
|
+
return 1
|
|
223
|
+
} else if (p2.prerelease.length === 0) {
|
|
224
|
+
return -1
|
|
225
|
+
} else {
|
|
226
|
+
for (
|
|
227
|
+
let i = 0;
|
|
228
|
+
i < Math.max(p1.prerelease.length, p2.prerelease.length);
|
|
229
|
+
i++
|
|
230
|
+
) {
|
|
231
|
+
const pr1 = p1.prerelease[i] || ''
|
|
232
|
+
const pr2 = p2.prerelease[i] || ''
|
|
233
|
+
|
|
234
|
+
const isNum1 = !isNaN(parseInt(pr1))
|
|
235
|
+
const isNum2 = !isNaN(parseInt(pr2))
|
|
236
|
+
|
|
237
|
+
if (isNum1 && isNum2) {
|
|
238
|
+
const num1 = parseInt(pr1)
|
|
239
|
+
const num2 = parseInt(pr2)
|
|
240
|
+
if (num1 < num2) return -1
|
|
241
|
+
if (num1 > num2) return 1
|
|
242
|
+
} else if (isNum1 && !isNum2) {
|
|
243
|
+
return 1
|
|
244
|
+
} else if (!isNum1 && isNum2) {
|
|
245
|
+
return -1
|
|
246
|
+
} else if (pr1 < pr2) {
|
|
247
|
+
return -1
|
|
248
|
+
} else if (pr1 > pr2) {
|
|
249
|
+
return 1
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return 0
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function formatBytes(bytes) {
|
|
257
|
+
if (bytes === 0) return '0 B'
|
|
258
|
+
const k = 1024
|
|
259
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
260
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
261
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function createProgressBar(percentage, width = 30) {
|
|
265
|
+
const filled = Math.round((width * percentage) / 100)
|
|
266
|
+
const empty = width - filled
|
|
267
|
+
return '[' + '█'.repeat(filled) + '░'.repeat(empty) + ']'
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
async function downloadBinary(version) {
|
|
271
|
+
const platformKey = `${process.platform}-${process.arch}`
|
|
272
|
+
const fileName = PLATFORM_TARGETS[platformKey]
|
|
273
|
+
|
|
274
|
+
if (!fileName) {
|
|
275
|
+
const error = new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
|
|
276
|
+
trackUpdateFailed(error.message, version, { stage: 'platform_check' })
|
|
277
|
+
throw error
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const downloadUrl = `${
|
|
281
|
+
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
|
|
282
|
+
}/api/releases/download/${version}/${fileName}`
|
|
283
|
+
|
|
284
|
+
fs.mkdirSync(CONFIG.configDir, { recursive: true })
|
|
285
|
+
|
|
286
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
287
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
288
|
+
}
|
|
289
|
+
fs.mkdirSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
290
|
+
|
|
291
|
+
term.write('Downloading...')
|
|
292
|
+
|
|
293
|
+
const res = await httpGet(downloadUrl)
|
|
294
|
+
|
|
295
|
+
if (res.statusCode !== 200) {
|
|
296
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
297
|
+
const error = new Error(`Download failed: HTTP ${res.statusCode}`)
|
|
298
|
+
trackUpdateFailed(error.message, version, { stage: 'http_download', statusCode: res.statusCode })
|
|
299
|
+
throw error
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const totalSize = parseInt(res.headers['content-length'] || '0', 10)
|
|
303
|
+
let downloadedSize = 0
|
|
304
|
+
let lastProgressTime = Date.now()
|
|
305
|
+
|
|
306
|
+
res.on('data', (chunk) => {
|
|
307
|
+
downloadedSize += chunk.length
|
|
308
|
+
const now = Date.now()
|
|
309
|
+
if (now - lastProgressTime >= 100 || downloadedSize === totalSize) {
|
|
310
|
+
lastProgressTime = now
|
|
311
|
+
if (totalSize > 0) {
|
|
312
|
+
const pct = Math.round((downloadedSize / totalSize) * 100)
|
|
313
|
+
term.write(
|
|
314
|
+
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
|
|
315
|
+
totalSize,
|
|
316
|
+
)}`,
|
|
317
|
+
)
|
|
318
|
+
} else {
|
|
319
|
+
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
await new Promise((resolve, reject) => {
|
|
325
|
+
res
|
|
326
|
+
.pipe(zlib.createGunzip())
|
|
327
|
+
.pipe(tar.x({ cwd: CONFIG.tempDownloadDir }))
|
|
328
|
+
.on('finish', resolve)
|
|
329
|
+
.on('error', reject)
|
|
330
|
+
})
|
|
331
|
+
|
|
332
|
+
const tempBinaryPath = path.join(CONFIG.tempDownloadDir, CONFIG.binaryName)
|
|
333
|
+
|
|
334
|
+
if (!fs.existsSync(tempBinaryPath)) {
|
|
335
|
+
const files = fs.readdirSync(CONFIG.tempDownloadDir)
|
|
336
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
337
|
+
const error = new Error(
|
|
338
|
+
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
|
|
339
|
+
)
|
|
340
|
+
trackUpdateFailed(error.message, version, { stage: 'extraction' })
|
|
341
|
+
throw error
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (process.platform !== 'win32') {
|
|
345
|
+
fs.chmodSync(tempBinaryPath, 0o755)
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
if (fs.existsSync(CONFIG.binaryPath)) {
|
|
350
|
+
try {
|
|
351
|
+
fs.unlinkSync(CONFIG.binaryPath)
|
|
352
|
+
} catch (err) {
|
|
353
|
+
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
354
|
+
try {
|
|
355
|
+
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
356
|
+
} catch (renameErr) {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Failed to replace existing binary. ` +
|
|
359
|
+
`unlink error: ${err.code || err.message}, ` +
|
|
360
|
+
`rename error: ${renameErr.code || renameErr.message}`,
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
fs.renameSync(tempBinaryPath, CONFIG.binaryPath)
|
|
366
|
+
|
|
367
|
+
fs.writeFileSync(
|
|
368
|
+
CONFIG.metadataPath,
|
|
369
|
+
JSON.stringify({ version }, null, 2),
|
|
370
|
+
)
|
|
371
|
+
} finally {
|
|
372
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
373
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
term.clearLine()
|
|
378
|
+
console.log('Download complete! Starting Freebuff...')
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
async function ensureBinaryExists() {
|
|
382
|
+
const currentVersion = getCurrentVersion()
|
|
383
|
+
if (currentVersion !== null) {
|
|
384
|
+
return
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const version = await getLatestVersion()
|
|
388
|
+
if (!version) {
|
|
389
|
+
console.error('❌ Failed to determine latest version')
|
|
390
|
+
console.error('Please check your internet connection and try again')
|
|
391
|
+
process.exit(1)
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
try {
|
|
395
|
+
await downloadBinary(version)
|
|
396
|
+
} catch (error) {
|
|
397
|
+
term.clearLine()
|
|
398
|
+
console.error('❌ Failed to download freebuff:', error.message)
|
|
399
|
+
console.error('Please check your internet connection and try again')
|
|
400
|
+
process.exit(1)
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
async function checkForUpdates(runningProcess, exitListener) {
|
|
405
|
+
try {
|
|
406
|
+
const currentVersion = getCurrentVersion()
|
|
407
|
+
|
|
408
|
+
const latestVersion = await getLatestVersion()
|
|
409
|
+
if (!latestVersion) return
|
|
410
|
+
|
|
411
|
+
if (
|
|
412
|
+
currentVersion === null ||
|
|
413
|
+
compareVersions(currentVersion, latestVersion) < 0
|
|
414
|
+
) {
|
|
415
|
+
term.clearLine()
|
|
416
|
+
|
|
417
|
+
runningProcess.removeListener('exit', exitListener)
|
|
418
|
+
runningProcess.kill('SIGTERM')
|
|
419
|
+
|
|
420
|
+
await new Promise((resolve) => {
|
|
421
|
+
runningProcess.on('exit', resolve)
|
|
422
|
+
setTimeout(() => {
|
|
423
|
+
if (!runningProcess.killed) {
|
|
424
|
+
runningProcess.kill('SIGKILL')
|
|
425
|
+
}
|
|
426
|
+
resolve()
|
|
427
|
+
}, 5000)
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
console.log(`Update available: ${currentVersion} → ${latestVersion}`)
|
|
431
|
+
|
|
432
|
+
await downloadBinary(latestVersion)
|
|
433
|
+
|
|
434
|
+
const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
435
|
+
stdio: 'inherit',
|
|
436
|
+
detached: false,
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
newChild.on('exit', (code) => {
|
|
440
|
+
process.exit(code || 0)
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
return new Promise(() => {})
|
|
444
|
+
}
|
|
445
|
+
} catch (error) {
|
|
446
|
+
// Ignore update failures
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
async function main() {
|
|
451
|
+
await ensureBinaryExists()
|
|
452
|
+
|
|
453
|
+
const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
454
|
+
stdio: 'inherit',
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
const exitListener = (code) => {
|
|
458
|
+
process.exit(code || 0)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
child.on('exit', exitListener)
|
|
462
|
+
|
|
463
|
+
setTimeout(() => {
|
|
464
|
+
checkForUpdates(child, exitListener)
|
|
465
|
+
}, 100)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
main().catch((error) => {
|
|
469
|
+
console.error('❌ Unexpected error:', error.message)
|
|
470
|
+
process.exit(1)
|
|
471
|
+
})
|
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "freebuff",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "The world's strongest free coding agent",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"freebuff": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"postinstall": "node postinstall.js"
|
|
10
|
+
"postinstall": "node postinstall.js",
|
|
11
|
+
"preuninstall": "node -e \"const fs = require('fs'); const path = require('path'); const os = require('os'); const binaryPath = path.join(os.homedir(), '.config', 'manicode', process.platform === 'win32' ? 'freebuff.exe' : 'freebuff'); try { fs.unlinkSync(binaryPath) } catch (e) { /* ignore if file doesn't exist */ }\""
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"index.js",
|
|
@@ -26,6 +27,9 @@
|
|
|
26
27
|
"engines": {
|
|
27
28
|
"node": ">=16"
|
|
28
29
|
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"tar": "^7.0.0"
|
|
32
|
+
},
|
|
29
33
|
"repository": {
|
|
30
34
|
"type": "git",
|
|
31
35
|
"url": "https://github.com/CodebuffAI/codebuff.git"
|
package/postinstall.js
CHANGED
|
@@ -1,7 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
|
|
7
|
+
// Clean up old binary to force fresh download on next launch
|
|
8
|
+
const binaryPath = path.join(
|
|
9
|
+
os.homedir(),
|
|
10
|
+
'.config',
|
|
11
|
+
'manicode',
|
|
12
|
+
process.platform === 'win32' ? 'freebuff.exe' : 'freebuff'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
fs.unlinkSync(binaryPath);
|
|
17
|
+
} catch (e) {
|
|
18
|
+
/* ignore if file doesn't exist */
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
console.log('\n');
|
|
22
|
+
console.log('⚡ Welcome to Freebuff!');
|
|
23
|
+
console.log('\n');
|
|
24
|
+
console.log('To get started:');
|
|
25
|
+
console.log(' 1. cd to your project directory');
|
|
26
|
+
console.log(' 2. Run: freebuff');
|
|
3
27
|
console.log('\n');
|
|
4
|
-
console.log('
|
|
28
|
+
console.log('Example:');
|
|
29
|
+
console.log(' $ cd ~/my-project');
|
|
30
|
+
console.log(' $ freebuff');
|
|
5
31
|
console.log('\n');
|
|
6
|
-
console.log('
|
|
32
|
+
console.log('For more information, visit: https://codebuff.com/docs');
|
|
7
33
|
console.log('\n');
|