fix(acp-adapter): convert Unix paths to Windows separators for ACP file RPC (#628)

- fix readTextFile and writeTextFile to use backslash separators on win32
- add changeset for acp-adapter and kimi-code
This commit is contained in:
Haozhe 2026-06-10 20:00:54 +08:00 committed by GitHub
parent ecc0496115
commit 0ee91066ea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 45 additions and 6 deletions

View file

@ -0,0 +1,6 @@
---
"@moonshot-ai/acp-adapter": patch
"@moonshot-ai/kimi-code": patch
---
Fix ACP file reads and edits for Windows workspaces opened through IDE clients.

View file

@ -122,11 +122,12 @@ export class AcpKaos implements Kaos {
path: string,
_options?: { encoding?: BufferEncoding; errors?: 'strict' | 'replace' | 'ignore' },
): Promise<string> {
const rpcPath = this.toClientPath(path);
try {
const resp = await this.conn.readTextFile({ sessionId: this.sessionId, path });
const resp = await this.conn.readTextFile({ sessionId: this.sessionId, path: rpcPath });
return resp.content;
} catch (err) {
throw wrapKaosError(`acp: readTextFile failed for ${path}`, err);
throw wrapKaosError(`acp: readTextFile failed for ${rpcPath}`, err);
}
}
@ -221,13 +222,19 @@ export class AcpKaos implements Kaos {
}
private async acpWrite(path: string, content: string): Promise<void> {
const rpcPath = this.toClientPath(path);
try {
await this.conn.writeTextFile({ sessionId: this.sessionId, path, content });
await this.conn.writeTextFile({ sessionId: this.sessionId, path: rpcPath, content });
} catch (err) {
throw wrapKaosError(`acp: writeTextFile failed for ${path}`, err);
throw wrapKaosError(`acp: writeTextFile failed for ${rpcPath}`, err);
}
}
private toClientPath(path: string): string {
if (this.inner.pathClass() !== 'win32') return path;
return path.replaceAll('/', '\\');
}
// ── process execution: delegate to inner ───────────────────────────
exec(...args: string[]): Promise<KaosProcess> {

View file

@ -82,7 +82,8 @@ interface MockInnerKaos extends Kaos {
};
}
function makeMockInner(): MockInnerKaos {
function makeMockInner(opts?: { pathClass?: 'posix' | 'win32' }): MockInnerKaos {
const pathClass = opts?.pathClass ?? 'posix';
const spy = {
pathClassCalls: 0,
normpathCalls: [] as string[],
@ -107,7 +108,7 @@ function makeMockInner(): MockInnerKaos {
osEnv: { os: 'linux', shell: 'bash' } as unknown as Environment,
pathClass: () => {
spy.pathClassCalls += 1;
return 'posix';
return pathClass;
},
normpath: (p: string) => {
spy.normpathCalls.push(p);
@ -231,6 +232,31 @@ describe('AcpKaos', () => {
expect((err as Error).message).toContain('rpc died');
}
});
it('uses win32-native separators for ACP file RPC paths', async () => {
const conn = makeMockConn({
readHandler: async () => ({ content: 'HELLO' }),
});
const inner = makeMockInner({ pathClass: 'win32' });
const kaos = new AcpKaos(conn.asConn(), 's1', inner);
await kaos.readText('G:/python-code/render_with_mult_gpu/README.md');
await kaos.writeText('G:/python-code/render_with_mult_gpu/README.md', 'updated');
expect(conn.readCalls).toEqual([
{
sessionId: 's1',
path: 'G:\\python-code\\render_with_mult_gpu\\README.md',
},
]);
expect(conn.writeCalls).toEqual([
{
sessionId: 's1',
path: 'G:\\python-code\\render_with_mult_gpu\\README.md',
content: 'updated',
},
]);
});
});
describe('readBytes', () => {