fix(server): percent-encode non-ASCII directory headers to OpenCode

This commit is contained in:
fitzgpt
2026-08-24 02:21:35 +03:00
parent fe80d246ea
commit d6a5192fba
4 changed files with 44 additions and 2 deletions
@@ -83,7 +83,10 @@ const resolveVariant = (providers, providerID, modelID, variant) => {
const parseConfigModel = (value) => splitModel(value);
const buildDirectoryHeaders = (directory) => ({
...(directory ? { 'x-opencode-directory': directory } : {}),
// OpenCode rejects non-ASCII header values; the official SDK sends this
// header percent-encoded, so match that wire format (non-ASCII checkout
// paths such as "Masaüstü" otherwise fail every dispatched prompt).
...(directory ? { 'x-opencode-directory': encodeURIComponent(directory) } : {}),
});
const fetchJson = async (url, authHeaders, fallback, directory) => {
@@ -161,6 +161,29 @@ describe('openchamber session routes', () => {
}
});
it('percent-encodes the directory header for non-ASCII checkout paths', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = vi.fn(async () => ({ ok: true, json: async () => ({ id: 'ses_123' }) }));
try {
const { app } = createApp();
await request(app)
.post('/api/openchamber/sessions')
.send({ directory: '/home/user/Masaüstü/projeler', title: 'Side task' })
.expect(200);
expect(globalThis.fetch).toHaveBeenCalledWith(
expect.any(String),
expect.objectContaining({
headers: expect.objectContaining({
'x-opencode-directory': encodeURIComponent('/home/user/Masaüstü/projeler'),
}),
}),
);
} finally {
globalThis.fetch = originalFetch;
}
});
it('parses JSON body without global middleware', async () => {
const originalFetch = globalThis.fetch;
globalThis.fetch = vi.fn(async () => ({ ok: true, json: async () => ({ id: 'ses_123' }) }));