codo-ai 2.23.12 → 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/README.md CHANGED
@@ -100,6 +100,8 @@ npx codo-ai
100
100
 
101
101
  Requirements: Node 18+ or Bun · macOS / Linux / Windows · any provider API key (Anthropic, OpenAI, NVIDIA free tier, Google, or OpenAI-compatible).
102
102
 
103
+ > No extra flags needed: the install works out of the box even when install scripts are blocked (`allowScripts` policies, `--ignore-scripts`), because the launcher resolves the prebuilt platform binary automatically on first run.
104
+
103
105
  > Migrating from `@codo-ai/cli`? That package is deprecated — `codo-ai` replaces it.
104
106
 
105
107
  ## Documentation
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
@@ -3,7 +3,9 @@
3
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.12",
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.12",
48
- "@codo-ai/codo-windows-x64": "2.23.12",
49
- "@codo-ai/codo-windows-arm64": "2.23.12",
50
- "@codo-ai/codo-linux-x64-musl": "2.23.12",
51
- "@codo-ai/codo-linux-x64-baseline-musl": "2.23.12",
52
- "@codo-ai/codo-linux-x64-baseline": "2.23.12",
53
- "@codo-ai/codo-linux-x64": "2.23.12",
54
- "@codo-ai/codo-linux-arm64-musl": "2.23.12",
55
- "@codo-ai/codo-linux-arm64": "2.23.12",
56
- "@codo-ai/codo-darwin-x64-baseline": "2.23.12",
57
- "@codo-ai/codo-darwin-x64": "2.23.12",
58
- "@codo-ai/codo-darwin-arm64": "2.23.12"
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,205 +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() {
175
- const result = childProcess.spawnSync(targetBinary, ["--version"], {
176
- encoding: "utf8",
177
- stdio: "ignore",
178
- windowsHide: true,
179
- })
180
- return result.status === 0
181
- }
182
-
183
- function main() {
184
- for (const name of packageNames()) {
185
- try {
186
- copyBinary(resolveBinary(name), targetBinary)
187
- if (verifyBinary()) return
188
- } catch {
189
- if (installPackage(name) && verifyBinary()) return
190
- }
191
- }
192
-
193
- throw new Error(
194
- `It seems your package manager failed to install the right codo CLI package. Try manually installing ${packageNames()
195
- .map((name) => JSON.stringify(`@codo-ai/${name}`))
196
- .join(" or ")}.`,
197
- )
198
- }
199
-
200
- try {
201
- main()
202
- } catch (error) {
203
- console.error(error.message)
204
- process.exit(1)
205
- }