(lines: string[])
| 886 | } |
| 887 | |
| 888 | function parseCodexMessagesFromLines(lines: string[]): WebCodexThreadMessage[] { |
| 889 | const messages: WebCodexThreadMessage[] = []; |
| 890 | for (const line of lines) { |
| 891 | const trimmed = line.trim(); |
| 892 | if (!trimmed) { |
| 893 | continue; |
| 894 | } |
| 895 | try { |
| 896 | const parsed = JSON.parse(trimmed) as { |
| 897 | timestamp?: string; |
| 898 | type?: string; |
| 899 | payload?: { |
| 900 | type?: string; |
| 901 | role?: string; |
| 902 | content?: Array<{ type?: string; text?: string; content?: Array<{ text?: string }> }>; |
| 903 | }; |
| 904 | }; |
| 905 | if (parsed.type !== 'response_item' || parsed.payload?.type !== 'message') { |
| 906 | continue; |
| 907 | } |
| 908 | if (parsed.payload.role !== 'user' && parsed.payload.role !== 'assistant') { |
| 909 | continue; |
| 910 | } |
| 911 | const text = extractMessageText(parsed.payload.content); |
| 912 | if (!text || isInternalCodexUserMessage(parsed.payload.role, text)) { |
| 913 | continue; |
| 914 | } |
| 915 | messages.push({ |
| 916 | id: `${parsed.timestamp ?? 'msg'}:${messages.length}`, |
| 917 | role: parsed.payload.role, |
| 918 | source: 'history', |
| 919 | text, |
| 920 | timestamp: parsed.timestamp ?? null, |
| 921 | }); |
| 922 | } catch { |
| 923 | continue; |
| 924 | } |
| 925 | } |
| 926 | return messages; |
| 927 | } |
| 928 | |
| 929 | function readFileTail(filePath: string, maxBytes = 1024 * 1024): string { |
| 930 | const stat = fs.statSync(filePath); |
no test coverage detected