codecane 1.0.420-beta.26 → 1.0.420-beta.260
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 +1 -3
- package/index.js +81 -130
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Once running, simply chat with Codecane to say what coding task you want done.
|
|
|
39
39
|
- Can run your tests or type checker or linter; can install packages
|
|
40
40
|
- It's powerful: ask Codecane to keep working until it reaches a condition and it will.
|
|
41
41
|
|
|
42
|
-
Our users regularly use Codecane to implement new features, write unit tests, refactor code,write scripts, or give advice.
|
|
42
|
+
Our users regularly use Codecane to implement new features, write unit tests, refactor code, write scripts, or give advice.
|
|
43
43
|
|
|
44
44
|
## Knowledge Files
|
|
45
45
|
|
|
@@ -69,5 +69,3 @@ If you still have errors, it's a good idea to [reinstall Node](https://nodejs.or
|
|
|
69
69
|
## Feedback
|
|
70
70
|
|
|
71
71
|
We value your input! Please email your feedback to `founders@codebuff.com`. Thank you for using Codecane!
|
|
72
|
-
|
|
73
|
-
<!-- Test comment for staging workflow -->
|
package/index.js
CHANGED
|
@@ -1,37 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
const os = require('os')
|
|
6
3
|
const { spawn } = require('child_process')
|
|
4
|
+
const fs = require('fs')
|
|
7
5
|
const https = require('https')
|
|
6
|
+
const path = require('path')
|
|
8
7
|
const zlib = require('zlib')
|
|
8
|
+
|
|
9
9
|
const tar = require('tar')
|
|
10
10
|
|
|
11
|
-
// Hardcoded package name for codecane
|
|
12
11
|
const packageName = 'codecane'
|
|
13
12
|
|
|
14
13
|
function createConfig(packageName) {
|
|
15
|
-
|
|
16
|
-
const
|
|
14
|
+
// Store binary in package directory instead of ~/.config/manicode
|
|
15
|
+
const packageDir = __dirname
|
|
16
|
+
const binDir = path.join(packageDir, 'bin')
|
|
17
17
|
const binaryName =
|
|
18
18
|
process.platform === 'win32' ? `${packageName}.exe` : packageName
|
|
19
19
|
|
|
20
20
|
return {
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
packageDir,
|
|
22
|
+
binDir,
|
|
23
23
|
binaryName,
|
|
24
|
-
binaryPath: path.join(
|
|
25
|
-
githubRepo: 'CodebuffAI/codebuff-community',
|
|
24
|
+
binaryPath: path.join(binDir, binaryName),
|
|
26
25
|
userAgent: `${packageName}-cli`,
|
|
27
26
|
requestTimeout: 20000,
|
|
28
|
-
isPrerelease: true, // codecane always looks for prereleases
|
|
29
27
|
}
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
const CONFIG = createConfig(packageName)
|
|
33
31
|
|
|
34
|
-
// Platform target mapping
|
|
35
32
|
const PLATFORM_TARGETS = {
|
|
36
33
|
'linux-x64': `${packageName}-linux-x64.tar.gz`,
|
|
37
34
|
'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
|
|
@@ -40,7 +37,6 @@ const PLATFORM_TARGETS = {
|
|
|
40
37
|
'win32-x64': `${packageName}-win32-x64.tar.gz`,
|
|
41
38
|
}
|
|
42
39
|
|
|
43
|
-
// Terminal utilities
|
|
44
40
|
const term = {
|
|
45
41
|
clearLine: () => {
|
|
46
42
|
if (process.stderr.isTTY) {
|
|
@@ -57,7 +53,6 @@ const term = {
|
|
|
57
53
|
},
|
|
58
54
|
}
|
|
59
55
|
|
|
60
|
-
// Utility functions
|
|
61
56
|
function httpGet(url, options = {}) {
|
|
62
57
|
return new Promise((resolve, reject) => {
|
|
63
58
|
const parsedUrl = new URL(url)
|
|
@@ -70,13 +65,6 @@ function httpGet(url, options = {}) {
|
|
|
70
65
|
},
|
|
71
66
|
}
|
|
72
67
|
|
|
73
|
-
// Add GitHub token if available
|
|
74
|
-
const token = process.env.GITHUB_TOKEN
|
|
75
|
-
if (token) {
|
|
76
|
-
console.log('Using your GITHUB_TOKEN to download the latest version.')
|
|
77
|
-
reqOptions.headers.Authorization = `Bearer ${token}`
|
|
78
|
-
}
|
|
79
|
-
|
|
80
68
|
const req = https.get(reqOptions, (res) => {
|
|
81
69
|
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
82
70
|
return httpGet(new URL(res.headers.location, url).href, options)
|
|
@@ -99,43 +87,15 @@ function httpGet(url, options = {}) {
|
|
|
99
87
|
async function getLatestVersion() {
|
|
100
88
|
try {
|
|
101
89
|
const res = await httpGet(
|
|
102
|
-
`https://
|
|
90
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
103
91
|
)
|
|
104
92
|
|
|
105
93
|
if (res.statusCode !== 200) return null
|
|
106
94
|
|
|
107
95
|
const body = await streamToString(res)
|
|
96
|
+
const packageData = JSON.parse(body)
|
|
108
97
|
|
|
109
|
-
|
|
110
|
-
const tagMatches = body.match(
|
|
111
|
-
/<id>tag:github\.com,2008:Repository\/\d+\/([^<]+)<\/id>/g
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
if (!tagMatches) return null
|
|
115
|
-
|
|
116
|
-
// Extract all version tags
|
|
117
|
-
const versions = tagMatches
|
|
118
|
-
.map((match) => {
|
|
119
|
-
const tagMatch = match.match(
|
|
120
|
-
/<id>tag:github\.com,2008:Repository\/\d+\/([^<]+)<\/id>/
|
|
121
|
-
)
|
|
122
|
-
return tagMatch ? tagMatch[1].replace(/^v/, '') : null
|
|
123
|
-
})
|
|
124
|
-
.filter(Boolean)
|
|
125
|
-
|
|
126
|
-
if (versions.length === 0) return null
|
|
127
|
-
|
|
128
|
-
// Filter versions based on whether we want prereleases or stable releases
|
|
129
|
-
const filteredVersions = versions.filter((version) => {
|
|
130
|
-
const isPrerelease = version.includes('-')
|
|
131
|
-
return CONFIG.isPrerelease === isPrerelease
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
if (filteredVersions.length === 0) return null
|
|
135
|
-
|
|
136
|
-
// Sort and return the latest version
|
|
137
|
-
filteredVersions.sort(compareVersions)
|
|
138
|
-
return filteredVersions[filteredVersions.length - 1]
|
|
98
|
+
return packageData.version || null
|
|
139
99
|
} catch (error) {
|
|
140
100
|
return null
|
|
141
101
|
}
|
|
@@ -154,21 +114,20 @@ function getCurrentVersion() {
|
|
|
154
114
|
if (!fs.existsSync(CONFIG.binaryPath)) return null
|
|
155
115
|
|
|
156
116
|
try {
|
|
157
|
-
return new Promise((resolve
|
|
117
|
+
return new Promise((resolve) => {
|
|
158
118
|
const child = spawn(CONFIG.binaryPath, ['--version'], {
|
|
159
|
-
cwd:
|
|
119
|
+
cwd: CONFIG.packageDir,
|
|
160
120
|
stdio: 'pipe',
|
|
161
121
|
})
|
|
162
122
|
|
|
163
123
|
let output = ''
|
|
164
|
-
let errorOutput = ''
|
|
165
124
|
|
|
166
125
|
child.stdout.on('data', (data) => {
|
|
167
126
|
output += data.toString()
|
|
168
127
|
})
|
|
169
128
|
|
|
170
|
-
child.stderr.on('data', (
|
|
171
|
-
|
|
129
|
+
child.stderr.on('data', () => {
|
|
130
|
+
// Ignore stderr output
|
|
172
131
|
})
|
|
173
132
|
|
|
174
133
|
const timeout = setTimeout(() => {
|
|
@@ -177,9 +136,9 @@ function getCurrentVersion() {
|
|
|
177
136
|
if (!child.killed) {
|
|
178
137
|
child.kill('SIGKILL')
|
|
179
138
|
}
|
|
180
|
-
},
|
|
139
|
+
}, 4000)
|
|
181
140
|
resolve('error')
|
|
182
|
-
},
|
|
141
|
+
}, 4000)
|
|
183
142
|
|
|
184
143
|
child.on('exit', (code) => {
|
|
185
144
|
clearTimeout(timeout)
|
|
@@ -213,7 +172,6 @@ function compareVersions(v1, v2) {
|
|
|
213
172
|
const p1 = parseVersion(v1)
|
|
214
173
|
const p2 = parseVersion(v2)
|
|
215
174
|
|
|
216
|
-
// Compare main version parts
|
|
217
175
|
for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
|
|
218
176
|
const n1 = p1.main[i] || 0
|
|
219
177
|
const n2 = p2.main[i] || 0
|
|
@@ -222,15 +180,13 @@ function compareVersions(v1, v2) {
|
|
|
222
180
|
if (n1 > n2) return 1
|
|
223
181
|
}
|
|
224
182
|
|
|
225
|
-
// If main versions are equal, compare prerelease parts
|
|
226
183
|
if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
|
|
227
|
-
return 0
|
|
184
|
+
return 0
|
|
228
185
|
} else if (p1.prerelease.length === 0) {
|
|
229
|
-
return 1
|
|
186
|
+
return 1
|
|
230
187
|
} else if (p2.prerelease.length === 0) {
|
|
231
|
-
return -1
|
|
188
|
+
return -1
|
|
232
189
|
} else {
|
|
233
|
-
// Both have prerelease parts, compare them
|
|
234
190
|
for (
|
|
235
191
|
let i = 0;
|
|
236
192
|
i < Math.max(p1.prerelease.length, p2.prerelease.length);
|
|
@@ -239,7 +195,6 @@ function compareVersions(v1, v2) {
|
|
|
239
195
|
const pr1 = p1.prerelease[i] || ''
|
|
240
196
|
const pr2 = p2.prerelease[i] || ''
|
|
241
197
|
|
|
242
|
-
// Handle numeric vs. string parts
|
|
243
198
|
const isNum1 = !isNaN(parseInt(pr1))
|
|
244
199
|
const isNum2 = !isNaN(parseInt(pr2))
|
|
245
200
|
|
|
@@ -249,16 +204,16 @@ function compareVersions(v1, v2) {
|
|
|
249
204
|
if (num1 < num2) return -1
|
|
250
205
|
if (num1 > num2) return 1
|
|
251
206
|
} else if (isNum1 && !isNum2) {
|
|
252
|
-
return 1
|
|
207
|
+
return 1
|
|
253
208
|
} else if (!isNum1 && isNum2) {
|
|
254
209
|
return -1
|
|
255
|
-
} else {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
210
|
+
} else if (pr1 < pr2) {
|
|
211
|
+
return -1
|
|
212
|
+
} else if (pr1 > pr2) {
|
|
213
|
+
return 1
|
|
259
214
|
}
|
|
260
215
|
}
|
|
261
|
-
return 0
|
|
216
|
+
return 0
|
|
262
217
|
}
|
|
263
218
|
}
|
|
264
219
|
|
|
@@ -284,13 +239,29 @@ async function downloadBinary(version) {
|
|
|
284
239
|
throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
|
|
285
240
|
}
|
|
286
241
|
|
|
287
|
-
const downloadUrl =
|
|
242
|
+
const downloadUrl = `${
|
|
243
|
+
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
|
|
244
|
+
}/api/releases/download/${version}/${fileName}`
|
|
288
245
|
|
|
289
|
-
//
|
|
290
|
-
fs.mkdirSync(CONFIG.
|
|
246
|
+
// Create bin directory in package directory
|
|
247
|
+
fs.mkdirSync(CONFIG.binDir, { recursive: true })
|
|
291
248
|
|
|
292
249
|
if (fs.existsSync(CONFIG.binaryPath)) {
|
|
293
|
-
|
|
250
|
+
try {
|
|
251
|
+
fs.unlinkSync(CONFIG.binaryPath)
|
|
252
|
+
} catch (err) {
|
|
253
|
+
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
257
|
+
} catch (renameErr) {
|
|
258
|
+
throw new Error(
|
|
259
|
+
`Failed to replace existing binary. ` +
|
|
260
|
+
`unlink error: ${err.code || err.message}, ` +
|
|
261
|
+
`rename error: ${renameErr.code || renameErr.message}`,
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
294
265
|
}
|
|
295
266
|
|
|
296
267
|
term.write('Downloading...')
|
|
@@ -314,8 +285,8 @@ async function downloadBinary(version) {
|
|
|
314
285
|
const pct = Math.round((downloadedSize / totalSize) * 100)
|
|
315
286
|
term.write(
|
|
316
287
|
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
|
|
317
|
-
totalSize
|
|
318
|
-
)}
|
|
288
|
+
totalSize,
|
|
289
|
+
)}`,
|
|
319
290
|
)
|
|
320
291
|
} else {
|
|
321
292
|
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
|
|
@@ -326,15 +297,14 @@ async function downloadBinary(version) {
|
|
|
326
297
|
await new Promise((resolve, reject) => {
|
|
327
298
|
res
|
|
328
299
|
.pipe(zlib.createGunzip())
|
|
329
|
-
.pipe(tar.x({ cwd: CONFIG.
|
|
300
|
+
.pipe(tar.x({ cwd: CONFIG.binDir }))
|
|
330
301
|
.on('finish', resolve)
|
|
331
302
|
.on('error', reject)
|
|
332
303
|
})
|
|
333
304
|
|
|
334
305
|
try {
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
const extractedPath = path.join(CONFIG.configDir, CONFIG.binaryName)
|
|
306
|
+
const files = fs.readdirSync(CONFIG.binDir)
|
|
307
|
+
const extractedPath = path.join(CONFIG.binDir, CONFIG.binaryName)
|
|
338
308
|
|
|
339
309
|
if (fs.existsSync(extractedPath)) {
|
|
340
310
|
if (process.platform !== 'win32') {
|
|
@@ -342,7 +312,7 @@ async function downloadBinary(version) {
|
|
|
342
312
|
}
|
|
343
313
|
} else {
|
|
344
314
|
throw new Error(
|
|
345
|
-
`Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}
|
|
315
|
+
`Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}`,
|
|
346
316
|
)
|
|
347
317
|
}
|
|
348
318
|
} catch (error) {
|
|
@@ -356,22 +326,25 @@ async function downloadBinary(version) {
|
|
|
356
326
|
}
|
|
357
327
|
|
|
358
328
|
async function ensureBinaryExists() {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
console.error('Please check your internet connection and try again')
|
|
364
|
-
process.exit(1)
|
|
365
|
-
}
|
|
329
|
+
const currentVersion = await getCurrentVersion()
|
|
330
|
+
if (currentVersion !== null && currentVersion !== 'error') {
|
|
331
|
+
return
|
|
332
|
+
}
|
|
366
333
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
334
|
+
const version = await getLatestVersion()
|
|
335
|
+
if (!version) {
|
|
336
|
+
console.error('❌ Failed to determine latest version')
|
|
337
|
+
console.error('Please check your internet connection and try again')
|
|
338
|
+
process.exit(1)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
await downloadBinary(version)
|
|
343
|
+
} catch (error) {
|
|
344
|
+
term.clearLine()
|
|
345
|
+
console.error('❌ Failed to download codecane:', error.message)
|
|
346
|
+
console.error('Please check your internet connection and try again')
|
|
347
|
+
process.exit(1)
|
|
375
348
|
}
|
|
376
349
|
}
|
|
377
350
|
|
|
@@ -384,22 +357,16 @@ async function checkForUpdates(runningProcess, exitListener) {
|
|
|
384
357
|
if (!latestVersion) return
|
|
385
358
|
|
|
386
359
|
if (
|
|
387
|
-
// Download new version if current binary errors.
|
|
388
360
|
currentVersion === 'error' ||
|
|
389
361
|
compareVersions(currentVersion, latestVersion) < 0
|
|
390
362
|
) {
|
|
391
363
|
term.clearLine()
|
|
392
364
|
|
|
393
|
-
// Remove the specific exit listener to prevent it from interfering with the update
|
|
394
365
|
runningProcess.removeListener('exit', exitListener)
|
|
395
|
-
|
|
396
|
-
// Kill the running process
|
|
397
366
|
runningProcess.kill('SIGTERM')
|
|
398
367
|
|
|
399
|
-
// Wait for the process to actually exit
|
|
400
368
|
await new Promise((resolve) => {
|
|
401
369
|
runningProcess.on('exit', resolve)
|
|
402
|
-
// Fallback timeout in case the process doesn't exit gracefully
|
|
403
370
|
setTimeout(() => {
|
|
404
371
|
if (!runningProcess.killed) {
|
|
405
372
|
runningProcess.kill('SIGKILL')
|
|
@@ -412,64 +379,48 @@ async function checkForUpdates(runningProcess, exitListener) {
|
|
|
412
379
|
|
|
413
380
|
await downloadBinary(latestVersion)
|
|
414
381
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
{
|
|
420
|
-
stdio: 'inherit',
|
|
421
|
-
detached: false,
|
|
422
|
-
}
|
|
423
|
-
)
|
|
382
|
+
const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
383
|
+
stdio: 'inherit',
|
|
384
|
+
detached: false,
|
|
385
|
+
})
|
|
424
386
|
|
|
425
|
-
// Set up exit handler for the new process
|
|
426
387
|
newChild.on('exit', (code) => {
|
|
427
388
|
process.exit(code || 0)
|
|
428
389
|
})
|
|
429
390
|
|
|
430
|
-
|
|
431
|
-
return new Promise(() => {}) // Never resolves, keeps wrapper alive
|
|
391
|
+
return new Promise(() => {})
|
|
432
392
|
}
|
|
433
393
|
} catch (error) {
|
|
434
|
-
//
|
|
394
|
+
// Ignore update failures
|
|
435
395
|
}
|
|
436
396
|
}
|
|
437
397
|
|
|
438
398
|
async function main() {
|
|
439
|
-
// Bold, bright warning for staging environment
|
|
440
399
|
console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
|
|
441
400
|
console.log('\x1b[1m\x1b[93m❄️ CODECANE STAGING ENVIRONMENT ❄️\x1b[0m')
|
|
442
401
|
console.log(
|
|
443
|
-
'\x1b[1m\x1b[91mFOR TESTING PURPOSES ONLY - NOT FOR PRODUCTION USE\x1b[0m'
|
|
402
|
+
'\x1b[1m\x1b[91mFOR TESTING PURPOSES ONLY - NOT FOR PRODUCTION USE\x1b[0m',
|
|
444
403
|
)
|
|
445
404
|
console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
|
|
446
405
|
console.log('')
|
|
447
406
|
|
|
448
407
|
await ensureBinaryExists()
|
|
449
408
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
[packageName, ...process.argv.slice(2)],
|
|
454
|
-
{
|
|
455
|
-
stdio: 'inherit',
|
|
456
|
-
}
|
|
457
|
-
)
|
|
409
|
+
const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
410
|
+
stdio: 'inherit',
|
|
411
|
+
})
|
|
458
412
|
|
|
459
|
-
// Store reference to the exit listener so we can remove it during updates
|
|
460
413
|
const exitListener = (code) => {
|
|
461
414
|
process.exit(code || 0)
|
|
462
415
|
}
|
|
463
416
|
|
|
464
417
|
child.on('exit', exitListener)
|
|
465
418
|
|
|
466
|
-
// Check for updates in background
|
|
467
419
|
setTimeout(() => {
|
|
468
420
|
checkForUpdates(child, exitListener)
|
|
469
421
|
}, 100)
|
|
470
422
|
}
|
|
471
423
|
|
|
472
|
-
// Run the main function
|
|
473
424
|
main().catch((error) => {
|
|
474
425
|
console.error('❌ Unexpected error:', error.message)
|
|
475
426
|
process.exit(1)
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecane",
|
|
3
|
-
"version": "1.0.420-beta.
|
|
4
|
-
"description": "AI coding agent (staging)",
|
|
3
|
+
"version": "1.0.420-beta.260",
|
|
4
|
+
"description": "AI coding agent CLI (staging)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codecane": "index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"
|
|
10
|
+
"postinstall": "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' ? 'codecane.exe' : 'codecane'); try { fs.unlinkSync(binaryPath) } catch (e) { /* ignore if file doesn't exist */ }\""
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"index.js",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
},
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/CodebuffAI/codebuff
|
|
33
|
+
"url": "https://github.com/CodebuffAI/codebuff.git"
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://codebuff.com",
|
|
36
36
|
"publishConfig": {
|