fix(subagent): quote Windows Claude batch paths
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||
# after editing either side, bring the other along and re-record with:
|
||||
# pnpm run verify-translation-pairing --write packages/subagent/subagent-claude-code/README.md
|
||||
README.md: 5e3138b9211b01de9096fa1b8e8b68321aad0c7d
|
||||
README.zh.md: cc536d13d58e08efc77f4f7b4374c8a1af8c5caa
|
||||
README.md: 432a73474fee1c0cd3a3046247252a9b48cf865c
|
||||
README.zh.md: 8af6f5050de44c01391662b4f92676711ef8892f
|
||||
|
||||
@@ -29,7 +29,7 @@ The provider advertises no optional start-time capabilities and reports `inherit
|
||||
| `env` | `{}` | Explicit SDK/CLI environment layered over the shared credential-scrubbed parent environment. |
|
||||
| `disposeGraceMs` | `3000` | Positive finite grace in milliseconds, no greater than [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.md), between the shared process-tree owner's termination tiers; disposal then waits for whole-tree exit. |
|
||||
|
||||
Production resolves `claude` from the subprocess execution world's credential-scrubbed `PATH`, with explicit `env` entries applied, and passes the resulting path to the SDK as `pathToClaudeCodeExecutable`. Native settings and authentication remain authoritative. The plugin does not install another CLI, select a model, create a product home, log in, or probe an account. Credential-shaped ambient variables are removed before the explicit `env` overlay is applied, so an API key or token intended for the child must be supplied there. Non-credential endpoint variables such as `ANTHROPIC_BASE_URL`, along with ordinary ambient values such as `PATH` and `HOME`, remain inherited unless overridden.
|
||||
Production resolves `claude` from the subprocess execution world's credential-scrubbed `PATH`, with explicit `env` entries applied, and passes the resulting path to the SDK as `pathToClaudeCodeExecutable`. On Windows, a resolved `.cmd` or `.bat` path is carried as a quoted, per-spawn environment value that `cmd.exe` expands once, so valid path metacharacters remain data while the SDK's fixed arguments stay ordinary argv. Native settings and authentication remain authoritative. The plugin does not install another CLI, select a model, create a product home, log in, or probe an account. Credential-shaped ambient variables are removed before the explicit `env` overlay is applied, so an API key or token intended for the child must be supplied there. Non-credential endpoint variables such as `ANTHROPIC_BASE_URL`, along with ordinary ambient values such as `PATH` and `HOME`, remain inherited unless overridden.
|
||||
|
||||
Shipped profiles load this provider once on the host and start no Claude process until a tool call. Full Agent Presets carry the tool row below with `disabled: true`; copy a preset and remove that field to expose `subagent_claude_code` only to agents composed from the copy. A custom host composition can still use both rows directly.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ SDK 接收由文本块原样拼接成的任务。提供方会完整迭代 SDK
|
||||
| `env` | `{}` | 显式指定的 SDK/CLI 环境,叠加在由共享机制清除凭证后的父环境之上。 |
|
||||
| `disposeGraceMs` | `3000` | 共享进程树责任方各终止层级之间的宽限期,单位为毫秒且须为正有限值,并不得大于仓库共享的 [`MAX_TIMER_DELAY_MS`](../../util/timeout/README.md);随后资源释放会等待整棵进程树退出。 |
|
||||
|
||||
生产环境从子进程执行世界清除凭证后的 `PATH` 解析 `claude`,再应用显式 `env` 条目,并把所得路径作为 `pathToClaudeCodeExecutable` 交给 SDK。原生设置与身份验证继续是权威来源。本插件不安装另一份 CLI、不选择模型、不创建产品主目录、不执行登录,也不探测账户。具有凭证特征的环境变量会在显式 `env` 覆盖生效前被清除,因此供子进程使用的 API 密钥或 token 必须在该配置中显式提供。除非被覆盖,`ANTHROPIC_BASE_URL` 等非凭证端点变量以及 `PATH` 和 `HOME` 等普通环境变量仍会被继承。
|
||||
生产环境从子进程执行世界清除凭证后的 `PATH` 解析 `claude`,再应用显式 `env` 条目,并把所得路径作为 `pathToClaudeCodeExecutable` 交给 SDK。在 Windows 上,解析到的 `.cmd` 或 `.bat` 路径会作为带引号、仅供本次 spawn 使用的环境值交给 `cmd.exe` 展开一次,因此合法路径中的元字符仍只是数据,而 SDK 的固定参数继续使用普通 argv。原生设置与身份验证继续是权威来源。本插件不安装另一份 CLI、不选择模型、不创建产品主目录、不执行登录,也不探测账户。具有凭证特征的环境变量会在显式 `env` 覆盖生效前被清除,因此供子进程使用的 API 密钥或 token 必须在该配置中显式提供。除非被覆盖,`ANTHROPIC_BASE_URL` 等非凭证端点变量以及 `PATH` 和 `HOME` 等普通环境变量仍会被继承。
|
||||
|
||||
随附 profile 会在宿主上加载一次该提供方,而且在工具被调用前不会启动 Claude 进程。完整 Agent Preset 携带下列工具行并设置 `disabled: true`;复制一个 preset 后删除该字段,即可只向由该副本组装的 agent 暴露 `subagent_claude_code`。自定义宿主组装仍可直接使用两条配置行。
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
type SubprocessSpawnSpec,
|
||||
} from '@deepseek-ai/dsh-subprocess'
|
||||
|
||||
const WINDOWS_BATCH_EXECUTABLE_ENV = 'DSH_CLAUDE_CODE_EXECUTABLE'
|
||||
|
||||
function thrown(value: unknown): Error {
|
||||
/* v8 ignore next -- the subprocess seam rejects with Error. */
|
||||
return value instanceof Error ? value : new Error(String(value))
|
||||
@@ -53,16 +55,19 @@ export function claudeSpawnSpec(
|
||||
throw new Error('subagent-claude-code: SDK spawn request omitted its workspace')
|
||||
}
|
||||
const extension = extname(options.command).toLowerCase()
|
||||
const argv = platform === 'win32' && (extension === '.cmd' || extension === '.bat')
|
||||
? ['cmd.exe', '/d', '/s', '/c', options.command, ...options.args]
|
||||
const batchShim = platform === 'win32' && (extension === '.cmd' || extension === '.bat')
|
||||
const env = sdkEnvironmentOverlay(options.env)
|
||||
const argv = batchShim
|
||||
? ['cmd.exe', '/d', '/s', '/c', `%${WINDOWS_BATCH_EXECUTABLE_ENV}%`, ...options.args]
|
||||
: [options.command, ...options.args]
|
||||
if (batchShim) env[WINDOWS_BATCH_EXECUTABLE_ENV] = `"${options.command}"`
|
||||
return {
|
||||
argv,
|
||||
cwd: options.cwd,
|
||||
stdio: { stdin: 'pipe', stdout: 'pipe', stderr: 'inherit' },
|
||||
graceMs,
|
||||
signal: options.signal,
|
||||
env: sdkEnvironmentOverlay(options.env),
|
||||
env,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,9 +464,12 @@ describe('official spawn projection', () => {
|
||||
}), 7, 'win32')
|
||||
|
||||
expect(spec.argv).toEqual([
|
||||
'cmd.exe', '/d', '/s', '/c', command,
|
||||
'cmd.exe', '/d', '/s', '/c', '%DSH_CLAUDE_CODE_EXECUTABLE%',
|
||||
'--output-format', 'stream-json',
|
||||
])
|
||||
expect(spec.env).toEqual(expect.objectContaining({
|
||||
DSH_CLAUDE_CODE_EXECUTABLE: `"${command}"`,
|
||||
}))
|
||||
})
|
||||
|
||||
it('projects streams, exit facts, listeners, and idempotent tree termination', async () => {
|
||||
|
||||
Reference in New Issue
Block a user