codo-ai 2.23.13 → 2.23.14
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/bin/codo.js +307 -0
- package/package.json +23 -18
- package/bin/codo +0 -10
- package/postinstall.mjs +0 -222
package/bin/codo.js
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const childProcess = require("child_process")
|
|
4
|
+
const fs = require("fs")
|
|
5
|
+
const path = require("path")
|
|
6
|
+
const os = require("os")
|
|
7
|
+
|
|
8
|
+
const forwardedSignals = ["SIGINT", "SIGTERM", "SIGHUP"]
|
|
9
|
+
|
|
10
|
+
function run(target) {
|
|
11
|
+
const child = childProcess.spawn(target, process.argv.slice(2), {
|
|
12
|
+
stdio: "inherit",
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
child.on("error", (error) => {
|
|
16
|
+
console.error(error.message)
|
|
17
|
+
process.exit(1)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const forwarders = {}
|
|
21
|
+
for (const signal of forwardedSignals) {
|
|
22
|
+
forwarders[signal] = () => {
|
|
23
|
+
try {
|
|
24
|
+
child.kill(signal)
|
|
25
|
+
} catch {
|
|
26
|
+
// The child may have already exited.
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
process.on(signal, forwarders[signal])
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
child.on("exit", (code, signal) => {
|
|
33
|
+
for (const forwardedSignal of forwardedSignals) {
|
|
34
|
+
process.removeListener(forwardedSignal, forwarders[forwardedSignal])
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (signal) {
|
|
38
|
+
process.kill(process.pid, signal)
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
process.exit(typeof code === "number" ? code : 0)
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const envPath = process.env.CODO_BIN_PATH
|
|
47
|
+
|
|
48
|
+
const scriptPath = fs.realpathSync(__filename)
|
|
49
|
+
const scriptDir = path.dirname(scriptPath)
|
|
50
|
+
|
|
51
|
+
const platformMap = {
|
|
52
|
+
darwin: "darwin",
|
|
53
|
+
linux: "linux",
|
|
54
|
+
win32: "windows",
|
|
55
|
+
}
|
|
56
|
+
const archMap = {
|
|
57
|
+
x64: "x64",
|
|
58
|
+
arm64: "arm64",
|
|
59
|
+
arm: "arm",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let platform = platformMap[os.platform()]
|
|
63
|
+
if (!platform) {
|
|
64
|
+
platform = os.platform()
|
|
65
|
+
}
|
|
66
|
+
let arch = archMap[os.arch()]
|
|
67
|
+
if (!arch) {
|
|
68
|
+
arch = os.arch()
|
|
69
|
+
}
|
|
70
|
+
const base = "codo-" + platform + "-" + arch
|
|
71
|
+
const binary = platform === "windows" ? "codo.exe" : "codo"
|
|
72
|
+
|
|
73
|
+
function supportsAvx2() {
|
|
74
|
+
if (arch !== "x64") return false
|
|
75
|
+
|
|
76
|
+
if (platform === "linux") {
|
|
77
|
+
try {
|
|
78
|
+
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
79
|
+
} catch {
|
|
80
|
+
return false
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (platform === "darwin") {
|
|
85
|
+
try {
|
|
86
|
+
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
87
|
+
encoding: "utf8",
|
|
88
|
+
timeout: 1500,
|
|
89
|
+
})
|
|
90
|
+
if (result.status !== 0) return false
|
|
91
|
+
return (result.stdout || "").trim() === "1"
|
|
92
|
+
} catch {
|
|
93
|
+
return false
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (platform === "windows") {
|
|
98
|
+
const cmd =
|
|
99
|
+
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
100
|
+
|
|
101
|
+
for (const exe of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
102
|
+
try {
|
|
103
|
+
const result = childProcess.spawnSync(exe, ["-NoProfile", "-NonInteractive", "-Command", cmd], {
|
|
104
|
+
encoding: "utf8",
|
|
105
|
+
timeout: 3000,
|
|
106
|
+
windowsHide: true,
|
|
107
|
+
})
|
|
108
|
+
if (result.status !== 0) continue
|
|
109
|
+
const out = (result.stdout || "").trim().toLowerCase()
|
|
110
|
+
if (out === "true" || out === "1") return true
|
|
111
|
+
if (out === "false" || out === "0") return false
|
|
112
|
+
} catch {
|
|
113
|
+
continue
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return false
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return false
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const names = (() => {
|
|
124
|
+
const avx2 = supportsAvx2()
|
|
125
|
+
const baseline = arch === "x64" && !avx2
|
|
126
|
+
|
|
127
|
+
if (platform === "linux") {
|
|
128
|
+
const musl = (() => {
|
|
129
|
+
try {
|
|
130
|
+
if (fs.existsSync("/etc/alpine-release")) return true
|
|
131
|
+
} catch {
|
|
132
|
+
// ignore
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
137
|
+
const text = ((result.stdout || "") + (result.stderr || "")).toLowerCase()
|
|
138
|
+
if (text.includes("musl")) return true
|
|
139
|
+
} catch {
|
|
140
|
+
// ignore
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return false
|
|
144
|
+
})()
|
|
145
|
+
|
|
146
|
+
if (musl) {
|
|
147
|
+
if (arch === "x64") {
|
|
148
|
+
if (baseline) return [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
149
|
+
return [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
150
|
+
}
|
|
151
|
+
return [`${base}-musl`, base]
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (arch === "x64") {
|
|
155
|
+
if (baseline) return [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
156
|
+
return [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
157
|
+
}
|
|
158
|
+
return [base, `${base}-musl`]
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (arch === "x64") {
|
|
162
|
+
if (baseline) return [`${base}-baseline`, base]
|
|
163
|
+
return [base, `${base}-baseline`]
|
|
164
|
+
}
|
|
165
|
+
return [base]
|
|
166
|
+
})()
|
|
167
|
+
|
|
168
|
+
// Published platform packages name the binary with a version suffix
|
|
169
|
+
// ("codo-2.23.12" / "codo-2.23.12.exe"), so resolve by pattern rather than
|
|
170
|
+
// by a single hardcoded filename. Prefer an exact (unsuffixed) match so a
|
|
171
|
+
// binary staged by postinstall wins over a stale suffixed one.
|
|
172
|
+
function findBinaryIn(packageDir) {
|
|
173
|
+
let entries
|
|
174
|
+
try {
|
|
175
|
+
entries = fs.readdirSync(path.join(packageDir, "bin"))
|
|
176
|
+
} catch {
|
|
177
|
+
return undefined
|
|
178
|
+
}
|
|
179
|
+
if (entries.includes(binary)) return path.join(packageDir, "bin", binary)
|
|
180
|
+
const suffix = platform === "windows" ? ".exe" : ""
|
|
181
|
+
const match = entries
|
|
182
|
+
.filter((entry) => entry.startsWith("codo-") && (suffix === "" ? !entry.endsWith(".exe") : entry.endsWith(suffix)))
|
|
183
|
+
.sort()
|
|
184
|
+
.at(-1)
|
|
185
|
+
if (!match) return undefined
|
|
186
|
+
return path.join(packageDir, "bin", match)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function findBinary(startDir) {
|
|
190
|
+
let current = startDir
|
|
191
|
+
for (;;) {
|
|
192
|
+
const modules = path.join(current, "node_modules")
|
|
193
|
+
if (fs.existsSync(modules)) {
|
|
194
|
+
for (const name of names) {
|
|
195
|
+
const candidate = findBinaryIn(path.join(modules, `@codo-ai/${name}`))
|
|
196
|
+
if (candidate && fs.existsSync(candidate)) return candidate
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const parent = path.dirname(current)
|
|
200
|
+
if (parent === current) {
|
|
201
|
+
return
|
|
202
|
+
}
|
|
203
|
+
current = parent
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// A fetched binary is staged next to this launcher so it can be found
|
|
208
|
+
// without walking node_modules on later runs.
|
|
209
|
+
const stagedName = platform === "windows" ? "codo.exe" : "codo"
|
|
210
|
+
const staged = path.join(scriptDir, stagedName)
|
|
211
|
+
const stagedBinary = (() => {
|
|
212
|
+
try {
|
|
213
|
+
if (fs.existsSync(staged)) return staged
|
|
214
|
+
} catch {
|
|
215
|
+
// ignore
|
|
216
|
+
}
|
|
217
|
+
return undefined
|
|
218
|
+
})()
|
|
219
|
+
|
|
220
|
+
const wrapperPkg = (() => {
|
|
221
|
+
try {
|
|
222
|
+
return JSON.parse(fs.readFileSync(path.join(scriptDir, "..", "package.json"), "utf8"))
|
|
223
|
+
} catch {
|
|
224
|
+
return {}
|
|
225
|
+
}
|
|
226
|
+
})()
|
|
227
|
+
|
|
228
|
+
// Last resort: download the platform package on demand with npm and stage
|
|
229
|
+
// its binary next to this launcher. Runs only when the package manager
|
|
230
|
+
// skipped the optional platform packages at install time.
|
|
231
|
+
function fetchBinary() {
|
|
232
|
+
const deps = wrapperPkg.optionalDependencies || {}
|
|
233
|
+
for (const name of names) {
|
|
234
|
+
const scoped = `@codo-ai/${name}`
|
|
235
|
+
const version = deps[scoped]
|
|
236
|
+
if (!version) continue
|
|
237
|
+
let tmp
|
|
238
|
+
try {
|
|
239
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), "codo-bin-"))
|
|
240
|
+
} catch {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
// `npm` is a shell script on Windows, so plain spawn fails with
|
|
245
|
+
// ENOENT. Prefer node + npm-cli.js (no shell, no quoting hazards);
|
|
246
|
+
// fall back to a shell spawn only when the CLI file is missing.
|
|
247
|
+
let cmd = "npm"
|
|
248
|
+
const args = ["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", tmp, `${scoped}@${version}`]
|
|
249
|
+
const opts = { encoding: "utf8", timeout: 300000, windowsHide: true }
|
|
250
|
+
if (process.platform === "win32") {
|
|
251
|
+
const cli = path.join(path.dirname(process.execPath), "node_modules", "npm", "bin", "npm-cli.js")
|
|
252
|
+
let cliExists = false
|
|
253
|
+
try {
|
|
254
|
+
cliExists = fs.existsSync(cli)
|
|
255
|
+
} catch {
|
|
256
|
+
cliExists = false
|
|
257
|
+
}
|
|
258
|
+
if (cliExists) {
|
|
259
|
+
cmd = process.execPath
|
|
260
|
+
args.unshift(cli)
|
|
261
|
+
} else {
|
|
262
|
+
opts.shell = true
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const result = childProcess.spawnSync(cmd, args, opts)
|
|
266
|
+
if (result.status !== 0) continue
|
|
267
|
+
const found = findBinaryIn(path.join(tmp, "node_modules", scoped))
|
|
268
|
+
if (!found) continue
|
|
269
|
+
try {
|
|
270
|
+
if (!fs.existsSync(found)) continue
|
|
271
|
+
} catch {
|
|
272
|
+
continue
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
fs.copyFileSync(found, staged)
|
|
276
|
+
try {
|
|
277
|
+
fs.chmodSync(staged, 0o755)
|
|
278
|
+
} catch {
|
|
279
|
+
// chmod is best-effort on Windows.
|
|
280
|
+
}
|
|
281
|
+
return staged
|
|
282
|
+
} catch {
|
|
283
|
+
return found
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
continue
|
|
287
|
+
} finally {
|
|
288
|
+
try {
|
|
289
|
+
fs.rmSync(tmp, { recursive: true, force: true })
|
|
290
|
+
} catch {
|
|
291
|
+
// ignore
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return undefined
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const resolved = envPath || stagedBinary || findBinary(scriptDir) || fetchBinary()
|
|
299
|
+
if (!resolved) {
|
|
300
|
+
console.error(
|
|
301
|
+
"Could not find the codo native binary. Your package manager may have skipped the optional platform packages. Try installing manually: " +
|
|
302
|
+
names.map((n) => `"@codo-ai/${n}"`).join(" or "),
|
|
303
|
+
)
|
|
304
|
+
process.exit(1)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
run(resolved)
|
package/package.json
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codo-ai",
|
|
3
|
-
"description": "The security-native AI coding agent. Six Sec-Test security personas, compliance-first /scraper, a business documents & research suite, and multi-agent Compose orchestration
|
|
3
|
+
"description": "The security-native AI coding agent. Six Sec-Test security personas, compliance-first /scraper, a business documents & research suite, and multi-agent Compose orchestration — spec-driven workflow engines in one terminal-first CLI.",
|
|
4
4
|
"keywords": [
|
|
5
5
|
"ai",
|
|
6
|
+
"ai-agent",
|
|
6
7
|
"coding-agent",
|
|
8
|
+
"coding-assistant",
|
|
7
9
|
"cli",
|
|
8
10
|
"terminal",
|
|
9
11
|
"tui",
|
|
@@ -16,8 +18,14 @@
|
|
|
16
18
|
"pentest",
|
|
17
19
|
"spec-driven",
|
|
18
20
|
"multi-agent",
|
|
21
|
+
"autonomous",
|
|
22
|
+
"mcp",
|
|
23
|
+
"devtools",
|
|
19
24
|
"opencode"
|
|
20
25
|
],
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18"
|
|
28
|
+
},
|
|
21
29
|
"homepage": "https://codo-ai.vercel.app/",
|
|
22
30
|
"repository": {
|
|
23
31
|
"type": "git",
|
|
@@ -27,12 +35,9 @@
|
|
|
27
35
|
"url": "https://github.com/Mosalah4351/COdo/issues"
|
|
28
36
|
},
|
|
29
37
|
"bin": {
|
|
30
|
-
"codo": "./bin/codo"
|
|
31
|
-
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"postinstall": "node ./postinstall.mjs"
|
|
38
|
+
"codo": "./bin/codo.js"
|
|
34
39
|
},
|
|
35
|
-
"version": "2.23.
|
|
40
|
+
"version": "2.23.14",
|
|
36
41
|
"license": "MIT",
|
|
37
42
|
"os": [
|
|
38
43
|
"darwin",
|
|
@@ -44,17 +49,17 @@
|
|
|
44
49
|
"x64"
|
|
45
50
|
],
|
|
46
51
|
"optionalDependencies": {
|
|
47
|
-
"@codo-ai/codo-windows-x64-baseline": "2.23.
|
|
48
|
-
"@codo-ai/codo-windows-x64": "2.23.
|
|
49
|
-
"@codo-ai/codo-windows-arm64": "2.23.
|
|
50
|
-
"@codo-ai/codo-linux-x64-musl": "2.23.
|
|
51
|
-
"@codo-ai/codo-linux-x64-baseline-musl": "2.23.
|
|
52
|
-
"@codo-ai/codo-linux-x64-baseline": "2.23.
|
|
53
|
-
"@codo-ai/codo-linux-x64": "2.23.
|
|
54
|
-
"@codo-ai/codo-linux-arm64-musl": "2.23.
|
|
55
|
-
"@codo-ai/codo-linux-arm64": "2.23.
|
|
56
|
-
"@codo-ai/codo-darwin-x64-baseline": "2.23.
|
|
57
|
-
"@codo-ai/codo-darwin-x64": "2.23.
|
|
58
|
-
"@codo-ai/codo-darwin-arm64": "2.23.
|
|
52
|
+
"@codo-ai/codo-windows-x64-baseline": "2.23.14",
|
|
53
|
+
"@codo-ai/codo-windows-x64": "2.23.14",
|
|
54
|
+
"@codo-ai/codo-windows-arm64": "2.23.14",
|
|
55
|
+
"@codo-ai/codo-linux-x64-musl": "2.23.14",
|
|
56
|
+
"@codo-ai/codo-linux-x64-baseline-musl": "2.23.14",
|
|
57
|
+
"@codo-ai/codo-linux-x64-baseline": "2.23.14",
|
|
58
|
+
"@codo-ai/codo-linux-x64": "2.23.14",
|
|
59
|
+
"@codo-ai/codo-linux-arm64-musl": "2.23.14",
|
|
60
|
+
"@codo-ai/codo-linux-arm64": "2.23.14",
|
|
61
|
+
"@codo-ai/codo-darwin-x64-baseline": "2.23.14",
|
|
62
|
+
"@codo-ai/codo-darwin-x64": "2.23.14",
|
|
63
|
+
"@codo-ai/codo-darwin-arm64": "2.23.14"
|
|
59
64
|
}
|
|
60
65
|
}
|
package/bin/codo
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
echo "Error: codo-ai's postinstall script was not run." >&2
|
|
2
|
-
echo "" >&2
|
|
3
|
-
echo "This occurs when using --ignore-scripts during installation, or when using a" >&2
|
|
4
|
-
echo "package manager like pnpm that does not run postinstall scripts by default." >&2
|
|
5
|
-
echo "" >&2
|
|
6
|
-
echo "To fix this, run the postinstall script manually:" >&2
|
|
7
|
-
echo " cd node_modules/codo-ai && node postinstall.mjs" >&2
|
|
8
|
-
echo "" >&2
|
|
9
|
-
echo "Or reinstall codo-ai without the --ignore-scripts flag." >&2
|
|
10
|
-
exit 1
|
package/postinstall.mjs
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import childProcess from "child_process"
|
|
4
|
-
import fs from "fs"
|
|
5
|
-
import os from "os"
|
|
6
|
-
import path from "path"
|
|
7
|
-
import { createRequire } from "module"
|
|
8
|
-
import { fileURLToPath } from "url"
|
|
9
|
-
|
|
10
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
11
|
-
const require = createRequire(import.meta.url)
|
|
12
|
-
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "package.json"), "utf8"))
|
|
13
|
-
|
|
14
|
-
const platformMap = {
|
|
15
|
-
darwin: "darwin",
|
|
16
|
-
linux: "linux",
|
|
17
|
-
win32: "windows",
|
|
18
|
-
}
|
|
19
|
-
const archMap = {
|
|
20
|
-
x64: "x64",
|
|
21
|
-
arm64: "arm64",
|
|
22
|
-
arm: "arm",
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const platform = platformMap[os.platform()] ?? os.platform()
|
|
26
|
-
const arch = archMap[os.arch()] ?? os.arch()
|
|
27
|
-
const base = `codo-${platform}-${arch}`
|
|
28
|
-
const sourceBinary = platform === "windows" ? "codo.exe" : "codo"
|
|
29
|
-
const targetBinary = path.join(__dirname, "bin", "codo.exe")
|
|
30
|
-
|
|
31
|
-
function supportsAvx2() {
|
|
32
|
-
if (arch !== "x64") return false
|
|
33
|
-
|
|
34
|
-
if (platform === "linux") {
|
|
35
|
-
try {
|
|
36
|
-
return /(^|\s)avx2(\s|$)/i.test(fs.readFileSync("/proc/cpuinfo", "utf8"))
|
|
37
|
-
} catch {
|
|
38
|
-
return false
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (platform === "darwin") {
|
|
43
|
-
try {
|
|
44
|
-
const result = childProcess.spawnSync("sysctl", ["-n", "hw.optional.avx2_0"], {
|
|
45
|
-
encoding: "utf8",
|
|
46
|
-
timeout: 1500,
|
|
47
|
-
})
|
|
48
|
-
if (result.status !== 0) return false
|
|
49
|
-
return (result.stdout || "").trim() === "1"
|
|
50
|
-
} catch {
|
|
51
|
-
return false
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (platform === "windows") {
|
|
56
|
-
const command =
|
|
57
|
-
'(Add-Type -MemberDefinition "[DllImport(""kernel32.dll"")] public static extern bool IsProcessorFeaturePresent(int ProcessorFeature);" -Name Kernel32 -Namespace Win32 -PassThru)::IsProcessorFeaturePresent(40)'
|
|
58
|
-
|
|
59
|
-
for (const executable of ["powershell.exe", "pwsh.exe", "pwsh", "powershell"]) {
|
|
60
|
-
try {
|
|
61
|
-
const result = childProcess.spawnSync(executable, ["-NoProfile", "-NonInteractive", "-Command", command], {
|
|
62
|
-
encoding: "utf8",
|
|
63
|
-
timeout: 3000,
|
|
64
|
-
windowsHide: true,
|
|
65
|
-
})
|
|
66
|
-
if (result.status !== 0) continue
|
|
67
|
-
const output = (result.stdout || "").trim().toLowerCase()
|
|
68
|
-
if (output === "true" || output === "1") return true
|
|
69
|
-
if (output === "false" || output === "0") return false
|
|
70
|
-
} catch {
|
|
71
|
-
continue
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return false
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function isMusl() {
|
|
80
|
-
if (platform !== "linux") return false
|
|
81
|
-
|
|
82
|
-
try {
|
|
83
|
-
if (fs.existsSync("/etc/alpine-release")) return true
|
|
84
|
-
} catch {
|
|
85
|
-
// Ignore filesystem probes that are blocked by the host.
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
try {
|
|
89
|
-
const result = childProcess.spawnSync("ldd", ["--version"], { encoding: "utf8" })
|
|
90
|
-
return `${result.stdout || ""}${result.stderr || ""}`.toLowerCase().includes("musl")
|
|
91
|
-
} catch {
|
|
92
|
-
return false
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function packageNames() {
|
|
97
|
-
const baseline = arch === "x64" && !supportsAvx2()
|
|
98
|
-
|
|
99
|
-
if (platform === "linux") {
|
|
100
|
-
if (isMusl()) {
|
|
101
|
-
if (arch === "x64")
|
|
102
|
-
return baseline
|
|
103
|
-
? [`${base}-baseline-musl`, `${base}-musl`, `${base}-baseline`, base]
|
|
104
|
-
: [`${base}-musl`, `${base}-baseline-musl`, base, `${base}-baseline`]
|
|
105
|
-
return [`${base}-musl`, base]
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (arch === "x64")
|
|
109
|
-
return baseline
|
|
110
|
-
? [`${base}-baseline`, base, `${base}-baseline-musl`, `${base}-musl`]
|
|
111
|
-
: [base, `${base}-baseline`, `${base}-musl`, `${base}-baseline-musl`]
|
|
112
|
-
return [base, `${base}-musl`]
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (arch === "x64") return baseline ? [`${base}-baseline`, base] : [base, `${base}-baseline`]
|
|
116
|
-
return [base]
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Published packages may name the binary exactly ("codo.exe" / "codo") or with
|
|
120
|
-
// a version suffix ("codo-2.23.5.exe"), so resolve by pattern rather than by
|
|
121
|
-
// a single hardcoded filename.
|
|
122
|
-
function findBinary(dir) {
|
|
123
|
-
const entries = fs.readdirSync(path.join(dir, "bin"))
|
|
124
|
-
const match =
|
|
125
|
-
entries.find((entry) => entry === sourceBinary) ??
|
|
126
|
-
(platform === "windows"
|
|
127
|
-
? entries.find((entry) => entry.startsWith("codo") && entry.endsWith(".exe"))
|
|
128
|
-
: undefined) ??
|
|
129
|
-
entries.find((entry) => entry.startsWith("codo"))
|
|
130
|
-
if (!match) throw new Error(`Binary not found in ${path.join(dir, "bin")}`)
|
|
131
|
-
return path.join(dir, "bin", match)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Platform binaries live in scoped packages (@codo-ai/codo-<platform>-<arch>),
|
|
135
|
-
// while packageNames() works with the unscoped suffix.
|
|
136
|
-
const scope = "@codo-ai/"
|
|
137
|
-
|
|
138
|
-
function resolveBinary(name) {
|
|
139
|
-
return findBinary(path.dirname(require.resolve(`${scope}${name}/package.json`)))
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
function installPackage(name) {
|
|
143
|
-
const version = packageJson.optionalDependencies?.[`${scope}${name}`]
|
|
144
|
-
if (!version) return
|
|
145
|
-
|
|
146
|
-
const temp = fs.mkdtempSync(path.join(os.tmpdir(), "codo-install-"))
|
|
147
|
-
try {
|
|
148
|
-
const result = childProcess.spawnSync(
|
|
149
|
-
"npm",
|
|
150
|
-
["install", "--ignore-scripts", "--no-save", "--loglevel=error", "--prefix", temp, `${scope}${name}@${version}`],
|
|
151
|
-
{ stdio: "inherit", windowsHide: true },
|
|
152
|
-
)
|
|
153
|
-
if (result.status !== 0) return
|
|
154
|
-
const packageDir = path.join(temp, "node_modules", scope, name)
|
|
155
|
-
copyBinary(findBinary(packageDir), targetBinary)
|
|
156
|
-
return true
|
|
157
|
-
} finally {
|
|
158
|
-
fs.rmSync(temp, { recursive: true, force: true })
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function copyBinary(source, target) {
|
|
163
|
-
if (!fs.existsSync(source)) throw new Error(`Binary not found at ${source}`)
|
|
164
|
-
fs.mkdirSync(path.dirname(target), { recursive: true })
|
|
165
|
-
if (fs.existsSync(target)) fs.unlinkSync(target)
|
|
166
|
-
try {
|
|
167
|
-
fs.linkSync(source, target)
|
|
168
|
-
} catch {
|
|
169
|
-
fs.copyFileSync(source, target)
|
|
170
|
-
}
|
|
171
|
-
fs.chmodSync(target, 0o755)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function verifyBinary(target = targetBinary) {
|
|
175
|
-
try {
|
|
176
|
-
const result = childProcess.spawnSync(target, ["--version"], {
|
|
177
|
-
encoding: "utf8",
|
|
178
|
-
stdio: "ignore",
|
|
179
|
-
windowsHide: true,
|
|
180
|
-
})
|
|
181
|
-
return result.status === 0
|
|
182
|
-
} catch {
|
|
183
|
-
return false
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
function main() {
|
|
188
|
-
// Fast path: npm installs optionalDependencies by default, so the platform
|
|
189
|
-
// binary is usually already on disk. The launcher resolves it at runtime,
|
|
190
|
-
// so there is nothing to copy — just validate it actually runs.
|
|
191
|
-
for (const name of packageNames()) {
|
|
192
|
-
try {
|
|
193
|
-
if (verifyBinary(resolveBinary(name))) return
|
|
194
|
-
} catch {
|
|
195
|
-
// Package not installed; try the next candidate, then the fallback below.
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Fallback: the platform package is missing (isolated pnpm layouts,
|
|
200
|
-
// --no-optional installs). Fetch it into a temp dir and stage the binary
|
|
201
|
-
// next to the launcher, where the launcher looks for it.
|
|
202
|
-
for (const name of packageNames()) {
|
|
203
|
-
try {
|
|
204
|
-
if (installPackage(name) && verifyBinary()) return
|
|
205
|
-
} catch {
|
|
206
|
-
// Try the next candidate.
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
throw new Error(
|
|
211
|
-
`It seems your package manager failed to install the right codo CLI package. Try manually installing ${packageNames()
|
|
212
|
-
.map((name) => JSON.stringify(`@codo-ai/${name}`))
|
|
213
|
-
.join(" or ")}.`,
|
|
214
|
-
)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
try {
|
|
218
|
-
main()
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.error(error.message)
|
|
221
|
-
process.exit(1)
|
|
222
|
-
}
|