mirror of
https://github.com/openai/codex.git
synced 2026-04-29 00:55:38 +00:00
514 lines
16 KiB
Markdown
514 lines
16 KiB
Markdown
# PR #2237: [codex-cli] Add ripgrep as a dependency for node environment
|
|
|
|
- URL: https://github.com/openai/codex/pull/2237
|
|
- Author: dylan-hurd-oai
|
|
- Created: 2025-08-12 18:25:23 UTC
|
|
- Updated: 2025-08-13 20:49:42 UTC
|
|
- Changes: +196/-5, Files changed: 5, Commits: 5
|
|
|
|
## Description
|
|
|
|
## Summary
|
|
Ripgrep is our preferred tool for file search. When users install via `brew install codex`, it's automatically installed as a dependency. We want to ensure that users running via an npm install also have this tool! Microsoft has already solved this problem for VS Code - let's not reinvent the wheel.
|
|
|
|
This approach of appending to the PATH directly might be a bit heavy-handed, but feels reasonably robust to a variety of environment concerns. Open to thoughts on better approaches here!
|
|
|
|
## Testing
|
|
- [x] confirmed this import approach works with `node -e "const { rgPath } = require('@vscode/ripgrep'); require('child_process').spawn(rgPath, ['--version'], { stdio: 'inherit' })"`
|
|
- [x] Ran codex.js locally with `rg` uninstalled, asked it to run `which rg`. Output below:
|
|
|
|
```
|
|
⚡ Ran command which rg; echo $?
|
|
⎿ /Users/dylan.hurd/code/dh--npm-rg/node_modules/@vscode/ripgrep/bin/rg
|
|
0
|
|
|
|
codex
|
|
Re-running to confirm the path and exit code.
|
|
|
|
- Path: `/Users/dylan.hurd/code/dh--npm-rg/node_modules/@vscode/ripgrep/bin/rg`
|
|
- Exit code: `0`
|
|
```
|
|
|
|
## Full Diff
|
|
|
|
```diff
|
|
diff --git a/codex-cli/bin/codex.js b/codex-cli/bin/codex.js
|
|
index d92d8f2f4f..b22c5180c0 100755
|
|
--- a/codex-cli/bin/codex.js
|
|
+++ b/codex-cli/bin/codex.js
|
|
@@ -43,7 +43,7 @@ switch (platform) {
|
|
targetTriple = "x86_64-pc-windows-msvc.exe";
|
|
break;
|
|
case "arm64":
|
|
- // We do not build this today, fall through...
|
|
+ // We do not build this today, fall through...
|
|
default:
|
|
break;
|
|
}
|
|
@@ -65,9 +65,43 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch (err) {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgDir() {
|
|
+ const ripgrep = await tryImport("@vscode/ripgrep");
|
|
+ if (!ripgrep?.rgPath) {
|
|
+ return null;
|
|
+ }
|
|
+ return path.dirname(ripgrep.rgPath);
|
|
+}
|
|
+
|
|
+function getUpdatedPath(newDirs) {
|
|
+ const pathSep = process.platform === "win32" ? ";" : ":";
|
|
+ const existingPath = process.env.PATH || "";
|
|
+ const updatedPath = [
|
|
+ ...newDirs,
|
|
+ ...existingPath.split(pathSep).filter(Boolean),
|
|
+ ].join(pathSep);
|
|
+ return updatedPath;
|
|
+}
|
|
+
|
|
+const additionalDirs = [];
|
|
+const rgDir = await resolveRgDir();
|
|
+if (rgDir) {
|
|
+ additionalDirs.push(rgDir);
|
|
+}
|
|
+const updatedPath = getUpdatedPath(additionalDirs);
|
|
+
|
|
const child = spawn(binaryPath, process.argv.slice(2), {
|
|
stdio: "inherit",
|
|
- env: { ...process.env, CODEX_MANAGED_BY_NPM: "1" },
|
|
+ env: { ...process.env, PATH: updatedPath, CODEX_MANAGED_BY_NPM: "1" },
|
|
});
|
|
|
|
child.on("error", (err) => {
|
|
@@ -120,4 +154,3 @@ if (childResult.type === "signal") {
|
|
} else {
|
|
process.exit(childResult.exitCode);
|
|
}
|
|
-
|
|
diff --git a/codex-cli/package-lock.json b/codex-cli/package-lock.json
|
|
new file mode 100644
|
|
index 0000000000..a1c840ade0
|
|
--- /dev/null
|
|
+++ b/codex-cli/package-lock.json
|
|
@@ -0,0 +1,119 @@
|
|
+{
|
|
+ "name": "@openai/codex",
|
|
+ "version": "0.0.0-dev",
|
|
+ "lockfileVersion": 3,
|
|
+ "requires": true,
|
|
+ "packages": {
|
|
+ "": {
|
|
+ "name": "@openai/codex",
|
|
+ "version": "0.0.0-dev",
|
|
+ "license": "Apache-2.0",
|
|
+ "dependencies": {
|
|
+ "@vscode/ripgrep": "^1.15.14"
|
|
+ },
|
|
+ "bin": {
|
|
+ "codex": "bin/codex.js"
|
|
+ },
|
|
+ "engines": {
|
|
+ "node": ">=20"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/@vscode/ripgrep": {
|
|
+ "version": "1.15.14",
|
|
+ "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.15.14.tgz",
|
|
+ "integrity": "sha512-/G1UJPYlm+trBWQ6cMO3sv6b8D1+G16WaJH1/DSqw32JOVlzgZbLkDxRyzIpTpv30AcYGMkCf5tUqGlW6HbDWw==",
|
|
+ "hasInstallScript": true,
|
|
+ "license": "MIT",
|
|
+ "dependencies": {
|
|
+ "https-proxy-agent": "^7.0.2",
|
|
+ "proxy-from-env": "^1.1.0",
|
|
+ "yauzl": "^2.9.2"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/agent-base": {
|
|
+ "version": "7.1.4",
|
|
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
|
|
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
|
|
+ "license": "MIT",
|
|
+ "engines": {
|
|
+ "node": ">= 14"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/buffer-crc32": {
|
|
+ "version": "0.2.13",
|
|
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
|
|
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
|
|
+ "license": "MIT",
|
|
+ "engines": {
|
|
+ "node": "*"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/debug": {
|
|
+ "version": "4.4.1",
|
|
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
|
|
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
|
|
+ "license": "MIT",
|
|
+ "dependencies": {
|
|
+ "ms": "^2.1.3"
|
|
+ },
|
|
+ "engines": {
|
|
+ "node": ">=6.0"
|
|
+ },
|
|
+ "peerDependenciesMeta": {
|
|
+ "supports-color": {
|
|
+ "optional": true
|
|
+ }
|
|
+ }
|
|
+ },
|
|
+ "node_modules/fd-slicer": {
|
|
+ "version": "1.1.0",
|
|
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
|
|
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
|
|
+ "license": "MIT",
|
|
+ "dependencies": {
|
|
+ "pend": "~1.2.0"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/https-proxy-agent": {
|
|
+ "version": "7.0.6",
|
|
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
|
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
|
|
+ "license": "MIT",
|
|
+ "dependencies": {
|
|
+ "agent-base": "^7.1.2",
|
|
+ "debug": "4"
|
|
+ },
|
|
+ "engines": {
|
|
+ "node": ">= 14"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/ms": {
|
|
+ "version": "2.1.3",
|
|
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
|
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
|
+ "license": "MIT"
|
|
+ },
|
|
+ "node_modules/pend": {
|
|
+ "version": "1.2.0",
|
|
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
|
|
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
|
|
+ "license": "MIT"
|
|
+ },
|
|
+ "node_modules/proxy-from-env": {
|
|
+ "version": "1.1.0",
|
|
+ "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
+ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
|
+ "license": "MIT"
|
|
+ },
|
|
+ "node_modules/yauzl": {
|
|
+ "version": "2.10.0",
|
|
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
|
|
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
|
|
+ "license": "MIT",
|
|
+ "dependencies": {
|
|
+ "buffer-crc32": "~0.2.3",
|
|
+ "fd-slicer": "~1.1.0"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/codex-cli/package.json b/codex-cli/package.json
|
|
index c5464beae5..614ca1a832 100644
|
|
--- a/codex-cli/package.json
|
|
+++ b/codex-cli/package.json
|
|
@@ -16,5 +16,11 @@
|
|
"repository": {
|
|
"type": "git",
|
|
"url": "git+https://github.com/openai/codex.git"
|
|
+ },
|
|
+ "dependencies": {
|
|
+ "@vscode/ripgrep": "^1.15.14"
|
|
+ },
|
|
+ "devDependencies": {
|
|
+ "prettier": "^3.3.3"
|
|
}
|
|
}
|
|
diff --git a/package-lock.json b/package-lock.json
|
|
new file mode 100644
|
|
index 0000000000..bb67c75258
|
|
--- /dev/null
|
|
+++ b/package-lock.json
|
|
@@ -0,0 +1,33 @@
|
|
+{
|
|
+ "name": "codex-monorepo",
|
|
+ "lockfileVersion": 3,
|
|
+ "requires": true,
|
|
+ "packages": {
|
|
+ "": {
|
|
+ "name": "codex-monorepo",
|
|
+ "devDependencies": {
|
|
+ "prettier": "^3.5.3"
|
|
+ },
|
|
+ "engines": {
|
|
+ "node": ">=22",
|
|
+ "pnpm": ">=9.0.0"
|
|
+ }
|
|
+ },
|
|
+ "node_modules/prettier": {
|
|
+ "version": "3.6.2",
|
|
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
|
|
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
|
|
+ "dev": true,
|
|
+ "license": "MIT",
|
|
+ "bin": {
|
|
+ "prettier": "bin/prettier.cjs"
|
|
+ },
|
|
+ "engines": {
|
|
+ "node": ">=14"
|
|
+ },
|
|
+ "funding": {
|
|
+ "url": "https://github.com/prettier/prettier?sponsor=1"
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+}
|
|
diff --git a/package.json b/package.json
|
|
index c13e6cb314..606c323ff4 100644
|
|
--- a/package.json
|
|
+++ b/package.json
|
|
@@ -3,8 +3,8 @@
|
|
"private": true,
|
|
"description": "Tools for repo-wide maintenance.",
|
|
"scripts": {
|
|
- "format": "prettier --check *.json *.md .github/workflows/*.yml",
|
|
- "format:fix": "prettier --write *.json *.md .github/workflows/*.yml"
|
|
+ "format": "prettier --check *.json *.md .github/workflows/*.yml **/*.js",
|
|
+ "format:fix": "prettier --write *.json *.md .github/workflows/*.yml **/*.js"
|
|
},
|
|
"devDependencies": {
|
|
"prettier": "^3.5.3"
|
|
```
|
|
|
|
## Review Comments
|
|
|
|
### codex-cli/bin/codex.js
|
|
|
|
- Created: 2025-08-12 20:59:40 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271220497
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
```
|
|
|
|
> In deleting most of `codex-cli`, I guess we lost Prettier coverage for this file...
|
|
|
|
- Created: 2025-08-12 21:03:35 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271231764
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
+ try {
|
|
+ const { rgPath } = await tryImport("@vscode/ripgrep");
|
|
+ return path.dirname(rgPath);
|
|
+ } catch(err) {
|
|
+ console.error('unable to import ripgrep', err);
|
|
+ }
|
|
+}
|
|
+
|
|
+function getUpdatedPath(newDirs) {
|
|
+ const pathSep = process.platform === "win32" ? ";" : ":";
|
|
+ const existingPath = process.env.PATH || process.env.Path || "";
|
|
```
|
|
|
|
> I thought env vars are case-insensitive and that `process.env` does some internal `get` voodoo to enforce that, so I don't believe you need `process.env.Path`, do you?
|
|
|
|
- Created: 2025-08-12 21:04:15 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271233374
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
+ try {
|
|
+ const { rgPath } = await tryImport("@vscode/ripgrep");
|
|
+ return path.dirname(rgPath);
|
|
```
|
|
|
|
> To confirm: `rg` is definitely the only file in that directory?
|
|
|
|
- Created: 2025-08-12 21:05:54 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271237698
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
```
|
|
|
|
> Can you verify this works on Node 20? I recently lowered the minimum version:
|
|
>
|
|
> https://github.com/openai/codex/blob/eaa3969e681c1cea592777361183eb27c6c0a1a9/codex-cli/package.json#L10
|
|
|
|
- Created: 2025-08-12 21:10:53 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271253807
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
+ try {
|
|
+ const { rgPath } = await tryImport("@vscode/ripgrep");
|
|
```
|
|
|
|
> You already have try/catch in `tryImport()`, so I don't think we should do it again here. Just check whether the result is `null` or not.
|
|
>
|
|
> I worry we might swallow logical errors by accident.
|
|
|
|
- Created: 2025-08-12 22:43:33 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271475227
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch {
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
+ try {
|
|
+ const { rgPath } = await tryImport("@vscode/ripgrep");
|
|
+ return path.dirname(rgPath);
|
|
+ } catch(err) {
|
|
+ console.error('unable to import ripgrep', err);
|
|
+ }
|
|
+}
|
|
+
|
|
+function getUpdatedPath(newDirs) {
|
|
+ const pathSep = process.platform === "win32" ? ";" : ":";
|
|
+ const existingPath = process.env.PATH || process.env.Path || "";
|
|
```
|
|
|
|
> I just tested on my Windows machine and `process.env.Path` and `process.env.PATH` are the same value.
|
|
|
|
- Created: 2025-08-13 15:26:30 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2273823865
|
|
|
|
```diff
|
|
@@ -65,9 +65,40 @@ const binaryPath = path.join(__dirname, "..", "bin", `codex-${targetTriple}`);
|
|
// receives a fatal signal, both processes exit in a predictable manner.
|
|
const { spawn } = await import("child_process");
|
|
|
|
+async function tryImport(moduleName) {
|
|
+ try {
|
|
+ // eslint-disable-next-line node/no-unsupported-features/es-syntax
|
|
+ return await import(moduleName);
|
|
+ } catch (err) {
|
|
+ console.error(`unable to import ${moduleName}`, err);
|
|
+ return null;
|
|
+ }
|
|
+}
|
|
+
|
|
+async function resolveRgPath() {
|
|
+ const { rgPath } = await tryImport("@vscode/ripgrep");
|
|
+ if (!rgPath) {
|
|
+ throw new Error("ripgrep not found");
|
|
+ }
|
|
+ return path.dirname(rgPath);
|
|
+}
|
|
+
|
|
+function getUpdatedPath(newDirs) {
|
|
+ const pathSep = process.platform === "win32" ? ";" : ":";
|
|
+ const existingPath = process.env.PATH || "";
|
|
+ const updatedPath = [
|
|
+ ...newDirs,
|
|
+ ...existingPath.split(pathSep).filter(Boolean),
|
|
+ ].join(pathSep);
|
|
+ return updatedPath;
|
|
+}
|
|
+
|
|
+const rgPath = await resolveRgPath();
|
|
+const updatedPath = getUpdatedPath([rgPath]);
|
|
```
|
|
|
|
> Should we throw in this case? I feel like, if the user has installed with `--ignore-scripts` or the network request failed to fetch `rg` for some reason, we should still let them run the CLI?
|
|
|
|
### package.json
|
|
|
|
- Created: 2025-08-12 21:02:41 UTC | Link: https://github.com/openai/codex/pull/2237#discussion_r2271228887
|
|
|
|
```diff
|
|
@@ -21,5 +21,8 @@
|
|
"node": ">=22",
|
|
"pnpm": ">=9.0.0"
|
|
},
|
|
- "packageManager": "pnpm@10.8.1"
|
|
+ "packageManager": "pnpm@10.8.1",
|
|
+ "dependencies": {
|
|
+ "@vscode/ripgrep": "^1.15.14"
|
|
```
|
|
|
|
> Note that this does not guarantee that `rg` gets installed because the npm package does not bundle the binaries itself, but downloads them via `postinstall`:
|
|
>
|
|
> https://www.npmjs.com/package/@vscode/ripgrep
|
|
>
|
|
> Because we expect our users to install via `npm -g` and have network access at that time, this should be fine, but if they used `--ignore-scripts` for some reason, then it wouldn't end up getting fetched. |