ポート競合回避

This commit is contained in:
nrslib 2026-02-11 07:04:46 +09:00
parent 36e77ae0fa
commit 166d6d9b5c
2 changed files with 28 additions and 0 deletions

View File

@ -109,6 +109,7 @@ describe('PieceEngine Integration: Error Handling', () => {
const reason = abortFn.mock.calls[0]![1] as string;
expect(reason).toContain('API connection failed');
});
});
// =====================================================

View File

@ -6,6 +6,7 @@
*/
import { createOpencode } from '@opencode-ai/sdk/v2';
import { createServer } from 'node:net';
import type { AgentResponse } from '../../core/models/index.js';
import { createLogger, getErrorMessage } from '../../shared/utils/index.js';
import { mapToOpenCodePermissionReply, type OpenCodeCallOptions } from './types.js';
@ -35,8 +36,32 @@ const OPENCODE_RETRYABLE_ERROR_PATTERNS = [
'etimedout',
'eai_again',
'fetch failed',
'failed to start server on port',
];
async function getFreePort(): Promise<number> {
return new Promise<number>((resolve, reject) => {
const server = createServer();
server.unref();
server.on('error', reject);
server.listen(0, '127.0.0.1', () => {
const addr = server.address();
if (!addr || typeof addr === 'string') {
server.close(() => reject(new Error('Failed to allocate free TCP port')));
return;
}
const port = addr.port;
server.close((err) => {
if (err) {
reject(err);
return;
}
resolve(port);
});
});
});
}
/**
* Client for OpenCode SDK agent interactions.
*
@ -129,7 +154,9 @@ export class OpenCodeClient {
attempt,
});
const port = await getFreePort();
const { client, server } = await createOpencode({
port,
signal: streamAbortController.signal,
...(options.opencodeApiKey
? { config: { provider: { opencode: { options: { apiKey: options.opencodeApiKey } } } } }