From 179dfe9aaf2e5569f7c688256fbc256d129c5592 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 11:13:50 +0200 Subject: [PATCH 1/7] Fix license publishing --- LICENSE.txt => LICENSE | 0 package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename LICENSE.txt => LICENSE (100%) diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/package.json b/package.json index 3e61cf87..09ee86a6 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ }, "keywords": [], "author": "", - "license": "ISC", + "license": "Apache-2.0", "type": "module", "devDependencies": { "@types/node": "^24.10.1", From 5a2759ef17cdf784b3cb7fb9c0bd7444adcf10a8 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 11:37:19 +0200 Subject: [PATCH 2/7] Add npm badge to README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 6047b9a4..4da834c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# codex-acp +# ACP adapter for Codex CLI +[![npm version](https://img.shields.io/npm/v/%40jetbrains%2Fcodex-acp)](https://www.npmjs.com/package/@jetbrains/codex-acp) -ACP server implementation that exposes Codex CLI functionality for smoother client and IDE integration. ## Documentation From 63f54e7fe6c29ad2e5e82da65abc309e7ad4f6a9 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 11:47:22 +0200 Subject: [PATCH 3/7] Cleanup Bun usage Use npm for dev workflows, keep Bun for binary builds. Bun is still required for `bun build --compile`, which produces the standalone release executables --- .github/workflows/ci.yml | 17 +-- .github/workflows/release.yml | 17 +-- bun.lock | 217 ---------------------------------- package.json | 2 +- readme-dev.md | 5 +- 5 files changed, 24 insertions(+), 234 deletions(-) delete mode 100644 bun.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5ee68d1e..d8e38080 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,30 +11,33 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v6 with: - bun-version: 1.3.11 - - run: bun install + node-version: '24' + - run: npm ci - run: npm run typecheck test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v6 with: - bun-version: 1.3.11 - - run: bun install + node-version: '24' + - run: npm ci - run: npm test build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + - uses: actions/setup-node@v6 + with: + node-version: '24' + - run: npm ci - uses: oven-sh/setup-bun@v2 with: bun-version: 1.3.11 - - run: bun install - run: npm run bundle:all - uses: actions/upload-artifact@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index be4c957e..9b6315ce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,9 +12,9 @@ jobs: contents: write steps: - uses: actions/checkout@v4 - - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v6 with: - bun-version: 1.3.11 + node-version: '24' - name: Install zip run: | for i in 1 2 3; do @@ -22,10 +22,13 @@ jobs: echo "Attempt $i failed, retrying in 5s..." sleep 5 done - - run: bun install - - run: bun run typecheck - - run: bun run test - - run: bun run bundle:all + - run: npm ci + - run: npm run typecheck + - run: npm run test + - uses: oven-sh/setup-bun@v2 + with: + bun-version: 1.3.11 + - run: npm run bundle:all - name: Download Codesign Client and signatures run: | @@ -137,7 +140,7 @@ jobs: unzip -o dist/bin/codex-acp-arm64-darwin.zip -d dist/bin/ rm dist/bin/codex-acp-x64-darwin.zip dist/bin/codex-acp-arm64-darwin.zip - - run: bun run package:all + - run: npm run package:all - name: Create Release uses: softprops/action-gh-release@v2 diff --git a/bun.lock b/bun.lock deleted file mode 100644 index 8b36f9cf..00000000 --- a/bun.lock +++ /dev/null @@ -1,217 +0,0 @@ -{ - "lockfileVersion": 1, - "configVersion": 0, - "workspaces": { - "": { - "name": "codex-acp", - "dependencies": { - "@agentclientprotocol/sdk": "^0.5.1", - "diff": "^8.0.2", - "vscode-jsonrpc": "^8.2.1", - }, - "devDependencies": { - "@types/node": "^24.10.1", - "tsx": "^4.20.6", - "typescript": "^5.9.3", - "vitest": "^4.0.10", - }, - }, - }, - "packages": { - "@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.5.1", "", { "dependencies": { "zod": "^3.0.0" } }, "sha512-9bq2TgjhLBSUSC5jE04MEe+Hqw8YePzKghhYZ9QcjOyonY3q2oJfX6GoSO83hURpEnsqEPIrex6VZN3+61fBJg=="], - - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], - - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], - - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], - - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], - - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], - - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], - - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], - - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], - - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], - - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], - - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], - - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], - - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], - - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], - - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], - - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], - - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], - - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], - - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], - - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], - - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], - - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], - - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], - - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], - - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], - - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], - - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], - - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.53.3", "", { "os": "android", "cpu": "arm" }, "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w=="], - - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.53.3", "", { "os": "android", "cpu": "arm64" }, "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w=="], - - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.53.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA=="], - - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.53.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ=="], - - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.53.3", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w=="], - - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.53.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q=="], - - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw=="], - - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.53.3", "", { "os": "linux", "cpu": "arm" }, "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg=="], - - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w=="], - - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.53.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A=="], - - "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g=="], - - "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.53.3", "", { "os": "linux", "cpu": "ppc64" }, "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw=="], - - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g=="], - - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.53.3", "", { "os": "linux", "cpu": "none" }, "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A=="], - - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.53.3", "", { "os": "linux", "cpu": "s390x" }, "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg=="], - - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w=="], - - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.53.3", "", { "os": "linux", "cpu": "x64" }, "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q=="], - - "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.53.3", "", { "os": "none", "cpu": "arm64" }, "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw=="], - - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.53.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw=="], - - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.53.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA=="], - - "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg=="], - - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.53.3", "", { "os": "win32", "cpu": "x64" }, "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ=="], - - "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], - - "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], - - "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], - - "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], - - "@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="], - - "@vitest/expect": ["@vitest/expect@4.0.10", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.0.10", "@vitest/utils": "4.0.10", "chai": "^6.2.1", "tinyrainbow": "^3.0.3" } }, "sha512-3QkTX/lK39FBNwARCQRSQr0TP9+ywSdxSX+LgbJ2M1WmveXP72anTbnp2yl5fH+dU6SUmBzNMrDHs80G8G2DZg=="], - - "@vitest/mocker": ["@vitest/mocker@4.0.10", "", { "dependencies": { "@vitest/spy": "4.0.10", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw"] }, "sha512-e2OfdexYkjkg8Hh3L9NVEfbwGXq5IZbDovkf30qW2tOh7Rh9sVtmSr2ztEXOFbymNxS4qjzLXUQIvATvN4B+lg=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@4.0.10", "", { "dependencies": { "tinyrainbow": "^3.0.3" } }, "sha512-99EQbpa/zuDnvVjthwz5bH9o8iPefoQZ63WV8+bsRJZNw3qQSvSltfut8yu1Jc9mqOYi7pEbsKxYTi/rjaq6PA=="], - - "@vitest/runner": ["@vitest/runner@4.0.10", "", { "dependencies": { "@vitest/utils": "4.0.10", "pathe": "^2.0.3" } }, "sha512-EXU2iSkKvNwtlL8L8doCpkyclw0mc/t4t9SeOnfOFPyqLmQwuceMPA4zJBa6jw0MKsZYbw7kAn+gl7HxrlB8UQ=="], - - "@vitest/snapshot": ["@vitest/snapshot@4.0.10", "", { "dependencies": { "@vitest/pretty-format": "4.0.10", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-2N4X2ZZl7kZw0qeGdQ41H0KND96L3qX1RgwuCfy6oUsF2ISGD/HpSbmms+CkIOsQmg2kulwfhJ4CI0asnZlvkg=="], - - "@vitest/spy": ["@vitest/spy@4.0.10", "", {}, "sha512-AsY6sVS8OLb96GV5RoG8B6I35GAbNrC49AO+jNRF9YVGb/g9t+hzNm1H6kD0NDp8tt7VJLs6hb7YMkDXqu03iw=="], - - "@vitest/utils": ["@vitest/utils@4.0.10", "", { "dependencies": { "@vitest/pretty-format": "4.0.10", "tinyrainbow": "^3.0.3" } }, "sha512-kOuqWnEwZNtQxMKg3WmPK1vmhZu9WcoX69iwWjVz+jvKTsF1emzsv3eoPcDr6ykA3qP2bsCQE7CwqfNtAVzsmg=="], - - "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], - - "chai": ["chai@6.2.1", "", {}, "sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg=="], - - "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], - - "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], - - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], - - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": "bin/esbuild" }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], - - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], - - "expect-type": ["expect-type@1.2.2", "", {}, "sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA=="], - - "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" } }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], - - "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], - - "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], - - "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], - - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - - "nanoid": ["nanoid@3.3.11", "", { "bin": "bin/nanoid.cjs" }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], - - "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], - - "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - - "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - - "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], - - "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - - "rollup": ["rollup@4.53.3", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.53.3", "@rollup/rollup-android-arm64": "4.53.3", "@rollup/rollup-darwin-arm64": "4.53.3", "@rollup/rollup-darwin-x64": "4.53.3", "@rollup/rollup-freebsd-arm64": "4.53.3", "@rollup/rollup-freebsd-x64": "4.53.3", "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", "@rollup/rollup-linux-arm-musleabihf": "4.53.3", "@rollup/rollup-linux-arm64-gnu": "4.53.3", "@rollup/rollup-linux-arm64-musl": "4.53.3", "@rollup/rollup-linux-loong64-gnu": "4.53.3", "@rollup/rollup-linux-ppc64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-gnu": "4.53.3", "@rollup/rollup-linux-riscv64-musl": "4.53.3", "@rollup/rollup-linux-s390x-gnu": "4.53.3", "@rollup/rollup-linux-x64-gnu": "4.53.3", "@rollup/rollup-linux-x64-musl": "4.53.3", "@rollup/rollup-openharmony-arm64": "4.53.3", "@rollup/rollup-win32-arm64-msvc": "4.53.3", "@rollup/rollup-win32-ia32-msvc": "4.53.3", "@rollup/rollup-win32-x64-gnu": "4.53.3", "@rollup/rollup-win32-x64-msvc": "4.53.3", "fsevents": "~2.3.2" }, "bin": "dist/bin/rollup" }, "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA=="], - - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - - "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], - - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - - "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], - - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - - "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], - - "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], - - "tinyrainbow": ["tinyrainbow@3.0.3", "", {}, "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q=="], - - "tsx": ["tsx@4.20.6", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": "dist/cli.mjs" }, "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg=="], - - "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], - - "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], - - "vite": ["vite@7.2.2", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "yaml"], "bin": "bin/vite.js" }, "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ=="], - - "vitest": ["vitest@4.0.10", "", { "dependencies": { "@vitest/expect": "4.0.10", "@vitest/mocker": "4.0.10", "@vitest/pretty-format": "4.0.10", "@vitest/runner": "4.0.10", "@vitest/snapshot": "4.0.10", "@vitest/spy": "4.0.10", "@vitest/utils": "4.0.10", "debug": "^4.4.3", "es-module-lexer": "^1.7.0", "expect-type": "^1.2.2", "magic-string": "^0.30.21", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^3.10.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.0.10", "@vitest/browser-preview": "4.0.10", "@vitest/browser-webdriverio": "4.0.10", "@vitest/ui": "4.0.10", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/ui", "happy-dom", "jsdom"], "bin": "vitest.mjs" }, "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg=="], - - "vscode-jsonrpc": ["vscode-jsonrpc@8.2.1", "", {}, "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ=="], - - "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": "cli.js" }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - } -} diff --git a/package.json b/package.json index 09ee86a6..9dc63b11 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "package:darwin-arm64": "cd dist/bin && zip codex-acp-arm64-darwin.zip codex-acp-arm64-darwin", "package:win-x64": "cd dist/bin && zip codex-acp-x64-windows.zip codex-acp-x64-windows.exe", "package:win-arm64": "cd dist/bin && zip codex-acp-arm64-windows.zip codex-acp-arm64-windows.exe", - "start": "bun run src/index.ts", + "start": "node --import tsx src/index.ts", "generate-types": "./node_modules/.bin/codex app-server generate-ts --out src/app-server", "test": "vitest run", "test:watch": "vitest", diff --git a/readme-dev.md b/readme-dev.md index 740c79a8..e1b8d3e4 100644 --- a/readme-dev.md +++ b/readme-dev.md @@ -4,7 +4,6 @@ It may not work with versions other than the one specified in package.json. ### Quick start #### Develop on Windows? -- Download and install [bun](https://bun.com/docs/installation#windows) - Download and install [C++ redistributable package](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#latest-supported-redistributable-version) #### Adjust acp config for IDE @@ -55,6 +54,8 @@ Run from binaries ### Build binaries +Building standalone binaries requires [bun](https://bun.com/docs/installation). + Build single-file executables in `dist/bin` directory: ```bash @@ -71,4 +72,4 @@ npm run package:all 1. Update Codex dependency: `package.json` 2. Regenerate Codex types in `src/app-server/`: `npm run generate-types` -3. Ensure there are no type errors or failed tests: `npm run typecheck` and `npm run test` \ No newline at end of file +3. Ensure there are no type errors or failed tests: `npm run typecheck` and `npm run test` From 7fa59eb85e57e8b6e2e4a1f6fdb2db845632d100 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 11:55:36 +0200 Subject: [PATCH 4/7] Publish only dist/index.js to npm --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dc63b11..5972985e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "codex-acp": "dist/index.js" }, "files": [ - "dist/", + "dist/index.js", "README.md", "LICENSE", "package.json" From 90ecc21f9a11a7973879e2e051c17e9cfa15cb4f Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 11:57:17 +0200 Subject: [PATCH 5/7] Cleanup skipped tests --- .../CodexACPAgent/CodexAcpClient.test.ts | 53 --- .../data/start-conversation.json | 148 -------- .../CodexACPAgent/data/thread-resume.json | 64 ---- .../CodexACPAgent/reasoning-events.test.ts | 345 ------------------ 4 files changed, 610 deletions(-) delete mode 100644 src/__tests__/CodexACPAgent/data/start-conversation.json delete mode 100644 src/__tests__/CodexACPAgent/data/thread-resume.json delete mode 100644 src/__tests__/CodexACPAgent/reasoning-events.test.ts diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 6287b280..7a7147d7 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -64,19 +64,6 @@ describe('ACP server test', { timeout: 40_000 }, () => { const ignoredFields = ["thread", "cwd", "id", "createdAt", "path", "threadId", "userAgent", "sandbox", "conversationId", "origins", "supportedReasoningEfforts", "reasoningEffort", "model", "readOnlyAccess", "approvalsReviewer"]; - it.skip('should start conversation', async () => { - const codexAcpAgent = fixture.getCodexAcpAgent(); - await codexAcpAgent.initialize({protocolVersion: 1}); - - fixture.getCodexAcpClient().authRequired = vi.fn().mockResolvedValue(false); - const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); - // noinspection ES6MissingAwait - we're only check initialization - codexAcpAgent.prompt({sessionId: newSessionResponse.sessionId, prompt: [{type: "text", text: "Hi!"}]}); - - const transportDump = fixture.getCodexConnectionDump(ignoredFields); - await expect(transportDump).toMatchFileSnapshot("data/start-conversation.json"); - }); - it('should throw error without authentication', async () => { await overrideCodexHome('cli_auth_credentials_store = "file"', async () => { const authFixture = createTestFixture(); @@ -551,46 +538,6 @@ describe('ACP server test', { timeout: 40_000 }, () => { await expect(mockFixture.getCodexConnectionDump(ignoredFields)).toMatchFileSnapshot("data/send-attachments-turn-start.json"); }); - async function createSessionInSeparateInstance(): Promise { - const initFixture = createTestFixture(); - initFixture.getCodexAcpClient().authRequired = vi.fn().mockResolvedValue(false); - await initFixture.getCodexAcpAgent().initialize({protocolVersion: 1}); - const newSessionResponse = await initFixture.getCodexAcpAgent().newSession({ - cwd: "", - mcpServers: [] - }); - try { - await initFixture.getCodexAcpAgent().prompt({ - sessionId: newSessionResponse.sessionId, - prompt: [{type: "text", text: "Hi!"}] - }); - } catch (e) {} - - return newSessionResponse.sessionId; - } - - // too long, requires auth - it.skip('should resume session', async () => { - const sessionId = await createSessionInSeparateInstance(); - - await fixture.getCodexAcpAgent().initialize({protocolVersion: 1}); - fixture.getCodexAcpClient().authRequired = vi.fn().mockResolvedValue(false); - fixture.clearCodexConnectionDump(); - - await fixture.getCodexAcpAgent().unstable_resumeSession({ - cwd: "", - sessionId: sessionId - }); - await expect(fixture.getCodexConnectionDump(ignoredFields.concat("data", "model"))).toMatchFileSnapshot("data/thread-resume.json"); - - const promptResult: Promise = fixture.getCodexAcpAgent().prompt({ - sessionId: sessionId, - prompt: [] - }); - - expect(promptResult).toBeDefined(); - }); - it('should fail on wrong sessionId', async () => { const sessionId = "not-existing-session"; diff --git a/src/__tests__/CodexACPAgent/data/start-conversation.json b/src/__tests__/CodexACPAgent/data/start-conversation.json deleted file mode 100644 index 03a1a5c4..00000000 --- a/src/__tests__/CodexACPAgent/data/start-conversation.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "eventType": "request", - "method": "initialize", - "params": { - "capabilities": null, - "clientInfo": { - "name": "codex-acp", - "version": "0.0.23", - "title": "Codex ACP" - } - } -} -{ - "eventType": "response", - "userAgent": "userAgent" -} -{ - "eventType": "request", - "method": "thread/start", - "params": { - "config": { - "projects": { - "": { - "trust_level": "trusted" - } - } - }, - "modelProvider": null, - "model": "model", - "cwd": "cwd", - "approvalPolicy": null, - "sandbox": "sandbox", - "baseInstructions": null, - "developerInstructions": null, - "personality": null, - "ephemeral": null, - "experimentalRawEvents": false - } -} -{ - "eventType": "response", - "thread": "thread", - "model": "model", - "modelProvider": "openai", - "cwd": "cwd", - "approvalPolicy": "on-request", - "sandbox": "sandbox", - "reasoningEffort": "reasoningEffort" -} -{ - "eventType": "request", - "method": "model/list", - "params": { - "cursor": null, - "limit": null - } -} -{ - "eventType": "notification", - "method": "thread/started", - "params": { - "thread": "thread" - }, - "jsonrpc": "2.0" -} -{ - "eventType": "response", - "data": [ - { - "id": "id", - "model": "model", - "upgrade": "gpt-5.3-codex", - "displayName": "gpt-5.2-codex", - "description": "Frontier agentic coding model.", - "supportedReasoningEfforts": "supportedReasoningEfforts", - "defaultReasoningEffort": "medium", - "inputModalities": [ - "text", - "image" - ], - "supportsPersonality": true, - "isDefault": true - }, - { - "id": "id", - "model": "model", - "upgrade": "gpt-5.3-codex", - "displayName": "gpt-5.1-codex-max", - "description": "Codex-optimized flagship for deep and fast reasoning.", - "supportedReasoningEfforts": "supportedReasoningEfforts", - "defaultReasoningEffort": "medium", - "inputModalities": [ - "text", - "image" - ], - "supportsPersonality": false, - "isDefault": false - }, - { - "id": "id", - "model": "model", - "upgrade": "gpt-5.3-codex", - "displayName": "gpt-5.2", - "description": "Latest frontier model with improvements across knowledge, reasoning and coding", - "supportedReasoningEfforts": "supportedReasoningEfforts", - "defaultReasoningEffort": "medium", - "inputModalities": [ - "text", - "image" - ], - "supportsPersonality": false, - "isDefault": false - }, - { - "id": "id", - "model": "model", - "upgrade": "gpt-5.3-codex", - "displayName": "gpt-5.1-codex-mini", - "description": "Optimized for codex. Cheaper, faster, but less capable.", - "supportedReasoningEfforts": "supportedReasoningEfforts", - "defaultReasoningEffort": "medium", - "inputModalities": [ - "text", - "image" - ], - "supportsPersonality": false, - "isDefault": false - } - ], - "nextCursor": null -} -{ - "eventType": "request", - "method": "account/read", - "params": { - "refreshToken": false - } -} -{ - "eventType": "response", - "account": null, - "requiresOpenaiAuth": true -} -{ - "eventType": "request", - "method": "skills/list", - "params": {} -} \ No newline at end of file diff --git a/src/__tests__/CodexACPAgent/data/thread-resume.json b/src/__tests__/CodexACPAgent/data/thread-resume.json deleted file mode 100644 index 942873e7..00000000 --- a/src/__tests__/CodexACPAgent/data/thread-resume.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "eventType": "request", - "method": "thread/resume", - "params": { - "approvalPolicy": null, - "sandbox": "sandbox", - "baseInstructions": null, - "config": { - "projects": { - "": { - "trust_level": "trusted" - } - } - }, - "cwd": "cwd", - "developerInstructions": null, - "history": null, - "model": "model", - "modelProvider": null, - "path": "path", - "personality": null, - "threadId": "threadId" - } -} -{ - "eventType": "response", - "thread": "thread", - "model": "model", - "modelProvider": "openai", - "cwd": "cwd", - "approvalPolicy": "on-request", - "sandbox": "sandbox", - "reasoningEffort": "reasoningEffort" -} -{ - "eventType": "request", - "method": "model/list", - "params": { - "cursor": null, - "limit": null - } -} -{ - "eventType": "response", - "data": "data", - "nextCursor": null -} -{ - "eventType": "request", - "method": "account/read", - "params": { - "refreshToken": false - } -} -{ - "eventType": "response", - "account": null, - "requiresOpenaiAuth": true -} -{ - "eventType": "request", - "method": "skills/list", - "params": {} -} \ No newline at end of file diff --git a/src/__tests__/CodexACPAgent/reasoning-events.test.ts b/src/__tests__/CodexACPAgent/reasoning-events.test.ts deleted file mode 100644 index 59e1a41f..00000000 --- a/src/__tests__/CodexACPAgent/reasoning-events.test.ts +++ /dev/null @@ -1,345 +0,0 @@ -import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; -import { CodexAcpServer } from '../../CodexAcpServer'; -import { createMockConnections, testEventHandling, type MockConnections } from './test-utils'; - -//TODO rewrite to more stable version -describe.skip('CodexACPAgent - reasoning events', () => { - let agent: CodexAcpServer; - let mocks: MockConnections; - const sessionId = 'test-session-id'; - - beforeEach(async () => { - mocks = createMockConnections(); - - agent = new CodexAcpServer(mocks.mockAcpConnection, mocks.mockCodexConnection); - - mocks.mockCodexConnection.sendRequest.mockResolvedValueOnce( - { conversationId: sessionId } - ); - await agent.newSession({ - cwd: "", - mcpServers: [] - }); - - vi.clearAllMocks(); - }); - - afterEach(() => { - vi.clearAllMocks(); - }); - - describe('agent_reasoning event (non-streaming)', () => { - it('should send agent_thought_chunk with text content', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: 'Analyzing the problem and considering different approaches...', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: 'Analyzing the problem and considering different approaches...', - }, - }, - }); - }); - - it('should handle empty reasoning text', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: '', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: '', - }, - }, - }); - }); - - it('should handle multi-line reasoning text', async () => { - const multiLineText = `Step 1: Understanding the request -Step 2: Analyzing available options -Step 3: Making a decision`; - - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: multiLineText, - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: multiLineText, - }, - }, - }); - }); - - it('should handle special characters in reasoning text', async () => { - const specialText = 'Using `CodexACPAgent` with @annotations & symbols: {key: "value"}'; - - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: specialText, - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: specialText, - }, - }, - }); - }); - }); - - describe('reasoning_content_delta event (streaming)', () => { - it('should send agent_thought_chunk with delta text', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'reasoning_content_delta', - delta: 'First chunk of reasoning...', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: 'First chunk of reasoning...', - }, - }, - }); - }); - - it('should handle multiple delta chunks', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'First ' }, - { type: 'reasoning_content_delta', delta: 'second ' }, - { type: 'reasoning_content_delta', delta: 'third.' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(3); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('First '); - expect(thoughtChunkCalls[1][0].update.content.text).toBe('second '); - expect(thoughtChunkCalls[2][0].update.content.text).toBe('third.'); - }); - - it('should suppress agent_reasoning when deltas were sent', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'Streamed content' }, - { type: 'agent_reasoning', text: 'Should be suppressed' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - // Should only have the delta, not the full reasoning - expect(thoughtChunkCalls).toHaveLength(1); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Streamed content'); - }); - }); - - describe('reasoning_raw_content_delta event (streaming)', () => { - it('should send agent_thought_chunk with delta text', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'reasoning_raw_content_delta', - delta: 'Raw content delta...', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: 'Raw content delta...', - }, - }, - }); - }); - - it('should suppress agent_reasoning after raw delta', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_raw_content_delta', delta: 'Raw delta' }, - { type: 'agent_reasoning', text: 'Should be suppressed' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(1); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Raw delta'); - }); - }); - - describe('agent_reasoning_section_break event', () => { - it('should send double newline for section break', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning_section_break', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: '\n\n', - }, - }, - }); - }); - - it('should work between delta chunks for formatting', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'First section' }, - { type: 'agent_reasoning_section_break' }, - { type: 'reasoning_content_delta', delta: 'Second section' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(3); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('First section'); - expect(thoughtChunkCalls[1][0].update.content.text).toBe('\n\n'); - expect(thoughtChunkCalls[2][0].update.content.text).toBe('Second section'); - }); - - it('should suppress agent_reasoning after section break', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'agent_reasoning_section_break' }, - { type: 'agent_reasoning', text: 'Should be suppressed' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(1); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('\n\n'); - }); - }); - - describe('seenReasoningDeltas flag behavior', () => { - it('should send agent_reasoning when no deltas were sent (non-streaming)', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: 'Complete reasoning in non-streaming mode', - }); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(1); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Complete reasoning in non-streaming mode'); - }); - - it('should reset seenReasoningDeltas flag for next turn', async () => { - // First turn with streaming - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'Turn 1 delta' }, - { type: 'agent_reasoning', text: 'Turn 1 should be suppressed' }, - ]); - - vi.clearAllMocks(); - - // Second turn without streaming - await testEventHandling(agent, sessionId, mocks, { - type: 'agent_reasoning', - text: 'Turn 2 should be sent', - }); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - // Should send the agent_reasoning from turn 2 since flag was reset - expect(thoughtChunkCalls).toHaveLength(1); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Turn 2 should be sent'); - }); - }); - - describe('mixed streaming scenarios', () => { - it('should handle mix of content_delta and raw_content_delta', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'Formatted ' }, - { type: 'reasoning_raw_content_delta', delta: 'raw ' }, - { type: 'reasoning_content_delta', delta: 'content' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(3); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Formatted '); - expect(thoughtChunkCalls[1][0].update.content.text).toBe('raw '); - expect(thoughtChunkCalls[2][0].update.content.text).toBe('content'); - }); - - it('should handle empty deltas', async () => { - await testEventHandling(agent, sessionId, mocks, { - type: 'reasoning_content_delta', - delta: '', - }); - - expect(mocks.mockAcpConnection.sessionUpdate).toHaveBeenCalledWith({ - sessionId, - update: { - sessionUpdate: 'agent_thought_chunk', - content: { - type: 'text', - text: '', - }, - }, - }); - }); - - it('should handle complex streaming with section breaks', async () => { - await testEventHandling(agent, sessionId, mocks, [ - { type: 'reasoning_content_delta', delta: 'Section 1' }, - { type: 'agent_reasoning_section_break' }, - { type: 'reasoning_raw_content_delta', delta: 'Section 2 raw' }, - { type: 'agent_reasoning_section_break' }, - { type: 'reasoning_content_delta', delta: 'Section 3' }, - { type: 'agent_reasoning', text: 'Should be suppressed' }, - ]); - - const thoughtChunkCalls = mocks.mockAcpConnection.sessionUpdate.mock.calls.filter( - (call: any) => call[0].update.sessionUpdate === 'agent_thought_chunk' - ); - - expect(thoughtChunkCalls).toHaveLength(5); - expect(thoughtChunkCalls[0][0].update.content.text).toBe('Section 1'); - expect(thoughtChunkCalls[1][0].update.content.text).toBe('\n\n'); - expect(thoughtChunkCalls[2][0].update.content.text).toBe('Section 2 raw'); - expect(thoughtChunkCalls[3][0].update.content.text).toBe('\n\n'); - expect(thoughtChunkCalls[4][0].update.content.text).toBe('Section 3'); - }); - }); -}); From 2881b8eae8f8d78d65beff4a89038692b2bffaaf Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 12:21:18 +0200 Subject: [PATCH 6/7] Isolate tests with temporary CODEX_HOME --- src/CodexJsonRpcConnection.ts | 7 +- .../CodexACPAgent/CodexAcpClient.test.ts | 244 +++++++----------- src/__tests__/acp-test-utils.ts | 30 ++- 3 files changed, 130 insertions(+), 151 deletions(-) diff --git a/src/CodexJsonRpcConnection.ts b/src/CodexJsonRpcConnection.ts index d8b9c5be..d96736bd 100644 --- a/src/CodexJsonRpcConnection.ts +++ b/src/CodexJsonRpcConnection.ts @@ -11,10 +11,11 @@ export interface CodexConnection { readonly process: ChildProcessWithoutNullStreams; } -export function startCodexConnection(codexPath: string): CodexConnection { +export function startCodexConnection(codexPath: string, env?: NodeJS.ProcessEnv): CodexConnection { + const spawnEnv = env ?? process.env; const codex: ChildProcessWithoutNullStreams = process.platform === 'win32' - ? spawn(`"${codexPath}" app-server`, { shell: true }) - : spawn(codexPath, ['app-server']); + ? spawn(`"${codexPath}" app-server`, { shell: true, env: spawnEnv }) + : spawn(codexPath, ['app-server'], { env: spawnEnv }); attachLogs(codex); diff --git a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts index 7a7147d7..98d36037 100644 --- a/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts +++ b/src/__tests__/CodexACPAgent/CodexAcpClient.test.ts @@ -1,9 +1,6 @@ // noinspection ES6RedundantAwait import {describe, expect, it, vi, beforeEach} from 'vitest'; -import fs from "node:fs"; -import os from "node:os"; -import path from "node:path"; import type {CodexAuthRequest} from "../../CodexAuthMethod"; import type * as acp from "@agentclientprotocol/sdk"; import {createTestFixture, createCodexMockTestFixture, createTestSessionState, type TestFixture} from "../acp-test-utils"; @@ -14,46 +11,6 @@ import type {ListMcpServerStatusResponse, Model, SkillsListResponse} from "../.. import type {RateLimitsMap} from "../../RateLimitsMap"; import {ModelId} from "../../ModelId"; -const CODEX_HOME_ENV = "CODEX_HOME"; - -async function overrideCodexHome(configToml: string, run: () => Promise): Promise { - const previousCodexHome = process.env[CODEX_HOME_ENV]; - const codexHome = fs.mkdtempSync(path.join(os.tmpdir(), "codex-acp-codex-home-")); - fs.writeFileSync(path.join(codexHome, "config.toml"), configToml, "utf8"); - process.env[CODEX_HOME_ENV] = codexHome; - - try { - return await run(); - } finally { - if (previousCodexHome === undefined) { - delete process.env[CODEX_HOME_ENV]; - } else { - process.env[CODEX_HOME_ENV] = previousCodexHome; - } - await removeDirectoryWithRetry(codexHome); - } -} - -async function removeDirectoryWithRetry(directory: string): Promise { - let lastError: NodeJS.ErrnoException | null = null; - for (let attempt = 0; attempt < 5; attempt += 1) { - try { - fs.rmSync(directory, { recursive: true, force: true }); - return; - } catch (error) { - const err = error as NodeJS.ErrnoException; - if (err.code !== "ENOTEMPTY" && err.code !== "EBUSY") { - throw err; - } - lastError = err; - await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1))); - } - } - if (lastError) { - throw lastError; - } -} - describe('ACP server test', { timeout: 40_000 }, () => { let fixture: TestFixture; @@ -65,126 +22,119 @@ describe('ACP server test', { timeout: 40_000 }, () => { const ignoredFields = ["thread", "cwd", "id", "createdAt", "path", "threadId", "userAgent", "sandbox", "conversationId", "origins", "supportedReasoningEfforts", "reasoningEffort", "model", "readOnlyAccess", "approvalsReviewer"]; it('should throw error without authentication', async () => { - await overrideCodexHome('cli_auth_credentials_store = "file"', async () => { - const authFixture = createTestFixture(); - const codexAcpAgent = authFixture.getCodexAcpAgent(); + const authFixture = createTestFixture(); + const codexAcpAgent = authFixture.getCodexAcpAgent(); - await codexAcpAgent.initialize({protocolVersion: 1}); - await authFixture.getCodexAcpClient().logout(); - authFixture.clearCodexConnectionDump(); + await codexAcpAgent.initialize({protocolVersion: 1}); + await authFixture.getCodexAcpClient().logout(); + authFixture.clearCodexConnectionDump(); - await expect( - codexAcpAgent.newSession({cwd: "", mcpServers: []}) - ).rejects.toThrow("Authentication required"); + await expect( + codexAcpAgent.newSession({cwd: "", mcpServers: []}) + ).rejects.toThrow("Authentication required"); - const transportDump = authFixture.getCodexConnectionDump(ignoredFields); - await expect(transportDump).toMatchFileSnapshot("data/auth-failed.json"); - }); + const transportDump = authFixture.getCodexConnectionDump(ignoredFields); + await expect(transportDump).toMatchFileSnapshot("data/auth-failed.json"); }); it('should authenticate with key', async () => { - // In sandboxed environments Codex may fail when trying to write to the OS keychain (`Operation not permitted`). - await overrideCodexHome('cli_auth_credentials_store = "file"', async () => { - const keyFixture = createTestFixture(); - const codexAcpAgent = keyFixture.getCodexAcpAgent(); - - await codexAcpAgent.initialize({protocolVersion: 1}); - await keyFixture.getCodexAcpClient().logout(); - - - const unauthenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); - expect(unauthenticatedResponse).toEqual({type: "unauthenticated"}); - - keyFixture.clearCodexConnectionDump(); - - const authRequest: CodexAuthRequest = { methodId: "api-key", _meta: { "api-key": { apiKey: "TOKEN" }}} - await codexAcpAgent.authenticate(authRequest); - const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); - expect(newSessionResponse.sessionId).toBeDefined() - - const transportEvents = keyFixture.getCodexConnectionEvents([...ignoredFields, "upgrade"]); - const transportMethods = transportEvents.flatMap(event => "method" in event ? [event.method] : []); - const loginRequest = transportEvents.find(event => - event.eventType === "request" && - "method" in event && - event.method === "account/login/start" - ); - const loginResponse = transportEvents.find(event => - event.eventType === "response" && - "type" in event && - event.type === "apiKey" - ); - const threadStartResponse = transportEvents.find(event => - event.eventType === "response" && - "modelProvider" in event && - "approvalPolicy" in event - ); - expect(transportMethods).toEqual([ - "account/login/start", - "account/read", - "account/updated", - "thread/start", - "model/list", - "thread/started", - "account/read", - "skills/list", - ]); - expect(loginRequest).toEqual({ - eventType: "request", - method: "account/login/start", - params: { - type: "apiKey", - apiKey: "TOKEN", - } - }); - expect(loginResponse).toEqual({ - eventType: "response", + const keyFixture = createTestFixture(); + const codexAcpAgent = keyFixture.getCodexAcpAgent(); + + await codexAcpAgent.initialize({protocolVersion: 1}); + await keyFixture.getCodexAcpClient().logout(); + + + const unauthenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); + expect(unauthenticatedResponse).toEqual({type: "unauthenticated"}); + + keyFixture.clearCodexConnectionDump(); + + const authRequest: CodexAuthRequest = { methodId: "api-key", _meta: { "api-key": { apiKey: "TOKEN" }}} + await codexAcpAgent.authenticate(authRequest); + const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); + expect(newSessionResponse.sessionId).toBeDefined() + + const transportEvents = keyFixture.getCodexConnectionEvents([...ignoredFields, "upgrade"]); + const transportMethods = transportEvents.flatMap(event => "method" in event ? [event.method] : []); + const loginRequest = transportEvents.find(event => + event.eventType === "request" && + "method" in event && + event.method === "account/login/start" + ); + const loginResponse = transportEvents.find(event => + event.eventType === "response" && + "type" in event && + event.type === "apiKey" + ); + const threadStartResponse = transportEvents.find(event => + event.eventType === "response" && + "modelProvider" in event && + "approvalPolicy" in event + ); + expect(transportMethods).toEqual([ + "account/login/start", + "account/read", + "account/updated", + "thread/start", + "model/list", + "thread/started", + "account/read", + "skills/list", + ]); + expect(loginRequest).toEqual({ + eventType: "request", + method: "account/login/start", + params: { type: "apiKey", - }); - expect(threadStartResponse).toMatchObject({ - eventType: "response", - modelProvider: "openai", - approvalPolicy: "on-request", - approvalsReviewer: "approvalsReviewer", - }); - const authenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); - expect(authenticatedResponse).toEqual({type: "api-key"}); - - await keyFixture.getCodexAcpAgent().extMethod("authentication/logout", {}); - const logoutResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); - expect(logoutResponse).toEqual({type: "unauthenticated"}); + apiKey: "TOKEN", + } }); + expect(loginResponse).toEqual({ + eventType: "response", + type: "apiKey", + }); + expect(threadStartResponse).toMatchObject({ + eventType: "response", + modelProvider: "openai", + approvalPolicy: "on-request", + approvalsReviewer: "approvalsReviewer", + }); + const authenticatedResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); + expect(authenticatedResponse).toEqual({type: "api-key"}); + + await keyFixture.getCodexAcpAgent().extMethod("authentication/logout", {}); + const logoutResponse = await keyFixture.getCodexAcpAgent().extMethod("authentication/status", {}); + expect(logoutResponse).toEqual({type: "unauthenticated"}); }); it('should authenticate with a gateway', async () => { - await overrideCodexHome('cli_auth_credentials_store = "file"', async () => { - const gatewayFixture = createTestFixture(); - const codexAcpAgent = gatewayFixture.getCodexAcpAgent(); - - await codexAcpAgent.initialize({protocolVersion: 1}); - await gatewayFixture.getCodexAcpClient().logout(); - - const authRequest: CodexAuthRequest = { - methodId: "gateway", - _meta: { - "gateway": { - baseUrl: "https://www.example.com", - headers: { - "Custom-Auth-Header": "TOKEN" - } + const gatewayFixture = createTestFixture(); + const codexAcpAgent = gatewayFixture.getCodexAcpAgent(); + + await codexAcpAgent.initialize({protocolVersion: 1}); + await gatewayFixture.getCodexAcpClient().logout(); + + const authRequest: CodexAuthRequest = { + methodId: "gateway", + _meta: { + "gateway": { + baseUrl: "https://www.example.com", + headers: { + "Custom-Auth-Header": "TOKEN" } } - }; + } + }; - await codexAcpAgent.authenticate(authRequest); - expect(await gatewayFixture.getCodexAcpClient().authRequired()).toBe(false); + await codexAcpAgent.authenticate(authRequest); + expect(await gatewayFixture.getCodexAcpClient().authRequired()).toBe(false); - const authenticatedResponse = await gatewayFixture.getCodexAcpAgent().extMethod("authentication/status", {}); - expect(authenticatedResponse).toEqual({type: "gateway", name: "custom-gateway"}); + const authenticatedResponse = await gatewayFixture.getCodexAcpAgent().extMethod("authentication/status", {}); + expect(authenticatedResponse).toEqual({type: "gateway", name: "custom-gateway"}); - const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); - expect(newSessionResponse.sessionId).toBeDefined() - }); + const newSessionResponse = await codexAcpAgent.newSession({cwd: "", mcpServers: []}); + expect(newSessionResponse.sessionId).toBeDefined() }) it('prefetches session additional skill roots before thread start', async () => { diff --git a/src/__tests__/acp-test-utils.ts b/src/__tests__/acp-test-utils.ts index f8b02615..54053c77 100644 --- a/src/__tests__/acp-test-utils.ts +++ b/src/__tests__/acp-test-utils.ts @@ -7,6 +7,7 @@ import type {ServerNotification} from "../app-server"; import type {MessageConnection} from "vscode-jsonrpc/node"; import path from "node:path"; import fs from "node:fs"; +import os from "node:os"; import {AgentMode} from "../AgentMode"; import {expect, vi} from "vitest"; @@ -153,7 +154,14 @@ export function createTestFixture(): TestFixture { throw new Error(`Codex binary not found at ${pathToCodex}. Did you run 'npm install'?`); } - const codexConnection = startCodexConnection(pathToCodex); + const codexHome = createTestCodexHome(); + const codexConnection = startCodexConnection(pathToCodex, { + ...process.env, + CODEX_HOME: codexHome, + }); + codexConnection.process.on("exit", () => { + removeDirectoryWithRetry(codexHome); + }); return createBaseTestFixture({ connection: codexConnection.connection, @@ -161,6 +169,26 @@ export function createTestFixture(): TestFixture { }); } +function createTestCodexHome(): string { + const codexHome = fs.mkdtempSync(path.join(os.tmpdir(), "codex-acp-codex-home-")); + fs.writeFileSync(path.join(codexHome, "config.toml"), 'cli_auth_credentials_store = "file"\n', "utf8"); + return codexHome; +} + +function removeDirectoryWithRetry(directory: string): void { + for (let attempt = 0; attempt < 5; attempt += 1) { + try { + fs.rmSync(directory, { recursive: true, force: true }); + return; + } catch (error) { + const err = error as NodeJS.ErrnoException; + if (err.code !== "ENOTEMPTY" && err.code !== "EBUSY") { + return; + } + } + } +} + export interface CodexMockTestFixture extends TestFixture { sendServerNotification(notification: ServerNotification | Record): void, sendServerRequest(method: string, params: unknown): Promise, From 7d18be0f225a10e6c309c980cd455e27e9e654f7 Mon Sep 17 00:00:00 2001 From: "Ilia.Shulgin" Date: Mon, 20 Apr 2026 13:49:54 +0200 Subject: [PATCH 7/7] Publish to npm as part of release workflow --- .github/workflows/publish-npm.yml | 25 ------------------------- .github/workflows/release.yml | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 25 deletions(-) delete mode 100644 .github/workflows/publish-npm.yml diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml deleted file mode 100644 index 6499cd85..00000000 --- a/.github/workflows/publish-npm.yml +++ /dev/null @@ -1,25 +0,0 @@ -name: Publish to NPM - -on: - release: - types: [published] - workflow_dispatch: - -jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: read - id-token: write - steps: - - uses: actions/checkout@v6 - - uses: actions/setup-node@v6 - with: - node-version: '24' - registry-url: 'https://registry.npmjs.org' - - - name: Install dependencies - run: npm ci - - - name: Publish to NPM - run: npm publish --access public --tag beta diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b6315ce..670eee40 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -228,3 +228,24 @@ jobs: filename=$(basename "$file") echo " ${BASE_URL}/latest/${filename}" done + + publish-to-npm: + name: Publish to NPM + needs: build-and-release + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 + with: + node-version: '24' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + run: npm ci + + - name: Publish to NPM + run: npm publish --access public --tag beta