@ccpocket/bridge 1.53.1 → 1.54.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/sessions-index.js +203 -25
- package/dist/sessions-index.js.map +1 -1
- package/dist/websocket.d.ts +1 -0
- package/dist/websocket.js +52 -0
- package/dist/websocket.js.map +1 -1
- package/package.json +1 -1
package/dist/sessions-index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile, writeFile, appendFile, stat, open } from "node:fs/promises";
|
|
2
2
|
import { createReadStream } from "node:fs";
|
|
3
3
|
import { createInterface } from "node:readline";
|
|
4
|
-
import { basename, join } from "node:path";
|
|
4
|
+
import { basename, extname, join } from "node:path";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
6
|
import { isAutoRenamePromptText } from "./auto-rename.js";
|
|
7
7
|
import { CODEX_ASSIST_MODEL } from "./codex-assist.js";
|
|
@@ -2104,46 +2104,221 @@ async function extractCodexMessageImages(sessionId, messageUuid) {
|
|
|
2104
2104
|
catch {
|
|
2105
2105
|
return [];
|
|
2106
2106
|
}
|
|
2107
|
-
// Codex doesn't have per-message UUIDs in the same way.
|
|
2108
|
-
//
|
|
2109
|
-
//
|
|
2107
|
+
// Codex doesn't have per-message UUIDs in the same way. Newer app history
|
|
2108
|
+
// uses a stable turn ordinal (codex:user-turn:N); older builds encoded the
|
|
2109
|
+
// JSONL line index (codex-line:N).
|
|
2110
2110
|
const lineIndex = messageUuid.startsWith("codex-line-")
|
|
2111
2111
|
? parseInt(messageUuid.slice("codex-line-".length), 10)
|
|
2112
2112
|
: -1;
|
|
2113
|
-
if (lineIndex < 0)
|
|
2114
|
-
return [];
|
|
2115
2113
|
const lines = raw.split("\n");
|
|
2116
|
-
if (lineIndex >=
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2114
|
+
if (lineIndex >= 0) {
|
|
2115
|
+
if (lineIndex >= lines.length)
|
|
2116
|
+
return [];
|
|
2117
|
+
const line = lines[lineIndex];
|
|
2118
|
+
if (!line?.trim())
|
|
2119
|
+
return [];
|
|
2120
|
+
let entry;
|
|
2121
|
+
try {
|
|
2122
|
+
entry = JSON.parse(line);
|
|
2123
|
+
}
|
|
2124
|
+
catch {
|
|
2125
|
+
return [];
|
|
2126
|
+
}
|
|
2127
|
+
if (entry.type !== "event_msg")
|
|
2128
|
+
return [];
|
|
2129
|
+
const payload = asObject(entry.payload);
|
|
2130
|
+
if (!payload || payload.type !== "user_message")
|
|
2131
|
+
return [];
|
|
2132
|
+
return extractCodexUserMessagePayloadImages(payload);
|
|
2124
2133
|
}
|
|
2125
|
-
|
|
2134
|
+
const turnMatch = messageUuid.match(/^codex:user-turn:(\d+)$/);
|
|
2135
|
+
const targetOrdinal = turnMatch ? Number(turnMatch[1]) : -1;
|
|
2136
|
+
if (!Number.isInteger(targetOrdinal) || targetOrdinal <= 0)
|
|
2126
2137
|
return [];
|
|
2138
|
+
const responseItemImagesByOrdinal = collectCodexUserResponseItemImagesByOrdinal(lines);
|
|
2139
|
+
let ordinal = 0;
|
|
2140
|
+
for (const line of lines) {
|
|
2141
|
+
if (!line.trim())
|
|
2142
|
+
continue;
|
|
2143
|
+
let entry;
|
|
2144
|
+
try {
|
|
2145
|
+
entry = JSON.parse(line);
|
|
2146
|
+
}
|
|
2147
|
+
catch {
|
|
2148
|
+
continue;
|
|
2149
|
+
}
|
|
2150
|
+
if (entry.type !== "event_msg")
|
|
2151
|
+
continue;
|
|
2152
|
+
const payload = asObject(entry.payload);
|
|
2153
|
+
if (!payload || payload.type !== "user_message")
|
|
2154
|
+
continue;
|
|
2155
|
+
if (!codexUserMessagePayloadHasDisplayContent(payload))
|
|
2156
|
+
continue;
|
|
2157
|
+
ordinal += 1;
|
|
2158
|
+
if (ordinal === targetOrdinal) {
|
|
2159
|
+
const images = await extractCodexUserMessagePayloadImages(payload);
|
|
2160
|
+
return images.length > 0
|
|
2161
|
+
? images
|
|
2162
|
+
: (responseItemImagesByOrdinal.get(targetOrdinal) ?? []);
|
|
2163
|
+
}
|
|
2127
2164
|
}
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
if (!payload || payload.type !== "user_message")
|
|
2132
|
-
return [];
|
|
2165
|
+
return [];
|
|
2166
|
+
}
|
|
2167
|
+
async function extractCodexUserMessagePayloadImages(payload) {
|
|
2133
2168
|
const images = [];
|
|
2134
|
-
// Parse payload.images (Data URI format: "data:image/png;base64,...")
|
|
2135
2169
|
if (Array.isArray(payload.images)) {
|
|
2136
2170
|
for (const img of payload.images) {
|
|
2137
|
-
if (typeof img
|
|
2171
|
+
if (typeof img === "string") {
|
|
2172
|
+
const match = img.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
2173
|
+
if (match) {
|
|
2174
|
+
images.push({ base64: match[2], mimeType: match[1] });
|
|
2175
|
+
}
|
|
2138
2176
|
continue;
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2177
|
+
}
|
|
2178
|
+
const item = asObject(img);
|
|
2179
|
+
if (!item)
|
|
2180
|
+
continue;
|
|
2181
|
+
const base64 = typeof item.base64 === "string"
|
|
2182
|
+
? item.base64
|
|
2183
|
+
: typeof item.data === "string"
|
|
2184
|
+
? item.data
|
|
2185
|
+
: undefined;
|
|
2186
|
+
const mimeType = typeof item.mimeType === "string"
|
|
2187
|
+
? item.mimeType
|
|
2188
|
+
: typeof item.mime_type === "string"
|
|
2189
|
+
? item.mime_type
|
|
2190
|
+
: typeof item.media_type === "string"
|
|
2191
|
+
? item.media_type
|
|
2192
|
+
: undefined;
|
|
2193
|
+
if (base64 && mimeType) {
|
|
2194
|
+
images.push({ base64, mimeType });
|
|
2142
2195
|
}
|
|
2143
2196
|
}
|
|
2144
2197
|
}
|
|
2198
|
+
if (Array.isArray(payload.local_images)) {
|
|
2199
|
+
for (const imagePath of payload.local_images) {
|
|
2200
|
+
if (typeof imagePath !== "string" || imagePath.length === 0)
|
|
2201
|
+
continue;
|
|
2202
|
+
const image = await readLocalImageAsBase64(imagePath);
|
|
2203
|
+
if (image)
|
|
2204
|
+
images.push(image);
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2145
2207
|
return images;
|
|
2146
2208
|
}
|
|
2209
|
+
function collectCodexUserResponseItemImagesByOrdinal(lines) {
|
|
2210
|
+
const imagesByOrdinal = new Map();
|
|
2211
|
+
let ordinal = 0;
|
|
2212
|
+
for (const line of lines) {
|
|
2213
|
+
if (!line.trim())
|
|
2214
|
+
continue;
|
|
2215
|
+
let entry;
|
|
2216
|
+
try {
|
|
2217
|
+
entry = JSON.parse(line);
|
|
2218
|
+
}
|
|
2219
|
+
catch {
|
|
2220
|
+
continue;
|
|
2221
|
+
}
|
|
2222
|
+
if (entry.type !== "response_item")
|
|
2223
|
+
continue;
|
|
2224
|
+
const payload = asObject(entry.payload);
|
|
2225
|
+
if (!payload ||
|
|
2226
|
+
payload.type !== "message" ||
|
|
2227
|
+
payload.role !== "user" ||
|
|
2228
|
+
!codexUserResponseItemHasDisplayContent(payload)) {
|
|
2229
|
+
continue;
|
|
2230
|
+
}
|
|
2231
|
+
ordinal += 1;
|
|
2232
|
+
const images = extractCodexUserResponseItemImages(payload);
|
|
2233
|
+
if (images.length > 0) {
|
|
2234
|
+
imagesByOrdinal.set(ordinal, images);
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
return imagesByOrdinal;
|
|
2238
|
+
}
|
|
2239
|
+
function codexUserResponseItemHasDisplayContent(payload) {
|
|
2240
|
+
const texts = [];
|
|
2241
|
+
let hasImage = false;
|
|
2242
|
+
for (const item of arrayValue(payload.content)) {
|
|
2243
|
+
const content = asObject(item);
|
|
2244
|
+
if (!content)
|
|
2245
|
+
continue;
|
|
2246
|
+
if (content.type === "input_image") {
|
|
2247
|
+
hasImage = true;
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
if (content.type !== "input_text" || typeof content.text !== "string") {
|
|
2251
|
+
continue;
|
|
2252
|
+
}
|
|
2253
|
+
texts.push(content.text);
|
|
2254
|
+
}
|
|
2255
|
+
const userText = texts
|
|
2256
|
+
.filter((text) => !isCodexImageWrapperText(text.trim()))
|
|
2257
|
+
.join("\n")
|
|
2258
|
+
.trim();
|
|
2259
|
+
if (userText && isCodexInjectedUserContext(userText))
|
|
2260
|
+
return false;
|
|
2261
|
+
return userText.length > 0 || hasImage;
|
|
2262
|
+
}
|
|
2263
|
+
function extractCodexUserResponseItemImages(payload) {
|
|
2264
|
+
const images = [];
|
|
2265
|
+
for (const item of arrayValue(payload.content)) {
|
|
2266
|
+
const content = asObject(item);
|
|
2267
|
+
if (!content || content.type !== "input_image")
|
|
2268
|
+
continue;
|
|
2269
|
+
const imageUrl = typeof content.image_url === "string"
|
|
2270
|
+
? content.image_url
|
|
2271
|
+
: typeof content.url === "string"
|
|
2272
|
+
? content.url
|
|
2273
|
+
: undefined;
|
|
2274
|
+
const image = extractDataUriImage(imageUrl);
|
|
2275
|
+
if (image)
|
|
2276
|
+
images.push(image);
|
|
2277
|
+
}
|
|
2278
|
+
return images;
|
|
2279
|
+
}
|
|
2280
|
+
function extractDataUriImage(value) {
|
|
2281
|
+
const match = value?.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
2282
|
+
return match ? { base64: match[2], mimeType: match[1] } : null;
|
|
2283
|
+
}
|
|
2284
|
+
function isCodexImageWrapperText(text) {
|
|
2285
|
+
return /^<image(?:\s[^>]*)?>$/.test(text) || text === "</image>";
|
|
2286
|
+
}
|
|
2287
|
+
async function readLocalImageAsBase64(imagePath) {
|
|
2288
|
+
const mimeType = mimeTypeForLocalImagePath(imagePath);
|
|
2289
|
+
if (!mimeType)
|
|
2290
|
+
return null;
|
|
2291
|
+
try {
|
|
2292
|
+
const buffer = await readFile(imagePath);
|
|
2293
|
+
return { base64: buffer.toString("base64"), mimeType };
|
|
2294
|
+
}
|
|
2295
|
+
catch {
|
|
2296
|
+
return null;
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
function mimeTypeForLocalImagePath(imagePath) {
|
|
2300
|
+
switch (extname(imagePath).toLowerCase()) {
|
|
2301
|
+
case ".png":
|
|
2302
|
+
return "image/png";
|
|
2303
|
+
case ".jpg":
|
|
2304
|
+
case ".jpeg":
|
|
2305
|
+
return "image/jpeg";
|
|
2306
|
+
case ".gif":
|
|
2307
|
+
return "image/gif";
|
|
2308
|
+
case ".webp":
|
|
2309
|
+
return "image/webp";
|
|
2310
|
+
default:
|
|
2311
|
+
return null;
|
|
2312
|
+
}
|
|
2313
|
+
}
|
|
2314
|
+
function codexUserMessagePayloadHasDisplayContent(payload) {
|
|
2315
|
+
const message = typeof payload.message === "string" ? payload.message : "";
|
|
2316
|
+
const images = Array.isArray(payload.images) ? payload.images.length : 0;
|
|
2317
|
+
const localImages = Array.isArray(payload.local_images)
|
|
2318
|
+
? payload.local_images.length
|
|
2319
|
+
: 0;
|
|
2320
|
+
return message.trim().length > 0 || images + localImages > 0;
|
|
2321
|
+
}
|
|
2147
2322
|
export async function getCodexSessionHistory(threadId) {
|
|
2148
2323
|
const jsonlPath = await findCodexSessionJsonlPath(threadId);
|
|
2149
2324
|
if (!jsonlPath)
|
|
@@ -2250,6 +2425,9 @@ export async function getCodexSessionHistory(threadId) {
|
|
|
2250
2425
|
continue;
|
|
2251
2426
|
}
|
|
2252
2427
|
if (payload.role === "user") {
|
|
2428
|
+
if (content.some((item) => item.type === "input_image")) {
|
|
2429
|
+
continue;
|
|
2430
|
+
}
|
|
2253
2431
|
const text = content
|
|
2254
2432
|
.filter((item) => item.type === "input_text" && typeof item.text === "string")
|
|
2255
2433
|
.map((item) => item.text)
|