codecane 1.0.420-beta.26 → 1.0.420-beta.261
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 +169 -169
- package/package.json +5 -3
- package/postinstall.js +36 -0
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,14 +1,14 @@
|
|
|
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 os = require('os')
|
|
7
|
+
const path = require('path')
|
|
8
8
|
const zlib = require('zlib')
|
|
9
|
+
|
|
9
10
|
const tar = require('tar')
|
|
10
11
|
|
|
11
|
-
// Hardcoded package name for codecane
|
|
12
12
|
const packageName = 'codecane'
|
|
13
13
|
|
|
14
14
|
function createConfig(packageName) {
|
|
@@ -22,16 +22,15 @@ function createConfig(packageName) {
|
|
|
22
22
|
configDir,
|
|
23
23
|
binaryName,
|
|
24
24
|
binaryPath: path.join(configDir, binaryName),
|
|
25
|
-
|
|
25
|
+
metadataPath: path.join(configDir, 'codecane-metadata.json'),
|
|
26
|
+
tempDownloadDir: path.join(configDir, '.download-temp-staging'),
|
|
26
27
|
userAgent: `${packageName}-cli`,
|
|
27
28
|
requestTimeout: 20000,
|
|
28
|
-
isPrerelease: true, // codecane always looks for prereleases
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const CONFIG = createConfig(packageName)
|
|
33
33
|
|
|
34
|
-
// Platform target mapping
|
|
35
34
|
const PLATFORM_TARGETS = {
|
|
36
35
|
'linux-x64': `${packageName}-linux-x64.tar.gz`,
|
|
37
36
|
'linux-arm64': `${packageName}-linux-arm64.tar.gz`,
|
|
@@ -40,7 +39,6 @@ const PLATFORM_TARGETS = {
|
|
|
40
39
|
'win32-x64': `${packageName}-win32-x64.tar.gz`,
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
// Terminal utilities
|
|
44
42
|
const term = {
|
|
45
43
|
clearLine: () => {
|
|
46
44
|
if (process.stderr.isTTY) {
|
|
@@ -57,7 +55,6 @@ const term = {
|
|
|
57
55
|
},
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
// Utility functions
|
|
61
58
|
function httpGet(url, options = {}) {
|
|
62
59
|
return new Promise((resolve, reject) => {
|
|
63
60
|
const parsedUrl = new URL(url)
|
|
@@ -70,13 +67,6 @@ function httpGet(url, options = {}) {
|
|
|
70
67
|
},
|
|
71
68
|
}
|
|
72
69
|
|
|
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
70
|
const req = https.get(reqOptions, (res) => {
|
|
81
71
|
if (res.statusCode === 302 || res.statusCode === 301) {
|
|
82
72
|
return httpGet(new URL(res.headers.location, url).href, options)
|
|
@@ -99,43 +89,15 @@ function httpGet(url, options = {}) {
|
|
|
99
89
|
async function getLatestVersion() {
|
|
100
90
|
try {
|
|
101
91
|
const res = await httpGet(
|
|
102
|
-
`https://
|
|
92
|
+
`https://registry.npmjs.org/${packageName}/latest`,
|
|
103
93
|
)
|
|
104
94
|
|
|
105
95
|
if (res.statusCode !== 200) return null
|
|
106
96
|
|
|
107
97
|
const body = await streamToString(res)
|
|
98
|
+
const packageData = JSON.parse(body)
|
|
108
99
|
|
|
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]
|
|
100
|
+
return packageData.version || null
|
|
139
101
|
} catch (error) {
|
|
140
102
|
return null
|
|
141
103
|
}
|
|
@@ -151,58 +113,75 @@ function streamToString(stream) {
|
|
|
151
113
|
}
|
|
152
114
|
|
|
153
115
|
function getCurrentVersion() {
|
|
154
|
-
if (!fs.existsSync(CONFIG.binaryPath)) return null
|
|
155
|
-
|
|
156
116
|
try {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
117
|
+
if (!fs.existsSync(CONFIG.metadataPath)) {
|
|
118
|
+
return null
|
|
119
|
+
}
|
|
120
|
+
const metadata = JSON.parse(fs.readFileSync(CONFIG.metadataPath, 'utf8'))
|
|
121
|
+
// Also verify the binary still exists
|
|
122
|
+
if (!fs.existsSync(CONFIG.binaryPath)) {
|
|
123
|
+
return null
|
|
124
|
+
}
|
|
125
|
+
return metadata.version || null
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return null
|
|
128
|
+
}
|
|
129
|
+
}
|
|
162
130
|
|
|
163
|
-
|
|
164
|
-
|
|
131
|
+
function runSmokeTest(binaryPath) {
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
if (!fs.existsSync(binaryPath)) {
|
|
134
|
+
resolve(false)
|
|
135
|
+
return
|
|
136
|
+
}
|
|
165
137
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
138
|
+
const child = spawn(binaryPath, ['--version'], {
|
|
139
|
+
cwd: os.homedir(),
|
|
140
|
+
stdio: 'pipe',
|
|
141
|
+
})
|
|
169
142
|
|
|
170
|
-
|
|
171
|
-
errorOutput += data.toString()
|
|
172
|
-
})
|
|
143
|
+
let output = ''
|
|
173
144
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
if (!child.killed) {
|
|
178
|
-
child.kill('SIGKILL')
|
|
179
|
-
}
|
|
180
|
-
}, 1000)
|
|
181
|
-
resolve('error')
|
|
182
|
-
}, 1000)
|
|
145
|
+
child.stdout.on('data', (data) => {
|
|
146
|
+
output += data.toString()
|
|
147
|
+
})
|
|
183
148
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
resolve('error')
|
|
149
|
+
const timeout = setTimeout(() => {
|
|
150
|
+
child.kill('SIGTERM')
|
|
151
|
+
setTimeout(() => {
|
|
152
|
+
if (!child.killed) {
|
|
153
|
+
child.kill('SIGKILL')
|
|
190
154
|
}
|
|
191
|
-
})
|
|
155
|
+
}, 1000)
|
|
156
|
+
resolve(false)
|
|
157
|
+
}, 5000)
|
|
158
|
+
|
|
159
|
+
child.on('exit', (code) => {
|
|
160
|
+
clearTimeout(timeout)
|
|
161
|
+
// Check that it exits successfully and outputs something that looks like a version
|
|
162
|
+
if (code === 0 && output.trim().match(/^\d+(\.\d+)*(-beta\.\d+)?$/)) {
|
|
163
|
+
resolve(true)
|
|
164
|
+
} else {
|
|
165
|
+
resolve(false)
|
|
166
|
+
}
|
|
167
|
+
})
|
|
192
168
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
})
|
|
169
|
+
child.on('error', () => {
|
|
170
|
+
clearTimeout(timeout)
|
|
171
|
+
resolve(false)
|
|
197
172
|
})
|
|
198
|
-
}
|
|
199
|
-
return 'error'
|
|
200
|
-
}
|
|
173
|
+
})
|
|
201
174
|
}
|
|
202
175
|
|
|
203
176
|
function compareVersions(v1, v2) {
|
|
204
177
|
if (!v1 || !v2) return 0
|
|
205
178
|
|
|
179
|
+
// Always update if the current version is not a valid semver
|
|
180
|
+
// e.g. 1.0.420-beta.1
|
|
181
|
+
if (!v1.match(/^\d+(\.\d+)*$/)) {
|
|
182
|
+
return -1
|
|
183
|
+
}
|
|
184
|
+
|
|
206
185
|
const parseVersion = (version) => {
|
|
207
186
|
const parts = version.split('-')
|
|
208
187
|
const mainParts = parts[0].split('.').map(Number)
|
|
@@ -213,7 +192,6 @@ function compareVersions(v1, v2) {
|
|
|
213
192
|
const p1 = parseVersion(v1)
|
|
214
193
|
const p2 = parseVersion(v2)
|
|
215
194
|
|
|
216
|
-
// Compare main version parts
|
|
217
195
|
for (let i = 0; i < Math.max(p1.main.length, p2.main.length); i++) {
|
|
218
196
|
const n1 = p1.main[i] || 0
|
|
219
197
|
const n2 = p2.main[i] || 0
|
|
@@ -222,15 +200,13 @@ function compareVersions(v1, v2) {
|
|
|
222
200
|
if (n1 > n2) return 1
|
|
223
201
|
}
|
|
224
202
|
|
|
225
|
-
// If main versions are equal, compare prerelease parts
|
|
226
203
|
if (p1.prerelease.length === 0 && p2.prerelease.length === 0) {
|
|
227
|
-
return 0
|
|
204
|
+
return 0
|
|
228
205
|
} else if (p1.prerelease.length === 0) {
|
|
229
|
-
return 1
|
|
206
|
+
return 1
|
|
230
207
|
} else if (p2.prerelease.length === 0) {
|
|
231
|
-
return -1
|
|
208
|
+
return -1
|
|
232
209
|
} else {
|
|
233
|
-
// Both have prerelease parts, compare them
|
|
234
210
|
for (
|
|
235
211
|
let i = 0;
|
|
236
212
|
i < Math.max(p1.prerelease.length, p2.prerelease.length);
|
|
@@ -239,7 +215,6 @@ function compareVersions(v1, v2) {
|
|
|
239
215
|
const pr1 = p1.prerelease[i] || ''
|
|
240
216
|
const pr2 = p2.prerelease[i] || ''
|
|
241
217
|
|
|
242
|
-
// Handle numeric vs. string parts
|
|
243
218
|
const isNum1 = !isNaN(parseInt(pr1))
|
|
244
219
|
const isNum2 = !isNaN(parseInt(pr2))
|
|
245
220
|
|
|
@@ -249,16 +224,16 @@ function compareVersions(v1, v2) {
|
|
|
249
224
|
if (num1 < num2) return -1
|
|
250
225
|
if (num1 > num2) return 1
|
|
251
226
|
} else if (isNum1 && !isNum2) {
|
|
252
|
-
return 1
|
|
227
|
+
return 1
|
|
253
228
|
} else if (!isNum1 && isNum2) {
|
|
254
229
|
return -1
|
|
255
|
-
} else {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
230
|
+
} else if (pr1 < pr2) {
|
|
231
|
+
return -1
|
|
232
|
+
} else if (pr1 > pr2) {
|
|
233
|
+
return 1
|
|
259
234
|
}
|
|
260
235
|
}
|
|
261
|
-
return 0
|
|
236
|
+
return 0
|
|
262
237
|
}
|
|
263
238
|
}
|
|
264
239
|
|
|
@@ -284,20 +259,25 @@ async function downloadBinary(version) {
|
|
|
284
259
|
throw new Error(`Unsupported platform: ${process.platform} ${process.arch}`)
|
|
285
260
|
}
|
|
286
261
|
|
|
287
|
-
const downloadUrl =
|
|
262
|
+
const downloadUrl = `${
|
|
263
|
+
process.env.NEXT_PUBLIC_CODEBUFF_APP_URL || 'https://codebuff.com'
|
|
264
|
+
}/api/releases/download/${version}/${fileName}`
|
|
288
265
|
|
|
289
266
|
// Ensure config directory exists
|
|
290
267
|
fs.mkdirSync(CONFIG.configDir, { recursive: true })
|
|
291
268
|
|
|
292
|
-
|
|
293
|
-
|
|
269
|
+
// Clean up any previous temp download directory
|
|
270
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
271
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
294
272
|
}
|
|
273
|
+
fs.mkdirSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
295
274
|
|
|
296
275
|
term.write('Downloading...')
|
|
297
276
|
|
|
298
277
|
const res = await httpGet(downloadUrl)
|
|
299
278
|
|
|
300
279
|
if (res.statusCode !== 200) {
|
|
280
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
301
281
|
throw new Error(`Download failed: HTTP ${res.statusCode}`)
|
|
302
282
|
}
|
|
303
283
|
|
|
@@ -314,8 +294,8 @@ async function downloadBinary(version) {
|
|
|
314
294
|
const pct = Math.round((downloadedSize / totalSize) * 100)
|
|
315
295
|
term.write(
|
|
316
296
|
`Downloading... ${createProgressBar(pct)} ${pct}% of ${formatBytes(
|
|
317
|
-
totalSize
|
|
318
|
-
)}
|
|
297
|
+
totalSize,
|
|
298
|
+
)}`,
|
|
319
299
|
)
|
|
320
300
|
} else {
|
|
321
301
|
term.write(`Downloading... ${formatBytes(downloadedSize)}`)
|
|
@@ -323,32 +303,71 @@ async function downloadBinary(version) {
|
|
|
323
303
|
}
|
|
324
304
|
})
|
|
325
305
|
|
|
306
|
+
// Extract to temp directory
|
|
326
307
|
await new Promise((resolve, reject) => {
|
|
327
308
|
res
|
|
328
309
|
.pipe(zlib.createGunzip())
|
|
329
|
-
.pipe(tar.x({ cwd: CONFIG.
|
|
310
|
+
.pipe(tar.x({ cwd: CONFIG.tempDownloadDir }))
|
|
330
311
|
.on('finish', resolve)
|
|
331
312
|
.on('error', reject)
|
|
332
313
|
})
|
|
333
314
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
315
|
+
const tempBinaryPath = path.join(CONFIG.tempDownloadDir, CONFIG.binaryName)
|
|
316
|
+
|
|
317
|
+
// Verify the binary was extracted
|
|
318
|
+
if (!fs.existsSync(tempBinaryPath)) {
|
|
319
|
+
const files = fs.readdirSync(CONFIG.tempDownloadDir)
|
|
320
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
321
|
+
throw new Error(
|
|
322
|
+
`Binary not found after extraction. Expected: ${CONFIG.binaryName}, Available files: ${files.join(', ')}`,
|
|
323
|
+
)
|
|
324
|
+
}
|
|
338
325
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
326
|
+
// Set executable permissions
|
|
327
|
+
if (process.platform !== 'win32') {
|
|
328
|
+
fs.chmodSync(tempBinaryPath, 0o755)
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Run smoke test on the downloaded binary
|
|
332
|
+
term.write('Verifying download...')
|
|
333
|
+
const smokeTestPassed = await runSmokeTest(tempBinaryPath)
|
|
334
|
+
|
|
335
|
+
if (!smokeTestPassed) {
|
|
336
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
337
|
+
throw new Error('Downloaded binary failed smoke test (--version check)')
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Smoke test passed - move binary to final location
|
|
341
|
+
try {
|
|
342
|
+
if (fs.existsSync(CONFIG.binaryPath)) {
|
|
343
|
+
try {
|
|
344
|
+
fs.unlinkSync(CONFIG.binaryPath)
|
|
345
|
+
} catch (err) {
|
|
346
|
+
// Fallback: try renaming the locked/undeletable binary (Windows)
|
|
347
|
+
const backupPath = CONFIG.binaryPath + `.old.${Date.now()}`
|
|
348
|
+
try {
|
|
349
|
+
fs.renameSync(CONFIG.binaryPath, backupPath)
|
|
350
|
+
} catch (renameErr) {
|
|
351
|
+
throw new Error(
|
|
352
|
+
`Failed to replace existing binary. ` +
|
|
353
|
+
`unlink error: ${err.code || err.message}, ` +
|
|
354
|
+
`rename error: ${renameErr.code || renameErr.message}`,
|
|
355
|
+
)
|
|
356
|
+
}
|
|
342
357
|
}
|
|
343
|
-
} else {
|
|
344
|
-
throw new Error(
|
|
345
|
-
`Binary not found after extraction. Expected: ${extractedPath}, Available files: ${files.join(', ')}`
|
|
346
|
-
)
|
|
347
358
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
359
|
+
fs.renameSync(tempBinaryPath, CONFIG.binaryPath)
|
|
360
|
+
|
|
361
|
+
// Save version metadata for fast version checking
|
|
362
|
+
fs.writeFileSync(
|
|
363
|
+
CONFIG.metadataPath,
|
|
364
|
+
JSON.stringify({ version }, null, 2),
|
|
365
|
+
)
|
|
366
|
+
} finally {
|
|
367
|
+
// Clean up temp directory even if rename fails
|
|
368
|
+
if (fs.existsSync(CONFIG.tempDownloadDir)) {
|
|
369
|
+
fs.rmSync(CONFIG.tempDownloadDir, { recursive: true })
|
|
370
|
+
}
|
|
352
371
|
}
|
|
353
372
|
|
|
354
373
|
term.clearLine()
|
|
@@ -356,50 +375,47 @@ async function downloadBinary(version) {
|
|
|
356
375
|
}
|
|
357
376
|
|
|
358
377
|
async function ensureBinaryExists() {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
console.error('Please check your internet connection and try again')
|
|
364
|
-
process.exit(1)
|
|
365
|
-
}
|
|
378
|
+
const currentVersion = getCurrentVersion()
|
|
379
|
+
if (currentVersion !== null) {
|
|
380
|
+
return
|
|
381
|
+
}
|
|
366
382
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
383
|
+
const version = await getLatestVersion()
|
|
384
|
+
if (!version) {
|
|
385
|
+
console.error('❌ Failed to determine latest version')
|
|
386
|
+
console.error('Please check your internet connection and try again')
|
|
387
|
+
process.exit(1)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
try {
|
|
391
|
+
await downloadBinary(version)
|
|
392
|
+
} catch (error) {
|
|
393
|
+
term.clearLine()
|
|
394
|
+
console.error('❌ Failed to download codecane:', error.message)
|
|
395
|
+
console.error('Please check your internet connection and try again')
|
|
396
|
+
process.exit(1)
|
|
375
397
|
}
|
|
376
398
|
}
|
|
377
399
|
|
|
378
400
|
async function checkForUpdates(runningProcess, exitListener) {
|
|
379
401
|
try {
|
|
380
|
-
const currentVersion =
|
|
381
|
-
if (!currentVersion) return
|
|
402
|
+
const currentVersion = getCurrentVersion()
|
|
382
403
|
|
|
383
404
|
const latestVersion = await getLatestVersion()
|
|
384
405
|
if (!latestVersion) return
|
|
385
406
|
|
|
386
407
|
if (
|
|
387
|
-
// Download new version if current
|
|
388
|
-
currentVersion ===
|
|
408
|
+
// Download new version if current version is unknown or outdated.
|
|
409
|
+
currentVersion === null ||
|
|
389
410
|
compareVersions(currentVersion, latestVersion) < 0
|
|
390
411
|
) {
|
|
391
412
|
term.clearLine()
|
|
392
413
|
|
|
393
|
-
// Remove the specific exit listener to prevent it from interfering with the update
|
|
394
414
|
runningProcess.removeListener('exit', exitListener)
|
|
395
|
-
|
|
396
|
-
// Kill the running process
|
|
397
415
|
runningProcess.kill('SIGTERM')
|
|
398
416
|
|
|
399
|
-
// Wait for the process to actually exit
|
|
400
417
|
await new Promise((resolve) => {
|
|
401
418
|
runningProcess.on('exit', resolve)
|
|
402
|
-
// Fallback timeout in case the process doesn't exit gracefully
|
|
403
419
|
setTimeout(() => {
|
|
404
420
|
if (!runningProcess.killed) {
|
|
405
421
|
runningProcess.kill('SIGKILL')
|
|
@@ -412,64 +428,48 @@ async function checkForUpdates(runningProcess, exitListener) {
|
|
|
412
428
|
|
|
413
429
|
await downloadBinary(latestVersion)
|
|
414
430
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
{
|
|
420
|
-
stdio: 'inherit',
|
|
421
|
-
detached: false,
|
|
422
|
-
}
|
|
423
|
-
)
|
|
431
|
+
const newChild = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
432
|
+
stdio: 'inherit',
|
|
433
|
+
detached: false,
|
|
434
|
+
})
|
|
424
435
|
|
|
425
|
-
// Set up exit handler for the new process
|
|
426
436
|
newChild.on('exit', (code) => {
|
|
427
437
|
process.exit(code || 0)
|
|
428
438
|
})
|
|
429
439
|
|
|
430
|
-
|
|
431
|
-
return new Promise(() => {}) // Never resolves, keeps wrapper alive
|
|
440
|
+
return new Promise(() => {})
|
|
432
441
|
}
|
|
433
442
|
} catch (error) {
|
|
434
|
-
//
|
|
443
|
+
// Ignore update failures
|
|
435
444
|
}
|
|
436
445
|
}
|
|
437
446
|
|
|
438
447
|
async function main() {
|
|
439
|
-
// Bold, bright warning for staging environment
|
|
440
448
|
console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
|
|
441
449
|
console.log('\x1b[1m\x1b[93m❄️ CODECANE STAGING ENVIRONMENT ❄️\x1b[0m')
|
|
442
450
|
console.log(
|
|
443
|
-
'\x1b[1m\x1b[91mFOR TESTING PURPOSES ONLY - NOT FOR PRODUCTION USE\x1b[0m'
|
|
451
|
+
'\x1b[1m\x1b[91mFOR TESTING PURPOSES ONLY - NOT FOR PRODUCTION USE\x1b[0m',
|
|
444
452
|
)
|
|
445
453
|
console.log('\x1b[1m\x1b[91m' + '='.repeat(60) + '\x1b[0m')
|
|
446
454
|
console.log('')
|
|
447
455
|
|
|
448
456
|
await ensureBinaryExists()
|
|
449
457
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
[packageName, ...process.argv.slice(2)],
|
|
454
|
-
{
|
|
455
|
-
stdio: 'inherit',
|
|
456
|
-
}
|
|
457
|
-
)
|
|
458
|
+
const child = spawn(CONFIG.binaryPath, process.argv.slice(2), {
|
|
459
|
+
stdio: 'inherit',
|
|
460
|
+
})
|
|
458
461
|
|
|
459
|
-
// Store reference to the exit listener so we can remove it during updates
|
|
460
462
|
const exitListener = (code) => {
|
|
461
463
|
process.exit(code || 0)
|
|
462
464
|
}
|
|
463
465
|
|
|
464
466
|
child.on('exit', exitListener)
|
|
465
467
|
|
|
466
|
-
// Check for updates in background
|
|
467
468
|
setTimeout(() => {
|
|
468
469
|
checkForUpdates(child, exitListener)
|
|
469
470
|
}, 100)
|
|
470
471
|
}
|
|
471
472
|
|
|
472
|
-
// Run the main function
|
|
473
473
|
main().catch((error) => {
|
|
474
474
|
console.error('❌ Unexpected error:', error.message)
|
|
475
475
|
process.exit(1)
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codecane",
|
|
3
|
-
"version": "1.0.420-beta.
|
|
4
|
-
"description": "AI coding agent (staging)",
|
|
3
|
+
"version": "1.0.420-beta.261",
|
|
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
|
+
"postinstall": "node postinstall.js",
|
|
10
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' ? 'codecane.exe' : 'codecane'); try { fs.unlinkSync(binaryPath) } catch (e) { /* ignore if file doesn't exist */ }\""
|
|
11
12
|
},
|
|
12
13
|
"files": [
|
|
13
14
|
"index.js",
|
|
15
|
+
"postinstall.js",
|
|
14
16
|
"README.md"
|
|
15
17
|
],
|
|
16
18
|
"os": [
|
|
@@ -30,7 +32,7 @@
|
|
|
30
32
|
},
|
|
31
33
|
"repository": {
|
|
32
34
|
"type": "git",
|
|
33
|
-
"url": "https://github.com/CodebuffAI/codebuff
|
|
35
|
+
"url": "https://github.com/CodebuffAI/codebuff.git"
|
|
34
36
|
},
|
|
35
37
|
"homepage": "https://codebuff.com",
|
|
36
38
|
"publishConfig": {
|
package/postinstall.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
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' ? 'codecane.exe' : 'codecane'
|
|
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 Codecane (Staging)!');
|
|
24
|
+
console.log('\n');
|
|
25
|
+
console.log('⚠️ This is a staging/beta release for testing purposes.');
|
|
26
|
+
console.log('\n');
|
|
27
|
+
console.log('To get started:');
|
|
28
|
+
console.log(' 1. cd to your project directory');
|
|
29
|
+
console.log(' 2. Run: codecane');
|
|
30
|
+
console.log('\n');
|
|
31
|
+
console.log('Example:');
|
|
32
|
+
console.log(' $ cd ~/my-project');
|
|
33
|
+
console.log(' $ codecane');
|
|
34
|
+
console.log('\n');
|
|
35
|
+
console.log('For more information, visit: https://codebuff.com/docs');
|
|
36
|
+
console.log('\n');
|