fix: clean up iMessage adapter type compatibility

Replace `as never` cast with proper polyfill for channelIdFromThreadId.
Narrow GatewayAdapter cast to only the gateway code path in bridge.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-09 11:42:49 +03:00
parent 9486d56b01
commit 2b64fec0e6
2 changed files with 11 additions and 6 deletions

View File

@@ -31,7 +31,7 @@ interface GatewayAdapter extends Adapter {
} }
export interface ChatSdkBridgeConfig { export interface ChatSdkBridgeConfig {
adapter: GatewayAdapter; adapter: Adapter;
concurrency?: ConcurrencyStrategy; concurrency?: ConcurrencyStrategy;
/** Bot token for authenticating forwarded Gateway events (required for interaction handling). */ /** Bot token for authenticating forwarded Gateway events (required for interaction handling). */
botToken?: string; botToken?: string;
@@ -114,17 +114,18 @@ export function createChatSdkBridge(config: ChatSdkBridgeConfig): ChannelAdapter
await chat.initialize(); await chat.initialize();
// Start Gateway listener for adapters that support it (e.g., Discord) // Start Gateway listener for adapters that support it (e.g., Discord)
if (adapter.startGatewayListener) { const gatewayAdapter = adapter as GatewayAdapter;
if (gatewayAdapter.startGatewayListener) {
gatewayAbort = new AbortController(); gatewayAbort = new AbortController();
// Start local HTTP server to receive forwarded Gateway events (including interactions) // Start local HTTP server to receive forwarded Gateway events (including interactions)
const webhookUrl = await startLocalWebhookServer(adapter, setupConfig, config.botToken); const webhookUrl = await startLocalWebhookServer(gatewayAdapter, setupConfig, config.botToken);
const startGateway = () => { const startGateway = () => {
if (gatewayAbort?.signal.aborted) return; if (gatewayAbort?.signal.aborted) return;
// Capture the long-running listener promise via waitUntil // Capture the long-running listener promise via waitUntil
let listenerPromise: Promise<unknown> | undefined; let listenerPromise: Promise<unknown> | undefined;
adapter.startGatewayListener!( gatewayAdapter.startGatewayListener!(
{ {
waitUntil: (p: Promise<unknown>) => { waitUntil: (p: Promise<unknown>) => {
listenerPromise = p; listenerPromise = p;

View File

@@ -15,11 +15,15 @@ registerChannelAdapter('imessage', {
const isLocal = env.IMESSAGE_LOCAL !== 'false'; const isLocal = env.IMESSAGE_LOCAL !== 'false';
if (isLocal && !env.IMESSAGE_ENABLED) return null; if (isLocal && !env.IMESSAGE_ENABLED) return null;
if (!isLocal && !env.IMESSAGE_SERVER_URL) return null; if (!isLocal && !env.IMESSAGE_SERVER_URL) return null;
const imessageAdapter = createiMessageAdapter({ const rawAdapter = createiMessageAdapter({
local: isLocal, local: isLocal,
serverUrl: env.IMESSAGE_SERVER_URL, serverUrl: env.IMESSAGE_SERVER_URL,
apiKey: env.IMESSAGE_API_KEY, apiKey: env.IMESSAGE_API_KEY,
}); });
return createChatSdkBridge({ adapter: imessageAdapter as never, concurrency: 'concurrent' }); // Polyfill channelIdFromThreadId (community adapter doesn't implement it)
const imessageAdapter = Object.assign(rawAdapter, {
channelIdFromThreadId: (threadId: string) => threadId,
});
return createChatSdkBridge({ adapter: imessageAdapter, concurrency: 'concurrent' });
}, },
}); });