@codex-app-sdk/electron 0.14.3 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{codex-electron-preload-z_8XhSEC.js → codex-electron-preload-DupT4aeD.js} +22 -11
- package/dist/chunks/{codex-electron-preload-z_8XhSEC.js.map → codex-electron-preload-DupT4aeD.js.map} +1 -1
- package/dist/codex-native-ipc.d.ts +2 -1
- package/dist/codex-native-ipc.d.ts.map +1 -1
- package/dist/codex-native-renderer.d.ts +12 -1
- package/dist/codex-native-renderer.d.ts.map +1 -1
- package/dist/codex-speech-sessions.d.ts +15 -0
- package/dist/codex-speech-sessions.d.ts.map +1 -0
- package/dist/index.js +178 -85
- package/dist/index.js.map +1 -1
- package/dist/preload.js +2 -2
- package/package.json +3 -3
package/dist/chunks/{codex-electron-preload-z_8XhSEC.js → codex-electron-preload-DupT4aeD.js}
RENAMED
|
@@ -216,17 +216,28 @@ var d = {
|
|
|
216
216
|
openExternal: "codex-native:open-external",
|
|
217
217
|
pickAttachments: "codex-native:pick-attachments",
|
|
218
218
|
readImagePreview: "codex-native:read-image-preview",
|
|
219
|
-
transcribeAudio: "codex-native:transcribe-audio"
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
transcribeAudio: "codex-native:transcribe-audio",
|
|
220
|
+
startSpeech: "codex-native:start-speech",
|
|
221
|
+
appendSpeech: "codex-native:append-speech",
|
|
222
|
+
stopSpeech: "codex-native:stop-speech",
|
|
223
|
+
cancelSpeech: "codex-native:cancel-speech"
|
|
224
|
+
}, f = "codex-native:speech-event";
|
|
225
|
+
function p(e, t = {}) {
|
|
226
|
+
let n = new r(e), i = t.transcription ?? process.platform === "darwin";
|
|
223
227
|
return {
|
|
224
228
|
capabilities: {
|
|
225
229
|
attachments: !0,
|
|
226
230
|
clipboard: !0,
|
|
227
231
|
externalLinks: !0,
|
|
228
|
-
transcription:
|
|
232
|
+
transcription: i
|
|
229
233
|
},
|
|
234
|
+
...i && (t.streamingTranscription ?? process.platform === "darwin") ? { streamingTranscription: {
|
|
235
|
+
start: (e) => n.invoke(d.startSpeech, e),
|
|
236
|
+
append: (e, t) => n.invoke(d.appendSpeech, e, t),
|
|
237
|
+
stop: (e) => n.invoke(d.stopSpeech, e),
|
|
238
|
+
cancel: (e) => n.invoke(d.cancelSpeech, e),
|
|
239
|
+
onEvent: (e) => n.on(f, e)
|
|
240
|
+
} } : {},
|
|
230
241
|
copyToClipboard: (e) => n.invoke(d.copyToClipboard, e),
|
|
231
242
|
ingestAttachments: (e) => n.invoke(d.ingestAttachments, e),
|
|
232
243
|
openExternal: (e) => n.invoke(d.openExternal, e),
|
|
@@ -235,20 +246,20 @@ function f(e, t = {}) {
|
|
|
235
246
|
transcribeAudio: (e, t) => n.invoke(d.transcribeAudio, e, t)
|
|
236
247
|
};
|
|
237
248
|
}
|
|
238
|
-
function
|
|
239
|
-
let r =
|
|
249
|
+
function m(e, t, n) {
|
|
250
|
+
let r = p(t, n);
|
|
240
251
|
return e.exposeInMainWorld("codexAppSdkNative", r), r;
|
|
241
252
|
}
|
|
242
253
|
//#endregion
|
|
243
254
|
//#region src/codex-electron-preload.ts
|
|
244
|
-
function
|
|
245
|
-
let r = u(t), i =
|
|
255
|
+
function h(e, t, n) {
|
|
256
|
+
let r = u(t), i = p(t, n);
|
|
246
257
|
return e.exposeInMainWorld("codexSurface", r), e.exposeInMainWorld("codexAppSdkNative", i), {
|
|
247
258
|
native: i,
|
|
248
259
|
surface: r
|
|
249
260
|
};
|
|
250
261
|
}
|
|
251
262
|
//#endregion
|
|
252
|
-
export {
|
|
263
|
+
export { m as a, i as c, a as d, o as f, p as i, r as l, d as n, u as o, f as r, l as s, h as t, s as u };
|
|
253
264
|
|
|
254
|
-
//# sourceMappingURL=codex-electron-preload-
|
|
265
|
+
//# sourceMappingURL=codex-electron-preload-DupT4aeD.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-electron-preload-z_8XhSEC.js","names":[],"sources":["../../src/typed-ipc.ts","../../src/codex-surface-ipc.ts","../../src/codex-native-renderer.ts","../../src/codex-electron-preload.ts"],"sourcesContent":["import type { TypedEventBus } from '@codex-app-sdk/core/events';\n\nexport type IpcRequest<Arguments extends unknown[] = unknown[], Result = unknown> = {\n args: Arguments;\n result: Result;\n};\n\nexport type IpcRequestArguments<Request> = Request extends IpcRequest<infer Arguments, unknown>\n ? Arguments\n : never;\n\nexport type IpcRequestResult<Request> = Request extends IpcRequest<unknown[], infer Result>\n ? Result\n : never;\n\nexport type IpcRendererPort = {\n invoke(channel: string, ...args: unknown[]): Promise<unknown>;\n on(channel: string, listener: (event: unknown, payload: unknown) => void): void;\n off(channel: string, listener: (event: unknown, payload: unknown) => void): void;\n};\n\nexport type IpcMainPort = {\n handle(channel: string, handler: (event: unknown, ...args: unknown[]) => unknown): void;\n removeHandler(channel: string): void;\n};\n\nexport type IpcEventSender = {\n send(channel: string, payload: unknown): void;\n};\n\ntype RequestName<Requests> = keyof Requests & string;\ntype EventName<Events> = keyof Events & string;\n\nexport class TypedIpcRenderer<Requests extends object, Events extends object> {\n constructor(private readonly port: IpcRendererPort) {}\n\n invoke<Name extends RequestName<Requests>>(\n channel: Name,\n ...args: IpcRequestArguments<Requests[Name]>\n ): Promise<IpcRequestResult<Requests[Name]>> {\n return this.port.invoke(channel, ...args) as Promise<IpcRequestResult<Requests[Name]>>;\n }\n\n on<Name extends EventName<Events>>(channel: Name, listener: (payload: Events[Name]) => void): () => void {\n const handler = (_event: unknown, payload: unknown) => listener(payload as Events[Name]);\n this.port.on(channel, handler);\n return () => this.port.off(channel, handler);\n }\n}\n\nexport type IpcMainHandlers<Requests extends object> = {\n [Name in keyof Requests]: (\n event: unknown,\n ...args: IpcRequestArguments<Requests[Name]>\n ) => IpcRequestResult<Requests[Name]> | Promise<IpcRequestResult<Requests[Name]>>;\n};\n\nexport class TypedIpcMain<Requests extends object> {\n private readonly registeredChannels = new Set<RequestName<Requests>>();\n\n constructor(private readonly port: IpcMainPort) {}\n\n handle<Name extends RequestName<Requests>>(\n channel: Name,\n handler: IpcMainHandlers<Pick<Requests, Name>>[Name],\n ): void {\n this.port.handle(channel, (event, ...args) => handler(\n event,\n ...args as IpcRequestArguments<Requests[Name]>,\n ));\n this.registeredChannels.add(channel);\n }\n\n removeHandler<Name extends RequestName<Requests>>(channel: Name): void {\n this.port.removeHandler(channel);\n this.registeredChannels.delete(channel);\n }\n\n dispose(): void {\n for (const channel of this.registeredChannels) {\n this.port.removeHandler(channel);\n }\n this.registeredChannels.clear();\n }\n}\n\nexport type IpcSenderPolicy = {\n /** Rejects an invocation before its handler runs when this returns false. */\n isTrustedSender?: (event: unknown) => boolean;\n};\n\nexport function registerIpcMainHandlers<Requests extends object>(\n port: IpcMainPort,\n handlers: IpcMainHandlers<Requests>,\n policy: IpcSenderPolicy = {},\n): () => void {\n const main = new TypedIpcMain<Requests>(port);\n const channels = Object.keys(handlers) as Array<RequestName<Requests>>;\n const { isTrustedSender } = policy;\n for (const channel of channels) {\n const handler = handlers[channel];\n main.handle(channel, isTrustedSender\n ? (event, ...args) => {\n if (!isTrustedSender(event)) throw new Error(`Rejected ${channel} from an untrusted sender`);\n return handler(event, ...args);\n }\n : handler);\n }\n return () => main.dispose();\n}\n\nexport function sendIpcEvent<Events extends object, Name extends EventName<Events>>(\n sender: IpcEventSender,\n channel: Name,\n payload: Events[Name],\n): void {\n sender.send(channel, payload);\n}\n\nexport function connectIpcEventsToBus<Events extends object>(\n renderer: Pick<TypedIpcRenderer<object, Events>, 'on'>,\n bus: TypedEventBus<Events>,\n channels: ReadonlyArray<EventName<Events>>,\n): () => void {\n const unsubscribers = channels.map((channel) => renderer.on(channel, (payload) => bus.emit(channel, payload)));\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n}\n","import {\n invokeCodexSurfaceBridgeOperation,\n type CodexSurfaceBridgeOperation,\n type CodexSurfaceBridgeTarget,\n} from '@codex-app-sdk/core/surface-bridge';\nimport type {\n CodexConversationHistory,\n CodexConversationPromptHistory,\n CodexConversationSummary,\n CodexSurfaceApprovalDecision,\n CodexSurfaceApprovalScope,\n CodexSurfaceChatGptDeviceCodeLogin,\n CodexSurfaceChatGptLogin,\n CodexSurfaceClientRequestResponse,\n CodexSurfaceEvent,\n CodexSurfaceModel,\n CodexSurfaceRendererApi,\n CodexSurfaceSnapshot,\n CreateCodexRendererConversationOptions,\n ListCodexConversationsOptions,\n ListCodexModelsOptions,\n CodexRendererSendMessageOptions,\n StartCodexReviewOptions,\n StartCodexLiveChatOptions,\n UpdateCodexConversationSettings,\n CodexSurfaceStatePatch,\n CodexVersionedSurfaceSnapshot,\n} from '@codex-app-sdk/core/surface';\nimport {\n isCodexSurfaceStatePatchSource,\n subscribeCodexSurfaceState,\n type CodexSurfaceStatePatchSource,\n} from '@codex-app-sdk/core/surface-bridge';\nimport {\n registerIpcMainHandlers,\n TypedIpcRenderer,\n type IpcEventSender,\n type IpcMainPort,\n type IpcRendererPort,\n type IpcRequest,\n type IpcSenderPolicy,\n} from './typed-ipc';\n\nexport type CodexSurfaceIpcOptions = IpcSenderPolicy & {\n resolveAttachment?: import('@codex-app-sdk/core/surface-bridge').CodexSurfaceBridgeAttachmentResolver;\n};\n\nconst channels = {\n archiveConversation: 'codex-surface:archive-conversation',\n cancelLogin: 'codex-surface:cancel-login',\n clearGoal: 'codex-surface:clear-goal',\n compactConversation: 'codex-surface:compact-conversation',\n connect: 'codex-surface:connect',\n continueInterruptedTurn: 'codex-surface:continue-interrupted-turn',\n createConversation: 'codex-surface:create-conversation',\n deleteConversation: 'codex-surface:delete-conversation',\n deleteTurn: 'codex-surface:delete-turn',\n deleteQueuedPrompt: 'codex-surface:delete-queued-prompt',\n editTurn: 'codex-surface:edit-turn',\n forkTurn: 'codex-surface:fork-turn',\n getSnapshot: 'codex-surface:get-snapshot',\n getVersionedSnapshot: 'codex-surface:get-versioned-snapshot',\n interrupt: 'codex-surface:interrupt',\n listConversations: 'codex-surface:list-conversations',\n listModels: 'codex-surface:list-models',\n logout: 'codex-surface:logout',\n readConversationHistory: 'codex-surface:read-conversation-history',\n readConversationPromptHistory: 'codex-surface:read-conversation-prompt-history',\n loadOlderConversationHistory: 'codex-surface:load-older-conversation-history',\n refreshAccount: 'codex-surface:refresh-account',\n refreshConversations: 'codex-surface:refresh-conversations',\n renameConversation: 'codex-surface:rename-conversation',\n respondToClientRequest: 'codex-surface:respond-to-client-request',\n resolveApproval: 'codex-surface:resolve-approval',\n retryTurn: 'codex-surface:retry-turn',\n setGoal: 'codex-surface:set-goal',\n selectConversation: 'codex-surface:select-conversation',\n sendMessage: 'codex-surface:send-message',\n startReview: 'codex-surface:start-review',\n startLiveChat: 'codex-surface:start-live-chat',\n stopLiveChat: 'codex-surface:stop-live-chat',\n startChatGptDeviceCodeLogin: 'codex-surface:start-chatgpt-device-code-login',\n startChatGptLogin: 'codex-surface:start-chatgpt-login',\n steerMessage: 'codex-surface:steer-message',\n steerQueuedPrompt: 'codex-surface:steer-queued-prompt',\n unarchiveConversation: 'codex-surface:unarchive-conversation',\n updateConversationSettings: 'codex-surface:update-conversation-settings',\n updateQueuedPrompt: 'codex-surface:update-queued-prompt',\n stateChanged: 'codex-surface:state-changed',\n statePatch: 'codex-surface:state-patch',\n event: 'codex-surface:event',\n} as const;\n\ntype SurfaceRequests = {\n [channels.archiveConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.cancelLogin]: IpcRequest<[loginId?: string], CodexSurfaceSnapshot>;\n [channels.clearGoal]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.compactConversation]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.connect]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.continueInterruptedTurn]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.createConversation]: IpcRequest<[options?: CreateCodexRendererConversationOptions], CodexSurfaceSnapshot>;\n [channels.deleteConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.deleteTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.deleteQueuedPrompt]: IpcRequest<[promptId: string], CodexSurfaceSnapshot>;\n [channels.editTurn]: IpcRequest<[turnId: string, content: string], CodexSurfaceSnapshot>;\n [channels.forkTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.getSnapshot]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.getVersionedSnapshot]: IpcRequest<[], CodexVersionedSurfaceSnapshot | null>;\n [channels.interrupt]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.listConversations]: IpcRequest<[\n options?: ListCodexConversationsOptions,\n ], CodexConversationSummary[]>;\n [channels.listModels]: IpcRequest<[options?: ListCodexModelsOptions], CodexSurfaceModel[]>;\n [channels.logout]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.readConversationHistory]: IpcRequest<[conversationId?: string], CodexConversationHistory>;\n [channels.readConversationPromptHistory]: IpcRequest<[\n conversationId?: string,\n ], CodexConversationPromptHistory>;\n [channels.loadOlderConversationHistory]: IpcRequest<[conversationId?: string], import('@codex-app-sdk/core/surface').CodexConversationHistoryPage>;\n [channels.refreshAccount]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.refreshConversations]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.renameConversation]: IpcRequest<[title: string], CodexSurfaceSnapshot>;\n [channels.respondToClientRequest]: IpcRequest<[response: CodexSurfaceClientRequestResponse], CodexSurfaceSnapshot>;\n [channels.resolveApproval]: IpcRequest<[\n approvalId: string,\n decision: CodexSurfaceApprovalDecision,\n scope?: CodexSurfaceApprovalScope,\n ], CodexSurfaceSnapshot>;\n [channels.retryTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.setGoal]: IpcRequest<[objective: string, tokenBudget?: number | null], CodexSurfaceSnapshot>;\n [channels.selectConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.sendMessage]: IpcRequest<[prompt: string, options?: CodexRendererSendMessageOptions], CodexSurfaceSnapshot>;\n [channels.startReview]: IpcRequest<[options?: StartCodexReviewOptions], CodexSurfaceSnapshot>;\n [channels.startLiveChat]: IpcRequest<[conversationId: string, options: StartCodexLiveChatOptions], { sdp: string }>;\n [channels.stopLiveChat]: IpcRequest<[conversationId: string], void>;\n [channels.startChatGptDeviceCodeLogin]: IpcRequest<[], CodexSurfaceChatGptDeviceCodeLogin>;\n [channels.startChatGptLogin]: IpcRequest<[], CodexSurfaceChatGptLogin>;\n [channels.steerMessage]: IpcRequest<[\n prompt: string,\n options?: CodexRendererSendMessageOptions,\n ], CodexSurfaceSnapshot>;\n [channels.steerQueuedPrompt]: IpcRequest<[promptId: string, prompt?: string], CodexSurfaceSnapshot>;\n [channels.unarchiveConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.updateConversationSettings]: IpcRequest<[settings: UpdateCodexConversationSettings], CodexSurfaceSnapshot>;\n [channels.updateQueuedPrompt]: IpcRequest<[promptId: string, prompt: string], CodexSurfaceSnapshot>;\n};\n\ntype SurfaceEvents = {\n [channels.stateChanged]: CodexSurfaceSnapshot;\n [channels.statePatch]: CodexSurfaceStatePatch;\n [channels.event]: CodexSurfaceEvent;\n};\n\nexport type { CodexSurfaceRendererApi } from '@codex-app-sdk/core/surface';\n\ntype OptionalSurfaceOperation = 'loadOlderConversationHistory' | 'startLiveChat' | 'stopLiveChat';\n\nexport function registerCodexSurfaceIpc(\n port: IpcMainPort,\n sender: IpcEventSender,\n surface: Omit<CodexSurfaceBridgeTarget, OptionalSurfaceOperation>\n & Partial<Pick<CodexSurfaceBridgeTarget, OptionalSurfaceOperation>>\n & Partial<CodexSurfaceStatePatchSource>,\n options: CodexSurfaceIpcOptions = {},\n): () => void {\n // State crosses IPC as full snapshots until a renderer asks for a versioned\n // base; from then on it streams small patches. Older renderers never ask, and\n // surfaces without native patch support decline, so both keep snapshots.\n const patchSource = isCodexSurfaceStatePatchSource(surface) ? surface : null;\n let streamingPatches = false;\n const invoke = <Name extends CodexSurfaceBridgeOperation>(name: Name, args: readonly unknown[]) => {\n if (name === 'loadOlderConversationHistory' && !surface.loadOlderConversationHistory) {\n return Promise.reject(new Error('Conversation history paging is not available.'));\n }\n if ((name === 'startLiveChat' || name === 'stopLiveChat') && !surface[name]) {\n return Promise.reject(new Error('Live chat is not available.'));\n }\n return invokeCodexSurfaceBridgeOperation(\n surface as CodexSurfaceBridgeTarget,\n name,\n args,\n {\n operationLabel: channels[name],\n ...(options.resolveAttachment === undefined\n ? {}\n : { resolveAttachment: options.resolveAttachment }),\n },\n );\n };\n const unregisterHandlers = registerIpcMainHandlers<SurfaceRequests>(port, {\n [channels.archiveConversation]: (_event, ...args) => invoke('archiveConversation', args),\n [channels.cancelLogin]: (_event, ...args) => invoke('cancelLogin', args),\n [channels.clearGoal]: (_event, ...args) => invoke('clearGoal', args),\n [channels.compactConversation]: (_event, ...args) => invoke('compactConversation', args),\n [channels.connect]: (_event, ...args) => invoke('connect', args),\n [channels.continueInterruptedTurn]: (_event, ...args) => invoke('continueInterruptedTurn', args),\n [channels.createConversation]: (_event, ...args) => invoke('createConversation', args),\n [channels.deleteConversation]: (_event, ...args) => invoke('deleteConversation', args),\n [channels.deleteTurn]: (_event, ...args) => invoke('deleteTurn', args),\n [channels.deleteQueuedPrompt]: (_event, ...args) => invoke('deleteQueuedPrompt', args),\n [channels.editTurn]: (_event, ...args) => invoke('editTurn', args),\n [channels.forkTurn]: (_event, ...args) => invoke('forkTurn', args),\n [channels.getSnapshot]: (_event, ...args) => invoke('getSnapshot', args),\n [channels.getVersionedSnapshot]: async () => {\n if (!patchSource) return null;\n streamingPatches = true;\n return patchSource.getVersionedSnapshot();\n },\n [channels.interrupt]: (_event, ...args) => invoke('interrupt', args),\n [channels.listConversations]: (_event, ...args) => invoke('listConversations', args),\n [channels.listModels]: (_event, ...args) => invoke('listModels', args),\n [channels.logout]: (_event, ...args) => invoke('logout', args),\n [channels.readConversationHistory]: (_event, ...args) => invoke('readConversationHistory', args),\n [channels.readConversationPromptHistory]: (_event, ...args) => invoke('readConversationPromptHistory', args),\n [channels.loadOlderConversationHistory]: (_event, ...args) => invoke('loadOlderConversationHistory', args),\n [channels.refreshAccount]: (_event, ...args) => invoke('refreshAccount', args),\n [channels.refreshConversations]: (_event, ...args) => invoke('refreshConversations', args),\n [channels.renameConversation]: (_event, ...args) => invoke('renameConversation', args),\n [channels.respondToClientRequest]: (_event, ...args) => invoke('respondToClientRequest', args),\n [channels.resolveApproval]: (_event, ...args) => invoke('resolveApproval', args),\n [channels.retryTurn]: (_event, ...args) => invoke('retryTurn', args),\n [channels.setGoal]: (_event, ...args) => invoke('setGoal', args),\n [channels.selectConversation]: (_event, ...args) => invoke('selectConversation', args),\n [channels.sendMessage]: (_event, ...args) => invoke('sendMessage', args),\n [channels.startReview]: (_event, ...args) => invoke('startReview', args),\n [channels.startLiveChat]: (_event, ...args) => invoke('startLiveChat', args),\n [channels.stopLiveChat]: (_event, ...args) => invoke('stopLiveChat', args),\n [channels.startChatGptDeviceCodeLogin]: (_event, ...args) => invoke('startChatGptDeviceCodeLogin', args),\n [channels.startChatGptLogin]: (_event, ...args) => invoke('startChatGptLogin', args),\n [channels.steerMessage]: (_event, ...args) => invoke('steerMessage', args),\n [channels.steerQueuedPrompt]: (_event, ...args) => invoke('steerQueuedPrompt', args),\n [channels.unarchiveConversation]: (_event, ...args) => invoke('unarchiveConversation', args),\n [channels.updateConversationSettings]: (_event, ...args) => invoke('updateConversationSettings', args),\n [channels.updateQueuedPrompt]: (_event, ...args) => invoke('updateQueuedPrompt', args),\n }, options);\n const unsubscribeSnapshots = surface.onStateChange((snapshot) => {\n if (!streamingPatches) sender.send(channels.stateChanged, snapshot);\n });\n const unsubscribePatches = patchSource?.onStatePatch((patch) => {\n if (streamingPatches) sender.send(channels.statePatch, patch);\n });\n const unsubscribeState = () => {\n unsubscribePatches?.();\n unsubscribeSnapshots();\n };\n const unsubscribeEvents = surface.onEvent((event) => sender.send(channels.event, event));\n return () => {\n unsubscribeEvents();\n unsubscribeState();\n unregisterHandlers();\n };\n}\n\nexport function createCodexSurfaceRendererApi(port: IpcRendererPort): CodexSurfaceRendererApi {\n const renderer = new TypedIpcRenderer<SurfaceRequests, SurfaceEvents>(port);\n const getVersionedSnapshot = () => renderer.invoke(channels.getVersionedSnapshot);\n const onStatePatch = (listener: (patch: CodexSurfaceStatePatch) => void) => renderer.on(channels.statePatch, listener);\n const stateSource = { getVersionedSnapshot, onStatePatch };\n return {\n // Mirror patches in the renderer's own world: contextBridge copies every\n // value it passes, so a mirror kept in the preload would lose identity.\n getVersionedSnapshot,\n onStatePatch,\n archiveConversation: (conversationId) => renderer.invoke(channels.archiveConversation, conversationId),\n cancelLogin: (loginId) => renderer.invoke(channels.cancelLogin, loginId),\n clearGoal: () => renderer.invoke(channels.clearGoal),\n compactConversation: () => renderer.invoke(channels.compactConversation),\n connect: () => renderer.invoke(channels.connect),\n continueInterruptedTurn: () => renderer.invoke(channels.continueInterruptedTurn),\n createConversation: (options) => renderer.invoke(channels.createConversation, options),\n deleteConversation: (conversationId) => renderer.invoke(channels.deleteConversation, conversationId),\n deleteTurn: (turnId) => renderer.invoke(channels.deleteTurn, turnId),\n deleteQueuedPrompt: (promptId) => renderer.invoke(channels.deleteQueuedPrompt, promptId),\n editTurn: (turnId, content) => renderer.invoke(channels.editTurn, turnId, content),\n forkTurn: (turnId) => renderer.invoke(channels.forkTurn, turnId),\n getSnapshot: () => renderer.invoke(channels.getSnapshot),\n interrupt: () => renderer.invoke(channels.interrupt),\n listConversations: (options) => renderer.invoke(channels.listConversations, options),\n listModels: (options) => renderer.invoke(channels.listModels, options),\n logout: () => renderer.invoke(channels.logout),\n onEvent: (listener) => renderer.on(channels.event, listener),\n onStateChange: (listener) => {\n // Snapshots arrive until the main process switches to patches; the\n // switch point's base was already delivered as a snapshot, so it stays silent.\n const unsubscribeSnapshots = renderer.on(channels.stateChanged, listener);\n const unsubscribePatches = subscribeCodexSurfaceState(stateSource, listener, { emitInitialSnapshot: false });\n return () => {\n unsubscribePatches();\n unsubscribeSnapshots();\n };\n },\n readConversationHistory: (conversationId) => renderer.invoke(channels.readConversationHistory, conversationId),\n readConversationPromptHistory: (conversationId) => (\n renderer.invoke(channels.readConversationPromptHistory, conversationId)\n ),\n loadOlderConversationHistory: (conversationId) => renderer.invoke(channels.loadOlderConversationHistory, conversationId),\n refreshAccount: () => renderer.invoke(channels.refreshAccount),\n refreshConversations: () => renderer.invoke(channels.refreshConversations),\n renameConversation: (title) => renderer.invoke(channels.renameConversation, title),\n respondToClientRequest: (response) => renderer.invoke(channels.respondToClientRequest, response),\n resolveApproval: (approvalId, decision, scope) => renderer.invoke(channels.resolveApproval, approvalId, decision, scope),\n retryTurn: (turnId) => renderer.invoke(channels.retryTurn, turnId),\n setGoal: (objective, tokenBudget) => renderer.invoke(channels.setGoal, objective, tokenBudget),\n selectConversation: (conversationId) => renderer.invoke(channels.selectConversation, conversationId),\n sendMessage: (prompt, options) => renderer.invoke(channels.sendMessage, prompt, options),\n startReview: (options) => renderer.invoke(channels.startReview, options),\n startLiveChat: (conversationId, options) => renderer.invoke(channels.startLiveChat, conversationId, options),\n stopLiveChat: (conversationId) => renderer.invoke(channels.stopLiveChat, conversationId),\n startChatGptDeviceCodeLogin: () => renderer.invoke(channels.startChatGptDeviceCodeLogin),\n startChatGptLogin: () => renderer.invoke(channels.startChatGptLogin),\n steerMessage: (prompt, options) => renderer.invoke(channels.steerMessage, prompt, options),\n steerQueuedPrompt: (promptId, prompt) => renderer.invoke(channels.steerQueuedPrompt, promptId, prompt),\n unarchiveConversation: (conversationId) => renderer.invoke(channels.unarchiveConversation, conversationId),\n updateConversationSettings: (settings) => renderer.invoke(channels.updateConversationSettings, settings),\n updateQueuedPrompt: (promptId, prompt) => renderer.invoke(channels.updateQueuedPrompt, promptId, prompt),\n };\n}\n","import type {\n CodexNativeAttachment,\n CodexNativeAttachmentInput,\n CodexNativeClipboardContent,\n CodexNativeRendererApi,\n CodexSpeechTranscriptionResult,\n} from '@codex-app-sdk/core/native';\nimport {\n TypedIpcRenderer,\n type IpcRendererPort,\n type IpcRequest,\n} from './typed-ipc';\n\nexport const codexNativeChannels = {\n copyToClipboard: 'codex-native:copy-to-clipboard',\n ingestAttachments: 'codex-native:ingest-attachments',\n openExternal: 'codex-native:open-external',\n pickAttachments: 'codex-native:pick-attachments',\n readImagePreview: 'codex-native:read-image-preview',\n transcribeAudio: 'codex-native:transcribe-audio',\n} as const;\n\nexport type CodexNativeRequests = {\n [codexNativeChannels.copyToClipboard]: IpcRequest<[content: CodexNativeClipboardContent], void>;\n [codexNativeChannels.ingestAttachments]: IpcRequest<[\n files: readonly CodexNativeAttachmentInput[],\n ], CodexNativeAttachment[]>;\n [codexNativeChannels.openExternal]: IpcRequest<[href: string], void>;\n [codexNativeChannels.pickAttachments]: IpcRequest<[], CodexNativeAttachment[]>;\n [codexNativeChannels.readImagePreview]: IpcRequest<[path: string], string | null>;\n [codexNativeChannels.transcribeAudio]: IpcRequest<[\n audioData: ArrayBuffer,\n options?: { locale?: string; live?: boolean },\n ], CodexSpeechTranscriptionResult>;\n};\n\nexport type CodexContextBridge = {\n exposeInMainWorld(key: string, api: unknown): void;\n};\n\nexport function createCodexNativeRendererApi(\n port: IpcRendererPort,\n options: { transcription?: boolean } = {},\n): CodexNativeRendererApi {\n const renderer = new TypedIpcRenderer<CodexNativeRequests, object>(port);\n return {\n capabilities: {\n attachments: true,\n clipboard: true,\n externalLinks: true,\n transcription: options.transcription ?? process.platform === 'darwin',\n },\n copyToClipboard: (content) => renderer.invoke(codexNativeChannels.copyToClipboard, content),\n ingestAttachments: (files) => renderer.invoke(codexNativeChannels.ingestAttachments, files),\n openExternal: (href) => renderer.invoke(codexNativeChannels.openExternal, href),\n pickAttachments: () => renderer.invoke(codexNativeChannels.pickAttachments),\n readImagePreview: (path) => renderer.invoke(codexNativeChannels.readImagePreview, path),\n transcribeAudio: (audioData, transcriptionOptions) => (\n renderer.invoke(codexNativeChannels.transcribeAudio, audioData, transcriptionOptions)\n ),\n };\n}\n\nexport function exposeCodexNativeRendererApi(\n contextBridge: CodexContextBridge,\n port: IpcRendererPort,\n options?: { transcription?: boolean },\n): CodexNativeRendererApi {\n const api = createCodexNativeRendererApi(port, options);\n contextBridge.exposeInMainWorld('codexAppSdkNative', api);\n return api;\n}\n","import type { CodexNativeRendererApi } from '@codex-app-sdk/core/native';\nimport type { CodexSurfaceRendererApi } from '@codex-app-sdk/core/surface';\nimport {\n createCodexNativeRendererApi,\n type CodexContextBridge,\n} from './codex-native-renderer';\nimport { createCodexSurfaceRendererApi } from './codex-surface-ipc';\nimport type { IpcRendererPort } from './typed-ipc';\n\nexport type CodexElectronRendererApis = {\n native: CodexNativeRendererApi;\n surface: CodexSurfaceRendererApi;\n};\n\n/** Exposes the SDK-owned renderer APIs under stable, automatically detected names. */\nexport function exposeCodexElectronPreload(\n contextBridge: CodexContextBridge,\n ipcRenderer: IpcRendererPort,\n options?: { transcription?: boolean },\n): CodexElectronRendererApis {\n const surface = createCodexSurfaceRendererApi(ipcRenderer);\n const native = createCodexNativeRendererApi(ipcRenderer, options);\n contextBridge.exposeInMainWorld('codexSurface', surface);\n contextBridge.exposeInMainWorld('codexAppSdkNative', native);\n return { native, surface };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"codex-electron-preload-DupT4aeD.js","names":[],"sources":["../../src/typed-ipc.ts","../../src/codex-surface-ipc.ts","../../src/codex-native-renderer.ts","../../src/codex-electron-preload.ts"],"sourcesContent":["import type { TypedEventBus } from '@codex-app-sdk/core/events';\n\nexport type IpcRequest<Arguments extends unknown[] = unknown[], Result = unknown> = {\n args: Arguments;\n result: Result;\n};\n\nexport type IpcRequestArguments<Request> = Request extends IpcRequest<infer Arguments, unknown>\n ? Arguments\n : never;\n\nexport type IpcRequestResult<Request> = Request extends IpcRequest<unknown[], infer Result>\n ? Result\n : never;\n\nexport type IpcRendererPort = {\n invoke(channel: string, ...args: unknown[]): Promise<unknown>;\n on(channel: string, listener: (event: unknown, payload: unknown) => void): void;\n off(channel: string, listener: (event: unknown, payload: unknown) => void): void;\n};\n\nexport type IpcMainPort = {\n handle(channel: string, handler: (event: unknown, ...args: unknown[]) => unknown): void;\n removeHandler(channel: string): void;\n};\n\nexport type IpcEventSender = {\n send(channel: string, payload: unknown): void;\n};\n\ntype RequestName<Requests> = keyof Requests & string;\ntype EventName<Events> = keyof Events & string;\n\nexport class TypedIpcRenderer<Requests extends object, Events extends object> {\n constructor(private readonly port: IpcRendererPort) {}\n\n invoke<Name extends RequestName<Requests>>(\n channel: Name,\n ...args: IpcRequestArguments<Requests[Name]>\n ): Promise<IpcRequestResult<Requests[Name]>> {\n return this.port.invoke(channel, ...args) as Promise<IpcRequestResult<Requests[Name]>>;\n }\n\n on<Name extends EventName<Events>>(channel: Name, listener: (payload: Events[Name]) => void): () => void {\n const handler = (_event: unknown, payload: unknown) => listener(payload as Events[Name]);\n this.port.on(channel, handler);\n return () => this.port.off(channel, handler);\n }\n}\n\nexport type IpcMainHandlers<Requests extends object> = {\n [Name in keyof Requests]: (\n event: unknown,\n ...args: IpcRequestArguments<Requests[Name]>\n ) => IpcRequestResult<Requests[Name]> | Promise<IpcRequestResult<Requests[Name]>>;\n};\n\nexport class TypedIpcMain<Requests extends object> {\n private readonly registeredChannels = new Set<RequestName<Requests>>();\n\n constructor(private readonly port: IpcMainPort) {}\n\n handle<Name extends RequestName<Requests>>(\n channel: Name,\n handler: IpcMainHandlers<Pick<Requests, Name>>[Name],\n ): void {\n this.port.handle(channel, (event, ...args) => handler(\n event,\n ...args as IpcRequestArguments<Requests[Name]>,\n ));\n this.registeredChannels.add(channel);\n }\n\n removeHandler<Name extends RequestName<Requests>>(channel: Name): void {\n this.port.removeHandler(channel);\n this.registeredChannels.delete(channel);\n }\n\n dispose(): void {\n for (const channel of this.registeredChannels) {\n this.port.removeHandler(channel);\n }\n this.registeredChannels.clear();\n }\n}\n\nexport type IpcSenderPolicy = {\n /** Rejects an invocation before its handler runs when this returns false. */\n isTrustedSender?: (event: unknown) => boolean;\n};\n\nexport function registerIpcMainHandlers<Requests extends object>(\n port: IpcMainPort,\n handlers: IpcMainHandlers<Requests>,\n policy: IpcSenderPolicy = {},\n): () => void {\n const main = new TypedIpcMain<Requests>(port);\n const channels = Object.keys(handlers) as Array<RequestName<Requests>>;\n const { isTrustedSender } = policy;\n for (const channel of channels) {\n const handler = handlers[channel];\n main.handle(channel, isTrustedSender\n ? (event, ...args) => {\n if (!isTrustedSender(event)) throw new Error(`Rejected ${channel} from an untrusted sender`);\n return handler(event, ...args);\n }\n : handler);\n }\n return () => main.dispose();\n}\n\nexport function sendIpcEvent<Events extends object, Name extends EventName<Events>>(\n sender: IpcEventSender,\n channel: Name,\n payload: Events[Name],\n): void {\n sender.send(channel, payload);\n}\n\nexport function connectIpcEventsToBus<Events extends object>(\n renderer: Pick<TypedIpcRenderer<object, Events>, 'on'>,\n bus: TypedEventBus<Events>,\n channels: ReadonlyArray<EventName<Events>>,\n): () => void {\n const unsubscribers = channels.map((channel) => renderer.on(channel, (payload) => bus.emit(channel, payload)));\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n}\n","import {\n invokeCodexSurfaceBridgeOperation,\n type CodexSurfaceBridgeOperation,\n type CodexSurfaceBridgeTarget,\n} from '@codex-app-sdk/core/surface-bridge';\nimport type {\n CodexConversationHistory,\n CodexConversationPromptHistory,\n CodexConversationSummary,\n CodexSurfaceApprovalDecision,\n CodexSurfaceApprovalScope,\n CodexSurfaceChatGptDeviceCodeLogin,\n CodexSurfaceChatGptLogin,\n CodexSurfaceClientRequestResponse,\n CodexSurfaceEvent,\n CodexSurfaceModel,\n CodexSurfaceRendererApi,\n CodexSurfaceSnapshot,\n CreateCodexRendererConversationOptions,\n ListCodexConversationsOptions,\n ListCodexModelsOptions,\n CodexRendererSendMessageOptions,\n StartCodexReviewOptions,\n StartCodexLiveChatOptions,\n UpdateCodexConversationSettings,\n CodexSurfaceStatePatch,\n CodexVersionedSurfaceSnapshot,\n} from '@codex-app-sdk/core/surface';\nimport {\n isCodexSurfaceStatePatchSource,\n subscribeCodexSurfaceState,\n type CodexSurfaceStatePatchSource,\n} from '@codex-app-sdk/core/surface-bridge';\nimport {\n registerIpcMainHandlers,\n TypedIpcRenderer,\n type IpcEventSender,\n type IpcMainPort,\n type IpcRendererPort,\n type IpcRequest,\n type IpcSenderPolicy,\n} from './typed-ipc';\n\nexport type CodexSurfaceIpcOptions = IpcSenderPolicy & {\n resolveAttachment?: import('@codex-app-sdk/core/surface-bridge').CodexSurfaceBridgeAttachmentResolver;\n};\n\nconst channels = {\n archiveConversation: 'codex-surface:archive-conversation',\n cancelLogin: 'codex-surface:cancel-login',\n clearGoal: 'codex-surface:clear-goal',\n compactConversation: 'codex-surface:compact-conversation',\n connect: 'codex-surface:connect',\n continueInterruptedTurn: 'codex-surface:continue-interrupted-turn',\n createConversation: 'codex-surface:create-conversation',\n deleteConversation: 'codex-surface:delete-conversation',\n deleteTurn: 'codex-surface:delete-turn',\n deleteQueuedPrompt: 'codex-surface:delete-queued-prompt',\n editTurn: 'codex-surface:edit-turn',\n forkTurn: 'codex-surface:fork-turn',\n getSnapshot: 'codex-surface:get-snapshot',\n getVersionedSnapshot: 'codex-surface:get-versioned-snapshot',\n interrupt: 'codex-surface:interrupt',\n listConversations: 'codex-surface:list-conversations',\n listModels: 'codex-surface:list-models',\n logout: 'codex-surface:logout',\n readConversationHistory: 'codex-surface:read-conversation-history',\n readConversationPromptHistory: 'codex-surface:read-conversation-prompt-history',\n loadOlderConversationHistory: 'codex-surface:load-older-conversation-history',\n refreshAccount: 'codex-surface:refresh-account',\n refreshConversations: 'codex-surface:refresh-conversations',\n renameConversation: 'codex-surface:rename-conversation',\n respondToClientRequest: 'codex-surface:respond-to-client-request',\n resolveApproval: 'codex-surface:resolve-approval',\n retryTurn: 'codex-surface:retry-turn',\n setGoal: 'codex-surface:set-goal',\n selectConversation: 'codex-surface:select-conversation',\n sendMessage: 'codex-surface:send-message',\n startReview: 'codex-surface:start-review',\n startLiveChat: 'codex-surface:start-live-chat',\n stopLiveChat: 'codex-surface:stop-live-chat',\n startChatGptDeviceCodeLogin: 'codex-surface:start-chatgpt-device-code-login',\n startChatGptLogin: 'codex-surface:start-chatgpt-login',\n steerMessage: 'codex-surface:steer-message',\n steerQueuedPrompt: 'codex-surface:steer-queued-prompt',\n unarchiveConversation: 'codex-surface:unarchive-conversation',\n updateConversationSettings: 'codex-surface:update-conversation-settings',\n updateQueuedPrompt: 'codex-surface:update-queued-prompt',\n stateChanged: 'codex-surface:state-changed',\n statePatch: 'codex-surface:state-patch',\n event: 'codex-surface:event',\n} as const;\n\ntype SurfaceRequests = {\n [channels.archiveConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.cancelLogin]: IpcRequest<[loginId?: string], CodexSurfaceSnapshot>;\n [channels.clearGoal]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.compactConversation]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.connect]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.continueInterruptedTurn]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.createConversation]: IpcRequest<[options?: CreateCodexRendererConversationOptions], CodexSurfaceSnapshot>;\n [channels.deleteConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.deleteTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.deleteQueuedPrompt]: IpcRequest<[promptId: string], CodexSurfaceSnapshot>;\n [channels.editTurn]: IpcRequest<[turnId: string, content: string], CodexSurfaceSnapshot>;\n [channels.forkTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.getSnapshot]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.getVersionedSnapshot]: IpcRequest<[], CodexVersionedSurfaceSnapshot | null>;\n [channels.interrupt]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.listConversations]: IpcRequest<[\n options?: ListCodexConversationsOptions,\n ], CodexConversationSummary[]>;\n [channels.listModels]: IpcRequest<[options?: ListCodexModelsOptions], CodexSurfaceModel[]>;\n [channels.logout]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.readConversationHistory]: IpcRequest<[conversationId?: string], CodexConversationHistory>;\n [channels.readConversationPromptHistory]: IpcRequest<[\n conversationId?: string,\n ], CodexConversationPromptHistory>;\n [channels.loadOlderConversationHistory]: IpcRequest<[conversationId?: string], import('@codex-app-sdk/core/surface').CodexConversationHistoryPage>;\n [channels.refreshAccount]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.refreshConversations]: IpcRequest<[], CodexSurfaceSnapshot>;\n [channels.renameConversation]: IpcRequest<[title: string], CodexSurfaceSnapshot>;\n [channels.respondToClientRequest]: IpcRequest<[response: CodexSurfaceClientRequestResponse], CodexSurfaceSnapshot>;\n [channels.resolveApproval]: IpcRequest<[\n approvalId: string,\n decision: CodexSurfaceApprovalDecision,\n scope?: CodexSurfaceApprovalScope,\n ], CodexSurfaceSnapshot>;\n [channels.retryTurn]: IpcRequest<[turnId: string], CodexSurfaceSnapshot>;\n [channels.setGoal]: IpcRequest<[objective: string, tokenBudget?: number | null], CodexSurfaceSnapshot>;\n [channels.selectConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.sendMessage]: IpcRequest<[prompt: string, options?: CodexRendererSendMessageOptions], CodexSurfaceSnapshot>;\n [channels.startReview]: IpcRequest<[options?: StartCodexReviewOptions], CodexSurfaceSnapshot>;\n [channels.startLiveChat]: IpcRequest<[conversationId: string, options: StartCodexLiveChatOptions], { sdp: string }>;\n [channels.stopLiveChat]: IpcRequest<[conversationId: string], void>;\n [channels.startChatGptDeviceCodeLogin]: IpcRequest<[], CodexSurfaceChatGptDeviceCodeLogin>;\n [channels.startChatGptLogin]: IpcRequest<[], CodexSurfaceChatGptLogin>;\n [channels.steerMessage]: IpcRequest<[\n prompt: string,\n options?: CodexRendererSendMessageOptions,\n ], CodexSurfaceSnapshot>;\n [channels.steerQueuedPrompt]: IpcRequest<[promptId: string, prompt?: string], CodexSurfaceSnapshot>;\n [channels.unarchiveConversation]: IpcRequest<[conversationId: string], CodexSurfaceSnapshot>;\n [channels.updateConversationSettings]: IpcRequest<[settings: UpdateCodexConversationSettings], CodexSurfaceSnapshot>;\n [channels.updateQueuedPrompt]: IpcRequest<[promptId: string, prompt: string], CodexSurfaceSnapshot>;\n};\n\ntype SurfaceEvents = {\n [channels.stateChanged]: CodexSurfaceSnapshot;\n [channels.statePatch]: CodexSurfaceStatePatch;\n [channels.event]: CodexSurfaceEvent;\n};\n\nexport type { CodexSurfaceRendererApi } from '@codex-app-sdk/core/surface';\n\ntype OptionalSurfaceOperation = 'loadOlderConversationHistory' | 'startLiveChat' | 'stopLiveChat';\n\nexport function registerCodexSurfaceIpc(\n port: IpcMainPort,\n sender: IpcEventSender,\n surface: Omit<CodexSurfaceBridgeTarget, OptionalSurfaceOperation>\n & Partial<Pick<CodexSurfaceBridgeTarget, OptionalSurfaceOperation>>\n & Partial<CodexSurfaceStatePatchSource>,\n options: CodexSurfaceIpcOptions = {},\n): () => void {\n // State crosses IPC as full snapshots until a renderer asks for a versioned\n // base; from then on it streams small patches. Older renderers never ask, and\n // surfaces without native patch support decline, so both keep snapshots.\n const patchSource = isCodexSurfaceStatePatchSource(surface) ? surface : null;\n let streamingPatches = false;\n const invoke = <Name extends CodexSurfaceBridgeOperation>(name: Name, args: readonly unknown[]) => {\n if (name === 'loadOlderConversationHistory' && !surface.loadOlderConversationHistory) {\n return Promise.reject(new Error('Conversation history paging is not available.'));\n }\n if ((name === 'startLiveChat' || name === 'stopLiveChat') && !surface[name]) {\n return Promise.reject(new Error('Live chat is not available.'));\n }\n return invokeCodexSurfaceBridgeOperation(\n surface as CodexSurfaceBridgeTarget,\n name,\n args,\n {\n operationLabel: channels[name],\n ...(options.resolveAttachment === undefined\n ? {}\n : { resolveAttachment: options.resolveAttachment }),\n },\n );\n };\n const unregisterHandlers = registerIpcMainHandlers<SurfaceRequests>(port, {\n [channels.archiveConversation]: (_event, ...args) => invoke('archiveConversation', args),\n [channels.cancelLogin]: (_event, ...args) => invoke('cancelLogin', args),\n [channels.clearGoal]: (_event, ...args) => invoke('clearGoal', args),\n [channels.compactConversation]: (_event, ...args) => invoke('compactConversation', args),\n [channels.connect]: (_event, ...args) => invoke('connect', args),\n [channels.continueInterruptedTurn]: (_event, ...args) => invoke('continueInterruptedTurn', args),\n [channels.createConversation]: (_event, ...args) => invoke('createConversation', args),\n [channels.deleteConversation]: (_event, ...args) => invoke('deleteConversation', args),\n [channels.deleteTurn]: (_event, ...args) => invoke('deleteTurn', args),\n [channels.deleteQueuedPrompt]: (_event, ...args) => invoke('deleteQueuedPrompt', args),\n [channels.editTurn]: (_event, ...args) => invoke('editTurn', args),\n [channels.forkTurn]: (_event, ...args) => invoke('forkTurn', args),\n [channels.getSnapshot]: (_event, ...args) => invoke('getSnapshot', args),\n [channels.getVersionedSnapshot]: async () => {\n if (!patchSource) return null;\n streamingPatches = true;\n return patchSource.getVersionedSnapshot();\n },\n [channels.interrupt]: (_event, ...args) => invoke('interrupt', args),\n [channels.listConversations]: (_event, ...args) => invoke('listConversations', args),\n [channels.listModels]: (_event, ...args) => invoke('listModels', args),\n [channels.logout]: (_event, ...args) => invoke('logout', args),\n [channels.readConversationHistory]: (_event, ...args) => invoke('readConversationHistory', args),\n [channels.readConversationPromptHistory]: (_event, ...args) => invoke('readConversationPromptHistory', args),\n [channels.loadOlderConversationHistory]: (_event, ...args) => invoke('loadOlderConversationHistory', args),\n [channels.refreshAccount]: (_event, ...args) => invoke('refreshAccount', args),\n [channels.refreshConversations]: (_event, ...args) => invoke('refreshConversations', args),\n [channels.renameConversation]: (_event, ...args) => invoke('renameConversation', args),\n [channels.respondToClientRequest]: (_event, ...args) => invoke('respondToClientRequest', args),\n [channels.resolveApproval]: (_event, ...args) => invoke('resolveApproval', args),\n [channels.retryTurn]: (_event, ...args) => invoke('retryTurn', args),\n [channels.setGoal]: (_event, ...args) => invoke('setGoal', args),\n [channels.selectConversation]: (_event, ...args) => invoke('selectConversation', args),\n [channels.sendMessage]: (_event, ...args) => invoke('sendMessage', args),\n [channels.startReview]: (_event, ...args) => invoke('startReview', args),\n [channels.startLiveChat]: (_event, ...args) => invoke('startLiveChat', args),\n [channels.stopLiveChat]: (_event, ...args) => invoke('stopLiveChat', args),\n [channels.startChatGptDeviceCodeLogin]: (_event, ...args) => invoke('startChatGptDeviceCodeLogin', args),\n [channels.startChatGptLogin]: (_event, ...args) => invoke('startChatGptLogin', args),\n [channels.steerMessage]: (_event, ...args) => invoke('steerMessage', args),\n [channels.steerQueuedPrompt]: (_event, ...args) => invoke('steerQueuedPrompt', args),\n [channels.unarchiveConversation]: (_event, ...args) => invoke('unarchiveConversation', args),\n [channels.updateConversationSettings]: (_event, ...args) => invoke('updateConversationSettings', args),\n [channels.updateQueuedPrompt]: (_event, ...args) => invoke('updateQueuedPrompt', args),\n }, options);\n const unsubscribeSnapshots = surface.onStateChange((snapshot) => {\n if (!streamingPatches) sender.send(channels.stateChanged, snapshot);\n });\n const unsubscribePatches = patchSource?.onStatePatch((patch) => {\n if (streamingPatches) sender.send(channels.statePatch, patch);\n });\n const unsubscribeState = () => {\n unsubscribePatches?.();\n unsubscribeSnapshots();\n };\n const unsubscribeEvents = surface.onEvent((event) => sender.send(channels.event, event));\n return () => {\n unsubscribeEvents();\n unsubscribeState();\n unregisterHandlers();\n };\n}\n\nexport function createCodexSurfaceRendererApi(port: IpcRendererPort): CodexSurfaceRendererApi {\n const renderer = new TypedIpcRenderer<SurfaceRequests, SurfaceEvents>(port);\n const getVersionedSnapshot = () => renderer.invoke(channels.getVersionedSnapshot);\n const onStatePatch = (listener: (patch: CodexSurfaceStatePatch) => void) => renderer.on(channels.statePatch, listener);\n const stateSource = { getVersionedSnapshot, onStatePatch };\n return {\n // Mirror patches in the renderer's own world: contextBridge copies every\n // value it passes, so a mirror kept in the preload would lose identity.\n getVersionedSnapshot,\n onStatePatch,\n archiveConversation: (conversationId) => renderer.invoke(channels.archiveConversation, conversationId),\n cancelLogin: (loginId) => renderer.invoke(channels.cancelLogin, loginId),\n clearGoal: () => renderer.invoke(channels.clearGoal),\n compactConversation: () => renderer.invoke(channels.compactConversation),\n connect: () => renderer.invoke(channels.connect),\n continueInterruptedTurn: () => renderer.invoke(channels.continueInterruptedTurn),\n createConversation: (options) => renderer.invoke(channels.createConversation, options),\n deleteConversation: (conversationId) => renderer.invoke(channels.deleteConversation, conversationId),\n deleteTurn: (turnId) => renderer.invoke(channels.deleteTurn, turnId),\n deleteQueuedPrompt: (promptId) => renderer.invoke(channels.deleteQueuedPrompt, promptId),\n editTurn: (turnId, content) => renderer.invoke(channels.editTurn, turnId, content),\n forkTurn: (turnId) => renderer.invoke(channels.forkTurn, turnId),\n getSnapshot: () => renderer.invoke(channels.getSnapshot),\n interrupt: () => renderer.invoke(channels.interrupt),\n listConversations: (options) => renderer.invoke(channels.listConversations, options),\n listModels: (options) => renderer.invoke(channels.listModels, options),\n logout: () => renderer.invoke(channels.logout),\n onEvent: (listener) => renderer.on(channels.event, listener),\n onStateChange: (listener) => {\n // Snapshots arrive until the main process switches to patches; the\n // switch point's base was already delivered as a snapshot, so it stays silent.\n const unsubscribeSnapshots = renderer.on(channels.stateChanged, listener);\n const unsubscribePatches = subscribeCodexSurfaceState(stateSource, listener, { emitInitialSnapshot: false });\n return () => {\n unsubscribePatches();\n unsubscribeSnapshots();\n };\n },\n readConversationHistory: (conversationId) => renderer.invoke(channels.readConversationHistory, conversationId),\n readConversationPromptHistory: (conversationId) => (\n renderer.invoke(channels.readConversationPromptHistory, conversationId)\n ),\n loadOlderConversationHistory: (conversationId) => renderer.invoke(channels.loadOlderConversationHistory, conversationId),\n refreshAccount: () => renderer.invoke(channels.refreshAccount),\n refreshConversations: () => renderer.invoke(channels.refreshConversations),\n renameConversation: (title) => renderer.invoke(channels.renameConversation, title),\n respondToClientRequest: (response) => renderer.invoke(channels.respondToClientRequest, response),\n resolveApproval: (approvalId, decision, scope) => renderer.invoke(channels.resolveApproval, approvalId, decision, scope),\n retryTurn: (turnId) => renderer.invoke(channels.retryTurn, turnId),\n setGoal: (objective, tokenBudget) => renderer.invoke(channels.setGoal, objective, tokenBudget),\n selectConversation: (conversationId) => renderer.invoke(channels.selectConversation, conversationId),\n sendMessage: (prompt, options) => renderer.invoke(channels.sendMessage, prompt, options),\n startReview: (options) => renderer.invoke(channels.startReview, options),\n startLiveChat: (conversationId, options) => renderer.invoke(channels.startLiveChat, conversationId, options),\n stopLiveChat: (conversationId) => renderer.invoke(channels.stopLiveChat, conversationId),\n startChatGptDeviceCodeLogin: () => renderer.invoke(channels.startChatGptDeviceCodeLogin),\n startChatGptLogin: () => renderer.invoke(channels.startChatGptLogin),\n steerMessage: (prompt, options) => renderer.invoke(channels.steerMessage, prompt, options),\n steerQueuedPrompt: (promptId, prompt) => renderer.invoke(channels.steerQueuedPrompt, promptId, prompt),\n unarchiveConversation: (conversationId) => renderer.invoke(channels.unarchiveConversation, conversationId),\n updateConversationSettings: (settings) => renderer.invoke(channels.updateConversationSettings, settings),\n updateQueuedPrompt: (promptId, prompt) => renderer.invoke(channels.updateQueuedPrompt, promptId, prompt),\n };\n}\n","import type {\n CodexNativeAttachment,\n CodexNativeAttachmentInput,\n CodexNativeClipboardContent,\n CodexNativeRendererApi,\n CodexSpeechTranscriptionResult,\n CodexSpeechSessionOptions,\n CodexSpeechSessionEvent,\n} from '@codex-app-sdk/core/native';\nimport {\n TypedIpcRenderer,\n type IpcRendererPort,\n type IpcRequest,\n} from './typed-ipc';\n\nexport const codexNativeChannels = {\n copyToClipboard: 'codex-native:copy-to-clipboard',\n ingestAttachments: 'codex-native:ingest-attachments',\n openExternal: 'codex-native:open-external',\n pickAttachments: 'codex-native:pick-attachments',\n readImagePreview: 'codex-native:read-image-preview',\n transcribeAudio: 'codex-native:transcribe-audio',\n startSpeech: 'codex-native:start-speech',\n appendSpeech: 'codex-native:append-speech',\n stopSpeech: 'codex-native:stop-speech',\n cancelSpeech: 'codex-native:cancel-speech',\n} as const;\nexport const codexSpeechEventChannel = 'codex-native:speech-event';\n\nexport type CodexNativeRequests = {\n [codexNativeChannels.startSpeech]: IpcRequest<[options: CodexSpeechSessionOptions], void>;\n [codexNativeChannels.appendSpeech]: IpcRequest<[sessionId: string, audio: ArrayBuffer], void>;\n [codexNativeChannels.stopSpeech]: IpcRequest<[sessionId: string], CodexSpeechTranscriptionResult>;\n [codexNativeChannels.cancelSpeech]: IpcRequest<[sessionId: string], void>;\n [codexNativeChannels.copyToClipboard]: IpcRequest<[content: CodexNativeClipboardContent], void>;\n [codexNativeChannels.ingestAttachments]: IpcRequest<[\n files: readonly CodexNativeAttachmentInput[],\n ], CodexNativeAttachment[]>;\n [codexNativeChannels.openExternal]: IpcRequest<[href: string], void>;\n [codexNativeChannels.pickAttachments]: IpcRequest<[], CodexNativeAttachment[]>;\n [codexNativeChannels.readImagePreview]: IpcRequest<[path: string], string | null>;\n [codexNativeChannels.transcribeAudio]: IpcRequest<[\n audioData: ArrayBuffer,\n options?: { locale?: string; live?: boolean },\n ], CodexSpeechTranscriptionResult>;\n};\n\nexport type CodexContextBridge = {\n exposeInMainWorld(key: string, api: unknown): void;\n};\n\nexport function createCodexNativeRendererApi(\n port: IpcRendererPort,\n options: { transcription?: boolean; streamingTranscription?: boolean } = {},\n): CodexNativeRendererApi {\n const renderer = new TypedIpcRenderer<CodexNativeRequests, { [codexSpeechEventChannel]: CodexSpeechSessionEvent }>(port);\n const transcription = options.transcription ?? process.platform === 'darwin';\n return {\n capabilities: {\n attachments: true,\n clipboard: true,\n externalLinks: true,\n transcription,\n },\n ...(transcription && (options.streamingTranscription ?? process.platform === 'darwin') ? {\n streamingTranscription: {\n start: (input: CodexSpeechSessionOptions) => renderer.invoke(codexNativeChannels.startSpeech, input),\n append: (id: string, audio: ArrayBuffer) => renderer.invoke(codexNativeChannels.appendSpeech, id, audio),\n stop: (id: string) => renderer.invoke(codexNativeChannels.stopSpeech, id),\n cancel: (id: string) => renderer.invoke(codexNativeChannels.cancelSpeech, id),\n onEvent: (listener: (event: CodexSpeechSessionEvent) => void) => renderer.on(codexSpeechEventChannel, listener),\n },\n } : {}),\n copyToClipboard: (content) => renderer.invoke(codexNativeChannels.copyToClipboard, content),\n ingestAttachments: (files) => renderer.invoke(codexNativeChannels.ingestAttachments, files),\n openExternal: (href) => renderer.invoke(codexNativeChannels.openExternal, href),\n pickAttachments: () => renderer.invoke(codexNativeChannels.pickAttachments),\n readImagePreview: (path) => renderer.invoke(codexNativeChannels.readImagePreview, path),\n transcribeAudio: (audioData, transcriptionOptions) => (\n renderer.invoke(codexNativeChannels.transcribeAudio, audioData, transcriptionOptions)\n ),\n };\n}\n\nexport function exposeCodexNativeRendererApi(\n contextBridge: CodexContextBridge,\n port: IpcRendererPort,\n options?: { transcription?: boolean; streamingTranscription?: boolean },\n): CodexNativeRendererApi {\n const api = createCodexNativeRendererApi(port, options);\n contextBridge.exposeInMainWorld('codexAppSdkNative', api);\n return api;\n}\n","import type { CodexNativeRendererApi } from '@codex-app-sdk/core/native';\nimport type { CodexSurfaceRendererApi } from '@codex-app-sdk/core/surface';\nimport {\n createCodexNativeRendererApi,\n type CodexContextBridge,\n} from './codex-native-renderer';\nimport { createCodexSurfaceRendererApi } from './codex-surface-ipc';\nimport type { IpcRendererPort } from './typed-ipc';\n\nexport type CodexElectronRendererApis = {\n native: CodexNativeRendererApi;\n surface: CodexSurfaceRendererApi;\n};\n\n/** Exposes the SDK-owned renderer APIs under stable, automatically detected names. */\nexport function exposeCodexElectronPreload(\n contextBridge: CodexContextBridge,\n ipcRenderer: IpcRendererPort,\n options?: { transcription?: boolean },\n): CodexElectronRendererApis {\n const surface = createCodexSurfaceRendererApi(ipcRenderer);\n const native = createCodexNativeRendererApi(ipcRenderer, options);\n contextBridge.exposeInMainWorld('codexSurface', surface);\n contextBridge.exposeInMainWorld('codexAppSdkNative', native);\n return { native, surface };\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type AppleSpeechTranscriptionOptions, type AppleSpeechTranscriptionResult } from '@codex-app-sdk/backend';
|
|
1
|
+
import { startAppleSpeechSession, type AppleSpeechTranscriptionOptions, type AppleSpeechTranscriptionResult } from '@codex-app-sdk/backend';
|
|
2
2
|
import { type IpcMainPort, type IpcSenderPolicy } from './typed-ipc';
|
|
3
3
|
import { CodexElectronAttachmentRegistry } from './codex-attachment-registry';
|
|
4
4
|
export type CodexNativeDialog = {
|
|
@@ -29,6 +29,7 @@ export type CodexNativeMainOptions = IpcSenderPolicy & {
|
|
|
29
29
|
maxTotalAttachmentBytes?: number;
|
|
30
30
|
maxAudioBytes?: number;
|
|
31
31
|
maxImagePreviewBytes?: number;
|
|
32
|
+
startSpeechSession?: typeof startAppleSpeechSession;
|
|
32
33
|
transcribeAudio?: (audioData: Buffer, options?: AppleSpeechTranscriptionOptions) => Promise<AppleSpeechTranscriptionResult>;
|
|
33
34
|
};
|
|
34
35
|
export type CodexNativeMainDependencies = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-native-ipc.d.ts","sourceRoot":"","sources":["../src/codex-native-ipc.ts"],"names":[],"mappings":"AAQA,OAAO,
|
|
1
|
+
{"version":3,"file":"codex-native-ipc.d.ts","sourceRoot":"","sources":["../src/codex-native-ipc.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,uBAAuB,EAEvB,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACpC,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAC;AAKrB,OAAO,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AAG9E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,cAAc,CAAC,OAAO,EAAE;QACtB,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,EAAE,MAAM,EAAE,CAAA;SAAE,EAAE,CAAC;QACnD,UAAU,EAAE,KAAK,CAAC,iBAAiB,GAAG,UAAU,CAAC,CAAC;QAClD,KAAK,EAAE,MAAM,CAAC;KACf,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CACzD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,CAAC,OAAO,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACvD,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3C,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,eAAe,GAAG;IACrD,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,OAAO,uBAAuB,CAAC;IACpD,eAAe,CAAC,EAAE,CAChB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,+BAA+B,KACtC,OAAO,CAAC,8BAA8B,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,SAAS,EAAE,oBAAoB,CAAC;IAChC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,KAAK,EAAE,gBAAgB,CAAC;CACzB,CAAC;AAQF,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,2BAA2B,EACzC,OAAO,GAAE,sBAA2B,EACpC,WAAW,kCAAwC,GAClD,MAAM,IAAI,CA8HZ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CodexNativeAttachment, CodexNativeAttachmentInput, CodexNativeClipboardContent, CodexNativeRendererApi, CodexSpeechTranscriptionResult } from '@codex-app-sdk/core/native';
|
|
1
|
+
import type { CodexNativeAttachment, CodexNativeAttachmentInput, CodexNativeClipboardContent, CodexNativeRendererApi, CodexSpeechTranscriptionResult, CodexSpeechSessionOptions } from '@codex-app-sdk/core/native';
|
|
2
2
|
import { type IpcRendererPort, type IpcRequest } from './typed-ipc';
|
|
3
3
|
export declare const codexNativeChannels: {
|
|
4
4
|
readonly copyToClipboard: "codex-native:copy-to-clipboard";
|
|
@@ -7,8 +7,17 @@ export declare const codexNativeChannels: {
|
|
|
7
7
|
readonly pickAttachments: "codex-native:pick-attachments";
|
|
8
8
|
readonly readImagePreview: "codex-native:read-image-preview";
|
|
9
9
|
readonly transcribeAudio: "codex-native:transcribe-audio";
|
|
10
|
+
readonly startSpeech: "codex-native:start-speech";
|
|
11
|
+
readonly appendSpeech: "codex-native:append-speech";
|
|
12
|
+
readonly stopSpeech: "codex-native:stop-speech";
|
|
13
|
+
readonly cancelSpeech: "codex-native:cancel-speech";
|
|
10
14
|
};
|
|
15
|
+
export declare const codexSpeechEventChannel = "codex-native:speech-event";
|
|
11
16
|
export type CodexNativeRequests = {
|
|
17
|
+
[codexNativeChannels.startSpeech]: IpcRequest<[options: CodexSpeechSessionOptions], void>;
|
|
18
|
+
[codexNativeChannels.appendSpeech]: IpcRequest<[sessionId: string, audio: ArrayBuffer], void>;
|
|
19
|
+
[codexNativeChannels.stopSpeech]: IpcRequest<[sessionId: string], CodexSpeechTranscriptionResult>;
|
|
20
|
+
[codexNativeChannels.cancelSpeech]: IpcRequest<[sessionId: string], void>;
|
|
12
21
|
[codexNativeChannels.copyToClipboard]: IpcRequest<[content: CodexNativeClipboardContent], void>;
|
|
13
22
|
[codexNativeChannels.ingestAttachments]: IpcRequest<[
|
|
14
23
|
files: readonly CodexNativeAttachmentInput[]
|
|
@@ -29,8 +38,10 @@ export type CodexContextBridge = {
|
|
|
29
38
|
};
|
|
30
39
|
export declare function createCodexNativeRendererApi(port: IpcRendererPort, options?: {
|
|
31
40
|
transcription?: boolean;
|
|
41
|
+
streamingTranscription?: boolean;
|
|
32
42
|
}): CodexNativeRendererApi;
|
|
33
43
|
export declare function exposeCodexNativeRendererApi(contextBridge: CodexContextBridge, port: IpcRendererPort, options?: {
|
|
34
44
|
transcription?: boolean;
|
|
45
|
+
streamingTranscription?: boolean;
|
|
35
46
|
}): CodexNativeRendererApi;
|
|
36
47
|
//# sourceMappingURL=codex-native-renderer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-native-renderer.d.ts","sourceRoot":"","sources":["../src/codex-native-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,0BAA0B,EAC1B,2BAA2B,EAC3B,sBAAsB,EACtB,8BAA8B,
|
|
1
|
+
{"version":3,"file":"codex-native-renderer.d.ts","sourceRoot":"","sources":["../src/codex-native-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,0BAA0B,EAC1B,2BAA2B,EAC3B,sBAAsB,EACtB,8BAA8B,EAC9B,yBAAyB,EAE1B,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,UAAU,EAChB,MAAM,aAAa,CAAC;AAErB,eAAO,MAAM,mBAAmB;;;;;;;;;;;CAWtB,CAAC;AACX,eAAO,MAAM,uBAAuB,8BAA8B,CAAC;AAEnE,MAAM,MAAM,mBAAmB,GAAG;IAChC,CAAC,mBAAmB,CAAC,WAAW,CAAC,EAAE,UAAU,CAAC,CAAC,OAAO,EAAE,yBAAyB,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1F,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,IAAI,CAAC,CAAC;IAC9F,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,8BAA8B,CAAC,CAAC;IAClG,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAC1E,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC,CAAC,OAAO,EAAE,2BAA2B,CAAC,EAAE,IAAI,CAAC,CAAC;IAChG,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,EAAE,UAAU,CAAC;QAClD,KAAK,EAAE,SAAS,0BAA0B,EAAE;KAC7C,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC5B,CAAC,mBAAmB,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IACrE,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC,EAAE,EAAE,qBAAqB,EAAE,CAAC,CAAC;IAC/E,CAAC,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,UAAU,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,CAAC;IAClF,CAAC,mBAAmB,CAAC,eAAe,CAAC,EAAE,UAAU,CAAC;QAChD,SAAS,EAAE,WAAW;QACtB,OAAO,CAAC,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,OAAO,CAAA;SAAE;KAC9C,EAAE,8BAA8B,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,IAAI,CAAC;CACpD,CAAC;AAEF,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC1E,sBAAsB,CA4BxB;AAED,wBAAgB,4BAA4B,CAC1C,aAAa,EAAE,kBAAkB,EACjC,IAAI,EAAE,eAAe,EACrB,OAAO,CAAC,EAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,sBAAsB,CAAC,EAAE,OAAO,CAAA;CAAE,GACtE,sBAAsB,CAIxB"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { startAppleSpeechSession } from '@codex-app-sdk/backend';
|
|
2
|
+
import { codexNativeChannels as channels, type CodexNativeRequests } from './codex-native-renderer';
|
|
3
|
+
import type { IpcMainHandlers } from './typed-ipc';
|
|
4
|
+
type SpeechChannels = typeof channels.startSpeech | typeof channels.appendSpeech | typeof channels.stopSpeech | typeof channels.cancelSpeech;
|
|
5
|
+
/** One bounded recording per renderer, owned by its invoking frame. */
|
|
6
|
+
export declare function createCodexSpeechSessions(options: {
|
|
7
|
+
maxAudioBytes: number;
|
|
8
|
+
appleSpeechAssetsPath?: string;
|
|
9
|
+
startSpeechSession?: typeof startAppleSpeechSession;
|
|
10
|
+
}): {
|
|
11
|
+
handlers: IpcMainHandlers<Pick<CodexNativeRequests, SpeechChannels>>;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=codex-speech-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-speech-sessions.d.ts","sourceRoot":"","sources":["../src/codex-speech-sessions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAA2B,MAAM,wBAAwB,CAAC;AAE1F,OAAO,EAAE,mBAAmB,IAAI,QAAQ,EAA2B,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC7H,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAkBnD,KAAK,cAAc,GAAG,OAAO,QAAQ,CAAC,WAAW,GAAG,OAAO,QAAQ,CAAC,YAAY,GAAG,OAAO,QAAQ,CAAC,UAAU,GAAG,OAAO,QAAQ,CAAC,YAAY,CAAC;AAE7I,uEAAuE;AACvE,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,aAAa,EAAE,MAAM,CAAC;IACtB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,kBAAkB,CAAC,EAAE,OAAO,uBAAuB,CAAC;CACrD,GAAG;IAAE,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,mBAAmB,EAAE,cAAc,CAAC,CAAC,CAAC;IAAC,OAAO,IAAI,IAAI,CAAA;CAAE,CAkG5F"}
|
package/dist/index.js
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
import { a as e, c as t, d as n,
|
|
2
|
-
import { promises as
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import { transcribeWithAppleSpeechAnalyzer as
|
|
6
|
-
import { randomUUID as
|
|
1
|
+
import { a as e, c as t, d as n, f as r, i, l as a, n as o, o as s, r as c, s as l, t as u, u as d } from "./chunks/codex-electron-preload-DupT4aeD.js";
|
|
2
|
+
import { promises as f } from "node:fs";
|
|
3
|
+
import p from "node:os";
|
|
4
|
+
import m from "node:path";
|
|
5
|
+
import { startAppleSpeechSession as h, transcribeWithAppleSpeechAnalyzer as g } from "@codex-app-sdk/backend";
|
|
6
|
+
import { randomUUID as _ } from "node:crypto";
|
|
7
7
|
//#region src/codex-window-policy.ts
|
|
8
|
-
var
|
|
8
|
+
var v = /* @__PURE__ */ new Set([
|
|
9
9
|
"http:",
|
|
10
10
|
"https:",
|
|
11
11
|
"mailto:",
|
|
12
12
|
"tel:"
|
|
13
13
|
]);
|
|
14
|
-
function
|
|
14
|
+
function y(e) {
|
|
15
15
|
try {
|
|
16
16
|
let t = new URL(e);
|
|
17
|
-
return
|
|
17
|
+
return v.has(t.protocol) ? t.href : null;
|
|
18
18
|
} catch {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
function
|
|
22
|
+
function b(e, t) {
|
|
23
23
|
if (!e) return !1;
|
|
24
24
|
try {
|
|
25
25
|
let n = new URL(e), r = new URL(t);
|
|
@@ -28,25 +28,25 @@ function v(e, t) {
|
|
|
28
28
|
return !1;
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
-
function
|
|
31
|
+
function x(e, t) {
|
|
32
32
|
let n = (e) => {
|
|
33
|
-
let n =
|
|
33
|
+
let n = y(e);
|
|
34
34
|
n && Promise.resolve(t.openExternal(n)).catch(() => void 0);
|
|
35
35
|
};
|
|
36
36
|
e.setWindowOpenHandler(({ url: e }) => (n(e), { action: "deny" })), e.on("will-navigate", (e) => {
|
|
37
|
-
|
|
37
|
+
b(e.url, t.rendererUrl) || (e.preventDefault(), n(e.url));
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
-
function
|
|
40
|
+
function S(e, t) {
|
|
41
41
|
let n = e?.senderFrame;
|
|
42
|
-
return !!n && n.parent === null &&
|
|
42
|
+
return !!n && n.parent === null && b(n.url, t);
|
|
43
43
|
}
|
|
44
44
|
//#endregion
|
|
45
45
|
//#region src/codex-attachment-registry.ts
|
|
46
|
-
var
|
|
46
|
+
var C = class {
|
|
47
47
|
#e = /* @__PURE__ */ new Map();
|
|
48
48
|
register(e) {
|
|
49
|
-
let t = `electron-attachment:${
|
|
49
|
+
let t = `electron-attachment:${_()}`;
|
|
50
50
|
return this.#e.set(t, {
|
|
51
51
|
type: e.type,
|
|
52
52
|
path: e.path,
|
|
@@ -85,30 +85,123 @@ var x = class {
|
|
|
85
85
|
if (!t) throw TypeError("Attachment reference is invalid or expired");
|
|
86
86
|
return t;
|
|
87
87
|
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
};
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/codex-speech-sessions.ts
|
|
91
|
+
function w(e) {
|
|
92
|
+
let t = /* @__PURE__ */ new Map(), n = e.startSpeechSession ?? h;
|
|
93
|
+
function r(e) {
|
|
94
|
+
let t = e;
|
|
95
|
+
if (!t?.sender?.on || !t.sender.removeListener || !t.senderFrame?.send) throw Error("Speech requires an owning renderer frame.");
|
|
96
|
+
return t;
|
|
97
|
+
}
|
|
98
|
+
function i(e, n) {
|
|
99
|
+
let i = r(e), a = t.get(n);
|
|
100
|
+
if (!a || a.owner.sender !== i.sender || a.owner.senderFrame !== i.senderFrame) throw Error("Speech session owner does not match.");
|
|
101
|
+
return a;
|
|
102
|
+
}
|
|
103
|
+
async function a(e, n) {
|
|
104
|
+
t.get(e) === n && (t.delete(e), n.cleanup(), n.abort.abort(), await n.session.then((e) => e.cancel()).catch(() => void 0));
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
handlers: {
|
|
108
|
+
[o.startSpeech]: async (i, o) => {
|
|
109
|
+
let s = o;
|
|
110
|
+
if (!s || typeof s.sessionId != "string" || !/^[\w-]{1,128}$/.test(s.sessionId) || !Number.isInteger(s.sampleRate) || s.sampleRate < 8e3 || s.sampleRate > 96e3 || s.locale !== void 0 && (typeof s.locale != "string" || s.locale.length > 128)) throw TypeError("Invalid speech session options.");
|
|
111
|
+
let l = r(i);
|
|
112
|
+
if (t.has(s.sessionId) || [...t.values()].some((e) => e.owner.sender === l.sender)) throw Error("A speech session is already active in this renderer.");
|
|
113
|
+
let u = s.sessionId, d = new AbortController(), f = () => {
|
|
114
|
+
a(u, m);
|
|
115
|
+
}, p = (e, t, n, r, i, a) => {
|
|
116
|
+
let o = e, s = o.frame ?? {
|
|
117
|
+
processId: i,
|
|
118
|
+
routingId: a
|
|
119
|
+
};
|
|
120
|
+
!(o.isSameDocument ?? n) && ((o.isMainFrame ?? r) || s.processId === l.senderFrame.processId && s.routingId === l.senderFrame.routingId) && f();
|
|
121
|
+
}, m = {
|
|
122
|
+
owner: l,
|
|
123
|
+
abort: d,
|
|
124
|
+
bytes: 0,
|
|
125
|
+
session: Promise.resolve().then(() => n(s, (e) => {
|
|
126
|
+
if (t.get(u) === m) {
|
|
127
|
+
try {
|
|
128
|
+
l.senderFrame.send(c, e);
|
|
129
|
+
} catch {
|
|
130
|
+
f();
|
|
131
|
+
}
|
|
132
|
+
e.type === "error" && f();
|
|
133
|
+
}
|
|
134
|
+
}, {
|
|
135
|
+
assetsPath: e.appleSpeechAssetsPath,
|
|
136
|
+
signal: d.signal
|
|
137
|
+
})),
|
|
138
|
+
cleanup: () => {
|
|
139
|
+
for (let e of ["destroyed", "render-process-gone"]) l.sender.removeListener(e, f);
|
|
140
|
+
l.sender.removeListener("did-start-navigation", p);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
t.set(u, m);
|
|
144
|
+
for (let e of ["destroyed", "render-process-gone"]) l.sender.on(e, f);
|
|
145
|
+
l.sender.on("did-start-navigation", p);
|
|
146
|
+
try {
|
|
147
|
+
await m.session;
|
|
148
|
+
} catch (e) {
|
|
149
|
+
throw await a(u, m), e;
|
|
150
|
+
}
|
|
151
|
+
if (d.signal.aborted) throw Error("Speech recording cancelled.");
|
|
152
|
+
},
|
|
153
|
+
[o.appendSpeech]: async (t, n, r) => {
|
|
154
|
+
let o = i(t, n);
|
|
155
|
+
if (o.stopping) throw Error("Speech session is finalizing.");
|
|
156
|
+
if (!(r instanceof ArrayBuffer) || !r.byteLength || r.byteLength % 4 || r.byteLength > 256 * 1024) throw TypeError("Invalid PCM audio chunk.");
|
|
157
|
+
if (o.bytes += r.byteLength, o.bytes > e.maxAudioBytes) throw await a(n, o), RangeError("Speech recording exceeds the audio byte limit.");
|
|
158
|
+
await (await o.session).append(r);
|
|
159
|
+
},
|
|
160
|
+
[o.stopSpeech]: async (e, n) => {
|
|
161
|
+
let r = i(e, n);
|
|
162
|
+
return r.stopping ??= r.session.then((e) => e.stop()).finally(() => {
|
|
163
|
+
t.delete(n), r.cleanup();
|
|
164
|
+
}), r.stopping;
|
|
165
|
+
},
|
|
166
|
+
[o.cancelSpeech]: async (e, n) => {
|
|
167
|
+
t.has(n) && await a(n, i(e, n));
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
dispose: () => {
|
|
171
|
+
for (let [e, n] of t) a(e, n);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
//#endregion
|
|
176
|
+
//#region src/codex-native-ipc.ts
|
|
177
|
+
var T = 25 * 1024 * 1024, E = 100 * 1024 * 1024, D = 25 * 1024 * 1024, O = 8 * 1024 * 1024, k = 16 * 1024 * 1024;
|
|
178
|
+
function A(e, t = {}, r = new C()) {
|
|
179
|
+
let i = H(t.maxAttachmentBytes, T, "Attachment byte limit"), a = H(t.maxAudioBytes, D, "Audio byte limit"), s = H(t.maxTotalAttachmentBytes, E, "Total attachment byte limit"), c = H(t.maxImagePreviewBytes, O, "Image preview byte limit"), l = w({
|
|
180
|
+
...t,
|
|
181
|
+
maxAudioBytes: a
|
|
182
|
+
}), u = n(e.ipcMain, {
|
|
183
|
+
...l.handlers,
|
|
184
|
+
[o.copyToClipboard]: (t, n) => {
|
|
185
|
+
let r = N(n);
|
|
93
186
|
e.clipboard.write(r);
|
|
94
187
|
},
|
|
95
|
-
[
|
|
96
|
-
let
|
|
97
|
-
if (
|
|
98
|
-
let a = await
|
|
99
|
-
return Promise.all(
|
|
100
|
-
let
|
|
101
|
-
return await
|
|
102
|
-
mimeType: e.mimeType ??
|
|
103
|
-
name:
|
|
188
|
+
[o.ingestAttachments]: async (e, t) => {
|
|
189
|
+
let n = j(t, i, s);
|
|
190
|
+
if (n.length === 0) return [];
|
|
191
|
+
let a = await f.mkdtemp(m.join(p.tmpdir(), "codex-app-sdk-attachments-"));
|
|
192
|
+
return Promise.all(n.map(async (e, t) => {
|
|
193
|
+
let n = m.join(a, `${t}-${B(e.name)}`), i = Buffer.from(e.data);
|
|
194
|
+
return await f.writeFile(n, i, { mode: 384 }), M(r, n, {
|
|
195
|
+
mimeType: e.mimeType ?? U(e.name),
|
|
196
|
+
name: B(e.name),
|
|
104
197
|
previewData: i
|
|
105
198
|
});
|
|
106
199
|
}));
|
|
107
200
|
},
|
|
108
|
-
[
|
|
109
|
-
await e.shell.openExternal(
|
|
201
|
+
[o.openExternal]: async (t, n) => {
|
|
202
|
+
await e.shell.openExternal(P(n));
|
|
110
203
|
},
|
|
111
|
-
[
|
|
204
|
+
[o.pickAttachments]: async () => {
|
|
112
205
|
let t = await e.dialog.showOpenDialog({
|
|
113
206
|
title: "Add Files & Photos",
|
|
114
207
|
properties: ["openFile", "multiSelections"],
|
|
@@ -118,63 +211,63 @@ function D(e, t = {}, n = new x()) {
|
|
|
118
211
|
}]
|
|
119
212
|
});
|
|
120
213
|
if (t.canceled) return [];
|
|
121
|
-
let
|
|
214
|
+
let n = [], a = 0, o = 0;
|
|
122
215
|
for (let e of t.filePaths) {
|
|
123
|
-
let t = await
|
|
216
|
+
let t = await f.stat(e);
|
|
124
217
|
if (!t.isFile()) throw TypeError(`Attachment is not a file: ${e}`);
|
|
125
|
-
if (t.size >
|
|
126
|
-
if (a += t.size, a >
|
|
127
|
-
let c =
|
|
128
|
-
l && (
|
|
218
|
+
if (t.size > i) throw RangeError(`Attachment exceeds the ${i} byte limit: ${e}`);
|
|
219
|
+
if (a += t.size, a > s) throw RangeError(`Attachments exceed the ${s} total byte limit`);
|
|
220
|
+
let c = U(e), l = c.startsWith("image/") && c !== "image/svg+xml" && t.size <= O && o + t.size <= k ? await f.readFile(e) : void 0;
|
|
221
|
+
l && (o += l.byteLength), n.push(M(r, e, {
|
|
129
222
|
mimeType: c,
|
|
130
|
-
name:
|
|
223
|
+
name: m.basename(e),
|
|
131
224
|
previewData: l,
|
|
132
225
|
size: t.size
|
|
133
226
|
}));
|
|
134
227
|
}
|
|
135
|
-
return
|
|
228
|
+
return n;
|
|
136
229
|
},
|
|
137
|
-
[
|
|
138
|
-
let
|
|
230
|
+
[o.readImagePreview]: async (e, t) => {
|
|
231
|
+
let n = R(t, "Image preview attachment reference"), i = V(n) ?? r.path(n), a = U(i);
|
|
139
232
|
if (!a.startsWith("image/") || a === "image/svg+xml") return null;
|
|
140
233
|
let o;
|
|
141
234
|
try {
|
|
142
|
-
o = await
|
|
235
|
+
o = await f.lstat(i);
|
|
143
236
|
} catch (e) {
|
|
144
|
-
if (
|
|
237
|
+
if (z(e)) return null;
|
|
145
238
|
throw e;
|
|
146
239
|
}
|
|
147
|
-
if (!o.isFile() || o.size >
|
|
148
|
-
let
|
|
149
|
-
return
|
|
240
|
+
if (!o.isFile() || o.size > c) return null;
|
|
241
|
+
let s = await f.readFile(i);
|
|
242
|
+
return s.byteLength > c ? null : `data:${a};base64,${s.toString("base64")}`;
|
|
150
243
|
},
|
|
151
|
-
[
|
|
152
|
-
let
|
|
153
|
-
if (
|
|
154
|
-
let o =
|
|
155
|
-
return (t.transcribeAudio ?? ((e, n) =>
|
|
244
|
+
[o.transcribeAudio]: async (e, n, r) => {
|
|
245
|
+
let i = I(n, "Audio data");
|
|
246
|
+
if (i.byteLength > a) throw RangeError(`Audio data exceeds the ${a} byte limit`);
|
|
247
|
+
let o = F(r);
|
|
248
|
+
return (t.transcribeAudio ?? ((e, n) => g(e, n, { ...t.appleSpeechAssetsPath ? { assetsPath: t.appleSpeechAssetsPath } : {} })))(Buffer.from(i), o);
|
|
156
249
|
}
|
|
157
250
|
}, t);
|
|
158
251
|
return () => {
|
|
159
|
-
|
|
252
|
+
u(), l.dispose(), r.clear();
|
|
160
253
|
};
|
|
161
254
|
}
|
|
162
|
-
function
|
|
255
|
+
function j(e, t, n) {
|
|
163
256
|
if (!Array.isArray(e)) throw TypeError("Attachments must be an array");
|
|
164
257
|
if (e.length > 20) throw RangeError("At most 20 attachments can be added at once");
|
|
165
258
|
let r = 0;
|
|
166
259
|
return e.map((e) => {
|
|
167
|
-
let i =
|
|
260
|
+
let i = L(e, "Attachment"), a = R(i.name, "Attachment name"), o = I(i.data, "Attachment data");
|
|
168
261
|
if (o.byteLength > t) throw RangeError(`Attachment '${a}' exceeds the ${t} byte limit`);
|
|
169
262
|
if (r += o.byteLength, r > n) throw RangeError(`Attachments exceed the ${n} total byte limit`);
|
|
170
263
|
return {
|
|
171
264
|
name: a,
|
|
172
265
|
data: o,
|
|
173
|
-
...i.mimeType === void 0 ? {} : { mimeType:
|
|
266
|
+
...i.mimeType === void 0 ? {} : { mimeType: R(i.mimeType, "Attachment MIME type") }
|
|
174
267
|
};
|
|
175
268
|
});
|
|
176
269
|
}
|
|
177
|
-
function
|
|
270
|
+
function M(e, t, n) {
|
|
178
271
|
let r = n.size ?? n.previewData?.byteLength ?? 0;
|
|
179
272
|
return e.register({
|
|
180
273
|
type: n.mimeType.startsWith("image/") ? "image" : "file",
|
|
@@ -182,19 +275,19 @@ function k(e, t, n) {
|
|
|
182
275
|
name: n.name,
|
|
183
276
|
mimeType: n.mimeType,
|
|
184
277
|
size: r,
|
|
185
|
-
...n.previewData && n.mimeType !== "image/svg+xml" && n.previewData.byteLength <=
|
|
278
|
+
...n.previewData && n.mimeType !== "image/svg+xml" && n.previewData.byteLength <= O ? { previewUrl: `data:${n.mimeType};base64,${Buffer.from(n.previewData).toString("base64")}` } : {}
|
|
186
279
|
});
|
|
187
280
|
}
|
|
188
|
-
function
|
|
189
|
-
let t =
|
|
281
|
+
function N(e) {
|
|
282
|
+
let t = L(e, "Clipboard content"), n = typeof t.text == "string" ? t.text : W("Clipboard text must be a string"), r = t.html === void 0 ? void 0 : typeof t.html == "string" ? t.html : W("Clipboard HTML must be a string");
|
|
190
283
|
if (n.length > 5e6 || (r?.length ?? 0) > 1e7) throw RangeError("Clipboard content is too large");
|
|
191
284
|
return {
|
|
192
285
|
text: n,
|
|
193
286
|
...r ? { html: r } : {}
|
|
194
287
|
};
|
|
195
288
|
}
|
|
196
|
-
function
|
|
197
|
-
let t =
|
|
289
|
+
function P(e) {
|
|
290
|
+
let t = R(e, "External URL"), n = new URL(t);
|
|
198
291
|
if (![
|
|
199
292
|
"http:",
|
|
200
293
|
"https:",
|
|
@@ -203,44 +296,44 @@ function j(e) {
|
|
|
203
296
|
].includes(n.protocol.toLowerCase())) throw TypeError(`Unsupported external URL protocol: ${n.protocol}`);
|
|
204
297
|
return n.href;
|
|
205
298
|
}
|
|
206
|
-
function
|
|
299
|
+
function F(e) {
|
|
207
300
|
if (e === void 0) return;
|
|
208
|
-
let t =
|
|
301
|
+
let t = L(e, "Speech transcription options");
|
|
209
302
|
if (t.live !== void 0 && typeof t.live != "boolean") throw TypeError("Speech transcription live mode must be a boolean");
|
|
210
303
|
return {
|
|
211
|
-
...t.locale === void 0 ? {} : { locale:
|
|
304
|
+
...t.locale === void 0 ? {} : { locale: R(t.locale, "Speech transcription locale") },
|
|
212
305
|
...t.live === void 0 ? {} : { live: t.live }
|
|
213
306
|
};
|
|
214
307
|
}
|
|
215
|
-
function
|
|
308
|
+
function I(e, t) {
|
|
216
309
|
if (!(e instanceof ArrayBuffer)) throw TypeError(`${t} must be an ArrayBuffer`);
|
|
217
310
|
return e;
|
|
218
311
|
}
|
|
219
|
-
function
|
|
312
|
+
function L(e, t) {
|
|
220
313
|
if (!e || typeof e != "object" || Array.isArray(e)) throw TypeError(`${t} must be an object`);
|
|
221
314
|
return e;
|
|
222
315
|
}
|
|
223
|
-
function
|
|
316
|
+
function R(e, t) {
|
|
224
317
|
if (typeof e != "string" || !e.trim()) throw TypeError(`${t} must be a non-empty string`);
|
|
225
318
|
return e.trim();
|
|
226
319
|
}
|
|
227
|
-
function
|
|
320
|
+
function z(e) {
|
|
228
321
|
return !!(e && typeof e == "object" && "code" in e && e.code === "ENOENT");
|
|
229
322
|
}
|
|
230
|
-
function
|
|
231
|
-
return
|
|
323
|
+
function B(e) {
|
|
324
|
+
return m.basename(e).replace(/[\u0000-\u001f\u007f]/g, "").trim() || "attachment";
|
|
232
325
|
}
|
|
233
|
-
function
|
|
234
|
-
if (!
|
|
235
|
-
let t =
|
|
236
|
-
return
|
|
326
|
+
function V(e) {
|
|
327
|
+
if (!m.isAbsolute(e)) return null;
|
|
328
|
+
let t = m.resolve(e), n = m.dirname(t);
|
|
329
|
+
return m.dirname(n) === m.resolve(p.tmpdir()) && /^codex-app-sdk-attachments-[a-z\d_-]+$/i.test(m.basename(n)) ? t : null;
|
|
237
330
|
}
|
|
238
|
-
function
|
|
331
|
+
function H(e, t, n) {
|
|
239
332
|
if (e === void 0) return t;
|
|
240
333
|
if (!Number.isSafeInteger(e) || e <= 0) throw TypeError(`${n} must be a positive integer`);
|
|
241
334
|
return e;
|
|
242
335
|
}
|
|
243
|
-
function
|
|
336
|
+
function U(e) {
|
|
244
337
|
return {
|
|
245
338
|
avif: "image/avif",
|
|
246
339
|
bmp: "image/bmp",
|
|
@@ -252,20 +345,20 @@ function B(e) {
|
|
|
252
345
|
png: "image/png",
|
|
253
346
|
svg: "image/svg+xml",
|
|
254
347
|
webp: "image/webp"
|
|
255
|
-
}[
|
|
348
|
+
}[m.extname(e).slice(1).toLowerCase()] ?? "application/octet-stream";
|
|
256
349
|
}
|
|
257
|
-
function
|
|
350
|
+
function W(e) {
|
|
258
351
|
throw TypeError(e);
|
|
259
352
|
}
|
|
260
353
|
//#endregion
|
|
261
354
|
//#region src/codex-electron-integration.ts
|
|
262
|
-
function
|
|
263
|
-
let t = new
|
|
355
|
+
function G(e) {
|
|
356
|
+
let t = new C(), n = e.isTrustedSender ? { isTrustedSender: e.isTrustedSender } : {}, r = l(e.ipcMain, e.sender, e.surface, {
|
|
264
357
|
...n,
|
|
265
358
|
resolveAttachment: (e) => t.resolve(e)
|
|
266
359
|
});
|
|
267
360
|
try {
|
|
268
|
-
let i =
|
|
361
|
+
let i = A(e, {
|
|
269
362
|
...e.native,
|
|
270
363
|
...n
|
|
271
364
|
}, t);
|
|
@@ -277,6 +370,6 @@ function H(e) {
|
|
|
277
370
|
}
|
|
278
371
|
}
|
|
279
372
|
//#endregion
|
|
280
|
-
export {
|
|
373
|
+
export { C as CodexElectronAttachmentRegistry, t as TypedIpcMain, a as TypedIpcRenderer, y as codexExternalUrl, d as connectIpcEventsToBus, i as createCodexNativeRendererApi, s as createCodexSurfaceRendererApi, u as exposeCodexElectronPreload, e as exposeCodexNativeRendererApi, x as installCodexWindowPolicy, S as isCodexRendererSender, b as isCodexRendererUrl, G as registerCodexElectronMain, A as registerCodexNativeIpc, l as registerCodexSurfaceIpc, n as registerIpcMainHandlers, r as sendIpcEvent };
|
|
281
374
|
|
|
282
375
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#attachments","#registered"],"sources":["../src/codex-window-policy.ts","../src/codex-attachment-registry.ts","../src/codex-native-ipc.ts","../src/codex-electron-integration.ts"],"sourcesContent":["type CodexNavigationEvent = {\n url: string;\n preventDefault(): void;\n};\n\n/** The `WebContents` subset used by `installCodexWindowPolicy`. */\nexport type CodexWindowPolicyTarget = {\n on(event: 'will-navigate', listener: (event: CodexNavigationEvent) => void): unknown;\n setWindowOpenHandler(handler: (details: { url: string }) => { action: 'deny' }): void;\n};\n\nexport type CodexWindowPolicyOptions = {\n /** The URL loaded into the window: the dev server URL or the packaged renderer's `file:` URL. */\n rendererUrl: string;\n /** Usually Electron's `shell.openExternal`. Receives only http(s), mailto, and tel URLs. */\n openExternal(url: string): Promise<void> | void;\n};\n\nconst externalProtocols = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\n/** Returns a normalized http(s), mailto, or tel URL that is safe to hand to the OS, otherwise null. */\nexport function codexExternalUrl(value: string): string | null {\n try {\n const url = new URL(value);\n return externalProtocols.has(url.protocol) ? url.href : null;\n } catch {\n return null;\n }\n}\n\n/**\n * True when `url` is the app's own renderer: the same origin as an http(s)\n * dev server, or the same document as a packaged `file:` renderer.\n */\nexport function isCodexRendererUrl(url: string | null | undefined, rendererUrl: string): boolean {\n if (!url) return false;\n try {\n const target = new URL(url);\n const renderer = new URL(rendererUrl);\n if (renderer.protocol === 'file:') {\n return target.protocol === 'file:' && target.pathname === renderer.pathname;\n }\n return target.origin === renderer.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Keeps a window on the app renderer. Electron re-runs the preload script on\n * every navigation, so any other page the window reached would receive the\n * full Codex bridge. New windows and foreign navigations are denied; safe\n * external URLs open in the system browser instead.\n */\nexport function installCodexWindowPolicy(\n webContents: CodexWindowPolicyTarget,\n options: CodexWindowPolicyOptions,\n): void {\n const openExternal = (value: string) => {\n const url = codexExternalUrl(value);\n if (url) void Promise.resolve(options.openExternal(url)).catch(() => undefined);\n };\n webContents.setWindowOpenHandler(({ url }) => {\n openExternal(url);\n return { action: 'deny' };\n });\n webContents.on('will-navigate', (event) => {\n if (isCodexRendererUrl(event.url, options.rendererUrl)) return;\n event.preventDefault();\n openExternal(event.url);\n });\n}\n\n/**\n * IPC sender predicate for `registerCodexElectronMain`: accepts only the main\n * frame of a window that is currently showing the app renderer.\n */\nexport function isCodexRendererSender(event: unknown, rendererUrl: string): boolean {\n const frame = (event as { senderFrame?: { parent: unknown; url: string } | null } | null)?.senderFrame;\n return Boolean(frame) && frame!.parent === null && isCodexRendererUrl(frame!.url, rendererUrl);\n}\n","import { randomUUID } from 'node:crypto';\nimport type { CodexHostAttachment } from '@codex-app-sdk/core/native';\nimport type {\n CodexRendererAttachment,\n CodexSurfaceAttachment,\n} from '@codex-app-sdk/core/surface';\n\nexport type CodexElectronAttachmentRegistration = {\n type: CodexHostAttachment['type'];\n path: string;\n name: string;\n mimeType: string;\n size: number;\n previewUrl?: string;\n};\n\ntype RegisteredAttachment = Omit<CodexElectronAttachmentRegistration, 'size'>;\n\n/** Keeps trusted local paths out of the renderer and scopes references to one Electron integration. */\nexport class CodexElectronAttachmentRegistry {\n readonly #attachments = new Map<string, RegisteredAttachment>();\n\n register(input: CodexElectronAttachmentRegistration): CodexHostAttachment {\n const reference = `electron-attachment:${randomUUID()}`;\n this.#attachments.set(reference, {\n type: input.type,\n path: input.path,\n name: input.name,\n mimeType: input.mimeType,\n ...(input.previewUrl === undefined ? {} : { previewUrl: input.previewUrl }),\n });\n return {\n id: reference,\n reference,\n type: input.type,\n name: input.name,\n mimeType: input.mimeType,\n size: input.size,\n ...(input.previewUrl === undefined ? {} : { previewUrl: input.previewUrl }),\n };\n }\n\n resolve(attachment: CodexRendererAttachment): CodexSurfaceAttachment {\n const registered = this.#registered(attachment.reference);\n if (registered.type !== attachment.type) {\n throw new TypeError('Attachment reference type does not match');\n }\n return {\n type: registered.type,\n path: registered.path,\n name: registered.name,\n mimeType: registered.mimeType,\n ...(registered.type === 'image' && registered.previewUrl\n ? { previewUrl: registered.previewUrl }\n : {}),\n };\n }\n\n path(reference: string): string {\n return this.#registered(reference).path;\n }\n\n clear(): void {\n this.#attachments.clear();\n }\n\n #registered(reference: string): RegisteredAttachment {\n const attachment = this.#attachments.get(reference);\n if (!attachment) throw new TypeError('Attachment reference is invalid or expired');\n return attachment;\n }\n}\n","import { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type {\n CodexHostAttachment,\n CodexNativeAttachmentInput,\n CodexNativeClipboardContent,\n} from '@codex-app-sdk/core/native';\nimport {\n transcribeWithAppleSpeechAnalyzer,\n type AppleSpeechTranscriptionOptions,\n type AppleSpeechTranscriptionResult,\n} from '@codex-app-sdk/backend';\nimport {\n registerIpcMainHandlers,\n type IpcMainPort,\n type IpcSenderPolicy,\n} from './typed-ipc';\nimport {\n codexNativeChannels as channels,\n type CodexNativeRequests as NativeRequests,\n} from './codex-native-renderer';\nimport { CodexElectronAttachmentRegistry } from './codex-attachment-registry';\n\nexport type CodexNativeDialog = {\n showOpenDialog(options: {\n filters?: { name: string; extensions: string[] }[];\n properties: Array<'multiSelections' | 'openFile'>;\n title: string;\n }): Promise<{ canceled: boolean; filePaths: string[] }>;\n};\n\nexport type CodexNativeClipboard = {\n write(content: { html?: string; text: string }): void;\n};\n\nexport type CodexNativeShell = {\n openExternal(href: string): Promise<void>;\n};\n\nexport type CodexNativeMainOptions = IpcSenderPolicy & {\n appleSpeechAssetsPath?: string;\n maxAttachmentBytes?: number;\n maxTotalAttachmentBytes?: number;\n maxAudioBytes?: number;\n maxImagePreviewBytes?: number;\n transcribeAudio?: (\n audioData: Buffer,\n options?: AppleSpeechTranscriptionOptions,\n ) => Promise<AppleSpeechTranscriptionResult>;\n};\n\nexport type CodexNativeMainDependencies = {\n clipboard: CodexNativeClipboard;\n dialog: CodexNativeDialog;\n ipcMain: IpcMainPort;\n shell: CodexNativeShell;\n};\n\nconst defaultMaxAttachmentBytes = 25 * 1024 * 1024;\nconst defaultMaxTotalAttachmentBytes = 100 * 1024 * 1024;\nconst defaultMaxAudioBytes = 25 * 1024 * 1024;\nconst maxPreviewBytes = 8 * 1024 * 1024;\nconst maxTotalPreviewBytes = 16 * 1024 * 1024;\n\nexport function registerCodexNativeIpc(\n dependencies: CodexNativeMainDependencies,\n options: CodexNativeMainOptions = {},\n attachments = new CodexElectronAttachmentRegistry(),\n): () => void {\n const maxAttachmentBytes = positiveByteLimit(\n options.maxAttachmentBytes,\n defaultMaxAttachmentBytes,\n 'Attachment byte limit',\n );\n const maxAudioBytes = positiveByteLimit(\n options.maxAudioBytes,\n defaultMaxAudioBytes,\n 'Audio byte limit',\n );\n const maxTotalAttachmentBytes = positiveByteLimit(\n options.maxTotalAttachmentBytes,\n defaultMaxTotalAttachmentBytes,\n 'Total attachment byte limit',\n );\n const maxImagePreviewBytes = positiveByteLimit(\n options.maxImagePreviewBytes,\n maxPreviewBytes,\n 'Image preview byte limit',\n );\n\n const unregister = registerIpcMainHandlers<NativeRequests>(dependencies.ipcMain, {\n [channels.copyToClipboard]: (_event, value) => {\n const content = clipboardContent(value);\n dependencies.clipboard.write(content);\n },\n [channels.ingestAttachments]: async (_event, value) => {\n const inputs = attachmentInputs(value, maxAttachmentBytes, maxTotalAttachmentBytes);\n if (inputs.length === 0) return [];\n const temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'codex-app-sdk-attachments-'));\n return Promise.all(inputs.map(async (input, index) => {\n const filePath = path.join(temporaryDirectory, `${index}-${safeFileName(input.name)}`);\n const data = Buffer.from(input.data);\n await fs.writeFile(filePath, data, { mode: 0o600 });\n return attachmentFromPath(attachments, filePath, {\n mimeType: input.mimeType ?? mimeTypeForPath(input.name),\n name: safeFileName(input.name),\n previewData: data,\n });\n }));\n },\n [channels.openExternal]: async (_event, value) => {\n await dependencies.shell.openExternal(externalHref(value));\n },\n [channels.pickAttachments]: async () => {\n const result = await dependencies.dialog.showOpenDialog({\n title: 'Add Files & Photos',\n properties: ['openFile', 'multiSelections'],\n filters: [\n { name: 'Files and photos', extensions: ['*'] },\n ],\n });\n if (result.canceled) return [];\n const pickedAttachments: CodexHostAttachment[] = [];\n let totalBytes = 0;\n let previewBytes = 0;\n for (const filePath of result.filePaths) {\n const metadata = await fs.stat(filePath);\n if (!metadata.isFile()) throw new TypeError(`Attachment is not a file: ${filePath}`);\n if (metadata.size > maxAttachmentBytes) {\n throw new RangeError(`Attachment exceeds the ${maxAttachmentBytes} byte limit: ${filePath}`);\n }\n totalBytes += metadata.size;\n if (totalBytes > maxTotalAttachmentBytes) {\n throw new RangeError(`Attachments exceed the ${maxTotalAttachmentBytes} total byte limit`);\n }\n const mimeType = mimeTypeForPath(filePath);\n const previewData = mimeType.startsWith('image/')\n && mimeType !== 'image/svg+xml'\n && metadata.size <= maxPreviewBytes\n && previewBytes + metadata.size <= maxTotalPreviewBytes\n ? await fs.readFile(filePath)\n : undefined;\n if (previewData) previewBytes += previewData.byteLength;\n pickedAttachments.push(attachmentFromPath(attachments, filePath, {\n mimeType,\n name: path.basename(filePath),\n previewData,\n size: metadata.size,\n }));\n }\n return pickedAttachments;\n },\n [channels.readImagePreview]: async (_event, value) => {\n const reference = nonEmptyString(value, 'Image preview attachment reference');\n const filePath = managedTemporaryAttachmentPath(reference) ?? attachments.path(reference);\n const mimeType = mimeTypeForPath(filePath);\n if (!mimeType.startsWith('image/') || mimeType === 'image/svg+xml') return null;\n let metadata;\n try {\n metadata = await fs.lstat(filePath);\n } catch (error) {\n if (isMissingFileError(error)) return null;\n throw error;\n }\n if (!metadata.isFile() || metadata.size > maxImagePreviewBytes) return null;\n const data = await fs.readFile(filePath);\n if (data.byteLength > maxImagePreviewBytes) return null;\n return `data:${mimeType};base64,${data.toString('base64')}`;\n },\n [channels.transcribeAudio]: async (_event, value, rawOptions) => {\n const audioData = arrayBuffer(value, 'Audio data');\n if (audioData.byteLength > maxAudioBytes) {\n throw new RangeError(`Audio data exceeds the ${maxAudioBytes} byte limit`);\n }\n const transcriptionOptions = appleSpeechOptions(rawOptions);\n const transcribe = options.transcribeAudio\n ?? ((data: Buffer, inputOptions?: AppleSpeechTranscriptionOptions) => (\n transcribeWithAppleSpeechAnalyzer(data, inputOptions, {\n ...(options.appleSpeechAssetsPath\n ? { assetsPath: options.appleSpeechAssetsPath }\n : {}),\n })\n ));\n return transcribe(Buffer.from(audioData), transcriptionOptions);\n },\n }, options);\n\n return () => {\n unregister();\n attachments.clear();\n };\n}\n\nfunction attachmentInputs(\n value: unknown,\n maxBytes: number,\n maxTotalBytes: number,\n): CodexNativeAttachmentInput[] {\n if (!Array.isArray(value)) throw new TypeError('Attachments must be an array');\n if (value.length > 20) throw new RangeError('At most 20 attachments can be added at once');\n let totalBytes = 0;\n return value.map((entry) => {\n const record = objectRecord(entry, 'Attachment');\n const name = nonEmptyString(record.name, 'Attachment name');\n const data = arrayBuffer(record.data, 'Attachment data');\n if (data.byteLength > maxBytes) {\n throw new RangeError(`Attachment '${name}' exceeds the ${maxBytes} byte limit`);\n }\n totalBytes += data.byteLength;\n if (totalBytes > maxTotalBytes) {\n throw new RangeError(`Attachments exceed the ${maxTotalBytes} total byte limit`);\n }\n return {\n name,\n data,\n ...(record.mimeType === undefined\n ? {}\n : { mimeType: nonEmptyString(record.mimeType, 'Attachment MIME type') }),\n };\n });\n}\n\nfunction attachmentFromPath(\n attachments: CodexElectronAttachmentRegistry,\n filePath: string,\n metadata: {\n mimeType: string;\n name: string;\n previewData?: Uint8Array;\n size?: number;\n },\n): CodexHostAttachment {\n const size = metadata.size ?? metadata.previewData?.byteLength ?? 0;\n return attachments.register({\n type: metadata.mimeType.startsWith('image/') ? 'image' : 'file',\n path: filePath,\n name: metadata.name,\n mimeType: metadata.mimeType,\n size,\n ...(metadata.previewData\n && metadata.mimeType !== 'image/svg+xml'\n && metadata.previewData.byteLength <= maxPreviewBytes\n ? { previewUrl: `data:${metadata.mimeType};base64,${Buffer.from(metadata.previewData).toString('base64')}` }\n : {}),\n });\n}\n\nfunction clipboardContent(value: unknown): CodexNativeClipboardContent {\n const record = objectRecord(value, 'Clipboard content');\n const text = typeof record.text === 'string' ? record.text : invalid('Clipboard text must be a string');\n const html = record.html === undefined\n ? undefined\n : typeof record.html === 'string'\n ? record.html\n : invalid('Clipboard HTML must be a string');\n if (text.length > 5_000_000 || (html?.length ?? 0) > 10_000_000) {\n throw new RangeError('Clipboard content is too large');\n }\n return { text, ...(html ? { html } : {}) };\n}\n\nfunction externalHref(value: unknown): string {\n const href = nonEmptyString(value, 'External URL');\n const url = new URL(href);\n if (!['http:', 'https:', 'mailto:', 'tel:'].includes(url.protocol.toLowerCase())) {\n throw new TypeError(`Unsupported external URL protocol: ${url.protocol}`);\n }\n return url.href;\n}\n\nfunction appleSpeechOptions(value: unknown): AppleSpeechTranscriptionOptions | undefined {\n if (value === undefined) return undefined;\n const record = objectRecord(value, 'Speech transcription options');\n if (record.live !== undefined && typeof record.live !== 'boolean') {\n throw new TypeError('Speech transcription live mode must be a boolean');\n }\n return {\n ...(record.locale === undefined\n ? {}\n : { locale: nonEmptyString(record.locale, 'Speech transcription locale') }),\n ...(record.live === undefined ? {} : { live: record.live }),\n };\n}\n\nfunction arrayBuffer(value: unknown, label: string): ArrayBuffer {\n if (!(value instanceof ArrayBuffer)) throw new TypeError(`${label} must be an ArrayBuffer`);\n return value;\n}\n\nfunction objectRecord(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new TypeError(`${label} must be an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction nonEmptyString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} must be a non-empty string`);\n return value.trim();\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');\n}\n\nfunction safeFileName(value: string): string {\n const name = path.basename(value).replace(/[\\u0000-\\u001f\\u007f]/g, '').trim();\n return name || 'attachment';\n}\n\nfunction managedTemporaryAttachmentPath(value: string): string | null {\n if (!path.isAbsolute(value)) return null;\n const resolved = path.resolve(value);\n const directory = path.dirname(resolved);\n if (path.dirname(directory) !== path.resolve(os.tmpdir())) return null;\n return /^codex-app-sdk-attachments-[a-z\\d_-]+$/i.test(path.basename(directory))\n ? resolved\n : null;\n}\n\nfunction positiveByteLimit(value: number | undefined, fallback: number, label: string): number {\n if (value === undefined) return fallback;\n if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${label} must be a positive integer`);\n return value;\n}\n\nfunction mimeTypeForPath(filePath: string): string {\n const extension = path.extname(filePath).slice(1).toLowerCase();\n return ({\n avif: 'image/avif',\n bmp: 'image/bmp',\n gif: 'image/gif',\n heic: 'image/heic',\n heif: 'image/heif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n svg: 'image/svg+xml',\n webp: 'image/webp',\n } as Record<string, string>)[extension] ?? 'application/octet-stream';\n}\n\nfunction invalid(message: string): never {\n throw new TypeError(message);\n}\n","import type { CodexSurface } from '@codex-app-sdk/backend';\nimport {\n registerCodexNativeIpc,\n type CodexNativeMainDependencies,\n type CodexNativeMainOptions,\n} from './codex-native-ipc';\nimport { registerCodexSurfaceIpc } from './codex-surface-ipc';\nimport type { IpcEventSender, IpcSenderPolicy } from './typed-ipc';\nimport { CodexElectronAttachmentRegistry } from './codex-attachment-registry';\n\nexport type CodexElectronMainOptions = CodexNativeMainDependencies & IpcSenderPolicy & {\n native?: CodexNativeMainOptions;\n sender: IpcEventSender;\n surface: CodexSurface;\n};\n\n/** Installs the complete SDK-owned surface and native capability bridge. */\nexport function registerCodexElectronMain(options: CodexElectronMainOptions): () => void {\n const attachments = new CodexElectronAttachmentRegistry();\n const senderPolicy = options.isTrustedSender ? { isTrustedSender: options.isTrustedSender } : {};\n const unregisterSurface = registerCodexSurfaceIpc(\n options.ipcMain,\n options.sender,\n options.surface,\n { ...senderPolicy, resolveAttachment: (attachment) => attachments.resolve(attachment) },\n );\n try {\n const unregisterNative = registerCodexNativeIpc(options, { ...options.native, ...senderPolicy }, attachments);\n return () => {\n unregisterNative();\n unregisterSurface();\n };\n } catch (error) {\n unregisterSurface();\n throw error;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#attachments","#registered"],"sources":["../src/codex-window-policy.ts","../src/codex-attachment-registry.ts","../src/codex-speech-sessions.ts","../src/codex-native-ipc.ts","../src/codex-electron-integration.ts"],"sourcesContent":["type CodexNavigationEvent = {\n url: string;\n preventDefault(): void;\n};\n\n/** The `WebContents` subset used by `installCodexWindowPolicy`. */\nexport type CodexWindowPolicyTarget = {\n on(event: 'will-navigate', listener: (event: CodexNavigationEvent) => void): unknown;\n setWindowOpenHandler(handler: (details: { url: string }) => { action: 'deny' }): void;\n};\n\nexport type CodexWindowPolicyOptions = {\n /** The URL loaded into the window: the dev server URL or the packaged renderer's `file:` URL. */\n rendererUrl: string;\n /** Usually Electron's `shell.openExternal`. Receives only http(s), mailto, and tel URLs. */\n openExternal(url: string): Promise<void> | void;\n};\n\nconst externalProtocols = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\n/** Returns a normalized http(s), mailto, or tel URL that is safe to hand to the OS, otherwise null. */\nexport function codexExternalUrl(value: string): string | null {\n try {\n const url = new URL(value);\n return externalProtocols.has(url.protocol) ? url.href : null;\n } catch {\n return null;\n }\n}\n\n/**\n * True when `url` is the app's own renderer: the same origin as an http(s)\n * dev server, or the same document as a packaged `file:` renderer.\n */\nexport function isCodexRendererUrl(url: string | null | undefined, rendererUrl: string): boolean {\n if (!url) return false;\n try {\n const target = new URL(url);\n const renderer = new URL(rendererUrl);\n if (renderer.protocol === 'file:') {\n return target.protocol === 'file:' && target.pathname === renderer.pathname;\n }\n return target.origin === renderer.origin;\n } catch {\n return false;\n }\n}\n\n/**\n * Keeps a window on the app renderer. Electron re-runs the preload script on\n * every navigation, so any other page the window reached would receive the\n * full Codex bridge. New windows and foreign navigations are denied; safe\n * external URLs open in the system browser instead.\n */\nexport function installCodexWindowPolicy(\n webContents: CodexWindowPolicyTarget,\n options: CodexWindowPolicyOptions,\n): void {\n const openExternal = (value: string) => {\n const url = codexExternalUrl(value);\n if (url) void Promise.resolve(options.openExternal(url)).catch(() => undefined);\n };\n webContents.setWindowOpenHandler(({ url }) => {\n openExternal(url);\n return { action: 'deny' };\n });\n webContents.on('will-navigate', (event) => {\n if (isCodexRendererUrl(event.url, options.rendererUrl)) return;\n event.preventDefault();\n openExternal(event.url);\n });\n}\n\n/**\n * IPC sender predicate for `registerCodexElectronMain`: accepts only the main\n * frame of a window that is currently showing the app renderer.\n */\nexport function isCodexRendererSender(event: unknown, rendererUrl: string): boolean {\n const frame = (event as { senderFrame?: { parent: unknown; url: string } | null } | null)?.senderFrame;\n return Boolean(frame) && frame!.parent === null && isCodexRendererUrl(frame!.url, rendererUrl);\n}\n","import { randomUUID } from 'node:crypto';\nimport type { CodexHostAttachment } from '@codex-app-sdk/core/native';\nimport type {\n CodexRendererAttachment,\n CodexSurfaceAttachment,\n} from '@codex-app-sdk/core/surface';\n\nexport type CodexElectronAttachmentRegistration = {\n type: CodexHostAttachment['type'];\n path: string;\n name: string;\n mimeType: string;\n size: number;\n previewUrl?: string;\n};\n\ntype RegisteredAttachment = Omit<CodexElectronAttachmentRegistration, 'size'>;\n\n/** Keeps trusted local paths out of the renderer and scopes references to one Electron integration. */\nexport class CodexElectronAttachmentRegistry {\n readonly #attachments = new Map<string, RegisteredAttachment>();\n\n register(input: CodexElectronAttachmentRegistration): CodexHostAttachment {\n const reference = `electron-attachment:${randomUUID()}`;\n this.#attachments.set(reference, {\n type: input.type,\n path: input.path,\n name: input.name,\n mimeType: input.mimeType,\n ...(input.previewUrl === undefined ? {} : { previewUrl: input.previewUrl }),\n });\n return {\n id: reference,\n reference,\n type: input.type,\n name: input.name,\n mimeType: input.mimeType,\n size: input.size,\n ...(input.previewUrl === undefined ? {} : { previewUrl: input.previewUrl }),\n };\n }\n\n resolve(attachment: CodexRendererAttachment): CodexSurfaceAttachment {\n const registered = this.#registered(attachment.reference);\n if (registered.type !== attachment.type) {\n throw new TypeError('Attachment reference type does not match');\n }\n return {\n type: registered.type,\n path: registered.path,\n name: registered.name,\n mimeType: registered.mimeType,\n ...(registered.type === 'image' && registered.previewUrl\n ? { previewUrl: registered.previewUrl }\n : {}),\n };\n }\n\n path(reference: string): string {\n return this.#registered(reference).path;\n }\n\n clear(): void {\n this.#attachments.clear();\n }\n\n #registered(reference: string): RegisteredAttachment {\n const attachment = this.#attachments.get(reference);\n if (!attachment) throw new TypeError('Attachment reference is invalid or expired');\n return attachment;\n }\n}\n","import { startAppleSpeechSession, type AppleSpeechSession } from '@codex-app-sdk/backend';\nimport type { CodexSpeechSessionOptions, CodexSpeechTranscriptionResult } from '@codex-app-sdk/core/native';\nimport { codexNativeChannels as channels, codexSpeechEventChannel, type CodexNativeRequests } from './codex-native-renderer';\nimport type { IpcMainHandlers } from './typed-ipc';\nimport type { WebContentsDidStartNavigationEventParams } from 'electron';\n\ntype Owner = {\n sender: {\n on(name: string, listener: (...args: unknown[]) => void): unknown;\n removeListener(name: string, listener: (...args: unknown[]) => void): unknown;\n };\n senderFrame: { processId: number; routingId: number; send(name: string, event: unknown): void };\n};\ntype Entry = {\n owner: Owner;\n abort: AbortController;\n session: Promise<AppleSpeechSession>;\n stopping?: Promise<CodexSpeechTranscriptionResult>;\n bytes: number;\n cleanup: () => void;\n};\ntype SpeechChannels = typeof channels.startSpeech | typeof channels.appendSpeech | typeof channels.stopSpeech | typeof channels.cancelSpeech;\n\n/** One bounded recording per renderer, owned by its invoking frame. */\nexport function createCodexSpeechSessions(options: {\n maxAudioBytes: number;\n appleSpeechAssetsPath?: string;\n startSpeechSession?: typeof startAppleSpeechSession;\n}): { handlers: IpcMainHandlers<Pick<CodexNativeRequests, SpeechChannels>>; dispose(): void } {\n const sessions = new Map<string, Entry>();\n const start = options.startSpeechSession ?? startAppleSpeechSession;\n function owner(value: unknown): Owner {\n const event = value as Partial<Owner> | null;\n if (!event?.sender?.on || !event.sender.removeListener || !event.senderFrame?.send) {\n throw new Error('Speech requires an owning renderer frame.');\n }\n return event as Owner;\n }\n function get(event: unknown, id: string): Entry {\n const current = owner(event);\n const entry = sessions.get(id);\n if (!entry || entry.owner.sender !== current.sender || entry.owner.senderFrame !== current.senderFrame) {\n throw new Error('Speech session owner does not match.');\n }\n return entry;\n }\n async function cancel(id: string, entry: Entry): Promise<void> {\n if (sessions.get(id) !== entry) return;\n sessions.delete(id);\n entry.cleanup();\n entry.abort.abort();\n await entry.session.then((session) => session.cancel()).catch(() => undefined);\n }\n return {\n handlers: {\n [channels.startSpeech]: async (event, raw) => {\n const input = raw as CodexSpeechSessionOptions | null;\n if (!input || typeof input.sessionId !== 'string' || !/^[\\w-]{1,128}$/.test(input.sessionId)\n || !Number.isInteger(input.sampleRate) || input.sampleRate < 8000 || input.sampleRate > 96000\n || (input.locale !== undefined && (typeof input.locale !== 'string' || input.locale.length > 128))) {\n throw new TypeError('Invalid speech session options.');\n }\n const current = owner(event);\n if (sessions.has(input.sessionId) || [...sessions.values()].some((entry) => entry.owner.sender === current.sender)) {\n throw new Error('A speech session is already active in this renderer.');\n }\n const id = input.sessionId;\n const abort = new AbortController();\n const teardown = () => { void cancel(id, entry); };\n const onNavigation = (details: unknown, _url: unknown, sameDocument: unknown, mainFrame: unknown, processId: unknown, routingId: unknown) => {\n const navigation = details as Partial<WebContentsDidStartNavigationEventParams>;\n const frame = navigation.frame ?? { processId, routingId };\n // Older Electron versions supply positional arguments instead of details.\n if (!(navigation.isSameDocument ?? sameDocument) && ((navigation.isMainFrame ?? mainFrame)\n || (frame.processId === current.senderFrame.processId && frame.routingId === current.senderFrame.routingId))) teardown();\n };\n const entry: Entry = {\n owner: current, abort, bytes: 0,\n // Defer startup until the entry exists, including synchronous custom adapters.\n session: Promise.resolve().then(() => start(input, (result) => {\n if (sessions.get(id) !== entry) return;\n try { current.senderFrame.send(codexSpeechEventChannel, result); }\n catch { teardown(); }\n if (result.type === 'error') teardown();\n }, { assetsPath: options.appleSpeechAssetsPath, signal: abort.signal })),\n cleanup: () => {\n for (const name of ['destroyed', 'render-process-gone']) current.sender.removeListener(name, teardown);\n current.sender.removeListener('did-start-navigation', onNavigation);\n },\n };\n sessions.set(id, entry);\n for (const name of ['destroyed', 'render-process-gone']) current.sender.on(name, teardown);\n current.sender.on('did-start-navigation', onNavigation);\n try { await entry.session; }\n catch (error) { await cancel(id, entry); throw error; }\n if (abort.signal.aborted) throw new Error('Speech recording cancelled.');\n },\n [channels.appendSpeech]: async (event, id, audio) => {\n const entry = get(event, id);\n if (entry.stopping) throw new Error('Speech session is finalizing.');\n if (!(audio instanceof ArrayBuffer) || !audio.byteLength || audio.byteLength % 4 || audio.byteLength > 256 * 1024) {\n throw new TypeError('Invalid PCM audio chunk.');\n }\n entry.bytes += audio.byteLength;\n if (entry.bytes > options.maxAudioBytes) {\n await cancel(id, entry);\n throw new RangeError('Speech recording exceeds the audio byte limit.');\n }\n await (await entry.session).append(audio);\n },\n [channels.stopSpeech]: async (event, id) => {\n const entry = get(event, id);\n entry.stopping ??= entry.session.then((session) => session.stop()).finally(() => {\n sessions.delete(id);\n entry.cleanup();\n });\n return entry.stopping;\n },\n [channels.cancelSpeech]: async (event, id) => {\n // A final result/error may already have removed this session.\n if (!sessions.has(id)) return;\n await cancel(id, get(event, id));\n },\n },\n dispose: () => { for (const [id, entry] of sessions) void cancel(id, entry); },\n };\n}\n","import { promises as fs } from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport type {\n CodexHostAttachment,\n CodexNativeAttachmentInput,\n CodexNativeClipboardContent,\n} from '@codex-app-sdk/core/native';\nimport {\n startAppleSpeechSession,\n transcribeWithAppleSpeechAnalyzer,\n type AppleSpeechTranscriptionOptions,\n type AppleSpeechTranscriptionResult,\n} from '@codex-app-sdk/backend';\nimport {\n registerIpcMainHandlers,\n type IpcMainPort,\n type IpcSenderPolicy,\n} from './typed-ipc';\nimport {\n codexNativeChannels as channels,\n type CodexNativeRequests as NativeRequests,\n} from './codex-native-renderer';\nimport { CodexElectronAttachmentRegistry } from './codex-attachment-registry';\nimport { createCodexSpeechSessions } from './codex-speech-sessions';\n\nexport type CodexNativeDialog = {\n showOpenDialog(options: {\n filters?: { name: string; extensions: string[] }[];\n properties: Array<'multiSelections' | 'openFile'>;\n title: string;\n }): Promise<{ canceled: boolean; filePaths: string[] }>;\n};\n\nexport type CodexNativeClipboard = {\n write(content: { html?: string; text: string }): void;\n};\n\nexport type CodexNativeShell = {\n openExternal(href: string): Promise<void>;\n};\n\nexport type CodexNativeMainOptions = IpcSenderPolicy & {\n appleSpeechAssetsPath?: string;\n maxAttachmentBytes?: number;\n maxTotalAttachmentBytes?: number;\n maxAudioBytes?: number;\n maxImagePreviewBytes?: number;\n startSpeechSession?: typeof startAppleSpeechSession;\n transcribeAudio?: (\n audioData: Buffer,\n options?: AppleSpeechTranscriptionOptions,\n ) => Promise<AppleSpeechTranscriptionResult>;\n};\n\nexport type CodexNativeMainDependencies = {\n clipboard: CodexNativeClipboard;\n dialog: CodexNativeDialog;\n ipcMain: IpcMainPort;\n shell: CodexNativeShell;\n};\n\nconst defaultMaxAttachmentBytes = 25 * 1024 * 1024;\nconst defaultMaxTotalAttachmentBytes = 100 * 1024 * 1024;\nconst defaultMaxAudioBytes = 25 * 1024 * 1024;\nconst maxPreviewBytes = 8 * 1024 * 1024;\nconst maxTotalPreviewBytes = 16 * 1024 * 1024;\n\nexport function registerCodexNativeIpc(\n dependencies: CodexNativeMainDependencies,\n options: CodexNativeMainOptions = {},\n attachments = new CodexElectronAttachmentRegistry(),\n): () => void {\n const maxAttachmentBytes = positiveByteLimit(\n options.maxAttachmentBytes,\n defaultMaxAttachmentBytes,\n 'Attachment byte limit',\n );\n const maxAudioBytes = positiveByteLimit(\n options.maxAudioBytes,\n defaultMaxAudioBytes,\n 'Audio byte limit',\n );\n const maxTotalAttachmentBytes = positiveByteLimit(\n options.maxTotalAttachmentBytes,\n defaultMaxTotalAttachmentBytes,\n 'Total attachment byte limit',\n );\n const maxImagePreviewBytes = positiveByteLimit(\n options.maxImagePreviewBytes,\n maxPreviewBytes,\n 'Image preview byte limit',\n );\n\n const speech = createCodexSpeechSessions({ ...options, maxAudioBytes });\n const unregister = registerIpcMainHandlers<NativeRequests>(dependencies.ipcMain, {\n ...speech.handlers,\n [channels.copyToClipboard]: (_event, value) => {\n const content = clipboardContent(value);\n dependencies.clipboard.write(content);\n },\n [channels.ingestAttachments]: async (_event, value) => {\n const inputs = attachmentInputs(value, maxAttachmentBytes, maxTotalAttachmentBytes);\n if (inputs.length === 0) return [];\n const temporaryDirectory = await fs.mkdtemp(path.join(os.tmpdir(), 'codex-app-sdk-attachments-'));\n return Promise.all(inputs.map(async (input, index) => {\n const filePath = path.join(temporaryDirectory, `${index}-${safeFileName(input.name)}`);\n const data = Buffer.from(input.data);\n await fs.writeFile(filePath, data, { mode: 0o600 });\n return attachmentFromPath(attachments, filePath, {\n mimeType: input.mimeType ?? mimeTypeForPath(input.name),\n name: safeFileName(input.name),\n previewData: data,\n });\n }));\n },\n [channels.openExternal]: async (_event, value) => {\n await dependencies.shell.openExternal(externalHref(value));\n },\n [channels.pickAttachments]: async () => {\n const result = await dependencies.dialog.showOpenDialog({\n title: 'Add Files & Photos',\n properties: ['openFile', 'multiSelections'],\n filters: [\n { name: 'Files and photos', extensions: ['*'] },\n ],\n });\n if (result.canceled) return [];\n const pickedAttachments: CodexHostAttachment[] = [];\n let totalBytes = 0;\n let previewBytes = 0;\n for (const filePath of result.filePaths) {\n const metadata = await fs.stat(filePath);\n if (!metadata.isFile()) throw new TypeError(`Attachment is not a file: ${filePath}`);\n if (metadata.size > maxAttachmentBytes) {\n throw new RangeError(`Attachment exceeds the ${maxAttachmentBytes} byte limit: ${filePath}`);\n }\n totalBytes += metadata.size;\n if (totalBytes > maxTotalAttachmentBytes) {\n throw new RangeError(`Attachments exceed the ${maxTotalAttachmentBytes} total byte limit`);\n }\n const mimeType = mimeTypeForPath(filePath);\n const previewData = mimeType.startsWith('image/')\n && mimeType !== 'image/svg+xml'\n && metadata.size <= maxPreviewBytes\n && previewBytes + metadata.size <= maxTotalPreviewBytes\n ? await fs.readFile(filePath)\n : undefined;\n if (previewData) previewBytes += previewData.byteLength;\n pickedAttachments.push(attachmentFromPath(attachments, filePath, {\n mimeType,\n name: path.basename(filePath),\n previewData,\n size: metadata.size,\n }));\n }\n return pickedAttachments;\n },\n [channels.readImagePreview]: async (_event, value) => {\n const reference = nonEmptyString(value, 'Image preview attachment reference');\n const filePath = managedTemporaryAttachmentPath(reference) ?? attachments.path(reference);\n const mimeType = mimeTypeForPath(filePath);\n if (!mimeType.startsWith('image/') || mimeType === 'image/svg+xml') return null;\n let metadata;\n try {\n metadata = await fs.lstat(filePath);\n } catch (error) {\n if (isMissingFileError(error)) return null;\n throw error;\n }\n if (!metadata.isFile() || metadata.size > maxImagePreviewBytes) return null;\n const data = await fs.readFile(filePath);\n if (data.byteLength > maxImagePreviewBytes) return null;\n return `data:${mimeType};base64,${data.toString('base64')}`;\n },\n [channels.transcribeAudio]: async (_event, value, rawOptions) => {\n const audioData = arrayBuffer(value, 'Audio data');\n if (audioData.byteLength > maxAudioBytes) {\n throw new RangeError(`Audio data exceeds the ${maxAudioBytes} byte limit`);\n }\n const transcriptionOptions = appleSpeechOptions(rawOptions);\n const transcribe = options.transcribeAudio\n ?? ((data: Buffer, inputOptions?: AppleSpeechTranscriptionOptions) => (\n transcribeWithAppleSpeechAnalyzer(data, inputOptions, {\n ...(options.appleSpeechAssetsPath\n ? { assetsPath: options.appleSpeechAssetsPath }\n : {}),\n })\n ));\n return transcribe(Buffer.from(audioData), transcriptionOptions);\n },\n }, options);\n\n return () => {\n unregister();\n speech.dispose();\n attachments.clear();\n };\n}\n\nfunction attachmentInputs(\n value: unknown,\n maxBytes: number,\n maxTotalBytes: number,\n): CodexNativeAttachmentInput[] {\n if (!Array.isArray(value)) throw new TypeError('Attachments must be an array');\n if (value.length > 20) throw new RangeError('At most 20 attachments can be added at once');\n let totalBytes = 0;\n return value.map((entry) => {\n const record = objectRecord(entry, 'Attachment');\n const name = nonEmptyString(record.name, 'Attachment name');\n const data = arrayBuffer(record.data, 'Attachment data');\n if (data.byteLength > maxBytes) {\n throw new RangeError(`Attachment '${name}' exceeds the ${maxBytes} byte limit`);\n }\n totalBytes += data.byteLength;\n if (totalBytes > maxTotalBytes) {\n throw new RangeError(`Attachments exceed the ${maxTotalBytes} total byte limit`);\n }\n return {\n name,\n data,\n ...(record.mimeType === undefined\n ? {}\n : { mimeType: nonEmptyString(record.mimeType, 'Attachment MIME type') }),\n };\n });\n}\n\nfunction attachmentFromPath(\n attachments: CodexElectronAttachmentRegistry,\n filePath: string,\n metadata: {\n mimeType: string;\n name: string;\n previewData?: Uint8Array;\n size?: number;\n },\n): CodexHostAttachment {\n const size = metadata.size ?? metadata.previewData?.byteLength ?? 0;\n return attachments.register({\n type: metadata.mimeType.startsWith('image/') ? 'image' : 'file',\n path: filePath,\n name: metadata.name,\n mimeType: metadata.mimeType,\n size,\n ...(metadata.previewData\n && metadata.mimeType !== 'image/svg+xml'\n && metadata.previewData.byteLength <= maxPreviewBytes\n ? { previewUrl: `data:${metadata.mimeType};base64,${Buffer.from(metadata.previewData).toString('base64')}` }\n : {}),\n });\n}\n\nfunction clipboardContent(value: unknown): CodexNativeClipboardContent {\n const record = objectRecord(value, 'Clipboard content');\n const text = typeof record.text === 'string' ? record.text : invalid('Clipboard text must be a string');\n const html = record.html === undefined\n ? undefined\n : typeof record.html === 'string'\n ? record.html\n : invalid('Clipboard HTML must be a string');\n if (text.length > 5_000_000 || (html?.length ?? 0) > 10_000_000) {\n throw new RangeError('Clipboard content is too large');\n }\n return { text, ...(html ? { html } : {}) };\n}\n\nfunction externalHref(value: unknown): string {\n const href = nonEmptyString(value, 'External URL');\n const url = new URL(href);\n if (!['http:', 'https:', 'mailto:', 'tel:'].includes(url.protocol.toLowerCase())) {\n throw new TypeError(`Unsupported external URL protocol: ${url.protocol}`);\n }\n return url.href;\n}\n\nfunction appleSpeechOptions(value: unknown): AppleSpeechTranscriptionOptions | undefined {\n if (value === undefined) return undefined;\n const record = objectRecord(value, 'Speech transcription options');\n if (record.live !== undefined && typeof record.live !== 'boolean') {\n throw new TypeError('Speech transcription live mode must be a boolean');\n }\n return {\n ...(record.locale === undefined\n ? {}\n : { locale: nonEmptyString(record.locale, 'Speech transcription locale') }),\n ...(record.live === undefined ? {} : { live: record.live }),\n };\n}\n\nfunction arrayBuffer(value: unknown, label: string): ArrayBuffer {\n if (!(value instanceof ArrayBuffer)) throw new TypeError(`${label} must be an ArrayBuffer`);\n return value;\n}\n\nfunction objectRecord(value: unknown, label: string): Record<string, unknown> {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new TypeError(`${label} must be an object`);\n }\n return value as Record<string, unknown>;\n}\n\nfunction nonEmptyString(value: unknown, label: string): string {\n if (typeof value !== 'string' || !value.trim()) throw new TypeError(`${label} must be a non-empty string`);\n return value.trim();\n}\n\nfunction isMissingFileError(error: unknown): boolean {\n return Boolean(error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT');\n}\n\nfunction safeFileName(value: string): string {\n const name = path.basename(value).replace(/[\\u0000-\\u001f\\u007f]/g, '').trim();\n return name || 'attachment';\n}\n\nfunction managedTemporaryAttachmentPath(value: string): string | null {\n if (!path.isAbsolute(value)) return null;\n const resolved = path.resolve(value);\n const directory = path.dirname(resolved);\n if (path.dirname(directory) !== path.resolve(os.tmpdir())) return null;\n return /^codex-app-sdk-attachments-[a-z\\d_-]+$/i.test(path.basename(directory))\n ? resolved\n : null;\n}\n\nfunction positiveByteLimit(value: number | undefined, fallback: number, label: string): number {\n if (value === undefined) return fallback;\n if (!Number.isSafeInteger(value) || value <= 0) throw new TypeError(`${label} must be a positive integer`);\n return value;\n}\n\nfunction mimeTypeForPath(filePath: string): string {\n const extension = path.extname(filePath).slice(1).toLowerCase();\n return ({\n avif: 'image/avif',\n bmp: 'image/bmp',\n gif: 'image/gif',\n heic: 'image/heic',\n heif: 'image/heif',\n jpeg: 'image/jpeg',\n jpg: 'image/jpeg',\n png: 'image/png',\n svg: 'image/svg+xml',\n webp: 'image/webp',\n } as Record<string, string>)[extension] ?? 'application/octet-stream';\n}\n\nfunction invalid(message: string): never {\n throw new TypeError(message);\n}\n","import type { CodexSurface } from '@codex-app-sdk/backend';\nimport {\n registerCodexNativeIpc,\n type CodexNativeMainDependencies,\n type CodexNativeMainOptions,\n} from './codex-native-ipc';\nimport { registerCodexSurfaceIpc } from './codex-surface-ipc';\nimport type { IpcEventSender, IpcSenderPolicy } from './typed-ipc';\nimport { CodexElectronAttachmentRegistry } from './codex-attachment-registry';\n\nexport type CodexElectronMainOptions = CodexNativeMainDependencies & IpcSenderPolicy & {\n native?: CodexNativeMainOptions;\n sender: IpcEventSender;\n surface: CodexSurface;\n};\n\n/** Installs the complete SDK-owned surface and native capability bridge. */\nexport function registerCodexElectronMain(options: CodexElectronMainOptions): () => void {\n const attachments = new CodexElectronAttachmentRegistry();\n const senderPolicy = options.isTrustedSender ? { isTrustedSender: options.isTrustedSender } : {};\n const unregisterSurface = registerCodexSurfaceIpc(\n options.ipcMain,\n options.sender,\n options.surface,\n { ...senderPolicy, resolveAttachment: (attachment) => attachments.resolve(attachment) },\n );\n try {\n const unregisterNative = registerCodexNativeIpc(options, { ...options.native, ...senderPolicy }, attachments);\n return () => {\n unregisterNative();\n unregisterSurface();\n };\n } catch (error) {\n unregisterSurface();\n throw error;\n }\n}\n"],"mappings":";;;;;;;AAkBA,IAAM,oBAAoB,IAAI,IAAI;CAAC;CAAS;CAAU;CAAW;AAAM,CAAC;AAGxE,SAAgB,EAAiB,GAA8B;CAC7D,IAAI;EACF,IAAM,IAAM,IAAI,IAAI,CAAK;EACzB,OAAO,EAAkB,IAAI,EAAI,QAAQ,IAAI,EAAI,OAAO;CAC1D,QAAQ;EACN,OAAO;CACT;AACF;AAMA,SAAgB,EAAmB,GAAgC,GAA8B;CAC/F,IAAI,CAAC,GAAK,OAAO;CACjB,IAAI;EACF,IAAM,IAAS,IAAI,IAAI,CAAG,GACpB,IAAW,IAAI,IAAI,CAAW;EAIpC,OAHI,EAAS,aAAa,UACjB,EAAO,aAAa,WAAW,EAAO,aAAa,EAAS,WAE9D,EAAO,WAAW,EAAS;CACpC,QAAQ;EACN,OAAO;CACT;AACF;AAQA,SAAgB,EACd,GACA,GACM;CACN,IAAM,KAAgB,MAAkB;EACtC,IAAM,IAAM,EAAiB,CAAK;EAClC,AAAI,KAAK,QAAa,QAAQ,EAAQ,aAAa,CAAG,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;CAChF;CAKA,AAJA,EAAY,sBAAsB,EAAE,cAClC,EAAa,CAAG,GACT,EAAE,QAAQ,OAAO,EACzB,GACD,EAAY,GAAG,kBAAkB,MAAU;EACrC,EAAmB,EAAM,KAAK,EAAQ,WAAW,MACrD,EAAM,eAAe,GACrB,EAAa,EAAM,GAAG;CACxB,CAAC;AACH;AAMA,SAAgB,EAAsB,GAAgB,GAA8B;CAClF,IAAM,IAAS,GAA4E;CAC3F,OAAO,EAAQ,KAAU,EAAO,WAAW,QAAQ,EAAmB,EAAO,KAAK,CAAW;AAC/F;;;AC7DA,IAAa,IAAb,MAA6C;CAC3C,qBAAwB,IAAI,IAAkC;CAE9D,SAAS,GAAiE;EACxE,IAAM,IAAY,uBAAuB,EAAW;EAQpD,OAPA,KAAKA,GAAa,IAAI,GAAW;GAC/B,MAAM,EAAM;GACZ,MAAM,EAAM;GACZ,MAAM,EAAM;GACZ,UAAU,EAAM;GAChB,GAAI,EAAM,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,EAAM,WAAW;EAC3E,CAAC,GACM;GACL,IAAI;GACJ;GACA,MAAM,EAAM;GACZ,MAAM,EAAM;GACZ,UAAU,EAAM;GAChB,MAAM,EAAM;GACZ,GAAI,EAAM,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,EAAM,WAAW;EAC3E;CACF;CAEA,QAAQ,GAA6D;EACnE,IAAM,IAAa,KAAKC,GAAY,EAAW,SAAS;EACxD,IAAI,EAAW,SAAS,EAAW,MACjC,MAAU,UAAU,0CAA0C;EAEhE,OAAO;GACL,MAAM,EAAW;GACjB,MAAM,EAAW;GACjB,MAAM,EAAW;GACjB,UAAU,EAAW;GACrB,GAAI,EAAW,SAAS,WAAW,EAAW,aAC1C,EAAE,YAAY,EAAW,WAAW,IACpC,CAAC;EACP;CACF;CAEA,KAAK,GAA2B;EAC9B,OAAO,KAAKA,GAAY,CAAS,CAAC,CAAC;CACrC;CAEA,QAAc;EACZ,KAAKD,GAAa,MAAM;CAC1B;CAEA,GAAY,GAAyC;EACnD,IAAM,IAAa,KAAKA,GAAa,IAAI,CAAS;EAClD,IAAI,CAAC,GAAY,MAAU,UAAU,4CAA4C;EACjF,OAAO;CACT;AACF;;;AC/CA,SAAgB,EAA0B,GAIoD;CAC5F,IAAM,oBAAW,IAAI,IAAmB,GAClC,IAAQ,EAAQ,sBAAsB;CAC5C,SAAS,EAAM,GAAuB;EACpC,IAAM,IAAQ;EACd,IAAI,CAAC,GAAO,QAAQ,MAAM,CAAC,EAAM,OAAO,kBAAkB,CAAC,EAAM,aAAa,MAC5E,MAAU,MAAM,2CAA2C;EAE7D,OAAO;CACT;CACA,SAAS,EAAI,GAAgB,GAAmB;EAC9C,IAAM,IAAU,EAAM,CAAK,GACrB,IAAQ,EAAS,IAAI,CAAE;EAC7B,IAAI,CAAC,KAAS,EAAM,MAAM,WAAW,EAAQ,UAAU,EAAM,MAAM,gBAAgB,EAAQ,aACzF,MAAU,MAAM,sCAAsC;EAExD,OAAO;CACT;CACA,eAAe,EAAO,GAAY,GAA6B;EACzD,EAAS,IAAI,CAAE,MAAM,MACzB,EAAS,OAAO,CAAE,GAClB,EAAM,QAAQ,GACd,EAAM,MAAM,MAAM,GAClB,MAAM,EAAM,QAAQ,MAAM,MAAY,EAAQ,OAAO,CAAC,CAAC,CAAC,YAAY,KAAA,CAAS;CAC/E;CACA,OAAO;EACL,UAAU;IACP,EAAS,cAAc,OAAO,GAAO,MAAQ;IAC5C,IAAM,IAAQ;IACd,IAAI,CAAC,KAAS,OAAO,EAAM,aAAc,YAAY,CAAC,iBAAiB,KAAK,EAAM,SAAS,KACtF,CAAC,OAAO,UAAU,EAAM,UAAU,KAAK,EAAM,aAAa,OAAQ,EAAM,aAAa,QACpF,EAAM,WAAW,KAAA,MAAc,OAAO,EAAM,UAAW,YAAY,EAAM,OAAO,SAAS,MAC7F,MAAU,UAAU,iCAAiC;IAEvD,IAAM,IAAU,EAAM,CAAK;IAC3B,IAAI,EAAS,IAAI,EAAM,SAAS,KAAK,CAAC,GAAG,EAAS,OAAO,CAAC,CAAC,CAAC,MAAM,MAAU,EAAM,MAAM,WAAW,EAAQ,MAAM,GAC/G,MAAU,MAAM,sDAAsD;IAExE,IAAM,IAAK,EAAM,WACX,IAAQ,IAAI,gBAAgB,GAC5B,UAAiB;KAAE,EAAY,GAAI,CAAK;IAAG,GAC3C,KAAgB,GAAkB,GAAe,GAAuB,GAAoB,GAAoB,MAAuB;KAC3I,IAAM,IAAa,GACb,IAAQ,EAAW,SAAS;MAAE;MAAW;KAAU;KAEzD,AAAI,EAAE,EAAW,kBAAkB,QAAmB,EAAW,eAAe,MAC1E,EAAM,cAAc,EAAQ,YAAY,aAAa,EAAM,cAAc,EAAQ,YAAY,cAAa,EAAS;IAC3H,GACM,IAAe;KACnB,OAAO;KAAS;KAAO,OAAO;KAE9B,SAAS,QAAQ,QAAQ,CAAC,CAAC,WAAW,EAAM,IAAQ,MAAW;MACzD,MAAS,IAAI,CAAE,MAAM,GACzB;WAAI;QAAE,EAAQ,YAAY,KAAK,GAAyB,CAAM;OAAG,QAC3D;QAAE,EAAS;OAAG;OACpB,AAAI,EAAO,SAAS,WAAS,EAAS;MADlB;KAEtB,GAAG;MAAE,YAAY,EAAQ;MAAuB,QAAQ,EAAM;KAAO,CAAC,CAAC;KACvE,eAAe;MACb,KAAK,IAAM,KAAQ,CAAC,aAAa,qBAAqB,GAAG,EAAQ,OAAO,eAAe,GAAM,CAAQ;MACrG,EAAQ,OAAO,eAAe,wBAAwB,CAAY;KACpE;IACF;IACA,EAAS,IAAI,GAAI,CAAK;IACtB,KAAK,IAAM,KAAQ,CAAC,aAAa,qBAAqB,GAAG,EAAQ,OAAO,GAAG,GAAM,CAAQ;IACzF,EAAQ,OAAO,GAAG,wBAAwB,CAAY;IACtD,IAAI;KAAE,MAAM,EAAM;IAAS,SACpB,GAAO;KAA2B,MAAzB,MAAM,EAAO,GAAI,CAAK,GAAS;IAAO;IACtD,IAAI,EAAM,OAAO,SAAS,MAAU,MAAM,6BAA6B;GACzE;IACC,EAAS,eAAe,OAAO,GAAO,GAAI,MAAU;IACnD,IAAM,IAAQ,EAAI,GAAO,CAAE;IAC3B,IAAI,EAAM,UAAU,MAAU,MAAM,+BAA+B;IACnE,IAAI,EAAE,aAAiB,gBAAgB,CAAC,EAAM,cAAc,EAAM,aAAa,KAAK,EAAM,aAAa,MAAM,MAC3G,MAAU,UAAU,0BAA0B;IAGhD,IADA,EAAM,SAAS,EAAM,YACjB,EAAM,QAAQ,EAAQ,eAExB,MADA,MAAM,EAAO,GAAI,CAAK,GACZ,WAAW,gDAAgD;IAEvE,OAAO,MAAM,EAAM,QAAA,CAAS,OAAO,CAAK;GAC1C;IACC,EAAS,aAAa,OAAO,GAAO,MAAO;IAC1C,IAAM,IAAQ,EAAI,GAAO,CAAE;IAK3B,OAJA,EAAM,aAAa,EAAM,QAAQ,MAAM,MAAY,EAAQ,KAAK,CAAC,CAAC,CAAC,cAAc;KAE/E,AADA,EAAS,OAAO,CAAE,GAClB,EAAM,QAAQ;IAChB,CAAC,GACM,EAAM;GACf;IACC,EAAS,eAAe,OAAO,GAAO,MAAO;IAEvC,EAAS,IAAI,CAAE,KACpB,MAAM,EAAO,GAAI,EAAI,GAAO,CAAE,CAAC;GACjC;EACF;EACA,eAAe;GAAE,KAAK,IAAM,CAAC,GAAI,MAAU,GAAU,EAAY,GAAI,CAAK;EAAG;CAC/E;AACF;;;AChEA,IAAM,IAA4B,KAAK,OAAO,MACxC,IAAiC,MAAM,OAAO,MAC9C,IAAuB,KAAK,OAAO,MACnC,IAAkB,IAAI,OAAO,MAC7B,IAAuB,KAAK,OAAO;AAEzC,SAAgB,EACd,GACA,IAAkC,CAAC,GACnC,IAAc,IAAI,EAAgC,GACtC;CACZ,IAAM,IAAqB,EACzB,EAAQ,oBACR,GACA,uBACF,GACM,IAAgB,EACpB,EAAQ,eACR,GACA,kBACF,GACM,IAA0B,EAC9B,EAAQ,yBACR,GACA,6BACF,GACM,IAAuB,EAC3B,EAAQ,sBACR,GACA,0BACF,GAEM,IAAS,EAA0B;EAAE,GAAG;EAAS;CAAc,CAAC,GAChE,IAAa,EAAwC,EAAa,SAAS;EAC/E,GAAG,EAAO;GACT,EAAS,mBAAmB,GAAQ,MAAU;GAC7C,IAAM,IAAU,EAAiB,CAAK;GACtC,EAAa,UAAU,MAAM,CAAO;EACtC;GACC,EAAS,oBAAoB,OAAO,GAAQ,MAAU;GACrD,IAAM,IAAS,EAAiB,GAAO,GAAoB,CAAuB;GAClF,IAAI,EAAO,WAAW,GAAG,OAAO,CAAC;GACjC,IAAM,IAAqB,MAAM,EAAG,QAAQ,EAAK,KAAK,EAAG,OAAO,GAAG,4BAA4B,CAAC;GAChG,OAAO,QAAQ,IAAI,EAAO,IAAI,OAAO,GAAO,MAAU;IACpD,IAAM,IAAW,EAAK,KAAK,GAAoB,GAAG,EAAM,GAAG,EAAa,EAAM,IAAI,GAAG,GAC/E,IAAO,OAAO,KAAK,EAAM,IAAI;IAEnC,OADA,MAAM,EAAG,UAAU,GAAU,GAAM,EAAE,MAAM,IAAM,CAAC,GAC3C,EAAmB,GAAa,GAAU;KAC/C,UAAU,EAAM,YAAY,EAAgB,EAAM,IAAI;KACtD,MAAM,EAAa,EAAM,IAAI;KAC7B,aAAa;IACf,CAAC;GACH,CAAC,CAAC;EACJ;GACC,EAAS,eAAe,OAAO,GAAQ,MAAU;GAChD,MAAM,EAAa,MAAM,aAAa,EAAa,CAAK,CAAC;EAC3D;GACC,EAAS,kBAAkB,YAAY;GACtC,IAAM,IAAS,MAAM,EAAa,OAAO,eAAe;IACtD,OAAO;IACP,YAAY,CAAC,YAAY,iBAAiB;IAC1C,SAAS,CACP;KAAE,MAAM;KAAoB,YAAY,CAAC,GAAG;IAAE,CAChD;GACF,CAAC;GACD,IAAI,EAAO,UAAU,OAAO,CAAC;GAC7B,IAAM,IAA2C,CAAC,GAC9C,IAAa,GACb,IAAe;GACnB,KAAK,IAAM,KAAY,EAAO,WAAW;IACvC,IAAM,IAAW,MAAM,EAAG,KAAK,CAAQ;IACvC,IAAI,CAAC,EAAS,OAAO,GAAG,MAAU,UAAU,6BAA6B,GAAU;IACnF,IAAI,EAAS,OAAO,GAClB,MAAU,WAAW,0BAA0B,EAAmB,eAAe,GAAU;IAG7F,IADA,KAAc,EAAS,MACnB,IAAa,GACf,MAAU,WAAW,0BAA0B,EAAwB,kBAAkB;IAE3F,IAAM,IAAW,EAAgB,CAAQ,GACnC,IAAc,EAAS,WAAW,QAAQ,KAC3C,MAAa,mBACb,EAAS,QAAQ,KACjB,IAAe,EAAS,QAAQ,IACjC,MAAM,EAAG,SAAS,CAAQ,IAC1B,KAAA;IAEJ,AADI,MAAa,KAAgB,EAAY,aAC7C,EAAkB,KAAK,EAAmB,GAAa,GAAU;KAC/D;KACA,MAAM,EAAK,SAAS,CAAQ;KAC5B;KACA,MAAM,EAAS;IACjB,CAAC,CAAC;GACJ;GACA,OAAO;EACT;GACC,EAAS,mBAAmB,OAAO,GAAQ,MAAU;GACpD,IAAM,IAAY,EAAe,GAAO,oCAAoC,GACtE,IAAW,EAA+B,CAAS,KAAK,EAAY,KAAK,CAAS,GAClF,IAAW,EAAgB,CAAQ;GACzC,IAAI,CAAC,EAAS,WAAW,QAAQ,KAAK,MAAa,iBAAiB,OAAO;GAC3E,IAAI;GACJ,IAAI;IACF,IAAW,MAAM,EAAG,MAAM,CAAQ;GACpC,SAAS,GAAO;IACd,IAAI,EAAmB,CAAK,GAAG,OAAO;IACtC,MAAM;GACR;GACA,IAAI,CAAC,EAAS,OAAO,KAAK,EAAS,OAAO,GAAsB,OAAO;GACvE,IAAM,IAAO,MAAM,EAAG,SAAS,CAAQ;GAEvC,OADI,EAAK,aAAa,IAA6B,OAC5C,QAAQ,EAAS,UAAU,EAAK,SAAS,QAAQ;EAC1D;GACC,EAAS,kBAAkB,OAAO,GAAQ,GAAO,MAAe;GAC/D,IAAM,IAAY,EAAY,GAAO,YAAY;GACjD,IAAI,EAAU,aAAa,GACzB,MAAU,WAAW,0BAA0B,EAAc,YAAY;GAE3E,IAAM,IAAuB,EAAmB,CAAU;GAS1D,QARmB,EAAQ,qBACpB,GAAc,MACjB,EAAkC,GAAM,GAAc,EACpD,GAAI,EAAQ,wBACR,EAAE,YAAY,EAAQ,sBAAsB,IAC5C,CAAC,EACP,CAAC,GAAA,CAEa,OAAO,KAAK,CAAS,GAAG,CAAoB;EAChE;CACF,GAAG,CAAO;CAEV,aAAa;EAGX,AAFA,EAAW,GACX,EAAO,QAAQ,GACf,EAAY,MAAM;CACpB;AACF;AAEA,SAAS,EACP,GACA,GACA,GAC8B;CAC9B,IAAI,CAAC,MAAM,QAAQ,CAAK,GAAG,MAAU,UAAU,8BAA8B;CAC7E,IAAI,EAAM,SAAS,IAAI,MAAU,WAAW,6CAA6C;CACzF,IAAI,IAAa;CACjB,OAAO,EAAM,KAAK,MAAU;EAC1B,IAAM,IAAS,EAAa,GAAO,YAAY,GACzC,IAAO,EAAe,EAAO,MAAM,iBAAiB,GACpD,IAAO,EAAY,EAAO,MAAM,iBAAiB;EACvD,IAAI,EAAK,aAAa,GACpB,MAAU,WAAW,eAAe,EAAK,gBAAgB,EAAS,YAAY;EAGhF,IADA,KAAc,EAAK,YACf,IAAa,GACf,MAAU,WAAW,0BAA0B,EAAc,kBAAkB;EAEjF,OAAO;GACL;GACA;GACA,GAAI,EAAO,aAAa,KAAA,IACpB,CAAC,IACD,EAAE,UAAU,EAAe,EAAO,UAAU,sBAAsB,EAAE;EAC1E;CACF,CAAC;AACH;AAEA,SAAS,EACP,GACA,GACA,GAMqB;CACrB,IAAM,IAAO,EAAS,QAAQ,EAAS,aAAa,cAAc;CAClE,OAAO,EAAY,SAAS;EAC1B,MAAM,EAAS,SAAS,WAAW,QAAQ,IAAI,UAAU;EACzD,MAAM;EACN,MAAM,EAAS;EACf,UAAU,EAAS;EACnB;EACA,GAAI,EAAS,eACR,EAAS,aAAa,mBACtB,EAAS,YAAY,cAAc,IACpC,EAAE,YAAY,QAAQ,EAAS,SAAS,UAAU,OAAO,KAAK,EAAS,WAAW,CAAC,CAAC,SAAS,QAAQ,IAAI,IACzG,CAAC;CACP,CAAC;AACH;AAEA,SAAS,EAAiB,GAA6C;CACrE,IAAM,IAAS,EAAa,GAAO,mBAAmB,GAChD,IAAO,OAAO,EAAO,QAAS,WAAW,EAAO,OAAO,EAAQ,iCAAiC,GAChG,IAAO,EAAO,SAAS,KAAA,IACzB,KAAA,IACA,OAAO,EAAO,QAAS,WACrB,EAAO,OACP,EAAQ,iCAAiC;CAC/C,IAAI,EAAK,SAAS,QAAc,GAAM,UAAU,KAAK,KACnD,MAAU,WAAW,gCAAgC;CAEvD,OAAO;EAAE;EAAM,GAAI,IAAO,EAAE,QAAK,IAAI,CAAC;CAAG;AAC3C;AAEA,SAAS,EAAa,GAAwB;CAC5C,IAAM,IAAO,EAAe,GAAO,cAAc,GAC3C,IAAM,IAAI,IAAI,CAAI;CACxB,IAAI,CAAC;EAAC;EAAS;EAAU;EAAW;CAAM,CAAC,CAAC,SAAS,EAAI,SAAS,YAAY,CAAC,GAC7E,MAAU,UAAU,sCAAsC,EAAI,UAAU;CAE1E,OAAO,EAAI;AACb;AAEA,SAAS,EAAmB,GAA6D;CACvF,IAAI,MAAU,KAAA,GAAW;CACzB,IAAM,IAAS,EAAa,GAAO,8BAA8B;CACjE,IAAI,EAAO,SAAS,KAAA,KAAa,OAAO,EAAO,QAAS,WACtD,MAAU,UAAU,kDAAkD;CAExE,OAAO;EACL,GAAI,EAAO,WAAW,KAAA,IAClB,CAAC,IACD,EAAE,QAAQ,EAAe,EAAO,QAAQ,6BAA6B,EAAE;EAC3E,GAAI,EAAO,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,MAAM,EAAO,KAAK;CAC3D;AACF;AAEA,SAAS,EAAY,GAAgB,GAA4B;CAC/D,IAAI,EAAE,aAAiB,cAAc,MAAU,UAAU,GAAG,EAAM,wBAAwB;CAC1F,OAAO;AACT;AAEA,SAAS,EAAa,GAAgB,GAAwC;CAC5E,IAAI,CAAC,KAAS,OAAO,KAAU,YAAY,MAAM,QAAQ,CAAK,GAC5D,MAAU,UAAU,GAAG,EAAM,mBAAmB;CAElD,OAAO;AACT;AAEA,SAAS,EAAe,GAAgB,GAAuB;CAC7D,IAAI,OAAO,KAAU,YAAY,CAAC,EAAM,KAAK,GAAG,MAAU,UAAU,GAAG,EAAM,4BAA4B;CACzG,OAAO,EAAM,KAAK;AACpB;AAEA,SAAS,EAAmB,GAAyB;CACnD,OAAO,GAAQ,KAAS,OAAO,KAAU,YAAY,UAAU,KAAS,EAAM,SAAS;AACzF;AAEA,SAAS,EAAa,GAAuB;CAE3C,OADa,EAAK,SAAS,CAAK,CAAC,CAAC,QAAQ,0BAA0B,EAAE,CAAC,CAAC,KACjE,KAAQ;AACjB;AAEA,SAAS,EAA+B,GAA8B;CACpE,IAAI,CAAC,EAAK,WAAW,CAAK,GAAG,OAAO;CACpC,IAAM,IAAW,EAAK,QAAQ,CAAK,GAC7B,IAAY,EAAK,QAAQ,CAAQ;CAEvC,OADI,EAAK,QAAQ,CAAS,MAAM,EAAK,QAAQ,EAAG,OAAO,CAAC,KACjD,0CAA0C,KAAK,EAAK,SAAS,CAAS,CAAC,IAC1E,IACA;AACN;AAEA,SAAS,EAAkB,GAA2B,GAAkB,GAAuB;CAC7F,IAAI,MAAU,KAAA,GAAW,OAAO;CAChC,IAAI,CAAC,OAAO,cAAc,CAAK,KAAK,KAAS,GAAG,MAAU,UAAU,GAAG,EAAM,4BAA4B;CACzG,OAAO;AACT;AAEA,SAAS,EAAgB,GAA0B;CAEjD,OAAQ;EACN,MAAM;EACN,KAAK;EACL,KAAK;EACL,MAAM;EACN,MAAM;EACN,MAAM;EACN,KAAK;EACL,KAAK;EACL,KAAK;EACL,MAAM;CACR,EAZkB,EAAK,QAAQ,CAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAYrB,MAAc;AAC7C;AAEA,SAAS,EAAQ,GAAwB;CACvC,MAAU,UAAU,CAAO;AAC7B;;;AC9UA,SAAgB,EAA0B,GAA+C;CACvF,IAAM,IAAc,IAAI,EAAgC,GAClD,IAAe,EAAQ,kBAAkB,EAAE,iBAAiB,EAAQ,gBAAgB,IAAI,CAAC,GACzF,IAAoB,EACxB,EAAQ,SACR,EAAQ,QACR,EAAQ,SACR;EAAE,GAAG;EAAc,oBAAoB,MAAe,EAAY,QAAQ,CAAU;CAAE,CACxF;CACA,IAAI;EACF,IAAM,IAAmB,EAAuB,GAAS;GAAE,GAAG,EAAQ;GAAQ,GAAG;EAAa,GAAG,CAAW;EAC5G,aAAa;GAEX,AADA,EAAiB,GACjB,EAAkB;EACpB;CACF,SAAS,GAAO;EAEd,MADA,EAAkB,GACZ;CACR;AACF"}
|
package/dist/preload.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as e,
|
|
2
|
-
export {
|
|
1
|
+
import { a as e, i as t, l as n, o as r, t as i } from "./chunks/codex-electron-preload-DupT4aeD.js";
|
|
2
|
+
export { n as TypedIpcRenderer, t as createCodexNativeRendererApi, r as createCodexSurfaceRendererApi, i as exposeCodexElectronPreload, e as exposeCodexNativeRendererApi };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codex-app-sdk/electron",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "Electron main, preload, IPC, and native capability adapter for Codex applications.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@codex-app-sdk/backend": "0.
|
|
41
|
-
"@codex-app-sdk/core": "0.
|
|
40
|
+
"@codex-app-sdk/backend": "0.15.0",
|
|
41
|
+
"@codex-app-sdk/core": "0.15.0"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"electron": ">=30"
|