fix: resolve spawn EINVAL on Windows — filter undefined env values and skip .cmd wrappers
Two root causes of spawn EINVAL on Windows: 1. process.env spread can include undefined values. Windows child_process.spawn requires all env values to be strings. sanitizeEnv now builds a clean object, dropping non-string entries. 2. Claude CLI installed via npm creates .cmd wrappers that cannot be spawned without shell:true. Detect .cmd/.bat extensions and fall back to SDK's internal resolution instead. Bump version to 0.10.7. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
83a0109af1
commit
c0e96bd1aa
3 files changed
+19
-8
No files matched your search
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "codepilot",
|
||||
"version": "0.10.6",
|
||||
"version": "0.10.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "codepilot",
|
||||
"version": "0.10.6",
|
||||
"version": "0.10.7",
|
||||
"dependencies": {
|
||||
"@anthropic-ai/claude-agent-sdk": "^0.2.33",
|
||||
"@hugeicons/core-free-icons": "^3.1.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "codepilot",
|
||||
"version": "0.10.6",
|
||||
"version": "0.10.7",
|
||||
"private": true,
|
||||
"author": {
|
||||
"name": "op7418",
|
||||
|
||||
@@ -35,15 +35,18 @@ function sanitizeEnvValue(value: string): string {
|
||||
|
||||
/**
|
||||
* Sanitize all values in an env record so child_process.spawn won't
|
||||
* throw EINVAL due to invalid characters (null bytes, control chars).
|
||||
* throw EINVAL due to invalid characters or non-string values.
|
||||
* On Windows, spawn is strict: every env value MUST be a string.
|
||||
* Spreading process.env can include undefined values which cause EINVAL.
|
||||
*/
|
||||
function sanitizeEnv(env: Record<string, string>): Record<string, string> {
|
||||
const clean: Record<string, string> = {};
|
||||
for (const [key, value] of Object.entries(env)) {
|
||||
if (typeof value === 'string') {
|
||||
env[key] = sanitizeEnvValue(value);
|
||||
clean[key] = sanitizeEnvValue(value);
|
||||
}
|
||||
}
|
||||
return env;
|
||||
return clean;
|
||||
}
|
||||
|
||||
let cachedClaudePath: string | null | undefined;
|
||||
@@ -291,10 +294,18 @@ export function streamClaude(options: ClaudeStreamOptions): ReadableStream<strin
|
||||
queryOptions.allowDangerouslySkipPermissions = true;
|
||||
}
|
||||
|
||||
// Find claude binary for packaged app where PATH is limited
|
||||
// Find claude binary for packaged app where PATH is limited.
|
||||
// On Windows, npm installs Claude CLI as a .cmd wrapper which cannot
|
||||
// be spawned directly without shell:true — skip it and let the SDK
|
||||
// resolve the executable internally.
|
||||
const claudePath = findClaudePath();
|
||||
if (claudePath) {
|
||||
queryOptions.pathToClaudeCodeExecutable = claudePath;
|
||||
const ext = path.extname(claudePath).toLowerCase();
|
||||
if (ext === '.cmd' || ext === '.bat') {
|
||||
console.warn('[claude-client] Ignoring .cmd/.bat wrapper, falling back to SDK resolution:', claudePath);
|
||||
} else {
|
||||
queryOptions.pathToClaudeCodeExecutable = claudePath;
|
||||
}
|
||||
}
|
||||
|
||||
if (model) {
|
||||
|
||||
Reference in new issue
Block a user