Repository navigation
Conversation
On Windows, spawning npx.cmd without a shell throws a synchronous EINVAL (Node/Bun hardening after CVE-2024-27980), so scripts/setup-learning.ts stopped with "spawn npx.cmd EINVAL" and the server's Learning setup at boot could not run the CLI either. - npxCommand() starts npx.cmd with shell: true on Windows only. - cmdLineSafe() refuses an argument cmd.exe would run or expand (the project id read from .copilotkit/project.json is not a literal), and setup-learning.ts names such an id instead of failing later. - stopProcessTree() ends the whole tree with taskkill /T /F on timeout; child.kill() ended only cmd.exe and left the CLI holding the pipes. Fixes CopilotKit#792
Caldas
requested review from
MikeRyanDev,
davidmckayv,
guidovizoso,
mxmzb and
tylerslaton
as code owners
October 14, 2026 23:37
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this changes
Fixes #792. On Windows,
bun scripts/setup-learning.tsstopped at once withspawn npx.cmd EINVALand wrote nothing to.env. The cause is the hardening that followed CVE-2024-27980: Node and Bun refuse to spawn a.cmdwithout a shell, and the refusal is a synchronous throw. The server's own Learning setup at boot (ensureLearningContainer,server/src/index.ts:360) goes through the samerunCli, so it could not run the CLI on Windows either.In
server/src/learning/ensure-container.ts:npxCommand(platform)returnsnpx.cmdwithshell: trueon Windows, andnpxwithout a shell everywhere else. macOS and Linux are unchanged.cmdLineSafe(args): with a shell, the arguments become one unquotedcmd.exeline. A&or|would start another command, and a%would expand a variable. Not every argument is a literal:setup-learning.tspasses the project id it reads from.copilotkit/project.json. On the shell path,runNpxrefuses any argument outside[A-Za-z0-9@._:/+-]without starting anything. Every argument any caller passes today fits.setup-learning.tschecks the project id where it reads it, so a bad id is named instead of surfacing later as "CopilotKit returned an invalid setup response".stopProcessTree(child, shell): on Windows,child.kill()endscmd.exeonly. The CLI under it keeps the pipes open, soclose(and the promise) waited for it, and the two-minute limit did not hold. The timer now runs%SystemRoot%\System32\taskkill.exe /pid <pid> /T /F, falling back tochild.kill(). It does nothing oncecmd.exehas exited, so it cannot act on a reused pid.runNpx(args, directory): the spawn moved here fromrunCli, which now calls it with--yes copilotkit@latest. That lets the tests run the real spawn path.Tests
server/tests/learning-ensure-container.test.tsandscripts/setup-learning.test.ts, 35 pass on Windows 11 with Bun 1.4.3:npxCommandandcmdLineSafeare tested on every platform.test.if(process.platform === "win32")), because the behaviour does not exist elsewhere:runNpx(["--version"])really starts and exits 0, where before it threw EINVAL.runNpxrefusesx&echo INJECTEDwithout starting anything.stopProcessTreeends aping -n 30undercmd.exewithin 4 s. Withchild.kill()in its place, the test times out:closetook 6.2 s for an 8-ping run.setup-learningnames a12&calcproject id and never calls the CLI.test:cionly enforces a floor of 400 tests, so skipping the Windows tests on Linux cannot lower the count below it.bun run format:checkandbun run lintare clean on the changed files, and so isbiome check.tsc --noEmitinserver/exits 0.bun scripts/setup-learning.tsnow exits 0 and writesINTELLIGENCE_API_KEYandCPK_INTELLIGENCE_LEARNING_CONTAINER_ID.Not changed: with a UNC working directory (
\\server\share),cmd.exefalls back toC:\Windows. This is rare, it existed before this change, and the checks that follow fail closed.Where it runs
Boundary and audit
runClifailures already are.Changelog
CHANGELOG.mdunderUnreleased: "setup-learning.tsruns on Windows".